diff options
122 files changed, 1418 insertions, 1096 deletions
diff --git a/.github/workflows/macos_builds.yml b/.github/workflows/macos_builds.yml index 04bdf971ca..70031ec4c3 100644 --- a/.github/workflows/macos_builds.yml +++ b/.github/workflows/macos_builds.yml @@ -14,7 +14,7 @@ concurrency: jobs: build-macos: - runs-on: "macos-12" + runs-on: "macos-latest" name: ${{ matrix.name }} strategy: fail-fast: false @@ -24,7 +24,7 @@ jobs: cache-name: macos-editor target: editor tests: true - bin: "./bin/godot.macos.editor.x86_64" + bin: "./bin/godot.macos.editor.universal" - name: Template (target=template_release) cache-name: macos-template @@ -50,16 +50,26 @@ jobs: run: | sh misc/scripts/install_vulkan_sdk_macos.sh - - name: Compilation + - name: Compilation (x86_64) uses: ./.github/actions/godot-build with: - sconsflags: ${{ env.SCONSFLAGS }} + sconsflags: ${{ env.SCONSFLAGS }} arch=x86_64 + platform: macos + target: ${{ matrix.target }} + tests: ${{ matrix.tests }} + + - name: Compilation (arm64) + uses: ./.github/actions/godot-build + with: + sconsflags: ${{ env.SCONSFLAGS }} arch=arm64 platform: macos target: ${{ matrix.target }} tests: ${{ matrix.tests }} - name: Prepare artifact run: | + lipo -create ./bin/godot.macos.${{ matrix.target }}.x86_64 ./bin/godot.macos.${{ matrix.target }}.arm64 -output ./bin/godot.macos.${{ matrix.target }}.universal + rm ./bin/godot.macos.${{ matrix.target }}.x86_64 ./bin/godot.macos.${{ matrix.target }}.arm64 strip bin/godot.* chmod +x bin/godot.* diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 104b17961d..ee20aea35d 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -1017,7 +1017,7 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust } } // Check for the existence of a csproj file. - if (_csproj_exists(get_resource_path())) { + if (_csproj_exists(p_path.get_base_dir())) { // If there is a csproj file, add the C# feature if it doesn't already exist. if (!project_features.has("C#")) { project_features.append("C#"); @@ -1473,7 +1473,9 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF(PropertyInfo(Variant::INT, "display/window/size/window_height_override", PROPERTY_HINT_RANGE, "0,4320,1,or_greater"), 0); // 8K resolution GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true); - GLOBAL_DEF("display/window/energy_saving/keep_screen_on.editor", false); +#ifdef TOOLS_ENABLED + GLOBAL_DEF("display/window/energy_saving/keep_screen_on.editor_hint", false); +#endif GLOBAL_DEF("animation/warnings/check_invalid_track_paths", true); GLOBAL_DEF("animation/warnings/check_angle_interpolation_type_conflicting", true); @@ -1531,6 +1533,10 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF_BASIC("internationalization/rendering/root_node_auto_translate", true); GLOBAL_DEF(PropertyInfo(Variant::INT, "gui/timers/incremental_search_max_interval_msec", PROPERTY_HINT_RANGE, "0,10000,1,or_greater"), 2000); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "gui/timers/tooltip_delay_sec", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater"), 0.5); +#ifdef TOOLS_ENABLED + GLOBAL_DEF("gui/timers/tooltip_delay_sec.editor_hint", 0.5); +#endif GLOBAL_DEF_BASIC("gui/common/snap_controls_to_pixels", true); GLOBAL_DEF_BASIC("gui/fonts/dynamic_fonts/use_oversampling", true); @@ -1568,6 +1574,14 @@ ProjectSettings::ProjectSettings() { ProjectSettings::get_singleton()->add_hidden_prefix("input/"); } +ProjectSettings::ProjectSettings(const String &p_path) { + if (load_custom(p_path) == OK) { + project_loaded = true; + } +} + ProjectSettings::~ProjectSettings() { - singleton = nullptr; + if (singleton == this) { + singleton = nullptr; + } } diff --git a/core/config/project_settings.h b/core/config/project_settings.h index 1bad76acb1..922c88c151 100644 --- a/core/config/project_settings.h +++ b/core/config/project_settings.h @@ -224,6 +224,7 @@ public: #endif ProjectSettings(); + ProjectSettings(const String &p_path); ~ProjectSettings(); }; diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp index 3ea5791282..22a5df9935 100644 --- a/core/extension/gdextension.cpp +++ b/core/extension/gdextension.cpp @@ -275,7 +275,7 @@ public: ret_opaque = r_ret->get_type() == Variant::NIL ? r_ret : VariantInternal::get_opaque_pointer(r_ret); } - ptrcall(p_object, argptrs, ret_opaque); + ptrcall_func(method_userdata, extension_instance, reinterpret_cast<GDExtensionConstTypePtr *>(argptrs), (GDExtensionTypePtr)ret_opaque); if (r_ret && r_ret->get_type() == Variant::OBJECT) { VariantInternal::update_object_id(r_ret); @@ -289,7 +289,7 @@ public: ERR_FAIL_COND_MSG(p_object && p_object->is_extension_placeholder(), vformat("Cannot call GDExtension method bind '%s' on placeholder instance.", name)); #endif ERR_FAIL_COND_MSG(vararg, "Vararg methods don't have ptrcall support. This is most likely an engine bug."); - GDExtensionClassInstancePtr extension_instance = p_object->_get_extension_instance(); + GDExtensionClassInstancePtr extension_instance = is_static() ? nullptr : p_object->_get_extension_instance(); ptrcall_func(method_userdata, extension_instance, reinterpret_cast<GDExtensionConstTypePtr *>(p_args), (GDExtensionTypePtr)r_ret); } diff --git a/core/input/gamecontrollerdb.txt b/core/input/gamecontrollerdb.txt index a17e15a62c..7150911e75 100644 --- a/core/input/gamecontrollerdb.txt +++ b/core/input/gamecontrollerdb.txt @@ -917,6 +917,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000000d0f00003801000008010000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,platform:Mac OS X, 030000000d0f00009200000000010000,Hori Pokken Tournament DX Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f0000aa00000072050000,Hori Real Arcade Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, +030000000d0f00000002000017010000,Hori Split Pad Fit,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00000002000015010000,Hori Switch Split Pad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00006e00000000010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00006600000000010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, @@ -1282,6 +1283,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000000d0f00006b00000011010000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00001600000000010000,Hori Real Arcade Pro EXSE,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux, 030000000d0f0000aa00000011010000,Hori Real Arcade Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +030000000d0f00008501000017010000,Hori Split Pad Fit,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000000d0f00008501000015010000,Hori Switch Split Pad Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000000d0f00006e00000011010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00006600000011010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, @@ -1696,6 +1698,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 060000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 050000005e040000130b000017050000,Xbox Series X Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 060000005e040000120b00000d050000,Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b000011050000,Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000120b000014050000,Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 050000005e040000200b000013050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000005e040000200b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000005e040000220b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, diff --git a/core/io/logger.cpp b/core/io/logger.cpp index 441df7f5d1..1476b8ccac 100644 --- a/core/io/logger.cpp +++ b/core/io/logger.cpp @@ -37,6 +37,8 @@ #include "core/os/time.h" #include "core/string/print_string.h" +#include "modules/modules_enabled.gen.h" // For regex. + #if defined(MINGW_ENABLED) || defined(_MSC_VER) #define sprintf sprintf_s #endif @@ -180,6 +182,12 @@ RotatedFileLogger::RotatedFileLogger(const String &p_base_path, int p_max_files) base_path(p_base_path.simplify_path()), max_files(p_max_files > 0 ? p_max_files : 1) { rotate_file(); + +#ifdef MODULE_REGEX_ENABLED + strip_ansi_regex.instantiate(); + strip_ansi_regex->detach_from_objectdb(); // Note: This RegEx instance will exist longer than ObjectDB, therefore can't be registered in ObjectDB. + strip_ansi_regex->compile("\u001b\\[((?:\\d|;)*)([a-zA-Z])"); +#endif // MODULE_REGEX_ENABLED } void RotatedFileLogger::logv(const char *p_format, va_list p_list, bool p_err) { @@ -199,7 +207,15 @@ void RotatedFileLogger::logv(const char *p_format, va_list p_list, bool p_err) { vsnprintf(buf, len + 1, p_format, list_copy); } va_end(list_copy); + +#ifdef MODULE_REGEX_ENABLED + // Strip ANSI escape codes (such as those inserted by `print_rich()`) + // before writing to file, as text editors cannot display those + // correctly. + file->store_string(strip_ansi_regex->sub(String(buf), "", true)); +#else file->store_buffer((uint8_t *)buf, len); +#endif // MODULE_REGEX_ENABLED if (len >= static_buf_size) { Memory::free_static(buf); diff --git a/core/io/logger.h b/core/io/logger.h index 3cd18965c5..85ef3031ec 100644 --- a/core/io/logger.h +++ b/core/io/logger.h @@ -34,6 +34,10 @@ #include "core/io/file_access.h" #include "core/string/ustring.h" #include "core/templates/vector.h" +#include "modules/modules_enabled.gen.h" // For regex. +#ifdef MODULE_REGEX_ENABLED +#include "modules/regex/regex.h" +#endif // MODULE_REGEX_ENABLED #include <stdarg.h> @@ -86,6 +90,10 @@ class RotatedFileLogger : public Logger { void clear_old_backups(); void rotate_file(); +#ifdef MODULE_REGEX_ENABLED + Ref<RegEx> strip_ansi_regex; +#endif // MODULE_REGEX_ENABLED + public: explicit RotatedFileLogger(const String &p_base_path, int p_max_files = 10); diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index eea6357084..c3c37aa89d 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -242,16 +242,20 @@ ResourceLoader::LoadToken::~LoadToken() { } Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_original_path, const String &p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, Error *r_error, bool p_use_sub_threads, float *r_progress) { + const String &original_path = p_original_path.is_empty() ? p_path : p_original_path; load_nesting++; if (load_paths_stack->size()) { thread_load_mutex.lock(); - HashMap<String, ThreadLoadTask>::Iterator E = thread_load_tasks.find(load_paths_stack->get(load_paths_stack->size() - 1)); - if (E) { + const String &parent_task_path = load_paths_stack->get(load_paths_stack->size() - 1); + HashMap<String, ThreadLoadTask>::Iterator E = thread_load_tasks.find(parent_task_path); + // Avoid double-tracking, for progress reporting, resources that boil down to a remapped path containing the real payload (e.g., imported resources). + bool is_remapped_load = original_path == parent_task_path; + if (E && !is_remapped_load) { E->value.sub_tasks.insert(p_original_path); } thread_load_mutex.unlock(); } - load_paths_stack->push_back(p_original_path); + load_paths_stack->push_back(original_path); // Try all loaders and pick the first match for the type hint bool found = false; @@ -261,7 +265,7 @@ Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_origin continue; } found = true; - res = loader[i]->load(p_path, !p_original_path.is_empty() ? p_original_path : p_path, r_error, p_use_sub_threads, r_progress, p_cache_mode); + res = loader[i]->load(p_path, original_path, r_error, p_use_sub_threads, r_progress, p_cache_mode); if (!res.is_null()) { break; } diff --git a/core/object/class_db.h b/core/object/class_db.h index adb525cbe8..37a864c109 100644 --- a/core/object/class_db.h +++ b/core/object/class_db.h @@ -195,7 +195,7 @@ public: template <typename T> static void register_class(bool p_virtual = false) { GLOBAL_LOCK_FUNCTION; - static_assert(types_are_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); + static_assert(std::is_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); ERR_FAIL_NULL(t); @@ -210,7 +210,7 @@ public: template <typename T> static void register_abstract_class() { GLOBAL_LOCK_FUNCTION; - static_assert(types_are_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); + static_assert(std::is_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); ERR_FAIL_NULL(t); @@ -223,7 +223,7 @@ public: template <typename T> static void register_internal_class() { GLOBAL_LOCK_FUNCTION; - static_assert(types_are_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); + static_assert(std::is_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); ERR_FAIL_NULL(t); @@ -238,7 +238,7 @@ public: template <typename T> static void register_runtime_class() { GLOBAL_LOCK_FUNCTION; - static_assert(types_are_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); + static_assert(std::is_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); ERR_FAIL_NULL(t); @@ -263,7 +263,7 @@ public: template <typename T> static void register_custom_instance_class() { GLOBAL_LOCK_FUNCTION; - static_assert(types_are_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); + static_assert(std::is_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); ERR_FAIL_NULL(t); diff --git a/core/os/os.cpp b/core/os/os.cpp index 8582888740..fa7f23ded0 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -398,6 +398,11 @@ bool OS::has_feature(const String &p_feature) { if (p_feature == "editor") { return true; } + if (p_feature == "editor_hint") { + return _in_editor; + } else if (p_feature == "editor_runtime") { + return !_in_editor; + } #else if (p_feature == "template") { return true; diff --git a/core/os/os.h b/core/os/os.h index 069a3876af..d20f84b4ff 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -63,6 +63,7 @@ class OS { bool _stdout_enabled = true; bool _stderr_enabled = true; bool _writing_movie = false; + bool _in_editor = false; CompositeLogger *_logger = nullptr; 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 8e702ce8bb..fcbfdd4741 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -3495,50 +3495,6 @@ bool Variant::is_ref_counted() const { return type == OBJECT && _get_obj().id.is_ref_counted(); } -Vector<Variant> varray() { - return Vector<Variant>(); -} - -Vector<Variant> varray(const Variant &p_arg1) { - Vector<Variant> v; - v.push_back(p_arg1); - return v; -} - -Vector<Variant> varray(const Variant &p_arg1, const Variant &p_arg2) { - Vector<Variant> v; - v.push_back(p_arg1); - v.push_back(p_arg2); - return v; -} - -Vector<Variant> varray(const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3) { - Vector<Variant> v; - v.push_back(p_arg1); - v.push_back(p_arg2); - v.push_back(p_arg3); - return v; -} - -Vector<Variant> varray(const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4) { - Vector<Variant> v; - v.push_back(p_arg1); - v.push_back(p_arg2); - v.push_back(p_arg3); - v.push_back(p_arg4); - return v; -} - -Vector<Variant> varray(const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4, const Variant &p_arg5) { - Vector<Variant> v; - v.push_back(p_arg1); - v.push_back(p_arg2); - v.push_back(p_arg3); - v.push_back(p_arg4); - v.push_back(p_arg5); - return v; -} - void Variant::static_assign(const Variant &p_variant) { } diff --git a/core/variant/variant.h b/core/variant/variant.h index e40df3171f..ea6ae02c1e 100644 --- a/core/variant/variant.h +++ b/core/variant/variant.h @@ -798,12 +798,23 @@ public: //typedef Dictionary Dictionary; no //typedef Array Array; -Vector<Variant> varray(); -Vector<Variant> varray(const Variant &p_arg1); -Vector<Variant> varray(const Variant &p_arg1, const Variant &p_arg2); -Vector<Variant> varray(const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3); -Vector<Variant> varray(const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4); -Vector<Variant> varray(const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4, const Variant &p_arg5); +template <typename... VarArgs> +Vector<Variant> varray(VarArgs... p_args) { + Vector<Variant> v; + + Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. + uint32_t argc = sizeof...(p_args); + + if (argc > 0) { + v.resize(argc); + Variant *vw = v.ptrw(); + + for (uint32_t i = 0; i < argc; i++) { + vw[i] = args[i]; + } + } + return v; +} struct VariantHasher { static _FORCE_INLINE_ uint32_t hash(const Variant &p_variant) { return p_variant.hash(); } diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml index 1cfd44467f..18413c4be5 100644 --- a/doc/classes/CanvasItem.xml +++ b/doc/classes/CanvasItem.xml @@ -8,6 +8,7 @@ Any [CanvasItem] can draw. For this, [method queue_redraw] is called by the engine, then [constant NOTIFICATION_DRAW] will be received on idle time to request a redraw. Because of this, canvas items don't need to be redrawn on every frame, improving the performance significantly. Several functions for drawing on the [CanvasItem] are provided (see [code]draw_*[/code] functions). However, they can only be used inside [method _draw], its corresponding [method Object._notification] or methods connected to the [signal draw] signal. Canvas items are drawn in tree order on their canvas layer. By default, children are on top of their parents, so a root [CanvasItem] will be drawn behind everything. This behavior can be changed on a per-item basis. A [CanvasItem] can be hidden, which will also hide its children. By adjusting various other properties of a [CanvasItem], you can also modulate its color (via [member modulate] or [member self_modulate]), change its Z-index, blend mode, and more. + Note that properties like transform, modulation, and visibility are only propagated to [i]direct[/i] [CanvasItem] child nodes. If there is a non-[CanvasItem] node in between, like [Node] or [AnimationPlayer], the [CanvasItem] nodes below will have an independent position and [member modulate] chain. See also [member top_level]. </description> <tutorials> <link title="Viewport and canvas transforms">$DOCS_URL/tutorials/2d/2d_transforms.html</link> diff --git a/doc/classes/FileAccess.xml b/doc/classes/FileAccess.xml index fafc02734a..99574da808 100644 --- a/doc/classes/FileAccess.xml +++ b/doc/classes/FileAccess.xml @@ -8,23 +8,23 @@ Here's a sample on how to write and read from a file: [codeblocks] [gdscript] - func save(content): + func save_to_file(content): var file = FileAccess.open("user://save_game.dat", FileAccess.WRITE) file.store_string(content) - func load(): + func load_from_file(): var file = FileAccess.open("user://save_game.dat", FileAccess.READ) var content = file.get_as_text() return content [/gdscript] [csharp] - public void Save(string content) + public void SaveToFile(string content) { using var file = FileAccess.Open("user://save_game.dat", FileAccess.ModeFlags.Write); file.StoreString(content); } - public string Load() + public string LoadFromFile() { using var file = FileAccess.Open("user://save_game.dat", FileAccess.ModeFlags.Read); string content = file.GetAsText(); diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index 37e64da8c8..3342e99ab6 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -619,6 +619,12 @@ [method request_ready] resets it back to [code]false[/code]. </description> </method> + <method name="is_part_of_edited_scene" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code] if the node is part of the scene currently opened in the editor. + </description> + </method> <method name="is_physics_interpolated" qualifiers="const"> <return type="bool" /> <description> diff --git a/doc/classes/ParticleProcessMaterial.xml b/doc/classes/ParticleProcessMaterial.xml index 8d0ae317b9..1502690b45 100644 --- a/doc/classes/ParticleProcessMaterial.xml +++ b/doc/classes/ParticleProcessMaterial.xml @@ -185,6 +185,7 @@ </member> <member name="emission_box_extents" type="Vector3" setter="set_emission_box_extents" getter="get_emission_box_extents"> The box's extents if [member emission_shape] is set to [constant EMISSION_SHAPE_BOX]. + [b]Note:[/b] [member emission_box_extents] starts from the center point and applies the X, Y, and Z values in both directions. The size is twice the area of the extents. </member> <member name="emission_color_texture" type="Texture2D" setter="set_emission_color_texture" getter="get_emission_color_texture"> Particle color will be modulated by color determined by sampling this texture at the same point as the [member emission_point_texture]. diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 1daa1b04e4..0d4d8bbebb 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -254,6 +254,7 @@ Path to an image used as the boot splash. If left empty, the default Godot Engine splash will be displayed instead. [b]Note:[/b] Only effective if [member application/boot_splash/show_image] is [code]true[/code]. [b]Note:[/b] The only supported format is PNG. Using another image format will result in an error. + [b]Note:[/b] The image will also show when opening the project in the editor. If you want to display the default splash image in the editor, add an empty override for [code]editor_hint[/code] feature. </member> <member name="application/boot_splash/minimum_display_time" type="int" setter="" getter="" default="0"> Minimum boot splash display time (in milliseconds). It is not recommended to set too high values for this setting. @@ -797,8 +798,8 @@ <member name="display/window/energy_saving/keep_screen_on" type="bool" setter="" getter="" default="true"> If [code]true[/code], keeps the screen on (even in case of inactivity), so the screensaver does not take over. Works on desktop and mobile platforms. </member> - <member name="display/window/energy_saving/keep_screen_on.editor" type="bool" setter="" getter="" default="false"> - Editor-only override for [member display/window/energy_saving/keep_screen_on]. Does not affect exported projects in debug or release mode. + <member name="display/window/energy_saving/keep_screen_on.editor_hint" type="bool" setter="" getter="" default="false"> + Editor-only override for [member display/window/energy_saving/keep_screen_on]. Does not affect running project. </member> <member name="display/window/handheld/orientation" type="int" setter="" getter="" default="0"> The default screen orientation to use on mobile devices. See [enum DisplayServer.ScreenOrientation] for possible values. @@ -1071,6 +1072,9 @@ <member name="gui/timers/tooltip_delay_sec" type="float" setter="" getter="" default="0.5"> Default delay for tooltips (in seconds). </member> + <member name="gui/timers/tooltip_delay_sec.editor_hint" type="float" setter="" getter="" default="0.5"> + Delay for tooltips in the editor. + </member> <member name="input/ui_accept" type="Dictionary" setter="" getter=""> Default [InputEventAction] to confirm a focused button, menu or list item, or validate input. [b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified. 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/ScriptEditorBase.xml b/doc/classes/ScriptEditorBase.xml index dca4fe9276..403608355a 100644 --- a/doc/classes/ScriptEditorBase.xml +++ b/doc/classes/ScriptEditorBase.xml @@ -71,6 +71,12 @@ Emitted when the user contextual goto and the item is in the same script. </description> </signal> + <signal name="request_save_previous_state"> + <param index="0" name="line" type="int" /> + <description> + Emitted when the user changes current script or moves caret by 10 or more columns within the same script. + </description> + </signal> <signal name="search_in_files_requested"> <param index="0" name="text" type="String" /> <description> diff --git a/doc/classes/TabContainer.xml b/doc/classes/TabContainer.xml index f4d69c3076..090afa0220 100644 --- a/doc/classes/TabContainer.xml +++ b/doc/classes/TabContainer.xml @@ -64,6 +64,13 @@ Returns the [Texture2D] for the tab at index [param tab_idx] or [code]null[/code] if the tab has no [Texture2D]. </description> </method> + <method name="get_tab_icon_max_width" qualifiers="const"> + <return type="int" /> + <param index="0" name="tab_idx" type="int" /> + <description> + Returns the maximum allowed width of the icon for the tab at index [param tab_idx]. + </description> + </method> <method name="get_tab_idx_at_point" qualifiers="const"> <return type="int" /> <param index="0" name="point" type="Vector2" /> @@ -164,6 +171,14 @@ Sets an icon for the tab at index [param tab_idx]. </description> </method> + <method name="set_tab_icon_max_width"> + <return type="void" /> + <param index="0" name="tab_idx" type="int" /> + <param index="1" name="width" type="int" /> + <description> + Sets the maximum allowed width of the icon for the tab at index [param tab_idx]. This limit is applied on top of the default size of the icon and on top of [theme_item icon_max_width]. The height is adjusted according to the icon's ratio. + </description> + </method> <method name="set_tab_metadata"> <return type="void" /> <param index="0" name="tab_idx" type="int" /> diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml index d95492479c..43cd1a8aaa 100644 --- a/doc/classes/Tree.xml +++ b/doc/classes/Tree.xml @@ -546,16 +546,16 @@ The maximum allowed width of the icon in item's cells. This limit is applied on top of the default size of the icon, but before the value set with [method TreeItem.set_icon_max_width]. The height is adjusted according to the icon's ratio. </theme_item> <theme_item name="inner_item_margin_bottom" data_type="constant" type="int" default="0"> - The inner bottom margin of an item. + The inner bottom margin of a cell. </theme_item> <theme_item name="inner_item_margin_left" data_type="constant" type="int" default="0"> - The inner left margin of an item. + The inner left margin of a cell. </theme_item> <theme_item name="inner_item_margin_right" data_type="constant" type="int" default="0"> - The inner right margin of an item. + The inner right margin of a cell. </theme_item> <theme_item name="inner_item_margin_top" data_type="constant" type="int" default="0"> - The inner top margin of an item. + The inner top margin of a cell. </theme_item> <theme_item name="item_margin" data_type="constant" type="int" default="16"> The horizontal margin at the start of an item. This is used when folding is enabled for the item. diff --git a/doc/classes/XRHandTracker.xml b/doc/classes/XRHandTracker.xml index 69390df696..636af6625b 100644 --- a/doc/classes/XRHandTracker.xml +++ b/doc/classes/XRHandTracker.xml @@ -11,12 +11,6 @@ <link title="XR documentation index">$DOCS_URL/tutorials/xr/index.html</link> </tutorials> <methods> - <method name="get_hand" qualifiers="const"> - <return type="int" enum="XRHandTracker.Hand" /> - <description> - Returns the type of hand. - </description> - </method> <method name="get_hand_joint_angular_velocity" qualifiers="const"> <return type="Vector3" /> <param index="0" name="joint" type="int" enum="XRHandTracker.HandJoint" /> @@ -52,13 +46,6 @@ Returns the transform for the given hand joint. </description> </method> - <method name="set_hand"> - <return type="void" /> - <param index="0" name="hand" type="int" enum="XRHandTracker.Hand" /> - <description> - Sets the type of hand. - </description> - </method> <method name="set_hand_joint_angular_velocity"> <return type="void" /> <param index="0" name="joint" type="int" enum="XRHandTracker.HandJoint" /> @@ -101,6 +88,7 @@ </method> </methods> <members> + <member name="hand" type="int" setter="set_tracker_hand" getter="get_tracker_hand" overrides="XRPositionalTracker" enum="XRPositionalTracker.TrackerHand" default="1" /> <member name="hand_tracking_source" type="int" setter="set_hand_tracking_source" getter="get_hand_tracking_source" enum="XRHandTracker.HandTrackingSource" default="0"> The source of the hand tracking data. </member> @@ -110,15 +98,6 @@ <member name="type" type="int" setter="set_tracker_type" getter="get_tracker_type" overrides="XRTracker" enum="XRServer.TrackerType" default="16" /> </members> <constants> - <constant name="HAND_LEFT" value="0" enum="Hand"> - A left hand. - </constant> - <constant name="HAND_RIGHT" value="1" enum="Hand"> - A right hand. - </constant> - <constant name="HAND_MAX" value="2" enum="Hand"> - Represents the size of the [enum Hand] enum. - </constant> <constant name="HAND_TRACKING_SOURCE_UNKNOWN" value="0" enum="HandTrackingSource"> The source of hand tracking data is unknown. </constant> diff --git a/drivers/gles3/effects/post_effects.cpp b/drivers/gles3/effects/post_effects.cpp index 8ad872f319..105c8f6b71 100644 --- a/drivers/gles3/effects/post_effects.cpp +++ b/drivers/gles3/effects/post_effects.cpp @@ -87,7 +87,7 @@ void PostEffects::_draw_screen_triangle() { glBindVertexArray(0); } -void PostEffects::post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuint p_source_color, Size2i p_source_size, float p_luminance_multiplier, const Glow::GLOWLEVEL *p_glow_buffers, float p_glow_intensity, uint32_t p_view, bool p_use_multiview) { +void PostEffects::post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuint p_source_color, Size2i p_source_size, float p_luminance_multiplier, const Glow::GLOWLEVEL *p_glow_buffers, float p_glow_intensity, uint32_t p_view, bool p_use_multiview, uint64_t p_spec_constants) { glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); glDisable(GL_BLEND); @@ -96,7 +96,7 @@ void PostEffects::post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuin glViewport(0, 0, p_dest_size.x, p_dest_size.y); PostShaderGLES3::ShaderVariant mode = PostShaderGLES3::MODE_DEFAULT; - uint64_t flags = 0; + uint64_t flags = p_spec_constants; if (p_use_multiview) { flags |= PostShaderGLES3::USE_MULTIVIEW; } diff --git a/drivers/gles3/effects/post_effects.h b/drivers/gles3/effects/post_effects.h index b90c77d6c7..916d29a052 100644 --- a/drivers/gles3/effects/post_effects.h +++ b/drivers/gles3/effects/post_effects.h @@ -59,7 +59,7 @@ public: PostEffects(); ~PostEffects(); - void post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuint p_source_color, Size2i p_source_size, float p_luminance_multiplier, const Glow::GLOWLEVEL *p_glow_buffers, float p_glow_intensity, uint32_t p_view = 0, bool p_use_multiview = false); + void post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuint p_source_color, Size2i p_source_size, float p_luminance_multiplier, const Glow::GLOWLEVEL *p_glow_buffers, float p_glow_intensity, uint32_t p_view = 0, bool p_use_multiview = false, uint64_t p_spec_constants = 0); }; } //namespace GLES3 diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index bc1af86938..606bcee775 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -788,7 +788,6 @@ void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection, } if (!p_apply_color_adjustments_in_post) { spec_constants |= SkyShaderGLES3::APPLY_TONEMAPPING; - // TODO add BCS and color corrections once supported. } RS::EnvironmentBG background = environment_get_background(p_env); @@ -2336,9 +2335,18 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_ SceneState::TonemapUBO tonemap_ubo; if (render_data.environment.is_valid()) { + bool use_bcs = environment_get_adjustments_enabled(render_data.environment); + if (use_bcs) { + apply_color_adjustments_in_post = true; + } + tonemap_ubo.exposure = environment_get_exposure(render_data.environment); tonemap_ubo.white = environment_get_white(render_data.environment); tonemap_ubo.tonemapper = int32_t(environment_get_tone_mapper(render_data.environment)); + + tonemap_ubo.brightness = environment_get_adjustments_brightness(render_data.environment); + tonemap_ubo.contrast = environment_get_adjustments_contrast(render_data.environment); + tonemap_ubo.saturation = environment_get_adjustments_saturation(render_data.environment); } if (scene_state.tonemap_buffer == 0) { @@ -2558,8 +2566,6 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_ if (!apply_color_adjustments_in_post) { spec_constant_base_flags |= SceneShaderGLES3::APPLY_TONEMAPPING; - - // TODO add BCS and Color corrections here once supported. } } // Render Opaque Objects. @@ -2700,6 +2706,29 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend rb->check_glow_buffers(); } + bool use_bcs = environment_get_adjustments_enabled(p_render_data->environment); + uint64_t bcs_spec_constants = 0; + RID color_correction_texture = environment_get_color_correction(p_render_data->environment); + if (use_bcs && color_correction_texture.is_valid()) { + bcs_spec_constants |= PostShaderGLES3::USE_BCS; + bcs_spec_constants |= PostShaderGLES3::USE_COLOR_CORRECTION; + + bool use_1d_lut = environment_get_use_1d_color_correction(p_render_data->environment); + GLenum texture_target = GL_TEXTURE_3D; + if (use_1d_lut) { + bcs_spec_constants |= PostShaderGLES3::USE_1D_LUT; + texture_target = GL_TEXTURE_2D; + } + + glActiveTexture(GL_TEXTURE2); + glBindTexture(texture_target, texture_storage->texture_get_texid(color_correction_texture)); + glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + } + if (view_count == 1) { // Resolve if needed. if (fbo_msaa_3d != 0 && msaa3d_needs_resolve) { @@ -2735,7 +2764,7 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend } // Copy color buffer - post_effects->post_copy(fbo_rt, target_size, color, internal_size, p_render_data->luminance_multiplier, glow_buffers, glow_intensity); + post_effects->post_copy(fbo_rt, target_size, color, internal_size, p_render_data->luminance_multiplier, glow_buffers, glow_intensity, 0, false, bcs_spec_constants); // Copy depth buffer glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_int); @@ -2803,7 +2832,7 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend glBindFramebuffer(GL_FRAMEBUFFER, fbos[2]); glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, write_color, 0, v); - post_effects->post_copy(fbos[2], target_size, source_color, internal_size, p_render_data->luminance_multiplier, glow_buffers, glow_intensity, v, true); + post_effects->post_copy(fbos[2], target_size, source_color, internal_size, p_render_data->luminance_multiplier, glow_buffers, glow_intensity, v, true, bcs_spec_constants); } // Copy depth @@ -2824,6 +2853,9 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend glBindFramebuffer(GL_FRAMEBUFFER, fbo_rt); glDeleteFramebuffers(3, fbos); } + + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, 0); } template <PassMode p_pass_mode> diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index cc479bd4e9..c656ee3cc7 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -434,6 +434,11 @@ private: float white = 1.0; int32_t tonemapper = 0; int32_t pad = 0; + + int32_t pad2 = 0; + float brightness = 1.0; + float contrast = 1.0; + float saturation = 1.0; }; static_assert(sizeof(TonemapUBO) % 16 == 0, "Tonemap UBO size must be a multiple of 16 bytes"); diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl index efddbe9ad2..65332c06be 100644 --- a/drivers/gles3/shaders/canvas.glsl +++ b/drivers/gles3/shaders/canvas.glsl @@ -776,6 +776,12 @@ void main() { vec2 tex_uv = (vec4(vertex, 0.0, 1.0) * mat4(light_array[light_base].texture_matrix[0], light_array[light_base].texture_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations. vec2 tex_uv_atlas = tex_uv * light_array[light_base].atlas_rect.zw + light_array[light_base].atlas_rect.xy; + + if (any(lessThan(tex_uv, vec2(0.0, 0.0))) || any(greaterThanEqual(tex_uv, vec2(1.0, 1.0)))) { + //if outside the light texture, light color is zero + continue; + } + vec4 light_color = textureLod(atlas_texture, tex_uv_atlas, 0.0); vec4 light_base_color = light_array[light_base].color; @@ -800,10 +806,6 @@ void main() { light_color.rgb *= base_color.rgb; } #endif - if (any(lessThan(tex_uv, vec2(0.0, 0.0))) || any(greaterThanEqual(tex_uv, vec2(1.0, 1.0)))) { - //if outside the light texture, light color is zero - light_color.a = 0.0; - } if (bool(light_array[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) { vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array[light_base].shadow_matrix[0], light_array[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations. diff --git a/drivers/gles3/shaders/effects/post.glsl b/drivers/gles3/shaders/effects/post.glsl index e61171c92a..1d17510c52 100644 --- a/drivers/gles3/shaders/effects/post.glsl +++ b/drivers/gles3/shaders/effects/post.glsl @@ -1,13 +1,15 @@ /* clang-format off */ #[modes] -mode_default = #define MODE_DEFAULT -// mode_glow = #define MODE_GLOW +mode_default = #[specializations] USE_MULTIVIEW = false USE_GLOW = false USE_LUMINANCE_MULTIPLIER = false +USE_BCS = false +USE_COLOR_CORRECTION = false +USE_1D_LUT = false #[vertex] layout(location = 0) in vec2 vertex_attrib; @@ -25,6 +27,9 @@ void main() { #[fragment] /* clang-format on */ +// If we reach this code, we always tonemap. +#define APPLY_TONEMAPPING + #include "../tonemap_inc.glsl" #ifdef USE_MULTIVIEW @@ -57,6 +62,35 @@ vec4 get_glow_color(vec2 uv) { } #endif // USE_GLOW +#ifdef USE_COLOR_CORRECTION +#ifdef USE_1D_LUT +uniform sampler2D source_color_correction; //texunit:2 + +vec3 apply_color_correction(vec3 color) { + color.r = texture(source_color_correction, vec2(color.r, 0.0f)).r; + color.g = texture(source_color_correction, vec2(color.g, 0.0f)).g; + color.b = texture(source_color_correction, vec2(color.b, 0.0f)).b; + return color; +} +#else +uniform sampler3D source_color_correction; //texunit:2 + +vec3 apply_color_correction(vec3 color) { + return textureLod(source_color_correction, color, 0.0).rgb; +} +#endif // USE_1D_LUT +#endif // USE_COLOR_CORRECTION + +#ifdef USE_BCS +vec3 apply_bcs(vec3 color) { + color = mix(vec3(0.0), color, brightness); + color = mix(vec3(0.5), color, contrast); + color = mix(vec3(dot(vec3(1.0), color) * 0.33333), color, saturation); + + return color; +} +#endif + in vec2 uv_interp; layout(location = 0) out vec4 frag_color; @@ -85,11 +119,11 @@ void main() { color.rgb = linear_to_srgb(color.rgb); #ifdef USE_BCS - color.rgb = apply_bcs(color.rgb, bcs); + color.rgb = apply_bcs(color.rgb); #endif #ifdef USE_COLOR_CORRECTION - color.rgb = apply_color_correction(color.rgb, color_correction); + color.rgb = apply_color_correction(color.rgb); #endif frag_color = color; diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index 36bbca8728..797b9066a9 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -1511,16 +1511,31 @@ void main() { float alpha_antialiasing_edge = 0.0; vec2 alpha_texture_coordinate = vec2(0.0, 0.0); #endif // ALPHA_ANTIALIASING_EDGE_USED + +#ifdef LIGHT_VERTEX_USED + vec3 light_vertex = vertex; +#endif //LIGHT_VERTEX_USED + { #CODE : FRAGMENT } +#ifdef LIGHT_VERTEX_USED + vertex = light_vertex; +#ifdef USE_MULTIVIEW + view = -normalize(vertex - eye_offset); +#else + view = -normalize(vertex); +#endif //USE_MULTIVIEW +#endif //LIGHT_VERTEX_USED + #ifndef USE_SHADOW_TO_OPACITY #if defined(ALPHA_SCISSOR_USED) if (alpha < alpha_scissor_threshold) { discard; } + alpha = 1.0; #else #ifdef MODE_RENDER_DEPTH #ifdef USE_OPAQUE_PREPASS @@ -1844,23 +1859,16 @@ void main() { #endif // !MODE_RENDER_DEPTH #if defined(USE_SHADOW_TO_OPACITY) +#ifndef MODE_RENDER_DEPTH alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0)); #if defined(ALPHA_SCISSOR_USED) if (alpha < alpha_scissor) { discard; } -#else -#ifdef MODE_RENDER_DEPTH -#ifdef USE_OPAQUE_PREPASS - - if (alpha < opaque_prepass_threshold) { - discard; - } -#endif // USE_OPAQUE_PREPASS -#endif // MODE_RENDER_DEPTH #endif // !ALPHA_SCISSOR_USED +#endif // !MODE_RENDER_DEPTH #endif // USE_SHADOW_TO_OPACITY #ifdef MODE_RENDER_DEPTH @@ -1920,13 +1928,6 @@ void main() { #endif frag_color.rgb = linear_to_srgb(frag_color.rgb); -#ifdef USE_BCS - frag_color.rgb = apply_bcs(frag_color.rgb, bcs); -#endif - -#ifdef USE_COLOR_CORRECTION - frag_color.rgb = apply_color_correction(frag_color.rgb, color_correction); -#endif #else // !BASE_PASS frag_color = vec4(0.0, 0.0, 0.0, alpha); #endif // !BASE_PASS @@ -2138,14 +2139,6 @@ void main() { #endif additive_light_color = linear_to_srgb(additive_light_color); -#ifdef USE_BCS - additive_light_color = apply_bcs(additive_light_color, bcs); -#endif - -#ifdef USE_COLOR_CORRECTION - additive_light_color = apply_color_correction(additive_light_color, color_correction); -#endif - frag_color.rgb += additive_light_color; #endif // USE_ADDITIVE_LIGHTING diff --git a/drivers/gles3/shaders/sky.glsl b/drivers/gles3/shaders/sky.glsl index 6c33bf7123..26549901a6 100644 --- a/drivers/gles3/shaders/sky.glsl +++ b/drivers/gles3/shaders/sky.glsl @@ -209,14 +209,6 @@ void main() { #endif color = linear_to_srgb(color); -#ifdef USE_BCS - color = apply_bcs(color, bcs); -#endif - -#ifdef USE_COLOR_CORRECTION - color = apply_color_correction(color, color_correction); -#endif - frag_color.rgb = color * luminance_multiplier; frag_color.a = alpha; diff --git a/drivers/gles3/shaders/tonemap.glsl b/drivers/gles3/shaders/tonemap.glsl deleted file mode 100644 index 0b769e77f2..0000000000 --- a/drivers/gles3/shaders/tonemap.glsl +++ /dev/null @@ -1,333 +0,0 @@ -/* clang-format off */ -[vertex] - -#ifdef USE_GLES_OVER_GL -#define lowp -#define mediump -#define highp -#else -precision highp float; -precision highp int; -#endif - -layout(location = 0) in vec2 vertex_attrib; -/* clang-format on */ -layout(location = 4) in vec2 uv_in; - -out vec2 uv_interp; - -void main() { - gl_Position = vec4(vertex_attrib, 0.0, 1.0); - - uv_interp = uv_in; -} - -/* clang-format off */ -[fragment] - -#ifdef USE_GLES_OVER_GL -#define lowp -#define mediump -#define highp -#else -#if defined(USE_HIGHP_PRECISION) -precision highp float; -precision highp int; -#else -precision mediump float; -precision mediump int; -#endif -#endif - -in vec2 uv_interp; -/* clang-format on */ - -layout(location = 0) out vec4 frag_color; - -#ifdef USE_MULTIVIEW -uniform highp sampler2DArray source; //texunit:0 -#else -uniform highp sampler2D source; //texunit:0 -#endif - -#if defined(USE_GLOW_LEVEL1) || defined(USE_GLOW_LEVEL2) || defined(USE_GLOW_LEVEL3) || defined(USE_GLOW_LEVEL4) || defined(USE_GLOW_LEVEL5) || defined(USE_GLOW_LEVEL6) || defined(USE_GLOW_LEVEL7) -#define USING_GLOW // only use glow when at least one glow level is selected - -#ifdef USE_MULTI_TEXTURE_GLOW -uniform highp sampler2D source_glow1; //texunit:2 -uniform highp sampler2D source_glow2; //texunit:3 -uniform highp sampler2D source_glow3; //texunit:4 -uniform highp sampler2D source_glow4; //texunit:5 -uniform highp sampler2D source_glow5; //texunit:6 -uniform highp sampler2D source_glow6; //texunit:7 -#ifdef USE_GLOW_LEVEL7 -uniform highp sampler2D source_glow7; //texunit:8 -#endif -#else -uniform highp sampler2D source_glow; //texunit:2 -#endif -uniform highp float glow_intensity; -#endif - -#ifdef USE_BCS -uniform vec3 bcs; -#endif - -#ifdef USE_FXAA -uniform vec2 pixel_size; -#endif - -#ifdef USE_COLOR_CORRECTION -uniform sampler2D color_correction; //texunit:1 -#endif - -#ifdef USE_GLOW_FILTER_BICUBIC -// w0, w1, w2, and w3 are the four cubic B-spline basis functions -float w0(float a) { - return (1.0 / 6.0) * (a * (a * (-a + 3.0) - 3.0) + 1.0); -} - -float w1(float a) { - return (1.0 / 6.0) * (a * a * (3.0 * a - 6.0) + 4.0); -} - -float w2(float a) { - return (1.0 / 6.0) * (a * (a * (-3.0 * a + 3.0) + 3.0) + 1.0); -} - -float w3(float a) { - return (1.0 / 6.0) * (a * a * a); -} - -// g0 and g1 are the two amplitude functions -float g0(float a) { - return w0(a) + w1(a); -} - -float g1(float a) { - return w2(a) + w3(a); -} - -// h0 and h1 are the two offset functions -float h0(float a) { - return -1.0 + w1(a) / (w0(a) + w1(a)); -} - -float h1(float a) { - return 1.0 + w3(a) / (w2(a) + w3(a)); -} - -uniform ivec2 glow_texture_size; - -vec4 texture_bicubic(sampler2D tex, vec2 uv, int p_lod) { - float lod = float(p_lod); - vec2 tex_size = vec2(glow_texture_size >> p_lod); - vec2 texel_size = vec2(1.0) / tex_size; - - uv = uv * tex_size + vec2(0.5); - - vec2 iuv = floor(uv); - vec2 fuv = fract(uv); - - float g0x = g0(fuv.x); - float g1x = g1(fuv.x); - float h0x = h0(fuv.x); - float h1x = h1(fuv.x); - float h0y = h0(fuv.y); - float h1y = h1(fuv.y); - - vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - vec2(0.5)) * texel_size; - vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - vec2(0.5)) * texel_size; - vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5)) * texel_size; - vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5)) * texel_size; - - return (g0(fuv.y) * (g0x * textureLod(tex, p0, lod) + g1x * textureLod(tex, p1, lod))) + - (g1(fuv.y) * (g0x * textureLod(tex, p2, lod) + g1x * textureLod(tex, p3, lod))); -} - -#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) texture_bicubic(m_tex, m_uv, m_lod) -#else //!USE_GLOW_FILTER_BICUBIC -#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) textureLod(m_tex, m_uv, float(m_lod)) -#endif //USE_GLOW_FILTER_BICUBIC - -vec3 apply_glow(vec3 color, vec3 glow) { // apply glow using the selected blending mode -#ifdef USE_GLOW_REPLACE - color = glow; -#endif - -#ifdef USE_GLOW_SCREEN - color = max((color + glow) - (color * glow), vec3(0.0)); -#endif - -#ifdef USE_GLOW_SOFTLIGHT - glow = glow * vec3(0.5) + vec3(0.5); - - color.r = (glow.r <= 0.5) ? (color.r - (1.0 - 2.0 * glow.r) * color.r * (1.0 - color.r)) : (((glow.r > 0.5) && (color.r <= 0.25)) ? (color.r + (2.0 * glow.r - 1.0) * (4.0 * color.r * (4.0 * color.r + 1.0) * (color.r - 1.0) + 7.0 * color.r)) : (color.r + (2.0 * glow.r - 1.0) * (sqrt(color.r) - color.r))); - color.g = (glow.g <= 0.5) ? (color.g - (1.0 - 2.0 * glow.g) * color.g * (1.0 - color.g)) : (((glow.g > 0.5) && (color.g <= 0.25)) ? (color.g + (2.0 * glow.g - 1.0) * (4.0 * color.g * (4.0 * color.g + 1.0) * (color.g - 1.0) + 7.0 * color.g)) : (color.g + (2.0 * glow.g - 1.0) * (sqrt(color.g) - color.g))); - color.b = (glow.b <= 0.5) ? (color.b - (1.0 - 2.0 * glow.b) * color.b * (1.0 - color.b)) : (((glow.b > 0.5) && (color.b <= 0.25)) ? (color.b + (2.0 * glow.b - 1.0) * (4.0 * color.b * (4.0 * color.b + 1.0) * (color.b - 1.0) + 7.0 * color.b)) : (color.b + (2.0 * glow.b - 1.0) * (sqrt(color.b) - color.b))); -#endif - -#if !defined(USE_GLOW_SCREEN) && !defined(USE_GLOW_SOFTLIGHT) && !defined(USE_GLOW_REPLACE) // no other selected -> additive - color += glow; -#endif - - return color; -} - -vec3 apply_bcs(vec3 color, vec3 bcs) { - color = mix(vec3(0.0), color, bcs.x); - color = mix(vec3(0.5), color, bcs.y); - color = mix(vec3(dot(vec3(1.0), color) * 0.33333), color, bcs.z); - - return color; -} - -vec3 apply_color_correction(vec3 color, sampler2D correction_tex) { - color.r = texture(correction_tex, vec2(color.r, 0.0)).r; - color.g = texture(correction_tex, vec2(color.g, 0.0)).g; - color.b = texture(correction_tex, vec2(color.b, 0.0)).b; - - return color; -} - -vec3 apply_fxaa(vec3 color, vec2 uv_interp, vec2 pixel_size) { - const float FXAA_REDUCE_MIN = (1.0 / 128.0); - const float FXAA_REDUCE_MUL = (1.0 / 8.0); - const float FXAA_SPAN_MAX = 8.0; - -#ifdef USE_MULTIVIEW - vec3 rgbNW = textureLod(source, vec3(uv_interp + vec2(-1.0, -1.0) * pixel_size, ViewIndex), 0.0).xyz; - vec3 rgbNE = textureLod(source, vec3(uv_interp + vec2(1.0, -1.0) * pixel_size, ViewIndex), 0.0).xyz; - vec3 rgbSW = textureLod(source, vec3(uv_interp + vec2(-1.0, 1.0) * pixel_size, ViewIndex), 0.0).xyz; - vec3 rgbSE = textureLod(source, vec3(uv_interp + vec2(1.0, 1.0) * pixel_size, ViewIndex), 0.0).xyz; -#else - vec3 rgbNW = textureLod(source, uv_interp + vec2(-1.0, -1.0) * pixel_size, 0.0).xyz; - vec3 rgbNE = textureLod(source, uv_interp + vec2(1.0, -1.0) * pixel_size, 0.0).xyz; - vec3 rgbSW = textureLod(source, uv_interp + vec2(-1.0, 1.0) * pixel_size, 0.0).xyz; - vec3 rgbSE = textureLod(source, uv_interp + vec2(1.0, 1.0) * pixel_size, 0.0).xyz; -#endif - vec3 rgbM = color; - vec3 luma = vec3(0.299, 0.587, 0.114); - float lumaNW = dot(rgbNW, luma); - float lumaNE = dot(rgbNE, luma); - float lumaSW = dot(rgbSW, luma); - float lumaSE = dot(rgbSE, luma); - float lumaM = dot(rgbM, luma); - float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); - float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); - - vec2 dir; - dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); - dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); - - float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * - (0.25 * FXAA_REDUCE_MUL), - FXAA_REDUCE_MIN); - - float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce); - dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), - max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), - dir * rcpDirMin)) * - pixel_size; - -#ifdef USE_MULTIVIEW - vec3 rgbA = 0.5 * (textureLod(source, vec3(uv_interp + dir * (1.0 / 3.0 - 0.5), ViewIndex), 0.0).xyz + textureLod(source, vec3(uv_interp + dir * (2.0 / 3.0 - 0.5), ViewIndex), 0.0).xyz); - vec3 rgbB = rgbA * 0.5 + 0.25 * (textureLod(source, vec3(uv_interp + dir * -0.5, ViewIndex), 0.0).xyz + textureLod(source, vec3(uv_interp + dir * 0.5, ViewIndex), 0.0).xyz); -#else - vec3 rgbA = 0.5 * (textureLod(source, uv_interp + dir * (1.0 / 3.0 - 0.5), 0.0).xyz + textureLod(source, uv_interp + dir * (2.0 / 3.0 - 0.5), 0.0).xyz); - vec3 rgbB = rgbA * 0.5 + 0.25 * (textureLod(source, uv_interp + dir * -0.5, 0.0).xyz + textureLod(source, uv_interp + dir * 0.5, 0.0).xyz); -#endif - - float lumaB = dot(rgbB, luma); - if ((lumaB < lumaMin) || (lumaB > lumaMax)) { - return rgbA; - } else { - return rgbB; - } -} - -void main() { -#ifdef USE_MULTIVIEW - vec4 color = textureLod(source, vec3(uv_interp, ViewIndex), 0.0); -#else - vec4 color = textureLod(source, uv_interp, 0.0); -#endif - -#ifdef USE_FXAA - color.rgb = apply_fxaa(color.rgb, uv_interp, pixel_size); -#endif - - // Glow - -#ifdef USING_GLOW - vec3 glow = vec3(0.0); -#ifdef USE_MULTI_TEXTURE_GLOW -#ifdef USE_GLOW_LEVEL1 - glow += GLOW_TEXTURE_SAMPLE(source_glow1, uv_interp, 0).rgb; -#ifdef USE_GLOW_LEVEL2 - glow += GLOW_TEXTURE_SAMPLE(source_glow2, uv_interp, 0).rgb; -#ifdef USE_GLOW_LEVEL3 - glow += GLOW_TEXTURE_SAMPLE(source_glow3, uv_interp, 0).rgb; -#ifdef USE_GLOW_LEVEL4 - glow += GLOW_TEXTURE_SAMPLE(source_glow4, uv_interp, 0).rgb; -#ifdef USE_GLOW_LEVEL5 - glow += GLOW_TEXTURE_SAMPLE(source_glow5, uv_interp, 0).rgb; -#ifdef USE_GLOW_LEVEL6 - glow += GLOW_TEXTURE_SAMPLE(source_glow6, uv_interp, 0).rgb; -#ifdef USE_GLOW_LEVEL7 - glow += GLOW_TEXTURE_SAMPLE(source_glow7, uv_interp, 0).rgb; -#endif -#endif -#endif -#endif -#endif -#endif -#endif - -#else - -#ifdef USE_GLOW_LEVEL1 - glow += GLOW_TEXTURE_SAMPLE(source_glow, uv_interp, 1).rgb; -#endif - -#ifdef USE_GLOW_LEVEL2 - glow += GLOW_TEXTURE_SAMPLE(source_glow, uv_interp, 2).rgb; -#endif - -#ifdef USE_GLOW_LEVEL3 - glow += GLOW_TEXTURE_SAMPLE(source_glow, uv_interp, 3).rgb; -#endif - -#ifdef USE_GLOW_LEVEL4 - glow += GLOW_TEXTURE_SAMPLE(source_glow, uv_interp, 4).rgb; -#endif - -#ifdef USE_GLOW_LEVEL5 - glow += GLOW_TEXTURE_SAMPLE(source_glow, uv_interp, 5).rgb; -#endif - -#ifdef USE_GLOW_LEVEL6 - glow += GLOW_TEXTURE_SAMPLE(source_glow, uv_interp, 6).rgb; -#endif - -#ifdef USE_GLOW_LEVEL7 - glow += GLOW_TEXTURE_SAMPLE(source_glow, uv_interp, 7).rgb; -#endif -#endif //USE_MULTI_TEXTURE_GLOW - - glow *= glow_intensity; - color.rgb = apply_glow(color.rgb, glow); -#endif - - // Additional effects - -#ifdef USE_BCS - color.rgb = apply_bcs(color.rgb, bcs); -#endif - -#ifdef USE_COLOR_CORRECTION - color.rgb = apply_color_correction(color.rgb, color_correction); -#endif - - frag_color = color; -} diff --git a/drivers/gles3/shaders/tonemap_inc.glsl b/drivers/gles3/shaders/tonemap_inc.glsl index f8f12760ec..fb915aeb38 100644 --- a/drivers/gles3/shaders/tonemap_inc.glsl +++ b/drivers/gles3/shaders/tonemap_inc.glsl @@ -1,43 +1,31 @@ -#ifdef USE_BCS -uniform vec3 bcs; -#endif - -#ifdef USE_COLOR_CORRECTION -#ifdef USE_1D_LUT -uniform sampler2D source_color_correction; //texunit:-1 -#else -uniform sampler3D source_color_correction; //texunit:-1 -#endif -#endif - layout(std140) uniform TonemapData { //ubo:0 float exposure; float white; int tonemapper; int pad; -}; -vec3 apply_bcs(vec3 color, vec3 bcs) { - color = mix(vec3(0.0), color, bcs.x); - color = mix(vec3(0.5), color, bcs.y); - color = mix(vec3(dot(vec3(1.0), color) * 0.33333), color, bcs.z); + int pad2; + float brightness; + float contrast; + float saturation; +}; - return color; -} -#ifdef USE_COLOR_CORRECTION -#ifdef USE_1D_LUT -vec3 apply_color_correction(vec3 color) { - color.r = texture(source_color_correction, vec2(color.r, 0.0f)).r; - color.g = texture(source_color_correction, vec2(color.g, 0.0f)).g; - color.b = texture(source_color_correction, vec2(color.b, 0.0f)).b; - return color; +// This expects 0-1 range input. +vec3 linear_to_srgb(vec3 color) { + //color = clamp(color, vec3(0.0), vec3(1.0)); + //const vec3 a = vec3(0.055f); + //return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f))); + // Approximation from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html + return max(vec3(1.055) * pow(color, vec3(0.416666667)) - vec3(0.055), vec3(0.0)); } -#else -vec3 apply_color_correction(vec3 color) { - return textureLod(source_color_correction, color, 0.0).rgb; + +// This expects 0-1 range input, outside that range it behaves poorly. +vec3 srgb_to_linear(vec3 color) { + // Approximation from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html + return color * (color * (color * 0.305306011 + 0.682171111) + 0.012522878); } -#endif -#endif + +#ifdef APPLY_TONEMAPPING vec3 tonemap_filmic(vec3 color, float p_white) { // exposure bias: input scale (color *= bias, white *= bias) to make the brightness consistent with other tonemappers @@ -92,21 +80,6 @@ vec3 tonemap_reinhard(vec3 color, float p_white) { return (p_white * color + color) / (color * p_white + p_white); } -// This expects 0-1 range input. -vec3 linear_to_srgb(vec3 color) { - //color = clamp(color, vec3(0.0), vec3(1.0)); - //const vec3 a = vec3(0.055f); - //return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f))); - // Approximation from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html - return max(vec3(1.055) * pow(color, vec3(0.416666667)) - vec3(0.055), vec3(0.0)); -} - -// This expects 0-1 range input, outside that range it behaves poorly. -vec3 srgb_to_linear(vec3 color) { - // Approximation from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html - return color * (color * (color * 0.305306011 + 0.682171111) + 0.012522878); -} - #define TONEMAPPER_LINEAR 0 #define TONEMAPPER_REINHARD 1 #define TONEMAPPER_FILMIC 2 @@ -125,3 +98,5 @@ vec3 apply_tonemapping(vec3 color, float p_white) { // inputs are LINEAR, always return tonemap_aces(max(vec3(0.0f), color), p_white); } } + +#endif // APPLY_TONEMAPPING diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index 23376b4381..62d22dac4d 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -1274,6 +1274,7 @@ MaterialStorage::MaterialStorage() { actions.renames["CUSTOM2"] = "custom2_attrib"; actions.renames["CUSTOM3"] = "custom3_attrib"; actions.renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB"; + actions.renames["LIGHT_VERTEX"] = "light_vertex"; actions.renames["NODE_POSITION_WORLD"] = "model_matrix[3].xyz"; actions.renames["CAMERA_POSITION_WORLD"] = "scene_data.inv_view_matrix[3].xyz"; @@ -1320,6 +1321,7 @@ MaterialStorage::MaterialStorage() { actions.usage_defines["COLOR"] = "#define COLOR_USED\n"; actions.usage_defines["INSTANCE_CUSTOM"] = "#define ENABLE_INSTANCE_CUSTOM\n"; actions.usage_defines["POSITION"] = "#define OVERRIDE_POSITION\n"; + actions.usage_defines["LIGHT_VERTEX"] = "#define LIGHT_VERTEX_USED\n"; actions.usage_defines["ALPHA_SCISSOR_THRESHOLD"] = "#define ALPHA_SCISSOR_USED\n"; actions.usage_defines["ALPHA_HASH_SCALE"] = "#define ALPHA_HASH_USED\n"; diff --git a/drivers/vulkan/rendering_device_driver_vulkan.cpp b/drivers/vulkan/rendering_device_driver_vulkan.cpp index 1906d168fe..803555cb07 100644 --- a/drivers/vulkan/rendering_device_driver_vulkan.cpp +++ b/drivers/vulkan/rendering_device_driver_vulkan.cpp @@ -1008,7 +1008,7 @@ VkResult RenderingDeviceDriverVulkan::_create_render_pass(VkDevice p_device, con const uint32_t depth_attachment_index = vector_base_index + 3; _convert_subpass_attachments(p_create_info->pSubpasses[i].pInputAttachments, p_create_info->pSubpasses[i].inputAttachmentCount, subpasses_attachments[input_attachments_index]); _convert_subpass_attachments(p_create_info->pSubpasses[i].pColorAttachments, p_create_info->pSubpasses[i].colorAttachmentCount, subpasses_attachments[color_attachments_index]); - _convert_subpass_attachments(p_create_info->pSubpasses[i].pResolveAttachments, p_create_info->pSubpasses[i].colorAttachmentCount, subpasses_attachments[resolve_attachments_index]); + _convert_subpass_attachments(p_create_info->pSubpasses[i].pResolveAttachments, (p_create_info->pSubpasses[i].pResolveAttachments != nullptr) ? p_create_info->pSubpasses[i].colorAttachmentCount : 0, subpasses_attachments[resolve_attachments_index]); _convert_subpass_attachments(p_create_info->pSubpasses[i].pDepthStencilAttachment, (p_create_info->pSubpasses[i].pDepthStencilAttachment != nullptr) ? 1 : 0, subpasses_attachments[depth_attachment_index]); // Ignores sType and pNext from the subpass. diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 779080786a..49896d66d8 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -1613,13 +1613,26 @@ Variant CodeTextEditor::get_edit_state() { return state; } +Variant CodeTextEditor::get_previous_state() { + return previous_state; +} + +void CodeTextEditor::store_previous_state() { + previous_state = get_navigation_state(); +} + void CodeTextEditor::set_edit_state(const Variant &p_state) { Dictionary state = p_state; /* update the row first as it sets the column to 0 */ text_editor->set_caret_line(state["row"]); text_editor->set_caret_column(state["column"]); - text_editor->set_v_scroll(state["scroll_position"]); + if (int(state["scroll_position"]) == -1) { + // Special case for previous state. + text_editor->center_viewport_to_caret(); + } else { + text_editor->set_v_scroll(state["scroll_position"]); + } text_editor->set_h_scroll(state["h_scroll_position"]); if (state.get("selection", false)) { @@ -1648,6 +1661,10 @@ void CodeTextEditor::set_edit_state(const Variant &p_state) { text_editor->set_line_as_bookmarked(bookmarks[i], true); } } + + if (previous_state.is_empty()) { + previous_state = p_state; + } } Variant CodeTextEditor::get_navigation_state() { diff --git a/editor/code_editor.h b/editor/code_editor.h index 64b13b9006..c36eedb580 100644 --- a/editor/code_editor.h +++ b/editor/code_editor.h @@ -174,6 +174,8 @@ class CodeTextEditor : public VBoxContainer { int error_line; int error_column; + Dictionary previous_state; + void _update_text_editor_theme(); void _update_font_ligatures(); void _complete_request(); @@ -254,6 +256,8 @@ public: Variant get_edit_state(); void set_edit_state(const Variant &p_state); Variant get_navigation_state(); + Variant get_previous_state(); + void store_previous_state(); void set_error_count(int p_error_count); void set_warning_count(int p_warning_count); diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 5cc09b7104..63c2ebe3d9 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -287,6 +287,7 @@ void EditorHelp::_class_desc_select(const String &p_select) { if (table->has(link)) { // Found in the current page. if (class_desc->is_ready()) { + emit_signal(SNAME("request_save_history")); class_desc->scroll_to_paragraph((*table)[link]); } else { scroll_to = (*table)[link]; @@ -3077,6 +3078,7 @@ void EditorHelp::_bind_methods() { ClassDB::bind_method("_help_callback", &EditorHelp::_help_callback); ADD_SIGNAL(MethodInfo("go_to_help")); + ADD_SIGNAL(MethodInfo("request_save_history")); } void EditorHelp::init_gdext_pointers() { diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 5b24fb1559..134733fa11 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -456,6 +456,9 @@ void EditorNode::_update_from_settings() { void EditorNode::_gdextensions_reloaded() { // In case the developer is inspecting an object that will be changed by the reload. InspectorDock::get_inspector_singleton()->update_tree(); + + // Regenerate documentation. + EditorHelp::generate_doc(); } void EditorNode::_select_default_main_screen_plugin() { diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index ea364d8a0d..2964fb364b 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -3408,7 +3408,7 @@ void EditorPropertyResource::update_property() { } } - resource_picker->set_edited_resource(res); + resource_picker->set_edited_resource_no_check(res); } void EditorPropertyResource::collapse_all_folding() { diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp index eee589489d..9e4bf2b7d9 100644 --- a/editor/editor_resource_picker.cpp +++ b/editor/editor_resource_picker.cpp @@ -896,7 +896,10 @@ void EditorResourcePicker::set_edited_resource(Ref<Resource> p_resource) { ERR_FAIL_MSG(vformat("Failed to set a resource of the type '%s' because this EditorResourcePicker only accepts '%s' and its derivatives.", class_str, base_type)); } } + set_edited_resource_no_check(p_resource); +} +void EditorResourcePicker::set_edited_resource_no_check(Ref<Resource> p_resource) { edited_resource = p_resource; _update_resource(); } diff --git a/editor/editor_resource_picker.h b/editor/editor_resource_picker.h index 8146c02dff..333d9c9915 100644 --- a/editor/editor_resource_picker.h +++ b/editor/editor_resource_picker.h @@ -127,6 +127,7 @@ public: Vector<String> get_allowed_types() const; void set_edited_resource(Ref<Resource> p_resource); + void set_edited_resource_no_check(Ref<Resource> p_resource); Ref<Resource> get_edited_resource(); void set_toggle_mode(bool p_enable); diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp index 33fdd7418e..aa44189782 100644 --- a/editor/export/editor_export_platform.cpp +++ b/editor/export/editor_export_platform.cpp @@ -510,6 +510,12 @@ HashSet<String> EditorExportPlatform::get_features(const Ref<EditorExportPreset> result.insert("template_release"); } +#ifdef REAL_T_IS_DOUBLE + result.insert("double"); +#else + result.insert("single"); +#endif // REAL_T_IS_DOUBLE + if (!p_preset->get_custom_features().is_empty()) { Vector<String> tmp_custom_list = p_preset->get_custom_features().split(","); diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp index 038e357ce2..c995e590f1 100644 --- a/editor/export/project_export.cpp +++ b/editor/export/project_export.cpp @@ -419,6 +419,12 @@ void ProjectExportDialog::_update_feature_list() { feature_set.insert(E); } +#ifdef REAL_T_IS_DOUBLE + feature_set.insert("double"); +#else + feature_set.insert("single"); +#endif // REAL_T_IS_DOUBLE + custom_feature_display->clear(); String text; bool first = true; diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index b7deb6afa6..3f96d934a8 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -2394,6 +2394,12 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected } } break; + case FILE_SHOW_IN_FILESYSTEM: { + if (!p_selected.is_empty()) { + navigate_to_path(p_selected[0]); + } + } break; + case FILE_DEPENDENCIES: { // Checkout the file dependencies. if (!p_selected.is_empty()) { @@ -3286,8 +3292,33 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, const Vect if (p_paths.size() == 1) { const String &fpath = p_paths[0]; + bool added_separator = false; + + if (favorites_list.has(fpath)) { + TreeItem *favorites_item = tree->get_root()->get_first_child(); + TreeItem *cursor_item = tree->get_selected(); + bool is_item_in_favorites = false; + while (cursor_item != nullptr) { + if (cursor_item == favorites_item) { + is_item_in_favorites = true; + break; + } + + cursor_item = cursor_item->get_parent(); + } + + if (is_item_in_favorites) { + p_popup->add_separator(); + added_separator = true; + p_popup->add_icon_item(get_editor_theme_icon(SNAME("ShowInFileSystem")), TTR("Show in FileSystem"), FILE_SHOW_IN_FILESYSTEM); + } + } + #if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED) - p_popup->add_separator(); + if (!added_separator) { + p_popup->add_separator(); + added_separator = true; + } // Opening the system file manager is not supported on the Android and web editors. const bool is_directory = fpath.ends_with("/"); @@ -4153,6 +4184,7 @@ FileSystemDock::FileSystemDock() { ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &FileSystemDock::_project_settings_changed)); add_resource_tooltip_plugin(memnew(EditorTextureTooltipPlugin)); + add_resource_tooltip_plugin(memnew(EditorAudioStreamTooltipPlugin)); } FileSystemDock::~FileSystemDock() { diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index 058886c91a..7449657c06 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -116,6 +116,7 @@ private: FILE_INSTANTIATE, FILE_ADD_FAVORITE, FILE_REMOVE_FAVORITE, + FILE_SHOW_IN_FILESYSTEM, FILE_DEPENDENCIES, FILE_OWNERS, FILE_MOVE, diff --git a/editor/gui/editor_dir_dialog.cpp b/editor/gui/editor_dir_dialog.cpp index 9d5464210b..08612f419c 100644 --- a/editor/gui/editor_dir_dialog.cpp +++ b/editor/gui/editor_dir_dialog.cpp @@ -117,23 +117,13 @@ void EditorDirDialog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { FileSystemDock::get_singleton()->connect("folder_color_changed", callable_mp(this, &EditorDirDialog::reload).bind("")); - EditorFileSystem::get_singleton()->connect("filesystem_changed", callable_mp(this, &EditorDirDialog::reload).bind("")); reload(); - - if (!tree->is_connected("item_collapsed", callable_mp(this, &EditorDirDialog::_item_collapsed))) { - tree->connect("item_collapsed", callable_mp(this, &EditorDirDialog::_item_collapsed), CONNECT_DEFERRED); - } - - if (!EditorFileSystem::get_singleton()->is_connected("filesystem_changed", callable_mp(this, &EditorDirDialog::reload))) { - EditorFileSystem::get_singleton()->connect("filesystem_changed", callable_mp(this, &EditorDirDialog::reload).bind("")); - } } break; case NOTIFICATION_EXIT_TREE: { - if (EditorFileSystem::get_singleton()->is_connected("filesystem_changed", callable_mp(this, &EditorDirDialog::reload))) { - EditorFileSystem::get_singleton()->disconnect("filesystem_changed", callable_mp(this, &EditorDirDialog::reload)); - } + EditorFileSystem::get_singleton()->disconnect("filesystem_changed", callable_mp(this, &EditorDirDialog::reload)); + FileSystemDock::get_singleton()->disconnect("folder_color_changed", callable_mp(this, &EditorDirDialog::reload)); } break; case NOTIFICATION_VISIBILITY_CHANGED: { @@ -229,6 +219,7 @@ EditorDirDialog::EditorDirDialog() { vb->add_child(tree); tree->set_v_size_flags(Control::SIZE_EXPAND_FILL); tree->connect("item_activated", callable_mp(this, &EditorDirDialog::_item_activated)); + tree->connect("item_collapsed", callable_mp(this, &EditorDirDialog::_item_collapsed), CONNECT_DEFERRED); set_ok_button_text(TTR("Move")); diff --git a/editor/gui/editor_scene_tabs.cpp b/editor/gui/editor_scene_tabs.cpp index 5d1e68f008..94fac59b9b 100644 --- a/editor/gui/editor_scene_tabs.cpp +++ b/editor/gui/editor_scene_tabs.cpp @@ -178,7 +178,7 @@ void EditorSceneTabs::_update_context_menu() { if (tab_id >= 0) { scene_tabs_context_menu->add_separator(); - scene_tabs_context_menu->add_icon_item(get_editor_theme_icon(SNAME("ShowInFileSystem")), TTR("Show in FileSystem"), EditorNode::FILE_SHOW_IN_FILESYSTEM); + scene_tabs_context_menu->add_item(TTR("Show in FileSystem"), EditorNode::FILE_SHOW_IN_FILESYSTEM); _disable_menu_option_if(EditorNode::FILE_SHOW_IN_FILESYSTEM, !ResourceLoader::exists(EditorNode::get_editor_data().get_scene_path(tab_id))); scene_tabs_context_menu->add_item(TTR("Play This Scene"), EditorNode::FILE_RUN_SCENE); _disable_menu_option_if(EditorNode::FILE_RUN_SCENE, no_root_node); diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp index 221061f9f7..361ae2a945 100644 --- a/editor/gui/scene_tree_editor.cpp +++ b/editor/gui/scene_tree_editor.cpp @@ -1013,7 +1013,6 @@ void SceneTreeEditor::set_selected(Node *p_node, bool p_emit_selected) { if (!p_node) { selected = nullptr; } - _update_tree(); selected = p_node; } diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp index 6f4a376496..fa503cc4df 100644 --- a/editor/import_dock.cpp +++ b/editor/import_dock.cpp @@ -200,7 +200,8 @@ void ImportDock::_update_options(const String &p_path, const Ref<ConfigFile> &p_ params->update(); _update_preset_menu(); - if (params->importer.is_valid() && params->paths.size() == 1 && params->importer->has_advanced_options()) { + bool was_imported = p_config.is_valid() && p_config->get_value("remap", "importer") != "skip" && p_config->get_value("remap", "importer") != "keep"; + if (was_imported && params->importer.is_valid() && params->paths.size() == 1 && params->importer->has_advanced_options()) { advanced->show(); advanced_spacer->show(); } else { @@ -508,6 +509,18 @@ static bool _find_owners(EditorFileSystemDirectory *efsd, const String &p_path) return false; } +void ImportDock::_reimport_pressed() { + _reimport_attempt(); + + if (params->importer.is_valid() && params->paths.size() == 1 && params->importer->has_advanced_options()) { + advanced->show(); + advanced_spacer->show(); + } else { + advanced->hide(); + advanced_spacer->hide(); + } +} + void ImportDock::_reimport_attempt() { bool used_in_resources = false; @@ -528,7 +541,7 @@ void ImportDock::_reimport_attempt() { ERR_CONTINUE(err != OK); String imported_with = config->get_value("remap", "importer"); - if (imported_with != importer_name) { + if (imported_with != importer_name && imported_with != "keep" && imported_with != "skip") { Ref<Resource> resource = ResourceLoader::load(params->paths[i]); if (resource.is_valid()) { need_cleanup.push_back(params->paths[i]); @@ -575,7 +588,10 @@ void ImportDock::_reimport_and_cleanup() { for (const String &path : need_cleanup) { Ref<Resource> old_res = old_resources[path]; - Ref<Resource> new_res = ResourceLoader::load(path); + Ref<Resource> new_res; + if (params->importer.is_valid()) { + new_res = ResourceLoader::load(path); + } for (int i = 0; i < EditorNode::get_editor_data().get_edited_scene_count(); i++) { Node *edited_scene_root = EditorNode::get_editor_data().get_edited_scene_root(i); @@ -782,7 +798,7 @@ ImportDock::ImportDock() { import = memnew(Button); import->set_text(TTR("Reimport")); import->set_disabled(true); - import->connect("pressed", callable_mp(this, &ImportDock::_reimport_attempt)); + import->connect("pressed", callable_mp(this, &ImportDock::_reimport_pressed)); if (!DisplayServer::get_singleton()->get_swap_cancel_ok()) { advanced_spacer = hb->add_spacer(); advanced = memnew(Button); diff --git a/editor/import_dock.h b/editor/import_dock.h index 78cd6a556c..c0a1dee7ca 100644 --- a/editor/import_dock.h +++ b/editor/import_dock.h @@ -76,6 +76,7 @@ class ImportDock : public VBoxContainer { void _property_edited(const StringName &p_prop); void _property_toggled(const StringName &p_prop, bool p_checked); void _set_dirty(bool p_dirty); + void _reimport_pressed(); void _reimport_attempt(); void _reimport_and_cleanup(); void _reimport(); diff --git a/editor/plugin_config_dialog.cpp b/editor/plugin_config_dialog.cpp index 6f54a1c57a..184a9233a8 100644 --- a/editor/plugin_config_dialog.cpp +++ b/editor/plugin_config_dialog.cpp @@ -72,6 +72,7 @@ void PluginConfigDialog::_on_confirmed() { String script_path = path.path_join(script_name); Ref<ConfigFile> cf = memnew(ConfigFile); + cf->load(path.path_join("plugin.cfg")); cf->set_value("plugin", "name", name_edit->get_text()); cf->set_value("plugin", "description", desc_edit->get_text()); cf->set_value("plugin", "author", author_edit->get_text()); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 8a9118a03e..5ac5dd6ee6 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -1344,22 +1344,33 @@ bool CanvasItemEditor::_gui_input_pivot(const Ref<InputEvent> &p_event) { // Drag the pivot (in pivot mode / with V key) if (drag_type == DRAG_NONE) { + bool move_temp_pivot = ((b.is_valid() && b->is_shift_pressed()) || (k.is_valid() && k->is_shift_pressed())); + if ((b.is_valid() && b->is_pressed() && b->get_button_index() == MouseButton::LEFT && tool == TOOL_EDIT_PIVOT) || - (k.is_valid() && k->is_pressed() && !k->is_echo() && k->get_keycode() == Key::V && tool == TOOL_SELECT && k->get_modifiers_mask().is_empty())) { + (k.is_valid() && k->is_pressed() && !k->is_echo() && k->get_keycode() == Key::V && tool == TOOL_SELECT && (k->get_modifiers_mask().is_empty() || move_temp_pivot))) { List<CanvasItem *> selection = _get_edited_canvas_items(); // Filters the selection with nodes that allow setting the pivot drag_selection = List<CanvasItem *>(); for (CanvasItem *ci : selection) { - if (ci->_edit_use_pivot()) { + if (ci->_edit_use_pivot() || move_temp_pivot) { drag_selection.push_back(ci); } } // Start dragging if we still have nodes if (drag_selection.size() > 0) { + Vector2 event_pos = (b.is_valid()) ? b->get_position() : viewport->get_local_mouse_position(); + + if (move_temp_pivot) { + drag_type = DRAG_TEMP_PIVOT; + temp_pivot = transform.affine_inverse().xform(event_pos); + viewport->queue_redraw(); + return true; + } + _save_canvas_item_state(drag_selection); - drag_from = transform.affine_inverse().xform((b.is_valid()) ? b->get_position() : viewport->get_local_mouse_position()); + drag_from = transform.affine_inverse().xform(event_pos); Vector2 new_pos; if (drag_selection.size() == 1) { new_pos = snap_point(drag_from, SNAP_NODE_SIDES | SNAP_NODE_CENTER | SNAP_NODE_ANCHORS | SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL, 0, drag_selection[0]); @@ -1416,6 +1427,20 @@ bool CanvasItemEditor::_gui_input_pivot(const Ref<InputEvent> &p_event) { return true; } } + + if (drag_type == DRAG_TEMP_PIVOT) { + if (m.is_valid()) { + temp_pivot = transform.affine_inverse().xform(m->get_position()); + viewport->queue_redraw(); + return true; + } + + if ((b.is_valid() && !b->is_pressed() && b->get_button_index() == MouseButton::LEFT && tool == TOOL_EDIT_PIVOT) || + (k.is_valid() && !k->is_pressed() && k->get_keycode() == Key::V)) { + drag_type = DRAG_NONE; + return true; + } + } return false; } @@ -1441,7 +1466,9 @@ bool CanvasItemEditor::_gui_input_rotate(const Ref<InputEvent> &p_event) { drag_type = DRAG_ROTATE; drag_from = transform.affine_inverse().xform(b->get_position()); CanvasItem *ci = drag_selection[0]; - if (ci->_edit_use_pivot()) { + if (!Math::is_inf(temp_pivot.x) || !Math::is_inf(temp_pivot.y)) { + drag_rotation_center = temp_pivot; + } else if (ci->_edit_use_pivot()) { drag_rotation_center = ci->get_global_transform_with_canvas().xform(ci->_edit_get_pivot()); } else { drag_rotation_center = ci->get_global_transform_with_canvas().get_origin(); @@ -1461,7 +1488,16 @@ bool CanvasItemEditor::_gui_input_rotate(const Ref<InputEvent> &p_event) { drag_to = transform.affine_inverse().xform(m->get_position()); //Rotate the opposite way if the canvas item's compounded scale has an uneven number of negative elements bool opposite = (ci->get_global_transform().get_scale().sign().dot(ci->get_transform().get_scale().sign()) == 0); - ci->_edit_set_rotation(snap_angle(ci->_edit_get_rotation() + (opposite ? -1 : 1) * (drag_from - drag_rotation_center).angle_to(drag_to - drag_rotation_center), ci->_edit_get_rotation())); + real_t prev_rotation = ci->_edit_get_rotation(); + real_t new_rotation = snap_angle(ci->_edit_get_rotation() + (opposite ? -1 : 1) * (drag_from - drag_rotation_center).angle_to(drag_to - drag_rotation_center), prev_rotation); + + ci->_edit_set_rotation(new_rotation); + if (!Math::is_inf(temp_pivot.x) || !Math::is_inf(temp_pivot.y)) { + Transform2D xform = ci->get_global_transform_with_canvas() * ci->get_transform().affine_inverse(); + Vector2 radius = xform.xform(ci->_edit_get_position()) - temp_pivot; + radius = radius.rotated(new_rotation - prev_rotation); + ci->_edit_set_position(xform.affine_inverse().xform(temp_pivot + radius)); + } viewport->queue_redraw(); } return true; @@ -3161,7 +3197,7 @@ void CanvasItemEditor::_draw_ruler_tool() { } else { if (grid_snap_active) { Ref<Texture2D> position_icon = get_editor_theme_icon(SNAME("EditorPosition")); - viewport->draw_texture(get_editor_theme_icon(SNAME("EditorPosition")), (ruler_tool_origin - view_offset) * zoom - position_icon->get_size() / 2); + viewport->draw_texture(position_icon, (ruler_tool_origin - view_offset) * zoom - position_icon->get_size() / 2); } } } @@ -3583,6 +3619,10 @@ void CanvasItemEditor::_draw_selection() { get_theme_color(SNAME("accent_color"), EditorStringName(Editor)) * Color(1, 1, 1, 0.6), Math::round(2 * EDSCALE)); } + + if (!Math::is_inf(temp_pivot.x) || !Math::is_inf(temp_pivot.y)) { + viewport->draw_texture(pivot_icon, (temp_pivot - view_offset) * zoom - (pivot_icon->get_size() / 2).floor(), get_theme_color(SNAME("accent_color"), SNAME("Editor"))); + } } void CanvasItemEditor::_draw_straight_line(Point2 p_from, Point2 p_to, Color p_color) { @@ -3931,8 +3971,6 @@ void CanvasItemEditor::_notification(int p_what) { } break; case NOTIFICATION_PROCESS: { - int nb_having_pivot = 0; - // Update the viewport if the canvas_item changes List<CanvasItem *> selection = _get_edited_canvas_items(true); for (CanvasItem *ci : selection) { @@ -3972,14 +4010,10 @@ void CanvasItemEditor::_notification(int p_what) { viewport->queue_redraw(); } } - - if (ci->_edit_use_pivot()) { - nb_having_pivot++; - } } - // Activate / Deactivate the pivot tool - pivot_button->set_disabled(nb_having_pivot == 0); + // Activate / Deactivate the pivot tool. + pivot_button->set_disabled(selection.is_empty()); // Update the viewport if bones changes for (KeyValue<BoneKey, BoneList> &E : bone_list) { @@ -4048,6 +4082,11 @@ void CanvasItemEditor::_selection_changed() { _reset_drag(); } selected_from_canvas = false; + + if (temp_pivot != Vector2(INFINITY, INFINITY)) { + temp_pivot = Vector2(INFINITY, INFINITY); + viewport->queue_redraw(); + } } void CanvasItemEditor::edit(CanvasItem *p_canvas_item) { @@ -4202,6 +4241,18 @@ void CanvasItemEditor::_button_tool_select(int p_index) { tool = (Tool)p_index; + if (p_index == TOOL_EDIT_PIVOT && Input::get_singleton()->is_key_pressed(Key::SHIFT)) { + // Special action that places temporary rotation pivot in the middle of the selection. + List<CanvasItem *> selection = _get_edited_canvas_items(); + if (!selection.is_empty()) { + Vector2 center; + for (const CanvasItem *ci : selection) { + center += ci->_edit_get_position(); + } + temp_pivot = center / selection.size(); + } + } + viewport->queue_redraw(); _update_cursor(); } @@ -5279,7 +5330,7 @@ CanvasItemEditor::CanvasItemEditor() { main_menu_hbox->add_child(pivot_button); pivot_button->set_toggle_mode(true); pivot_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select).bind(TOOL_EDIT_PIVOT)); - pivot_button->set_tooltip_text(TTR("Click to change object's rotation pivot.")); + pivot_button->set_tooltip_text(TTR("Click to change object's rotation pivot.") + "\n" + TTR("Shift: Set temporary rotation pivot.") + "\n" + TTR("Click this button while holding Shift to put the rotation pivot in the center of the selected nodes.")); pan_button = memnew(Button); pan_button->set_theme_type_variation("FlatButton"); diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index c4b995b048..f52ad3dc4f 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -174,6 +174,7 @@ private: DRAG_SCALE_BOTH, DRAG_ROTATE, DRAG_PIVOT, + DRAG_TEMP_PIVOT, DRAG_V_GUIDE, DRAG_H_GUIDE, DRAG_DOUBLE_GUIDE, @@ -251,6 +252,7 @@ private: bool key_scale = false; bool pan_pressed = false; + Vector2 temp_pivot = Vector2(INFINITY, INFINITY); bool ruler_tool_active = false; Point2 ruler_tool_origin; diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index 70213b280c..9fb4d86fa2 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -678,6 +678,8 @@ Ref<Texture2D> EditorAudioStreamPreviewPlugin::generate(const Ref<Resource> &p_f } } + p_metadata["length"] = stream->get_length(); + //post_process_preview(img); Ref<Image> image = Image::create_from_data(w, h, false, Image::FORMAT_RGB8, img); diff --git a/editor/plugins/editor_resource_tooltip_plugins.cpp b/editor/plugins/editor_resource_tooltip_plugins.cpp index fab8ee9f59..dfeb59214c 100644 --- a/editor/plugins/editor_resource_tooltip_plugins.cpp +++ b/editor/plugins/editor_resource_tooltip_plugins.cpp @@ -103,6 +103,7 @@ bool EditorTextureTooltipPlugin::handles(const String &p_resource_type) const { Control *EditorTextureTooltipPlugin::make_tooltip_for_path(const String &p_resource_path, const Dictionary &p_metadata, Control *p_base) const { HBoxContainer *hb = memnew(HBoxContainer); VBoxContainer *vb = Object::cast_to<VBoxContainer>(p_base); + DEV_ASSERT(vb); vb->set_alignment(BoxContainer::ALIGNMENT_CENTER); Vector2 dimensions = p_metadata.get("dimensions", Vector2()); @@ -117,3 +118,29 @@ Control *EditorTextureTooltipPlugin::make_tooltip_for_path(const String &p_resou hb->add_child(vb); return hb; } + +// EditorAudioStreamTooltipPlugin + +bool EditorAudioStreamTooltipPlugin::handles(const String &p_resource_type) const { + return ClassDB::is_parent_class(p_resource_type, "AudioStream"); +} + +Control *EditorAudioStreamTooltipPlugin::make_tooltip_for_path(const String &p_resource_path, const Dictionary &p_metadata, Control *p_base) const { + VBoxContainer *vb = Object::cast_to<VBoxContainer>(p_base); + DEV_ASSERT(vb); + + double length = p_metadata.get("length", 0.0); + if (length >= 60.0) { + vb->add_child(memnew(Label(vformat(TTR("Length: %0dm %0ds"), int(length / 60.0), int(fmod(length, 60)))))); + } else if (length >= 1.0) { + vb->add_child(memnew(Label(vformat(TTR("Length: %0.1fs"), length)))); + } else { + vb->add_child(memnew(Label(vformat(TTR("Length: %0.3fs"), length)))); + } + + TextureRect *tr = memnew(TextureRect); + vb->add_child(tr); + request_thumbnail(p_resource_path, tr); + + return vb; +} diff --git a/editor/plugins/editor_resource_tooltip_plugins.h b/editor/plugins/editor_resource_tooltip_plugins.h index e3a27de0bb..43be8fd8e8 100644 --- a/editor/plugins/editor_resource_tooltip_plugins.h +++ b/editor/plugins/editor_resource_tooltip_plugins.h @@ -33,9 +33,8 @@ #include "core/object/gdvirtual.gen.inc" #include "core/object/ref_counted.h" -#include <scene/gui/control.h> +#include "scene/gui/control.h" -class Control; class Texture2D; class TextureRect; class VBoxContainer; @@ -67,4 +66,12 @@ public: virtual Control *make_tooltip_for_path(const String &p_resource_path, const Dictionary &p_metadata, Control *p_base) const override; }; +class EditorAudioStreamTooltipPlugin : public EditorResourceTooltipPlugin { + GDCLASS(EditorAudioStreamTooltipPlugin, EditorResourceTooltipPlugin); + +public: + virtual bool handles(const String &p_resource_type) const override; + virtual Control *make_tooltip_for_path(const String &p_resource_path, const Dictionary &p_metadata, Control *p_base) const override; +}; + #endif // EDITOR_RESOURCE_TOOLTIP_PLUGINS_H 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/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index cc9e887448..c48af90622 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -273,6 +273,7 @@ void ScriptEditorBase::_bind_methods() { ADD_SIGNAL(MethodInfo("request_help", PropertyInfo(Variant::STRING, "topic"))); ADD_SIGNAL(MethodInfo("request_open_script_at_line", PropertyInfo(Variant::OBJECT, "script"), PropertyInfo(Variant::INT, "line"))); ADD_SIGNAL(MethodInfo("request_save_history")); + ADD_SIGNAL(MethodInfo("request_save_previous_state", PropertyInfo(Variant::INT, "line"))); ADD_SIGNAL(MethodInfo("go_to_help", PropertyInfo(Variant::STRING, "what"))); ADD_SIGNAL(MethodInfo("search_in_files_requested", PropertyInfo(Variant::STRING, "text"))); ADD_SIGNAL(MethodInfo("replace_in_files_requested", PropertyInfo(Variant::STRING, "text"))); @@ -639,6 +640,32 @@ void ScriptEditor::_save_history() { _update_history_arrows(); } +void ScriptEditor::_save_previous_state(Dictionary p_state) { + if (lock_history) { + // Done as a result of a deferred call triggered by set_edit_state(). + lock_history = false; + return; + } + + if (history_pos >= 0 && history_pos < history.size() && history[history_pos].control == tab_container->get_current_tab_control()) { + Node *n = tab_container->get_current_tab_control(); + + if (Object::cast_to<ScriptTextEditor>(n)) { + history.write[history_pos].state = p_state; + } + } + + history.resize(history_pos + 1); + ScriptHistory sh; + sh.control = tab_container->get_current_tab_control(); + sh.state = Variant(); + + history.push_back(sh); + history_pos++; + + _update_history_arrows(); +} + void ScriptEditor::_go_to_tab(int p_idx) { ScriptEditorBase *current = _get_current_editor(); if (current) { @@ -668,8 +695,10 @@ void ScriptEditor::_go_to_tab(int p_idx) { sh.control = c; sh.state = Variant(); - history.push_back(sh); - history_pos++; + if (!lock_history && (history.is_empty() || history[history.size() - 1].control != sh.control)) { + history.push_back(sh); + history_pos++; + } tab_container->set_current_tab(p_idx); @@ -2185,8 +2214,11 @@ void ScriptEditor::_update_script_names() { sd.index = i; sedata.set(i, sd); } + + lock_history = true; _go_to_tab(new_prev_tab); _go_to_tab(new_cur_tab); + lock_history = false; _sort_list_on_update = false; } @@ -2474,6 +2506,10 @@ bool ScriptEditor::edit(const Ref<Resource> &p_resource, int p_line, int p_col, if (script_editor_cache->has_section(p_resource->get_path())) { se->set_edit_state(script_editor_cache->get_value(p_resource->get_path(), "state")); + ScriptTextEditor *ste = Object::cast_to<ScriptTextEditor>(se); + if (ste) { + ste->store_previous_state(); + } } _sort_list_on_update = true; @@ -2485,6 +2521,7 @@ bool ScriptEditor::edit(const Ref<Resource> &p_resource, int p_line, int p_col, se->connect("request_open_script_at_line", callable_mp(this, &ScriptEditor::_goto_script_line)); se->connect("go_to_help", callable_mp(this, &ScriptEditor::_help_class_goto)); se->connect("request_save_history", callable_mp(this, &ScriptEditor::_save_history)); + se->connect("request_save_previous_state", callable_mp(this, &ScriptEditor::_save_previous_state)); se->connect("search_in_files_requested", callable_mp(this, &ScriptEditor::_on_find_in_files_requested)); se->connect("replace_in_files_requested", callable_mp(this, &ScriptEditor::_on_replace_in_files_requested)); se->connect("go_to_method", callable_mp(this, &ScriptEditor::script_goto_method)); @@ -3421,6 +3458,7 @@ void ScriptEditor::_help_class_open(const String &p_class) { _go_to_tab(tab_container->get_tab_count() - 1); eh->go_to_class(p_class); eh->connect("go_to_help", callable_mp(this, &ScriptEditor::_help_class_goto)); + eh->connect("request_save_history", callable_mp(this, &ScriptEditor::_save_history)); _add_recent_script(p_class); _sort_list_on_update = true; _update_script_names(); @@ -3549,6 +3587,7 @@ void ScriptEditor::_update_history_pos(int p_new_pos) { ScriptEditorBase *seb = Object::cast_to<ScriptEditorBase>(n); if (seb) { + lock_history = true; seb->set_edit_state(history[history_pos].state); seb->ensure_focus(); @@ -3558,9 +3597,10 @@ void ScriptEditor::_update_history_pos(int p_new_pos) { } } - if (Object::cast_to<EditorHelp>(n)) { - Object::cast_to<EditorHelp>(n)->set_scroll(history[history_pos].state); - Object::cast_to<EditorHelp>(n)->set_focused(); + EditorHelp *eh = Object::cast_to<EditorHelp>(n); + if (eh) { + eh->set_scroll(history[history_pos].state); + eh->set_focused(); } n->set_meta("__editor_pass", ++edit_pass); diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index f87cb0958c..de3ab3fd5a 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -472,12 +472,14 @@ class ScriptEditor : public PanelContainer { void _history_back(); bool waiting_update_names; + bool lock_history = false; void _help_class_open(const String &p_class); void _help_class_goto(const String &p_desc); bool _help_tab_goto(const String &p_name, const String &p_desc); void _update_history_arrows(); void _save_history(); + void _save_previous_state(Dictionary p_state); void _go_to_tab(int p_idx); void _update_history_pos(int p_new_pos); void _update_script_colors(); diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 0a6eacf11d..a642f35d6f 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -412,6 +412,14 @@ Variant ScriptTextEditor::get_navigation_state() { return code_editor->get_navigation_state(); } +Variant ScriptTextEditor::get_previous_state() { + return code_editor->get_previous_state(); +} + +void ScriptTextEditor::store_previous_state() { + return code_editor->store_previous_state(); +} + void ScriptTextEditor::_convert_case(CodeTextEditor::CaseStyle p_case) { code_editor->convert_case(p_case); } @@ -904,6 +912,18 @@ void ScriptTextEditor::_breakpoint_toggled(int p_row) { EditorDebuggerNode::get_singleton()->set_breakpoint(script->get_path(), p_row + 1, code_editor->get_text_editor()->is_line_breakpointed(p_row)); } +void ScriptTextEditor::_on_caret_moved() { + int current_line = code_editor->get_text_editor()->get_caret_line(); + if (ABS(current_line - previous_line) >= 10) { + Dictionary nav_state = get_navigation_state(); + nav_state["row"] = previous_line; + nav_state["scroll_position"] = -1; + emit_signal(SNAME("request_save_previous_state"), nav_state); + store_previous_state(); + } + previous_line = current_line; +} + void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_column) { Node *base = get_tree()->get_edited_scene_root(); if (base) { @@ -1344,12 +1364,12 @@ void ScriptTextEditor::_edit_option(int p_op) { code_editor->get_text_editor()->duplicate_lines(); } break; case EDIT_TOGGLE_FOLD_LINE: { - int previous_line = -1; + int prev_line = -1; for (int caret_idx : tx->get_caret_index_edit_order()) { int line_idx = tx->get_caret_line(caret_idx); - if (line_idx != previous_line) { + if (line_idx != prev_line) { tx->toggle_foldable_line(line_idx); - previous_line = line_idx; + prev_line = line_idx; } } tx->queue_redraw(); @@ -2352,6 +2372,7 @@ ScriptTextEditor::ScriptTextEditor() { code_editor->get_text_editor()->set_draw_breakpoints_gutter(true); code_editor->get_text_editor()->set_draw_executing_lines_gutter(true); code_editor->get_text_editor()->connect("breakpoint_toggled", callable_mp(this, &ScriptTextEditor::_breakpoint_toggled)); + code_editor->get_text_editor()->connect("caret_changed", callable_mp(this, &ScriptTextEditor::_on_caret_moved)); connection_gutter = 1; code_editor->get_text_editor()->add_gutter(connection_gutter); diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h index 2ea73d4c73..de89fe458c 100644 --- a/editor/plugins/script_text_editor.h +++ b/editor/plugins/script_text_editor.h @@ -99,6 +99,7 @@ class ScriptTextEditor : public ScriptEditorBase { Color marked_line_color = Color(1, 1, 1); Color folded_code_region_color = Color(1, 1, 1); + int previous_line = 0; PopupPanel *color_panel = nullptr; ColorPicker *color_picker = nullptr; @@ -164,6 +165,8 @@ protected: void _breakpoint_item_pressed(int p_idx); void _breakpoint_toggled(int p_row); + void _on_caret_moved(); + void _validate_script(); // No longer virtual. void _update_warnings(); void _update_errors(); @@ -260,6 +263,9 @@ public: virtual void validate() override; + Variant get_previous_state(); + void store_previous_state(); + ScriptTextEditor(); ~ScriptTextEditor(); }; diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp index fe02e3096c..8c41858d62 100644 --- a/editor/plugins/tiles/tile_set_editor.cpp +++ b/editor/plugins/tiles/tile_set_editor.cpp @@ -881,7 +881,9 @@ TileSetEditor::TileSetEditor() { sources_add_button->set_flat(false); sources_add_button->set_theme_type_variation("FlatButton"); sources_add_button->get_popup()->add_item(TTR("Atlas")); + sources_add_button->get_popup()->set_item_tooltip(-1, TTR("A palette of tiles made from a texture.")); sources_add_button->get_popup()->add_item(TTR("Scenes Collection")); + sources_add_button->get_popup()->set_item_tooltip(-1, TTR("A collection of scenes that can be instantiated and placed as tiles.")); sources_add_button->get_popup()->connect("id_pressed", callable_mp(this, &TileSetEditor::_source_add_id_pressed)); sources_bottom_actions->add_child(sources_add_button); diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index 70cef0e345..4fe91d1cc5 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -802,18 +802,20 @@ void ProjectManager::_apply_project_tags() { } } - ConfigFile cfg; const String project_godot = project_list->get_selected_projects()[0].path.path_join("project.godot"); - Error err = cfg.load(project_godot); - if (err != OK) { - tag_edit_error->set_text(vformat(TTR("Couldn't load project at '%s' (error %d). It may be missing or corrupted."), project_godot, err)); + ProjectSettings *cfg = memnew(ProjectSettings(project_godot)); + if (!cfg->is_project_loaded()) { + memdelete(cfg); + tag_edit_error->set_text(vformat(TTR("Couldn't load project at '%s'. It may be missing or corrupted."), project_godot)); tag_edit_error->show(); callable_mp((Window *)tag_manage_dialog, &Window::show).call_deferred(); // Make sure the dialog does not disappear. return; } else { tags.sort(); - cfg.set_value("application", "config/tags", tags); - err = cfg.save(project_godot); + cfg->set("application/config/tags", tags); + Error err = cfg->save_custom(project_godot); + memdelete(cfg); + if (err != OK) { tag_edit_error->set_text(vformat(TTR("Couldn't save project at '%s' (error %d)."), project_godot, err)); tag_edit_error->show(); diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index 70e8484a78..c4aeac434b 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -287,6 +287,8 @@ void ProjectSettingsEditor::_add_feature_overrides() { presets.insert("s3tc"); presets.insert("etc2"); presets.insert("editor"); + presets.insert("editor_hint"); + presets.insert("editor_runtime"); presets.insert("template_debug"); presets.insert("template_release"); presets.insert("debug"); diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp index cd043e165e..c12f95753f 100644 --- a/editor/script_create_dialog.cpp +++ b/editor/script_create_dialog.cpp @@ -883,6 +883,7 @@ ScriptCreateDialog::ScriptCreateDialog() { parent_name->connect("text_changed", callable_mp(this, &ScriptCreateDialog::_parent_name_changed)); parent_name->set_h_size_flags(Control::SIZE_EXPAND_FILL); hb->add_child(parent_name); + register_text_enter(parent_name); parent_search_button = memnew(Button); parent_search_button->connect("pressed", callable_mp(this, &ScriptCreateDialog::_browse_class_in_tree)); hb->add_child(parent_search_button); diff --git a/editor/shader_create_dialog.cpp b/editor/shader_create_dialog.cpp index 8d77b14ab0..43fd559393 100644 --- a/editor/shader_create_dialog.cpp +++ b/editor/shader_create_dialog.cpp @@ -243,7 +243,12 @@ void fog() { emit_signal(SNAME("shader_include_created"), shader_inc); } else { - if (!is_built_in) { + if (is_built_in) { + Node *edited_scene = get_tree()->get_edited_scene_root(); + if (likely(edited_scene)) { + shader->set_path(edited_scene->get_scene_file_path() + "::"); + } + } else { String lpath = ProjectSettings::get_singleton()->localize_path(file_path->get_text()); shader->set_path(lpath); diff --git a/editor/shader_globals_editor.cpp b/editor/shader_globals_editor.cpp index 86a78d813e..216ccd71ab 100644 --- a/editor/shader_globals_editor.cpp +++ b/editor/shader_globals_editor.cpp @@ -219,7 +219,7 @@ protected: case RS::GLOBAL_VAR_TYPE_SAMPLER2DARRAY: { pinfo.type = Variant::OBJECT; pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE; - pinfo.hint_string = "Texture2DArray"; + pinfo.hint_string = "Texture2DArray,CompressedTexture2DArray"; } break; case RS::GLOBAL_VAR_TYPE_SAMPLER3D: { pinfo.type = Variant::OBJECT; @@ -229,7 +229,7 @@ protected: case RS::GLOBAL_VAR_TYPE_SAMPLERCUBE: { pinfo.type = Variant::OBJECT; pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE; - pinfo.hint_string = "Cubemap"; + pinfo.hint_string = "Cubemap,CompressedCubemap"; } break; default: { } break; diff --git a/main/main.cpp b/main/main.cpp index 78a539fff7..eee634086e 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1746,6 +1746,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } } + OS::get_singleton()->_in_editor = editor; if (globals->setup(project_path, main_pack, upwards, editor) == OK) { #ifdef TOOLS_ENABLED found_project = true; @@ -2341,10 +2342,13 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph Engine::get_singleton()->set_max_physics_steps_per_frame(GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "physics/common/max_physics_steps_per_frame", PROPERTY_HINT_RANGE, "1,100,1"), 8)); Engine::get_singleton()->set_physics_jitter_fix(GLOBAL_DEF("physics/common/physics_jitter_fix", 0.5)); Engine::get_singleton()->set_max_fps(GLOBAL_DEF(PropertyInfo(Variant::INT, "application/run/max_fps", PROPERTY_HINT_RANGE, "0,1000,1"), 0)); - Engine::get_singleton()->set_audio_output_latency(GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "audio/driver/output_latency", PROPERTY_HINT_RANGE, "1,100,1"), 15)); + + GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "audio/driver/output_latency", PROPERTY_HINT_RANGE, "1,100,1"), 15); // Use a safer default output_latency for web to avoid audio cracking on low-end devices, especially mobile. GLOBAL_DEF_RST("audio/driver/output_latency.web", 50); + Engine::get_singleton()->set_audio_output_latency(GLOBAL_GET("audio/driver/output_latency")); + GLOBAL_DEF("debug/settings/stdout/print_fps", false); GLOBAL_DEF("debug/settings/stdout/print_gpu_profile", false); GLOBAL_DEF("debug/settings/stdout/verbose_stdout", false); diff --git a/modules/fbx/fbx_document.cpp b/modules/fbx/fbx_document.cpp index e92609f42f..5f94a80566 100644 --- a/modules/fbx/fbx_document.cpp +++ b/modules/fbx/fbx_document.cpp @@ -86,6 +86,17 @@ static Quaternion _as_quaternion(const ufbx_quat &p_quat) { return Quaternion(real_t(p_quat.x), real_t(p_quat.y), real_t(p_quat.z), real_t(p_quat.w)); } +static Transform3D _as_transform(const ufbx_transform &p_xform) { + Transform3D result; + result.origin = FBXDocument::_as_vec3(p_xform.translation); + result.basis.set_quaternion_scale(_as_quaternion(p_xform.rotation), FBXDocument::_as_vec3(p_xform.scale)); + return result; +} + +static real_t _relative_error(const Vector3 &p_a, const Vector3 &p_b) { + return p_a.distance_to(p_b) / MAX(p_a.length(), p_b.length()); +} + static Color _material_color(const ufbx_material_map &p_map) { if (p_map.value_components == 1) { float r = float(p_map.value_real); @@ -196,6 +207,16 @@ static uint32_t _decode_vertex_index(const Vector3 &p_vertex) { return uint32_t(p_vertex.x) | uint32_t(p_vertex.y) << 16; } +static ufbx_skin_deformer *_find_skin_deformer(ufbx_skin_cluster *p_cluster) { + for (const ufbx_connection &conn : p_cluster->element.connections_src) { + ufbx_skin_deformer *deformer = ufbx_as_skin_deformer(conn.dst); + if (deformer) { + return deformer; + } + } + return nullptr; +} + struct ThreadPoolFBX { struct Group { ufbx_thread_pool_context ctx = {}; @@ -333,23 +354,67 @@ Error FBXDocument::_parse_nodes(Ref<FBXState> p_state) { } { - node->transform.origin = _as_vec3(fbx_node->local_transform.translation); - node->transform.basis.set_quaternion_scale(_as_quaternion(fbx_node->local_transform.rotation), _as_vec3(fbx_node->local_transform.scale)); - - if (fbx_node->bind_pose) { - ufbx_bone_pose *pose = ufbx_get_bone_pose(fbx_node->bind_pose, fbx_node); - ufbx_transform rest_transform = ufbx_matrix_to_transform(&pose->bone_to_parent); - - Vector3 rest_position = _as_vec3(rest_transform.translation); - Quaternion rest_rotation = _as_quaternion(rest_transform.rotation); - Vector3 rest_scale = _as_vec3(rest_transform.scale); - Transform3D godot_rest_xform; - godot_rest_xform.basis.set_quaternion_scale(rest_rotation, rest_scale); - godot_rest_xform.origin = rest_position; - node->set_additional_data("GODOT_rest_transform", godot_rest_xform); - } else { - node->set_additional_data("GODOT_rest_transform", node->transform); + node->transform = _as_transform(fbx_node->local_transform); + + bool found_rest_xform = false; + bool bad_rest_xform = false; + Transform3D candidate_rest_xform; + + if (fbx_node->parent) { + // Attempt to resolve a rest pose for bones: This uses internal FBX connections to find + // all skin clusters connected to the bone. + for (const ufbx_connection &child_conn : fbx_node->element.connections_src) { + ufbx_skin_cluster *child_cluster = ufbx_as_skin_cluster(child_conn.dst); + if (!child_cluster) + continue; + ufbx_skin_deformer *child_deformer = _find_skin_deformer(child_cluster); + if (!child_deformer) + continue; + + // Found a skin cluster: Now iterate through all the skin clusters of the parent and + // try to find one that used by the same deformer. + for (const ufbx_connection &parent_conn : fbx_node->parent->element.connections_src) { + ufbx_skin_cluster *parent_cluster = ufbx_as_skin_cluster(parent_conn.dst); + if (!parent_cluster) + continue; + ufbx_skin_deformer *parent_deformer = _find_skin_deformer(parent_cluster); + if (parent_deformer != child_deformer) + continue; + + // Success: Found two skin clusters from the same deformer, now we can resolve the + // local bind pose from the difference between the two world-space bind poses. + ufbx_matrix child_to_world = child_cluster->bind_to_world; + ufbx_matrix world_to_parent = ufbx_matrix_invert(&parent_cluster->bind_to_world); + ufbx_matrix child_to_parent = ufbx_matrix_mul(&world_to_parent, &child_to_world); + Transform3D xform = _as_transform(ufbx_matrix_to_transform(&child_to_parent)); + + if (!found_rest_xform) { + // Found the first bind pose for the node, assume that this one is good + found_rest_xform = true; + candidate_rest_xform = xform; + } else if (!bad_rest_xform) { + // Found another: Let's hope it's similar to the previous one, if not warn and + // use the initial pose, which is used by default if rest pose is not found. + real_t error = 0.0f; + error += _relative_error(candidate_rest_xform.origin, xform.origin); + for (int i = 0; i < 3; i++) { + error += _relative_error(candidate_rest_xform.basis.rows[i], xform.basis.rows[i]); + } + const real_t max_error = 0.01f; + if (error >= max_error) { + WARN_PRINT(vformat("FBX: Node '%s' has multiple bind poses, using initial pose as rest pose.", node->get_name())); + bad_rest_xform = true; + } + } + } + } + } + + Transform3D godot_rest_xform = node->transform; + if (found_rest_xform && !bad_rest_xform) { + godot_rest_xform = candidate_rest_xform; } + node->set_additional_data("GODOT_rest_transform", godot_rest_xform); } for (const ufbx_node *child : fbx_node->children) { diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp index 36b157521d..bfe090edb0 100644 --- a/modules/gdscript/gdscript_byte_codegen.cpp +++ b/modules/gdscript/gdscript_byte_codegen.cpp @@ -45,8 +45,8 @@ uint32_t GDScriptByteCodeGenerator::add_parameter(const StringName &p_name, bool } uint32_t GDScriptByteCodeGenerator::add_local(const StringName &p_name, const GDScriptDataType &p_type) { - int stack_pos = locals.size() + RESERVED_STACK; - locals.push_back(StackSlot(p_type.builtin_type)); + int stack_pos = locals.size() + GDScriptFunction::FIXED_ADDRESSES_MAX; + locals.push_back(StackSlot(p_type.builtin_type, p_type.can_contain_object())); add_stack_identifier(p_name, stack_pos); return stack_pos; } @@ -122,7 +122,7 @@ uint32_t GDScriptByteCodeGenerator::add_temporary(const GDScriptDataType &p_type List<int> &pool = temporaries_pool[temp_type]; if (pool.is_empty()) { - StackSlot new_temp(temp_type); + StackSlot new_temp(temp_type, p_type.can_contain_object()); int idx = temporaries.size(); pool.push_back(idx); temporaries.push_back(new_temp); @@ -136,15 +136,14 @@ uint32_t GDScriptByteCodeGenerator::add_temporary(const GDScriptDataType &p_type void GDScriptByteCodeGenerator::pop_temporary() { ERR_FAIL_COND(used_temporaries.is_empty()); int slot_idx = used_temporaries.back()->get(); - const StackSlot &slot = temporaries[slot_idx]; - if (slot.type == Variant::NIL) { + if (temporaries[slot_idx].can_contain_object) { // Avoid keeping in the stack long-lived references to objects, - // which may prevent RefCounted objects from being freed. + // which may prevent `RefCounted` objects from being freed. // However, the cleanup will be performed an the end of the // statement, to allow object references to survive chaining. - temporaries_pending_clear.push_back(slot_idx); + temporaries_pending_clear.insert(slot_idx); } - temporaries_pool[slot.type].push_back(slot_idx); + temporaries_pool[temporaries[slot_idx].type].push_back(slot_idx); used_temporaries.pop_back(); } @@ -187,7 +186,7 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() { append_opcode(GDScriptFunction::OPCODE_END); for (int i = 0; i < temporaries.size(); i++) { - int stack_index = i + max_locals + RESERVED_STACK; + int stack_index = i + max_locals + GDScriptFunction::FIXED_ADDRESSES_MAX; for (int j = 0; j < temporaries[i].bytecode_indices.size(); j++) { opcodes.write[temporaries[i].bytecode_indices[j]] = stack_index | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS); } @@ -398,7 +397,7 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() { if (debug_stack) { function->stack_debug = stack_debug; } - function->_stack_size = RESERVED_STACK + max_locals + temporaries.size(); + function->_stack_size = GDScriptFunction::FIXED_ADDRESSES_MAX + max_locals + temporaries.size(); function->_instruction_args_size = instr_args_max; #ifdef DEBUG_ENABLED @@ -945,6 +944,11 @@ void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Addr } } +void GDScriptByteCodeGenerator::write_assign_null(const Address &p_target) { + append_opcode(GDScriptFunction::OPCODE_ASSIGN_NULL); + append(p_target); +} + void GDScriptByteCodeGenerator::write_assign_true(const Address &p_target) { append_opcode(GDScriptFunction::OPCODE_ASSIGN_TRUE); append(p_target); @@ -1579,9 +1583,8 @@ void GDScriptByteCodeGenerator::write_for(const Address &p_variable, bool p_use_ if (p_use_conversion) { write_assign_with_conversion(p_variable, temp); - const GDScriptDataType &type = p_variable.type; - if (type.kind != GDScriptDataType::BUILTIN || type.builtin_type == Variant::ARRAY || type.builtin_type == Variant::DICTIONARY) { - write_assign_false(temp); // Can contain RefCounted, so clear it. + if (p_variable.type.can_contain_object()) { + clear_address(temp); // Can contain `RefCounted`, so clear it. } } } @@ -1746,23 +1749,56 @@ void GDScriptByteCodeGenerator::end_block() { pop_stack_identifiers(); } -void GDScriptByteCodeGenerator::clean_temporaries() { - List<int>::Element *E = temporaries_pending_clear.front(); - while (E) { - // The temporary may have been re-used as something else than an object - // since it was added to the list. In that case, there's no need to clear it. - int slot_idx = E->get(); - const StackSlot &slot = temporaries[slot_idx]; - if (slot.type == Variant::NIL) { - write_assign_false(Address(Address::TEMPORARY, slot_idx)); +void GDScriptByteCodeGenerator::clear_temporaries() { + for (int slot_idx : temporaries_pending_clear) { + // The temporary may have been re-used as something else since it was added to the list. + // In that case, there's **no** need to clear it. + if (temporaries[slot_idx].can_contain_object) { + clear_address(Address(Address::TEMPORARY, slot_idx)); // Can contain `RefCounted`, so clear it. + } + } + temporaries_pending_clear.clear(); +} + +void GDScriptByteCodeGenerator::clear_address(const Address &p_address) { + // Do not check `is_local_dirty()` here! Always clear the address since the codegen doesn't track the compiler. + // Also, this method is used to initialize local variables of built-in types, since they cannot be `null`. + + if (p_address.type.has_type && p_address.type.kind == GDScriptDataType::BUILTIN) { + switch (p_address.type.builtin_type) { + case Variant::BOOL: + write_assign_false(p_address); + break; + case Variant::ARRAY: + if (p_address.type.has_container_element_type(0)) { + write_construct_typed_array(p_address, p_address.type.get_container_element_type(0), Vector<GDScriptCodeGenerator::Address>()); + } else { + write_construct(p_address, p_address.type.builtin_type, Vector<GDScriptCodeGenerator::Address>()); + } + break; + case Variant::NIL: + case Variant::OBJECT: + write_assign_null(p_address); + break; + default: + write_construct(p_address, p_address.type.builtin_type, Vector<GDScriptCodeGenerator::Address>()); + break; } + } else { + write_assign_null(p_address); + } - List<int>::Element *next = E->next(); - E->erase(); - E = next; + if (p_address.mode == Address::LOCAL_VARIABLE) { + dirty_locals.erase(p_address.address); } } +// Returns `true` if the local has been re-used and not cleaned up with `clear_address()`. +bool GDScriptByteCodeGenerator::is_local_dirty(const Address &p_address) const { + ERR_FAIL_COND_V(p_address.mode != Address::LOCAL_VARIABLE, false); + return dirty_locals.has(p_address.address); +} + GDScriptByteCodeGenerator::~GDScriptByteCodeGenerator() { if (!ended && function != nullptr) { memdelete(function); diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h index f902cb10cc..5a736b2554 100644 --- a/modules/gdscript/gdscript_byte_codegen.h +++ b/modules/gdscript/gdscript_byte_codegen.h @@ -38,15 +38,14 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { struct StackSlot { Variant::Type type = Variant::NIL; + bool can_contain_object = true; Vector<int> bytecode_indices; StackSlot() = default; - StackSlot(Variant::Type p_type) : - type(p_type) {} + StackSlot(Variant::Type p_type, bool p_can_contain_object) : + type(p_type), can_contain_object(p_can_contain_object) {} }; - const static int RESERVED_STACK = 3; // For self, class, and nil. - struct CallTarget { Address target; bool is_new_temporary = false; @@ -85,9 +84,11 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { RBMap<StringName, int> local_constants; Vector<StackSlot> locals; + HashSet<int> dirty_locals; + Vector<StackSlot> temporaries; List<int> used_temporaries; - List<int> temporaries_pending_clear; + HashSet<int> temporaries_pending_clear; RBMap<Variant::Type, List<int>> temporaries_pool; List<GDScriptFunction::StackDebug> stack_debug; @@ -193,6 +194,9 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { ERR_PRINT("Leaving block with non-zero temporary variables: " + itos(used_temporaries.size())); } #endif + for (int i = current_locals; i < locals.size(); i++) { + dirty_locals.insert(i + GDScriptFunction::FIXED_ADDRESSES_MAX); + } locals.resize(current_locals); if (debug_stack) { for (const KeyValue<StringName, int> &E : block_identifiers) { @@ -455,7 +459,9 @@ public: virtual uint32_t add_or_get_name(const StringName &p_name) override; virtual uint32_t add_temporary(const GDScriptDataType &p_type) override; virtual void pop_temporary() override; - virtual void clean_temporaries() override; + virtual void clear_temporaries() override; + virtual void clear_address(const Address &p_address) override; + virtual bool is_local_dirty(const Address &p_address) const override; virtual void start_parameters() override; virtual void end_parameters() override; @@ -496,6 +502,7 @@ public: virtual void write_get_static_variable(const Address &p_target, const Address &p_class, int p_index) override; virtual void write_assign(const Address &p_target, const Address &p_source) override; virtual void write_assign_with_conversion(const Address &p_target, const Address &p_source) override; + virtual void write_assign_null(const Address &p_target) override; virtual void write_assign_true(const Address &p_target) override; virtual void write_assign_false(const Address &p_target) override; virtual void write_assign_default_parameter(const Address &p_dst, const Address &p_src, bool p_use_conversion) override; diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h index 7ad8f841aa..4c33ed499a 100644 --- a/modules/gdscript/gdscript_codegen.h +++ b/modules/gdscript/gdscript_codegen.h @@ -73,7 +73,9 @@ public: virtual uint32_t add_or_get_name(const StringName &p_name) = 0; virtual uint32_t add_temporary(const GDScriptDataType &p_type) = 0; virtual void pop_temporary() = 0; - virtual void clean_temporaries() = 0; + virtual void clear_temporaries() = 0; + virtual void clear_address(const Address &p_address) = 0; + virtual bool is_local_dirty(const Address &p_address) const = 0; virtual void start_parameters() = 0; virtual void end_parameters() = 0; @@ -114,6 +116,7 @@ public: virtual void write_get_static_variable(const Address &p_target, const Address &p_class, int p_index) = 0; virtual void write_assign(const Address &p_target, const Address &p_source) = 0; virtual void write_assign_with_conversion(const Address &p_target, const Address &p_source) = 0; + virtual void write_assign_null(const Address &p_target) = 0; virtual void write_assign_true(const Address &p_target) = 0; virtual void write_assign_false(const Address &p_target) = 0; virtual void write_assign_default_parameter(const Address &dst, const Address &src, bool p_use_conversion) = 0; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index c526d9c0a4..734e37bc09 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -1826,7 +1826,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c ERR_FAIL_V_MSG(p_previous_test, "Reaching the end of pattern compilation without matching a pattern."); } -List<GDScriptCodeGenerator::Address> GDScriptCompiler::_add_locals_in_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block) { +List<GDScriptCodeGenerator::Address> GDScriptCompiler::_add_block_locals(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block) { List<GDScriptCodeGenerator::Address> addresses; for (int i = 0; i < p_block->locals.size(); i++) { if (p_block->locals[i].type == GDScriptParser::SuiteNode::Local::PARAMETER || p_block->locals[i].type == GDScriptParser::SuiteNode::Local::FOR_VARIABLE) { @@ -1838,27 +1838,25 @@ List<GDScriptCodeGenerator::Address> GDScriptCompiler::_add_locals_in_block(Code return addresses; } -// Avoid keeping in the stack long-lived references to objects, which may prevent RefCounted objects from being freed. -void GDScriptCompiler::_clear_addresses(CodeGen &codegen, const List<GDScriptCodeGenerator::Address> &p_addresses) { - for (const List<GDScriptCodeGenerator::Address>::Element *E = p_addresses.front(); E; E = E->next()) { - GDScriptDataType type = E->get().type; - // If not an object and cannot contain an object, no need to clear. - if (type.kind != GDScriptDataType::BUILTIN || type.builtin_type == Variant::ARRAY || type.builtin_type == Variant::DICTIONARY) { - codegen.generator->write_assign_false(E->get()); +// Avoid keeping in the stack long-lived references to objects, which may prevent `RefCounted` objects from being freed. +void GDScriptCompiler::_clear_block_locals(CodeGen &codegen, const List<GDScriptCodeGenerator::Address> &p_locals) { + for (const GDScriptCodeGenerator::Address &local : p_locals) { + if (local.type.can_contain_object()) { + codegen.generator->clear_address(local); } } } -Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals, bool p_reset_locals) { +Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals, bool p_clear_locals) { Error err = OK; GDScriptCodeGenerator *gen = codegen.generator; List<GDScriptCodeGenerator::Address> block_locals; - gen->clean_temporaries(); + gen->clear_temporaries(); codegen.start_block(); if (p_add_locals) { - block_locals = _add_locals_in_block(codegen, p_block); + block_locals = _add_block_locals(codegen, p_block); } for (int i = 0; i < p_block->statements.size(); i++) { @@ -1873,7 +1871,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui case GDScriptParser::Node::MATCH: { const GDScriptParser::MatchNode *match = static_cast<const GDScriptParser::MatchNode *>(s); - codegen.start_block(); + codegen.start_block(); // Add an extra block, since the binding pattern and @special variables belong to the branch scope. // Evaluate the match expression. GDScriptCodeGenerator::Address value = codegen.add_local("@match_value", _gdtype_from_datatype(match->test->get_datatype(), codegen.script)); @@ -1914,7 +1912,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui codegen.start_block(); // Create an extra block around for binds. // Add locals in block before patterns, so temporaries don't use the stack address for binds. - List<GDScriptCodeGenerator::Address> branch_locals = _add_locals_in_block(codegen, branch->block); + List<GDScriptCodeGenerator::Address> branch_locals = _add_block_locals(codegen, branch->block); #ifdef DEBUG_ENABLED // Add a newline before each branch, since the debugger needs those. @@ -1961,7 +1959,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui return err; } - _clear_addresses(codegen, branch_locals); + _clear_block_locals(codegen, branch_locals); codegen.end_block(); // Get out of extra block. } @@ -2003,7 +2001,8 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui case GDScriptParser::Node::FOR: { const GDScriptParser::ForNode *for_n = static_cast<const GDScriptParser::ForNode *>(s); - codegen.start_block(); + codegen.start_block(); // Add an extra block, since the iterator and @special variables belong to the loop scope. + GDScriptCodeGenerator::Address iterator = codegen.add_local(for_n->variable->name, _gdtype_from_datatype(for_n->variable->get_datatype(), codegen.script)); gen->start_for(iterator.type, _gdtype_from_datatype(for_n->list->get_datatype(), codegen.script)); @@ -2021,14 +2020,21 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui gen->write_for(iterator, for_n->use_conversion_assign); - err = _parse_block(codegen, for_n->loop); + // Loop variables must be cleared even when `break`/`continue` is used. + List<GDScriptCodeGenerator::Address> loop_locals = _add_block_locals(codegen, for_n->loop); + + //_clear_block_locals(codegen, loop_locals); // Inside loop, before block - for `continue`. // TODO + + err = _parse_block(codegen, for_n->loop, false); // Don't add locals again. if (err) { return err; } gen->write_endfor(); - codegen.end_block(); + _clear_block_locals(codegen, loop_locals); // Outside loop, after block - for `break` and normal exit. + + codegen.end_block(); // Get out of extra block. } break; case GDScriptParser::Node::WHILE: { const GDScriptParser::WhileNode *while_n = static_cast<const GDScriptParser::WhileNode *>(s); @@ -2046,12 +2052,19 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui codegen.generator->pop_temporary(); } - err = _parse_block(codegen, while_n->loop); + // Loop variables must be cleared even when `break`/`continue` is used. + List<GDScriptCodeGenerator::Address> loop_locals = _add_block_locals(codegen, while_n->loop); + + //_clear_block_locals(codegen, loop_locals); // Inside loop, before block - for `continue`. // TODO + + err = _parse_block(codegen, while_n->loop, false); // Don't add locals again. if (err) { return err; } gen->write_endwhile(); + + _clear_block_locals(codegen, loop_locals); // Outside loop, after block - for `break` and normal exit. } break; case GDScriptParser::Node::BREAK: { gen->write_break(); @@ -2134,21 +2147,16 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui codegen.generator->pop_temporary(); } initialized = true; - } else if (local_type.has_type) { - // Initialize with default for type. - if (local_type.has_container_element_type(0)) { - codegen.generator->write_construct_typed_array(local, local_type.get_container_element_type(0), Vector<GDScriptCodeGenerator::Address>()); - initialized = true; - } else if (local_type.kind == GDScriptDataType::BUILTIN) { - codegen.generator->write_construct(local, local_type.builtin_type, Vector<GDScriptCodeGenerator::Address>()); - initialized = true; - } - // The `else` branch is for objects, in such case we leave it as `null`. + } else if ((local_type.has_type && local_type.kind == GDScriptDataType::BUILTIN) || codegen.generator->is_local_dirty(local)) { + // Initialize with default for the type. Built-in types must always be cleared (they cannot be `null`). + // Objects and untyped variables are assigned to `null` only if the stack address has been re-used and not cleared. + codegen.generator->clear_address(local); + initialized = true; } - // Assigns a null for the unassigned variables in loops. + // Don't check `is_local_dirty()` since the variable must be assigned to `null` **on each iteration**. if (!initialized && p_block->is_in_loop) { - codegen.generator->write_construct(local, Variant::NIL, Vector<GDScriptCodeGenerator::Address>()); + codegen.generator->clear_address(local); } } break; case GDScriptParser::Node::CONSTANT: { @@ -2180,11 +2188,11 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui } break; } - gen->clean_temporaries(); + gen->clear_temporaries(); } - if (p_add_locals && p_reset_locals) { - _clear_addresses(codegen, block_locals); + if (p_add_locals && p_clear_locals) { + _clear_block_locals(codegen, block_locals); } codegen.end_block(); diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h index 637d61ca3b..45f0f9e19b 100644 --- a/modules/gdscript/gdscript_compiler.h +++ b/modules/gdscript/gdscript_compiler.h @@ -153,9 +153,9 @@ class GDScriptCompiler { GDScriptCodeGenerator::Address _parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root = false, bool p_initializer = false); GDScriptCodeGenerator::Address _parse_match_pattern(CodeGen &codegen, Error &r_error, const GDScriptParser::PatternNode *p_pattern, const GDScriptCodeGenerator::Address &p_value_addr, const GDScriptCodeGenerator::Address &p_type_addr, const GDScriptCodeGenerator::Address &p_previous_test, bool p_is_first, bool p_is_nested); - List<GDScriptCodeGenerator::Address> _add_locals_in_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block); - void _clear_addresses(CodeGen &codegen, const List<GDScriptCodeGenerator::Address> &p_addresses); - Error _parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals = true, bool p_reset_locals = true); + List<GDScriptCodeGenerator::Address> _add_block_locals(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block); + void _clear_block_locals(CodeGen &codegen, const List<GDScriptCodeGenerator::Address> &p_locals); + Error _parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals = true, bool p_clear_locals = true); GDScriptFunction *_parse_function(Error &r_error, GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false, bool p_for_lambda = false); GDScriptFunction *_make_static_initializer(Error &r_error, GDScript *p_script, const GDScriptParser::ClassNode *p_class); Error _parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter); diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp index 26f7cb7537..c7873dcd52 100644 --- a/modules/gdscript/gdscript_disassembler.cpp +++ b/modules/gdscript/gdscript_disassembler.cpp @@ -360,6 +360,13 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const { incr += 3; } break; + case OPCODE_ASSIGN_NULL: { + text += "assign "; + text += DADDR(1); + text += " = null"; + + incr += 2; + } break; case OPCODE_ASSIGN_TRUE: { text += "assign "; text += DADDR(1); diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index 002fc159fa..184d256bcd 100644 --- a/modules/gdscript/gdscript_function.h +++ b/modules/gdscript/gdscript_function.h @@ -78,17 +78,17 @@ public: if (valid && builtin_type == Variant::ARRAY && has_container_element_type(0)) { Array array = p_variant; if (array.is_typed()) { - GDScriptDataType array_container_type = get_container_element_type(0); + const GDScriptDataType &elem_type = container_element_types[0]; Variant::Type array_builtin_type = (Variant::Type)array.get_typed_builtin(); StringName array_native_type = array.get_typed_class_name(); Ref<Script> array_script_type_ref = array.get_typed_script(); if (array_script_type_ref.is_valid()) { - valid = (array_container_type.kind == SCRIPT || array_container_type.kind == GDSCRIPT) && array_container_type.script_type == array_script_type_ref.ptr(); + valid = (elem_type.kind == SCRIPT || elem_type.kind == GDSCRIPT) && elem_type.script_type == array_script_type_ref.ptr(); } else if (array_native_type != StringName()) { - valid = array_container_type.kind == NATIVE && array_container_type.native_type == array_native_type; + valid = elem_type.kind == NATIVE && elem_type.native_type == array_native_type; } else { - valid = array_container_type.kind == BUILTIN && array_container_type.builtin_type == array_builtin_type; + valid = elem_type.kind == BUILTIN && elem_type.builtin_type == array_builtin_type; } } else { valid = false; @@ -147,6 +147,25 @@ public: return false; } + bool can_contain_object() const { + if (has_type && kind == BUILTIN) { + switch (builtin_type) { + case Variant::ARRAY: + if (has_container_element_type(0)) { + return container_element_types[0].can_contain_object(); + } + return true; + case Variant::DICTIONARY: + case Variant::NIL: + case Variant::OBJECT: + return true; + default: + return false; + } + } + return true; + } + void set_container_element_type(int p_index, const GDScriptDataType &p_element_type) { ERR_FAIL_COND(p_index < 0); while (p_index >= container_element_types.size()) { @@ -218,6 +237,7 @@ public: OPCODE_SET_STATIC_VARIABLE, // Only for GDScript. OPCODE_GET_STATIC_VARIABLE, // Only for GDScript. OPCODE_ASSIGN, + OPCODE_ASSIGN_NULL, OPCODE_ASSIGN_TRUE, OPCODE_ASSIGN_FALSE, OPCODE_ASSIGN_TYPED_BUILTIN, diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index 1a8c22cc11..842975698b 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -234,6 +234,7 @@ void (*type_init_function_table[])(Variant *) = { &&OPCODE_SET_STATIC_VARIABLE, \ &&OPCODE_GET_STATIC_VARIABLE, \ &&OPCODE_ASSIGN, \ + &&OPCODE_ASSIGN_NULL, \ &&OPCODE_ASSIGN_TRUE, \ &&OPCODE_ASSIGN_FALSE, \ &&OPCODE_ASSIGN_TYPED_BUILTIN, \ @@ -1256,6 +1257,16 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } DISPATCH_OPCODE; + OPCODE(OPCODE_ASSIGN_NULL) { + CHECK_SPACE(2); + GET_VARIANT_PTR(dst, 0); + + *dst = Variant(); + + ip += 2; + } + DISPATCH_OPCODE; + OPCODE(OPCODE_ASSIGN_TRUE) { CHECK_SPACE(2); GET_VARIANT_PTR(dst, 0); @@ -2649,6 +2660,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GET_VARIANT_PTR(counter, 0); GET_VARIANT_PTR(container, 1); + *counter = Variant(); + bool valid; if (!container->iter_init(*counter, valid)) { #ifdef DEBUG_ENABLED @@ -2976,20 +2989,22 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #else Object *obj = *VariantInternal::get_object(container); #endif + + *counter = Variant(); + Array ref; ref.push_back(*counter); Variant vref; VariantInternal::initialize(&vref, Variant::ARRAY); *VariantInternal::get_array(&vref) = ref; - Variant **args = instruction_args; // Overriding an instruction argument, but we don't need access to that anymore. - args[0] = &vref; + const Variant *args[] = { &vref }; Callable::CallError ce; - Variant has_next = obj->callp(CoreStringNames::get_singleton()->_iter_init, (const Variant **)args, 1, ce); + Variant has_next = obj->callp(CoreStringNames::get_singleton()->_iter_init, args, 1, ce); #ifdef DEBUG_ENABLED - if (ce.error != Callable::CallError::CALL_OK) { + if (ref.size() != 1 || ce.error != Callable::CallError::CALL_OK) { err_text = vformat(R"(There was an error calling "_iter_next" on iterator object of type %s.)", *container); OPCODE_BREAK; } @@ -2999,8 +3014,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size); ip = jumpto; } else { + *counter = ref[0]; + GET_VARIANT_PTR(iterator, 2); - *iterator = obj->callp(CoreStringNames::get_singleton()->_iter_get, (const Variant **)args, 1, ce); + *iterator = obj->callp(CoreStringNames::get_singleton()->_iter_get, (const Variant **)&counter, 1, ce); #ifdef DEBUG_ENABLED if (ce.error != Callable::CallError::CALL_OK) { err_text = vformat(R"(There was an error calling "_iter_get" on iterator object of type %s.)", *container); @@ -3307,20 +3324,20 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #else Object *obj = *VariantInternal::get_object(container); #endif + Array ref; ref.push_back(*counter); Variant vref; VariantInternal::initialize(&vref, Variant::ARRAY); *VariantInternal::get_array(&vref) = ref; - Variant **args = instruction_args; // Overriding an instruction argument, but we don't need access to that anymore. - args[0] = &vref; + const Variant *args[] = { &vref }; Callable::CallError ce; - Variant has_next = obj->callp(CoreStringNames::get_singleton()->_iter_next, (const Variant **)args, 1, ce); + Variant has_next = obj->callp(CoreStringNames::get_singleton()->_iter_next, args, 1, ce); #ifdef DEBUG_ENABLED - if (ce.error != Callable::CallError::CALL_OK) { + if (ref.size() != 1 || ce.error != Callable::CallError::CALL_OK) { err_text = vformat(R"(There was an error calling "_iter_next" on iterator object of type %s.)", *container); OPCODE_BREAK; } @@ -3330,8 +3347,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size); ip = jumpto; } else { + *counter = ref[0]; + GET_VARIANT_PTR(iterator, 2); - *iterator = obj->callp(CoreStringNames::get_singleton()->_iter_get, (const Variant **)args, 1, ce); + *iterator = obj->callp(CoreStringNames::get_singleton()->_iter_get, (const Variant **)&counter, 1, ce); #ifdef DEBUG_ENABLED if (ce.error != Callable::CallError::CALL_OK) { err_text = vformat(R"(There was an error calling "_iter_get" on iterator object of type %s.)", *container); diff --git a/modules/gdscript/tests/scripts/runtime/features/object_iterators.gd b/modules/gdscript/tests/scripts/runtime/features/object_iterators.gd new file mode 100644 index 0000000000..6fe28c6f78 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/object_iterators.gd @@ -0,0 +1,49 @@ +class MyIterator: + var count: int + + func _init(p_count: int) -> void: + count = p_count + + func _iter_init(arg: Array) -> bool: + prints("_iter_init", arg) + arg[0] = 0 + return arg[0] < count + + func _iter_next(arg: Array) -> bool: + prints("_iter_next", arg) + arg[0] += 1 + return arg[0] < count + + func _iter_get(arg: Variant) -> Variant: + prints("_iter_get", arg) + return arg + +func test(): + var container := PackedDataContainer.new() + var _err := container.pack([{ + id = 123, + node_path = ^"/some/path", + data = PackedByteArray(), + }]) + + for ref: PackedDataContainerRef in container: + for key: String in ref: + print(key) + + print("===") + + for ref: Variant in container: + for key: String in ref: + print(key) + + print("===") + + var hard_custom := MyIterator.new(3) + for x in hard_custom: + print(x) + + print("===") + + var weak_custom: Variant = MyIterator.new(3) + for x in weak_custom: + print(x) diff --git a/modules/gdscript/tests/scripts/runtime/features/object_iterators.out b/modules/gdscript/tests/scripts/runtime/features/object_iterators.out new file mode 100644 index 0000000000..942a2c9dd8 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/object_iterators.out @@ -0,0 +1,30 @@ +GDTEST_OK +id +node_path +data +=== +id +node_path +data +=== +_iter_init [<null>] +_iter_get 0 +0 +_iter_next [0] +_iter_get 1 +1 +_iter_next [1] +_iter_get 2 +2 +_iter_next [2] +=== +_iter_init [<null>] +_iter_get 0 +0 +_iter_next [0] +_iter_get 1 +1 +_iter_next [1] +_iter_get 2 +2 +_iter_next [2] diff --git a/modules/gdscript/tests/scripts/runtime/features/reset_uninit_local_vars.gd b/modules/gdscript/tests/scripts/runtime/features/reset_uninit_local_vars.gd new file mode 100644 index 0000000000..ef2d9a1d60 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/reset_uninit_local_vars.gd @@ -0,0 +1,21 @@ +# GH-89958 + +func test(): + if true: + @warning_ignore("unused_variable") + var a = 1 + @warning_ignore("unused_variable") + var b := 1 + @warning_ignore("unused_variable") + var c := 1 + + if true: + var a + @warning_ignore("unassigned_variable") + print(a) + var b + @warning_ignore("unassigned_variable") + print(b) + var c: Object + @warning_ignore("unassigned_variable") + print(c) diff --git a/modules/gdscript/tests/scripts/runtime/features/reset_uninit_local_vars.out b/modules/gdscript/tests/scripts/runtime/features/reset_uninit_local_vars.out new file mode 100644 index 0000000000..279ea2d3b1 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/reset_uninit_local_vars.out @@ -0,0 +1,4 @@ +GDTEST_OK +<null> +<null> +<null> diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp index abc89e4e97..42c3ecc7cb 100644 --- a/modules/gltf/editor/editor_scene_importer_blend.cpp +++ b/modules/gltf/editor/editor_scene_importer_blend.cpp @@ -298,6 +298,7 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_ } return nullptr; } + ERR_FAIL_COND_V(!p_options.has("animation/fps"), nullptr); #ifndef DISABLE_DEPRECATED bool trimming = p_options.has("animation/trimming") ? (bool)p_options["animation/trimming"] : false; diff --git a/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp b/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp index 352b439332..7e52cde059 100644 --- a/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp +++ b/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp @@ -127,6 +127,11 @@ Error GLTFDocumentExtensionPhysics::parse_node_extensions(Ref<GLTFState> p_state trigger_body->set_body_type("trigger"); p_gltf_node->set_additional_data(StringName("GLTFPhysicsBody"), trigger_body); } + // If this node defines explicit member shape nodes, save this information. + if (node_trigger.has("nodes")) { + Array node_trigger_nodes = node_trigger["nodes"]; + p_gltf_node->set_additional_data(StringName("GLTFPhysicsCompoundTriggerNodes"), node_trigger_nodes); + } } if (physics_body_ext.has("motion") || physics_body_ext.has("type")) { p_gltf_node->set_additional_data(StringName("GLTFPhysicsBody"), GLTFPhysicsBody::from_dictionary(physics_body_ext)); @@ -241,6 +246,19 @@ Node3D *_add_physics_node_to_given_node(Node3D *p_current_node, Node3D *p_child, return p_current_node; } +Array _get_ancestor_compound_trigger_nodes(Ref<GLTFState> p_state, TypedArray<GLTFNode> p_state_nodes, CollisionObject3D *p_ancestor_col_obj) { + GLTFNodeIndex ancestor_index = p_state->get_node_index(p_ancestor_col_obj); + ERR_FAIL_INDEX_V(ancestor_index, p_state_nodes.size(), Array()); + Ref<GLTFNode> ancestor_gltf_node = p_state_nodes[ancestor_index]; + Variant compound_trigger_nodes = ancestor_gltf_node->get_additional_data(StringName("GLTFPhysicsCompoundTriggerNodes")); + if (compound_trigger_nodes.is_array()) { + return compound_trigger_nodes; + } + Array ret; + ancestor_gltf_node->set_additional_data(StringName("GLTFPhysicsCompoundTriggerNodes"), ret); + return ret; +} + Node3D *GLTFDocumentExtensionPhysics::generate_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_parent) { Ref<GLTFPhysicsBody> gltf_physics_body = p_gltf_node->get_additional_data(StringName("GLTFPhysicsBody")); #ifndef DISABLE_DEPRECATED @@ -269,12 +287,27 @@ Node3D *GLTFDocumentExtensionPhysics::generate_scene_node(Ref<GLTFState> p_state #endif // DISABLE_DEPRECATED Node3D *ret = nullptr; CollisionObject3D *ancestor_col_obj = nullptr; + Ref<GLTFPhysicsShape> gltf_physics_collider_shape = p_gltf_node->get_additional_data(StringName("GLTFPhysicsColliderShape")); + Ref<GLTFPhysicsShape> gltf_physics_trigger_shape = p_gltf_node->get_additional_data(StringName("GLTFPhysicsTriggerShape")); if (gltf_physics_body.is_valid()) { ancestor_col_obj = gltf_physics_body->to_node(); ret = ancestor_col_obj; } else { ancestor_col_obj = _get_ancestor_collision_object(p_scene_parent); - if (!Object::cast_to<PhysicsBody3D>(ancestor_col_obj)) { + if (Object::cast_to<Area3D>(ancestor_col_obj) && gltf_physics_trigger_shape.is_valid()) { + // At this point, we found an ancestor Area3D node. But do we want to use it for this trigger shape? + TypedArray<GLTFNode> state_nodes = p_state->get_nodes(); + GLTFNodeIndex self_index = state_nodes.find(p_gltf_node); + Array compound_trigger_nodes = _get_ancestor_compound_trigger_nodes(p_state, state_nodes, ancestor_col_obj); + // Check if the ancestor specifies compound trigger nodes, and if this node is in there. + // Remember that JSON does not have integers, only "number", aka double-precision floats. + if (compound_trigger_nodes.size() > 0 && !compound_trigger_nodes.has(double(self_index))) { + // If the compound trigger we found is not the intended user of + // this shape node, then we need to create a new Area3D node. + ancestor_col_obj = memnew(Area3D); + ret = ancestor_col_obj; + } + } else if (!Object::cast_to<PhysicsBody3D>(ancestor_col_obj)) { if (p_gltf_node->get_additional_data(StringName("GLTFPhysicsCompoundCollider"))) { // If the GLTF file wants this node to group solid shapes together, // and there is no parent body, we need to create a static body. @@ -288,8 +321,6 @@ Node3D *GLTFDocumentExtensionPhysics::generate_scene_node(Ref<GLTFState> p_state // set above. If there is no ancestor body, we will either generate an // Area3D or StaticBody3D implicitly, so prefer an Area3D as the base // node for best compatibility with signal connections to this node. - Ref<GLTFPhysicsShape> gltf_physics_collider_shape = p_gltf_node->get_additional_data(StringName("GLTFPhysicsColliderShape")); - Ref<GLTFPhysicsShape> gltf_physics_trigger_shape = p_gltf_node->get_additional_data(StringName("GLTFPhysicsTriggerShape")); bool is_ancestor_col_obj_solid = Object::cast_to<PhysicsBody3D>(ancestor_col_obj); if (is_ancestor_col_obj_solid && gltf_physics_collider_shape.is_valid()) { Node3D *child = _generate_shape_node_and_body_if_needed(p_state, p_gltf_node, gltf_physics_collider_shape, ancestor_col_obj, false); @@ -362,8 +393,14 @@ void GLTFDocumentExtensionPhysics::convert_scene_node(Ref<GLTFState> p_state, Re gltf_shape->set_mesh_index(_get_or_insert_mesh_in_state(p_state, importer_mesh)); } } - if (cast_to<Area3D>(_get_ancestor_collision_object(p_scene_node->get_parent()))) { + CollisionObject3D *ancestor_col_obj = _get_ancestor_collision_object(p_scene_node->get_parent()); + if (cast_to<Area3D>(ancestor_col_obj)) { p_gltf_node->set_additional_data(StringName("GLTFPhysicsTriggerShape"), gltf_shape); + // Write explicit member shape nodes to the ancestor compound trigger node. + TypedArray<GLTFNode> state_nodes = p_state->get_nodes(); + GLTFNodeIndex self_index = state_nodes.size(); // The current p_gltf_node will be inserted next. + Array compound_trigger_nodes = _get_ancestor_compound_trigger_nodes(p_state, p_state->get_nodes(), ancestor_col_obj); + compound_trigger_nodes.push_back(double(self_index)); } else { p_gltf_node->set_additional_data(StringName("GLTFPhysicsColliderShape"), gltf_shape); } @@ -422,6 +459,11 @@ Error GLTFDocumentExtensionPhysics::export_node(Ref<GLTFState> p_state, Ref<GLTF Ref<GLTFPhysicsBody> physics_body = p_gltf_node->get_additional_data(StringName("GLTFPhysicsBody")); if (physics_body.is_valid()) { physics_body_ext = physics_body->to_dictionary(); + Variant compound_trigger_nodes = p_gltf_node->get_additional_data(StringName("GLTFPhysicsCompoundTriggerNodes")); + if (compound_trigger_nodes.is_array()) { + Dictionary trigger_property = physics_body_ext.get_or_add("trigger", {}); + trigger_property["nodes"] = compound_trigger_nodes; + } } Ref<GLTFPhysicsShape> collider_shape = p_gltf_node->get_additional_data(StringName("GLTFPhysicsColliderShape")); if (collider_shape.is_valid()) { diff --git a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp index f8cc3d1d8c..12fa3bed7e 100644 --- a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp +++ b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp @@ -195,7 +195,7 @@ void OpenXRHandTrackingExtension::on_process() { Ref<XRHandTracker> godot_tracker; godot_tracker.instantiate(); - godot_tracker->set_hand(i == 0 ? XRHandTracker::HAND_LEFT : XRHandTracker::HAND_RIGHT); + godot_tracker->set_tracker_hand(i == 0 ? XRPositionalTracker::TRACKER_HAND_LEFT : XRPositionalTracker::TRACKER_HAND_RIGHT); godot_tracker->set_tracker_name(i == 0 ? "/user/hand_tracker/left" : "/user/hand_tracker/right"); XRServer::get_singleton()->add_tracker(godot_tracker); hand_trackers[i].godot_tracker = godot_tracker; diff --git a/modules/webxr/webxr_interface_js.cpp b/modules/webxr/webxr_interface_js.cpp index 535d464d6f..45c1d8ec06 100644 --- a/modules/webxr/webxr_interface_js.cpp +++ b/modules/webxr/webxr_interface_js.cpp @@ -713,7 +713,7 @@ void WebXRInterfaceJS::_update_input_source(int p_input_source_id) { if (unlikely(hand_tracker.is_null())) { hand_tracker.instantiate(); - hand_tracker->set_hand(p_input_source_id == 0 ? XRHandTracker::HAND_LEFT : XRHandTracker::HAND_RIGHT); + hand_tracker->set_tracker_hand(p_input_source_id == 0 ? XRPositionalTracker::TRACKER_HAND_LEFT : XRPositionalTracker::TRACKER_HAND_RIGHT); hand_tracker->set_tracker_name(p_input_source_id == 0 ? "/user/hand_tracker/left" : "/user/hand_tracker/right"); // These flags always apply, since WebXR doesn't give us enough insight to be more fine grained. diff --git a/scene/2d/audio_listener_2d.cpp b/scene/2d/audio_listener_2d.cpp index b4484694a5..cff0654ecc 100644 --- a/scene/2d/audio_listener_2d.cpp +++ b/scene/2d/audio_listener_2d.cpp @@ -45,7 +45,7 @@ bool AudioListener2D::_set(const StringName &p_name, const Variant &p_value) { bool AudioListener2D::_get(const StringName &p_name, Variant &r_ret) const { if (p_name == "current") { - if (is_inside_tree() && get_tree()->is_node_being_edited(this)) { + if (is_part_of_edited_scene()) { r_ret = current; } else { r_ret = is_current(); @@ -63,13 +63,13 @@ void AudioListener2D::_get_property_list(List<PropertyInfo> *p_list) const { void AudioListener2D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - if (!get_tree()->is_node_being_edited(this) && current) { + if (!is_part_of_edited_scene() && current) { make_current(); } } break; case NOTIFICATION_EXIT_TREE: { - if (!get_tree()->is_node_being_edited(this)) { + if (!is_part_of_edited_scene()) { if (is_current()) { clear_current(); current = true; // Keep it true. @@ -98,7 +98,7 @@ void AudioListener2D::clear_current() { } bool AudioListener2D::is_current() const { - if (is_inside_tree() && !get_tree()->is_node_being_edited(this)) { + if (is_inside_tree() && !is_part_of_edited_scene()) { return get_viewport()->get_audio_listener_2d() == this; } else { return current; diff --git a/scene/3d/audio_listener_3d.cpp b/scene/3d/audio_listener_3d.cpp index 437d840d5f..4a18447b3b 100644 --- a/scene/3d/audio_listener_3d.cpp +++ b/scene/3d/audio_listener_3d.cpp @@ -55,7 +55,7 @@ bool AudioListener3D::_set(const StringName &p_name, const Variant &p_value) { bool AudioListener3D::_get(const StringName &p_name, Variant &r_ret) const { if (p_name == "current") { - if (is_inside_tree() && get_tree()->is_node_being_edited(this)) { + if (is_part_of_edited_scene()) { r_ret = current; } else { r_ret = is_current(); @@ -81,7 +81,7 @@ void AudioListener3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_WORLD: { bool first_listener = get_viewport()->_audio_listener_3d_add(this); - if (!get_tree()->is_node_being_edited(this) && (current || first_listener)) { + if (!is_part_of_edited_scene() && (current || first_listener)) { make_current(); } } break; @@ -91,7 +91,7 @@ void AudioListener3D::_notification(int p_what) { } break; case NOTIFICATION_EXIT_WORLD: { - if (!get_tree()->is_node_being_edited(this)) { + if (!is_part_of_edited_scene()) { if (is_current()) { clear_current(); current = true; //keep it true @@ -133,7 +133,7 @@ void AudioListener3D::clear_current() { } bool AudioListener3D::is_current() const { - if (is_inside_tree() && !get_tree()->is_node_being_edited(this)) { + if (is_inside_tree() && !is_part_of_edited_scene()) { return get_viewport()->get_audio_listener_3d() == this; } else { return current; diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index e8bd498e1f..8515aacba7 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -90,7 +90,7 @@ void Camera3D::_update_camera() { RenderingServer::get_singleton()->camera_set_transform(camera, get_camera_transform()); - if (get_tree()->is_node_being_edited(this) || !is_current()) { + if (is_part_of_edited_scene() || !is_current()) { return; } @@ -126,7 +126,7 @@ void Camera3D::_notification(int p_what) { } break; case NOTIFICATION_EXIT_WORLD: { - if (!get_tree()->is_node_being_edited(this)) { + if (!is_part_of_edited_scene()) { if (is_current()) { clear_current(); current = true; //keep it true @@ -286,7 +286,7 @@ void Camera3D::set_current(bool p_enabled) { } bool Camera3D::is_current() const { - if (is_inside_tree() && !get_tree()->is_node_being_edited(this)) { + if (is_inside_tree() && !is_part_of_edited_scene()) { return get_viewport()->get_camera_3d() == this; } else { return current; diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index 3b788b2fd0..98a5134283 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -197,7 +197,7 @@ void Node3D::_notification(int p_what) { } #ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint() && get_tree()->is_node_being_edited(this)) { + if (is_part_of_edited_scene()) { get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, SceneStringNames::get_singleton()->_spatial_editor_group, SNAME("_request_gizmo_for_id"), get_instance_id()); } #endif @@ -582,7 +582,7 @@ void Node3D::set_subgizmo_selection(Ref<Node3DGizmo> p_gizmo, int p_id, Transfor return; } - if (Engine::get_singleton()->is_editor_hint() && get_tree()->is_node_being_edited(this)) { + if (is_part_of_edited_scene()) { get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, SceneStringNames::get_singleton()->_spatial_editor_group, SceneStringNames::get_singleton()->_set_subgizmo_selection, this, p_gizmo, p_id, p_transform); } #endif @@ -599,7 +599,7 @@ void Node3D::clear_subgizmo_selection() { return; } - if (Engine::get_singleton()->is_editor_hint() && get_tree()->is_node_being_edited(this)) { + if (is_part_of_edited_scene()) { get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, SceneStringNames::get_singleton()->_spatial_editor_group, SceneStringNames::get_singleton()->_clear_subgizmo_selection, this); } #endif diff --git a/scene/3d/physics/character_body_3d.cpp b/scene/3d/physics/character_body_3d.cpp index b13c279234..dda3ea9cca 100644 --- a/scene/3d/physics/character_body_3d.cpp +++ b/scene/3d/physics/character_body_3d.cpp @@ -232,7 +232,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo } else { // Travel is too high to be safely canceled, we take it into account. result.travel = result.travel.slide(up_direction); - motion = motion.normalized() * result.travel.length(); + motion = result.remainder; } set_global_transform(gt); // Determines if you are on the ground, and limits the possibility of climbing on the walls because of the approximations. diff --git a/scene/3d/xr_body_modifier_3d.cpp b/scene/3d/xr_body_modifier_3d.cpp index 8aec3e856e..cf73882a7b 100644 --- a/scene/3d/xr_body_modifier_3d.cpp +++ b/scene/3d/xr_body_modifier_3d.cpp @@ -312,7 +312,7 @@ void XRBodyModifier3D::_process_modification() { } } -void XRBodyModifier3D::_tracker_changed(const StringName &p_tracker_name, const Ref<XRBodyTracker> &p_tracker) { +void XRBodyModifier3D::_tracker_changed(const StringName &p_tracker_name, XRServer::TrackerType p_tracker_type) { if (tracker_name == p_tracker_name) { _get_joint_data(); } @@ -327,18 +327,18 @@ void XRBodyModifier3D::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { XRServer *xr_server = XRServer::get_singleton(); if (xr_server) { - xr_server->connect("body_tracker_added", callable_mp(this, &XRBodyModifier3D::_tracker_changed)); - xr_server->connect("body_tracker_updated", callable_mp(this, &XRBodyModifier3D::_tracker_changed)); - xr_server->connect("body_tracker_removed", callable_mp(this, &XRBodyModifier3D::_tracker_changed).bind(Ref<XRBodyTracker>())); + xr_server->connect("tracker_added", callable_mp(this, &XRBodyModifier3D::_tracker_changed)); + xr_server->connect("tracker_updated", callable_mp(this, &XRBodyModifier3D::_tracker_changed)); + xr_server->connect("tracker_removed", callable_mp(this, &XRBodyModifier3D::_tracker_changed)); } _get_joint_data(); } break; case NOTIFICATION_EXIT_TREE: { XRServer *xr_server = XRServer::get_singleton(); if (xr_server) { - xr_server->disconnect("body_tracker_added", callable_mp(this, &XRBodyModifier3D::_tracker_changed)); - xr_server->disconnect("body_tracker_updated", callable_mp(this, &XRBodyModifier3D::_tracker_changed)); - xr_server->disconnect("body_tracker_removed", callable_mp(this, &XRBodyModifier3D::_tracker_changed).bind(Ref<XRBodyTracker>())); + xr_server->disconnect("tracker_added", callable_mp(this, &XRBodyModifier3D::_tracker_changed)); + xr_server->disconnect("tracker_updated", callable_mp(this, &XRBodyModifier3D::_tracker_changed)); + xr_server->disconnect("tracker_removed", callable_mp(this, &XRBodyModifier3D::_tracker_changed)); } for (int i = 0; i < XRBodyTracker::JOINT_MAX; i++) { joints[i].bone = -1; diff --git a/scene/3d/xr_body_modifier_3d.h b/scene/3d/xr_body_modifier_3d.h index 9ff0cd7207..78d70146ee 100644 --- a/scene/3d/xr_body_modifier_3d.h +++ b/scene/3d/xr_body_modifier_3d.h @@ -86,7 +86,7 @@ private: JointData joints[XRBodyTracker::JOINT_MAX]; void _get_joint_data(); - void _tracker_changed(const StringName &p_tracker_name, const Ref<XRBodyTracker> &p_tracker); + void _tracker_changed(const StringName &p_tracker_name, XRServer::TrackerType p_tracker_type); }; VARIANT_BITFIELD_CAST(XRBodyModifier3D::BodyUpdate) diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index c3a586a1ee..97a2917dc1 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -53,7 +53,7 @@ void FileDialog::_focus_file_text() { int lp = file->get_text().rfind("."); if (lp != -1) { file->select(0, lp); - if (file->is_inside_tree() && !get_tree()->is_node_being_edited(file)) { + if (file->is_inside_tree() && !is_part_of_edited_scene()) { file->grab_focus(); } } diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp index 1b960a9b62..d035515b51 100644 --- a/scene/gui/graph_node.cpp +++ b/scene/gui/graph_node.cpp @@ -336,7 +336,8 @@ void GraphNode::_notification(int p_what) { int width = get_size().width - sb_panel->get_minimum_size().x; - if (get_child_count() > 0) { + // Take the HboxContainer child into account. + if (get_child_count(false) > 0) { int slot_index = 0; for (const KeyValue<int, Slot> &E : slot_table) { if (E.key < 0 || E.key >= slot_y_cache.size()) { @@ -647,6 +648,7 @@ void GraphNode::_port_pos_update() { left_port_cache.clear(); right_port_cache.clear(); int vertical_ofs = titlebar_hbox->get_size().height + sb_titlebar->get_minimum_size().height + sb_panel->get_margin(SIDE_TOP); + int slot_index = 0; for (int i = 0; i < get_child_count(false); i++) { Control *child = Object::cast_to<Control>(get_child(i, false)); @@ -656,27 +658,28 @@ void GraphNode::_port_pos_update() { Size2i size = child->get_rect().size; - if (slot_table.has(i)) { - if (slot_table[i].enable_left) { + if (slot_table.has(slot_index)) { + if (slot_table[slot_index].enable_left) { PortCache port_cache; port_cache.pos = Point2i(edgeofs, vertical_ofs + size.height / 2); - port_cache.type = slot_table[i].type_left; - port_cache.color = slot_table[i].color_left; - port_cache.slot_index = child->get_index(false); + port_cache.type = slot_table[slot_index].type_left; + port_cache.color = slot_table[slot_index].color_left; + port_cache.slot_index = slot_index; left_port_cache.push_back(port_cache); } - if (slot_table[i].enable_right) { + if (slot_table[slot_index].enable_right) { PortCache port_cache; port_cache.pos = Point2i(get_size().width - edgeofs, vertical_ofs + size.height / 2); - port_cache.type = slot_table[i].type_right; - port_cache.color = slot_table[i].color_right; - port_cache.slot_index = child->get_index(false); + port_cache.type = slot_table[slot_index].type_right; + port_cache.color = slot_table[slot_index].color_right; + port_cache.slot_index = slot_index; right_port_cache.push_back(port_cache); } } vertical_ofs += separation; vertical_ofs += size.height; + slot_index++; } port_pos_dirty = false; diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 1a94d92855..ddfe202c13 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -771,7 +771,7 @@ void LineEdit::_notification(int p_what) { switch (p_what) { #ifdef TOOLS_ENABLED case NOTIFICATION_ENTER_TREE: { - if (Engine::get_singleton()->is_editor_hint() && !get_tree()->is_node_being_edited(this)) { + if (Engine::get_singleton()->is_editor_hint() && !is_part_of_edited_scene()) { set_caret_blink_enabled(EDITOR_GET("text_editor/appearance/caret/caret_blink")); set_caret_blink_interval(EDITOR_GET("text_editor/appearance/caret/caret_blink_interval")); diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 260956a775..9b991972be 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -276,6 +276,7 @@ Size2 PopupMenu::_get_contents_minimum_size() const { } } + minsize.height = Math::ceil(minsize.height); // Ensures enough height at fractional content scales to prevent the v_scroll_bar from showing. return minsize; } @@ -2832,9 +2833,9 @@ void PopupMenu::popup(const Rect2i &p_bounds) { if (!is_embedded()) { float win_scale = get_parent_visible_window()->get_content_scale_factor(); set_content_scale_factor(win_scale); - Size2 minsize = get_contents_minimum_size(); - minsize.height += 0.5 * win_scale; // Ensures enough height at fractional content scales to prevent the v_scroll_bar from showing. - set_min_size(minsize * win_scale); + Size2 minsize = get_contents_minimum_size() * win_scale; + minsize.height = Math::ceil(minsize.height); // Ensures enough height at fractional content scales to prevent the v_scroll_bar from showing. + set_min_size(minsize); // `height` is truncated here by the cast to Size2i for Window.min_size. set_size(Vector2(0, 0)); // Shrinkwraps to min size. } Popup::popup(p_bounds); diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index 05f44891f6..e2feb59a8c 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -794,6 +794,22 @@ Ref<Texture2D> TabContainer::get_tab_icon(int p_tab) const { return tab_bar->get_tab_icon(p_tab); } +void TabContainer::set_tab_icon_max_width(int p_tab, int p_width) { + if (tab_bar->get_tab_icon_max_width(p_tab) == p_width) { + return; + } + + tab_bar->set_tab_icon_max_width(p_tab, p_width); + + _update_margins(); + _repaint(); + queue_redraw(); +} + +int TabContainer::get_tab_icon_max_width(int p_tab) const { + return tab_bar->get_tab_icon_max_width(p_tab); +} + void TabContainer::set_tab_disabled(int p_tab, bool p_disabled) { if (tab_bar->is_tab_disabled(p_tab) == p_disabled) { return; @@ -990,6 +1006,8 @@ void TabContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tab_tooltip", "tab_idx"), &TabContainer::get_tab_tooltip); ClassDB::bind_method(D_METHOD("set_tab_icon", "tab_idx", "icon"), &TabContainer::set_tab_icon); ClassDB::bind_method(D_METHOD("get_tab_icon", "tab_idx"), &TabContainer::get_tab_icon); + ClassDB::bind_method(D_METHOD("set_tab_icon_max_width", "tab_idx", "width"), &TabContainer::set_tab_icon_max_width); + ClassDB::bind_method(D_METHOD("get_tab_icon_max_width", "tab_idx"), &TabContainer::get_tab_icon_max_width); ClassDB::bind_method(D_METHOD("set_tab_disabled", "tab_idx", "disabled"), &TabContainer::set_tab_disabled); ClassDB::bind_method(D_METHOD("is_tab_disabled", "tab_idx"), &TabContainer::is_tab_disabled); ClassDB::bind_method(D_METHOD("set_tab_hidden", "tab_idx", "hidden"), &TabContainer::set_tab_hidden); diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h index c11d9824e7..e00bc780d4 100644 --- a/scene/gui/tab_container.h +++ b/scene/gui/tab_container.h @@ -161,6 +161,9 @@ public: void set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon); Ref<Texture2D> get_tab_icon(int p_tab) const; + void set_tab_icon_max_width(int p_tab, int p_width); + int get_tab_icon_max_width(int p_tab) const; + void set_tab_disabled(int p_tab, bool p_disabled); bool is_tab_disabled(int p_tab) const; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 0bb77a92f2..39fba72e09 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -752,16 +752,15 @@ void TextEdit::_notification(int p_what) { // Draw the minimap. // Add visual feedback when dragging or hovering the visible area rectangle. - float viewport_alpha; + Color viewport_color = theme_cache.caret_color; if (dragging_minimap) { - viewport_alpha = 0.25; + viewport_color.a = 0.25; } else if (hovering_minimap) { - viewport_alpha = 0.175; + viewport_color.a = 0.175; } else { - viewport_alpha = 0.1; + viewport_color.a = 0.1; } - const Color viewport_color = (theme_cache.background_color.get_v() < 0.5) ? Color(1, 1, 1, viewport_alpha) : Color(0, 0, 0, viewport_alpha); if (rtl) { RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - (xmargin_end + 2) - minimap_width, viewport_offset_y, minimap_width, viewport_height), viewport_color); } else { diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 982473deee..b17d345f1f 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -379,8 +379,8 @@ void TreeItem::set_text_overrun_behavior(int p_column, TextServer::OverrunBehavi cells.write[p_column].text_buf->set_text_overrun_behavior(p_behavior); cells.write[p_column].dirty = true; - _changed_notify(p_column); cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); } TextServer::OverrunBehavior TreeItem::get_text_overrun_behavior(int p_column) const { @@ -1512,7 +1512,9 @@ Size2 TreeItem::get_minimum_size(int p_column) { const TreeItem::Cell &cell = cells[p_column]; if (cell.cached_minimum_size_dirty) { - Size2 size; + Size2 size = Size2( + parent_tree->theme_cache.inner_item_margin_left + parent_tree->theme_cache.inner_item_margin_right, + parent_tree->theme_cache.inner_item_margin_top + parent_tree->theme_cache.inner_item_margin_bottom); // Text. if (!cell.text.is_empty()) { @@ -1520,7 +1522,9 @@ Size2 TreeItem::get_minimum_size(int p_column) { parent_tree->update_item_cell(this, p_column); } Size2 text_size = cell.text_buf->get_size(); - size.width += text_size.width; + if (get_text_overrun_behavior(p_column) == TextServer::OVERRUN_NO_TRIMMING) { + size.width += text_size.width; + } size.height = MAX(size.height, text_size.height); } @@ -1539,13 +1543,10 @@ Size2 TreeItem::get_minimum_size(int p_column) { Ref<Texture2D> texture = cell.buttons[i].texture; if (texture.is_valid()) { Size2 button_size = texture->get_size() + parent_tree->theme_cache.button_pressed->get_minimum_size(); - size.width += button_size.width; + size.width += button_size.width + parent_tree->theme_cache.button_margin; size.height = MAX(size.height, button_size.height); } } - if (cell.buttons.size() >= 2) { - size.width += (cell.buttons.size() - 1) * parent_tree->theme_cache.button_margin; - } cells.write[p_column].cached_minimum_size = size; cells.write[p_column].cached_minimum_size_dirty = false; @@ -4394,17 +4395,23 @@ void Tree::_update_all() { } Size2 Tree::get_minimum_size() const { - if (h_scroll_enabled && v_scroll_enabled) { - return Size2(); - } else { - Vector2 min_size = get_internal_min_size(); - Ref<StyleBox> bg = theme_cache.panel_style; - if (bg.is_valid()) { - min_size.x += bg->get_margin(SIDE_LEFT) + bg->get_margin(SIDE_RIGHT); - min_size.y += bg->get_margin(SIDE_TOP) + bg->get_margin(SIDE_BOTTOM); - } - return Vector2(h_scroll_enabled ? 0 : min_size.x, v_scroll_enabled ? 0 : min_size.y); + Vector2 min_size = Vector2(0, _get_title_button_height()); + + if (theme_cache.panel_style.is_valid()) { + min_size += theme_cache.panel_style->get_minimum_size(); + } + + Vector2 content_min_size = get_internal_min_size(); + if (h_scroll_enabled) { + content_min_size.x = 0; + min_size.y += h_scroll->get_combined_minimum_size().height; + } + if (v_scroll_enabled) { + min_size.x += v_scroll->get_combined_minimum_size().width; + content_min_size.y = 0; } + + return min_size + content_min_size; } TreeItem *Tree::create_item(TreeItem *p_parent, int p_index) { @@ -4582,6 +4589,7 @@ void Tree::set_hide_root(bool p_enabled) { hide_root = p_enabled; queue_redraw(); + update_minimum_size(); } bool Tree::is_root_hidden() const { @@ -4730,31 +4738,33 @@ int Tree::get_column_minimum_width(int p_column) const { min_width = MAX(theme_cache.font->get_string_size(columns[p_column].xl_title, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).width + padding, min_width); } - if (!columns[p_column].clip_content) { - int depth = 0; - TreeItem *next; - for (TreeItem *item = get_root(); item; item = next) { - next = item->get_next_visible(); - // Compute the depth in tree. - if (next && p_column == 0) { - if (next->get_parent() == item) { - depth += 1; - } else { - TreeItem *common_parent = item->get_parent(); - while (common_parent != next->get_parent() && common_parent) { - common_parent = common_parent->get_parent(); - depth -= 1; + if (root && !columns[p_column].clip_content) { + int depth = 1; + + TreeItem *last = nullptr; + TreeItem *first = hide_root ? root->get_next_visible() : root; + for (TreeItem *item = first; item; last = item, item = item->get_next_visible()) { + // Get column indentation. + int indent; + if (p_column == 0) { + if (last) { + if (item->parent == last) { + depth += 1; + } else if (item->parent != last->parent) { + depth = hide_root ? 0 : 1; + for (TreeItem *iter = item->parent; iter; iter = iter->parent) { + depth += 1; + } } } + indent = theme_cache.item_margin * depth; + } else { + indent = theme_cache.h_separation; } // Get the item minimum size. Size2 item_size = item->get_minimum_size(p_column); - if (p_column == 0) { - item_size.width += theme_cache.item_margin * depth; - } else { - item_size.width += theme_cache.h_separation; - } + item_size.width += indent; // Check if the item is wider. min_width = MAX(min_width, item_size.width); @@ -4968,6 +4978,7 @@ void Tree::set_column_titles_visible(bool p_show) { show_column_titles = p_show; queue_redraw(); + update_minimum_size(); } bool Tree::are_column_titles_visible() const { diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 5c5049759f..305b80ffe5 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -3520,6 +3520,7 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("get_node_and_resource", "path"), &Node::_get_node_and_resource); ClassDB::bind_method(D_METHOD("is_inside_tree"), &Node::is_inside_tree); + ClassDB::bind_method(D_METHOD("is_part_of_edited_scene"), &Node::is_part_of_edited_scene); ClassDB::bind_method(D_METHOD("is_ancestor_of", "node"), &Node::is_ancestor_of); ClassDB::bind_method(D_METHOD("is_greater_than", "node"), &Node::is_greater_than); ClassDB::bind_method(D_METHOD("get_path"), &Node::get_path); diff --git a/scene/main/node.h b/scene/main/node.h index f49eeec9cd..fe212ae0f7 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -525,6 +525,8 @@ public: bool is_property_pinned(const StringName &p_property) const; virtual StringName get_property_store_alias(const StringName &p_property) const; bool is_part_of_edited_scene() const; +#else + bool is_part_of_edited_scene() const { return false; } #endif void get_storable_properties(HashSet<StringName> &r_storable_properties) const; diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index c465a3385f..870bed7409 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -729,13 +729,6 @@ void SceneTree::set_quit_on_go_back(bool p_enable) { quit_on_go_back = p_enable; } -#ifdef TOOLS_ENABLED - -bool SceneTree::is_node_being_edited(const Node *p_node) const { - return Engine::get_singleton()->is_editor_hint() && edited_scene_root && (edited_scene_root->is_ancestor_of(p_node) || edited_scene_root == p_node); -} -#endif - #ifdef DEBUG_ENABLED void SceneTree::set_debug_collisions_hint(bool p_enabled) { debug_collisions_hint = p_enabled; diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index c9f3a4de1f..6f0a61ec51 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -330,12 +330,6 @@ public: _FORCE_INLINE_ double get_physics_process_time() const { return physics_process_time; } _FORCE_INLINE_ double get_process_time() const { return process_time; } -#ifdef TOOLS_ENABLED - bool is_node_being_edited(const Node *p_node) const; -#else - bool is_node_being_edited(const Node *p_node) const { return false; } -#endif - void set_pause(bool p_enabled); bool is_paused() const; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 2d30ea345d..73ce166123 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -4954,7 +4954,7 @@ Viewport::Viewport() { unhandled_key_input_group = "_vp_unhandled_key_input" + id; // Window tooltip. - gui.tooltip_delay = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "gui/timers/tooltip_delay_sec", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater"), 0.5); + gui.tooltip_delay = GLOBAL_GET("gui/timers/tooltip_delay_sec"); #ifndef _3D_DISABLED set_scaling_3d_mode((Viewport::Scaling3DMode)(int)GLOBAL_GET("rendering/scaling_3d/mode")); diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index 9f2fad410c..b83be8b6ef 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -803,6 +803,8 @@ const uint32_t SurfaceTool::custom_mask[RS::ARRAY_CUSTOM_COUNT] = { Mesh::ARRAY_ const uint32_t SurfaceTool::custom_shift[RS::ARRAY_CUSTOM_COUNT] = { Mesh::ARRAY_FORMAT_CUSTOM0_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM1_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM2_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM3_SHIFT }; void SurfaceTool::create_vertex_array_from_arrays(const Array &p_arrays, LocalVector<SurfaceTool::Vertex> &ret, uint64_t *r_format) { + ERR_FAIL_INDEX(RS::ARRAY_WEIGHTS, p_arrays.size()); + ret.clear(); Vector<Vector3> varr = p_arrays[RS::ARRAY_VERTEX]; diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp index 209fabeddf..8d865ba440 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp @@ -624,6 +624,7 @@ void SceneShaderForwardClustered::init(const String p_defines) { actions.renames["CUSTOM2"] = "custom2_attrib"; actions.renames["CUSTOM3"] = "custom3_attrib"; actions.renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB"; + actions.renames["LIGHT_VERTEX"] = "light_vertex"; actions.renames["NODE_POSITION_WORLD"] = "read_model_matrix[3].xyz"; actions.renames["CAMERA_POSITION_WORLD"] = "scene_data.inv_view_matrix[3].xyz"; @@ -670,6 +671,7 @@ void SceneShaderForwardClustered::init(const String p_defines) { actions.usage_defines["COLOR"] = "#define COLOR_USED\n"; actions.usage_defines["INSTANCE_CUSTOM"] = "#define ENABLE_INSTANCE_CUSTOM\n"; actions.usage_defines["POSITION"] = "#define OVERRIDE_POSITION\n"; + actions.usage_defines["LIGHT_VERTEX"] = "#define LIGHT_VERTEX_USED\n"; actions.usage_defines["ALPHA_SCISSOR_THRESHOLD"] = "#define ALPHA_SCISSOR_USED\n"; actions.usage_defines["ALPHA_HASH_SCALE"] = "#define ALPHA_HASH_USED\n"; diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp index a2f112669c..0810f567cb 100644 --- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp @@ -528,6 +528,7 @@ void SceneShaderForwardMobile::init(const String p_defines) { actions.renames["CUSTOM2"] = "custom2_attrib"; actions.renames["CUSTOM3"] = "custom3_attrib"; actions.renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB"; + actions.renames["LIGHT_VERTEX"] = "light_vertex"; actions.renames["NODE_POSITION_WORLD"] = "read_model_matrix[3].xyz"; actions.renames["CAMERA_POSITION_WORLD"] = "scene_data.inv_view_matrix[3].xyz"; @@ -574,6 +575,7 @@ void SceneShaderForwardMobile::init(const String p_defines) { actions.usage_defines["COLOR"] = "#define COLOR_USED\n"; actions.usage_defines["INSTANCE_CUSTOM"] = "#define ENABLE_INSTANCE_CUSTOM\n"; actions.usage_defines["POSITION"] = "#define OVERRIDE_POSITION\n"; + actions.usage_defines["LIGHT_VERTEX"] = "#define LIGHT_VERTEX\n"; actions.usage_defines["ALPHA_SCISSOR_THRESHOLD"] = "#define ALPHA_SCISSOR_USED\n"; actions.usage_defines["ALPHA_HASH_SCALE"] = "#define ALPHA_HASH_USED\n"; diff --git a/servers/rendering/renderer_rd/shaders/canvas.glsl b/servers/rendering/renderer_rd/shaders/canvas.glsl index 235c772e2d..dbff09c301 100644 --- a/servers/rendering/renderer_rd/shaders/canvas.glsl +++ b/servers/rendering/renderer_rd/shaders/canvas.glsl @@ -665,6 +665,12 @@ void main() { vec2 tex_uv = (vec4(vertex, 0.0, 1.0) * mat4(light_array.data[light_base].texture_matrix[0], light_array.data[light_base].texture_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations. vec2 tex_uv_atlas = tex_uv * light_array.data[light_base].atlas_rect.zw + light_array.data[light_base].atlas_rect.xy; + + if (any(lessThan(tex_uv, vec2(0.0, 0.0))) || any(greaterThanEqual(tex_uv, vec2(1.0, 1.0)))) { + //if outside the light texture, light color is zero + continue; + } + vec4 light_color = textureLod(sampler2D(atlas_texture, texture_sampler), tex_uv_atlas, 0.0); vec4 light_base_color = light_array.data[light_base].color; @@ -689,10 +695,6 @@ void main() { light_color.rgb *= base_color.rgb; } #endif - if (any(lessThan(tex_uv, vec2(0.0, 0.0))) || any(greaterThanEqual(tex_uv, vec2(1.0, 1.0)))) { - //if outside the light texture, light color is zero - light_color.a = 0.0; - } if (bool(light_array.data[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) { vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array.data[light_base].shadow_matrix[0], light_array.data[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations. diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl index 359d7799e5..cb07579c4b 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl @@ -1022,6 +1022,11 @@ void fragment_shader(in SceneData scene_data) { inv_view_matrix[1][3] = 0.0; inv_view_matrix[2][3] = 0.0; #endif + +#ifdef LIGHT_VERTEX_USED + vec3 light_vertex = vertex; +#endif //LIGHT_VERTEX_USED + mat4 read_view_matrix = scene_data.view_matrix; vec2 read_viewport_size = scene_data.viewport_size; { @@ -1032,6 +1037,15 @@ void fragment_shader(in SceneData scene_data) { transmittance_color.a *= sss_strength; #endif +#ifdef LIGHT_VERTEX_USED + vertex = light_vertex; +#ifdef USE_MULTIVIEW + view = -normalize(vertex - eye_offset); +#else + view = -normalize(vertex); +#endif //USE_MULTIVIEW +#endif //LIGHT_VERTEX_USED + #ifndef USE_SHADOW_TO_OPACITY #ifdef ALPHA_SCISSOR_USED @@ -2043,7 +2057,7 @@ void fragment_shader(in SceneData scene_data) { shadow = 1.0; #endif - float size_A = sc_use_light_soft_shadows ? directional_lights.data[i].size : 0.0; + float size_A = sc_use_directional_soft_shadows ? directional_lights.data[i].size : 0.0; light_compute(normal, directional_lights.data[i].direction, normalize(view), size_A, #ifndef DEBUG_DRAW_PSSM_SPLITS @@ -2221,24 +2235,16 @@ void fragment_shader(in SceneData scene_data) { } #ifdef USE_SHADOW_TO_OPACITY +#ifndef MODE_RENDER_DEPTH alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0)); #if defined(ALPHA_SCISSOR_USED) if (alpha < alpha_scissor) { discard; } -#else -#ifdef MODE_RENDER_DEPTH -#ifdef USE_OPAQUE_PREPASS - - if (alpha < scene_data.opaque_prepass_threshold) { - discard; - } - -#endif // USE_OPAQUE_PREPASS -#endif // MODE_RENDER_DEPTH #endif // ALPHA_SCISSOR_USED +#endif // !MODE_RENDER_DEPTH #endif // USE_SHADOW_TO_OPACITY #endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl index c26313092b..b98ea5a27f 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl @@ -830,6 +830,10 @@ void main() { inv_view_matrix[2][3] = 0.0; #endif +#ifdef LIGHT_VERTEX_USED + vec3 light_vertex = vertex; +#endif //LIGHT_VERTEX_USED + mat4 read_view_matrix = scene_data.view_matrix; vec2 read_viewport_size = scene_data.viewport_size; @@ -837,6 +841,15 @@ void main() { #CODE : FRAGMENT } +#ifdef LIGHT_VERTEX_USED + vertex = light_vertex; +#ifdef USE_MULTIVIEW + view = -normalize(vertex - eye_offset); +#else + view = -normalize(vertex); +#endif //USE_MULTIVIEW +#endif //LIGHT_VERTEX_USED + #ifdef LIGHT_TRANSMITTANCE_USED #ifdef SSS_MODE_SKIN transmittance_color.a = sss_strength; @@ -1743,24 +1756,16 @@ void main() { } //spot lights #ifdef USE_SHADOW_TO_OPACITY +#ifndef MODE_RENDER_DEPTH alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0)); #if defined(ALPHA_SCISSOR_USED) if (alpha < alpha_scissor) { discard; } -#else -#ifdef MODE_RENDER_DEPTH -#ifdef USE_OPAQUE_PREPASS - - if (alpha < scene_data.opaque_prepass_threshold) { - discard; - } - -#endif // USE_OPAQUE_PREPASS -#endif // MODE_RENDER_DEPTH #endif // !ALPHA_SCISSOR_USED +#endif // !MODE_RENDER_DEPTH #endif // USE_SHADOW_TO_OPACITY #endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl index 47e6fe5873..40ca74ae07 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl @@ -375,7 +375,7 @@ float sample_directional_soft_shadow(texture2D shadow, vec3 pssm_coord, vec2 tex for (uint i = 0; i < sc_directional_penumbra_shadow_samples; i++) { vec2 suv = pssm_coord.xy + (disk_rotation * scene_data_block.data.directional_penumbra_shadow_kernel[i].xy) * tex_scale; float d = textureLod(sampler2D(shadow, SAMPLER_LINEAR_CLAMP), suv, 0.0).r; - if (d < pssm_coord.z) { + if (d > pssm_coord.z) { blocker_average += d; blocker_count += 1.0; } @@ -384,7 +384,7 @@ float sample_directional_soft_shadow(texture2D shadow, vec3 pssm_coord, vec2 tex if (blocker_count > 0.0) { //blockers found, do soft shadow blocker_average /= blocker_count; - float penumbra = (pssm_coord.z - blocker_average) / blocker_average; + float penumbra = (-pssm_coord.z + blocker_average) / (1.0 - blocker_average); tex_scale *= penumbra; float s = 0.0; @@ -488,7 +488,7 @@ float light_process_omni_shadow(uint idx, vec3 vertex, vec3 normal) { if (blocker_count > 0.0) { //blockers found, do soft shadow blocker_average /= blocker_count; - float penumbra = (z_norm + blocker_average) / blocker_average; + float penumbra = (-z_norm + blocker_average) / (1.0 - blocker_average); tangent *= penumbra; bitangent *= penumbra; @@ -736,7 +736,7 @@ float light_process_spot_shadow(uint idx, vec3 vertex, vec3 normal) { vec2 suv = shadow_uv + (disk_rotation * scene_data_block.data.penumbra_shadow_kernel[i].xy) * uv_size; suv = clamp(suv, spot_lights.data[idx].atlas_rect.xy, clamp_max); float d = textureLod(sampler2D(shadow_atlas, SAMPLER_LINEAR_CLAMP), suv, 0.0).r; - if (d < splane.z) { + if (d > splane.z) { blocker_average += d; blocker_count += 1.0; } @@ -745,7 +745,7 @@ float light_process_spot_shadow(uint idx, vec3 vertex, vec3 normal) { if (blocker_count > 0.0) { //blockers found, do soft shadow blocker_average /= blocker_count; - float penumbra = (z_norm - blocker_average) / blocker_average; + float penumbra = (-z_norm + blocker_average) / (1.0 - blocker_average); uv_size *= penumbra; shadow = 0.0; diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp index aff874f452..af51083dc3 100644 --- a/servers/rendering/shader_types.cpp +++ b/servers/rendering/shader_types.cpp @@ -111,6 +111,7 @@ ShaderTypes::ShaderTypes() { shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["EYE_OFFSET"] = constt(ShaderLanguage::TYPE_VEC3); shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["VERTEX"] = constt(ShaderLanguage::TYPE_VEC3); + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["LIGHT_VERTEX"] = ShaderLanguage::TYPE_VEC3; shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["FRAGCOORD"] = constt(ShaderLanguage::TYPE_VEC4); shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["FRONT_FACING"] = constt(ShaderLanguage::TYPE_BOOL); shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["NORMAL"] = ShaderLanguage::TYPE_VEC3; diff --git a/servers/rendering/storage/environment_storage.cpp b/servers/rendering/storage/environment_storage.cpp index 7b75a12a19..1bbb5da6bb 100644 --- a/servers/rendering/storage/environment_storage.cpp +++ b/servers/rendering/storage/environment_storage.cpp @@ -773,11 +773,7 @@ RS::EnvironmentSDFGIYScale RendererEnvironmentStorage::environment_get_sdfgi_y_s void RendererEnvironmentStorage::environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, bool p_use_1d_color_correction, RID p_color_correction) { Environment *env = environment_owner.get_or_null(p_env); ERR_FAIL_NULL(env); -#ifdef DEBUG_ENABLED - if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility" && p_enable) { - WARN_PRINT_ONCE_ED("Adjustments are not supported when using the GL Compatibility backend yet. Support will be added in a future release."); - } -#endif + env->adjustments_enabled = p_enable; env->adjustments_brightness = p_brightness; env->adjustments_contrast = p_contrast; diff --git a/servers/xr/xr_hand_tracker.cpp b/servers/xr/xr_hand_tracker.cpp index cb0fbfb35f..abfe2e9867 100644 --- a/servers/xr/xr_hand_tracker.cpp +++ b/servers/xr/xr_hand_tracker.cpp @@ -33,9 +33,6 @@ #include "xr_body_tracker.h" void XRHandTracker::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_hand", "hand"), &XRHandTracker::set_hand); - ClassDB::bind_method(D_METHOD("get_hand"), &XRHandTracker::get_hand); - ClassDB::bind_method(D_METHOD("set_has_tracking_data", "has_data"), &XRHandTracker::set_has_tracking_data); ClassDB::bind_method(D_METHOD("get_has_tracking_data"), &XRHandTracker::get_has_tracking_data); @@ -60,10 +57,6 @@ void XRHandTracker::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "has_tracking_data", PROPERTY_HINT_NONE), "set_has_tracking_data", "get_has_tracking_data"); ADD_PROPERTY(PropertyInfo(Variant::INT, "hand_tracking_source", PROPERTY_HINT_ENUM, "Unknown,Unobstructed,Controller"), "set_hand_tracking_source", "get_hand_tracking_source"); - BIND_ENUM_CONSTANT(HAND_LEFT); - BIND_ENUM_CONSTANT(HAND_RIGHT); - BIND_ENUM_CONSTANT(HAND_MAX); - BIND_ENUM_CONSTANT(HAND_TRACKING_SOURCE_UNKNOWN); BIND_ENUM_CONSTANT(HAND_TRACKING_SOURCE_UNOBSTRUCTED); BIND_ENUM_CONSTANT(HAND_TRACKING_SOURCE_CONTROLLER); @@ -110,48 +103,8 @@ void XRHandTracker::set_tracker_type(XRServer::TrackerType p_type) { } void XRHandTracker::set_tracker_hand(const XRPositionalTracker::TrackerHand p_hand) { - ERR_FAIL_INDEX(p_hand, TRACKER_HAND_MAX); - - switch (p_hand) { - case TRACKER_HAND_LEFT: - tracker_hand = TRACKER_HAND_LEFT; - hand = HAND_LEFT; - break; - - case TRACKER_HAND_RIGHT: - tracker_hand = TRACKER_HAND_RIGHT; - hand = HAND_RIGHT; - break; - - case TRACKER_HAND_UNKNOWN: - default: - ERR_FAIL_MSG("XRHandTracker must specify hand"); - break; - } -} - -void XRHandTracker::set_hand(XRHandTracker::Hand p_hand) { - ERR_FAIL_INDEX(p_hand, HAND_MAX); - - switch (p_hand) { - case HAND_LEFT: - tracker_hand = TRACKER_HAND_LEFT; - hand = HAND_LEFT; - break; - - case HAND_RIGHT: - tracker_hand = TRACKER_HAND_RIGHT; - hand = HAND_RIGHT; - break; - - default: - ERR_FAIL_MSG("XRHandTracker must specify hand"); - break; - } -} - -XRHandTracker::Hand XRHandTracker::get_hand() const { - return hand; + ERR_FAIL_COND_MSG(p_hand != TRACKER_HAND_LEFT && p_hand != TRACKER_HAND_RIGHT, "XRHandTracker must specify hand."); + tracker_hand = p_hand; } void XRHandTracker::set_has_tracking_data(bool p_has_tracking_data) { @@ -222,4 +175,5 @@ Vector3 XRHandTracker::get_hand_joint_angular_velocity(XRHandTracker::HandJoint XRHandTracker::XRHandTracker() { type = XRServer::TRACKER_HAND; + tracker_hand = TRACKER_HAND_LEFT; } diff --git a/servers/xr/xr_hand_tracker.h b/servers/xr/xr_hand_tracker.h index 8ef3c229c3..c7c18a31f8 100644 --- a/servers/xr/xr_hand_tracker.h +++ b/servers/xr/xr_hand_tracker.h @@ -38,12 +38,6 @@ class XRHandTracker : public XRPositionalTracker { _THREAD_SAFE_CLASS_ public: - enum Hand { - HAND_LEFT, - HAND_RIGHT, - HAND_MAX, - }; - enum HandTrackingSource { HAND_TRACKING_SOURCE_UNKNOWN, HAND_TRACKING_SOURCE_UNOBSTRUCTED, @@ -93,9 +87,6 @@ public: void set_tracker_type(XRServer::TrackerType p_type) override; void set_tracker_hand(const XRPositionalTracker::TrackerHand p_hand) override; - void set_hand(Hand p_hand); - Hand get_hand() const; - void set_has_tracking_data(bool p_has_tracking_data); bool get_has_tracking_data() const; @@ -123,7 +114,6 @@ protected: static void _bind_methods(); private: - Hand hand = HAND_LEFT; bool has_tracking_data = false; HandTrackingSource hand_tracking_source = HAND_TRACKING_SOURCE_UNKNOWN; @@ -134,7 +124,6 @@ private: Vector3 hand_joint_angular_velocities[HAND_JOINT_MAX]; }; -VARIANT_ENUM_CAST(XRHandTracker::Hand) VARIANT_ENUM_CAST(XRHandTracker::HandTrackingSource) VARIANT_ENUM_CAST(XRHandTracker::HandJoint) VARIANT_BITFIELD_CAST(XRHandTracker::HandJointFlags) diff --git a/tests/scene/test_graph_node.h b/tests/scene/test_graph_node.h new file mode 100644 index 0000000000..72b8b682c9 --- /dev/null +++ b/tests/scene/test_graph_node.h @@ -0,0 +1,59 @@ +/**************************************************************************/ +/* test_graph_node.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef TEST_GRAPH_NODE_H +#define TEST_GRAPH_NODE_H + +#include "scene/gui/graph_node.h" +#include "scene/main/window.h" + +#include "tests/test_macros.h" + +namespace TestGraphNode { + +TEST_CASE("[GraphNode][SceneTree]") { + SUBCASE("[GraphNode] Graph Node only child on delete should not cause error.") { + // Setup. + GraphNode *test_node = memnew(GraphNode); + test_child->set_name("Graph Node"); + Control *test_child = memnew(Control); + test_child->set_name("child"); + test_node->add_child(test_child); + + // Test. + CHECK_NOTHROW_MESSAGE(test_node->remove_child(test_child)); + + memdelete(test_node); + } +} + +} // namespace TestGraphNode + +#endif // TEST_GRAPH_NODE_H diff --git a/tests/scene/test_navigation_region_3d.h b/tests/scene/test_navigation_region_3d.h index 0b20b3a1b2..f3d7f27361 100644 --- a/tests/scene/test_navigation_region_3d.h +++ b/tests/scene/test_navigation_region_3d.h @@ -65,7 +65,9 @@ TEST_SUITE("[Navigation]") { CHECK_EQ(navigation_mesh->get_vertices().size(), 0); SUBCASE("Synchronous bake should have immediate effects") { + ERR_PRINT_OFF; // Suppress warning about baking from visual meshes as source geometry. navigation_region->bake_navigation_mesh(false); + ERR_PRINT_ON; CHECK_FALSE(navigation_region->is_baking()); CHECK_NE(navigation_mesh->get_polygon_count(), 0); CHECK_NE(navigation_mesh->get_vertices().size(), 0); |