diff options
638 files changed, 12160 insertions, 7491 deletions
diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml index e5ab2fde43..c812996fd4 100644 --- a/.github/workflows/linux_builds.yml +++ b/.github/workflows/linux_builds.yml @@ -9,6 +9,7 @@ env: SCONSFLAGS: verbose=yes warnings=extra werror=yes module_text_server_fb_enabled=yes DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: true + TSAN_OPTIONS: suppressions=misc/error_suppressions/tsan.txt concurrency: group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-linux @@ -58,6 +59,16 @@ jobs: # Skip 2GiB artifact speeding up action. artifact: false + - name: Editor with ThreadSanitizer (target=editor, tests=yes, dev_build=yes, use_tsan=yes, use_llvm=yes, linker=lld) + cache-name: linux-editor-thread-sanitizer + target: editor + tests: true + sconsflags: dev_build=yes use_tsan=yes use_llvm=yes linker=lld + bin: "./bin/godot.linuxbsd.editor.dev.x86_64.llvm.san" + build-mono: false + # Skip 2GiB artifact speeding up action. + artifact: false + - name: Template w/ Mono (target=template_release) cache-name: linux-template-mono target: template_release @@ -165,7 +176,7 @@ jobs: - name: Check for GDExtension compatibility if: ${{ matrix.api-compat }} run: | - ./misc/scripts/validate_extension_api.sh "${{ matrix.bin }}" || true # don't fail the CI for now + ./misc/scripts/validate_extension_api.sh "${{ matrix.bin }}" # Download and run the test project - name: Test Godot project diff --git a/.github/workflows/runner.yml b/.github/workflows/runner.yml index 8e1741e844..34b6af4307 100644 --- a/.github/workflows/runner.yml +++ b/.github/workflows/runner.yml @@ -9,37 +9,44 @@ jobs: # First stage: Only static checks, fast and prevent expensive builds from running. static-checks: + if: ${{ vars.DISABLE_GODOT_CI == '' }} name: 📊 Static checks uses: ./.github/workflows/static_checks.yml # Second stage: Run all the builds and some of the tests. android-build: + if: ${{ vars.DISABLE_GODOT_CI == '' }} name: 🤖 Android needs: static-checks uses: ./.github/workflows/android_builds.yml ios-build: + if: ${{ vars.DISABLE_GODOT_CI == '' }} name: 🍏 iOS needs: static-checks uses: ./.github/workflows/ios_builds.yml linux-build: + if: ${{ vars.DISABLE_GODOT_CI == '' }} name: 🐧 Linux needs: static-checks uses: ./.github/workflows/linux_builds.yml macos-build: + if: ${{ vars.DISABLE_GODOT_CI == '' }} name: 🍎 macOS needs: static-checks uses: ./.github/workflows/macos_builds.yml windows-build: + if: ${{ vars.DISABLE_GODOT_CI == '' }} name: 🏁 Windows needs: static-checks uses: ./.github/workflows/windows_builds.yml web-build: + if: ${{ vars.DISABLE_GODOT_CI == '' }} name: 🌐 Web needs: static-checks uses: ./.github/workflows/web_builds.yml @@ -49,6 +56,7 @@ jobs: # Can be turned off for PRs that intentionally break compat with godot-cpp, # until both the upstream PR and the matching godot-cpp changes are merged. godot-cpp-test: + if: ${{ vars.DISABLE_GODOT_CI == '' }} name: 🪲 Godot CPP # This can be changed to depend on another platform, if we decide to use it for # godot-cpp instead. Make sure to move the .github/actions/godot-api-dump step diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index 582784d78e..f884c690fa 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -302,7 +302,7 @@ License: CC0-1.0 Files: ./thirdparty/miniupnpc/ Comment: MiniUPnP Project -Copyright: 2005-2022, Thomas Bernard +Copyright: 2005-2023, Thomas Bernard License: BSD-3-clause Files: ./thirdparty/minizip/ diff --git a/SConstruct b/SConstruct index f82c9c656e..7ea056f839 100644 --- a/SConstruct +++ b/SConstruct @@ -193,6 +193,7 @@ opts.Add(BoolVariable("vulkan", "Enable the vulkan rendering driver", True)) opts.Add(BoolVariable("opengl3", "Enable the OpenGL/GLES3 rendering driver", True)) opts.Add(BoolVariable("openxr", "Enable the OpenXR driver", True)) opts.Add(BoolVariable("use_volk", "Use the volk library to load the Vulkan loader dynamically", True)) +opts.Add(BoolVariable("disable_exceptions", "Force disabling exception handling code", True)) opts.Add("custom_modules", "A list of comma-separated directory paths containing custom modules to build.", "") opts.Add(BoolVariable("custom_modules_recursive", "Detect custom modules recursively for each specified path.", True)) @@ -220,6 +221,7 @@ opts.Add( ) opts.Add(BoolVariable("use_precise_math_checks", "Math checks use very precise epsilon (debug option)", False)) opts.Add(BoolVariable("scu_build", "Use single compilation unit build", False)) +opts.Add("scu_limit", "Max includes per SCU file when using scu_build (determines RAM use)", "0") # Thirdparty libraries opts.Add(BoolVariable("builtin_brotli", "Use the built-in Brotli library", True)) @@ -551,7 +553,16 @@ if selected_platform in platform_list: # Run SCU file generation script if in a SCU build. if env["scu_build"]: - methods.set_scu_folders(scu_builders.generate_scu_files(env["verbose"], env_base.dev_build == False)) + max_includes_per_scu = 8 + if env_base.dev_build == True: + max_includes_per_scu = 1024 + + read_scu_limit = int(env["scu_limit"]) + read_scu_limit = max(0, min(read_scu_limit, 1024)) + if read_scu_limit != 0: + max_includes_per_scu = read_scu_limit + + methods.set_scu_folders(scu_builders.generate_scu_files(env["verbose"], max_includes_per_scu)) # Must happen after the flags' definition, as configure is when most flags # are actually handled to change compile options, etc. @@ -700,6 +711,16 @@ if selected_platform in platform_list: ) Exit(255) + # Disable exception handling. Godot doesn't use exceptions anywhere, and this + # saves around 20% of binary size and very significant build time (GH-80513). + if env["disable_exceptions"]: + if env.msvc: + env.Append(CPPDEFINES=[("_HAS_EXCEPTIONS", 0)]) + else: + env.Append(CCFLAGS=["-fno-exceptions"]) + elif env.msvc: + env.Append(CCFLAGS=["/EHsc"]) + # Configure compiler warnings if env.msvc: # MSVC if env["warnings"] == "no": @@ -729,9 +750,6 @@ if selected_platform in platform_list: ] ) - # Set exception handling model to avoid warnings caused by Windows system headers. - env.Append(CCFLAGS=["/EHsc"]) - if env["werror"]: env.Append(CCFLAGS=["/WX"]) else: # GCC, Clang diff --git a/core/config/engine.cpp b/core/config/engine.cpp index 7fdea7d1aa..6727c58fd1 100644 --- a/core/config/engine.cpp +++ b/core/config/engine.cpp @@ -239,6 +239,10 @@ bool Engine::is_validation_layers_enabled() const { return use_validation_layers; } +bool Engine::is_generate_spirv_debug_info_enabled() const { + return generate_spirv_debug_info; +} + void Engine::set_print_error_messages(bool p_enabled) { CoreGlobals::print_error_enabled = p_enabled; } diff --git a/core/config/engine.h b/core/config/engine.h index 5ea653ba6c..73d40d50ae 100644 --- a/core/config/engine.h +++ b/core/config/engine.h @@ -67,6 +67,7 @@ private: double _physics_interpolation_fraction = 0.0f; bool abort_on_gpu_errors = false; bool use_validation_layers = false; + bool generate_spirv_debug_info = false; int32_t gpu_idx = -1; uint64_t _process_frames = 0; @@ -156,6 +157,7 @@ public: bool is_abort_on_gpu_errors_enabled() const; bool is_validation_layers_enabled() const; + bool is_generate_spirv_debug_info_enabled() const; int32_t get_gpu_index() const; Engine(); diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 715ed61770..1bfb745662 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -146,30 +146,30 @@ const PackedStringArray ProjectSettings::_trim_to_supported_features(const Packe #endif // TOOLS_ENABLED String ProjectSettings::localize_path(const String &p_path) const { - if (resource_path.is_empty() || (p_path.is_absolute_path() && !p_path.begins_with(resource_path))) { - return p_path.simplify_path(); + String path = p_path.simplify_path(); + + if (resource_path.is_empty() || (path.is_absolute_path() && !path.begins_with(resource_path))) { + return path; } // Check if we have a special path (like res://) or a protocol identifier. - int p = p_path.find("://"); + int p = path.find("://"); bool found = false; if (p > 0) { found = true; for (int i = 0; i < p; i++) { - if (!is_ascii_alphanumeric_char(p_path[i])) { + if (!is_ascii_alphanumeric_char(path[i])) { found = false; break; } } } if (found) { - return p_path.simplify_path(); + return path; } Ref<DirAccess> dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - String path = p_path.replace("\\", "/").simplify_path(); - if (dir->change_dir(path) == OK) { String cwd = dir->get_current_dir(); cwd = cwd.replace("\\", "/"); @@ -187,7 +187,7 @@ String ProjectSettings::localize_path(const String &p_path) const { cwd = cwd.path_join(""); if (!cwd.begins_with(res_path)) { - return p_path; + return path; } return cwd.replace_first(res_path, "res://"); @@ -283,6 +283,7 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) { for (int i = 0; i < custom_feature_array.size(); i++) { custom_features.insert(custom_feature_array[i]); } + _queue_changed(); return true; } @@ -324,6 +325,7 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) { } } + _queue_changed(); return true; } @@ -424,6 +426,22 @@ void ProjectSettings::_get_property_list(List<PropertyInfo> *p_list) const { } } +void ProjectSettings::_queue_changed() { + if (is_changed || !MessageQueue::get_singleton() || MessageQueue::get_singleton()->get_max_buffer_usage() == 0) { + return; + } + is_changed = true; + callable_mp(this, &ProjectSettings::_emit_changed).call_deferred(); +} + +void ProjectSettings::_emit_changed() { + if (!is_changed) { + return; + } + is_changed = false; + emit_signal("settings_changed"); +} + bool ProjectSettings::_load_resource_pack(const String &p_pack, bool p_replace_files, int p_offset) { if (PackedData::get_singleton()->is_disabled()) { return false; @@ -1225,6 +1243,8 @@ void ProjectSettings::_bind_methods() { ClassDB::bind_method(D_METHOD("load_resource_pack", "pack", "replace_files", "offset"), &ProjectSettings::_load_resource_pack, DEFVAL(true), DEFVAL(0)); ClassDB::bind_method(D_METHOD("save_custom", "file"), &ProjectSettings::_save_custom_bnd); + + ADD_SIGNAL(MethodInfo("settings_changed")); } void ProjectSettings::_add_builtin_input_map() { @@ -1260,6 +1280,7 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF_BASIC("application/config/name", ""); GLOBAL_DEF_BASIC(PropertyInfo(Variant::DICTIONARY, "application/config/name_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary()); GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "application/config/description", PROPERTY_HINT_MULTILINE_TEXT), ""); + GLOBAL_DEF_BASIC("application/config/version", ""); GLOBAL_DEF_INTERNAL(PropertyInfo(Variant::STRING, "application/config/tags"), PackedStringArray()); GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "application/run/main_scene", PROPERTY_HINT_FILE, "*.tscn,*.scn,*.res"), ""); GLOBAL_DEF("application/run/disable_stdout", false); @@ -1328,6 +1349,7 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "display/window/stretch/mode", PROPERTY_HINT_ENUM, "disabled,canvas_items,viewport"), "disabled"); GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "display/window/stretch/aspect", PROPERTY_HINT_ENUM, "ignore,keep,keep_width,keep_height,expand"), "keep"); GLOBAL_DEF_BASIC(PropertyInfo(Variant::FLOAT, "display/window/stretch/scale", PROPERTY_HINT_RANGE, "0.5,8.0,0.01"), 1.0); + GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "display/window/stretch/scale_mode", PROPERTY_HINT_ENUM, "fractional,integer"), "fractional"); GLOBAL_DEF(PropertyInfo(Variant::INT, "debug/settings/profiler/max_functions", PROPERTY_HINT_RANGE, "128,65535,1"), 16384); diff --git a/core/config/project_settings.h b/core/config/project_settings.h index b89e6694b0..dba4aa6822 100644 --- a/core/config/project_settings.h +++ b/core/config/project_settings.h @@ -43,6 +43,9 @@ class TypedArray; class ProjectSettings : public Object { GDCLASS(ProjectSettings, Object); _THREAD_SAFE_CLASS_ + friend class TestProjectSettingsInternalsAccessor; + + bool is_changed = false; public: typedef HashMap<String, Variant> CustomMap; @@ -114,6 +117,9 @@ protected: bool _property_can_revert(const StringName &p_name) const; bool _property_get_revert(const StringName &p_name, Variant &r_property) const; + void _queue_changed(); + void _emit_changed(); + static ProjectSettings *singleton; Error _load_settings_text(const String &p_path); diff --git a/core/core_bind.cpp b/core/core_bind.cpp index a73b198be2..05fe393a2f 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -929,6 +929,17 @@ Geometry3D *Geometry3D::get_singleton() { return singleton; } +Vector<Vector3> Geometry3D::compute_convex_mesh_points(const TypedArray<Plane> &p_planes) { + Vector<Plane> planes_vec; + int size = p_planes.size(); + planes_vec.resize(size); + for (int i = 0; i < size; ++i) { + planes_vec.set(i, p_planes[i]); + } + Variant ret = ::Geometry3D::compute_convex_mesh_points(planes_vec.ptr(), size); + return ret; +} + TypedArray<Plane> Geometry3D::build_box_planes(const Vector3 &p_extents) { Variant ret = ::Geometry3D::build_box_planes(p_extents); return ret; @@ -961,6 +972,11 @@ Vector3 Geometry3D::get_closest_point_to_segment_uncapped(const Vector3 &p_point return ::Geometry3D::get_closest_point_to_segment_uncapped(p_point, s); } +Vector3 Geometry3D::get_triangle_barycentric_coords(const Vector3 &p_point, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2) { + Vector3 res = ::Geometry3D::triangle_get_barycentric_coords(p_v0, p_v1, p_v2, p_point); + return res; +} + Variant Geometry3D::ray_intersects_triangle(const Vector3 &p_from, const Vector3 &p_dir, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2) { Vector3 res; if (::Geometry3D::ray_intersects_triangle(p_from, p_dir, p_v0, p_v1, p_v2, &res)) { @@ -1024,6 +1040,7 @@ Vector<Vector3> Geometry3D::clip_polygon(const Vector<Vector3> &p_points, const } void Geometry3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("compute_convex_mesh_points", "planes"), &Geometry3D::compute_convex_mesh_points); ClassDB::bind_method(D_METHOD("build_box_planes", "extents"), &Geometry3D::build_box_planes); ClassDB::bind_method(D_METHOD("build_cylinder_planes", "radius", "height", "sides", "axis"), &Geometry3D::build_cylinder_planes, DEFVAL(Vector3::AXIS_Z)); ClassDB::bind_method(D_METHOD("build_capsule_planes", "radius", "height", "sides", "lats", "axis"), &Geometry3D::build_capsule_planes, DEFVAL(Vector3::AXIS_Z)); @@ -1034,6 +1051,8 @@ void Geometry3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_closest_point_to_segment_uncapped", "point", "s1", "s2"), &Geometry3D::get_closest_point_to_segment_uncapped); + ClassDB::bind_method(D_METHOD("get_triangle_barycentric_coords", "point", "a", "b", "c"), &Geometry3D::get_triangle_barycentric_coords); + ClassDB::bind_method(D_METHOD("ray_intersects_triangle", "from", "dir", "a", "b", "c"), &Geometry3D::ray_intersects_triangle); ClassDB::bind_method(D_METHOD("segment_intersects_triangle", "from", "to", "a", "b", "c"), &Geometry3D::segment_intersects_triangle); ClassDB::bind_method(D_METHOD("segment_intersects_sphere", "from", "to", "sphere_position", "sphere_radius"), &Geometry3D::segment_intersects_sphere); diff --git a/core/core_bind.h b/core/core_bind.h index dc0b2a1cf5..5f51b64eb7 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -320,12 +320,14 @@ protected: public: static Geometry3D *get_singleton(); + Vector<Vector3> compute_convex_mesh_points(const TypedArray<Plane> &p_planes); TypedArray<Plane> build_box_planes(const Vector3 &p_extents); TypedArray<Plane> build_cylinder_planes(float p_radius, float p_height, int p_sides, Vector3::Axis p_axis = Vector3::AXIS_Z); TypedArray<Plane> build_capsule_planes(float p_radius, float p_height, int p_sides, int p_lats, Vector3::Axis p_axis = Vector3::AXIS_Z); Vector<Vector3> get_closest_points_between_segments(const Vector3 &p1, const Vector3 &p2, const Vector3 &q1, const Vector3 &q2); Vector3 get_closest_point_to_segment(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b); Vector3 get_closest_point_to_segment_uncapped(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b); + Vector3 get_triangle_barycentric_coords(const Vector3 &p_point, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2); Variant ray_intersects_triangle(const Vector3 &p_from, const Vector3 &p_dir, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2); Variant segment_intersects_triangle(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2); diff --git a/core/error/error_macros.h b/core/error/error_macros.h index 65804b7796..c8182975d5 100644 --- a/core/error/error_macros.h +++ b/core/error/error_macros.h @@ -786,8 +786,19 @@ void _err_flush_stdout(); ((void)0) /** - * This should be a 'free' assert for program flow and should not be needed in any releases, - * only used in dev builds. + * Note: IN MOST CASES YOU SHOULD NOT USE THIS MACRO. + * Do not use unless you understand the trade-offs. + * + * DEV macros will be compiled out in releases, they are wrapped in DEV_ENABLED. + * + * Prefer WARNINGS / ERR_FAIL macros (which fail without crashing) - ERR_FAIL should be used in most cases. + * Then CRASH_NOW_MSG macros (on rare occasions where error cannot be recovered). + * + * DEV_ASSERT should generally only be used when both of the following conditions are met: + * 1) Bottleneck code where a check in release would be too expensive. + * 2) Situations where the check would fail obviously and straight away during the maintenance of the code + * (i.e. strict conditions that should be true no matter what) + * and that can't fail for other contributors once the code is finished and merged. */ #ifdef DEV_ENABLED #define DEV_ASSERT(m_cond) \ diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp index 67b55db3db..1e4cd81034 100644 --- a/core/extension/gdextension.cpp +++ b/core/extension/gdextension.cpp @@ -485,6 +485,13 @@ void GDExtension::close_library() { ERR_FAIL_COND(library == nullptr); OS::get_singleton()->close_dynamic_library(library); +#if defined(TOOLS_ENABLED) && defined(WINDOWS_ENABLED) + // Delete temporary copy of library if it exists. + if (!temp_lib_path.is_empty() && Engine::get_singleton()->is_editor_hint()) { + DirAccess::remove_absolute(temp_lib_path); + } +#endif + library = nullptr; } @@ -607,12 +614,13 @@ Ref<Resource> GDExtensionResourceLoader::load(const String &p_path, const String } bool compatible = true; - if (VERSION_MAJOR < compatibility_minimum[0]) { - compatible = false; - } else if (VERSION_MINOR < compatibility_minimum[1]) { - compatible = false; - } else if (VERSION_PATCH < compatibility_minimum[2]) { - compatible = false; + // Check version lexicographically. + if (VERSION_MAJOR != compatibility_minimum[0]) { + compatible = VERSION_MAJOR > compatibility_minimum[0]; + } else if (VERSION_MINOR != compatibility_minimum[1]) { + compatible = VERSION_MINOR > compatibility_minimum[1]; + } else { + compatible = VERSION_PATCH >= compatibility_minimum[2]; } if (!compatible) { if (r_error) { @@ -640,6 +648,40 @@ Ref<Resource> GDExtensionResourceLoader::load(const String &p_path, const String Ref<GDExtension> lib; lib.instantiate(); String abs_path = ProjectSettings::get_singleton()->globalize_path(library_path); + +#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED) + // If running on the editor on Windows, we copy the library and open the copy. + // This is so the original file isn't locked and can be updated by a compiler. + if (Engine::get_singleton()->is_editor_hint()) { + if (!FileAccess::exists(abs_path)) { + if (r_error) { + *r_error = ERR_FILE_NOT_FOUND; + } + ERR_PRINT("GDExtension library not found: " + library_path); + return Ref<Resource>(); + } + + // Copy the file to the same directory as the original with a prefix in the name. + // This is so relative path to dependencies are satisfied. + String copy_path = abs_path.get_base_dir().path_join("~" + abs_path.get_file()); + + Error copy_err = DirAccess::copy_absolute(abs_path, copy_path); + if (copy_err) { + if (r_error) { + *r_error = ERR_CANT_CREATE; + } + ERR_PRINT("Error copying GDExtension library: " + library_path); + return Ref<Resource>(); + } + FileAccess::set_hidden_attribute(copy_path, true); + + // Save the copied path so it can be deleted later. + lib->set_temp_library_path(copy_path); + + // Use the copy to open the library. + abs_path = copy_path; + } +#endif err = lib->open_library(abs_path, entry_symbol); if (r_error) { diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h index b935f8706f..5a4dd3d5f5 100644 --- a/core/extension/gdextension.h +++ b/core/extension/gdextension.h @@ -43,6 +43,9 @@ class GDExtension : public Resource { void *library = nullptr; // pointer if valid, String library_path; +#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED) + String temp_lib_path; +#endif struct Extension { ObjectGDExtension gdextension; @@ -76,6 +79,10 @@ public: Error open_library(const String &p_path, const String &p_entry_symbol); void close_library(); +#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED) + void set_temp_library_path(const String &p_path) { temp_lib_path = p_path; } +#endif + enum InitializationLevel { INITIALIZATION_LEVEL_CORE = GDEXTENSION_INITIALIZATION_CORE, INITIALIZATION_LEVEL_SERVERS = GDEXTENSION_INITIALIZATION_SERVERS, diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp index 7ef956a470..b4541de8fe 100644 --- a/core/extension/gdextension_interface.cpp +++ b/core/extension/gdextension_interface.cpp @@ -1048,6 +1048,25 @@ static GDExtensionScriptInstancePtr gdextension_script_instance_create(const GDE return reinterpret_cast<GDExtensionScriptInstancePtr>(script_instance_extension); } +static GDExtensionScriptInstancePtr gdextension_object_get_script_instance(GDExtensionConstObjectPtr p_object, GDExtensionConstObjectPtr p_language) { + if (!p_object || !p_language) { + return nullptr; + } + + const Object *o = (const Object *)p_object; + ScriptInstanceExtension *script_instance_extension = reinterpret_cast<ScriptInstanceExtension *>(o->get_script_instance()); + if (!script_instance_extension) { + return nullptr; + } + + const ScriptLanguage *language = script_instance_extension->get_language(); + if (language != p_language) { + return nullptr; + } + + return script_instance_extension->instance; +} + static GDExtensionMethodBindPtr gdextension_classdb_get_method_bind(GDExtensionConstStringNamePtr p_classname, GDExtensionConstStringNamePtr p_methodname, GDExtensionInt p_hash) { const StringName classname = *reinterpret_cast<const StringName *>(p_classname); const StringName methodname = *reinterpret_cast<const StringName *>(p_methodname); @@ -1216,6 +1235,7 @@ void gdextension_setup_interface() { REGISTER_INTERFACE_FUNC(ref_get_object); REGISTER_INTERFACE_FUNC(ref_set_object); REGISTER_INTERFACE_FUNC(script_instance_create); + REGISTER_INTERFACE_FUNC(object_get_script_instance); REGISTER_INTERFACE_FUNC(classdb_construct_object); REGISTER_INTERFACE_FUNC(classdb_get_method_bind); REGISTER_INTERFACE_FUNC(classdb_get_class_tag); diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h index 6c05f3988b..cfc21473d6 100644 --- a/core/extension/gdextension_interface.h +++ b/core/extension/gdextension_interface.h @@ -2126,6 +2126,19 @@ typedef void (*GDExtensionInterfaceRefSetObject)(GDExtensionRefPtr p_ref, GDExte */ typedef GDExtensionScriptInstancePtr (*GDExtensionInterfaceScriptInstanceCreate)(const GDExtensionScriptInstanceInfo *p_info, GDExtensionScriptInstanceDataPtr p_instance_data); +/** + * @name object_get_script_instance + * @since 4.2 + * + * Get the script instance data attached to this object. + * + * @param p_object A pointer to the Object. + * @param p_language A pointer to the language expected for this script instance. + * + * @return A GDExtensionScriptInstanceDataPtr that was attached to this object as part of script_instance_create. + */ +typedef GDExtensionScriptInstanceDataPtr (*GDExtensionInterfaceObjectGetScriptInstance)(GDExtensionConstObjectPtr p_object, GDExtensionObjectPtr p_language); + /* INTERFACE: ClassDB */ /** diff --git a/core/input/input.cpp b/core/input/input.cpp index d481acf005..39f1acf623 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -113,6 +113,7 @@ void Input::_bind_methods() { ClassDB::bind_method(D_METHOD("get_joy_axis", "device", "axis"), &Input::get_joy_axis); ClassDB::bind_method(D_METHOD("get_joy_name", "device"), &Input::get_joy_name); ClassDB::bind_method(D_METHOD("get_joy_guid", "device"), &Input::get_joy_guid); + ClassDB::bind_method(D_METHOD("get_joy_info", "device"), &Input::get_joy_info); ClassDB::bind_method(D_METHOD("should_ignore_device", "vendor_id", "product_id"), &Input::should_ignore_device); ClassDB::bind_method(D_METHOD("get_connected_joypads"), &Input::get_connected_joypads); ClassDB::bind_method(D_METHOD("get_joy_vibration_strength", "device"), &Input::get_joy_vibration_strength); @@ -437,11 +438,12 @@ static String _hex_str(uint8_t p_byte) { return ret; } -void Input::joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid) { +void Input::joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid, Dictionary p_joypad_info) { _THREAD_SAFE_METHOD_ Joypad js; js.name = p_connected ? p_name : ""; js.uid = p_connected ? p_guid : ""; + js.info = p_connected ? p_joypad_info : Dictionary(); if (p_connected) { String uidname = p_guid; @@ -473,7 +475,8 @@ void Input::joy_connection_changed(int p_idx, bool p_connected, String p_name, S } joy_names[p_idx] = js; - emit_signal(SNAME("joy_connection_changed"), p_idx, p_connected); + // Ensure this signal is emitted on the main thread, as some platforms (e.g. Linux) call this from a different thread. + call_deferred("emit_signal", SNAME("joy_connection_changed"), p_idx, p_connected); } Vector3 Input::get_gravity() const { @@ -1499,6 +1502,11 @@ String Input::get_joy_guid(int p_device) const { return joy_names[p_device].uid; } +Dictionary Input::get_joy_info(int p_device) const { + ERR_FAIL_COND_V(!joy_names.has(p_device), Dictionary()); + return joy_names[p_device].info; +} + bool Input::should_ignore_device(int p_vendor_id, int p_product_id) const { uint32_t full_id = (((uint32_t)p_vendor_id) << 16) | ((uint16_t)p_product_id); return ignored_device_ids.has(full_id); diff --git a/core/input/input.h b/core/input/input.h index ec16871b72..c63a4e52e3 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -149,6 +149,7 @@ private: HatMask last_hat = HatMask::CENTER; int mapping = -1; int hat_current = 0; + Dictionary info; }; VelocityTrack mouse_velocity_track; @@ -276,7 +277,7 @@ public: Vector2 get_joy_vibration_strength(int p_device); float get_joy_vibration_duration(int p_device); uint64_t get_joy_vibration_timestamp(int p_device); - void joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid = ""); + void joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid = "", Dictionary p_joypad_info = Dictionary()); Vector3 get_gravity() const; Vector3 get_accelerometer() const; @@ -332,6 +333,7 @@ public: bool is_joy_known(int p_device); String get_joy_guid(int p_device) const; bool should_ignore_device(int p_vendor_id, int p_product_id) const; + Dictionary get_joy_info(int p_device) const; void set_fallback_mapping(String p_guid); void flush_buffered_events(); diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp index b669afdc99..6026dbf896 100644 --- a/core/io/file_access.cpp +++ b/core/io/file_access.cpp @@ -583,7 +583,7 @@ uint64_t FileAccess::get_modified_time(const String &p_file) { return mt; } -uint32_t FileAccess::get_unix_permissions(const String &p_file) { +BitField<FileAccess::UnixPermissionFlags> FileAccess::get_unix_permissions(const String &p_file) { if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) { return 0; } @@ -591,11 +591,10 @@ uint32_t FileAccess::get_unix_permissions(const String &p_file) { Ref<FileAccess> fa = create_for_path(p_file); ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "Cannot create FileAccess for path '" + p_file + "'."); - uint32_t mt = fa->_get_unix_permissions(p_file); - return mt; + return fa->_get_unix_permissions(p_file); } -Error FileAccess::set_unix_permissions(const String &p_file, uint32_t p_permissions) { +Error FileAccess::set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) { if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) { return ERR_UNAVAILABLE; } @@ -607,6 +606,52 @@ Error FileAccess::set_unix_permissions(const String &p_file, uint32_t p_permissi return err; } +bool FileAccess::get_hidden_attribute(const String &p_file) { + if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) { + return false; + } + + Ref<FileAccess> fa = create_for_path(p_file); + ERR_FAIL_COND_V_MSG(fa.is_null(), false, "Cannot create FileAccess for path '" + p_file + "'."); + + return fa->_get_hidden_attribute(p_file); +} + +Error FileAccess::set_hidden_attribute(const String &p_file, bool p_hidden) { + if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) { + return ERR_UNAVAILABLE; + } + + Ref<FileAccess> fa = create_for_path(p_file); + ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, "Cannot create FileAccess for path '" + p_file + "'."); + + Error err = fa->_set_hidden_attribute(p_file, p_hidden); + return err; +} + +bool FileAccess::get_read_only_attribute(const String &p_file) { + if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) { + return false; + } + + Ref<FileAccess> fa = create_for_path(p_file); + ERR_FAIL_COND_V_MSG(fa.is_null(), false, "Cannot create FileAccess for path '" + p_file + "'."); + + return fa->_get_read_only_attribute(p_file); +} + +Error FileAccess::set_read_only_attribute(const String &p_file, bool p_ro) { + if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) { + return ERR_UNAVAILABLE; + } + + Ref<FileAccess> fa = create_for_path(p_file); + ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, "Cannot create FileAccess for path '" + p_file + "'."); + + Error err = fa->_set_read_only_attribute(p_file, p_ro); + return err; +} + void FileAccess::store_string(const String &p_string) { if (p_string.length() == 0) { return; @@ -865,6 +910,14 @@ void FileAccess::_bind_methods() { ClassDB::bind_static_method("FileAccess", D_METHOD("file_exists", "path"), &FileAccess::exists); ClassDB::bind_static_method("FileAccess", D_METHOD("get_modified_time", "file"), &FileAccess::get_modified_time); + ClassDB::bind_static_method("FileAccess", D_METHOD("get_unix_permissions", "file"), &FileAccess::get_unix_permissions); + ClassDB::bind_static_method("FileAccess", D_METHOD("set_unix_permissions", "file", "permissions"), &FileAccess::set_unix_permissions); + + ClassDB::bind_static_method("FileAccess", D_METHOD("get_hidden_attribute", "file"), &FileAccess::get_hidden_attribute); + ClassDB::bind_static_method("FileAccess", D_METHOD("set_hidden_attribute", "file", "hidden"), &FileAccess::set_hidden_attribute); + ClassDB::bind_static_method("FileAccess", D_METHOD("set_read_only_attribute", "file", "ro"), &FileAccess::set_read_only_attribute); + ClassDB::bind_static_method("FileAccess", D_METHOD("get_read_only_attribute", "file"), &FileAccess::get_read_only_attribute); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "big_endian"), "set_big_endian", "is_big_endian"); BIND_ENUM_CONSTANT(READ); @@ -877,4 +930,17 @@ void FileAccess::_bind_methods() { BIND_ENUM_CONSTANT(COMPRESSION_ZSTD); BIND_ENUM_CONSTANT(COMPRESSION_GZIP); BIND_ENUM_CONSTANT(COMPRESSION_BROTLI); + + BIND_BITFIELD_FLAG(UNIX_READ_OWNER); + BIND_BITFIELD_FLAG(UNIX_WRITE_OWNER); + BIND_BITFIELD_FLAG(UNIX_EXECUTE_OWNER); + BIND_BITFIELD_FLAG(UNIX_READ_GROUP); + BIND_BITFIELD_FLAG(UNIX_WRITE_GROUP); + BIND_BITFIELD_FLAG(UNIX_EXECUTE_GROUP); + BIND_BITFIELD_FLAG(UNIX_READ_OTHER); + BIND_BITFIELD_FLAG(UNIX_WRITE_OTHER); + BIND_BITFIELD_FLAG(UNIX_EXECUTE_OTHER); + BIND_BITFIELD_FLAG(UNIX_SET_USER_ID); + BIND_BITFIELD_FLAG(UNIX_SET_GROUP_ID); + BIND_BITFIELD_FLAG(UNIX_RESTRICTED_DELETE); } diff --git a/core/io/file_access.h b/core/io/file_access.h index ad1ac665f3..7b9e66bb83 100644 --- a/core/io/file_access.h +++ b/core/io/file_access.h @@ -60,6 +60,21 @@ public: WRITE_READ = 7, }; + enum UnixPermissionFlags { + UNIX_EXECUTE_OTHER = 0x001, + UNIX_WRITE_OTHER = 0x002, + UNIX_READ_OTHER = 0x004, + UNIX_EXECUTE_GROUP = 0x008, + UNIX_WRITE_GROUP = 0x010, + UNIX_READ_GROUP = 0x020, + UNIX_EXECUTE_OWNER = 0x040, + UNIX_WRITE_OWNER = 0x080, + UNIX_READ_OWNER = 0x100, + UNIX_RESTRICTED_DELETE = 0x200, + UNIX_SET_GROUP_ID = 0x400, + UNIX_SET_USER_ID = 0x800, + }; + enum CompressionMode { COMPRESSION_FASTLZ = Compression::MODE_FASTLZ, COMPRESSION_DEFLATE = Compression::MODE_DEFLATE, @@ -74,8 +89,13 @@ public: bool big_endian = false; bool real_is_double = false; - virtual uint32_t _get_unix_permissions(const String &p_file) = 0; - virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) = 0; + virtual BitField<UnixPermissionFlags> _get_unix_permissions(const String &p_file) = 0; + virtual Error _set_unix_permissions(const String &p_file, BitField<UnixPermissionFlags> p_permissions) = 0; + + virtual bool _get_hidden_attribute(const String &p_file) = 0; + virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) = 0; + virtual bool _get_read_only_attribute(const String &p_file) = 0; + virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) = 0; protected: static void _bind_methods(); @@ -185,8 +205,13 @@ public: static CreateFunc get_create_func(AccessType p_access); static bool exists(const String &p_name); ///< return true if a file exists static uint64_t get_modified_time(const String &p_file); - static uint32_t get_unix_permissions(const String &p_file); - static Error set_unix_permissions(const String &p_file, uint32_t p_permissions); + static BitField<FileAccess::UnixPermissionFlags> get_unix_permissions(const String &p_file); + static Error set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions); + + static bool get_hidden_attribute(const String &p_file); + static Error set_hidden_attribute(const String &p_file, bool p_hidden); + static bool get_read_only_attribute(const String &p_file); + static Error set_read_only_attribute(const String &p_file, bool p_ro); static void set_backup_save(bool p_enable) { backup_save = p_enable; }; static bool is_backup_save_enabled() { return backup_save; }; @@ -212,5 +237,6 @@ public: VARIANT_ENUM_CAST(FileAccess::CompressionMode); VARIANT_ENUM_CAST(FileAccess::ModeFlags); +VARIANT_BITFIELD_CAST(FileAccess::UnixPermissionFlags); #endif // FILE_ACCESS_H diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp index 3e5a1217dd..0f00bd292c 100644 --- a/core/io/file_access_compressed.cpp +++ b/core/io/file_access_compressed.cpp @@ -365,20 +365,48 @@ uint64_t FileAccessCompressed::_get_modified_time(const String &p_file) { } } -uint32_t FileAccessCompressed::_get_unix_permissions(const String &p_file) { +BitField<FileAccess::UnixPermissionFlags> FileAccessCompressed::_get_unix_permissions(const String &p_file) { if (f.is_valid()) { return f->_get_unix_permissions(p_file); } return 0; } -Error FileAccessCompressed::_set_unix_permissions(const String &p_file, uint32_t p_permissions) { +Error FileAccessCompressed::_set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) { if (f.is_valid()) { return f->_set_unix_permissions(p_file, p_permissions); } return FAILED; } +bool FileAccessCompressed::_get_hidden_attribute(const String &p_file) { + if (f.is_valid()) { + return f->_get_hidden_attribute(p_file); + } + return false; +} + +Error FileAccessCompressed::_set_hidden_attribute(const String &p_file, bool p_hidden) { + if (f.is_valid()) { + return f->_set_hidden_attribute(p_file, p_hidden); + } + return FAILED; +} + +bool FileAccessCompressed::_get_read_only_attribute(const String &p_file) { + if (f.is_valid()) { + return f->_get_read_only_attribute(p_file); + } + return false; +} + +Error FileAccessCompressed::_set_read_only_attribute(const String &p_file, bool p_ro) { + if (f.is_valid()) { + return f->_set_read_only_attribute(p_file, p_ro); + } + return FAILED; +} + void FileAccessCompressed::close() { _close(); } diff --git a/core/io/file_access_compressed.h b/core/io/file_access_compressed.h index 601b74a9c1..bf57eaa07c 100644 --- a/core/io/file_access_compressed.h +++ b/core/io/file_access_compressed.h @@ -94,8 +94,13 @@ public: virtual bool file_exists(const String &p_name) override; ///< return true if a file exists virtual uint64_t _get_modified_time(const String &p_file) override; - virtual uint32_t _get_unix_permissions(const String &p_file) override; - virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override; + virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override; + virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override; + + virtual bool _get_hidden_attribute(const String &p_file) override; + virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override; + virtual bool _get_read_only_attribute(const String &p_file) override; + virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override; virtual void close() override; diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp index c39d19d52b..b689f5b628 100644 --- a/core/io/file_access_encrypted.cpp +++ b/core/io/file_access_encrypted.cpp @@ -285,13 +285,46 @@ uint64_t FileAccessEncrypted::_get_modified_time(const String &p_file) { return 0; } -uint32_t FileAccessEncrypted::_get_unix_permissions(const String &p_file) { +BitField<FileAccess::UnixPermissionFlags> FileAccessEncrypted::_get_unix_permissions(const String &p_file) { + if (file.is_valid()) { + return file->_get_unix_permissions(p_file); + } return 0; } -Error FileAccessEncrypted::_set_unix_permissions(const String &p_file, uint32_t p_permissions) { - ERR_PRINT("Setting UNIX permissions on encrypted files is not implemented yet."); - return ERR_UNAVAILABLE; +Error FileAccessEncrypted::_set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) { + if (file.is_valid()) { + return file->_set_unix_permissions(p_file, p_permissions); + } + return FAILED; +} + +bool FileAccessEncrypted::_get_hidden_attribute(const String &p_file) { + if (file.is_valid()) { + return file->_get_hidden_attribute(p_file); + } + return false; +} + +Error FileAccessEncrypted::_set_hidden_attribute(const String &p_file, bool p_hidden) { + if (file.is_valid()) { + return file->_set_hidden_attribute(p_file, p_hidden); + } + return FAILED; +} + +bool FileAccessEncrypted::_get_read_only_attribute(const String &p_file) { + if (file.is_valid()) { + return file->_get_read_only_attribute(p_file); + } + return false; +} + +Error FileAccessEncrypted::_set_read_only_attribute(const String &p_file, bool p_ro) { + if (file.is_valid()) { + return file->_set_read_only_attribute(p_file, p_ro); + } + return FAILED; } void FileAccessEncrypted::close() { diff --git a/core/io/file_access_encrypted.h b/core/io/file_access_encrypted.h index 9702b5a517..489d213b8f 100644 --- a/core/io/file_access_encrypted.h +++ b/core/io/file_access_encrypted.h @@ -85,8 +85,13 @@ public: virtual bool file_exists(const String &p_name) override; ///< return true if a file exists virtual uint64_t _get_modified_time(const String &p_file) override; - virtual uint32_t _get_unix_permissions(const String &p_file) override; - virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override; + virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override; + virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override; + + virtual bool _get_hidden_attribute(const String &p_file) override; + virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override; + virtual bool _get_read_only_attribute(const String &p_file) override; + virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override; virtual void close() override; diff --git a/core/io/file_access_memory.h b/core/io/file_access_memory.h index 43fe6ab658..ac08e5406f 100644 --- a/core/io/file_access_memory.h +++ b/core/io/file_access_memory.h @@ -68,8 +68,13 @@ public: virtual bool file_exists(const String &p_name) override; ///< return true if a file exists virtual uint64_t _get_modified_time(const String &p_file) override { return 0; } - virtual uint32_t _get_unix_permissions(const String &p_file) override { return 0; } - virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override { return FAILED; } + virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; } + virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return FAILED; } + + virtual bool _get_hidden_attribute(const String &p_file) override { return false; } + virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; } + virtual bool _get_read_only_attribute(const String &p_file) override { return false; } + virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; } virtual void close() override {} diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h index 1538b302c2..97391a5611 100644 --- a/core/io/file_access_pack.h +++ b/core/io/file_access_pack.h @@ -150,8 +150,13 @@ class FileAccessPack : public FileAccess { Ref<FileAccess> f; virtual Error open_internal(const String &p_path, int p_mode_flags) override; virtual uint64_t _get_modified_time(const String &p_file) override { return 0; } - virtual uint32_t _get_unix_permissions(const String &p_file) override { return 0; } - virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override { return FAILED; } + virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; } + virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return FAILED; } + + virtual bool _get_hidden_attribute(const String &p_file) override { return false; } + virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; } + virtual bool _get_read_only_attribute(const String &p_file) override { return false; } + virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; } public: virtual bool is_open() const override; diff --git a/core/io/file_access_zip.h b/core/io/file_access_zip.h index f8b640946c..1062a06238 100644 --- a/core/io/file_access_zip.h +++ b/core/io/file_access_zip.h @@ -106,8 +106,13 @@ public: virtual bool file_exists(const String &p_name) override; ///< return true if a file exists virtual uint64_t _get_modified_time(const String &p_file) override { return 0; } // todo - virtual uint32_t _get_unix_permissions(const String &p_file) override { return 0; } - virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override { return FAILED; } + virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; } + virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return FAILED; } + + virtual bool _get_hidden_attribute(const String &p_file) override { return false; } + virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; } + virtual bool _get_read_only_attribute(const String &p_file) override { return false; } + virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; } virtual void close() override; diff --git a/core/io/remote_filesystem_client.cpp b/core/io/remote_filesystem_client.cpp index f22e442a34..1198810441 100644 --- a/core/io/remote_filesystem_client.cpp +++ b/core/io/remote_filesystem_client.cpp @@ -270,7 +270,7 @@ Error RemoteFilesystemClient::_synchronize_with_server(const String &p_host, int String file = temp_file_cache[i].path; if (temp_file_cache[i].server_modified_time == 0 || server_disconnected) { - // File was removed, or server disconnected before tranferring it. Since it's no longer valid, remove anyway. + // File was removed, or server disconnected before transferring it. Since it's no longer valid, remove anyway. _remove_file(file); continue; } diff --git a/core/io/remote_filesystem_client.h b/core/io/remote_filesystem_client.h index 42eba98eb1..fcb5c1cfc3 100644 --- a/core/io/remote_filesystem_client.h +++ b/core/io/remote_filesystem_client.h @@ -44,8 +44,8 @@ protected: String _get_cache_path() { return cache_path; } struct FileCache { String path; // Local path (as in "folder/to/file.png") - uint64_t server_modified_time; // MD5 checksum. - uint64_t modified_time; + uint64_t server_modified_time = 0; // MD5 checksum. + uint64_t modified_time = 0; }; virtual bool _is_configured() { return !cache_path.is_empty(); } // Can be re-implemented per platform. If so, feel free to ignore get_cache_path() diff --git a/core/math/a_star_grid_2d.cpp b/core/math/a_star_grid_2d.cpp index 63f7c80bdd..9ba4c2ff9a 100644 --- a/core/math/a_star_grid_2d.cpp +++ b/core/math/a_star_grid_2d.cpp @@ -194,6 +194,38 @@ real_t AStarGrid2D::get_point_weight_scale(const Vector2i &p_id) const { return GET_POINT_UNCHECKED(p_id).weight_scale; } +void AStarGrid2D::fill_solid_region(const Rect2i &p_region, bool p_solid) { + ERR_FAIL_COND_MSG(dirty, "Grid is not initialized. Call the update method."); + + Rect2i safe_region = p_region.intersection(region); + int from_x = safe_region.get_position().x; + int from_y = safe_region.get_position().y; + int end_x = safe_region.get_end().x; + int end_y = safe_region.get_end().y; + + for (int x = from_x; x < end_x; x++) { + for (int y = from_y; y < end_y; y++) { + GET_POINT_UNCHECKED(Vector2i(x, y)).solid = p_solid; + } + } +} + +void AStarGrid2D::fill_weight_scale_region(const Rect2i &p_region, real_t p_weight_scale) { + ERR_FAIL_COND_MSG(dirty, "Grid is not initialized. Call the update method."); + ERR_FAIL_COND_MSG(p_weight_scale < 0.0, vformat("Can't set point's weight scale less than 0.0: %f.", p_weight_scale)); + + Rect2i safe_region = p_region.intersection(region); + int from_x = safe_region.get_position().x; + int from_y = safe_region.get_position().y; + int end_x = safe_region.get_end().x; + int end_y = safe_region.get_end().y; + for (int x = from_x; x < end_x; x++) { + for (int y = from_y; y < end_y; y++) { + GET_POINT_UNCHECKED(Vector2i(x, y)).weight_scale = p_weight_scale; + } + } +} + AStarGrid2D::Point *AStarGrid2D::_jump(Point *p_from, Point *p_to) { if (!p_to || p_to->solid) { return nullptr; @@ -606,6 +638,8 @@ void AStarGrid2D::_bind_methods() { ClassDB::bind_method(D_METHOD("is_point_solid", "id"), &AStarGrid2D::is_point_solid); ClassDB::bind_method(D_METHOD("set_point_weight_scale", "id", "weight_scale"), &AStarGrid2D::set_point_weight_scale); ClassDB::bind_method(D_METHOD("get_point_weight_scale", "id"), &AStarGrid2D::get_point_weight_scale); + ClassDB::bind_method(D_METHOD("fill_solid_region", "region", "solid"), &AStarGrid2D::fill_solid_region, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("fill_weight_scale_region", "region", "weight_scale"), &AStarGrid2D::fill_weight_scale_region); ClassDB::bind_method(D_METHOD("clear"), &AStarGrid2D::clear); ClassDB::bind_method(D_METHOD("get_point_position", "id"), &AStarGrid2D::get_point_position); diff --git a/core/math/a_star_grid_2d.h b/core/math/a_star_grid_2d.h index 50df58e0e9..dd5f9d0575 100644 --- a/core/math/a_star_grid_2d.h +++ b/core/math/a_star_grid_2d.h @@ -177,6 +177,9 @@ public: void set_point_weight_scale(const Vector2i &p_id, real_t p_weight_scale); real_t get_point_weight_scale(const Vector2i &p_id) const; + void fill_solid_region(const Rect2i &p_region, bool p_solid = true); + void fill_weight_scale_region(const Rect2i &p_region, real_t p_weight_scale); + void clear(); Vector2 get_point_position(const Vector2i &p_id) const; diff --git a/core/math/delaunay_3d.h b/core/math/delaunay_3d.h index 55923e0133..7df8c37e3c 100644 --- a/core/math/delaunay_3d.h +++ b/core/math/delaunay_3d.h @@ -105,8 +105,8 @@ class Delaunay3D { }; _FORCE_INLINE_ static void circum_sphere_compute(const Vector3 *p_points, Simplex *p_simplex) { - // the only part in the algorithm where there may be precision errors is this one, so ensure that - // we do it as maximum precision as possible + // The only part in the algorithm where there may be precision errors is this one, + // so ensure that we do it with the maximum precision possible. R128 v0_x = p_points[p_simplex->points[0]].x; R128 v0_y = p_points[p_simplex->points[0]].y; @@ -121,7 +121,7 @@ class Delaunay3D { R128 v3_y = p_points[p_simplex->points[3]].y; R128 v3_z = p_points[p_simplex->points[3]].z; - //Create the rows of our "unrolled" 3x3 matrix + // Create the rows of our "unrolled" 3x3 matrix. R128 row1_x = v1_x - v0_x; R128 row1_y = v1_y - v0_y; R128 row1_z = v1_z - v0_z; @@ -138,10 +138,10 @@ class Delaunay3D { R128 sq_lenght2 = row2_x * row2_x + row2_y * row2_y + row2_z * row2_z; R128 sq_lenght3 = row3_x * row3_x + row3_y * row3_y + row3_z * row3_z; - //Compute the determinant of said matrix + // Compute the determinant of said matrix. R128 determinant = row1_x * (row2_y * row3_z - row3_y * row2_z) - row2_x * (row1_y * row3_z - row3_y * row1_z) + row3_x * (row1_y * row2_z - row2_y * row1_z); - // Compute the volume of the tetrahedron, and precompute a scalar quantity for re-use in the formula + // Compute the volume of the tetrahedron, and precompute a scalar quantity for reuse in the formula. R128 volume = determinant / R128(6.f); R128 i12volume = R128(1.f) / (volume * R128(12.f)); @@ -149,8 +149,7 @@ class Delaunay3D { R128 center_y = v0_y + i12volume * (-(row2_x * row3_z - row3_x * row2_z) * sq_lenght1 + (row1_x * row3_z - row3_x * row1_z) * sq_lenght2 - (row1_x * row2_z - row2_x * row1_z) * sq_lenght3); R128 center_z = v0_z + i12volume * ((row2_x * row3_y - row3_x * row2_y) * sq_lenght1 - (row1_x * row3_y - row3_x * row1_y) * sq_lenght2 + (row1_x * row2_y - row2_x * row1_y) * sq_lenght3); - //Once we know the center, the radius is clearly the distance to any vertex - + // Once we know the center, the radius is clearly the distance to any vertex. R128 rel1_x = center_x - v0_x; R128 rel1_y = center_y - v0_y; R128 rel1_z = center_z - v0_z; diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 12e6423724..80ca51573c 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -1750,7 +1750,7 @@ Vector<uint8_t> String::hex_decode() const { void String::print_unicode_error(const String &p_message, bool p_critical) const { if (p_critical) { - print_error(vformat("Unicode parsing error, some characters were replaced with � (U+FFFD): %s", p_message)); + print_error(vformat(U"Unicode parsing error, some characters were replaced with � (U+FFFD): %s", p_message)); } else { print_error(vformat("Unicode parsing error: %s", p_message)); } @@ -3635,6 +3635,23 @@ String String::repeat(int p_count) const { return new_string; } +String String::reverse() const { + int len = length(); + if (len <= 1) { + return *this; + } + String new_string; + new_string.resize(len + 1); + + const char32_t *src = ptr(); + char32_t *dst = new_string.ptrw(); + for (int i = 0; i < len; i++) { + dst[i] = src[len - i - 1]; + } + dst[len] = _null; + return new_string; +} + String String::left(int p_len) const { if (p_len < 0) { p_len = length() + p_len; diff --git a/core/string/ustring.h b/core/string/ustring.h index 295625395d..f45392eee1 100644 --- a/core/string/ustring.h +++ b/core/string/ustring.h @@ -305,6 +305,7 @@ public: String replace(const char *p_key, const char *p_with) const; String replacen(const String &p_key, const String &p_with) const; String repeat(int p_count) const; + String reverse() const; String insert(int p_at_pos, const String &p_string) const; String erase(int p_pos, int p_chars = 1) const; String pad_decimals(int p_digits) const; diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index dad9183216..ccf9b82022 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1659,6 +1659,7 @@ static void _register_variant_builtin_methods() { bind_string_methodv(replace, static_cast<String (String::*)(const String &, const String &) const>(&String::replace), sarray("what", "forwhat"), varray()); bind_string_method(replacen, sarray("what", "forwhat"), varray()); bind_string_method(repeat, sarray("count"), varray()); + bind_string_method(reverse, sarray(), varray()); bind_string_method(insert, sarray("position", "what"), varray()); bind_string_method(erase, sarray("position", "chars"), varray(1)); bind_string_method(capitalize, sarray(), varray()); diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp index 545825011a..4f6bcb58b3 100644 --- a/core/variant/variant_utility.cpp +++ b/core/variant/variant_utility.cpp @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#include "variant.h" +#include "variant_utility.h" #include "core/core_string_names.h" #include "core/io/marshalls.h" @@ -40,755 +40,772 @@ #include "core/variant/binder_common.h" #include "core/variant/variant_parser.h" -struct VariantUtilityFunctions { - // Math - - static inline double sin(double arg) { - return Math::sin(arg); - } +// Math +double VariantUtilityFunctions::sin(double arg) { + return Math::sin(arg); +} - static inline double cos(double arg) { - return Math::cos(arg); - } +double VariantUtilityFunctions::cos(double arg) { + return Math::cos(arg); +} - static inline double tan(double arg) { - return Math::tan(arg); - } +double VariantUtilityFunctions::tan(double arg) { + return Math::tan(arg); +} - static inline double sinh(double arg) { - return Math::sinh(arg); - } +double VariantUtilityFunctions::sinh(double arg) { + return Math::sinh(arg); +} - static inline double cosh(double arg) { - return Math::cosh(arg); - } +double VariantUtilityFunctions::cosh(double arg) { + return Math::cosh(arg); +} - static inline double tanh(double arg) { - return Math::tanh(arg); - } +double VariantUtilityFunctions::tanh(double arg) { + return Math::tanh(arg); +} - static inline double asin(double arg) { - return Math::asin(arg); - } +double VariantUtilityFunctions::asin(double arg) { + return Math::asin(arg); +} - static inline double acos(double arg) { - return Math::acos(arg); - } +double VariantUtilityFunctions::acos(double arg) { + return Math::acos(arg); +} - static inline double atan(double arg) { - return Math::atan(arg); - } +double VariantUtilityFunctions::atan(double arg) { + return Math::atan(arg); +} - static inline double atan2(double y, double x) { - return Math::atan2(y, x); - } +double VariantUtilityFunctions::atan2(double y, double x) { + return Math::atan2(y, x); +} - static inline double sqrt(double x) { - return Math::sqrt(x); - } +double VariantUtilityFunctions::sqrt(double x) { + return Math::sqrt(x); +} - static inline double fmod(double b, double r) { - return Math::fmod(b, r); - } +double VariantUtilityFunctions::fmod(double b, double r) { + return Math::fmod(b, r); +} - static inline double fposmod(double b, double r) { - return Math::fposmod(b, r); - } +double VariantUtilityFunctions::fposmod(double b, double r) { + return Math::fposmod(b, r); +} - static inline int64_t posmod(int64_t b, int64_t r) { - return Math::posmod(b, r); - } +int64_t VariantUtilityFunctions::posmod(int64_t b, int64_t r) { + return Math::posmod(b, r); +} - static inline Variant floor(Variant x, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; - switch (x.get_type()) { - case Variant::INT: { - return VariantInternalAccessor<int64_t>::get(&x); - } break; - case Variant::FLOAT: { - return Math::floor(VariantInternalAccessor<double>::get(&x)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor<Vector2>::get(&x).floor(); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor<Vector3>::get(&x).floor(); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor<Vector4>::get(&x).floor(); - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } +Variant VariantUtilityFunctions::floor(Variant x, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + switch (x.get_type()) { + case Variant::INT: { + return VariantInternalAccessor<int64_t>::get(&x); + } break; + case Variant::FLOAT: { + return Math::floor(VariantInternalAccessor<double>::get(&x)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&x).floor(); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&x).floor(); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor<Vector4>::get(&x).floor(); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); } } +} - static inline double floorf(double x) { - return Math::floor(x); - } +double VariantUtilityFunctions::floorf(double x) { + return Math::floor(x); +} - static inline int64_t floori(double x) { - return int64_t(Math::floor(x)); - } +int64_t VariantUtilityFunctions::floori(double x) { + return int64_t(Math::floor(x)); +} - static inline Variant ceil(Variant x, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; - switch (x.get_type()) { - case Variant::INT: { - return VariantInternalAccessor<int64_t>::get(&x); - } break; - case Variant::FLOAT: { - return Math::ceil(VariantInternalAccessor<double>::get(&x)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor<Vector2>::get(&x).ceil(); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor<Vector3>::get(&x).ceil(); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor<Vector4>::get(&x).ceil(); - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } +Variant VariantUtilityFunctions::ceil(Variant x, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + switch (x.get_type()) { + case Variant::INT: { + return VariantInternalAccessor<int64_t>::get(&x); + } break; + case Variant::FLOAT: { + return Math::ceil(VariantInternalAccessor<double>::get(&x)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&x).ceil(); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&x).ceil(); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor<Vector4>::get(&x).ceil(); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); } } +} - static inline double ceilf(double x) { - return Math::ceil(x); - } +double VariantUtilityFunctions::ceilf(double x) { + return Math::ceil(x); +} - static inline int64_t ceili(double x) { - return int64_t(Math::ceil(x)); - } +int64_t VariantUtilityFunctions::ceili(double x) { + return int64_t(Math::ceil(x)); +} - static inline Variant round(Variant x, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; - switch (x.get_type()) { - case Variant::INT: { - return VariantInternalAccessor<int64_t>::get(&x); - } break; - case Variant::FLOAT: { - return Math::round(VariantInternalAccessor<double>::get(&x)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor<Vector2>::get(&x).round(); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor<Vector3>::get(&x).round(); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor<Vector4>::get(&x).round(); - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } +Variant VariantUtilityFunctions::round(Variant x, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + switch (x.get_type()) { + case Variant::INT: { + return VariantInternalAccessor<int64_t>::get(&x); + } break; + case Variant::FLOAT: { + return Math::round(VariantInternalAccessor<double>::get(&x)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&x).round(); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&x).round(); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor<Vector4>::get(&x).round(); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); } } +} - static inline double roundf(double x) { - return Math::round(x); - } +double VariantUtilityFunctions::roundf(double x) { + return Math::round(x); +} - static inline int64_t roundi(double x) { - return int64_t(Math::round(x)); - } +int64_t VariantUtilityFunctions::roundi(double x) { + return int64_t(Math::round(x)); +} - static inline Variant abs(const Variant &x, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; - switch (x.get_type()) { - case Variant::INT: { - return ABS(VariantInternalAccessor<int64_t>::get(&x)); - } break; - case Variant::FLOAT: { - return Math::absd(VariantInternalAccessor<double>::get(&x)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor<Vector2>::get(&x).abs(); - } break; - case Variant::VECTOR2I: { - return VariantInternalAccessor<Vector2i>::get(&x).abs(); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor<Vector3>::get(&x).abs(); - } break; - case Variant::VECTOR3I: { - return VariantInternalAccessor<Vector3i>::get(&x).abs(); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor<Vector4>::get(&x).abs(); - } break; - case Variant::VECTOR4I: { - return VariantInternalAccessor<Vector4i>::get(&x).abs(); - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } +Variant VariantUtilityFunctions::abs(const Variant &x, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + switch (x.get_type()) { + case Variant::INT: { + return ABS(VariantInternalAccessor<int64_t>::get(&x)); + } break; + case Variant::FLOAT: { + return Math::absd(VariantInternalAccessor<double>::get(&x)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&x).abs(); + } break; + case Variant::VECTOR2I: { + return VariantInternalAccessor<Vector2i>::get(&x).abs(); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&x).abs(); + } break; + case Variant::VECTOR3I: { + return VariantInternalAccessor<Vector3i>::get(&x).abs(); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor<Vector4>::get(&x).abs(); + } break; + case Variant::VECTOR4I: { + return VariantInternalAccessor<Vector4i>::get(&x).abs(); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); } } +} - static inline double absf(double x) { - return Math::absd(x); - } +double VariantUtilityFunctions::absf(double x) { + return Math::absd(x); +} - static inline int64_t absi(int64_t x) { - return ABS(x); - } +int64_t VariantUtilityFunctions::absi(int64_t x) { + return ABS(x); +} - static inline Variant sign(const Variant &x, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; - switch (x.get_type()) { - case Variant::INT: { - return SIGN(VariantInternalAccessor<int64_t>::get(&x)); - } break; - case Variant::FLOAT: { - return SIGN(VariantInternalAccessor<double>::get(&x)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor<Vector2>::get(&x).sign(); - } break; - case Variant::VECTOR2I: { - return VariantInternalAccessor<Vector2i>::get(&x).sign(); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor<Vector3>::get(&x).sign(); - } break; - case Variant::VECTOR3I: { - return VariantInternalAccessor<Vector3i>::get(&x).sign(); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor<Vector4>::get(&x).sign(); - } break; - case Variant::VECTOR4I: { - return VariantInternalAccessor<Vector4i>::get(&x).sign(); - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } +Variant VariantUtilityFunctions::sign(const Variant &x, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + switch (x.get_type()) { + case Variant::INT: { + return SIGN(VariantInternalAccessor<int64_t>::get(&x)); + } break; + case Variant::FLOAT: { + return SIGN(VariantInternalAccessor<double>::get(&x)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&x).sign(); + } break; + case Variant::VECTOR2I: { + return VariantInternalAccessor<Vector2i>::get(&x).sign(); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&x).sign(); + } break; + case Variant::VECTOR3I: { + return VariantInternalAccessor<Vector3i>::get(&x).sign(); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor<Vector4>::get(&x).sign(); + } break; + case Variant::VECTOR4I: { + return VariantInternalAccessor<Vector4i>::get(&x).sign(); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); } } +} - static inline double signf(double x) { - return SIGN(x); - } +double VariantUtilityFunctions::signf(double x) { + return SIGN(x); +} - static inline int64_t signi(int64_t x) { - return SIGN(x); - } +int64_t VariantUtilityFunctions::signi(int64_t x) { + return SIGN(x); +} - static inline double pow(double x, double y) { - return Math::pow(x, y); - } +double VariantUtilityFunctions::pow(double x, double y) { + return Math::pow(x, y); +} - static inline double log(double x) { - return Math::log(x); - } +double VariantUtilityFunctions::log(double x) { + return Math::log(x); +} - static inline double exp(double x) { - return Math::exp(x); - } +double VariantUtilityFunctions::exp(double x) { + return Math::exp(x); +} - static inline bool is_nan(double x) { - return Math::is_nan(x); - } +bool VariantUtilityFunctions::is_nan(double x) { + return Math::is_nan(x); +} - static inline bool is_inf(double x) { - return Math::is_inf(x); - } +bool VariantUtilityFunctions::is_inf(double x) { + return Math::is_inf(x); +} - static inline bool is_equal_approx(double x, double y) { - return Math::is_equal_approx(x, y); - } +bool VariantUtilityFunctions::is_equal_approx(double x, double y) { + return Math::is_equal_approx(x, y); +} - static inline bool is_zero_approx(double x) { - return Math::is_zero_approx(x); - } +bool VariantUtilityFunctions::is_zero_approx(double x) { + return Math::is_zero_approx(x); +} - static inline bool is_finite(double x) { - return Math::is_finite(x); - } +bool VariantUtilityFunctions::is_finite(double x) { + return Math::is_finite(x); +} - static inline double ease(float x, float curve) { - return Math::ease(x, curve); - } +double VariantUtilityFunctions::ease(float x, float curve) { + return Math::ease(x, curve); +} - static inline int step_decimals(float step) { - return Math::step_decimals(step); - } +int VariantUtilityFunctions::step_decimals(float step) { + return Math::step_decimals(step); +} - static inline Variant snapped(const Variant &x, const Variant &step, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; - if (x.get_type() != step.get_type() && !((x.get_type() == Variant::INT && step.get_type() == Variant::FLOAT) || (x.get_type() == Variant::FLOAT && step.get_type() == Variant::INT))) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 1; +Variant VariantUtilityFunctions::snapped(const Variant &x, const Variant &step, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + if (x.get_type() != step.get_type() && !((x.get_type() == Variant::INT && step.get_type() == Variant::FLOAT) || (x.get_type() == Variant::FLOAT && step.get_type() == Variant::INT))) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 1; + return Variant(); + } + + switch (step.get_type()) { + case Variant::INT: { + return snappedi(x, VariantInternalAccessor<int64_t>::get(&step)); + } break; + case Variant::FLOAT: { + return snappedf(x, VariantInternalAccessor<double>::get(&step)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&x).snapped(VariantInternalAccessor<Vector2>::get(&step)); + } break; + case Variant::VECTOR2I: { + return VariantInternalAccessor<Vector2i>::get(&x).snapped(VariantInternalAccessor<Vector2i>::get(&step)); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&x).snapped(VariantInternalAccessor<Vector3>::get(&step)); + } break; + case Variant::VECTOR3I: { + return VariantInternalAccessor<Vector3i>::get(&x).snapped(VariantInternalAccessor<Vector3i>::get(&step)); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor<Vector4>::get(&x).snapped(VariantInternalAccessor<Vector4>::get(&step)); + } break; + case Variant::VECTOR4I: { + return VariantInternalAccessor<Vector4i>::get(&x).snapped(VariantInternalAccessor<Vector4i>::get(&step)); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; return Variant(); } - - switch (step.get_type()) { - case Variant::INT: { - return snappedi(x, VariantInternalAccessor<int64_t>::get(&step)); - } break; - case Variant::FLOAT: { - return snappedf(x, VariantInternalAccessor<double>::get(&step)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor<Vector2>::get(&x).snapped(VariantInternalAccessor<Vector2>::get(&step)); - } break; - case Variant::VECTOR2I: { - return VariantInternalAccessor<Vector2i>::get(&x).snapped(VariantInternalAccessor<Vector2i>::get(&step)); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor<Vector3>::get(&x).snapped(VariantInternalAccessor<Vector3>::get(&step)); - } break; - case Variant::VECTOR3I: { - return VariantInternalAccessor<Vector3i>::get(&x).snapped(VariantInternalAccessor<Vector3i>::get(&step)); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor<Vector4>::get(&x).snapped(VariantInternalAccessor<Vector4>::get(&step)); - } break; - case Variant::VECTOR4I: { - return VariantInternalAccessor<Vector4i>::get(&x).snapped(VariantInternalAccessor<Vector4i>::get(&step)); - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } - } } +} - static inline double snappedf(double x, double step) { - return Math::snapped(x, step); - } +double VariantUtilityFunctions::snappedf(double x, double step) { + return Math::snapped(x, step); +} - static inline int64_t snappedi(double x, int64_t step) { - return Math::snapped(x, step); - } +int64_t VariantUtilityFunctions::snappedi(double x, int64_t step) { + return Math::snapped(x, step); +} - static inline Variant lerp(const Variant &from, const Variant &to, double weight, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; - if (from.get_type() != to.get_type()) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = from.get_type(); - r_error.argument = 1; +Variant VariantUtilityFunctions::lerp(const Variant &from, const Variant &to, double weight, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + if (from.get_type() != to.get_type()) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.expected = from.get_type(); + r_error.argument = 1; + return Variant(); + } + + switch (from.get_type()) { + case Variant::INT: { + return lerpf(VariantInternalAccessor<int64_t>::get(&from), to, weight); + } break; + case Variant::FLOAT: { + return lerpf(VariantInternalAccessor<double>::get(&from), to, weight); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&from).lerp(VariantInternalAccessor<Vector2>::get(&to), weight); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&from).lerp(VariantInternalAccessor<Vector3>::get(&to), weight); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor<Vector4>::get(&from).lerp(VariantInternalAccessor<Vector4>::get(&to), weight); + } break; + case Variant::QUATERNION: { + return VariantInternalAccessor<Quaternion>::get(&from).slerp(VariantInternalAccessor<Quaternion>::get(&to), weight); + } break; + case Variant::BASIS: { + return VariantInternalAccessor<Basis>::get(&from).slerp(VariantInternalAccessor<Basis>::get(&to), weight); + } break; + case Variant::COLOR: { + return VariantInternalAccessor<Color>::get(&from).lerp(VariantInternalAccessor<Color>::get(&to), weight); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; return Variant(); } - - switch (from.get_type()) { - case Variant::INT: { - return lerpf(VariantInternalAccessor<int64_t>::get(&from), to, weight); - } break; - case Variant::FLOAT: { - return lerpf(VariantInternalAccessor<double>::get(&from), to, weight); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor<Vector2>::get(&from).lerp(VariantInternalAccessor<Vector2>::get(&to), weight); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor<Vector3>::get(&from).lerp(VariantInternalAccessor<Vector3>::get(&to), weight); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor<Vector4>::get(&from).lerp(VariantInternalAccessor<Vector4>::get(&to), weight); - } break; - case Variant::QUATERNION: { - return VariantInternalAccessor<Quaternion>::get(&from).slerp(VariantInternalAccessor<Quaternion>::get(&to), weight); - } break; - case Variant::BASIS: { - return VariantInternalAccessor<Basis>::get(&from).slerp(VariantInternalAccessor<Basis>::get(&to), weight); - } break; - case Variant::COLOR: { - return VariantInternalAccessor<Color>::get(&from).lerp(VariantInternalAccessor<Color>::get(&to), weight); - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } - } } +} - static inline double lerpf(double from, double to, double weight) { - return Math::lerp(from, to, weight); - } +double VariantUtilityFunctions::lerpf(double from, double to, double weight) { + return Math::lerp(from, to, weight); +} - static inline double cubic_interpolate(double from, double to, double pre, double post, double weight) { - return Math::cubic_interpolate(from, to, pre, post, weight); - } +double VariantUtilityFunctions::cubic_interpolate(double from, double to, double pre, double post, double weight) { + return Math::cubic_interpolate(from, to, pre, post, weight); +} - static inline double cubic_interpolate_angle(double from, double to, double pre, double post, double weight) { - return Math::cubic_interpolate_angle(from, to, pre, post, weight); - } +double VariantUtilityFunctions::cubic_interpolate_angle(double from, double to, double pre, double post, double weight) { + return Math::cubic_interpolate_angle(from, to, pre, post, weight); +} - static inline double cubic_interpolate_in_time(double from, double to, double pre, double post, double weight, - double to_t, double pre_t, double post_t) { - return Math::cubic_interpolate_in_time(from, to, pre, post, weight, to_t, pre_t, post_t); - } +double VariantUtilityFunctions::cubic_interpolate_in_time(double from, double to, double pre, double post, double weight, + double to_t, double pre_t, double post_t) { + return Math::cubic_interpolate_in_time(from, to, pre, post, weight, to_t, pre_t, post_t); +} - static inline double cubic_interpolate_angle_in_time(double from, double to, double pre, double post, double weight, - double to_t, double pre_t, double post_t) { - return Math::cubic_interpolate_angle_in_time(from, to, pre, post, weight, to_t, pre_t, post_t); - } +double VariantUtilityFunctions::cubic_interpolate_angle_in_time(double from, double to, double pre, double post, double weight, + double to_t, double pre_t, double post_t) { + return Math::cubic_interpolate_angle_in_time(from, to, pre, post, weight, to_t, pre_t, post_t); +} - static inline double bezier_interpolate(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) { - return Math::bezier_interpolate(p_start, p_control_1, p_control_2, p_end, p_t); - } +double VariantUtilityFunctions::bezier_interpolate(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) { + return Math::bezier_interpolate(p_start, p_control_1, p_control_2, p_end, p_t); +} - static inline double bezier_derivative(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) { - return Math::bezier_derivative(p_start, p_control_1, p_control_2, p_end, p_t); - } +double VariantUtilityFunctions::bezier_derivative(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) { + return Math::bezier_derivative(p_start, p_control_1, p_control_2, p_end, p_t); +} - static inline double lerp_angle(double from, double to, double weight) { - return Math::lerp_angle(from, to, weight); - } +double VariantUtilityFunctions::lerp_angle(double from, double to, double weight) { + return Math::lerp_angle(from, to, weight); +} - static inline double inverse_lerp(double from, double to, double weight) { - return Math::inverse_lerp(from, to, weight); - } +double VariantUtilityFunctions::inverse_lerp(double from, double to, double weight) { + return Math::inverse_lerp(from, to, weight); +} - static inline double remap(double value, double istart, double istop, double ostart, double ostop) { - return Math::remap(value, istart, istop, ostart, ostop); - } +double VariantUtilityFunctions::remap(double value, double istart, double istop, double ostart, double ostop) { + return Math::remap(value, istart, istop, ostart, ostop); +} - static inline double smoothstep(double from, double to, double val) { - return Math::smoothstep(from, to, val); - } +double VariantUtilityFunctions::smoothstep(double from, double to, double val) { + return Math::smoothstep(from, to, val); +} - static inline double move_toward(double from, double to, double delta) { - return Math::move_toward(from, to, delta); - } +double VariantUtilityFunctions::move_toward(double from, double to, double delta) { + return Math::move_toward(from, to, delta); +} - static inline double deg_to_rad(double angle_deg) { - return Math::deg_to_rad(angle_deg); - } +double VariantUtilityFunctions::deg_to_rad(double angle_deg) { + return Math::deg_to_rad(angle_deg); +} - static inline double rad_to_deg(double angle_rad) { - return Math::rad_to_deg(angle_rad); - } +double VariantUtilityFunctions::rad_to_deg(double angle_rad) { + return Math::rad_to_deg(angle_rad); +} - static inline double linear_to_db(double linear) { - return Math::linear_to_db(linear); - } +double VariantUtilityFunctions::linear_to_db(double linear) { + return Math::linear_to_db(linear); +} + +double VariantUtilityFunctions::db_to_linear(double db) { + return Math::db_to_linear(db); +} - static inline double db_to_linear(double db) { - return Math::db_to_linear(db); +Variant VariantUtilityFunctions::wrap(const Variant &p_x, const Variant &p_min, const Variant &p_max, Callable::CallError &r_error) { + Variant::Type x_type = p_x.get_type(); + if (x_type != Variant::INT && x_type != Variant::FLOAT) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = x_type; + return Variant(); } - static inline Variant wrap(const Variant &p_x, const Variant &p_min, const Variant &p_max, Callable::CallError &r_error) { - Variant::Type x_type = p_x.get_type(); - if (x_type != Variant::INT && x_type != Variant::FLOAT) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = x_type; - return Variant(); - } + Variant::Type min_type = p_min.get_type(); + if (min_type != Variant::INT && min_type != Variant::FLOAT) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 1; + r_error.expected = x_type; + return Variant(); + } - Variant::Type min_type = p_min.get_type(); - if (min_type != Variant::INT && min_type != Variant::FLOAT) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 1; - r_error.expected = x_type; - return Variant(); - } + Variant::Type max_type = p_max.get_type(); + if (max_type != Variant::INT && max_type != Variant::FLOAT) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 2; + r_error.expected = x_type; + return Variant(); + } - Variant::Type max_type = p_max.get_type(); - if (max_type != Variant::INT && max_type != Variant::FLOAT) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 2; - r_error.expected = x_type; - return Variant(); - } + Variant value; - Variant value; - - switch (x_type) { - case Variant::INT: { - if (x_type != min_type || x_type != max_type) { - value = wrapf((double)p_x, (double)p_min, (double)p_max); - } else { - value = wrapi((int)p_x, (int)p_min, (int)p_max); - } - } break; - case Variant::FLOAT: { + switch (x_type) { + case Variant::INT: { + if (x_type != min_type || x_type != max_type) { value = wrapf((double)p_x, (double)p_min, (double)p_max); - } break; - default: - break; - } - - r_error.error = Callable::CallError::CALL_OK; - return value; + } else { + value = wrapi((int)p_x, (int)p_min, (int)p_max); + } + } break; + case Variant::FLOAT: { + value = wrapf((double)p_x, (double)p_min, (double)p_max); + } break; + default: + break; } - static inline int64_t wrapi(int64_t value, int64_t min, int64_t max) { - return Math::wrapi(value, min, max); - } + r_error.error = Callable::CallError::CALL_OK; + return value; +} - static inline double wrapf(double value, double min, double max) { - return Math::wrapf(value, min, max); - } +int64_t VariantUtilityFunctions::wrapi(int64_t value, int64_t min, int64_t max) { + return Math::wrapi(value, min, max); +} - static inline double pingpong(double value, double length) { - return Math::pingpong(value, length); +double VariantUtilityFunctions::wrapf(double value, double min, double max) { + return Math::wrapf(value, min, max); +} + +double VariantUtilityFunctions::pingpong(double value, double length) { + return Math::pingpong(value, length); +} + +Variant VariantUtilityFunctions::max(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { + if (p_argcount < 2) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.expected = 2; + return Variant(); } + Variant base = *p_args[0]; + Variant ret; - static inline Variant max(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - if (p_argcount < 2) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.expected = 2; + for (int i = 0; i < p_argcount; i++) { + Variant::Type arg_type = p_args[i]->get_type(); + if (arg_type != Variant::INT && arg_type != Variant::FLOAT) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.expected = Variant::FLOAT; + r_error.argument = i; return Variant(); } - Variant base = *p_args[0]; - Variant ret; - - for (int i = 0; i < p_argcount; i++) { - Variant::Type arg_type = p_args[i]->get_type(); - if (arg_type != Variant::INT && arg_type != Variant::FLOAT) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = Variant::FLOAT; - r_error.argument = i; - return Variant(); - } - if (i == 0) { - continue; - } - bool valid; - Variant::evaluate(Variant::OP_LESS, base, *p_args[i], ret, valid); - if (!valid) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = base.get_type(); - r_error.argument = i; - return Variant(); - } - if (ret.booleanize()) { - base = *p_args[i]; - } + if (i == 0) { + continue; } - r_error.error = Callable::CallError::CALL_OK; - return base; - } - - static inline double maxf(double x, double y) { - return MAX(x, y); - } - - static inline int64_t maxi(int64_t x, int64_t y) { - return MAX(x, y); - } - - static inline Variant min(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - if (p_argcount < 2) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.expected = 2; + bool valid; + Variant::evaluate(Variant::OP_LESS, base, *p_args[i], ret, valid); + if (!valid) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.expected = base.get_type(); + r_error.argument = i; return Variant(); } - Variant base = *p_args[0]; - Variant ret; - - for (int i = 0; i < p_argcount; i++) { - Variant::Type arg_type = p_args[i]->get_type(); - if (arg_type != Variant::INT && arg_type != Variant::FLOAT) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = Variant::FLOAT; - r_error.argument = i; - return Variant(); - } - if (i == 0) { - continue; - } - bool valid; - Variant::evaluate(Variant::OP_GREATER, base, *p_args[i], ret, valid); - if (!valid) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = base.get_type(); - r_error.argument = i; - return Variant(); - } - if (ret.booleanize()) { - base = *p_args[i]; - } + if (ret.booleanize()) { + base = *p_args[i]; } - r_error.error = Callable::CallError::CALL_OK; - return base; - } - - static inline double minf(double x, double y) { - return MIN(x, y); } + r_error.error = Callable::CallError::CALL_OK; + return base; +} - static inline int64_t mini(int64_t x, int64_t y) { - return MIN(x, y); - } +double VariantUtilityFunctions::maxf(double x, double y) { + return MAX(x, y); +} - static inline Variant clamp(const Variant &x, const Variant &min, const Variant &max, Callable::CallError &r_error) { - Variant value = x; +int64_t VariantUtilityFunctions::maxi(int64_t x, int64_t y) { + return MAX(x, y); +} - Variant ret; +Variant VariantUtilityFunctions::min(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { + if (p_argcount < 2) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.expected = 2; + return Variant(); + } + Variant base = *p_args[0]; + Variant ret; - bool valid; - Variant::evaluate(Variant::OP_LESS, value, min, ret, valid); - if (!valid) { + for (int i = 0; i < p_argcount; i++) { + Variant::Type arg_type = p_args[i]->get_type(); + if (arg_type != Variant::INT && arg_type != Variant::FLOAT) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = value.get_type(); - r_error.argument = 1; + r_error.expected = Variant::FLOAT; + r_error.argument = i; return Variant(); } - if (ret.booleanize()) { - value = min; + if (i == 0) { + continue; } - Variant::evaluate(Variant::OP_GREATER, value, max, ret, valid); + bool valid; + Variant::evaluate(Variant::OP_GREATER, base, *p_args[i], ret, valid); if (!valid) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = value.get_type(); - r_error.argument = 2; + r_error.expected = base.get_type(); + r_error.argument = i; return Variant(); } if (ret.booleanize()) { - value = max; + base = *p_args[i]; } + } + r_error.error = Callable::CallError::CALL_OK; + return base; +} - r_error.error = Callable::CallError::CALL_OK; +double VariantUtilityFunctions::minf(double x, double y) { + return MIN(x, y); +} - return value; - } +int64_t VariantUtilityFunctions::mini(int64_t x, int64_t y) { + return MIN(x, y); +} - static inline double clampf(double x, double min, double max) { - return CLAMP(x, min, max); - } +Variant VariantUtilityFunctions::clamp(const Variant &x, const Variant &min, const Variant &max, Callable::CallError &r_error) { + Variant value = x; - static inline int64_t clampi(int64_t x, int64_t min, int64_t max) { - return CLAMP(x, min, max); - } + Variant ret; - static inline int64_t nearest_po2(int64_t x) { - return nearest_power_of_2_templated(uint64_t(x)); + bool valid; + Variant::evaluate(Variant::OP_LESS, value, min, ret, valid); + if (!valid) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.expected = value.get_type(); + r_error.argument = 1; + return Variant(); + } + if (ret.booleanize()) { + value = min; + } + Variant::evaluate(Variant::OP_GREATER, value, max, ret, valid); + if (!valid) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.expected = value.get_type(); + r_error.argument = 2; + return Variant(); + } + if (ret.booleanize()) { + value = max; } - // Random + r_error.error = Callable::CallError::CALL_OK; - static inline void randomize() { - Math::randomize(); - } + return value; +} - static inline int64_t randi() { - return Math::rand(); - } +double VariantUtilityFunctions::clampf(double x, double min, double max) { + return CLAMP(x, min, max); +} - static inline double randf() { - return Math::randf(); - } +int64_t VariantUtilityFunctions::clampi(int64_t x, int64_t min, int64_t max) { + return CLAMP(x, min, max); +} - static inline double randfn(double mean, double deviation) { - return Math::randfn(mean, deviation); - } +int64_t VariantUtilityFunctions::nearest_po2(int64_t x) { + return nearest_power_of_2_templated(uint64_t(x)); +} - static inline int64_t randi_range(int64_t from, int64_t to) { - return Math::random((int32_t)from, (int32_t)to); - } +// Random - static inline double randf_range(double from, double to) { - return Math::random(from, to); - } +void VariantUtilityFunctions::randomize() { + Math::randomize(); +} - static inline void seed(int64_t s) { - return Math::seed(s); - } +int64_t VariantUtilityFunctions::randi() { + return Math::rand(); +} - static inline PackedInt64Array rand_from_seed(int64_t seed) { - uint64_t s = seed; - PackedInt64Array arr; - arr.resize(2); - arr.write[0] = Math::rand_from_seed(&s); - arr.write[1] = s; - return arr; - } +double VariantUtilityFunctions::randf() { + return Math::randf(); +} - // Utility +double VariantUtilityFunctions::randfn(double mean, double deviation) { + return Math::randfn(mean, deviation); +} - static inline Variant weakref(const Variant &obj, Callable::CallError &r_error) { - if (obj.get_type() == Variant::OBJECT) { - r_error.error = Callable::CallError::CALL_OK; - if (obj.is_ref_counted()) { - Ref<WeakRef> wref = memnew(WeakRef); - Ref<RefCounted> r = obj; - if (r.is_valid()) { - wref->set_ref(r); - } - return wref; - } else { - Ref<WeakRef> wref = memnew(WeakRef); - Object *o = obj.get_validated_object(); - if (o) { - wref->set_obj(o); - } - return wref; - } - } else if (obj.get_type() == Variant::NIL) { - r_error.error = Callable::CallError::CALL_OK; +int64_t VariantUtilityFunctions::randi_range(int64_t from, int64_t to) { + return Math::random((int32_t)from, (int32_t)to); +} + +double VariantUtilityFunctions::randf_range(double from, double to) { + return Math::random(from, to); +} + +void VariantUtilityFunctions::seed(int64_t s) { + return Math::seed(s); +} + +PackedInt64Array VariantUtilityFunctions::rand_from_seed(int64_t seed) { + uint64_t s = seed; + PackedInt64Array arr; + arr.resize(2); + arr.write[0] = Math::rand_from_seed(&s); + arr.write[1] = s; + return arr; +} + +// Utility + +Variant VariantUtilityFunctions::weakref(const Variant &obj, Callable::CallError &r_error) { + if (obj.get_type() == Variant::OBJECT) { + r_error.error = Callable::CallError::CALL_OK; + if (obj.is_ref_counted()) { Ref<WeakRef> wref = memnew(WeakRef); + Ref<RefCounted> r = obj; + if (r.is_valid()) { + wref->set_ref(r); + } return wref; } else { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::OBJECT; - return Variant(); + Ref<WeakRef> wref = memnew(WeakRef); + Object *o = obj.get_validated_object(); + if (o) { + wref->set_obj(o); + } + return wref; } + } else if (obj.get_type() == Variant::NIL) { + r_error.error = Callable::CallError::CALL_OK; + Ref<WeakRef> wref = memnew(WeakRef); + return wref; + } else { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::OBJECT; + return Variant(); } +} - static inline int64_t _typeof(const Variant &obj) { - return obj.get_type(); +int64_t VariantUtilityFunctions::_typeof(const Variant &obj) { + return obj.get_type(); +} + +String VariantUtilityFunctions::str(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + if (p_arg_count < 1) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; + return String(); } + String s; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); - static inline String str(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - if (p_arg_count < 1) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 1; - return String(); + if (i == 0) { + s = os; + } else { + s += os; } - String s; - for (int i = 0; i < p_arg_count; i++) { - String os = p_args[i]->operator String(); + } - if (i == 0) { - s = os; - } else { - s += os; - } - } + r_error.error = Callable::CallError::CALL_OK; - r_error.error = Callable::CallError::CALL_OK; + return s; +} - return s; +String VariantUtilityFunctions::error_string(Error error) { + if (error < 0 || error >= ERR_MAX) { + return String("(invalid error code)"); } - static inline String error_string(Error error) { - if (error < 0 || error >= ERR_MAX) { - return String("(invalid error code)"); - } + return String(error_names[error]); +} - return String(error_names[error]); +void VariantUtilityFunctions::print(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + String s; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); + + if (i == 0) { + s = os; + } else { + s += os; + } } - static inline void print(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - String s; - for (int i = 0; i < p_arg_count; i++) { - String os = p_args[i]->operator String(); + print_line(s); + r_error.error = Callable::CallError::CALL_OK; +} - if (i == 0) { - s = os; - } else { - s += os; - } - } +void VariantUtilityFunctions::print_rich(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + String s; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); - print_line(s); - r_error.error = Callable::CallError::CALL_OK; + if (i == 0) { + s = os; + } else { + s += os; + } } - static inline void print_rich(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + print_line_rich(s); + r_error.error = Callable::CallError::CALL_OK; +} + +#undef print_verbose + +void VariantUtilityFunctions::print_verbose(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + if (OS::get_singleton()->is_stdout_verbose()) { String s; for (int i = 0; i < p_arg_count; i++) { String os = p_args[i]->operator String(); @@ -800,247 +817,227 @@ struct VariantUtilityFunctions { } } - print_line_rich(s); - r_error.error = Callable::CallError::CALL_OK; + // No need to use `print_verbose()` as this call already only happens + // when verbose mode is enabled. This avoids performing string argument concatenation + // when not needed. + print_line(s); } -#undef print_verbose + r_error.error = Callable::CallError::CALL_OK; +} - static inline void print_verbose(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - if (OS::get_singleton()->is_stdout_verbose()) { - String s; - for (int i = 0; i < p_arg_count; i++) { - String os = p_args[i]->operator String(); - - if (i == 0) { - s = os; - } else { - s += os; - } - } +void VariantUtilityFunctions::printerr(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + String s; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); - // No need to use `print_verbose()` as this call already only happens - // when verbose mode is enabled. This avoids performing string argument concatenation - // when not needed. - print_line(s); + if (i == 0) { + s = os; + } else { + s += os; } - - r_error.error = Callable::CallError::CALL_OK; } - static inline void printerr(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - String s; - for (int i = 0; i < p_arg_count; i++) { - String os = p_args[i]->operator String(); + print_error(s); + r_error.error = Callable::CallError::CALL_OK; +} - if (i == 0) { - s = os; - } else { - s += os; - } +void VariantUtilityFunctions::printt(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + String s; + for (int i = 0; i < p_arg_count; i++) { + if (i) { + s += "\t"; } - - print_error(s); - r_error.error = Callable::CallError::CALL_OK; + s += p_args[i]->operator String(); } - static inline void printt(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - String s; - for (int i = 0; i < p_arg_count; i++) { - if (i) { - s += "\t"; - } - s += p_args[i]->operator String(); - } - - print_line(s); - r_error.error = Callable::CallError::CALL_OK; - } + print_line(s); + r_error.error = Callable::CallError::CALL_OK; +} - static inline void prints(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - String s; - for (int i = 0; i < p_arg_count; i++) { - if (i) { - s += " "; - } - s += p_args[i]->operator String(); +void VariantUtilityFunctions::prints(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + String s; + for (int i = 0; i < p_arg_count; i++) { + if (i) { + s += " "; } - - print_line(s); - r_error.error = Callable::CallError::CALL_OK; + s += p_args[i]->operator String(); } - static inline void printraw(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - String s; - for (int i = 0; i < p_arg_count; i++) { - String os = p_args[i]->operator String(); + print_line(s); + r_error.error = Callable::CallError::CALL_OK; +} - if (i == 0) { - s = os; - } else { - s += os; - } +void VariantUtilityFunctions::printraw(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + String s; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); + + if (i == 0) { + s = os; + } else { + s += os; } + } - OS::get_singleton()->print("%s", s.utf8().get_data()); - r_error.error = Callable::CallError::CALL_OK; + OS::get_singleton()->print("%s", s.utf8().get_data()); + r_error.error = Callable::CallError::CALL_OK; +} + +void VariantUtilityFunctions::push_error(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + if (p_arg_count < 1) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; } + String s; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); - static inline void push_error(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - if (p_arg_count < 1) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 1; + if (i == 0) { + s = os; + } else { + s += os; } - String s; - for (int i = 0; i < p_arg_count; i++) { - String os = p_args[i]->operator String(); + } - if (i == 0) { - s = os; - } else { - s += os; - } - } + ERR_PRINT(s); + r_error.error = Callable::CallError::CALL_OK; +} - ERR_PRINT(s); - r_error.error = Callable::CallError::CALL_OK; +void VariantUtilityFunctions::push_warning(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + if (p_arg_count < 1) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; } + String s; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); - static inline void push_warning(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - if (p_arg_count < 1) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 1; + if (i == 0) { + s = os; + } else { + s += os; } - String s; - for (int i = 0; i < p_arg_count; i++) { - String os = p_args[i]->operator String(); + } - if (i == 0) { - s = os; - } else { - s += os; - } - } + WARN_PRINT(s); + r_error.error = Callable::CallError::CALL_OK; +} - WARN_PRINT(s); - r_error.error = Callable::CallError::CALL_OK; - } +String VariantUtilityFunctions::var_to_str(const Variant &p_var) { + String vars; + VariantWriter::write_to_string(p_var, vars); + return vars; +} - static inline String var_to_str(const Variant &p_var) { - String vars; - VariantWriter::write_to_string(p_var, vars); - return vars; - } +Variant VariantUtilityFunctions::str_to_var(const String &p_var) { + VariantParser::StreamString ss; + ss.s = p_var; - static inline Variant str_to_var(const String &p_var) { - VariantParser::StreamString ss; - ss.s = p_var; + String errs; + int line; + Variant ret; + (void)VariantParser::parse(&ss, ret, errs, line); - String errs; - int line; - Variant ret; - (void)VariantParser::parse(&ss, ret, errs, line); + return ret; +} - return ret; +PackedByteArray VariantUtilityFunctions::var_to_bytes(const Variant &p_var) { + int len; + Error err = encode_variant(p_var, nullptr, len, false); + if (err != OK) { + return PackedByteArray(); } - static inline PackedByteArray var_to_bytes(const Variant &p_var) { - int len; - Error err = encode_variant(p_var, nullptr, len, false); + PackedByteArray barr; + barr.resize(len); + { + uint8_t *w = barr.ptrw(); + err = encode_variant(p_var, w, len, false); if (err != OK) { return PackedByteArray(); } + } - PackedByteArray barr; - barr.resize(len); - { - uint8_t *w = barr.ptrw(); - err = encode_variant(p_var, w, len, false); - if (err != OK) { - return PackedByteArray(); - } - } + return barr; +} - return barr; +PackedByteArray VariantUtilityFunctions::var_to_bytes_with_objects(const Variant &p_var) { + int len; + Error err = encode_variant(p_var, nullptr, len, true); + if (err != OK) { + return PackedByteArray(); } - static inline PackedByteArray var_to_bytes_with_objects(const Variant &p_var) { - int len; - Error err = encode_variant(p_var, nullptr, len, true); + PackedByteArray barr; + barr.resize(len); + { + uint8_t *w = barr.ptrw(); + err = encode_variant(p_var, w, len, true); if (err != OK) { return PackedByteArray(); } - - PackedByteArray barr; - barr.resize(len); - { - uint8_t *w = barr.ptrw(); - err = encode_variant(p_var, w, len, true); - if (err != OK) { - return PackedByteArray(); - } - } - - return barr; } - static inline Variant bytes_to_var(const PackedByteArray &p_arr) { - Variant ret; - { - const uint8_t *r = p_arr.ptr(); - Error err = decode_variant(ret, r, p_arr.size(), nullptr, false); - if (err != OK) { - return Variant(); - } + return barr; +} + +Variant VariantUtilityFunctions::bytes_to_var(const PackedByteArray &p_arr) { + Variant ret; + { + const uint8_t *r = p_arr.ptr(); + Error err = decode_variant(ret, r, p_arr.size(), nullptr, false); + if (err != OK) { + return Variant(); } - return ret; } + return ret; +} - static inline Variant bytes_to_var_with_objects(const PackedByteArray &p_arr) { - Variant ret; - { - const uint8_t *r = p_arr.ptr(); - Error err = decode_variant(ret, r, p_arr.size(), nullptr, true); - if (err != OK) { - return Variant(); - } +Variant VariantUtilityFunctions::bytes_to_var_with_objects(const PackedByteArray &p_arr) { + Variant ret; + { + const uint8_t *r = p_arr.ptr(); + Error err = decode_variant(ret, r, p_arr.size(), nullptr, true); + if (err != OK) { + return Variant(); } - return ret; } + return ret; +} - static inline int64_t hash(const Variant &p_arr) { - return p_arr.hash(); - } +int64_t VariantUtilityFunctions::hash(const Variant &p_arr) { + return p_arr.hash(); +} - static inline Object *instance_from_id(int64_t p_id) { - ObjectID id = ObjectID((uint64_t)p_id); - Object *ret = ObjectDB::get_instance(id); - return ret; - } +Object *VariantUtilityFunctions::instance_from_id(int64_t p_id) { + ObjectID id = ObjectID((uint64_t)p_id); + Object *ret = ObjectDB::get_instance(id); + return ret; +} - static inline bool is_instance_id_valid(int64_t p_id) { - return ObjectDB::get_instance(ObjectID((uint64_t)p_id)) != nullptr; - } +bool VariantUtilityFunctions::is_instance_id_valid(int64_t p_id) { + return ObjectDB::get_instance(ObjectID((uint64_t)p_id)) != nullptr; +} - static inline bool is_instance_valid(const Variant &p_instance) { - if (p_instance.get_type() != Variant::OBJECT) { - return false; - } - return p_instance.get_validated_object() != nullptr; +bool VariantUtilityFunctions::is_instance_valid(const Variant &p_instance) { + if (p_instance.get_type() != Variant::OBJECT) { + return false; } + return p_instance.get_validated_object() != nullptr; +} - static inline uint64_t rid_allocate_id() { - return RID_AllocBase::_gen_id(); - } +uint64_t VariantUtilityFunctions::rid_allocate_id() { + return RID_AllocBase::_gen_id(); +} - static inline RID rid_from_int64(uint64_t p_base) { - return RID::from_uint64(p_base); - } +RID VariantUtilityFunctions::rid_from_int64(uint64_t p_base) { + return RID::from_uint64(p_base); +} - static inline bool is_same(const Variant &p_a, const Variant &p_b) { - return p_a.identity_compare(p_b); - } -}; +bool VariantUtilityFunctions::is_same(const Variant &p_a, const Variant &p_b) { + return p_a.identity_compare(p_b); +} #ifdef DEBUG_METHODS_ENABLED #define VCALLR *ret = p_func(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...) diff --git a/core/variant/variant_utility.h b/core/variant/variant_utility.h new file mode 100644 index 0000000000..78f66987cb --- /dev/null +++ b/core/variant/variant_utility.h @@ -0,0 +1,153 @@ +/**************************************************************************/ +/* variant_utility.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 VARIANT_UTILITY_H +#define VARIANT_UTILITY_H + +#include "variant.h" + +struct VariantUtilityFunctions { + // Math + static double sin(double arg); + static double cos(double arg); + static double tan(double arg); + static double sinh(double arg); + static double cosh(double arg); + static double tanh(double arg); + static double asin(double arg); + static double acos(double arg); + static double atan(double arg); + static double atan2(double y, double x); + static double sqrt(double x); + static double fmod(double b, double r); + static double fposmod(double b, double r); + static int64_t posmod(int64_t b, int64_t r); + static Variant floor(Variant x, Callable::CallError &r_error); + static double floorf(double x); + static int64_t floori(double x); + static Variant ceil(Variant x, Callable::CallError &r_error); + static double ceilf(double x); + static int64_t ceili(double x); + static Variant round(Variant x, Callable::CallError &r_error); + static double roundf(double x); + static int64_t roundi(double x); + static Variant abs(const Variant &x, Callable::CallError &r_error); + static double absf(double x); + static int64_t absi(int64_t x); + static Variant sign(const Variant &x, Callable::CallError &r_error); + static double signf(double x); + static int64_t signi(int64_t x); + static double pow(double x, double y); + static double log(double x); + static double exp(double x); + static bool is_nan(double x); + static bool is_inf(double x); + static bool is_equal_approx(double x, double y); + static bool is_zero_approx(double x); + static bool is_finite(double x); + static double ease(float x, float curve); + static int step_decimals(float step); + static Variant snapped(const Variant &x, const Variant &step, Callable::CallError &r_error); + static double snappedf(double x, double step); + static int64_t snappedi(double x, int64_t step); + static Variant lerp(const Variant &from, const Variant &to, double weight, Callable::CallError &r_error); + static double lerpf(double from, double to, double weight); + static double cubic_interpolate(double from, double to, double pre, double post, double weight); + static double cubic_interpolate_angle(double from, double to, double pre, double post, double weight); + static double cubic_interpolate_in_time(double from, double to, double pre, double post, double weight, + double to_t, double pre_t, double post_t); + static double cubic_interpolate_angle_in_time(double from, double to, double pre, double post, double weight, + double to_t, double pre_t, double post_t); + static double bezier_interpolate(double p_start, double p_control_1, double p_control_2, double p_end, double p_t); + static double bezier_derivative(double p_start, double p_control_1, double p_control_2, double p_end, double p_t); + static double lerp_angle(double from, double to, double weight); + static double inverse_lerp(double from, double to, double weight); + static double remap(double value, double istart, double istop, double ostart, double ostop); + static double smoothstep(double from, double to, double val); + static double move_toward(double from, double to, double delta); + static double deg_to_rad(double angle_deg); + static double rad_to_deg(double angle_rad); + static double linear_to_db(double linear); + static double db_to_linear(double db); + static Variant wrap(const Variant &p_x, const Variant &p_min, const Variant &p_max, Callable::CallError &r_error); + static int64_t wrapi(int64_t value, int64_t min, int64_t max); + static double wrapf(double value, double min, double max); + static double pingpong(double value, double length); + static Variant max(const Variant **p_args, int p_argcount, Callable::CallError &r_error); + static double maxf(double x, double y); + static int64_t maxi(int64_t x, int64_t y); + static Variant min(const Variant **p_args, int p_argcount, Callable::CallError &r_error); + static double minf(double x, double y); + static int64_t mini(int64_t x, int64_t y); + static Variant clamp(const Variant &x, const Variant &min, const Variant &max, Callable::CallError &r_error); + static double clampf(double x, double min, double max); + static int64_t clampi(int64_t x, int64_t min, int64_t max); + static int64_t nearest_po2(int64_t x); + // Random + static void randomize(); + static int64_t randi(); + static double randf(); + static double randfn(double mean, double deviation); + static int64_t randi_range(int64_t from, int64_t to); + static double randf_range(double from, double to); + static void seed(int64_t s); + static PackedInt64Array rand_from_seed(int64_t seed); + // Utility + static Variant weakref(const Variant &obj, Callable::CallError &r_error); + static int64_t _typeof(const Variant &obj); + static Variant type_convert(const Variant &p_variant, const Variant::Type p_type); + static String str(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static String error_string(Error error); + static void print(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static void print_rich(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); +#undef print_verbose + static void print_verbose(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static void printerr(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static void printt(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static void prints(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static void printraw(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static void push_error(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static void push_warning(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static String var_to_str(const Variant &p_var); + static Variant str_to_var(const String &p_var); + static PackedByteArray var_to_bytes(const Variant &p_var); + static PackedByteArray var_to_bytes_with_objects(const Variant &p_var); + static Variant bytes_to_var(const PackedByteArray &p_arr); + static Variant bytes_to_var_with_objects(const PackedByteArray &p_arr); + static int64_t hash(const Variant &p_arr); + static Object *instance_from_id(int64_t p_id); + static bool is_instance_id_valid(int64_t p_id); + static bool is_instance_valid(const Variant &p_instance); + static uint64_t rid_allocate_id(); + static RID rid_from_int64(uint64_t p_base); + static bool is_same(const Variant &p_a, const Variant &p_b); +}; + +#endif // VARIANT_UTILITY_H diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 3b7974f523..e44fdd9776 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -739,8 +739,7 @@ <return type="int" /> <param index="0" name="value" type="int" /> <description> - Returns the nearest equal or larger power of 2 for the integer [param value]. - In other words, returns the smallest value [code]a[/code] where [code]a = pow(2, n)[/code] such that [code]value <= a[/code] for some non-negative integer [code]n[/code]. + Returns the smallest integer power of 2 that is greater than or equal to [param value]. [codeblock] nearest_po2(3) # Returns 4 nearest_po2(4) # Returns 4 @@ -749,7 +748,7 @@ nearest_po2(0) # Returns 0 (this may not be expected) nearest_po2(-1) # Returns 0 (this may not be expected) [/codeblock] - [b]Warning:[/b] Due to the way it is implemented, this function returns [code]0[/code] rather than [code]1[/code] for negative values of [param value] (in reality, 1 is the smallest integer power of 2). + [b]Warning:[/b] Due to its implementation, this method returns [code]0[/code] rather than [code]1[/code] for values less than or equal to [code]0[/code], with an exception for [param value] being the smallest negative 64-bit integer ([code]-9223372036854775808[/code]) in which case the [param value] is returned unchanged. </description> </method> <method name="pingpong"> @@ -1464,6 +1463,10 @@ <member name="DisplayServer" type="DisplayServer" setter="" getter=""> The [DisplayServer] singleton. </member> + <member name="EditorInterface" type="EditorInterface" setter="" getter=""> + The [EditorInterface] singleton. + [b]Note:[/b] Only available in editor builds. + </member> <member name="Engine" type="Engine" setter="" getter=""> The [Engine] singleton. </member> diff --git a/doc/classes/AStarGrid2D.xml b/doc/classes/AStarGrid2D.xml index 5d54af60ec..d64941cb9b 100644 --- a/doc/classes/AStarGrid2D.xml +++ b/doc/classes/AStarGrid2D.xml @@ -53,6 +53,24 @@ Clears the grid and sets the [member region] to [code]Rect2i(0, 0, 0, 0)[/code]. </description> </method> + <method name="fill_solid_region"> + <return type="void" /> + <param index="0" name="region" type="Rect2i" /> + <param index="1" name="solid" type="bool" default="true" /> + <description> + Fills the given [param region] on the grid with the specified value for the solid flag. + [b]Note:[/b] Calling [method update] is not needed after the call of this function. + </description> + </method> + <method name="fill_weight_scale_region"> + <return type="void" /> + <param index="0" name="region" type="Rect2i" /> + <param index="1" name="weight_scale" type="float" /> + <description> + Fills the given [param region] on the grid with the specified value for the weight scale. + [b]Note:[/b] Calling [method update] is not needed after the call of this function. + </description> + </method> <method name="get_id_path"> <return type="Vector2i[]" /> <param index="0" name="from_id" type="Vector2i" /> diff --git a/doc/classes/AudioServer.xml b/doc/classes/AudioServer.xml index 20a87aea7b..cb4fb8d5ca 100644 --- a/doc/classes/AudioServer.xml +++ b/doc/classes/AudioServer.xml @@ -132,7 +132,7 @@ <method name="get_output_latency" qualifiers="const"> <return type="float" /> <description> - Returns the audio driver's output latency. + Returns the audio driver's output latency. This can be expensive, it is not recommended to call this every frame. </description> </method> <method name="get_speaker_mode" qualifiers="const"> diff --git a/doc/classes/BackBufferCopy.xml b/doc/classes/BackBufferCopy.xml index e4e6f1d76d..23d6bc3851 100644 --- a/doc/classes/BackBufferCopy.xml +++ b/doc/classes/BackBufferCopy.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="BackBufferCopy" inherits="Node2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Copies a region of the screen (or the whole screen) to a buffer so it can be accessed in your shader scripts using the screen texture (i.e. a uniform sampler with [code]hint_screen_texture[/code]). + A node that copies a region of the screen to a buffer for access in shader code. </brief_description> <description> - Node for back-buffering the currently-displayed screen. The region defined in the [BackBufferCopy] node is buffered with the content of the screen it covers, or the entire screen according to the copy mode set. Use the screen texture in your shader scripts to access the buffer. + Node for back-buffering the currently-displayed screen. The region defined in the [BackBufferCopy] node is buffered with the content of the screen it covers, or the entire screen according to the [member copy_mode]. It can be accessed in shader scripts using the screen texture (i.e. a uniform sampler with [code]hint_screen_texture[/code]). [b]Note:[/b] Since this node inherits from [Node2D] (and not [Control]), anchors and margins won't apply to child [Control]-derived nodes. This can be problematic when resizing the window. To avoid this, add [Control]-derived nodes as [i]siblings[/i] to the [BackBufferCopy] node instead of adding them as children. </description> <tutorials> diff --git a/doc/classes/BaseButton.xml b/doc/classes/BaseButton.xml index b18ee95a43..c5998ad8a1 100644 --- a/doc/classes/BaseButton.xml +++ b/doc/classes/BaseButton.xml @@ -17,7 +17,7 @@ </method> <method name="_toggled" qualifiers="virtual"> <return type="void" /> - <param index="0" name="button_pressed" type="bool" /> + <param index="0" name="toggled_on" type="bool" /> <description> Called when the button is toggled (only if [member toggle_mode] is active). </description> @@ -98,9 +98,9 @@ </description> </signal> <signal name="toggled"> - <param index="0" name="button_pressed" type="bool" /> + <param index="0" name="toggled_on" type="bool" /> <description> - Emitted when the button was just toggled between pressed and normal states (only if [member toggle_mode] is active). The new state is contained in the [param button_pressed] argument. + Emitted when the button was just toggled between pressed and normal states (only if [member toggle_mode] is active). The new state is contained in the [param toggled_on] argument. </description> </signal> </signals> diff --git a/doc/classes/BaseMaterial3D.xml b/doc/classes/BaseMaterial3D.xml index 949dcc24d0..1cc2976c81 100644 --- a/doc/classes/BaseMaterial3D.xml +++ b/doc/classes/BaseMaterial3D.xml @@ -1,13 +1,13 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="BaseMaterial3D" inherits="Material" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Default 3D rendering material. + Abstract base class for defining the 3D rendering properties of meshes. </brief_description> <description> - This provides a default material with a wide variety of rendering features and properties without the need to write shader code. See the tutorial below for details. + This class serves as a default material with a wide variety of rendering features and properties without the need to write shader code. See the tutorial below for details. </description> <tutorials> - <link title="Standard Material 3D">$DOCS_URL/tutorials/3d/standard_material_3d.html</link> + <link title="Standard Material 3D and ORM Material 3D">$DOCS_URL/tutorials/3d/standard_material_3d.html</link> </tutorials> <methods> <method name="get_feature" qualifiers="const"> diff --git a/doc/classes/Bone2D.xml b/doc/classes/Bone2D.xml index 605f217eff..fa8112de29 100644 --- a/doc/classes/Bone2D.xml +++ b/doc/classes/Bone2D.xml @@ -61,7 +61,7 @@ <param index="0" name="angle" type="float" /> <description> Sets the bone angle for the [Bone2D]. This is typically set to the rotation from the [Bone2D] to a child [Bone2D] node. - [b]Note:[/b] [b]Note:[/b] This is different from the [Bone2D]'s rotation. The bone's angle is the rotation of the bone shown by the gizmo, which is unaffected by the [Bone2D]'s [member Node2D.transform]. + [b]Note:[/b] This is different from the [Bone2D]'s rotation. The bone's angle is the rotation of the bone shown by the gizmo, which is unaffected by the [Bone2D]'s [member Node2D.transform]. </description> </method> <method name="set_length"> diff --git a/doc/classes/Button.xml b/doc/classes/Button.xml index 6faefcbe0d..25c71deb1f 100644 --- a/doc/classes/Button.xml +++ b/doc/classes/Button.xml @@ -115,7 +115,7 @@ <theme_item name="icon_pressed_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)"> Icon modulate [Color] used when the [Button] is being pressed. </theme_item> - <theme_item name="h_separation" data_type="constant" type="int" default="2"> + <theme_item name="h_separation" data_type="constant" type="int" default="4"> The horizontal space between [Button]'s icon and text. Negative values will be treated as [code]0[/code] when used. </theme_item> <theme_item name="icon_max_width" data_type="constant" type="int" default="0"> diff --git a/doc/classes/CPUParticles2D.xml b/doc/classes/CPUParticles2D.xml index eaacdd590d..051635cb48 100644 --- a/doc/classes/CPUParticles2D.xml +++ b/doc/classes/CPUParticles2D.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="CPUParticles2D" inherits="Node2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - CPU-based 2D particle emitter. + A CPU-based 2D particle emitter. </brief_description> <description> CPU-based 2D particle node used to create a variety of particle systems and effects. diff --git a/doc/classes/CPUParticles3D.xml b/doc/classes/CPUParticles3D.xml index f03c463049..bd096dc9c6 100644 --- a/doc/classes/CPUParticles3D.xml +++ b/doc/classes/CPUParticles3D.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="CPUParticles3D" inherits="GeometryInstance3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - CPU-based 3D particle emitter. + A CPU-based 3D particle emitter. </brief_description> <description> CPU-based 3D particle node used to create a variety of particle systems and effects. diff --git a/doc/classes/Callable.xml b/doc/classes/Callable.xml index c7b2d6df94..b903e98319 100644 --- a/doc/classes/Callable.xml +++ b/doc/classes/Callable.xml @@ -138,7 +138,7 @@ <method name="get_method" qualifiers="const"> <return type="StringName" /> <description> - Returns the name of the method represented by this [Callable]. If the callable is a lambda function, returns the function's name. + Returns the name of the method represented by this [Callable]. If the callable is a GDScript lambda function, returns the function's name or [code]"<anonymous lambda>"[/code]. </description> </method> <method name="get_object" qualifiers="const"> diff --git a/doc/classes/ColorPickerButton.xml b/doc/classes/ColorPickerButton.xml index 32ec27868a..f6af188998 100644 --- a/doc/classes/ColorPickerButton.xml +++ b/doc/classes/ColorPickerButton.xml @@ -74,7 +74,7 @@ <theme_item name="font_pressed_color" data_type="color" type="Color" default="Color(0.8, 0.8, 0.8, 1)"> Text [Color] used when the [ColorPickerButton] is being pressed. </theme_item> - <theme_item name="h_separation" data_type="constant" type="int" default="2"> + <theme_item name="h_separation" data_type="constant" type="int" default="4"> The horizontal space between [ColorPickerButton]'s icon and text. </theme_item> <theme_item name="outline_size" data_type="constant" type="int" default="0"> diff --git a/doc/classes/CompressedCubemap.xml b/doc/classes/CompressedCubemap.xml index e72d727d7d..6ab0cc5d88 100644 --- a/doc/classes/CompressedCubemap.xml +++ b/doc/classes/CompressedCubemap.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="CompressedCubemap" inherits="CompressedTextureLayered" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - 6-sided texture typically used in 3D rendering, optionally compressed. + An optionally compressed [Cubemap]. </brief_description> <description> A cubemap that is loaded from a [code].ccube[/code] file. This file format is internal to Godot; it is created by importing other image formats with the import system. [CompressedCubemap] can use one of 4 compresson methods: diff --git a/doc/classes/CompressedCubemapArray.xml b/doc/classes/CompressedCubemapArray.xml index f5829e4e3e..32687229ed 100644 --- a/doc/classes/CompressedCubemapArray.xml +++ b/doc/classes/CompressedCubemapArray.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="CompressedCubemapArray" inherits="CompressedTextureLayered" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Array of 6-sided textures typically used in 3D rendering, optionally compressed. + An optionally compressed [CubemapArray]. </brief_description> <description> A cubemap array that is loaded from a [code].ccubearray[/code] file. This file format is internal to Godot; it is created by importing other image formats with the import system. [CompressedCubemapArray] can use one of 4 compresson methods: diff --git a/doc/classes/Cubemap.xml b/doc/classes/Cubemap.xml index 0d3b52ddfc..b7da3c4ec6 100644 --- a/doc/classes/Cubemap.xml +++ b/doc/classes/Cubemap.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="Cubemap" inherits="ImageTextureLayered" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - 6-sided texture typically used in 3D rendering. + Six square textures representing the faces of a cube. Commonly used as a skybox. </brief_description> <description> A cubemap is made of 6 textures organized in layers. They are typically used for faking reflections in 3D rendering (see [ReflectionProbe]). It can be used to make an object look as if it's reflecting its surroundings. This usually delivers much better performance than other reflection methods. diff --git a/doc/classes/CubemapArray.xml b/doc/classes/CubemapArray.xml index ee4ec239f3..7ee497385e 100644 --- a/doc/classes/CubemapArray.xml +++ b/doc/classes/CubemapArray.xml @@ -1,13 +1,13 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="CubemapArray" inherits="ImageTextureLayered" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - A single composite texture resource which consists of multiple [Cubemap]s. + An array of [Cubemap]s, stored together and with a single reference. </brief_description> <description> - [CubemapArray]s are made of an array of [Cubemap]s. Accordingly, like [Cubemap]s they are made of multiple textures the amount of which must be divisible by 6 (one image for each face of the cube). The primary benefit of [CubemapArray]s is that they can be accessed in shader code using a single texture reference. In other words, you can pass multiple [Cubemap]s into a shader using a single [CubemapArray]. - Generally, [CubemapArray]s provide a more efficient way for storing multiple [Cubemap]s compared to storing multiple [Cubemap]s themselves in an array. - Internally, Godot uses [CubemapArray]s for many effects including the [Sky], if you set [member ProjectSettings.rendering/reflections/sky_reflections/texture_array_reflections] to [code]true[/code]. - To create such a texture file yourself, reimport your image files using the Godot Editor import presets. + [CubemapArray]s are made of an array of [Cubemap]s. Like [Cubemap]s, they are made of multiple textures, the amount of which must be divisible by 6 (one for each face of the cube). The primary benefit of [CubemapArray]s is that they can be accessed in shader code using a single texture reference. In other words, you can pass multiple [Cubemap]s into a shader using a single [CubemapArray]. + Moreover, [Cubemap]s are allocated in adjacent cache regions on the GPU. This makes [CubemapArray]s the most efficient way to store multiple [Cubemap]s. + Internally, Godot uses [CubemapArray]s for many effects, including the [Sky] if you set [member ProjectSettings.rendering/reflections/sky_reflections/texture_array_reflections] to [code]true[/code]. + To create such a texture file yourself, reimport your image files using the import presets of the File System dock. [b]Note:[/b] [CubemapArray] is not supported in the OpenGL 3 rendering backend. </description> <tutorials> diff --git a/doc/classes/Curve.xml b/doc/classes/Curve.xml index 8f5dc4e945..25b06f1063 100644 --- a/doc/classes/Curve.xml +++ b/doc/classes/Curve.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="Curve" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - A mathematic curve. + A mathematical curve. </brief_description> <description> - A curve that can be saved and re-used for other objects. By default, it ranges between [code]0[/code] and [code]1[/code] on the Y axis and positions points relative to the [code]0.5[/code] Y position. + This resource describes a mathematical curve by defining a set of points and tangents at each point. By default, it ranges between [code]0[/code] and [code]1[/code] on the Y axis and positions points relative to the [code]0.5[/code] Y position. See also [Gradient] which is designed for color interpolation. See also [Curve2D] and [Curve3D]. </description> <tutorials> diff --git a/doc/classes/CurveTexture.xml b/doc/classes/CurveTexture.xml index 4767c18d5a..8cb2384da3 100644 --- a/doc/classes/CurveTexture.xml +++ b/doc/classes/CurveTexture.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="CurveTexture" inherits="Texture2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - A texture that shows a curve. + A 1D texture where pixel brightness corresponds to points on a curve. </brief_description> <description> - Renders a given [Curve] provided to it. Simplifies the task of drawing curves and/or saving them as image files. + A 1D texture where pixel brightness corresponds to points on a [Curve] resource, either in grayscale or in red. This visual representation simplifies the task of saving curves as image files. If you need to store up to 3 curves within a single texture, use [CurveXYZTexture] instead. See also [GradientTexture1D] and [GradientTexture2D]. </description> <tutorials> diff --git a/doc/classes/CurveXYZTexture.xml b/doc/classes/CurveXYZTexture.xml index e0ab17a35c..8353ed9092 100644 --- a/doc/classes/CurveXYZTexture.xml +++ b/doc/classes/CurveXYZTexture.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="CurveXYZTexture" inherits="Texture2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - A texture that shows 3 different curves (stored on the red, green and blue color channels). + A 1D texture where the red, green, and blue color channels correspond to points on 3 curves. </brief_description> <description> - Renders 3 given [Curve]s provided to it, on the red, green and blue channels respectively. Compared to using separate [CurveTexture]s, this further simplifies the task of drawing curves and/or saving them as image files. + A 1D texture where the red, green, and blue color channels correspond to points on 3 [Curve] resources. Compared to using separate [CurveTexture]s, this further simplifies the task of saving curves as image files. If you only need to store one curve within a single texture, use [CurveTexture] instead. See also [GradientTexture1D] and [GradientTexture2D]. </description> <tutorials> diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index 465855cc92..c2f81ba109 100644 --- a/doc/classes/DisplayServer.xml +++ b/doc/classes/DisplayServer.xml @@ -121,7 +121,9 @@ Displays OS native dialog for selecting files or directories in the file system. Callbacks have the following arguments: [code]bool status, PackedStringArray selected_paths[/code]. [b]Note:[/b] This method is implemented if the display server has the [code]FEATURE_NATIVE_DIALOG[/code] feature. - [b]Note:[/b] This method is implemented on Windows and macOS. + [b]Note:[/b] This method is implemented on Linux, Windows and macOS. + [b]Note:[/b] [param current_directory] might be ignored. + [b]Note:[/b] On Linux, [param show_hidden] is ignored. [b]Note:[/b] On macOS, native file dialogs have no title. [b]Note:[/b] On macOS, sandboxed apps will save security-scoped bookmarks to retain access to the opened folders across multiple sessions. Use [method OS.get_granted_permissions] to get a list of saved bookmarks. </description> diff --git a/doc/classes/EditorCommandPalette.xml b/doc/classes/EditorCommandPalette.xml index df8bec8002..e6af82b1c3 100644 --- a/doc/classes/EditorCommandPalette.xml +++ b/doc/classes/EditorCommandPalette.xml @@ -8,13 +8,13 @@ Command key names use slash delimiters to distinguish sections, for example: [code]"example/command1"[/code] then [code]example[/code] will be the section name. [codeblocks] [gdscript] - var command_palette = get_editor_interface().get_command_palette() + var command_palette = EditorInterface.get_command_palette() # external_command is a function that will be called with the command is executed. var command_callable = Callable(self, "external_command").bind(arguments) command_palette.add_command("command", "test/command",command_callable) [/gdscript] [csharp] - EditorCommandPalette commandPalette = GetEditorInterface().GetCommandPalette(); + EditorCommandPalette commandPalette = EditorInterface.Singleton.GetCommandPalette(); // ExternalCommand is a function that will be called with the command is executed. Callable commandCallable = new Callable(this, MethodName.ExternalCommand); commandPalette.AddCommand("command", "test/command", commandCallable) diff --git a/doc/classes/EditorExportPlugin.xml b/doc/classes/EditorExportPlugin.xml index 5f9e1e3684..fde6307a13 100644 --- a/doc/classes/EditorExportPlugin.xml +++ b/doc/classes/EditorExportPlugin.xml @@ -44,7 +44,7 @@ <param index="1" name="path" type="String" /> <description> Customize a scene. If changes are made to it, return the same or a new scene. Otherwise, return [code]null[/code]. If a new scene is returned, it is up to you to dispose of the old one. - Implementing this method is required if [method _begin_customize_resources] returns [code]true[/code]. + Implementing this method is required if [method _begin_customize_scenes] returns [code]true[/code]. </description> </method> <method name="_end_customize_resources" qualifiers="virtual"> diff --git a/doc/classes/EditorInterface.xml b/doc/classes/EditorInterface.xml index cabd9c0da6..3ae4b7f812 100644 --- a/doc/classes/EditorInterface.xml +++ b/doc/classes/EditorInterface.xml @@ -5,7 +5,16 @@ </brief_description> <description> [EditorInterface] gives you control over Godot editor's window. It allows customizing the window, saving and (re-)loading scenes, rendering mesh previews, inspecting and editing resources and objects, and provides access to [EditorSettings], [EditorFileSystem], [EditorResourcePreview], [ScriptEditor], the editor viewport, and information about scenes. - [b]Note:[/b] This class shouldn't be instantiated directly. Instead, access the singleton using [method EditorPlugin.get_editor_interface]. + [b]Note:[/b] This class shouldn't be instantiated directly. Instead, access the singleton directly by its name. + [codeblocks] + [gdscript] + var editor_settings = EditorInterface.get_editor_settings() + [/gdscript] + [csharp] + // In C# you can access it via the static Singleton property. + EditorSettings settings = EditorInterface.Singleton.GetEditorSettings(); + [/csharp] + [/codeblocks] </description> <tutorials> </tutorials> diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml index ffb4df25d3..8d280b8276 100644 --- a/doc/classes/EditorPlugin.xml +++ b/doc/classes/EditorPlugin.xml @@ -245,7 +245,7 @@ # You can use a custom icon: return preload("res://addons/my_plugin/my_plugin_icon.svg") # Or use a built-in icon: - return get_editor_interface().get_base_control().get_theme_icon("Node", "EditorIcons") + return EditorInterface.get_base_control().get_theme_icon("Node", "EditorIcons") [/gdscript] [csharp] public override Texture2D _GetPluginIcon() @@ -253,7 +253,7 @@ // You can use a custom icon: return ResourceLoader.Load<Texture2D>("res://addons/my_plugin/my_plugin_icon.svg"); // Or use a built-in icon: - return GetEditorInterface().GetBaseControl().GetThemeIcon("Node", "EditorIcons"); + return EditorInterface.Singleton.GetBaseControl().GetThemeIcon("Node", "EditorIcons"); } [/csharp] [/codeblocks] @@ -340,7 +340,7 @@ func _enter_tree(): plugin_control = preload("my_plugin_control.tscn").instantiate() - get_editor_interface().get_editor_main_screen().add_child(plugin_control) + EditorInterface.get_editor_main_screen().add_child(plugin_control) plugin_control.hide() func _has_main_screen(): @@ -353,7 +353,7 @@ return "My Super Cool Plugin 3000" func _get_plugin_icon(): - return get_editor_interface().get_base_control().get_theme_icon("Node", "EditorIcons") + return EditorInterface.get_base_control().get_theme_icon("Node", "EditorIcons") [/codeblock] </description> </method> @@ -558,10 +558,11 @@ The callback should have 4 arguments: [Object] [code]undo_redo[/code], [Object] [code]modified_object[/code], [String] [code]property[/code] and [Variant] [code]new_value[/code]. They are, respectively, the [UndoRedo] object used by the inspector, the currently modified object, the name of the modified property and the new value the property is about to take. </description> </method> - <method name="get_editor_interface"> + <method name="get_editor_interface" is_deprecated="true"> <return type="EditorInterface" /> <description> - Returns the [EditorInterface] singleton. It provides access to some parts of the editor GUI as well as various inner states and tools. + Returns the [EditorInterface] singleton instance. + [i]Deprecated.[/i] [EditorInterface] is a global singleton and can be accessed directly by its name. </description> </method> <method name="get_export_as_menu"> diff --git a/doc/classes/EditorScript.xml b/doc/classes/EditorScript.xml index 01e6b9a52e..8033c18918 100644 --- a/doc/classes/EditorScript.xml +++ b/doc/classes/EditorScript.xml @@ -48,10 +48,11 @@ [b]Warning:[/b] The implementation of this method is currently disabled. </description> </method> - <method name="get_editor_interface" qualifiers="const"> + <method name="get_editor_interface" qualifiers="const" is_deprecated="true"> <return type="EditorInterface" /> <description> Returns the [EditorInterface] singleton instance. + [i]Deprecated.[/i] [EditorInterface] is a global singleton and can be accessed directly by its name. </description> </method> <method name="get_scene" qualifiers="const"> diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index 85efe4362e..5ca89dc03e 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -9,7 +9,7 @@ Accessing the settings can be done using the following methods, such as: [codeblocks] [gdscript] - var settings = get_editor_interface().get_editor_settings() + var settings = EditorInterface.get_editor_settings() # `settings.set("some/property", 10)` also works as this class overrides `_set()` internally. settings.set_setting("some/property", 10) # `settings.get("some/property")` also works as this class overrides `_get()` internally. @@ -17,7 +17,7 @@ var list_of_settings = settings.get_property_list() [/gdscript] [csharp] - EditorSettings settings = GetEditorInterface().GetEditorSettings(); + EditorSettings settings = EditorInterface.Singleton.GetEditorSettings(); // `settings.set("some/property", value)` also works as this class overrides `_set()` internally. settings.SetSetting("some/property", Value); // `settings.get("some/property", value)` also works as this class overrides `_get()` internally. diff --git a/doc/classes/FileAccess.xml b/doc/classes/FileAccess.xml index 5c1f6775a1..85fbff7932 100644 --- a/doc/classes/FileAccess.xml +++ b/doc/classes/FileAccess.xml @@ -170,6 +170,14 @@ Returns the next 32 bits from the file as a floating-point number. </description> </method> + <method name="get_hidden_attribute" qualifiers="static"> + <return type="bool" /> + <param index="0" name="file" type="String" /> + <description> + Returns [code]true[/code], if file [code]hidden[/code] attribute is set. + [b]Note:[/b] This method is implemented on iOS, BSD, macOS, and Windows. + </description> + </method> <method name="get_length" qualifiers="const"> <return type="int" /> <description> @@ -228,6 +236,14 @@ Returns the file cursor's position. </description> </method> + <method name="get_read_only_attribute" qualifiers="static"> + <return type="bool" /> + <param index="0" name="file" type="String" /> + <description> + Returns [code]true[/code], if file [code]read only[/code] attribute is set. + [b]Note:[/b] This method is implemented on iOS, BSD, macOS, and Windows. + </description> + </method> <method name="get_real" qualifiers="const"> <return type="float" /> <description> @@ -241,6 +257,14 @@ Returns a SHA-256 [String] representing the file at the given path or an empty [String] on failure. </description> </method> + <method name="get_unix_permissions" qualifiers="static"> + <return type="int" enum="FileAccess.UnixPermissionFlags" is_bitfield="true" /> + <param index="0" name="file" type="String" /> + <description> + Returns file UNIX permissions. + [b]Note:[/b] This method is implemented on iOS, Linux/BSD, and macOS. + </description> + </method> <method name="get_var" qualifiers="const"> <return type="Variant" /> <param index="0" name="allow_objects" type="bool" default="false" /> @@ -312,6 +336,33 @@ [b]Note:[/b] This is an offset, so you should use negative numbers or the cursor will be at the end of the file. </description> </method> + <method name="set_hidden_attribute" qualifiers="static"> + <return type="int" enum="Error" /> + <param index="0" name="file" type="String" /> + <param index="1" name="hidden" type="bool" /> + <description> + Sets file [code]hidden[/code] attribute. + [b]Note:[/b] This method is implemented on iOS, BSD, macOS, and Windows. + </description> + </method> + <method name="set_read_only_attribute" qualifiers="static"> + <return type="int" enum="Error" /> + <param index="0" name="file" type="String" /> + <param index="1" name="ro" type="bool" /> + <description> + Sets file [code]read only[/code] attribute. + [b]Note:[/b] This method is implemented on iOS, BSD, macOS, and Windows. + </description> + </method> + <method name="set_unix_permissions" qualifiers="static"> + <return type="int" enum="Error" /> + <param index="0" name="file" type="String" /> + <param index="1" name="permissions" type="int" enum="FileAccess.UnixPermissionFlags" is_bitfield="true" /> + <description> + Sets file UNIX permissions. + [b]Note:[/b] This method is implemented on iOS, Linux/BSD, and macOS. + </description> + </method> <method name="store_8"> <return type="void" /> <param index="0" name="value" type="int" /> @@ -485,5 +536,41 @@ <constant name="COMPRESSION_BROTLI" value="4" enum="CompressionMode"> Uses the [url=https://github.com/google/brotli]brotli[/url] compression method (only decompression is supported). </constant> + <constant name="UNIX_READ_OWNER" value="256" enum="UnixPermissionFlags" is_bitfield="true"> + Read for owner bit. + </constant> + <constant name="UNIX_WRITE_OWNER" value="128" enum="UnixPermissionFlags" is_bitfield="true"> + Write for owner bit. + </constant> + <constant name="UNIX_EXECUTE_OWNER" value="64" enum="UnixPermissionFlags" is_bitfield="true"> + Execute for owner bit. + </constant> + <constant name="UNIX_READ_GROUP" value="32" enum="UnixPermissionFlags" is_bitfield="true"> + Read for group bit. + </constant> + <constant name="UNIX_WRITE_GROUP" value="16" enum="UnixPermissionFlags" is_bitfield="true"> + Write for group bit. + </constant> + <constant name="UNIX_EXECUTE_GROUP" value="8" enum="UnixPermissionFlags" is_bitfield="true"> + Execute for group bit. + </constant> + <constant name="UNIX_READ_OTHER" value="4" enum="UnixPermissionFlags" is_bitfield="true"> + Read for other bit. + </constant> + <constant name="UNIX_WRITE_OTHER" value="2" enum="UnixPermissionFlags" is_bitfield="true"> + Write for other bit. + </constant> + <constant name="UNIX_EXECUTE_OTHER" value="1" enum="UnixPermissionFlags" is_bitfield="true"> + Execute for other bit. + </constant> + <constant name="UNIX_SET_USER_ID" value="2048" enum="UnixPermissionFlags" is_bitfield="true"> + Set user id on execution bit. + </constant> + <constant name="UNIX_SET_GROUP_ID" value="1024" enum="UnixPermissionFlags" is_bitfield="true"> + Set group id on execution bit. + </constant> + <constant name="UNIX_RESTRICTED_DELETE" value="512" enum="UnixPermissionFlags" is_bitfield="true"> + Restricted deletion (sticky) bit. + </constant> </constants> </class> diff --git a/doc/classes/FogMaterial.xml b/doc/classes/FogMaterial.xml index 13366f1813..a6acaaef00 100644 --- a/doc/classes/FogMaterial.xml +++ b/doc/classes/FogMaterial.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="FogMaterial" inherits="Material" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - [Material] used with a [FogVolume] to draw things with the volumetric fog effect. + A material that controls how volumetric fog is rendered, to be assigned to a [FogVolume]. </brief_description> <description> A [Material] resource that can be used by [FogVolume]s to draw volumetric effects. diff --git a/doc/classes/FogVolume.xml b/doc/classes/FogVolume.xml index 66aeb580c2..538411016e 100644 --- a/doc/classes/FogVolume.xml +++ b/doc/classes/FogVolume.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="FogVolume" inherits="VisualInstance3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - A node used to add local fog with the volumetric fog effect. + A region that contributes to the default volumetric fog from the world environment. </brief_description> <description> [FogVolume]s are used to add localized fog into the global volumetric fog effect. [FogVolume]s can also remove volumetric fog from specific areas if using a [FogMaterial] with a negative [member FogMaterial.density]. diff --git a/doc/classes/Font.xml b/doc/classes/Font.xml index 461e96def4..e87712825d 100644 --- a/doc/classes/Font.xml +++ b/doc/classes/Font.xml @@ -149,12 +149,6 @@ Returns number of faces in the TrueType / OpenType collection. </description> </method> - <method name="get_fallbacks" qualifiers="const"> - <return type="Font[]" /> - <description> - Returns array of fallback [Font]s. - </description> - </method> <method name="get_font_name" qualifiers="const"> <return type="String" /> <description> @@ -335,12 +329,11 @@ Sets LRU cache capacity for [code]draw_*[/code] methods. </description> </method> - <method name="set_fallbacks"> - <return type="void" /> - <param index="0" name="fallbacks" type="Font[]" /> - <description> - Sets array of fallback [Font]s. - </description> - </method> </methods> + <members> + <member name="fallbacks" type="Font[]" setter="set_fallbacks" getter="get_fallbacks" default="[]"> + Array of fallback [Font]s to use as a substitute if a glyph is not found in this current [Font]. + If this array is empty in a [FontVariation], the [member FontVariation.base_font]'s fallbacks are used instead. + </member> + </members> </class> diff --git a/doc/classes/FontFile.xml b/doc/classes/FontFile.xml index ba4761e900..86d1cb552a 100644 --- a/doc/classes/FontFile.xml +++ b/doc/classes/FontFile.xml @@ -568,9 +568,6 @@ <member name="data" type="PackedByteArray" setter="set_data" getter="get_data" default="PackedByteArray()"> Contents of the dynamic font source file. </member> - <member name="fallbacks" type="Font[]" setter="set_fallbacks" getter="get_fallbacks" default="[]"> - Array of fallback [Font]s. - </member> <member name="fixed_size" type="int" setter="set_fixed_size" getter="get_fixed_size" default="0"> Font size, used only for the bitmap fonts. </member> diff --git a/doc/classes/FontVariation.xml b/doc/classes/FontVariation.xml index eb72b4020c..f02fda31b9 100644 --- a/doc/classes/FontVariation.xml +++ b/doc/classes/FontVariation.xml @@ -46,9 +46,6 @@ <member name="base_font" type="Font" setter="set_base_font" getter="get_base_font"> Base font used to create a variation. If not set, default [Theme] font is used. </member> - <member name="fallbacks" type="Font[]" setter="set_fallbacks" getter="get_fallbacks" default="[]"> - Array of fallback [Font]s to use as a substitute if a glyph is not found in this [FontVariation]. If not set, [member base_font]'s fallbacks are used instead. - </member> <member name="opentype_features" type="Dictionary" setter="set_opentype_features" getter="get_opentype_features" default="{}"> A set of OpenType feature tags. More info: [url=https://docs.microsoft.com/en-us/typography/opentype/spec/featuretags]OpenType feature tags[/url]. </member> diff --git a/doc/classes/GPUParticles2D.xml b/doc/classes/GPUParticles2D.xml index 2928215c3e..d5a4c146e0 100644 --- a/doc/classes/GPUParticles2D.xml +++ b/doc/classes/GPUParticles2D.xml @@ -1,12 +1,12 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GPUParticles2D" inherits="Node2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - 2D particle emitter. + A 2D particle emitter. </brief_description> <description> 2D particle node used to create a variety of particle systems and effects. [GPUParticles2D] features an emitter that generates some number of particles at a given rate. Use the [member process_material] property to add a [ParticleProcessMaterial] to configure particle appearance and behavior. Alternatively, you can add a [ShaderMaterial] which will be applied to all particles. - 2D particles can optionally collide with [LightOccluder2D] nodes (note: they don't collide with [PhysicsBody2D] nodes). + 2D particles can optionally collide with [LightOccluder2D], but they don't collide with [PhysicsBody2D] nodes. </description> <tutorials> <link title="Particle systems (2D)">$DOCS_URL/tutorials/2d/particle_systems_2d.html</link> diff --git a/doc/classes/GPUParticles3D.xml b/doc/classes/GPUParticles3D.xml index 4c0b2d12ed..31f1f9e66e 100644 --- a/doc/classes/GPUParticles3D.xml +++ b/doc/classes/GPUParticles3D.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GPUParticles3D" inherits="GeometryInstance3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - 3D particle emitter. + A 3D particle emitter. </brief_description> <description> 3D particle node used to create a variety of particle systems and effects. [GPUParticles3D] features an emitter that generates some number of particles at a given rate. diff --git a/doc/classes/GPUParticlesAttractor3D.xml b/doc/classes/GPUParticlesAttractor3D.xml index b3ed3b4701..0d219e4bc9 100644 --- a/doc/classes/GPUParticlesAttractor3D.xml +++ b/doc/classes/GPUParticlesAttractor3D.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GPUParticlesAttractor3D" inherits="VisualInstance3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Abstract class for 3D particle attractors affecting [GPUParticles3D] nodes. + Abstract base class for 3D particle attractors. </brief_description> <description> Particle attractors can be used to attract particles towards the attractor's origin, or to push them away from the attractor's origin. @@ -25,7 +25,7 @@ [b]Note:[/b] If [member directionality] is greater than [code]0.0[/code], the direction in which particles are pushed can be changed by rotating the [GPUParticlesAttractor3D] node. </member> <member name="strength" type="float" setter="set_strength" getter="get_strength" default="1.0"> - If [member strength] is negative, particles will be pushed in the reverse direction. Particles will be pushed [i]away[/i] from the attractor's origin if [member directionality] is [code]0.0[/code], or towards local +Z if [member directionality] is greater than [code]0.0[/code]. + Adjusts the strength of the attractor. If [member strength] is negative, particles will be pushed in the opposite direction. Particles will be pushed [i]away[/i] from the attractor's origin if [member directionality] is [code]0.0[/code], or towards local +Z if [member directionality] is greater than [code]0.0[/code]. </member> </members> </class> diff --git a/doc/classes/GPUParticlesAttractorBox3D.xml b/doc/classes/GPUParticlesAttractorBox3D.xml index d43efc73bf..410d504428 100644 --- a/doc/classes/GPUParticlesAttractorBox3D.xml +++ b/doc/classes/GPUParticlesAttractorBox3D.xml @@ -1,10 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GPUParticlesAttractorBox3D" inherits="GPUParticlesAttractor3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Box-shaped 3D particle attractor affecting [GPUParticles3D] nodes. + A box-shaped attractor that influences particles from [GPUParticles3D] nodes. </brief_description> <description> - Box-shaped 3D particle attractor affecting [GPUParticles3D] nodes. + A box-shaped attractor that influences particles from [GPUParticles3D] nodes. Can be used to attract particles towards its origin, or to push them away from its origin. + Particle attractors work in real-time and can be moved, rotated and scaled during gameplay. Unlike collision shapes, non-uniform scaling of attractors is also supported. [b]Note:[/b] Particle attractors only affect [GPUParticles3D], not [CPUParticles3D]. </description> <tutorials> diff --git a/doc/classes/GPUParticlesAttractorSphere3D.xml b/doc/classes/GPUParticlesAttractorSphere3D.xml index 05c1e60d9a..599d7e4e2a 100644 --- a/doc/classes/GPUParticlesAttractorSphere3D.xml +++ b/doc/classes/GPUParticlesAttractorSphere3D.xml @@ -1,10 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GPUParticlesAttractorSphere3D" inherits="GPUParticlesAttractor3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Ellipse-shaped 3D particle attractor affecting [GPUParticles3D] nodes. + A spheroid-shaped attractor that influences particles from [GPUParticles3D] nodes. </brief_description> <description> - Ellipse-shaped 3D particle attractor affecting [GPUParticles3D] nodes. + A spheroid-shaped attractor that influences particles from [GPUParticles3D] nodes. Can be used to attract particles towards its origin, or to push them away from its origin. + Particle attractors work in real-time and can be moved, rotated and scaled during gameplay. Unlike collision shapes, non-uniform scaling of attractors is also supported. [b]Note:[/b] Particle attractors only affect [GPUParticles3D], not [CPUParticles3D]. </description> <tutorials> diff --git a/doc/classes/GPUParticlesAttractorVectorField3D.xml b/doc/classes/GPUParticlesAttractorVectorField3D.xml index ef9a11e57c..7cb5524a1b 100644 --- a/doc/classes/GPUParticlesAttractorVectorField3D.xml +++ b/doc/classes/GPUParticlesAttractorVectorField3D.xml @@ -1,11 +1,12 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GPUParticlesAttractorVectorField3D" inherits="GPUParticlesAttractor3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Box-shaped 3D particle attractor with strength varying within the box, affecting [GPUParticles3D] nodes. + A box-shaped attractor with varying directions and strengths defined in it that influences particles from [GPUParticles3D] nodes. </brief_description> <description> - Box-shaped 3D particle attractor with strength varying within the box, affecting [GPUParticles3D] nodes. + A box-shaped attractor with varying directions and strengths defined in it that influences particles from [GPUParticles3D] nodes. Unlike [GPUParticlesAttractorBox3D], [GPUParticlesAttractorVectorField3D] uses a [member texture] to affect attraction strength within the box. This can be used to create complex attraction scenarios where particles travel in different directions depending on their location. This can be useful for weather effects such as sandstorms. + Particle attractors work in real-time and can be moved, rotated and scaled during gameplay. Unlike collision shapes, non-uniform scaling of attractors is also supported. [b]Note:[/b] Particle attractors only affect [GPUParticles3D], not [CPUParticles3D]. </description> <tutorials> diff --git a/doc/classes/GPUParticlesCollision3D.xml b/doc/classes/GPUParticlesCollision3D.xml index 9471a7caf2..089747b7ee 100644 --- a/doc/classes/GPUParticlesCollision3D.xml +++ b/doc/classes/GPUParticlesCollision3D.xml @@ -1,11 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GPUParticlesCollision3D" inherits="VisualInstance3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Abstract class for 3D particle collision shapes affecting [GPUParticles3D] nodes. + Abstract base class for 3D particle collision shapes affecting [GPUParticles3D] nodes. </brief_description> <description> Particle collision shapes can be used to make particles stop or bounce against them. - Particle collision shapes in real-time and can be moved, rotated and scaled during gameplay. Unlike attractors, non-uniform scaling of collision shapes is [i]not[/i] supported. + Particle collision shapes work in real-time and can be moved, rotated and scaled during gameplay. Unlike attractors, non-uniform scaling of collision shapes is [i]not[/i] supported. Particle collision shapes can be temporarily disabled by hiding them. [b]Note:[/b] [member ParticleProcessMaterial.collision_mode] must be [constant ParticleProcessMaterial.COLLISION_RIGID] or [constant ParticleProcessMaterial.COLLISION_HIDE_ON_CONTACT] on the [GPUParticles3D]'s process material for collision to work. [b]Note:[/b] Particle collision only affects [GPUParticles3D], not [CPUParticles3D]. diff --git a/doc/classes/GPUParticlesCollisionBox3D.xml b/doc/classes/GPUParticlesCollisionBox3D.xml index 4bb947a82d..fc225e460f 100644 --- a/doc/classes/GPUParticlesCollisionBox3D.xml +++ b/doc/classes/GPUParticlesCollisionBox3D.xml @@ -1,10 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GPUParticlesCollisionBox3D" inherits="GPUParticlesCollision3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Box-shaped 3D particle collision shape affecting [GPUParticles3D] nodes. + A box-shaped 3D particle collision shape affecting [GPUParticles3D] nodes. </brief_description> <description> - Box-shaped 3D particle collision shape affecting [GPUParticles3D] nodes. + A box-shaped 3D particle collision shape affecting [GPUParticles3D] nodes. + Particle collision shapes work in real-time and can be moved, rotated and scaled during gameplay. Unlike attractors, non-uniform scaling of collision shapes is [i]not[/i] supported. [b]Note:[/b] [member ParticleProcessMaterial.collision_mode] must be [constant ParticleProcessMaterial.COLLISION_RIGID] or [constant ParticleProcessMaterial.COLLISION_HIDE_ON_CONTACT] on the [GPUParticles3D]'s process material for collision to work. [b]Note:[/b] Particle collision only affects [GPUParticles3D], not [CPUParticles3D]. </description> diff --git a/doc/classes/GPUParticlesCollisionHeightField3D.xml b/doc/classes/GPUParticlesCollisionHeightField3D.xml index 58219aa0b3..0b9a94af63 100644 --- a/doc/classes/GPUParticlesCollisionHeightField3D.xml +++ b/doc/classes/GPUParticlesCollisionHeightField3D.xml @@ -1,12 +1,12 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GPUParticlesCollisionHeightField3D" inherits="GPUParticlesCollision3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Real-time heightmap-shaped 3D particle attractor affecting [GPUParticles3D] nodes. + A real-time heightmap-shaped 3D particle collision shape affecting [GPUParticles3D] nodes. </brief_description> <description> - Real-time heightmap-shaped 3D particle attractor affecting [GPUParticles3D] nodes. + A real-time heightmap-shaped 3D particle collision shape affecting [GPUParticles3D] nodes. Heightmap shapes allow for efficiently representing collisions for convex and concave objects with a single "floor" (such as terrain). This is less flexible than [GPUParticlesCollisionSDF3D], but it doesn't require a baking step. - [GPUParticlesCollisionHeightField3D] can also be regenerated in real-time when it is moved, when the camera moves, or even continuously. This makes [GPUParticlesCollisionHeightField3D] a good choice for weather effects such as rain and snow and games with highly dynamic geometry. However, since heightmaps cannot represent overhangs, [GPUParticlesCollisionHeightField3D] is not suited for indoor particle collision. + [GPUParticlesCollisionHeightField3D] can also be regenerated in real-time when it is moved, when the camera moves, or even continuously. This makes [GPUParticlesCollisionHeightField3D] a good choice for weather effects such as rain and snow and games with highly dynamic geometry. However, this class is limited since heightmaps cannot represent overhangs (e.g. indoors or caves). [b]Note:[/b] [member ParticleProcessMaterial.collision_mode] must be [code]true[/code] on the [GPUParticles3D]'s process material for collision to work. [b]Note:[/b] Particle collision only affects [GPUParticles3D], not [CPUParticles3D]. </description> diff --git a/doc/classes/GPUParticlesCollisionSDF3D.xml b/doc/classes/GPUParticlesCollisionSDF3D.xml index b61be49619..11978e0737 100644 --- a/doc/classes/GPUParticlesCollisionSDF3D.xml +++ b/doc/classes/GPUParticlesCollisionSDF3D.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GPUParticlesCollisionSDF3D" inherits="GPUParticlesCollision3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Baked signed distance field 3D particle attractor affecting [GPUParticles3D] nodes. + A baked signed distance field 3D particle collision shape affecting [GPUParticles3D] nodes. </brief_description> <description> - Baked signed distance field 3D particle attractor affecting [GPUParticles3D] nodes. + A baked signed distance field 3D particle collision shape affecting [GPUParticles3D] nodes. Signed distance fields (SDF) allow for efficiently representing approximate collision shapes for convex and concave objects of any shape. This is more flexible than [GPUParticlesCollisionHeightField3D], but it requires a baking step. [b]Baking:[/b] The signed distance field texture can be baked by selecting the [GPUParticlesCollisionSDF3D] node in the editor, then clicking [b]Bake SDF[/b] at the top of the 3D viewport. Any [i]visible[/i] [MeshInstance3D]s within the [member size] will be taken into account for baking, regardless of their [member GeometryInstance3D.gi_mode]. [b]Note:[/b] Baking a [GPUParticlesCollisionSDF3D]'s [member texture] is only possible within the editor, as there is no bake method exposed for use in exported projects. However, it's still possible to load pre-baked [Texture3D]s into its [member texture] property in an exported project. diff --git a/doc/classes/GPUParticlesCollisionSphere3D.xml b/doc/classes/GPUParticlesCollisionSphere3D.xml index cade0eaeef..fe2fe3d414 100644 --- a/doc/classes/GPUParticlesCollisionSphere3D.xml +++ b/doc/classes/GPUParticlesCollisionSphere3D.xml @@ -1,10 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GPUParticlesCollisionSphere3D" inherits="GPUParticlesCollision3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Sphere-shaped 3D particle collision shape affecting [GPUParticles3D] nodes. + A sphere-shaped 3D particle collision shape affecting [GPUParticles3D] nodes. </brief_description> <description> - Sphere-shaped 3D particle collision shape affecting [GPUParticles3D] nodes. + A sphere-shaped 3D particle collision shape affecting [GPUParticles3D] nodes. + Particle collision shapes work in real-time and can be moved, rotated and scaled during gameplay. Unlike attractors, non-uniform scaling of collision shapes is [i]not[/i] supported. [b]Note:[/b] [member ParticleProcessMaterial.collision_mode] must be [constant ParticleProcessMaterial.COLLISION_RIGID] or [constant ParticleProcessMaterial.COLLISION_HIDE_ON_CONTACT] on the [GPUParticles3D]'s process material for collision to work. [b]Note:[/b] Particle collision only affects [GPUParticles3D], not [CPUParticles3D]. </description> diff --git a/doc/classes/Geometry3D.xml b/doc/classes/Geometry3D.xml index 014ebd2248..a85d17d925 100644 --- a/doc/classes/Geometry3D.xml +++ b/doc/classes/Geometry3D.xml @@ -45,6 +45,13 @@ Clips the polygon defined by the points in [param points] against the [param plane] and returns the points of the clipped polygon. </description> </method> + <method name="compute_convex_mesh_points"> + <return type="PackedVector3Array" /> + <param index="0" name="planes" type="Plane[]" /> + <description> + Calculates and returns all the vertex points of a convex shape defined by an array of [param planes]. + </description> + </method> <method name="get_closest_point_to_segment"> <return type="Vector3" /> <param index="0" name="point" type="Vector3" /> @@ -73,6 +80,17 @@ Given the two 3D segments ([param p1], [param p2]) and ([param q1], [param q2]), finds those two points on the two segments that are closest to each other. Returns a [PackedVector3Array] that contains this point on ([param p1], [param p2]) as well the accompanying point on ([param q1], [param q2]). </description> </method> + <method name="get_triangle_barycentric_coords"> + <return type="Vector3" /> + <param index="0" name="point" type="Vector3" /> + <param index="1" name="a" type="Vector3" /> + <param index="2" name="b" type="Vector3" /> + <param index="3" name="c" type="Vector3" /> + <description> + Returns a [Vector3] containing weights based on how close a 3D position ([param point]) is to a triangle's different vertices ([param a], [param b] and [param c]). This is useful for interpolating between the data of different vertices in a triangle. One example use case is using this to smoothly rotate over a mesh instead of relying solely on face normals. + [url=https://en.wikipedia.org/wiki/Barycentric_coordinate_system]Here is a more detailed explanation of barycentric coordinates.[/url] + </description> + </method> <method name="ray_intersects_triangle"> <return type="Variant" /> <param index="0" name="from" type="Vector3" /> diff --git a/doc/classes/Gradient.xml b/doc/classes/Gradient.xml index f2b894d612..c7fa1f40e0 100644 --- a/doc/classes/Gradient.xml +++ b/doc/classes/Gradient.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="Gradient" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - A color interpolator resource which can be used to generate colors between user-defined color points. + A color transition. </brief_description> <description> - Given a set of colors, this resource will interpolate them in order. This means that if you have color 1, color 2 and color 3, the gradient will interpolate from color 1 to color 2 and from color 2 to color 3. The gradient will initially have 2 colors (black and white), one (black) at gradient lower offset 0 and the other (white) at the gradient higher offset 1. + This resource describes a color transition by defining a set of colored points and how to interpolate between them. See also [Curve] which supports more complex easing methods, but does not support colors. </description> <tutorials> @@ -15,7 +15,7 @@ <param index="0" name="offset" type="float" /> <param index="1" name="color" type="Color" /> <description> - Adds the specified color to the end of the gradient, with the specified offset. + Adds the specified color to the gradient, with the specified offset. </description> </method> <method name="get_color"> @@ -42,7 +42,7 @@ <return type="void" /> <param index="0" name="point" type="int" /> <description> - Removes the color at the index [param point]. + Removes the color at index [param point]. </description> </method> <method name="reverse"> diff --git a/doc/classes/GradientTexture1D.xml b/doc/classes/GradientTexture1D.xml index 7207de57fd..4e6b76c0a6 100644 --- a/doc/classes/GradientTexture1D.xml +++ b/doc/classes/GradientTexture1D.xml @@ -1,16 +1,16 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GradientTexture1D" inherits="Texture2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Gradient-filled texture. + A 1D texture that uses colors obtained from a [Gradient]. </brief_description> <description> - GradientTexture1D uses a [Gradient] to fill the texture data. The gradient will be filled from left to right using colors obtained from the gradient. This means the texture does not necessarily represent an exact copy of the gradient, but instead an interpolation of samples obtained from the gradient at fixed steps (see [member width]). See also [GradientTexture2D], [CurveTexture] and [CurveXYZTexture]. + A 1D texture that obtains colors from a [Gradient] to fill the texture data. The texture is filled by sampling the gradient for each pixel. Therefore, the texture does not necessarily represent an exact copy of the gradient, as it may miss some colors if there are not enough pixels. See also [GradientTexture2D], [CurveTexture] and [CurveXYZTexture]. </description> <tutorials> </tutorials> <members> <member name="gradient" type="Gradient" setter="set_gradient" getter="get_gradient"> - The [Gradient] that will be used to fill the texture. + The [Gradient] used to fill the texture. </member> <member name="resource_local_to_scene" type="bool" setter="set_local_to_scene" getter="is_local_to_scene" overrides="Resource" default="false" /> <member name="use_hdr" type="bool" setter="set_use_hdr" getter="is_using_hdr" default="false"> diff --git a/doc/classes/GradientTexture2D.xml b/doc/classes/GradientTexture2D.xml index 63e511173a..f6588ef791 100644 --- a/doc/classes/GradientTexture2D.xml +++ b/doc/classes/GradientTexture2D.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GradientTexture2D" inherits="Texture2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Gradient-filled 2D texture. + A 2D texture that creates a pattern with colors obtained from a [Gradient]. </brief_description> <description> - The texture uses a [Gradient] to fill the texture data in 2D space. The gradient is filled according to the specified [member fill] and [member repeat] types using colors obtained from the gradient. The texture does not necessarily represent an exact copy of the gradient, but instead an interpolation of samples obtained from the gradient at fixed steps (see [member width] and [member height]). See also [GradientTexture1D], [CurveTexture] and [CurveXYZTexture]. + A 2D texture that obtains colors from a [Gradient] to fill the texture data. This texture is able to transform a color transition into different patterns such as a linear or a radial gradient. The gradient is sampled individually for each pixel so it does not necessarily represent an exact copy of the gradient(see [member width] and [member height]). See also [GradientTexture1D], [CurveTexture] and [CurveXYZTexture]. </description> <tutorials> </tutorials> diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index c2a20827d2..6d907bbb28 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -121,6 +121,20 @@ Returns a SDL2-compatible device GUID on platforms that use gamepad remapping, e.g. [code]030000004c050000c405000000010000[/code]. Returns [code]"Default Gamepad"[/code] otherwise. Godot uses the [url=https://github.com/gabomdq/SDL_GameControllerDB]SDL2 game controller database[/url] to determine gamepad names and mappings based on this GUID. </description> </method> + <method name="get_joy_info" qualifiers="const"> + <return type="Dictionary" /> + <param index="0" name="device" type="int" /> + <description> + Returns a dictionary with extra platform-specific information about the device, e.g. the raw gamepad name from the OS or the Steam Input index. + On Windows the dictionary contains the following fields: + [code]xinput_index[/code]: The index of the controller in the XInput system. + On Linux: + [code]raw_name[/code]: The name of the controller as it came from the OS, before getting renamed by the godot controller database. + [code]vendor_id[/code]: The USB vendor ID of the device. + [code]product_id[/code]: The USB product ID of the device. + [code]steam_input_index[/code]: The Steam Input gamepad index, if the device is not a Steam Input device this key won't be present. + </description> + </method> <method name="get_joy_name"> <return type="String" /> <param index="0" name="device" type="int" /> diff --git a/doc/classes/Material.xml b/doc/classes/Material.xml index 505a1465bb..1118461445 100644 --- a/doc/classes/Material.xml +++ b/doc/classes/Material.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="Material" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Abstract base [Resource] for coloring and shading geometry. + Abstract base class for applying visual properties to an object, such as color and roughness. </brief_description> <description> - Material is a base [Resource] used for coloring and shading geometry. All materials inherit from it and almost all [VisualInstance3D] derived nodes carry a Material. A few flags and parameters are shared between all material types and are configured here. + [Material] is a base resource used for coloring and shading geometry. All materials inherit from it and almost all [VisualInstance3D] derived nodes carry a [Material]. A few flags and parameters are shared between all material types and are configured here. </description> <tutorials> <link title="3D Material Testers Demo">https://godotengine.org/asset-library/asset/123</link> diff --git a/doc/classes/MenuButton.xml b/doc/classes/MenuButton.xml index 8af233427c..e3a707ba72 100644 --- a/doc/classes/MenuButton.xml +++ b/doc/classes/MenuButton.xml @@ -69,7 +69,7 @@ <theme_item name="font_pressed_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)"> Text [Color] used when the [MenuButton] is being pressed. </theme_item> - <theme_item name="h_separation" data_type="constant" type="int" default="3"> + <theme_item name="h_separation" data_type="constant" type="int" default="4"> The horizontal space between [MenuButton]'s icon and text. Negative values will be treated as [code]0[/code] when used. </theme_item> <theme_item name="outline_size" data_type="constant" type="int" default="0"> diff --git a/doc/classes/NavigationAgent2D.xml b/doc/classes/NavigationAgent2D.xml index c1570f3149..71f8c50667 100644 --- a/doc/classes/NavigationAgent2D.xml +++ b/doc/classes/NavigationAgent2D.xml @@ -180,7 +180,7 @@ The distance to search for other agents. </member> <member name="path_desired_distance" type="float" setter="set_path_desired_distance" getter="get_path_desired_distance" default="20.0"> - The distance threshold before a path point is considered to be reached. This allows agents to not have to hit a path point on the path exactly, but only to reach its general area. If this value is set too high, the NavigationAgent will skip points on the path, which can lead too leaving the navigation mesh. If this value is set too low, the NavigationAgent will be stuck in a repath loop because it will constantly overshoot or undershoot the distance to the next point on each physics frame update. + The distance threshold before a path point is considered to be reached. This allows agents to not have to hit a path point on the path exactly, but only to reach its general area. If this value is set too high, the NavigationAgent will skip points on the path, which can lead to leaving the navigation mesh. If this value is set too low, the NavigationAgent will be stuck in a repath loop because it will constantly overshoot or undershoot the distance to the next point on each physics frame update. </member> <member name="path_max_distance" type="float" setter="set_path_max_distance" getter="get_path_max_distance" default="100.0"> The maximum distance the agent is allowed away from the ideal path to the final position. This can happen due to trying to avoid collisions. When the maximum distance is exceeded, it recalculates the ideal path. diff --git a/doc/classes/NavigationMeshGenerator.xml b/doc/classes/NavigationMeshGenerator.xml index dc9b7dc3b2..0997354aff 100644 --- a/doc/classes/NavigationMeshGenerator.xml +++ b/doc/classes/NavigationMeshGenerator.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NavigationMeshGenerator" inherits="Object" is_experimental="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="NavigationMeshGenerator" inherits="Object" is_deprecated="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> Helper class for creating and clearing navigation meshes. </brief_description> diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml index 86c605b8a3..b56b86f435 100644 --- a/doc/classes/NavigationServer3D.xml +++ b/doc/classes/NavigationServer3D.xml @@ -216,6 +216,15 @@ Bakes the provided [param navigation_mesh] with the data from the provided [param source_geometry_data]. After the process is finished the optional [param callback] will be called. </description> </method> + <method name="bake_from_source_geometry_data_async"> + <return type="void" /> + <param index="0" name="navigation_mesh" type="NavigationMesh" /> + <param index="1" name="source_geometry_data" type="NavigationMeshSourceGeometryData3D" /> + <param index="2" name="callback" type="Callable" default="Callable()" /> + <description> + Bakes the provided [param navigation_mesh] with the data from the provided [param source_geometry_data] as an async task running on a background thread. After the process is finished the optional [param callback] will be called. + </description> + </method> <method name="free_rid"> <return type="void" /> <param index="0" name="rid" type="RID" /> diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index 49ab3918bb..7510d6bb64 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -1022,12 +1022,12 @@ Notification received right after the scene with the node is saved in the editor. This notification is only sent in the Godot editor and will not occur in exported projects. </constant> <constant name="NOTIFICATION_WM_MOUSE_ENTER" value="1002"> - Notification received from the OS when the mouse enters the game window. - Implemented on desktop and web platforms. + Notification received when the mouse enters the window. + Implemented for embedded windows and on desktop and web platforms. </constant> <constant name="NOTIFICATION_WM_MOUSE_EXIT" value="1003"> - Notification received from the OS when the mouse leaves the game window. - Implemented on desktop and web platforms. + Notification received when the mouse leaves the window. + Implemented for embedded windows and on desktop and web platforms. </constant> <constant name="NOTIFICATION_WM_WINDOW_FOCUS_IN" value="1004"> Notification received when the node's parent [Window] is focused. This may be a change of focus between two windows of the same engine instance, or from the OS desktop or a third-party application to a window of the game (in which case [constant NOTIFICATION_APPLICATION_FOCUS_IN] is also emitted). diff --git a/doc/classes/Node3D.xml b/doc/classes/Node3D.xml index b5ead07a10..2606a2b320 100644 --- a/doc/classes/Node3D.xml +++ b/doc/classes/Node3D.xml @@ -269,7 +269,7 @@ </methods> <members> <member name="basis" type="Basis" setter="set_basis" getter="get_basis"> - Direct access to the 3x3 basis of the [Transform3D] property. + Direct access to the 3x3 basis of the [member transform] property. </member> <member name="global_position" type="Vector3" setter="set_global_position" getter="get_global_position"> Global position of this node. This is equivalent to [code]global_transform.origin[/code]. diff --git a/doc/classes/NodePath.xml b/doc/classes/NodePath.xml index 77a5cfa6c6..a56e80ec1f 100644 --- a/doc/classes/NodePath.xml +++ b/doc/classes/NodePath.xml @@ -101,12 +101,12 @@ Returns all subnames concatenated with a colon character ([code]:[/code]) as separator, i.e. the right side of the first colon in a node path. [codeblocks] [gdscript] - var nodepath = NodePath("Path2D/PathFollow2D/Sprite2D:texture:load_path") - print(nodepath.get_concatenated_subnames()) # texture:load_path + var node_path = NodePath("Path2D/PathFollow2D/Sprite2D:texture:load_path") + print(node_path.get_concatenated_subnames()) # texture:load_path [/gdscript] [csharp] - var nodepath = new NodePath("Path2D/PathFollow2D/Sprite2D:texture:load_path"); - GD.Print(nodepath.GetConcatenatedSubnames()); // texture:load_path + var nodePath = new NodePath("Path2D/PathFollow2D/Sprite2D:texture:load_path"); + GD.Print(nodePath.GetConcatenatedSubnames()); // texture:load_path [/csharp] [/codeblocks] </description> diff --git a/doc/classes/ORMMaterial3D.xml b/doc/classes/ORMMaterial3D.xml index d134107618..72620573da 100644 --- a/doc/classes/ORMMaterial3D.xml +++ b/doc/classes/ORMMaterial3D.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="ORMMaterial3D" inherits="BaseMaterial3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Physically based rendering (PBR) material that can be applied to 3D objects, can use an ORM texture. + A PBR (Physically Based Rendering) material to be used on 3D objects. Uses an ORM texture. </brief_description> <description> ORMMaterial3D's properties are inherited from [BaseMaterial3D]. Unlike [StandardMaterial3D], ORMMaterial3D uses a single texture for ambient occlusion, roughness and metallic maps, known as an ORM texture. diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml index 03169d390a..d4a6b57c58 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -538,7 +538,7 @@ <return type="bool" /> <description> Returns [code]true[/code] if application is running in the sandbox. - [b]Note:[/b] This method is implemented on macOS. + [b]Note:[/b] This method is implemented on macOS and Linux. </description> </method> <method name="is_stdout_verbose" qualifiers="const"> diff --git a/doc/classes/OptionButton.xml b/doc/classes/OptionButton.xml index 659f08d49a..7737754fc6 100644 --- a/doc/classes/OptionButton.xml +++ b/doc/classes/OptionButton.xml @@ -262,7 +262,7 @@ <theme_item name="arrow_margin" data_type="constant" type="int" default="4"> The horizontal space between the arrow icon and the right edge of the button. </theme_item> - <theme_item name="h_separation" data_type="constant" type="int" default="2"> + <theme_item name="h_separation" data_type="constant" type="int" default="4"> The horizontal space between [OptionButton]'s icon and text. Negative values will be treated as [code]0[/code] when used. </theme_item> <theme_item name="modulate_arrow" data_type="constant" type="int" default="0"> diff --git a/doc/classes/PanoramaSkyMaterial.xml b/doc/classes/PanoramaSkyMaterial.xml index ab32ca48dd..0d041b9b90 100644 --- a/doc/classes/PanoramaSkyMaterial.xml +++ b/doc/classes/PanoramaSkyMaterial.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="PanoramaSkyMaterial" inherits="Material" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - A [Material] used with [Sky] to draw a background texture. + A material that provides a special texture to a [Sky], usually an HDR panorama. </brief_description> <description> - A resource referenced in a [Sky] that is used to draw a background. The Panorama sky material functions similar to skyboxes in other engines, except it uses an equirectangular sky map instead of a cubemap. + A resource referenced in a [Sky] that is used to draw a background. [PanoramaSkyMaterial] functions similar to skyboxes in other engines, except it uses an equirectangular sky map instead of a [Cubemap]. Using an HDR panorama is strongly recommended for accurate, high-quality reflections. Godot supports the Radiance HDR ([code].hdr[/code]) and OpenEXR ([code].exr[/code]) image formats for this purpose. You can use [url=https://danilw.github.io/GLSL-howto/cubemap_to_panorama_js/cubemap_to_panorama.html]this tool[/url] to convert a cubemap to an equirectangular sky map. </description> diff --git a/doc/classes/ParticleProcessMaterial.xml b/doc/classes/ParticleProcessMaterial.xml index 69d3351733..355fec3713 100644 --- a/doc/classes/ParticleProcessMaterial.xml +++ b/doc/classes/ParticleProcessMaterial.xml @@ -1,12 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="ParticleProcessMaterial" inherits="Material" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Particle properties for [GPUParticles3D] and [GPUParticles2D] nodes. + Holds a particle configuration for [GPUParticles2D] or [GPUParticles3D] nodes. </brief_description> <description> - ParticleProcessMaterial defines particle properties and behavior. It is used in the [code]process_material[/code] of [GPUParticles3D] and [GPUParticles2D] emitter nodes. - Some of this material's properties are applied to each particle when emitted, while others can have a [CurveTexture] applied to vary values over the lifetime of the particle. - Particle animation is available only in [GPUParticles2D]. To use it, attach a [CanvasItemMaterial], with [member CanvasItemMaterial.particles_animation] enabled, to the particles node. + [ParticleProcessMaterial] defines particle properties and behavior. It is used in the [code]process_material[/code] of the [GPUParticles2D] and [GPUParticles3D] nodes. Some of this material's properties are applied to each particle when emitted, while others can have a [CurveTexture] or a [GradientTexture1D] applied to vary numerical or color values over the lifetime of the particle. </description> <tutorials> </tutorials> diff --git a/doc/classes/Performance.xml b/doc/classes/Performance.xml index 377dbba9fc..4d01fe1760 100644 --- a/doc/classes/Performance.xml +++ b/doc/classes/Performance.xml @@ -192,7 +192,7 @@ Number of islands in the 3D physics engine. [i]Lower is better.[/i] </constant> <constant name="AUDIO_OUTPUT_LATENCY" value="23" enum="Monitor"> - Output latency of the [AudioServer]. [i]Lower is better.[/i] + Output latency of the [AudioServer]. Equivalent to calling [method AudioServer.get_output_latency], it is not recommended to call this every frame. </constant> <constant name="NAVIGATION_ACTIVE_MAPS" value="24" enum="Monitor"> Number of active navigation maps in the [NavigationServer3D]. This also includes the two empty default navigation maps created by World2D and World3D. diff --git a/doc/classes/PhysicalSkyMaterial.xml b/doc/classes/PhysicalSkyMaterial.xml index e6a9980e19..13f9d93e66 100644 --- a/doc/classes/PhysicalSkyMaterial.xml +++ b/doc/classes/PhysicalSkyMaterial.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="PhysicalSkyMaterial" inherits="Material" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - [Sky] [Material] used for a physically based sky. + A material that defines a sky for a [Sky] resource by a set of physical properties. </brief_description> <description> The [PhysicalSkyMaterial] uses the Preetham analytic daylight model to draw a sky based on physical properties. This results in a substantially more realistic sky than the [ProceduralSkyMaterial], but it is slightly slower and less flexible. diff --git a/doc/classes/PhysicsDirectSpaceState3D.xml b/doc/classes/PhysicsDirectSpaceState3D.xml index 17324e49c6..b00dc4236d 100644 --- a/doc/classes/PhysicsDirectSpaceState3D.xml +++ b/doc/classes/PhysicsDirectSpaceState3D.xml @@ -67,6 +67,8 @@ [code]collider_id[/code]: The colliding object's ID. [code]normal[/code]: The object's surface normal at the intersection point, or [code]Vector3(0, 0, 0)[/code] if the ray starts inside the shape and [member PhysicsRayQueryParameters3D.hit_from_inside] is [code]true[/code]. [code]position[/code]: The intersection point. + [code]face_index[/code]: The face index at the intersection point. + [b]Note:[/b] Returns a valid number only if the intersected shape is a [ConcavePolygonShape3D]. Otherwise, [code]-1[/code] is returned. [code]rid[/code]: The intersecting object's [RID]. [code]shape[/code]: The shape index of the colliding shape. If the ray did not intersect anything, then an empty dictionary is returned instead. diff --git a/doc/classes/PlaceholderCubemap.xml b/doc/classes/PlaceholderCubemap.xml index b760bf359b..9cd0c26926 100644 --- a/doc/classes/PlaceholderCubemap.xml +++ b/doc/classes/PlaceholderCubemap.xml @@ -1,13 +1,13 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="PlaceholderCubemap" inherits="PlaceholderTextureLayered" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Placeholder class for a cubemap texture. + A [Cubemap] without image data. </brief_description> <description> - This class is used when loading a project that uses a [Cubemap] subclass in 2 conditions: - - When running the project exported in dedicated server mode, only the texture's dimensions are kept (as they may be relied upon for gameplay purposes or positioning of other elements). This allows reducing the exported PCK's size significantly. - - When this subclass is missing due to using a different engine version or build (e.g. modules disabled). - [b]Note:[/b] This is not intended to be used as an actual texture for rendering. It is not guaranteed to work like one in shaders or materials (for example when calculating UV). + This class replaces a [Cubemap] or a [Cubemap]-derived class in 2 conditions: + - In dedicated server mode, where the image data shouldn't affect game logic. This allows reducing the exported PCK's size significantly. + - When the [Cubemap]-derived class is missing, for example when using a different engine version. + [b]Note:[/b] This class is not intended for rendering or for use in shaders. Operations like calculating UV are not guaranteed to work. </description> <tutorials> </tutorials> diff --git a/doc/classes/PlaceholderCubemapArray.xml b/doc/classes/PlaceholderCubemapArray.xml index 1074824c0f..9ffbc316a8 100644 --- a/doc/classes/PlaceholderCubemapArray.xml +++ b/doc/classes/PlaceholderCubemapArray.xml @@ -1,13 +1,13 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="PlaceholderCubemapArray" inherits="PlaceholderTextureLayered" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Placeholder class for a cubemap texture array. + A [CubemapArray] without image data. </brief_description> <description> - This class is used when loading a project that uses a [CubemapArray] subclass in 2 conditions: - - When running the project exported in dedicated server mode, only the texture's dimensions are kept (as they may be relied upon for gameplay purposes or positioning of other elements). This allows reducing the exported PCK's size significantly. - - When this subclass is missing due to using a different engine version or build (e.g. modules disabled). - [b]Note:[/b] This is not intended to be used as an actual texture for rendering. It is not guaranteed to work like one in shaders or materials (for example when calculating UV). + This class replaces a [CubemapArray] or a [CubemapArray]-derived class in 2 conditions: + - In dedicated server mode, where the image data shouldn't affect game logic. This allows reducing the exported PCK's size significantly. + - When the [CubemapArray]-derived class is missing, for example when using a different engine version. + [b]Note:[/b] This class is not intended for rendering or for use in shaders. Operations like calculating UV are not guaranteed to work. </description> <tutorials> </tutorials> diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml index daf3163842..b72e65e63a 100644 --- a/doc/classes/PopupMenu.xml +++ b/doc/classes/PopupMenu.xml @@ -96,9 +96,11 @@ <param index="1" name="shortcut" type="Shortcut" /> <param index="2" name="id" type="int" default="-1" /> <param index="3" name="global" type="bool" default="false" /> + <param index="4" name="allow_echo" type="bool" default="false" /> <description> Adds a new item and assigns the specified [Shortcut] and icon [param texture] to it. Sets the label of the checkbox to the [Shortcut]'s name. An [param id] can optionally be provided. If no [param id] is provided, one will be created from the index. + If [param allow_echo] is [code]true[/code], the shortcut can be activated with echo events. </description> </method> <method name="add_item"> @@ -161,9 +163,11 @@ <param index="0" name="shortcut" type="Shortcut" /> <param index="1" name="id" type="int" default="-1" /> <param index="2" name="global" type="bool" default="false" /> + <param index="3" name="allow_echo" type="bool" default="false" /> <description> Adds a [Shortcut]. An [param id] can optionally be provided. If no [param id] is provided, one will be created from the index. + If [param allow_echo] is [code]true[/code], the shortcut can be activated with echo events. </description> </method> <method name="add_submenu_item"> diff --git a/doc/classes/ProceduralSkyMaterial.xml b/doc/classes/ProceduralSkyMaterial.xml index e3a1177cfa..54bffd009f 100644 --- a/doc/classes/ProceduralSkyMaterial.xml +++ b/doc/classes/ProceduralSkyMaterial.xml @@ -1,12 +1,12 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="ProceduralSkyMaterial" inherits="Material" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - A [Material] used with [Sky] to generate a background based on user input parameters. + A material that defines a simple sky for a [Sky] resource. </brief_description> <description> - ProceduralSkyMaterial provides a way to create an effective background quickly by defining procedural parameters for the sun, the sky and the ground. The sky and ground are very similar, they are defined by a color at the horizon, another color, and finally an easing curve to interpolate between these two colors. Similarly, the sun is described by a position in the sky, a color, and an easing curve. However, the sun also defines a minimum and maximum angle, these two values define at what distance the easing curve begins and ends from the sun, and thus end up defining the size of the sun in the sky. - The [ProceduralSkyMaterial] uses a lightweight shader to draw the sky and is thus suited for real time updates. When you do not need a quick sky that is not realistic, this is a good option. If you need a more realistic option, try using [PhysicalSkyMaterial] instead. - The [ProceduralSkyMaterial] supports up to 4 suns. Each sun takes its color, energy, and direction from the corresponding [DirectionalLight3D] in the scene. + [ProceduralSkyMaterial] provides a way to create an effective background quickly by defining procedural parameters for the sun, the sky and the ground. The sky and ground are defined by a main color, a color at the horizon, and an easing curve to interpolate between them. Suns are described by a position in the sky, a color, and a max angle from the sun at which the easing curve ends. The max angle therefore defines the size of the sun in the sky. + [ProceduralSkyMaterial] supports up to 4 suns, using the color, and energy, direction, and angular distance of the first four [DirectionalLight3D] nodes in the scene. This means that the suns are defined individually by the properties of their corresponding [DirectionalLight3D]s and globally by [member sun_angle_max] and [member sun_curve]. + [ProceduralSkyMaterial] uses a lightweight shader to draw the sky and is therefore suited for real time updates. This makes it a great option for a sky that is simple and computationally cheap, but unrealistic. If you need a more realistic procedural option, use [PhysicalSkyMaterial]. </description> <tutorials> </tutorials> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index d240b6ef48..4407176fd2 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -297,6 +297,9 @@ [b]Note:[/b] Restart the application after changing this setting. [b]Note:[/b] Changing this value can help on platforms or with third-party tools where hidden directory patterns are disallowed. Only modify this setting if you know that your environment requires it, as changing the default can impact compatibility with some external tools or plugins which expect the default [code].godot[/code] folder. </member> + <member name="application/config/version" type="String" setter="" getter="" default=""""> + The project's human-readable version identifier. This is used by exporters if the version identifier isn't overridden there. If [member application/config/version] is an empty string and the version identifier isn't overridden in an exporter, the exporter will use [code]1.0.0[/code] as a version identifier. + </member> <member name="application/config/windows_native_icon" type="String" setter="" getter="" default=""""> Icon set in [code].ico[/code] format used on Windows to set the game's icon. This is done automatically on start by calling [method DisplayServer.set_native_icon]. </member> @@ -806,10 +809,14 @@ <member name="display/window/stretch/mode" type="String" setter="" getter="" default=""disabled""> Defines how the base size is stretched to fit the resolution of the window or screen. [b]"disabled"[/b]: No stretching happens. One unit in the scene corresponds to one pixel on the screen. In this mode, [member display/window/stretch/aspect] has no effect. Recommended for non-game applications. - [b]"canvas_items"[/b]: The base size specified in width and height in the project settings is stretched to cover the whole screen (taking [member display/window/stretch/aspect] into account). This means that everything is rendered directly at the target resolution. 3D is unaffected, while in 2D, there is no longer a 1:1 correspondence between sprite pixels and screen pixels, which may result in scaling artifacts. Recommended for most games that don't use a pixel art aesthetic, although it is possible to use this stretch mode for pixel art games too (especially in 3D). - [b]"viewport"[/b]: The size of the root [Viewport] is set precisely to the base size specified in the Project Settings' Display section. The scene is rendered to this viewport first. Finally, this viewport is scaled to fit the screen (taking [member display/window/stretch/aspect] into account). Recommended for games that use a pixel art aesthetic. + [b]"canvas_items"[/b]: The base size specified in width and height in the project settings is stretched to cover the whole screen (taking [member display/window/stretch/aspect] into account). This means that everything is rendered directly at the target resolution. 3D is unaffected, while in 2D, there is no longer a 1:1 correspondence between sprite pixels and screen pixels, which may result in scaling artifacts. Recommended for most games that don't use a pixel art esthetic, although it is possible to use this stretch mode for pixel art games too (especially in 3D). + [b]"viewport"[/b]: The size of the root [Viewport] is set precisely to the base size specified in the Project Settings' Display section. The scene is rendered to this viewport first. Finally, this viewport is scaled to fit the screen (taking [member display/window/stretch/aspect] into account). Recommended for games that use a pixel art esthetic. </member> <member name="display/window/stretch/scale" type="float" setter="" getter="" default="1.0"> + The scale factor multiplier to use for 2D elements. This multiplies the final scale factor determined by [member display/window/stretch/mode]. If using the [b]Disabled[/b] stretch mode, this scale factor is applied as-is. This can be adjusted to make the UI easier to read on certain displays. + </member> + <member name="display/window/stretch/scale_mode" type="String" setter="" getter="" default=""fractional""> + The policy to use to determine the final scale factor for 2D elements. This affects how [member display/window/stretch/scale] is applied, in addition to the automatic scale factor determined by [member display/window/stretch/mode]. </member> <member name="display/window/subwindows/embed_subwindows" type="bool" setter="" getter="" default="true"> If [code]true[/code] subwindows are embedded in the main window. @@ -1988,6 +1995,12 @@ <member name="navigation/avoidance/thread_model/avoidance_use_multiple_threads" type="bool" setter="" getter="" default="true"> If enabled the avoidance calculations use multiple threads. </member> + <member name="navigation/baking/thread_model/baking_use_high_priority_threads" type="bool" setter="" getter="" default="true"> + If enabled and async navmesh baking uses multiple threads the threads run with high priority. + </member> + <member name="navigation/baking/thread_model/baking_use_multiple_threads" type="bool" setter="" getter="" default="true"> + If enabled the async navmesh baking uses multiple threads. + </member> <member name="network/limits/debugger/max_chars_per_second" type="int" setter="" getter="" default="32768"> Maximum number of characters allowed to send as output from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection. </member> @@ -2620,7 +2633,7 @@ </member> <member name="rendering/textures/default_filters/texture_mipmap_bias" type="float" setter="" getter="" default="0.0"> Affects the final texture sharpness by reading from a lower or higher mipmap (also called "texture LOD bias"). Negative values make mipmapped textures sharper but grainier when viewed at a distance, while positive values make mipmapped textures blurrier (even when up close). - Enabling temporal antialiasing ([member rendering/anti_aliasing/quality/use_taa]) will automatically apply a [code]-0.5[/code] offset to this value, while enabling FXAA ([member rendering/anti_aliasing/quality/screen_space_aa]) will automatically apply a [code]-0.25[/code] offset to this value. If both TAA and FXAA are enbled at the same time, an offset of [code]-0.75[/code] is applied to this value. + Enabling temporal antialiasing ([member rendering/anti_aliasing/quality/use_taa]) will automatically apply a [code]-0.5[/code] offset to this value, while enabling FXAA ([member rendering/anti_aliasing/quality/screen_space_aa]) will automatically apply a [code]-0.25[/code] offset to this value. If both TAA and FXAA are enabled at the same time, an offset of [code]-0.75[/code] is applied to this value. [b]Note:[/b] If [member rendering/scaling_3d/scale] is lower than [code]1.0[/code] (exclusive), [member rendering/textures/default_filters/texture_mipmap_bias] is used to adjust the automatic mipmap bias which is calculated internally based on the scale factor. The formula for this is [code]log2(scaling_3d_scale) + mipmap_bias[/code]. [b]Note:[/b] This property is only read when the project starts. To change the mipmap LOD bias at run-time, set [member Viewport.texture_mipmap_bias] instead. </member> @@ -2650,6 +2663,9 @@ <member name="rendering/textures/webp_compression/lossless_compression_factor" type="float" setter="" getter="" default="25"> The default compression factor for lossless WebP. Decompression speed is mostly unaffected by the compression factor. Supported values are 0 to 100. </member> + <member name="rendering/viewport/hdr_2d" type="bool" setter="" getter="" default="false"> + If [code]true[/code], enables [member Viewport.use_hdr_2d] on the root viewport. This allows 2D rendering to take advantage of effects requiring high dynamic range (e.g. 2D glow). + </member> <member name="rendering/viewport/transparent_background" type="bool" setter="" getter="" default="false"> If [code]true[/code], enables [member Viewport.transparent_bg] on the root viewport. This allows per-pixel transparency to be effective after also enabling [member display/window/size/transparent] and [member display/window/per_pixel_transparency/allowed]. </member> @@ -2704,4 +2720,11 @@ If [code]true[/code], Godot will compile shaders required for XR. </member> </members> + <signals> + <signal name="settings_changed"> + <description> + Emitted when any setting is changed, up to once per process frame. + </description> + </signal> + </signals> </class> diff --git a/doc/classes/RDPipelineMultisampleState.xml b/doc/classes/RDPipelineMultisampleState.xml index 8f43b3a2f7..94b8e3d137 100644 --- a/doc/classes/RDPipelineMultisampleState.xml +++ b/doc/classes/RDPipelineMultisampleState.xml @@ -25,7 +25,7 @@ The number of MSAA samples (or SSAA samples if [member enable_sample_shading] is [code]true[/code]) to perform. Higher values result in better antialiasing, at the cost of performance. </member> <member name="sample_masks" type="int[]" setter="set_sample_masks" getter="get_sample_masks" default="[]"> - The sampleSee the [url=https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#fragops-samplemask]sample mask Vulkan documentation[/url] for more details. + The sample mask array. See the [url=https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#fragops-samplemask]sample mask Vulkan documentation[/url] for more details. </member> </members> </class> diff --git a/doc/classes/RDPipelineRasterizationState.xml b/doc/classes/RDPipelineRasterizationState.xml index 9da66f3d0d..e97113cce4 100644 --- a/doc/classes/RDPipelineRasterizationState.xml +++ b/doc/classes/RDPipelineRasterizationState.xml @@ -29,7 +29,7 @@ The winding order to use to determine which face of a triangle is considered its front face. </member> <member name="line_width" type="float" setter="set_line_width" getter="get_line_width" default="1.0"> - THe line width to use when drawing lines (in pixels). Thick lines may not be supported on all hardware. + The line width to use when drawing lines (in pixels). Thick lines may not be supported on all hardware. </member> <member name="patch_control_points" type="int" setter="set_patch_control_points" getter="get_patch_control_points" default="1"> The number of control points to use when drawing a patch with tessellation enabled. Higher values result in higher quality at the cost of performance. diff --git a/doc/classes/RayCast3D.xml b/doc/classes/RayCast3D.xml index 83476a6d48..4c4db36aca 100644 --- a/doc/classes/RayCast3D.xml +++ b/doc/classes/RayCast3D.xml @@ -59,6 +59,12 @@ Returns the shape ID of the first object that the ray intersects, or [code]0[/code] if no object is intersecting the ray (i.e. [method is_colliding] returns [code]false[/code]). </description> </method> + <method name="get_collision_face_index" qualifiers="const"> + <return type="int" /> + <description> + Returns the collision object's face index at the collision point, or [code]-1[/code] if the shape intersecting the ray is not a [ConcavePolygonShape3D]. + </description> + </method> <method name="get_collision_mask_value" qualifiers="const"> <return type="bool" /> <param index="0" name="layer_number" type="int" /> diff --git a/doc/classes/Rect2.xml b/doc/classes/Rect2.xml index fdabb110c6..4f17e3cf2d 100644 --- a/doc/classes/Rect2.xml +++ b/doc/classes/Rect2.xml @@ -83,7 +83,7 @@ <return type="Rect2" /> <param index="0" name="to" type="Vector2" /> <description> - Returns a copy of this rectangle expanded to include the given [param to] point, if necessary. + Returns a copy of this rectangle expanded to align the edges with the given [param to] point, if necessary. [codeblocks] [gdscript] var rect = Rect2(0, 0, 5, 2) diff --git a/doc/classes/Rect2i.xml b/doc/classes/Rect2i.xml index 79d9e3f6df..c021a1be26 100644 --- a/doc/classes/Rect2i.xml +++ b/doc/classes/Rect2i.xml @@ -82,7 +82,7 @@ <return type="Rect2i" /> <param index="0" name="to" type="Vector2i" /> <description> - Returns a copy of this rectangle expanded to include the given [param to] point, if necessary. + Returns a copy of this rectangle expanded to align the edges with the given [param to] point, if necessary. [codeblocks] [gdscript] var rect = Rect2i(0, 0, 5, 2) diff --git a/doc/classes/RenderSceneBuffersRD.xml b/doc/classes/RenderSceneBuffersRD.xml index 3af5b78fcc..d40d835d26 100644 --- a/doc/classes/RenderSceneBuffersRD.xml +++ b/doc/classes/RenderSceneBuffersRD.xml @@ -5,7 +5,7 @@ </brief_description> <description> This object manages all 3D rendering buffers for the rendering device based renderers. An instance of this object is created for every viewport that has 3D rendering enabled. - All buffers are organised in [b]contexts[/b]. The default context is called [b]render_buffers[/b] and can contain amongst others the color buffer, depth buffer, velocity buffers, VRS density map and MSAA variants of these buffers. + All buffers are organized in [b]contexts[/b]. The default context is called [b]render_buffers[/b] and can contain amongst others the color buffer, depth buffer, velocity buffers, VRS density map and MSAA variants of these buffers. Buffers are only guaranteed to exist during rendering of the viewport. [b]Note:[/b] this is an internal rendering server object only exposed for GDExtension plugins. </description> diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml index e4e194adf0..14f6235d08 100644 --- a/doc/classes/RenderingDevice.xml +++ b/doc/classes/RenderingDevice.xml @@ -1786,7 +1786,7 @@ <constant name="STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT" value="1" enum="StorageBufferUsage" is_bitfield="true"> </constant> <constant name="UNIFORM_TYPE_SAMPLER" value="0" enum="UniformType"> - Sampler uniform. TODO: Difference between sampler and texture uniform + Sampler uniform. </constant> <constant name="UNIFORM_TYPE_SAMPLER_WITH_TEXTURE" value="1" enum="UniformType"> Sampler uniform with a texture. @@ -1795,16 +1795,16 @@ Texture uniform. </constant> <constant name="UNIFORM_TYPE_IMAGE" value="3" enum="UniformType"> - Image uniform. TODO: Difference between texture and image uniform + Image uniform. </constant> <constant name="UNIFORM_TYPE_TEXTURE_BUFFER" value="4" enum="UniformType"> - Texture buffer uniform. TODO: Difference between texture and texture buffe uniformr + Texture buffer uniform. </constant> <constant name="UNIFORM_TYPE_SAMPLER_WITH_TEXTURE_BUFFER" value="5" enum="UniformType"> - Sampler uniform with a texture buffer. TODO: Difference between texture and texture buffer uniform + Sampler uniform with a texture buffer. </constant> <constant name="UNIFORM_TYPE_IMAGE_BUFFER" value="6" enum="UniformType"> - Image buffer uniform. TODO: Difference between texture and image uniforms + Image buffer uniform. </constant> <constant name="UNIFORM_TYPE_UNIFORM_BUFFER" value="7" enum="UniformType"> Uniform buffer uniform. diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 02c1286392..d972a2214a 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -1375,7 +1375,7 @@ <return type="void" /> <param index="0" name="rid" type="RID" /> <description> - Tries to free an object in the RenderingServer. To avoid memory leaks, this should be called after using an object as memory management does not occur automatically when using RendeeringServer directly. + Tries to free an object in the RenderingServer. To avoid memory leaks, this should be called after using an object as memory management does not occur automatically when using RenderingServer directly. </description> </method> <method name="get_default_clear_color"> @@ -1450,6 +1450,7 @@ <description> Returns the name of the video adapter (e.g. "GeForce GTX 1080/PCIe/SSE2"). [b]Note:[/b] When running a headless or server binary, this function returns an empty string. + [b]Note:[/b] On the web platform, some browsers such as Firefox may report a different, fixed GPU name such as "GeForce GTX 980" (regardless of the user's actual GPU model). This is done to make fingerprinting more difficult. </description> </method> <method name="get_video_adapter_type" qualifiers="const"> @@ -3756,6 +3757,15 @@ If [code]true[/code], enables debanding on the specified viewport. Equivalent to [member ProjectSettings.rendering/anti_aliasing/quality/use_debanding]. </description> </method> + <method name="viewport_set_use_hdr_2d"> + <return type="void" /> + <param index="0" name="viewport" type="RID" /> + <param index="1" name="enabled" type="bool" /> + <description> + If [code]true[/code], 2D rendering will use a high dynamic range (HDR) format framebuffer matching the bit depth of the 3D framebuffer. When using the Forward+ renderer this will be a [code]RGBA16[/code] framebuffer, while when using the Mobile renderer it will be a [code]RGB10_A2[/code] framebuffer. Additionally, 2D rendering will take place in linear color space and will be converted to sRGB space immediately before blitting to the screen (if the Viewport is attached to the screen). Practically speaking, this means that the end result of the Viewport will not be clamped into the [code]0-1[/code] range and can be used in 3D rendering without color space adjustments. This allows 2D rendering to take advantage of effects requiring high dynamic range (e.g. 2D glow) as well as substantially improves the appearance of effects requiring highly detailed gradients. This setting has the same effect as [member Viewport.use_hdr_2d]. + [b]Note:[/b] This setting will have no effect when using the GL Compatibility renderer as the GL Compatibility renderer always renders in low dynamic range for performance reasons. + </description> + </method> <method name="viewport_set_use_occlusion_culling"> <return type="void" /> <param index="0" name="viewport" type="RID" /> diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml index c0d98ef921..fcc5925f8d 100644 --- a/doc/classes/SceneTree.xml +++ b/doc/classes/SceneTree.xml @@ -137,6 +137,7 @@ <param index="0" name="name" type="StringName" /> <description> Returns [code]true[/code] if the given group exists. + A group exists if any [Node] in the tree belongs to it (see [method Node.add_to_group]). Groups without nodes are removed automatically. </description> </method> <method name="notify_group"> @@ -225,7 +226,8 @@ For mobile platforms, see [member quit_on_go_back]. </member> <member name="current_scene" type="Node" setter="set_current_scene" getter="get_current_scene"> - The current scene. + Returns the root node of the currently running scene, regardless of its structure. + [b]Warning:[/b] Setting this directly might not work as expected, and will [i]not[/i] add or remove any nodes from the tree, consider using [method change_scene_to_file] or [method change_scene_to_packed] instead. </member> <member name="debug_collisions_hint" type="bool" setter="set_debug_collisions_hint" getter="is_debugging_collisions_hint" default="false"> If [code]true[/code], collision shapes will be visible when running the game from the editor for debugging purposes. diff --git a/doc/classes/ShaderGlobalsOverride.xml b/doc/classes/ShaderGlobalsOverride.xml index afd6ee527e..c5fe2a3e8a 100644 --- a/doc/classes/ShaderGlobalsOverride.xml +++ b/doc/classes/ShaderGlobalsOverride.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="ShaderGlobalsOverride" inherits="Node" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Overrides global shader parameters' values in a specific scene. + A node used to override global shader parameters' values in a scene. </brief_description> <description> Similar to how a [WorldEnvironment] node can be used to override the environment while a specific scene is loaded, [ShaderGlobalsOverride] can be used to override global shader parameters temporarily. Once the node is removed, the project-wide values for the global shader parameters are restored. See the [RenderingServer] [code]global_shader_parameter_*[/code] methods for more information. @@ -9,5 +9,6 @@ [b]Note:[/b] All [ShaderGlobalsOverride] nodes are made part of a [code]"shader_overrides_group"[/code] group when they are added to the scene tree. The currently active [ShaderGlobalsOverride] node also has a [code]"shader_overrides_group_active"[/code] group added to it. You can use this to check which [ShaderGlobalsOverride] node is currently active. </description> <tutorials> + <link title="Shading language">$DOCS_URL/tutorials/shaders/shader_reference/shading_language.html</link> </tutorials> </class> diff --git a/doc/classes/ShaderMaterial.xml b/doc/classes/ShaderMaterial.xml index e2d0696a4b..d0752b4b25 100644 --- a/doc/classes/ShaderMaterial.xml +++ b/doc/classes/ShaderMaterial.xml @@ -1,11 +1,12 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="ShaderMaterial" inherits="Material" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - A material that uses a custom [Shader] program. + A material defined by a custom [Shader] program and the values of its shader parameters. </brief_description> <description> - A material that uses a custom [Shader] program to render either items to screen or process particles. You can create multiple materials for the same shader but configure different values for the uniforms defined in the shader. - [b]Note:[/b] For performance reasons the [signal Resource.changed] signal is only emitted when the [member Resource.resource_name] is changed. Only in editor, is also emitted for [member shader] changes. + A material that uses a custom [Shader] program to render visual items (canvas items, meshes, skies, fog), or to process particles. Compared to other materials, [ShaderMaterial] gives deeper control over the generated shader code. For more information, see the shaders documentation index below. + Multiple [ShaderMaterial]s can use the same shader and configure different values for the shader uniforms. + [b]Note:[/b] For performance reasons, the [signal Resource.changed] signal is only emitted when the [member Resource.resource_name] changes. Only in editor, it is also emitted for [member shader] changes. </description> <tutorials> <link title="Shaders documentation index">$DOCS_URL/tutorials/shaders/index.html</link> diff --git a/doc/classes/Sky.xml b/doc/classes/Sky.xml index d52fd6ce40..d92319b390 100644 --- a/doc/classes/Sky.xml +++ b/doc/classes/Sky.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="Sky" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Background that uses a [Material] to draw a sky. + Defines a 3D environment's background by using a [Material]. </brief_description> <description> - The [Sky] class uses a [Material] to draw the background and update the reflection/radiance cubemaps. + The [Sky] class uses a [Material] to render a 3D environment's background and the light it emits by updating the reflection/radiance cubemaps. </description> <tutorials> </tutorials> diff --git a/doc/classes/SoftBody3D.xml b/doc/classes/SoftBody3D.xml index 1d227241ad..b5202bd4e3 100644 --- a/doc/classes/SoftBody3D.xml +++ b/doc/classes/SoftBody3D.xml @@ -107,6 +107,7 @@ <member name="drag_coefficient" type="float" setter="set_drag_coefficient" getter="get_drag_coefficient" default="0.0"> </member> <member name="linear_stiffness" type="float" setter="set_linear_stiffness" getter="get_linear_stiffness" default="0.5"> + Higher values will result in a stiffer body, while lower values will increase the body's ability to bend. The value can be between [code]0.0[/code] and [code]1.0[/code] (inclusive). </member> <member name="parent_collision_ignore" type="NodePath" setter="set_parent_collision_ignore" getter="get_parent_collision_ignore" default="NodePath("")"> [NodePath] to a [CollisionObject3D] this SoftBody3D should avoid clipping. diff --git a/doc/classes/StandardMaterial3D.xml b/doc/classes/StandardMaterial3D.xml index 8814169d1d..55247678cb 100644 --- a/doc/classes/StandardMaterial3D.xml +++ b/doc/classes/StandardMaterial3D.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="StandardMaterial3D" inherits="BaseMaterial3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Physically based rendering (PBR) material that can be applied to 3D objects. + A PBR (Physically Based Rendering) material to be used on 3D objects. </brief_description> <description> [StandardMaterial3D]'s properties are inherited from [BaseMaterial3D]. [StandardMaterial3D] uses separate textures for ambient occlusion, roughness and metallic maps. To use a single ORM map for all 3 textures, use an [ORMMaterial3D] instead. diff --git a/doc/classes/String.xml b/doc/classes/String.xml index 913f2f2654..9c31576b72 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -720,6 +720,12 @@ Replaces all [b]case-insensitive[/b] occurrences of [param what] inside the string with the given [param forwhat]. </description> </method> + <method name="reverse" qualifiers="const"> + <return type="String" /> + <description> + Returns the copy of this string in reverse order. + </description> + </method> <method name="rfind" qualifiers="const"> <return type="int" /> <param index="0" name="what" type="String" /> diff --git a/doc/classes/StringName.xml b/doc/classes/StringName.xml index 0814a30a8d..1c5032be9b 100644 --- a/doc/classes/StringName.xml +++ b/doc/classes/StringName.xml @@ -627,6 +627,12 @@ Replaces all [b]case-insensitive[/b] occurrences of [param what] inside the string with the given [param forwhat]. </description> </method> + <method name="reverse" qualifiers="const"> + <return type="String" /> + <description> + Returns the copy of this string in reverse order. + </description> + </method> <method name="rfind" qualifiers="const"> <return type="int" /> <param index="0" name="what" type="String" /> diff --git a/doc/classes/StyleBox.xml b/doc/classes/StyleBox.xml index 75fbb03dd6..8ba57766eb 100644 --- a/doc/classes/StyleBox.xml +++ b/doc/classes/StyleBox.xml @@ -110,7 +110,7 @@ [method get_margin] should be used to fetch this value as consumer instead of reading these properties directly. This is because it correctly respects negative values and the fallback mentioned above. </member> <member name="content_margin_left" type="float" setter="set_content_margin" getter="get_content_margin" default="-1.0"> - The left margin for the contents of this style box. Increasing this value reduces the space available to the contents from the left. + The left margin for the contents of this style box. Increasing this value reduces the space available to the contents from the left. Refer to [member content_margin_bottom] for extra considerations. </member> <member name="content_margin_right" type="float" setter="set_content_margin" getter="get_content_margin" default="-1.0"> diff --git a/doc/classes/SystemFont.xml b/doc/classes/SystemFont.xml index ea3dd0acd2..239bcc257c 100644 --- a/doc/classes/SystemFont.xml +++ b/doc/classes/SystemFont.xml @@ -19,9 +19,6 @@ <member name="antialiasing" type="int" setter="set_antialiasing" getter="get_antialiasing" enum="TextServer.FontAntialiasing" default="1"> Font anti-aliasing mode. </member> - <member name="fallbacks" type="Font[]" setter="set_fallbacks" getter="get_fallbacks" default="[]"> - Array of fallback [Font]s. - </member> <member name="font_italic" type="bool" setter="set_font_italic" getter="get_font_italic" default="false"> If set to [code]true[/code], italic or oblique font is preferred. </member> diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml index 4ed831c213..3eac11792f 100644 --- a/doc/classes/TileMap.xml +++ b/doc/classes/TileMap.xml @@ -242,7 +242,7 @@ <param index="3" name="alternative_tile" type="int" default="-1" /> <description> Returns a [Vector2i] array with the positions of all cells containing a tile in the given layer. Tiles may be filtered according to their source ([param source_id]), their atlas coordinates ([param atlas_coords]) or alternative id ([param alternative_tile]). - If a parameter has it's value set to the default one, this parameter is not used to filter a cell. Thus, if all parameters have their respective default value, this method returns the same result as [method get_used_cells]. + If a parameter has its value set to the default one, this parameter is not used to filter a cell. Thus, if all parameters have their respective default value, this method returns the same result as [method get_used_cells]. A cell is considered empty if its source identifier equals -1, its atlas coordinates identifiers is [code]Vector2(-1, -1)[/code] and its alternative identifier is -1. If [param layer] is negative, the layers are accessed from the last one. </description> @@ -316,7 +316,7 @@ <param index="3" name="atlas_coords" type="Vector2i" default="Vector2i(-1, -1)" /> <param index="4" name="alternative_tile" type="int" default="0" /> <description> - Sets the tile indentifiers for the cell on layer [param layer] at coordinates [param coords]. Each tile of the [TileSet] is identified using three parts: + Sets the tile identifiers for the cell on layer [param layer] at coordinates [param coords]. Each tile of the [TileSet] is identified using three parts: - The source identifier [param source_id] identifies a [TileSetSource] identifier. See [method TileSet.set_source_id], - The atlas coordinates identifier [param atlas_coords] identifies a tile coordinates in the atlas (if the source is a [TileSetAtlasSource]). For [TileSetScenesCollectionSource] it should always be [code]Vector2i(0, 0)[/code]), - The alternative tile identifier [param alternative_tile] identifies a tile alternative in the atlas (if the source is a [TileSetAtlasSource]), and the scene for a [TileSetScenesCollectionSource]. diff --git a/doc/classes/TileMapPattern.xml b/doc/classes/TileMapPattern.xml index b1b7720810..1733ecf592 100644 --- a/doc/classes/TileMapPattern.xml +++ b/doc/classes/TileMapPattern.xml @@ -71,7 +71,7 @@ <param index="2" name="atlas_coords" type="Vector2i" default="Vector2i(-1, -1)" /> <param index="3" name="alternative_tile" type="int" default="-1" /> <description> - Sets the tile indentifiers for the cell at coordinates [param coords]. See [method TileMap.set_cell]. + Sets the tile identifiers for the cell at coordinates [param coords]. See [method TileMap.set_cell]. </description> </method> <method name="set_size"> diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml index 1193855d7c..3cba3d7d11 100644 --- a/doc/classes/TileSet.xml +++ b/doc/classes/TileSet.xml @@ -332,7 +332,7 @@ <param index="1" name="coords_from" type="Vector2i" /> <param index="2" name="alternative_from" type="int" /> <description> - According to the configured proxies, maps the provided indentifiers to a new set of identifiers. The source ID, atlas coordinates ID and alternative tile ID are returned as a 3 elements Array. + According to the configured proxies, maps the provided identifiers to a new set of identifiers. The source ID, atlas coordinates ID and alternative tile ID are returned as a 3 elements Array. This function first look for matching alternative-level proxies, then coordinates-level proxies, then source-level proxies. If no proxy corresponding to provided identifiers are found, returns the same values the ones used as arguments. </description> diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml index a1932ba5b6..004e93ac04 100644 --- a/doc/classes/Tree.xml +++ b/doc/classes/Tree.xml @@ -606,6 +606,9 @@ <theme_item name="font_size" data_type="font_size" type="int"> Font size of the item's text. </theme_item> + <theme_item name="title_button_font_size" data_type="font_size" type="int"> + Font size of the title button's text. + </theme_item> <theme_item name="arrow" data_type="icon" type="Texture2D"> The arrow icon used when a foldable item is not collapsed. </theme_item> diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml index 9a5e7ed6f6..101717966e 100644 --- a/doc/classes/Viewport.xml +++ b/doc/classes/Viewport.xml @@ -52,6 +52,13 @@ Returns an individual bit on the rendering layer mask. </description> </method> + <method name="get_embedded_subwindows" qualifiers="const"> + <return type="Window[]" /> + <description> + Returns a list of the visible embedded [Window]s inside the viewport. + [b]Note:[/b] [Window]s inside other viewports will not be listed. + </description> + </method> <method name="get_final_transform" qualifiers="const"> <return type="Transform2D" /> <description> @@ -337,7 +344,7 @@ </member> <member name="texture_mipmap_bias" type="float" setter="set_texture_mipmap_bias" getter="get_texture_mipmap_bias" default="0.0"> Affects the final texture sharpness by reading from a lower or higher mipmap (also called "texture LOD bias"). Negative values make mipmapped textures sharper but grainier when viewed at a distance, while positive values make mipmapped textures blurrier (even when up close). - Enabling temporal antialiasing ([member use_taa]) will automatically apply a [code]-0.5[/code] offset to this value, while enabling FXAA ([member screen_space_aa]) will automatically apply a [code]-0.25[/code] offset to this value. If both TAA and FXAA are enbled at the same time, an offset of [code]-0.75[/code] is applied to this value. + Enabling temporal antialiasing ([member use_taa]) will automatically apply a [code]-0.5[/code] offset to this value, while enabling FXAA ([member screen_space_aa]) will automatically apply a [code]-0.25[/code] offset to this value. If both TAA and FXAA are enabled at the same time, an offset of [code]-0.75[/code] is applied to this value. [b]Note:[/b] If [member scaling_3d_scale] is lower than [code]1.0[/code] (exclusive), [member texture_mipmap_bias] is used to adjust the automatic mipmap bias which is calculated internally based on the scale factor. The formula for this is [code]log2(scaling_3d_scale) + mipmap_bias[/code]. To control this property on the root viewport, set the [member ProjectSettings.rendering/textures/default_filters/texture_mipmap_bias] project setting. </member> @@ -348,6 +355,10 @@ If [code]true[/code], uses a fast post-processing filter to make banding significantly less visible in 3D. 2D rendering is [i]not[/i] affected by debanding unless the [member Environment.background_mode] is [constant Environment.BG_CANVAS]. See also [member ProjectSettings.rendering/anti_aliasing/quality/use_debanding]. In some cases, debanding may introduce a slightly noticeable dithering pattern. It's recommended to enable debanding only when actually needed since the dithering pattern will make lossless-compressed screenshots larger. </member> + <member name="use_hdr_2d" type="bool" setter="set_use_hdr_2d" getter="is_using_hdr_2d" default="false"> + If [code]true[/code], 2D rendering will use an high dynamic range (HDR) format framebuffer matching the bit depth of the 3D framebuffer. When using the Forward+ renderer this will be a [code]RGBA16[/code] framebuffer, while when using the Mobile renderer it will be a [code]RGB10_A2[/code] framebuffer. Additionally, 2D rendering will take place in linear color space and will be converted to sRGB space immediately before blitting to the screen (if the Viewport is attached to the screen). Practically speaking, this means that the end result of the Viewport will not be clamped into the [code]0-1[/code] range and can be used in 3D rendering without color space adjustments. This allows 2D rendering to take advantage of effects requiring high dynamic range (e.g. 2D glow) as well as substantially improves the appearance of effects requiring highly detailed gradients. + [b]Note:[/b] This setting will have no effect when using the GL Compatibility renderer as the GL Compatibility renderer always renders in low dynamic range for performance reasons. + </member> <member name="use_occlusion_culling" type="bool" setter="set_use_occlusion_culling" getter="is_using_occlusion_culling" default="false"> If [code]true[/code], [OccluderInstance3D] nodes will be usable for occlusion culling in 3D for this viewport. For the root viewport, [member ProjectSettings.rendering/occlusion_culling/use_occlusion_culling] must be set to [code]true[/code] instead. [b]Note:[/b] Enabling occlusion culling has a cost on the CPU. Only enable occlusion culling if you actually plan to use it, and think whether your scene can actually benefit from occlusion culling. Large, open scenes with few or no objects blocking the view will generally not benefit much from occlusion culling. Large open scenes generally benefit more from mesh LOD and visibility ranges ([member GeometryInstance3D.visibility_range_begin] and [member GeometryInstance3D.visibility_range_end]) compared to occlusion culling. diff --git a/doc/classes/VisualShader.xml b/doc/classes/VisualShader.xml index bf48d80c66..f424a27a8f 100644 --- a/doc/classes/VisualShader.xml +++ b/doc/classes/VisualShader.xml @@ -4,10 +4,10 @@ A custom shader program with a visual editor. </brief_description> <description> - This class allows you to define a custom shader program that can be used for various materials to render objects. - The visual shader editor creates the shader. + This class provides a graph-like visual editor for creating a [Shader]. Although [VisualShader]s do not require coding, they share the same logic with script shaders. They use [VisualShaderNode]s that can be connected to each other to control the flow of the shader. The visual shader graph is converted to a script shader behind the scenes. </description> <tutorials> + <link title="Using VisualShaders">$DOCS_URL/tutorials/shaders/visual_shaders.html</link> </tutorials> <methods> <method name="add_node"> diff --git a/doc/classes/VisualShaderNode.xml b/doc/classes/VisualShaderNode.xml index 5d147bd542..baf9eded56 100644 --- a/doc/classes/VisualShaderNode.xml +++ b/doc/classes/VisualShaderNode.xml @@ -1,13 +1,13 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisualShaderNode" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Base class for nodes in a visual shader graph. + Base class for [VisualShader] nodes. Not related to scene nodes. </brief_description> <description> - Visual shader graphs consist of various nodes. Each node in the graph is a separate object and they are represented as a rectangular boxes with title and a set of properties. Each node has also connection ports that allow to connect it to another nodes and control the flow of the shader. + Visual shader graphs consist of various nodes. Each node in the graph is a separate object and they are represented as a rectangular boxes with title and a set of properties. Each node also has connection ports that allow to connect it to another nodes and control the flow of the shader. </description> <tutorials> - <link title="VisualShaders">$DOCS_URL/tutorials/shaders/visual_shaders.html</link> + <link title="Using VisualShaders">$DOCS_URL/tutorials/shaders/visual_shaders.html</link> </tutorials> <methods> <method name="clear_default_input_values"> diff --git a/doc/classes/VisualShaderNodeIf.xml b/doc/classes/VisualShaderNodeIf.xml index 5fc9a0cdf4..82799bb5fd 100644 --- a/doc/classes/VisualShaderNodeIf.xml +++ b/doc/classes/VisualShaderNodeIf.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisualShaderNodeIf" inherits="VisualShaderNode" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Compares two floating-point numbers in order to return a required vector within the visual shader graph. + Outputs a 3D vector based on the result of a floating point comparison within the visual shader graph. </brief_description> <description> - First two ports are scalar floating-point numbers to compare, third is tolerance comparison amount and last three ports represents a vectors returned if [code]a == b[/code], [code]a > b[/code] and [code]a < b[/code] respectively. + This visual shader node has six input ports. Port 1 and 2 provide the two floating point numbers [code]a[/code] and [code]b[/code] that will be compared. Port 3 is the tolerance, which allows similar floating point number to be considered equal. Ports 4 to 6 are the possible outputs, returned if [code]a == b[/code], [code]a > b[/code], or [code]a < b[/code] respectively. </description> <tutorials> </tutorials> diff --git a/doc/classes/Window.xml b/doc/classes/Window.xml index 5790fc347a..92cd11d720 100644 --- a/doc/classes/Window.xml +++ b/doc/classes/Window.xml @@ -10,6 +10,12 @@ <tutorials> </tutorials> <methods> + <method name="_get_contents_minimum_size" qualifiers="virtual const"> + <return type="Vector2" /> + <description> + Virtual method to be implemented by the user. Overrides the value returned by [method get_contents_minimum_size]. + </description> + </method> <method name="add_theme_color_override"> <return type="void" /> <param index="0" name="name" type="StringName" /> @@ -92,6 +98,7 @@ <return type="Vector2" /> <description> Returns the combined minimum size from the child [Control] nodes of the window. Use [method child_controls_changed] to update it when children nodes have changed. + The value returned by this method can be overridden with [method _get_contents_minimum_size]. </description> </method> <method name="get_flag" qualifiers="const"> @@ -562,6 +569,9 @@ <member name="content_scale_size" type="Vector2i" setter="set_content_scale_size" getter="get_content_scale_size" default="Vector2i(0, 0)"> Base size of the content (i.e. nodes that are drawn inside the window). If non-zero, [Window]'s content will be scaled when the window is resized to a different size. </member> + <member name="content_scale_stretch" type="int" setter="set_content_scale_stretch" getter="get_content_scale_stretch" enum="Window.ContentScaleStretch" default="0"> + The policy to use to determine the final scale factor for 2D elements. This affects how [member content_scale_factor] is applied, in addition to the automatic scale factor determined by [member content_scale_size]. + </member> <member name="current_screen" type="int" setter="set_current_screen" getter="get_current_screen"> The screen the window is currently on. </member> @@ -833,6 +843,12 @@ <constant name="CONTENT_SCALE_ASPECT_EXPAND" value="4" enum="ContentScaleAspect"> The content's aspect will be preserved. If the target size has different aspect from the base one, the content will stay in the top-left corner and add an extra visible area in the stretched space. </constant> + <constant name="CONTENT_SCALE_STRETCH_FRACTIONAL" value="0" enum="ContentScaleStretch"> + The content will be stretched according to a fractional factor. This fills all the space available in the window, but allows "pixel wobble" to occur due to uneven pixel scaling. + </constant> + <constant name="CONTENT_SCALE_STRETCH_INTEGER" value="1" enum="ContentScaleStretch"> + The content will be stretched only according to an integer factor, preserving sharp pixels. This may leave a black background visible on the window's edges depending on the window size. + </constant> <constant name="LAYOUT_DIRECTION_INHERITED" value="0" enum="LayoutDirection"> Automatic layout direction, determined from the parent window layout direction. </constant> diff --git a/doc/classes/XRInterfaceExtension.xml b/doc/classes/XRInterfaceExtension.xml index 9059a5a465..b933abd182 100644 --- a/doc/classes/XRInterfaceExtension.xml +++ b/doc/classes/XRInterfaceExtension.xml @@ -101,7 +101,7 @@ <method name="_get_system_info" qualifiers="virtual const"> <return type="Dictionary" /> <description> - Returns a [Dictionary] with system informationr elated to this interface. + Returns a [Dictionary] with system information related to this interface. </description> </method> <method name="_get_tracking_status" qualifiers="virtual const"> diff --git a/doc/classes/bool.xml b/doc/classes/bool.xml index 9e7d1fc965..647bd85d3d 100644 --- a/doc/classes/bool.xml +++ b/doc/classes/bool.xml @@ -4,42 +4,46 @@ A built-in boolean type. </brief_description> <description> - A [bool] is always one of two values: [code]true[/code] or [code]false[/code], similar to a switch that is either on or off. Booleans are used in programming for logic in condition statements. - Booleans can be directly used in [code]if[/code] and [code]elif[/code] statements. You don't need to add [code]== true[/code] or [code]== false[/code]: + The [bool] is a built-in [Variant] type that may only store one of two values: [code]true[/code] or [code]false[/code]. You can imagine it as a switch that can be either turned on or off, or as a binary digit that can either be 1 or 0. + Booleans can be directly used in [code]if[/code], and other conditional statements: [codeblocks] [gdscript] + var can_shoot = true if can_shoot: launch_bullet() [/gdscript] [csharp] + bool canShoot = true; if (canShoot) { - launchBullet(); + LaunchBullet(); } [/csharp] [/codeblocks] - Many common methods and operations return [bool]s, for example, [code]shooting_cooldown <= 0.0[/code] may evaluate to [code]true[/code] or [code]false[/code] depending on the number's value. - [bool]s are usually used with the logical operators [code]and[/code], [code]or[/code], and [code]not[/code] to create complex conditions: + All comparison operators return booleans ([code]==[/code], [code]>[/code], [code]<=[/code], etc.). As such, it is not necessary to compare booleans themselves. You do not need to add [code]== true[/code] or [code]== false[/code]. + Booleans can be combined with the logical operators [code]and[/code], [code]or[/code], [code]not[/code] to create complex conditions: [codeblocks] [gdscript] - if bullets > 0 and not is_reloading: + if bullets > 0 and not is_reloading(): launch_bullet() - if bullets == 0 or is_reloading: + if bullets == 0 or is_reloading(): play_clack_sound() [/gdscript] [csharp] - if (bullets > 0 && !isReloading) + if (bullets > 0 && !IsReloading()) { - launchBullet(); + LaunchBullet(); } - if (bullets == 0 || isReloading) + if (bullets == 0 || IsReloading()) { - playClackSound(); + PlayClackSound(); } [/csharp] [/codeblocks] + [b]Note:[/b] In modern programming languages, logical operators are evaluated in order. All remaining conditions are skipped if their result would have no effect on the final value. This concept is known as [url=https://en.wikipedia.org/wiki/Short-circuit_evaluation]short-circuit evaluation[/url] and can be useful to avoid evaluating expensive conditions in some performance-critical cases. + [b]Note:[/b] By convention, built-in methods and properties that return booleans are usually defined as yes-no questions, single adjectives, or similar ([method String.is_empty], [method Node.can_process], [member Camera2D.enabled], etc.). </description> <tutorials> </tutorials> @@ -47,7 +51,7 @@ <constructor name="bool"> <return type="bool" /> <description> - Constructs a default-initialized [bool] set to [code]false[/code]. + Constructs a [bool] set to [code]false[/code]. </description> </constructor> <constructor name="bool"> @@ -61,14 +65,14 @@ <return type="bool" /> <param index="0" name="from" type="float" /> <description> - Cast a [float] value to a boolean value. This method will return [code]false[/code] if [code]0.0[/code] is passed in, and [code]true[/code] for all other values. + Cast a [float] value to a boolean value. Returns [code]false[/code] if [param from] is equal to [code]0.0[/code] (including [code]-0.0[/code]), and [code]true[/code] for all other values (including [constant @GDScript.INF] and [constant @GDScript.NAN]). </description> </constructor> <constructor name="bool"> <return type="bool" /> <param index="0" name="from" type="int" /> <description> - Cast an [int] value to a boolean value. This method will return [code]false[/code] if [code]0[/code] is passed in, and [code]true[/code] for all other values. + Cast an [int] value to a boolean value. Returns [code]false[/code] if [param from] is equal to [code]0[/code], and [code]true[/code] for all other values. </description> </constructor> </constructors> @@ -77,7 +81,7 @@ <return type="bool" /> <param index="0" name="right" type="bool" /> <description> - Returns [code]true[/code] if two bools are different, i.e. one is [code]true[/code] and the other is [code]false[/code]. + Returns [code]true[/code] if the two booleans are not equal. That is, one is [code]true[/code] and the other is [code]false[/code]. This operation can be seen as a logical XOR. </description> </operator> <operator name="operator <"> @@ -91,7 +95,7 @@ <return type="bool" /> <param index="0" name="right" type="bool" /> <description> - Returns [code]true[/code] if two bools are equal, i.e. both are [code]true[/code] or both are [code]false[/code]. + Returns [code]true[/code] if the two booleans are equal. That is, both are [code]true[/code] or both are [code]false[/code]. This operation can be seen as a logical EQ or XNOR. </description> </operator> <operator name="operator >"> diff --git a/doc/classes/float.xml b/doc/classes/float.xml index 14e30b69a5..7d74e3d832 100644 --- a/doc/classes/float.xml +++ b/doc/classes/float.xml @@ -70,7 +70,7 @@ <description> Multiplies each component of the [Color], including the alpha, by the given [float]. [codeblock] - print(1.5 * Color(0.5, 0.5, 0.5)) # Color(0.75, 0.75, 0.75) + print(1.5 * Color(0.5, 0.5, 0.5)) # Prints "(0.75, 0.75, 0.75, 1.5)" [/codeblock] </description> </operator> diff --git a/doc/classes/int.xml b/doc/classes/int.xml index bc0da03e98..914cf75929 100644 --- a/doc/classes/int.xml +++ b/doc/classes/int.xml @@ -390,7 +390,7 @@ <operator name="operator ~"> <return type="int" /> <description> - Performs the bitwise [code]NOT[/code] operation on the [int]. Due to [url=https://en.wikipedia.org/wiki/Two%27s_complement/]2's complement[/url], it's effectively equal to [code]-(int + 1)[/code]. + Performs the bitwise [code]NOT[/code] operation on the [int]. Due to [url=https://en.wikipedia.org/wiki/Two%27s_complement]2's complement[/url], it's effectively equal to [code]-(int + 1)[/code]. [codeblock] print(~4) # Prints -5 print(~(-7)) # Prints 6 diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 86405fa3f0..9f8d9164ce 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -2048,16 +2048,26 @@ void RasterizerCanvasGLES3::occluder_polygon_set_cull_mode(RID p_occluder, RS::C void RasterizerCanvasGLES3::set_shadow_texture_size(int p_size) { GLES3::Config *config = GLES3::Config::get_singleton(); p_size = nearest_power_of_2_templated(p_size); - if (p_size == state.shadow_texture_size) { - return; - } if (p_size > config->max_texture_size) { p_size = config->max_texture_size; WARN_PRINT("Attempting to set CanvasItem shadow atlas size to " + itos(p_size) + " which is beyond limit of " + itos(config->max_texture_size) + "supported by hardware."); } + if (p_size == state.shadow_texture_size) { + return; + } state.shadow_texture_size = p_size; + + if (state.shadow_fb != 0) { + glDeleteFramebuffers(1, &state.shadow_fb); + GLES3::Utilities::get_singleton()->texture_free_data(state.shadow_texture); + glDeleteRenderbuffers(1, &state.shadow_depth_buffer); + state.shadow_fb = 0; + state.shadow_texture = 0; + state.shadow_depth_buffer = 0; + } + _update_shadow_atlas(); } bool RasterizerCanvasGLES3::free(RID p_rid) { @@ -2442,6 +2452,7 @@ RendererCanvasRender::PolygonID RasterizerCanvasGLES3::request_polygon(const Vec return id; } + void RasterizerCanvasGLES3::free_polygon(PolygonID p_polygon) { PolygonBuffers *pb_ptr = polygon_buffers.polygons.getptr(p_polygon); ERR_FAIL_COND(!pb_ptr); @@ -2521,6 +2532,8 @@ RasterizerCanvasGLES3::RasterizerCanvasGLES3() { GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); GLES3::Config *config = GLES3::Config::get_singleton(); + glVertexAttrib4f(RS::ARRAY_COLOR, 1.0, 1.0, 1.0, 1.0); + polygon_buffers.last_id = 1; // quad buffer { @@ -2700,6 +2713,7 @@ RasterizerCanvasGLES3::RasterizerCanvasGLES3() { GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.initialize(global_defines, 1); data.canvas_shader_default_version = GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_create(); + state.shadow_texture_size = GLOBAL_GET("rendering/2d/shadow_atlas/size"); shadow_render.shader.initialize(); shadow_render.shader_version = shadow_render.shader.version_create(); diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h index 2eac5f57e1..c1b3e20e33 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.h +++ b/drivers/gles3/rasterizer_canvas_gles3.h @@ -192,7 +192,7 @@ public: GLuint index_buffer = 0; int count = 0; bool color_disabled = false; - Color color; + Color color = Color(1.0, 1.0, 1.0, 1.0); }; struct { diff --git a/drivers/gles3/shaders/canvas_uniforms_inc.glsl b/drivers/gles3/shaders/canvas_uniforms_inc.glsl index d53c0fcb26..21fd4d3d9d 100644 --- a/drivers/gles3/shaders/canvas_uniforms_inc.glsl +++ b/drivers/gles3/shaders/canvas_uniforms_inc.glsl @@ -12,7 +12,7 @@ #define FLAGS_CLIP_RECT_UV uint(1 << 9) #define FLAGS_TRANSPOSE_RECT uint(1 << 10) -#define FLAGS_USING_LIGHT_MASK uint(1 << 11) +// (1 << 11) is for FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR in RD backends, unused here. #define FLAGS_NINEPACH_DRAW_CENTER uint(1 << 12) #define FLAGS_USING_PARTICLES uint(1 << 13) diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index aa6319f0ef..a36004209b 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -1957,14 +1957,16 @@ void MaterialStorage::global_shader_parameters_load_settings(bool p_load_texture if (gvtype >= RS::GLOBAL_VAR_TYPE_SAMPLER2D) { //textire if (!p_load_textures) { - value = RID(); continue; } String path = value; - Ref<Resource> resource = ResourceLoader::load(path); - ERR_CONTINUE(resource.is_null()); - value = resource; + if (path.is_empty()) { + value = RID(); + } else { + Ref<Resource> resource = ResourceLoader::load(path); + value = resource; + } } if (global_shader_uniforms.variables.has(name)) { diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h index bad2b31a31..c25dbef288 100644 --- a/drivers/gles3/storage/texture_storage.h +++ b/drivers/gles3/storage/texture_storage.h @@ -629,6 +629,8 @@ public: void render_target_clear_used(RID p_render_target); virtual void render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) override; virtual RS::ViewportMSAA render_target_get_msaa(RID p_render_target) const override; + virtual void render_target_set_use_hdr(RID p_render_target, bool p_use_hdr_2d) override {} + virtual bool render_target_is_using_hdr(RID p_render_target) const override { return false; } // new void render_target_set_as_unused(RID p_render_target) override { diff --git a/drivers/png/SCsub b/drivers/png/SCsub index fe8c8fa8cc..dd4777a19b 100644 --- a/drivers/png/SCsub +++ b/drivers/png/SCsub @@ -33,30 +33,30 @@ if env["builtin_libpng"]: # Needed for drivers includes and in platform/web. env.Prepend(CPPPATH=[thirdparty_dir]) - # Currently .ASM filter_neon.S does not compile on NT. - import os - - # Enable ARM NEON instructions on 32-bit Android to compile more optimized code. - use_neon = env["platform"] == "android" and env["arch"] == "arm32" and os.name != "nt" - if use_neon: - env_png.Append(CPPDEFINES=[("PNG_ARM_NEON_OPT", 2)]) - else: - env_png.Append(CPPDEFINES=[("PNG_ARM_NEON_OPT", 0)]) - env_thirdparty = env_png.Clone() env_thirdparty.disable_warnings() env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) - if use_neon: - env_neon = env_thirdparty.Clone() - if "S_compiler" in env: - env_neon["CC"] = env["S_compiler"] - neon_sources = [] - neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/arm_init.c")) - neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/filter_neon_intrinsics.c")) - neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/filter_neon.S")) - neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/palette_neon_intrinsics.c")) - thirdparty_obj += neon_sources + if env["arch"].startswith("arm"): + if env.msvc: # Can't compile assembly files with MSVC. + env_thirdparty.Append(CPPDEFINES=[("PNG_ARM_NEON_OPT"), 0]) + else: + env_neon = env_thirdparty.Clone() + if "S_compiler" in env: + env_neon["CC"] = env["S_compiler"] + neon_sources = [] + neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/arm_init.c")) + neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/filter_neon_intrinsics.c")) + neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/filter_neon.S")) + neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/palette_neon_intrinsics.c")) + thirdparty_obj += neon_sources + elif env["arch"].startswith("x86"): + env_thirdparty.Append(CPPDEFINES=["PNG_INTEL_SSE"]) + env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/intel/intel_init.c") + env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/intel/filter_sse2_intrinsics.c") + elif env["arch"] == "ppc64": + env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/powerpc/powerpc_init.c") + env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/powerpc/filter_vsx_intrinsics.c") env.drivers_sources += thirdparty_obj diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp index ab94d8911f..8ca396af20 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp +++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp @@ -370,27 +370,24 @@ Error AudioDriverPulseAudio::init() { } float AudioDriverPulseAudio::get_latency() { - if (latency == 0) { //only do this once since it's approximate anyway - lock(); + lock(); - pa_usec_t palat = 0; - if (pa_stream_get_state(pa_str) == PA_STREAM_READY) { - int negative = 0; + pa_usec_t pa_lat = 0; + if (pa_stream_get_state(pa_str) == PA_STREAM_READY) { + int negative = 0; - if (pa_stream_get_latency(pa_str, &palat, &negative) >= 0) { - if (negative) { - palat = 0; - } + if (pa_stream_get_latency(pa_str, &pa_lat, &negative) >= 0) { + if (negative) { + pa_lat = 0; } } + } - if (palat > 0) { - latency = double(palat) / 1000000.0; - } - - unlock(); + if (pa_lat > 0) { + latency = double(pa_lat) / 1000000.0; } + unlock(); return latency; } diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp index a80b7d449e..3b15cf60f7 100644 --- a/drivers/unix/file_access_unix.cpp +++ b/drivers/unix/file_access_unix.cpp @@ -313,19 +313,19 @@ uint64_t FileAccessUnix::_get_modified_time(const String &p_file) { } } -uint32_t FileAccessUnix::_get_unix_permissions(const String &p_file) { +BitField<FileAccess::UnixPermissionFlags> FileAccessUnix::_get_unix_permissions(const String &p_file) { String file = fix_path(p_file); struct stat status = {}; int err = stat(file.utf8().get_data(), &status); if (!err) { - return status.st_mode & 0x7FF; //only permissions + return status.st_mode & 0xFFF; //only permissions } else { ERR_FAIL_V_MSG(0, "Failed to get unix permissions for: " + p_file + "."); } } -Error FileAccessUnix::_set_unix_permissions(const String &p_file, uint32_t p_permissions) { +Error FileAccessUnix::_set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) { String file = fix_path(p_file); int err = chmod(file.utf8().get_data(), p_permissions); @@ -336,6 +336,74 @@ Error FileAccessUnix::_set_unix_permissions(const String &p_file, uint32_t p_per return FAILED; } +bool FileAccessUnix::_get_hidden_attribute(const String &p_file) { +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) + String file = fix_path(p_file); + + struct stat st = {}; + int err = stat(file.utf8().get_data(), &st); + ERR_FAIL_COND_V_MSG(err, false, "Failed to get attributes for: " + p_file); + + return (st.st_flags & UF_HIDDEN); +#else + return false; +#endif +} + +Error FileAccessUnix::_set_hidden_attribute(const String &p_file, bool p_hidden) { +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) + String file = fix_path(p_file); + + struct stat st = {}; + int err = stat(file.utf8().get_data(), &st); + ERR_FAIL_COND_V_MSG(err, FAILED, "Failed to get attributes for: " + p_file); + + if (p_hidden) { + err = chflags(file.utf8().get_data(), st.st_flags | UF_HIDDEN); + } else { + err = chflags(file.utf8().get_data(), st.st_flags & ~UF_HIDDEN); + } + ERR_FAIL_COND_V_MSG(err, FAILED, "Failed to set attributes for: " + p_file); + return OK; +#else + return ERR_UNAVAILABLE; +#endif +} + +bool FileAccessUnix::_get_read_only_attribute(const String &p_file) { +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) + String file = fix_path(p_file); + + struct stat st = {}; + int err = stat(file.utf8().get_data(), &st); + ERR_FAIL_COND_V_MSG(err, false, "Failed to get attributes for: " + p_file); + + return st.st_flags & UF_IMMUTABLE; +#else + return false; +#endif +} + +Error FileAccessUnix::_set_read_only_attribute(const String &p_file, bool p_ro) { +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) + String file = fix_path(p_file); + + struct stat st = {}; + int err = stat(file.utf8().get_data(), &st); + ERR_FAIL_COND_V_MSG(err, FAILED, "Failed to get attributes for: " + p_file); + + if (p_ro) { + err = chflags(file.utf8().get_data(), st.st_flags | UF_IMMUTABLE); + } else { + err = chflags(file.utf8().get_data(), st.st_flags & ~UF_IMMUTABLE); + } + ERR_FAIL_COND_V_MSG(err, FAILED, "Failed to set attributes for: " + p_file); + return OK; +#else + return ERR_UNAVAILABLE; +#endif +} + void FileAccessUnix::close() { _close(); } diff --git a/drivers/unix/file_access_unix.h b/drivers/unix/file_access_unix.h index 79c4e73636..2bfac27c4f 100644 --- a/drivers/unix/file_access_unix.h +++ b/drivers/unix/file_access_unix.h @@ -79,8 +79,13 @@ public: virtual bool file_exists(const String &p_path) override; ///< return true if a file exists virtual uint64_t _get_modified_time(const String &p_file) override; - virtual uint32_t _get_unix_permissions(const String &p_file) override; - virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override; + virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override; + virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override; + + virtual bool _get_hidden_attribute(const String &p_file) override; + virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override; + virtual bool _get_read_only_attribute(const String &p_file) override; + virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override; virtual void close() override; diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index d521f675fb..a55abe6a82 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -5043,17 +5043,24 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ _THREAD_SAFE_METHOD_ - Shader shader; + RID id; + if (p_placeholder.is_null()) { + id = shader_owner.make_rid(); + } else { + id = p_placeholder; + } + + Shader *shader = shader_owner.get_or_null(id); - shader.vertex_input_mask = vertex_input_mask; - shader.fragment_output_mask = fragment_output_mask; - shader.push_constant = push_constant; - shader.is_compute = is_compute; - shader.compute_local_size[0] = compute_local_size[0]; - shader.compute_local_size[1] = compute_local_size[1]; - shader.compute_local_size[2] = compute_local_size[2]; - shader.specialization_constants = specialization_constants; - shader.name = name; + shader->vertex_input_mask = vertex_input_mask; + shader->fragment_output_mask = fragment_output_mask; + shader->push_constant = push_constant; + shader->is_compute = is_compute; + shader->compute_local_size[0] = compute_local_size[0]; + shader->compute_local_size[1] = compute_local_size[1]; + shader->compute_local_size[2] = compute_local_size[2]; + shader->specialization_constants = specialization_constants; + shader->name = name; String error_text; @@ -5085,7 +5092,7 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ shader_stage.pName = "main"; shader_stage.pSpecializationInfo = nullptr; - shader.pipeline_stages.push_back(shader_stage); + shader->pipeline_stages.push_back(shader_stage); } // Proceed to create descriptor sets. @@ -5128,8 +5135,8 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ } } - shader.sets.push_back(set); - shader.set_formats.push_back(format); + shader->sets.push_back(set); + shader->set_formats.push_back(format); } } @@ -5139,13 +5146,13 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipeline_layout_create_info.pNext = nullptr; pipeline_layout_create_info.flags = 0; - pipeline_layout_create_info.setLayoutCount = shader.sets.size(); + pipeline_layout_create_info.setLayoutCount = shader->sets.size(); Vector<VkDescriptorSetLayout> layouts; - layouts.resize(shader.sets.size()); + layouts.resize(shader->sets.size()); for (int i = 0; i < layouts.size(); i++) { - layouts.write[i] = shader.sets[i].descriptor_set_layout; + layouts.write[i] = shader->sets[i].descriptor_set_layout; } pipeline_layout_create_info.pSetLayouts = layouts.ptr(); @@ -5164,7 +5171,7 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ pipeline_layout_create_info.pPushConstantRanges = nullptr; } - VkResult err = vkCreatePipelineLayout(device, &pipeline_layout_create_info, nullptr, &shader.pipeline_layout); + VkResult err = vkCreatePipelineLayout(device, &pipeline_layout_create_info, nullptr, &shader->pipeline_layout); if (err) { error_text = "Error (" + itos(err) + ") creating pipeline layout."; @@ -5174,23 +5181,19 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ if (!success) { // Clean up if failed. - for (int i = 0; i < shader.pipeline_stages.size(); i++) { - vkDestroyShaderModule(device, shader.pipeline_stages[i].module, nullptr); + for (int i = 0; i < shader->pipeline_stages.size(); i++) { + vkDestroyShaderModule(device, shader->pipeline_stages[i].module, nullptr); } - for (int i = 0; i < shader.sets.size(); i++) { - vkDestroyDescriptorSetLayout(device, shader.sets[i].descriptor_set_layout, nullptr); + for (int i = 0; i < shader->sets.size(); i++) { + vkDestroyDescriptorSetLayout(device, shader->sets[i].descriptor_set_layout, nullptr); } + shader_owner.free(id); + ERR_FAIL_V_MSG(RID(), error_text); } - RID id; - if (p_placeholder.is_null()) { - id = shader_owner.make_rid(shader); - } else { - shader_owner.initialize_rid(p_placeholder, shader); - id = p_placeholder; - } + #ifdef DEV_ENABLED set_resource_name(id, "RID:" + itos(id.get_id())); #endif @@ -5198,7 +5201,8 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ } RID RenderingDeviceVulkan::shader_create_placeholder() { - return shader_owner.allocate_rid(); + Shader shader; + return shader_owner.make_rid(shader); } uint32_t RenderingDeviceVulkan::shader_get_vertex_input_attribute_mask(RID p_shader) { @@ -5901,6 +5905,64 @@ void RenderingDeviceVulkan::uniform_set_set_invalidation_callback(RID p_uniform_ us->invalidated_callback_userdata = p_userdata; } +Error RenderingDeviceVulkan::buffer_copy(RID p_src_buffer, RID p_dst_buffer, uint32_t p_src_offset, uint32_t p_dst_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V_MSG(draw_list, ERR_INVALID_PARAMETER, + "Copying buffers is forbidden during creation of a draw list"); + ERR_FAIL_COND_V_MSG(compute_list, ERR_INVALID_PARAMETER, + "Copying buffers is forbidden during creation of a compute list"); + + // This method assumes the barriers have been pushed prior to being called, therefore no barriers are pushed + // for the source or destination buffers before performing the copy. These masks are effectively ignored. + VkPipelineShaderStageCreateFlags src_stage_mask = 0; + VkAccessFlags src_access_mask = 0; + Buffer *src_buffer = _get_buffer_from_owner(p_src_buffer, src_stage_mask, src_access_mask, BARRIER_MASK_NO_BARRIER); + if (!src_buffer) { + ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Source buffer argument is not a valid buffer of any type."); + } + + VkPipelineStageFlags dst_stage_mask = 0; + VkAccessFlags dst_access = 0; + if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { + // If the post barrier mask defines it, we indicate the destination buffer will require a barrier with these flags set + // after the copy command is queued. + dst_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT; + dst_access = VK_ACCESS_TRANSFER_WRITE_BIT; + } + + Buffer *dst_buffer = _get_buffer_from_owner(p_dst_buffer, dst_stage_mask, dst_access, p_post_barrier); + if (!dst_buffer) { + ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Destination buffer argument is not a valid buffer of any type."); + } + + // Validate the copy's dimensions for both buffers. + ERR_FAIL_COND_V_MSG((p_size + p_src_offset) > src_buffer->size, ERR_INVALID_PARAMETER, "Size is larger than the source buffer."); + ERR_FAIL_COND_V_MSG((p_size + p_dst_offset) > dst_buffer->size, ERR_INVALID_PARAMETER, "Size is larger than the destination buffer."); + + // Perform the copy. + VkBufferCopy region; + region.srcOffset = p_src_offset; + region.dstOffset = p_dst_offset; + region.size = p_size; + vkCmdCopyBuffer(frames[frame].draw_command_buffer, src_buffer->buffer, dst_buffer->buffer, 1, ®ion); + +#ifdef FORCE_FULL_BARRIER + _full_barrier(true); +#else + if (dst_stage_mask == 0) { + dst_stage_mask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + } + + // As indicated by the post barrier mask, push a new barrier. + if (p_post_barrier != RD::BARRIER_MASK_NO_BARRIER) { + _buffer_memory_barrier(dst_buffer->buffer, p_dst_offset, p_size, VK_PIPELINE_STAGE_TRANSFER_BIT, dst_stage_mask, VK_ACCESS_TRANSFER_WRITE_BIT, dst_access, true); + } +#endif + + return OK; +} + Error RenderingDeviceVulkan::buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, BitField<BarrierMask> p_post_barrier) { _THREAD_SAFE_METHOD_ diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h index fd832312ac..edff19a70c 100644 --- a/drivers/vulkan/rendering_device_vulkan.h +++ b/drivers/vulkan/rendering_device_vulkan.h @@ -1152,6 +1152,7 @@ public: virtual bool uniform_set_is_valid(RID p_uniform_set); virtual void uniform_set_set_invalidation_callback(RID p_uniform_set, InvalidationCallback p_callback, void *p_userdata); + virtual Error buffer_copy(RID p_src_buffer, RID p_dst_buffer, uint32_t p_src_offset, uint32_t p_dst_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); virtual Error buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); // Works for any buffer. virtual Error buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); virtual Vector<uint8_t> buffer_get_data(RID p_buffer, uint32_t p_offset = 0, uint32_t p_size = 0); diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp index c167caeb7c..c1632907eb 100644 --- a/drivers/vulkan/vulkan_context.cpp +++ b/drivers/vulkan/vulkan_context.cpp @@ -504,6 +504,10 @@ Error VulkanContext::_initialize_device_extensions() { register_requested_device_extension(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME, false); register_requested_device_extension(VK_KHR_MAINTENANCE_2_EXTENSION_NAME, false); + if (Engine::get_singleton()->is_generate_spirv_debug_info_enabled()) { + register_requested_device_extension(VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME, true); + } + // TODO consider the following extensions: // - VK_KHR_spirv_1_4 // - VK_KHR_swapchain_mutable_format @@ -1700,17 +1704,6 @@ Error VulkanContext::_window_create(DisplayServer::WindowID p_window_id, Display Error err = _update_swap_chain(&window); ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); - VkSemaphoreCreateInfo semaphoreCreateInfo = { - /*sType*/ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, - /*pNext*/ nullptr, - /*flags*/ 0, - }; - - for (uint32_t i = 0; i < FRAME_LAG; i++) { - VkResult vkerr = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &window.image_acquired_semaphores[i]); - ERR_FAIL_COND_V(vkerr, ERR_CANT_CREATE); - } - windows[p_window_id] = window; return OK; } @@ -1760,9 +1753,6 @@ VkFramebuffer VulkanContext::window_get_framebuffer(DisplayServer::WindowID p_wi void VulkanContext::window_destroy(DisplayServer::WindowID p_window_id) { ERR_FAIL_COND(!windows.has(p_window_id)); _clean_up_swap_chain(&windows[p_window_id]); - for (uint32_t i = 0; i < FRAME_LAG; i++) { - vkDestroySemaphore(device, windows[p_window_id].image_acquired_semaphores[i], nullptr); - } vkDestroySurfaceKHR(inst, windows[p_window_id].surface, nullptr); windows.erase(p_window_id); @@ -1792,6 +1782,17 @@ Error VulkanContext::_clean_up_swap_chain(Window *window) { if (separate_present_queue) { vkDestroyCommandPool(device, window->present_cmd_pool, nullptr); } + + for (uint32_t i = 0; i < FRAME_LAG; i++) { + // Destroy the semaphores now (we'll re-create it later if we have to). + // We must do this because the semaphore cannot be reused if it's in a signaled state + // (which happens if vkAcquireNextImageKHR returned VK_ERROR_OUT_OF_DATE_KHR or VK_SUBOPTIMAL_KHR) + // The only way to reset it would be to present the swapchain... the one we just destroyed. + // And the API has no way to "unsignal" the semaphore. + vkDestroySemaphore(device, window->image_acquired_semaphores[i], nullptr); + window->image_acquired_semaphores[i] = 0; + } + return OK; } @@ -2175,6 +2176,17 @@ Error VulkanContext::_update_swap_chain(Window *window) { // Reset current buffer. window->current_buffer = 0; + VkSemaphoreCreateInfo semaphoreCreateInfo = { + /*sType*/ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + /*pNext*/ nullptr, + /*flags*/ 0, + }; + + for (uint32_t i = 0; i < FRAME_LAG; i++) { + VkResult vkerr = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &window->image_acquired_semaphores[i]); + ERR_FAIL_COND_V(vkerr, ERR_CANT_CREATE); + } + return OK; } diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp index eeeb6ee689..6c84aee2b7 100644 --- a/drivers/wasapi/audio_driver_wasapi.cpp +++ b/drivers/wasapi/audio_driver_wasapi.cpp @@ -706,7 +706,7 @@ void AudioDriverWASAPI::write_sample(WORD format_tag, int bits_per_sample, BYTE } void AudioDriverWASAPI::thread_func(void *p_udata) { - CoInitializeEx(nullptr, COINIT_MULTITHREADED); + CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); AudioDriverWASAPI *ad = static_cast<AudioDriverWASAPI *>(p_udata); uint32_t avail_frames = 0; diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp index 6e69743d4e..c1e0c3fb60 100644 --- a/drivers/windows/file_access_windows.cpp +++ b/drivers/windows/file_access_windows.cpp @@ -381,14 +381,62 @@ uint64_t FileAccessWindows::_get_modified_time(const String &p_file) { } } -uint32_t FileAccessWindows::_get_unix_permissions(const String &p_file) { +BitField<FileAccess::UnixPermissionFlags> FileAccessWindows::_get_unix_permissions(const String &p_file) { return 0; } -Error FileAccessWindows::_set_unix_permissions(const String &p_file, uint32_t p_permissions) { +Error FileAccessWindows::_set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) { return ERR_UNAVAILABLE; } +bool FileAccessWindows::_get_hidden_attribute(const String &p_file) { + String file = fix_path(p_file); + + DWORD attrib = GetFileAttributesW((LPCWSTR)file.utf16().get_data()); + ERR_FAIL_COND_V_MSG(attrib == INVALID_FILE_ATTRIBUTES, false, "Failed to get attributes for: " + p_file); + return (attrib & FILE_ATTRIBUTE_HIDDEN); +} + +Error FileAccessWindows::_set_hidden_attribute(const String &p_file, bool p_hidden) { + String file = fix_path(p_file); + + DWORD attrib = GetFileAttributesW((LPCWSTR)file.utf16().get_data()); + ERR_FAIL_COND_V_MSG(attrib == INVALID_FILE_ATTRIBUTES, FAILED, "Failed to get attributes for: " + p_file); + BOOL ok; + if (p_hidden) { + ok = SetFileAttributesW((LPCWSTR)file.utf16().get_data(), attrib | FILE_ATTRIBUTE_HIDDEN); + } else { + ok = SetFileAttributesW((LPCWSTR)file.utf16().get_data(), attrib & ~FILE_ATTRIBUTE_HIDDEN); + } + ERR_FAIL_COND_V_MSG(!ok, FAILED, "Failed to set attributes for: " + p_file); + + return OK; +} + +bool FileAccessWindows::_get_read_only_attribute(const String &p_file) { + String file = fix_path(p_file); + + DWORD attrib = GetFileAttributesW((LPCWSTR)file.utf16().get_data()); + ERR_FAIL_COND_V_MSG(attrib == INVALID_FILE_ATTRIBUTES, false, "Failed to get attributes for: " + p_file); + return (attrib & FILE_ATTRIBUTE_READONLY); +} + +Error FileAccessWindows::_set_read_only_attribute(const String &p_file, bool p_ro) { + String file = fix_path(p_file); + + DWORD attrib = GetFileAttributesW((LPCWSTR)file.utf16().get_data()); + ERR_FAIL_COND_V_MSG(attrib == INVALID_FILE_ATTRIBUTES, FAILED, "Failed to get attributes for: " + p_file); + BOOL ok; + if (p_ro) { + ok = SetFileAttributesW((LPCWSTR)file.utf16().get_data(), attrib | FILE_ATTRIBUTE_READONLY); + } else { + ok = SetFileAttributesW((LPCWSTR)file.utf16().get_data(), attrib & ~FILE_ATTRIBUTE_READONLY); + } + ERR_FAIL_COND_V_MSG(!ok, FAILED, "Failed to set attributes for: " + p_file); + + return OK; +} + void FileAccessWindows::close() { _close(); } diff --git a/drivers/windows/file_access_windows.h b/drivers/windows/file_access_windows.h index 13c881e562..73143009fc 100644 --- a/drivers/windows/file_access_windows.h +++ b/drivers/windows/file_access_windows.h @@ -80,8 +80,13 @@ public: virtual bool file_exists(const String &p_name) override; ///< return true if a file exists uint64_t _get_modified_time(const String &p_file) override; - virtual uint32_t _get_unix_permissions(const String &p_file) override; - virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override; + virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override; + virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override; + + virtual bool _get_hidden_attribute(const String &p_file) override; + virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override; + virtual bool _get_read_only_attribute(const String &p_file) override; + virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override; virtual void close() override; diff --git a/editor/debugger/editor_debugger_inspector.cpp b/editor/debugger/editor_debugger_inspector.cpp index 1e9b7c2c60..b2fd84d2ed 100644 --- a/editor/debugger/editor_debugger_inspector.cpp +++ b/editor/debugger/editor_debugger_inspector.cpp @@ -232,7 +232,7 @@ void EditorDebuggerInspector::add_stack_variable(const Array &p_array) { PropertyHint h = PROPERTY_HINT_NONE; String hs; - if (var.var_type == Variant::OBJECT) { + if (var.var_type == Variant::OBJECT && v) { v = Object::cast_to<EncodedObjectAsID>(v)->get_object_id(); h = PROPERTY_HINT_OBJECT_ID; hs = "Object"; diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index 2c40f0e120..afa6aaf395 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -569,8 +569,11 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread error->set_custom_color(1, color); String error_title; - if (oe.callstack.size() > 0) { - // If available, use the script's stack in the error title. + if (!oe.source_func.is_empty() && source_is_project_file) { + // If source function is inside the project file. + error_title += oe.source_func + ": "; + } else if (oe.callstack.size() > 0) { + // Otherwise, if available, use the script's stack in the error title. error_title = _format_frame_text(&oe.callstack[0]) + ": "; } else if (!oe.source_func.is_empty()) { // Otherwise try to use the C++ source function. @@ -645,7 +648,10 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread if (i == 0) { stack_trace->set_text(0, "<" + TTR("Stack Trace") + ">"); stack_trace->set_text_alignment(0, HORIZONTAL_ALIGNMENT_LEFT); - error->set_metadata(0, meta); + if (!source_is_project_file) { + // Only override metadata if the source is not inside the project. + error->set_metadata(0, meta); + } tooltip += TTR("Stack Trace:") + "\n"; } diff --git a/editor/directory_create_dialog.cpp b/editor/directory_create_dialog.cpp index df860bab2c..fed7cb82c9 100644 --- a/editor/directory_create_dialog.cpp +++ b/editor/directory_create_dialog.cpp @@ -33,10 +33,10 @@ #include "core/io/dir_access.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/gui/editor_validation_panel.h" #include "scene/gui/box_container.h" #include "scene/gui/label.h" #include "scene/gui/line_edit.h" -#include "scene/gui/panel_container.h" static String sanitize_input(const String &p_path) { String path = p_path.strip_edges(); @@ -73,24 +73,17 @@ String DirectoryCreateDialog::_validate_path(const String &p_path) const { return String(); } -void DirectoryCreateDialog::_on_dir_path_changed(const String &p_text) { - const String path = sanitize_input(p_text); +void DirectoryCreateDialog::_on_dir_path_changed() { + const String path = sanitize_input(dir_path->get_text()); const String error = _validate_path(path); if (error.is_empty()) { - status_label->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor"))); - if (path.contains("/")) { - status_label->set_text(TTR("Using slashes in folder names will create subfolders recursively.")); - } else { - status_label->set_text(TTR("Folder name is valid.")); + validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Using slashes in folder names will create subfolders recursively."), EditorValidationPanel::MSG_OK); } } else { - status_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor"))); - status_label->set_text(error); + validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, error, EditorValidationPanel::MSG_ERROR); } - - get_ok_button()->set_disabled(!error.is_empty()); } void DirectoryCreateDialog::ok_pressed() { @@ -127,21 +120,13 @@ void DirectoryCreateDialog::config(const String &p_base_dir) { label->set_text(vformat(TTR("Create new folder in %s:"), base_dir)); dir_path->set_text("new folder"); dir_path->select_all(); - _on_dir_path_changed(dir_path->get_text()); + validation_panel->update(); } void DirectoryCreateDialog::_bind_methods() { ADD_SIGNAL(MethodInfo("dir_created")); } -void DirectoryCreateDialog::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_THEME_CHANGED: { - status_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree"))); - } break; - } -} - DirectoryCreateDialog::DirectoryCreateDialog() { set_title(TTR("Create Folder")); set_min_size(Size2i(480, 0) * EDSCALE); @@ -154,7 +139,6 @@ DirectoryCreateDialog::DirectoryCreateDialog() { vb->add_child(label); dir_path = memnew(LineEdit); - dir_path->connect("text_changed", callable_mp(this, &DirectoryCreateDialog::_on_dir_path_changed)); vb->add_child(dir_path); register_text_enter(dir_path); @@ -162,11 +146,11 @@ DirectoryCreateDialog::DirectoryCreateDialog() { spacing->set_custom_minimum_size(Size2(0, 10 * EDSCALE)); vb->add_child(spacing); - status_panel = memnew(PanelContainer); - status_panel->set_v_size_flags(Control::SIZE_EXPAND_FILL); - vb->add_child(status_panel); + validation_panel = memnew(EditorValidationPanel); + vb->add_child(validation_panel); + validation_panel->add_line(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Folder name is valid.")); + validation_panel->set_update_callback(callable_mp(this, &DirectoryCreateDialog::_on_dir_path_changed)); + validation_panel->set_accept_button(get_ok_button()); - status_label = memnew(Label); - status_label->set_v_size_flags(Control::SIZE_EXPAND_FILL); - status_panel->add_child(status_label); + dir_path->connect("text_changed", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1)); } diff --git a/editor/directory_create_dialog.h b/editor/directory_create_dialog.h index e2601181da..82e2e98ae5 100644 --- a/editor/directory_create_dialog.h +++ b/editor/directory_create_dialog.h @@ -33,9 +33,9 @@ #include "scene/gui/dialogs.h" +class EditorValidationPanel; class Label; class LineEdit; -class PanelContainer; class DirectoryCreateDialog : public ConfirmationDialog { GDCLASS(DirectoryCreateDialog, ConfirmationDialog); @@ -44,17 +44,13 @@ class DirectoryCreateDialog : public ConfirmationDialog { Label *label = nullptr; LineEdit *dir_path = nullptr; - - PanelContainer *status_panel = nullptr; - Label *status_label = nullptr; + EditorValidationPanel *validation_panel = nullptr; String _validate_path(const String &p_path) const; - - void _on_dir_path_changed(const String &p_text); + void _on_dir_path_changed(); protected: static void _bind_methods(); - void _notification(int p_what); virtual void ok_pressed() override; virtual void _post_popup() override; diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index d95b1de365..7ac812101a 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -38,6 +38,7 @@ #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "editor/editor_undo_redo_manager.h" +#include "editor/gui/editor_validation_panel.h" #include "editor/inspector_dock.h" #include "editor/plugins/script_editor_plugin.h" #include "multi_node_edit.h" @@ -3927,12 +3928,6 @@ void EditorInspector::_notification(int p_what) { } } break; - case NOTIFICATION_THEME_CHANGED: { - if (add_meta_error_panel) { - add_meta_error_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree"))); - } - } break; - case NOTIFICATION_PREDELETE: { edit(nullptr); //just in case } break; @@ -4083,27 +4078,17 @@ void EditorInspector::_add_meta_confirm() { undo_redo->commit_action(); } -void EditorInspector::_check_meta_name(const String &p_name) { - String error; - - if (p_name == "") { - error = TTR("Metadata name can't be empty."); - } else if (!p_name.is_valid_identifier()) { - error = TTR("Metadata name must be a valid identifier."); - } else if (object->has_meta(p_name)) { - error = vformat(TTR("Metadata with name \"%s\" already exists."), p_name); - } else if (p_name[0] == '_') { - error = TTR("Names starting with _ are reserved for editor-only metadata."); - } +void EditorInspector::_check_meta_name() { + const String meta_name = add_meta_name->get_text(); - if (error != "") { - add_meta_error->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor"))); - add_meta_error->set_text(error); - add_meta_dialog->get_ok_button()->set_disabled(true); - } else { - add_meta_error->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor"))); - add_meta_error->set_text(TTR("Metadata name is valid.")); - add_meta_dialog->get_ok_button()->set_disabled(false); + if (meta_name.is_empty()) { + validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Metadata name can't be empty."), EditorValidationPanel::MSG_ERROR); + } else if (!meta_name.is_valid_identifier()) { + validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Metadata name must be a valid identifier."), EditorValidationPanel::MSG_ERROR); + } else if (object->has_meta(meta_name)) { + validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, vformat(TTR("Metadata with name \"%s\" already exists."), meta_name), EditorValidationPanel::MSG_ERROR); + } else if (meta_name[0] == '_') { + validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Names starting with _ are reserved for editor-only metadata."), EditorValidationPanel::MSG_ERROR); } } @@ -4143,16 +4128,13 @@ void EditorInspector::_show_add_meta_dialog() { add_meta_dialog->register_text_enter(add_meta_name); add_meta_dialog->connect("confirmed", callable_mp(this, &EditorInspector::_add_meta_confirm)); - add_meta_error_panel = memnew(PanelContainer); - vbc->add_child(add_meta_error_panel); - if (is_inside_tree()) { - add_meta_error_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree"))); - } - - add_meta_error = memnew(Label); - add_meta_error_panel->add_child(add_meta_error); + validation_panel = memnew(EditorValidationPanel); + vbc->add_child(validation_panel); + validation_panel->add_line(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Metadata name is valid.")); + validation_panel->set_update_callback(callable_mp(this, &EditorInspector::_check_meta_name)); + validation_panel->set_accept_button(add_meta_dialog->get_ok_button()); - add_meta_name->connect("text_changed", callable_mp(this, &EditorInspector::_check_meta_name)); + add_meta_name->connect("text_changed", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1)); } Node *node = Object::cast_to<Node>(object); @@ -4164,9 +4146,9 @@ void EditorInspector::_show_add_meta_dialog() { } add_meta_dialog->popup_centered(); - add_meta_name->set_text(""); - _check_meta_name(""); add_meta_name->grab_focus(); + add_meta_name->set_text(""); + validation_panel->update(); } void EditorInspector::_bind_methods() { diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index 9a4c4f7f99..ed0d0ec373 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -39,6 +39,7 @@ class AcceptDialog; class Button; class ConfirmationDialog; class EditorInspector; +class EditorValidationPanel; class LineEdit; class OptionButton; class PanelContainer; @@ -543,12 +544,11 @@ class EditorInspector : public ScrollContainer { ConfirmationDialog *add_meta_dialog = nullptr; LineEdit *add_meta_name = nullptr; OptionButton *add_meta_type = nullptr; - PanelContainer *add_meta_error_panel = nullptr; - Label *add_meta_error = nullptr; + EditorValidationPanel *validation_panel = nullptr; void _add_meta_confirm(); void _show_add_meta_dialog(); - void _check_meta_name(const String &p_name); + void _check_meta_name(); protected: static void _bind_methods(); diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp index 1bc9f00f08..4c2f6b3176 100644 --- a/editor/editor_log.cpp +++ b/editor/editor_log.cpp @@ -212,7 +212,7 @@ void EditorLog::clear() { _clear_request(); } -void EditorLog::_process_message(const String &p_msg, MessageType p_type) { +void EditorLog::_process_message(const String &p_msg, MessageType p_type, bool p_clear) { if (messages.size() > 0 && messages[messages.size() - 1].text == p_msg && messages[messages.size() - 1].type == p_type) { // If previous message is the same as the new one, increase previous count rather than adding another // instance to the messages list. @@ -222,7 +222,7 @@ void EditorLog::_process_message(const String &p_msg, MessageType p_type) { _add_log_line(previous, collapse); } else { // Different message to the previous one received. - LogMessage message(p_msg, p_type); + LogMessage message(p_msg, p_type, p_clear); _add_log_line(message); messages.push_back(message); } @@ -237,9 +237,10 @@ void EditorLog::add_message(const String &p_msg, MessageType p_type) { // search functionality (see the comments on the PR above for more details). This behavior // also matches that of other IDE's. Vector<String> lines = p_msg.split("\n", true); + int line_count = lines.size(); - for (int i = 0; i < lines.size(); i++) { - _process_message(lines[i], p_type); + for (int i = 0; i < line_count; i++) { + _process_message(lines[i], p_type, i == line_count - 1); } } @@ -338,7 +339,9 @@ void EditorLog::_add_log_line(LogMessage &p_message, bool p_replace_previous) { } else { log->add_text(p_message.text); } - log->pop_all(); // Pop all unclosed tags. + if (p_message.clear || p_message.type != MSG_TYPE_STD_RICH) { + log->pop_all(); // Pop all unclosed tags. + } log->add_newline(); if (p_replace_previous) { diff --git a/editor/editor_log.h b/editor/editor_log.h index b875066afa..07f3a25c3e 100644 --- a/editor/editor_log.h +++ b/editor/editor_log.h @@ -60,12 +60,14 @@ private: String text; MessageType type; int count = 1; + bool clear = true; LogMessage() {} - LogMessage(const String p_text, MessageType p_type) : + LogMessage(const String p_text, MessageType p_type, bool p_clear) : text(p_text), - type(p_type) { + type(p_type), + clear(p_clear) { } }; @@ -166,7 +168,7 @@ private: void _set_search_visible(bool p_visible); void _search_changed(const String &p_text); - void _process_message(const String &p_msg, MessageType p_type); + void _process_message(const String &p_msg, MessageType p_type, bool p_clear); void _reset_message_counts(); void _set_collapse(bool p_collapse); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 2fa02ae1d4..d9e392e852 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -496,6 +496,9 @@ void EditorNode::_update_from_settings() { Viewport::MSAA msaa = Viewport::MSAA(int(GLOBAL_GET("rendering/anti_aliasing/quality/msaa_2d"))); scene_root->set_msaa_2d(msaa); + bool use_hdr_2d = GLOBAL_GET("rendering/viewport/hdr_2d"); + scene_root->set_use_hdr_2d(use_hdr_2d); + float mesh_lod_threshold = GLOBAL_GET("rendering/mesh_lod/lod_change/threshold_pixels"); scene_root->set_mesh_lod_threshold(mesh_lod_threshold); @@ -5006,12 +5009,17 @@ void EditorNode::_load_editor_layout() { config.instantiate(); Error err = config->load(EditorPaths::get_singleton()->get_project_settings_dir().path_join("editor_layout.cfg")); if (err != OK) { // No config. - // If config is not found, expand the res:// folder by default. + // If config is not found, expand the res:// folder and favorites by default. TreeItem *root = FileSystemDock::get_singleton()->get_tree_control()->get_item_with_metadata("res://", 0); if (root) { root->set_collapsed(false); } + TreeItem *favorites = FileSystemDock::get_singleton()->get_tree_control()->get_item_with_metadata("Favorites", 0); + if (favorites) { + favorites->set_collapsed(false); + } + if (overridden_default_layout >= 0) { _layout_menu_option(overridden_default_layout); } @@ -5673,12 +5681,13 @@ void EditorNode::_scene_tab_exit() { } void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) { + int tab_id = scene_tabs->get_hovered_tab(); Ref<InputEventMouseButton> mb = p_input; if (mb.is_valid()) { - if (scene_tabs->get_hovered_tab() >= 0) { + if (tab_id >= 0) { if (mb->get_button_index() == MouseButton::MIDDLE && mb->is_pressed()) { - _scene_tab_closed(scene_tabs->get_hovered_tab()); + _scene_tab_closed(tab_id); } } else if (mb->get_button_index() == MouseButton::LEFT && mb->is_double_click()) { int tab_buttons = 0; @@ -5697,12 +5706,12 @@ void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) { scene_tabs_context_menu->reset_size(); scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/new_scene"), FILE_NEW_SCENE); - if (scene_tabs->get_hovered_tab() >= 0) { + if (tab_id >= 0) { scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_scene"), FILE_SAVE_SCENE); scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_scene_as"), FILE_SAVE_AS_SCENE); } scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_all_scenes"), FILE_SAVE_ALL_SCENES); - if (scene_tabs->get_hovered_tab() >= 0) { + if (tab_id >= 0) { scene_tabs_context_menu->add_separator(); scene_tabs_context_menu->add_item(TTR("Show in FileSystem"), FILE_SHOW_IN_FILESYSTEM); scene_tabs_context_menu->add_item(TTR("Play This Scene"), FILE_RUN_SCENE); @@ -5716,7 +5725,13 @@ void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) { scene_tabs_context_menu->set_item_disabled(scene_tabs_context_menu->get_item_index(FILE_OPEN_PREV), true); } scene_tabs_context_menu->add_item(TTR("Close Other Tabs"), FILE_CLOSE_OTHERS); + if (editor_data.get_edited_scene_count() <= 1) { + scene_tabs_context_menu->set_item_disabled(file_menu->get_item_index(FILE_CLOSE_OTHERS), true); + } scene_tabs_context_menu->add_item(TTR("Close Tabs to the Right"), FILE_CLOSE_RIGHT); + if (editor_data.get_edited_scene_count() == tab_id + 1) { + scene_tabs_context_menu->set_item_disabled(file_menu->get_item_index(FILE_CLOSE_RIGHT), true); + } scene_tabs_context_menu->add_item(TTR("Close All Tabs"), FILE_CLOSE_ALL); } scene_tabs_context_menu->set_position(scene_tabs->get_screen_position() + mb->get_position()); @@ -6730,6 +6745,9 @@ void EditorNode::_print_handler(void *p_this, const String &p_string, bool p_err } void EditorNode::_print_handler_impl(const String &p_string, bool p_error, bool p_rich) { + if (!singleton) { + return; + } if (p_error) { singleton->log->add_message(p_string, EditorLog::MSG_TYPE_ERROR); } else if (p_rich) { @@ -6845,6 +6863,7 @@ EditorNode::EditorNode() { DisplayServer::get_singleton()->cursor_set_custom_image(Ref<Resource>()); } + DEV_ASSERT(!singleton); singleton = this; EditorUndoRedoManager::get_singleton()->connect("version_changed", callable_mp(this, &EditorNode::_update_undo_redo_allowed)); @@ -6916,8 +6935,7 @@ EditorNode::EditorNode() { { // Register importers at the beginning, so dialogs are created with the right extensions. - Ref<ResourceImporterTexture> import_texture; - import_texture.instantiate(); + Ref<ResourceImporterTexture> import_texture = memnew(ResourceImporterTexture(true)); ResourceFormatImporter::get_singleton()->add_importer(import_texture); Ref<ResourceImporterLayeredTexture> import_cubemap; @@ -6935,8 +6953,7 @@ EditorNode::EditorNode() { import_cubemap_array->set_mode(ResourceImporterLayeredTexture::MODE_CUBEMAP_ARRAY); ResourceFormatImporter::get_singleton()->add_importer(import_cubemap_array); - Ref<ResourceImporterLayeredTexture> import_3d; - import_3d.instantiate(); + Ref<ResourceImporterLayeredTexture> import_3d = memnew(ResourceImporterLayeredTexture(true)); import_3d->set_mode(ResourceImporterLayeredTexture::MODE_3D); ResourceFormatImporter::get_singleton()->add_importer(import_3d); @@ -6976,12 +6993,10 @@ EditorNode::EditorNode() { import_shader_file.instantiate(); ResourceFormatImporter::get_singleton()->add_importer(import_shader_file); - Ref<ResourceImporterScene> import_scene; - import_scene.instantiate(); + Ref<ResourceImporterScene> import_scene = memnew(ResourceImporterScene(false, true)); ResourceFormatImporter::get_singleton()->add_importer(import_scene); - Ref<ResourceImporterScene> import_animation; - import_animation = Ref<ResourceImporterScene>(memnew(ResourceImporterScene(true))); + Ref<ResourceImporterScene> import_animation = memnew(ResourceImporterScene(true, true)); ResourceFormatImporter::get_singleton()->add_importer(import_animation); { @@ -7464,8 +7479,8 @@ EditorNode::EditorNode() { export_as_menu->connect("index_pressed", callable_mp(this, &EditorNode::_export_as_menu_option)); file_menu->add_separator(); - file_menu->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO, true); - file_menu->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO, true); + file_menu->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO, true, true); + file_menu->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO, true, true); file_menu->add_separator(); file_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/reload_saved_scene", TTR("Reload Saved Scene")), EDIT_RELOAD_SAVED_SCENE); @@ -7844,7 +7859,6 @@ EditorNode::EditorNode() { log = memnew(EditorLog); Button *output_button = add_bottom_panel_item(TTR("Output"), log); - output_button->set_theme_type_variation("BottomPanelButton"); log->set_tool_button(output_button); center_split->connect("resized", callable_mp(this, &EditorNode::_vp_resized)); @@ -7996,8 +8010,8 @@ EditorNode::EditorNode() { vcs_actions_menu = VersionControlEditorPlugin::get_singleton()->get_version_control_actions_panel(); vcs_actions_menu->set_name("Version Control"); vcs_actions_menu->connect("index_pressed", callable_mp(this, &EditorNode::_version_control_menu_option)); - vcs_actions_menu->add_item(TTR("Create Version Control Metadata"), RUN_VCS_METADATA); - vcs_actions_menu->add_item(TTR("Version Control Settings"), RUN_VCS_SETTINGS); + vcs_actions_menu->add_item(TTR("Create Version Control Metadata..."), RUN_VCS_METADATA); + vcs_actions_menu->add_item(TTR("Version Control Settings..."), RUN_VCS_SETTINGS); project_menu->add_child(vcs_actions_menu); project_menu->set_item_submenu(project_menu->get_item_index(VCS_MENU), "Version Control"); @@ -8210,6 +8224,8 @@ EditorNode::~EditorNode() { GDExtensionEditorPlugins::editor_node_add_plugin = nullptr; GDExtensionEditorPlugins::editor_node_remove_plugin = nullptr; + + singleton = nullptr; } /* diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 77d6ec9ab2..d6af7107a3 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -2797,7 +2797,7 @@ void EditorPropertyNodePath::_node_assign() { if (!scene_tree) { scene_tree = memnew(SceneTreeDialog); scene_tree->get_scene_tree()->set_show_enabled_subscene(true); - scene_tree->get_scene_tree()->set_valid_types(valid_types); + scene_tree->set_valid_types(valid_types); add_child(scene_tree); scene_tree->connect("selected", callable_mp(this, &EditorPropertyNodePath::_node_selected)); } @@ -3145,7 +3145,7 @@ void EditorPropertyResource::_resource_changed(const Ref<Resource> &p_resource) Vector<StringName> valid_types; valid_types.push_back("Viewport"); - scene_tree->get_scene_tree()->set_valid_types(valid_types); + scene_tree->set_valid_types(valid_types); scene_tree->get_scene_tree()->set_show_enabled_subscene(true); add_child(scene_tree); diff --git a/editor/editor_property_name_processor.cpp b/editor/editor_property_name_processor.cpp index fd14136fc7..a6cd07dd8b 100644 --- a/editor/editor_property_name_processor.cpp +++ b/editor/editor_property_name_processor.cpp @@ -269,9 +269,9 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() { capitalize_string_remaps["uri"] = "URI"; capitalize_string_remaps["url"] = "URL"; capitalize_string_remaps["urls"] = "URLs"; - capitalize_string_remaps["us"] = String::utf8("(µs)"); // Unit. + capitalize_string_remaps["us"] = U"(µs)"; // Unit. capitalize_string_remaps["usb"] = "USB"; - capitalize_string_remaps["usec"] = String::utf8("(µsec)"); // Unit. + capitalize_string_remaps["usec"] = U"(µsec)"; // Unit. capitalize_string_remaps["uuid"] = "UUID"; capitalize_string_remaps["uv"] = "UV"; capitalize_string_remaps["uv1"] = "UV1"; diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp index 38a78babfb..c6747c4481 100644 --- a/editor/editor_resource_preview.cpp +++ b/editor/editor_resource_preview.cpp @@ -35,7 +35,7 @@ #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" #include "core/object/message_queue.h" -#include "core/variant/variant_utility.cpp" +#include "core/variant/variant_utility.h" #include "editor/editor_node.h" #include "editor/editor_paths.h" #include "editor/editor_scale.h" diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 3302b1dc41..f904ed875d 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -176,12 +176,6 @@ void EditorColorMap::create() { add_conversion_exception("Sky"); add_conversion_exception("EditorControlAnchor"); add_conversion_exception("DefaultProjectIcon"); - add_conversion_exception("GuiChecked"); - add_conversion_exception("GuiRadioChecked"); - add_conversion_exception("GuiIndeterminate"); - add_conversion_exception("GuiCloseCustomizable"); - add_conversion_exception("GuiGraphNodePort"); - add_conversion_exception("GuiResizer"); add_conversion_exception("ZoomMore"); add_conversion_exception("ZoomLess"); add_conversion_exception("ZoomReset"); @@ -191,6 +185,18 @@ void EditorColorMap::create() { add_conversion_exception("StatusSuccess"); add_conversion_exception("StatusWarning"); add_conversion_exception("OverbrightIndicator"); + add_conversion_exception("MaterialPreviewCube"); + add_conversion_exception("MaterialPreviewSphere"); + add_conversion_exception("MaterialPreviewLight1"); + add_conversion_exception("MaterialPreviewLight2"); + + // GUI + add_conversion_exception("GuiChecked"); + add_conversion_exception("GuiRadioChecked"); + add_conversion_exception("GuiIndeterminate"); + add_conversion_exception("GuiCloseCustomizable"); + add_conversion_exception("GuiGraphNodePort"); + add_conversion_exception("GuiResizer"); add_conversion_exception("GuiMiniCheckerboard"); /// Code Editor. @@ -908,7 +914,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_color("icon_pressed_color", "Button", icon_pressed_color); theme->set_color("icon_disabled_color", "Button", icon_disabled_color); - theme->set_constant("h_separation", "Button", 2 * EDSCALE); + theme->set_constant("h_separation", "Button", 4 * EDSCALE); theme->set_constant("outline_size", "Button", 0); const float ACTION_BUTTON_EXTRA_MARGIN = 32 * EDSCALE; @@ -944,6 +950,25 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { editor_log_button_pressed->set_border_color(accent_color); theme->set_stylebox("pressed", "EditorLogFilterButton", editor_log_button_pressed); + // Buttons in material previews + const Color dim_light_color = icon_normal_color.darkened(0.24); + const Color dim_light_highlighted_color = icon_normal_color.darkened(0.18); + Ref<StyleBox> sb_empty_borderless = make_empty_stylebox(); + + theme->set_type_variation("PreviewLightButton", "Button"); + // When pressed, don't use the accent color tint. When unpressed, dim the icon. + theme->set_color("icon_normal_color", "PreviewLightButton", dim_light_color); + theme->set_color("icon_focus_color", "PreviewLightButton", dim_light_color); + theme->set_color("icon_pressed_color", "PreviewLightButton", icon_normal_color); + theme->set_color("icon_hover_pressed_color", "PreviewLightButton", icon_normal_color); + // Unpressed icon is dim, so use a dim highlight. + theme->set_color("icon_hover_color", "PreviewLightButton", dim_light_highlighted_color); + + theme->set_stylebox("normal", "PreviewLightButton", sb_empty_borderless); + theme->set_stylebox("hover", "PreviewLightButton", sb_empty_borderless); + theme->set_stylebox("focus", "PreviewLightButton", sb_empty_borderless); + theme->set_stylebox("pressed", "PreviewLightButton", sb_empty_borderless); + // ProjectTag { theme->set_type_variation("ProjectTag", "Button"); @@ -1255,6 +1280,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { } theme->set_stylebox("panel", "Tree", style_tree_bg); + theme->set_stylebox("panel", "EditorValidationPanel", style_tree_bg); // Tree theme->set_icon("checked", "Tree", theme->get_icon(SNAME("GuiChecked"), SNAME("EditorIcons"))); @@ -1453,9 +1479,6 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_stylebox("panel", "TabContainer", style_content_panel); // Bottom panel. - theme->set_type_variation("BottomPanelButton", "Button"); - // Add separation for the warning/error icon. - theme->set_constant("h_separation", "BottomPanelButton", 6 * EDSCALE); Ref<StyleBoxFlat> style_bottom_panel = style_content_panel->duplicate(); style_bottom_panel->set_corner_radius_all(corner_radius * EDSCALE); theme->set_stylebox("BottomPanel", "EditorStyles", style_bottom_panel); @@ -1944,7 +1967,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_color("files_disabled", "FileDialog", font_disabled_color); // ColorPicker - theme->set_constant("margin", "ColorPicker", popup_margin_size); + theme->set_constant("margin", "ColorPicker", default_margin_size); theme->set_constant("sv_width", "ColorPicker", 256 * EDSCALE); theme->set_constant("sv_height", "ColorPicker", 256 * EDSCALE); theme->set_constant("h_width", "ColorPicker", 30 * EDSCALE); diff --git a/editor/export/editor_export.cpp b/editor/export/editor_export.cpp index 6c4fb480d7..670fd0a06d 100644 --- a/editor/export/editor_export.cpp +++ b/editor/export/editor_export.cpp @@ -32,7 +32,6 @@ #include "core/config/project_settings.h" #include "core/io/config_file.h" -#include "editor/import/resource_importer_texture_settings.h" EditorExport *EditorExport::singleton = nullptr; @@ -138,22 +137,6 @@ void EditorExport::add_export_preset(const Ref<EditorExportPreset> &p_preset, in } } -String EditorExportPlatform::test_etc2() const { - if (!ResourceImporterTextureSettings::should_import_etc2_astc()) { - return TTR("Target platform requires 'ETC2/ASTC' texture compression. Enable 'Import ETC2 ASTC' in Project Settings.") + "\n"; - } - - return String(); -} - -String EditorExportPlatform::test_bc() const { - if (!ResourceImporterTextureSettings::should_import_s3tc_bptc()) { - return TTR("Target platform requires 'S3TC/BPTC' texture compression. Enable 'Import S3TC BPTC' in Project Settings.") + "\n"; - } - - return String(); -} - int EditorExport::get_export_preset_count() const { return export_presets.size(); } diff --git a/editor/export/editor_export_platform.h b/editor/export/editor_export_platform.h index 763836e3ec..5f5702026c 100644 --- a/editor/export/editor_export_platform.h +++ b/editor/export/editor_export_platform.h @@ -237,8 +237,6 @@ public: virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) { return OK; } virtual Ref<Texture2D> get_run_icon() const { return get_logo(); } - String test_etc2() const; - String test_bc() const; bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const; virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const = 0; virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const = 0; diff --git a/editor/export/editor_export_preset.cpp b/editor/export/editor_export_preset.cpp index dfc0c23afc..29a31c0470 100644 --- a/editor/export/editor_export_preset.cpp +++ b/editor/export/editor_export_preset.cpp @@ -30,6 +30,8 @@ #include "editor_export.h" +#include "core/config/project_settings.h" + bool EditorExportPreset::_set(const StringName &p_name, const Variant &p_value) { values[p_name] = p_value; EditorExport::singleton->save_presets(); @@ -332,4 +334,35 @@ Variant EditorExportPreset::get_or_env(const StringName &p_name, const String &p return get(p_name, r_valid); } +String EditorExportPreset::get_version(const StringName &p_preset_string, bool p_windows_version) const { + String result = get(p_preset_string); + if (result.is_empty()) { + result = GLOBAL_GET("application/config/version"); + + if (p_windows_version) { + // Modify version number to match Windows constraints (version numbers must have 4 components). + const PackedStringArray result_split = result.split("."); + String windows_version; + if (result_split.is_empty()) { + // Use a valid fallback if the version string is empty, as a version number must be specified. + result = "1.0.0.0"; + } else if (result_split.size() == 1) { + result = result + ".0.0.0"; + } else if (result_split.size() == 2) { + result = result + ".0.0"; + } else if (result_split.size() == 3) { + result = result + ".0"; + } else { + // 4 components or more in the version string. Trim to contain only the first 4 components. + result = vformat("%s.%s.%s.%s", result_split[0] + result_split[1] + result_split[2] + result_split[3]); + } + } else if (result.is_empty()) { + // Use a valid fallback if the version string is empty, as a version number must be specified. + result = "1.0.0"; + } + } + + return result; +} + EditorExportPreset::EditorExportPreset() {} diff --git a/editor/export/editor_export_preset.h b/editor/export/editor_export_preset.h index 8b59da06dd..025e7603f3 100644 --- a/editor/export/editor_export_preset.h +++ b/editor/export/editor_export_preset.h @@ -154,6 +154,13 @@ public: Variant get_or_env(const StringName &p_name, const String &p_env_var, bool *r_valid = nullptr) const; + // Return the preset's version number, or fall back to the + // `application/config/version` project setting if set to an empty string. + // If `p_windows_version` is `true`, formats the returned version number to + // be compatible with Windows executable metadata (which requires a + // 4-component format). + String get_version(const StringName &p_name, bool p_windows_version = false) const; + const HashMap<StringName, PropertyInfo> &get_properties() const { return properties; } const HashMap<StringName, Variant> &get_values() const { return values; } diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp index 7c7762e0fd..61f875713f 100644 --- a/editor/export/project_export.cpp +++ b/editor/export/project_export.cpp @@ -39,6 +39,7 @@ #include "editor/editor_settings.h" #include "editor/export/editor_export.h" #include "editor/gui/editor_file_dialog.h" +#include "editor/import/resource_importer_texture_settings.h" #include "scene/gui/check_box.h" #include "scene/gui/check_button.h" #include "scene/gui/item_list.h" @@ -51,6 +52,44 @@ #include "scene/gui/texture_rect.h" #include "scene/gui/tree.h" +void ProjectExportTextureFormatError::_on_fix_texture_format_pressed() { + ProjectSettings::get_singleton()->set_setting(setting_identifier, true); + ProjectSettings::get_singleton()->save(); + EditorFileSystem::get_singleton()->scan_changes(); + emit_signal("texture_format_enabled"); +} + +void ProjectExportTextureFormatError::_bind_methods() { + ADD_SIGNAL(MethodInfo("texture_format_enabled")); +} + +void ProjectExportTextureFormatError::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: { + texture_format_error_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor"))); + } break; + } +} + +void ProjectExportTextureFormatError::show_for_texture_format(const String &p_friendly_name, const String &p_setting_identifier) { + texture_format_error_label->set_text(vformat(TTR("Target platform requires '%s' texture compression. Enable 'Import %s' to fix."), p_friendly_name, p_friendly_name.replace("/", " "))); + setting_identifier = p_setting_identifier; + show(); +} + +ProjectExportTextureFormatError::ProjectExportTextureFormatError() { + // Set up the label. + texture_format_error_label = memnew(Label); + add_child(texture_format_error_label); + // Set up the fix button. + fix_texture_format_button = memnew(LinkButton); + fix_texture_format_button->set_v_size_flags(Control::SIZE_SHRINK_CENTER); + fix_texture_format_button->set_text(TTR("Fix Import")); + add_child(fix_texture_format_button); + fix_texture_format_button->connect("pressed", callable_mp(this, &ProjectExportTextureFormatError::_on_fix_texture_format_pressed)); +} + void ProjectExportDialog::_theme_changed() { duplicate_preset->set_icon(presets->get_theme_icon(SNAME("Duplicate"), SNAME("EditorIcons"))); delete_preset->set_icon(presets->get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"))); @@ -300,6 +339,14 @@ void ProjectExportDialog::_edit_preset(int p_index) { _update_export_all(); child_controls_changed(); + if ((feature_set.has("s3tc") || feature_set.has("bptc")) && !ResourceImporterTextureSettings::should_import_s3tc_bptc()) { + export_texture_format_error->show_for_texture_format("S3TC/BPTC", "rendering/textures/vram_compression/import_s3tc_bptc"); + } else if ((feature_set.has("etc2") || feature_set.has("astc")) && !ResourceImporterTextureSettings::should_import_etc2_astc()) { + export_texture_format_error->show_for_texture_format("ETC2/ASTC", "rendering/textures/vram_compression/import_etc2_astc"); + } else { + export_texture_format_error->hide(); + } + String enc_in_filters_str = current->get_enc_in_filter(); String enc_ex_filters_str = current->get_enc_ex_filter(); if (!updating_enc_filters) { @@ -343,29 +390,29 @@ void ProjectExportDialog::_update_feature_list() { Ref<EditorExportPreset> current = get_current_preset(); ERR_FAIL_COND(current.is_null()); - RBSet<String> fset; - List<String> features; + List<String> features_list; - current->get_platform()->get_platform_features(&features); - current->get_platform()->get_preset_features(current, &features); + current->get_platform()->get_platform_features(&features_list); + current->get_platform()->get_preset_features(current, &features_list); String custom = current->get_custom_features(); Vector<String> custom_list = custom.split(","); for (int i = 0; i < custom_list.size(); i++) { String f = custom_list[i].strip_edges(); if (!f.is_empty()) { - features.push_back(f); + features_list.push_back(f); } } - for (const String &E : features) { - fset.insert(E); + feature_set.clear(); + for (const String &E : features_list) { + feature_set.insert(E); } custom_feature_display->clear(); String text; bool first = true; - for (const String &E : fset) { + for (const String &E : feature_set) { if (!first) { text += ", "; } else { @@ -1356,6 +1403,13 @@ ProjectExportDialog::ProjectExportDialog() { add_child(export_pck_zip); export_pck_zip->connect("file_selected", callable_mp(this, &ProjectExportDialog::_export_pck_zip_selected)); + // Export warnings and errors bottom section. + + export_texture_format_error = memnew(ProjectExportTextureFormatError); + main_vb->add_child(export_texture_format_error); + export_texture_format_error->hide(); + export_texture_format_error->connect("texture_format_enabled", callable_mp(this, &ProjectExportDialog::_update_current_preset)); + export_error = memnew(Label); main_vb->add_child(export_error); export_error->hide(); @@ -1390,6 +1444,8 @@ ProjectExportDialog::ProjectExportDialog() { export_templates_error->add_child(download_templates); download_templates->connect("pressed", callable_mp(this, &ProjectExportDialog::_open_export_template_manager)); + // Export project file dialog. + export_project = memnew(EditorFileDialog); export_project->set_access(EditorFileDialog::ACCESS_FILESYSTEM); add_child(export_project); diff --git a/editor/export/project_export.h b/editor/export/project_export.h index e53e393432..4f76167e22 100644 --- a/editor/export/project_export.h +++ b/editor/export/project_export.h @@ -41,6 +41,7 @@ class EditorFileSystemDirectory; class EditorInspector; class EditorPropertyPath; class ItemList; +class LinkButton; class MenuButton; class OptionButton; class PopupMenu; @@ -49,6 +50,23 @@ class TabContainer; class Tree; class TreeItem; +class ProjectExportTextureFormatError : public HBoxContainer { + GDCLASS(ProjectExportTextureFormatError, HBoxContainer); + + Label *texture_format_error_label = nullptr; + LinkButton *fix_texture_format_button = nullptr; + String setting_identifier; + void _on_fix_texture_format_pressed(); + +protected: + static void _bind_methods(); + void _notification(int p_what); + +public: + void show_for_texture_format(const String &p_friendly_name, const String &p_setting_identifier); + ProjectExportTextureFormatError(); +}; + class ProjectExportDialog : public ConfirmationDialog { GDCLASS(ProjectExportDialog, ConfirmationDialog); @@ -86,12 +104,14 @@ private: Button *export_all_button = nullptr; AcceptDialog *export_all_dialog = nullptr; + RBSet<String> feature_set; LineEdit *custom_features = nullptr; RichTextLabel *custom_feature_display = nullptr; LineEdit *script_key = nullptr; Label *script_key_error = nullptr; + ProjectExportTextureFormatError *export_texture_format_error = nullptr; Label *export_error = nullptr; Label *export_warning = nullptr; HBoxContainer *export_templates_error = nullptr; diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 1ff68b7d36..751f1c575d 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -2200,13 +2200,7 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected } break; case FILE_REIMPORT: { - // Reimport all selected files. - Vector<String> reimport; - for (int i = 0; i < p_selected.size(); i++) { - reimport.push_back(p_selected[i]); - } - - ERR_FAIL_COND_MSG(reimport.size() == 0, "You need to select files to reimport them."); + ImportDock::get_singleton()->reimport_resources(p_selected); } break; case FILE_NEW_FOLDER: { @@ -2830,6 +2824,37 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, Vector<Str if (!all_not_favorites) { p_popup->add_icon_item(get_theme_icon(SNAME("NonFavorite"), SNAME("EditorIcons")), TTR("Remove from Favorites"), FILE_REMOVE_FAVORITE); } + + { + List<String> resource_extensions; + ResourceFormatImporter::get_singleton()->get_recognized_extensions_for_type("Resource", &resource_extensions); + HashSet<String> extension_list; + for (const String &extension : resource_extensions) { + extension_list.insert(extension); + } + + bool resource_valid = true; + String main_extension; + + for (int i = 0; i != p_paths.size(); ++i) { + String extension = p_paths[i].get_extension(); + if (extension_list.has(extension)) { + if (main_extension.is_empty()) { + main_extension = extension; + } else if (extension != main_extension) { + resource_valid = false; + break; + } + } else { + resource_valid = false; + break; + } + } + + if (resource_valid) { + p_popup->add_icon_item(get_theme_icon(SNAME("Load"), SNAME("EditorIcons")), TTR("Reimport"), FILE_REIMPORT); + } + } } if (p_paths.size() == 1) { diff --git a/editor/gui/editor_run_bar.cpp b/editor/gui/editor_run_bar.cpp index c226c1a2d6..e144d1d10d 100644 --- a/editor/gui/editor_run_bar.cpp +++ b/editor/gui/editor_run_bar.cpp @@ -348,6 +348,10 @@ bool EditorRunBar::is_movie_maker_enabled() const { return write_movie_button->is_pressed(); } +HBoxContainer *EditorRunBar::get_buttons_container() { + return main_hbox; +} + void EditorRunBar::_bind_methods() { ADD_SIGNAL(MethodInfo("play_pressed")); ADD_SIGNAL(MethodInfo("stop_pressed")); @@ -359,7 +363,7 @@ EditorRunBar::EditorRunBar() { main_panel = memnew(PanelContainer); add_child(main_panel); - HBoxContainer *main_hbox = memnew(HBoxContainer); + main_hbox = memnew(HBoxContainer); main_panel->add_child(main_hbox); play_button = memnew(Button); diff --git a/editor/gui/editor_run_bar.h b/editor/gui/editor_run_bar.h index b7e7db2bd6..1cb999612a 100644 --- a/editor/gui/editor_run_bar.h +++ b/editor/gui/editor_run_bar.h @@ -39,6 +39,7 @@ class Button; class EditorRunNative; class EditorQuickOpen; class PanelContainer; +class HBoxContainer; class EditorRunBar : public MarginContainer { GDCLASS(EditorRunBar, MarginContainer); @@ -53,6 +54,7 @@ class EditorRunBar : public MarginContainer { }; PanelContainer *main_panel = nullptr; + HBoxContainer *main_hbox = nullptr; Button *play_button = nullptr; Button *pause_button = nullptr; @@ -109,6 +111,8 @@ public: Button *get_pause_button() { return pause_button; } + HBoxContainer *get_buttons_container(); + EditorRunBar(); }; diff --git a/editor/gui/editor_validation_panel.cpp b/editor/gui/editor_validation_panel.cpp new file mode 100644 index 0000000000..af15010b78 --- /dev/null +++ b/editor/gui/editor_validation_panel.cpp @@ -0,0 +1,134 @@ +/**************************************************************************/ +/* editor_validation_panel.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "editor_validation_panel.h" + +#include "editor/editor_scale.h" +#include "scene/gui/box_container.h" +#include "scene/gui/button.h" +#include "scene/gui/label.h" + +void EditorValidationPanel::_update() { + for (const KeyValue<int, String> &E : valid_messages) { + set_message(E.key, E.value, MSG_OK); + } + + valid = true; + update_callback.callv(Array()); + + if (accept_button) { + accept_button->set_disabled(!valid); + } + pending_update = false; +} + +void EditorValidationPanel::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_THEME_CHANGED: { + theme_cache.valid_color = get_theme_color(SNAME("success_color"), SNAME("Editor")); + theme_cache.warning_color = get_theme_color(SNAME("warning_color"), SNAME("Editor")); + theme_cache.error_color = get_theme_color(SNAME("error_color"), SNAME("Editor")); + } break; + } +} + +void EditorValidationPanel::add_line(int p_id, const String &p_valid_message) { + ERR_FAIL_COND(valid_messages.has(p_id)); + + Label *label = memnew(Label); + message_container->add_child(label); + label->set_custom_minimum_size(Size2(200 * EDSCALE, 0)); + label->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART); + + valid_messages[p_id] = p_valid_message; + labels[p_id] = label; +} + +void EditorValidationPanel::set_accept_button(Button *p_button) { + accept_button = p_button; +} + +void EditorValidationPanel::set_update_callback(const Callable &p_callback) { + update_callback = p_callback; +} + +void EditorValidationPanel::update() { + ERR_FAIL_COND(update_callback.is_null()); + + if (pending_update) { + return; + } + pending_update = true; + callable_mp(this, &EditorValidationPanel::_update).call_deferred(); +} + +void EditorValidationPanel::set_message(int p_id, const String &p_text, MessageType p_type, bool p_auto_prefix) { + ERR_FAIL_COND(!valid_messages.has(p_id)); + + Label *label = labels[p_id]; + if (p_text.is_empty()) { + label->hide(); + return; + } + label->show(); + + if (p_auto_prefix) { + label->set_text(String(U"• ") + p_text); + } else { + label->set_text(p_text); + } + + switch (p_type) { + case MSG_OK: + label->add_theme_color_override(SNAME("font_color"), theme_cache.valid_color); + break; + case MSG_WARNING: + label->add_theme_color_override(SNAME("font_color"), theme_cache.warning_color); + break; + case MSG_ERROR: + label->add_theme_color_override(SNAME("font_color"), theme_cache.error_color); + valid = false; + break; + case MSG_INFO: + label->remove_theme_color_override(SNAME("font_color")); + break; + } +} + +bool EditorValidationPanel::is_valid() const { + return valid; +} + +EditorValidationPanel::EditorValidationPanel() { + set_v_size_flags(SIZE_EXPAND_FILL); + + message_container = memnew(VBoxContainer); + add_child(message_container); +} diff --git a/editor/gui/editor_validation_panel.h b/editor/gui/editor_validation_panel.h new file mode 100644 index 0000000000..c371795e15 --- /dev/null +++ b/editor/gui/editor_validation_panel.h @@ -0,0 +1,88 @@ +/**************************************************************************/ +/* editor_validation_panel.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 EDITOR_VALIDATION_PANEL_H +#define EDITOR_VALIDATION_PANEL_H + +#include "scene/gui/panel_container.h" + +class Button; +class Label; +class VBoxContainer; + +class EditorValidationPanel : public PanelContainer { + GDCLASS(EditorValidationPanel, PanelContainer); + +public: + enum MessageType { + MSG_OK, + MSG_WARNING, + MSG_ERROR, + MSG_INFO, + }; + + static const int MSG_ID_DEFAULT = 0; // Avoids hard-coding ID in dialogs with single-line validation. + +private: + VBoxContainer *message_container = nullptr; + + HashMap<int, String> valid_messages; + HashMap<int, Label *> labels; + + bool valid = false; + bool pending_update = false; + + struct ThemeCache { + Color valid_color; + Color warning_color; + Color error_color; + } theme_cache; + + void _update(); + + Callable update_callback; + Button *accept_button = nullptr; + +protected: + void _notification(int p_what); + +public: + void add_line(int p_id, const String &p_valid_message = ""); + void set_accept_button(Button *p_button); + void set_update_callback(const Callable &p_callback); + + void update(); + void set_message(int p_id, const String &p_text, MessageType p_type, bool p_auto_prefix = true); + bool is_valid() const; + + EditorValidationPanel(); +}; + +#endif // EDITOR_VALIDATION_PANEL_H diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp index fbe167814d..d07e5dfa1a 100644 --- a/editor/gui/scene_tree_editor.cpp +++ b/editor/gui/scene_tree_editor.cpp @@ -41,6 +41,7 @@ #include "editor/plugins/animation_player_editor_plugin.h" #include "editor/plugins/canvas_item_editor_plugin.h" #include "editor/plugins/script_editor_plugin.h" +#include "scene/gui/flow_container.h" #include "scene/gui/label.h" #include "scene/gui/tab_container.h" #include "scene/gui/texture_rect.h" @@ -293,7 +294,7 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { } // Improve looks on tooltip, extra spacing on non-bullet point newlines. - const String bullet_point = String::utf8("• "); + const String bullet_point = U"• "; int next_newline = 0; while (next_newline != -1) { next_newline = conf_warning.find("\n", next_newline + 2); @@ -964,26 +965,55 @@ void SceneTreeEditor::_rename_node(Node *p_node, const String &p_name) { String new_name = p_name.validate_node_name(); if (new_name != p_name) { - error->set_text(TTR("Invalid node name, the following characters are not allowed:") + "\n" + String::get_invalid_node_name_characters()); - error->popup_centered(); - - if (new_name.is_empty()) { - item->set_text(0, p_node->get_name()); - return; + String text = TTR("Invalid node name, the following characters are not allowed:") + "\n" + String::get_invalid_node_name_characters(); + if (error->is_visible()) { + if (!error->get_meta("invalid_character", false)) { + error->set_text(error->get_text() + "\n\n" + text); + error->set_meta("invalid_character", true); + } + } else { + error->set_text(text); + error->set_meta("invalid_character", true); + error->set_meta("same_unique_name", false); + error->popup_centered(); } + } - item->set_text(0, new_name); + // Trim leading/trailing whitespace to prevent node names from containing accidental whitespace, which would make it more difficult to get the node via `get_node()`. + new_name = new_name.strip_edges(); + if (new_name.is_empty()) { + // If name is empty, fallback to class name. + if (GLOBAL_GET("editor/naming/node_name_casing").operator int() != NAME_CASING_PASCAL_CASE) { + new_name = Node::adjust_name_casing(p_node->get_class()); + } else { + new_name = p_node->get_class(); + } } if (new_name == p_node->get_name()) { if (item->get_text(0).is_empty()) { item->set_text(0, new_name); } + return; + } + // We previously made sure name is not the same as current name so that it won't complain about already used unique name when not changing name. + if (p_node->is_unique_name_in_owner() && get_tree()->get_edited_scene_root()->get_node_or_null("%" + new_name)) { + String text = TTR("Another node already uses this unique name in the scene."); + if (error->is_visible()) { + if (!error->get_meta("same_unique_name", false)) { + error->set_text(error->get_text() + "\n\n" + text); + error->set_meta("same_unique_name", true); + } + } else { + error->set_text(text); + error->set_meta("same_unique_name", true); + error->set_meta("invalid_character", false); + error->popup_centered(); + } + item->set_text(0, p_node->get_name()); return; } - // Trim leading/trailing whitespace to prevent node names from containing accidental whitespace, which would make it more difficult to get the node via `get_node()`. - new_name = new_name.strip_edges(); if (!is_scene_tree_dock) { p_node->set_name(new_name); @@ -999,7 +1029,7 @@ void SceneTreeEditor::_rename_node(Node *p_node, const String &p_name) { undo_redo->add_undo_method(item, "set_metadata", 0, p_node->get_path()); undo_redo->add_undo_method(item, "set_text", 0, p_node->get_name()); - p_node->set_name(p_name); + p_node->set_name(new_name); undo_redo->add_do_method(p_node, "set_name", new_name); undo_redo->add_do_method(item, "set_metadata", 0, p_node->get_path()); undo_redo->add_do_method(item, "set_text", 0, new_name); @@ -1017,28 +1047,6 @@ void SceneTreeEditor::_renamed() { ERR_FAIL_COND(!n); String new_name = which->get_text(0); - if (new_name.strip_edges().is_empty()) { - // If name is empty, fallback to class name. - if (GLOBAL_GET("editor/naming/node_name_casing").operator int() != NAME_CASING_PASCAL_CASE) { - new_name = Node::adjust_name_casing(n->get_class()); - } else { - new_name = n->get_class(); - } - } - - if (n->is_unique_name_in_owner()) { - Node *existing = get_tree()->get_edited_scene_root()->get_node_or_null("%" + new_name); - if (existing == n) { - which->set_text(0, n->get_name()); - return; - } - if (existing != nullptr) { - error->set_text(TTR("Another node already uses this unique name in the scene.")); - error->popup_centered(); - which->set_text(0, n->get_name()); - return; - } - } _rename_node(n, new_name); } @@ -1486,8 +1494,51 @@ void SceneTreeDialog::popup_scenetree_dialog() { popup_centered_clamped(Size2(350, 700) * EDSCALE); } +void SceneTreeDialog::set_valid_types(const Vector<StringName> &p_valid) { + if (p_valid.is_empty()) { + return; + } + + tree->set_valid_types(p_valid); + + HBoxContainer *hbox = memnew(HBoxContainer); + content->add_child(hbox); + content->move_child(hbox, 0); + + { + Label *label = memnew(Label); + hbox->add_child(label); + label->set_text(TTR("Allowed:")); + } + + HFlowContainer *hflow = memnew(HFlowContainer); + hbox->add_child(hflow); + hflow->set_h_size_flags(Control::SIZE_EXPAND_FILL); + + for (const StringName &type : p_valid) { + HBoxContainer *hb = memnew(HBoxContainer); + hflow->add_child(hb); + + TextureRect *trect = memnew(TextureRect); + hb->add_child(trect); + trect->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE); + trect->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED); + trect->set_meta("type", type); + valid_type_icons.push_back(trect); + + Label *label = memnew(Label); + hb->add_child(label); + label->set_text(type); + label->set_auto_translate(false); + } +} + void SceneTreeDialog::_update_theme() { filter->set_right_icon(tree->get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); + for (TextureRect *trect : valid_type_icons) { + trect->set_custom_minimum_size(Vector2(get_theme_constant(SNAME("class_icon_size"), SNAME("Editor")), 0)); + trect->set_texture(EditorNode::get_singleton()->get_class_icon(trect->get_meta("type"))); + } } void SceneTreeDialog::_notification(int p_what) { @@ -1544,8 +1595,8 @@ void SceneTreeDialog::_bind_methods() { SceneTreeDialog::SceneTreeDialog() { set_title(TTR("Select a Node")); - VBoxContainer *vbc = memnew(VBoxContainer); - add_child(vbc); + content = memnew(VBoxContainer); + add_child(content); filter = memnew(LineEdit); filter->set_h_size_flags(Control::SIZE_EXPAND_FILL); @@ -1553,12 +1604,12 @@ SceneTreeDialog::SceneTreeDialog() { filter->set_clear_button_enabled(true); filter->add_theme_constant_override("minimum_character_width", 0); filter->connect("text_changed", callable_mp(this, &SceneTreeDialog::_filter_changed)); - vbc->add_child(filter); + content->add_child(filter); tree = memnew(SceneTreeEditor(false, false, true)); tree->set_v_size_flags(Control::SIZE_EXPAND_FILL); tree->get_scene_tree()->connect("item_activated", callable_mp(this, &SceneTreeDialog::_select)); - vbc->add_child(tree); + content->add_child(tree); // Disable the OK button when no node is selected. get_ok_button()->set_disabled(!tree->get_selected()); diff --git a/editor/gui/scene_tree_editor.h b/editor/gui/scene_tree_editor.h index 8878256009..c99f84912b 100644 --- a/editor/gui/scene_tree_editor.h +++ b/editor/gui/scene_tree_editor.h @@ -35,6 +35,7 @@ #include "scene/gui/tree.h" class EditorSelection; +class TextureRect; class SceneTreeEditor : public Control { GDCLASS(SceneTreeEditor, Control); @@ -172,10 +173,10 @@ public: class SceneTreeDialog : public ConfirmationDialog { GDCLASS(SceneTreeDialog, ConfirmationDialog); + VBoxContainer *content = nullptr; SceneTreeEditor *tree = nullptr; - //Button *select; - //Button *cancel; LineEdit *filter = nullptr; + LocalVector<TextureRect *> valid_type_icons; void _select(); void _cancel(); @@ -189,8 +190,11 @@ protected: public: void popup_scenetree_dialog(); + void set_valid_types(const Vector<StringName> &p_valid); + SceneTreeEditor *get_scene_tree() { return tree; } LineEdit *get_filter_line_edit() { return filter; } + SceneTreeDialog(); ~SceneTreeDialog(); }; diff --git a/editor/icons/Camera2D.svg b/editor/icons/Camera2D.svg index 81e5cc2c8e..24198a8f06 100644 --- a/editor/icons/Camera2D.svg +++ b/editor/icons/Camera2D.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M9 2a3 3 0 0 0-3 2.777 3 3 0 1 0-3 5.047V12a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1v-1l3 2V7l-3 2V7.23A3 3 0 0 0 9 2z" fill="#8da5f3"/></svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#8da5f3"><path d="m7 2-2 4h-3s-1 .000001-1 1v6s.000001 1 1 1h12s1-.000001 1-1v-6s-.000001-1-1-1h-3l-2-4zm1 5a3 3 0 0 1 3 3 3 3 0 0 1 -3 3 3 3 0 0 1 -3-3 3 3 0 0 1 3-3z"/><path d="m2 3h2v2h-2z"/></g></svg> diff --git a/editor/icons/Camera3D.svg b/editor/icons/Camera3D.svg index bf61aa48fc..69d435ff17 100644 --- a/editor/icons/Camera3D.svg +++ b/editor/icons/Camera3D.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M9 2a3 3 0 0 0-3 2.777 3 3 0 1 0-3 5.047V12a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1v-1l3 2V7l-3 2V7.23A3 3 0 0 0 9 2z" fill="#fc7f7f"/></svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#fc7f7f" fill-opacity=".996078"><path d="m7 2-2 4h-3s-1 .000001-1 1v6s.000001 1 1 1h12s1-.000001 1-1v-6s-.000001-1-1-1h-3l-2-4zm1 5a3 3 0 0 1 3 3 3 3 0 0 1 -3 3 3 3 0 0 1 -3-3 3 3 0 0 1 3-3z"/><path d="m2 3h2v2h-2z"/></g></svg> diff --git a/editor/icons/CameraAttributes.svg b/editor/icons/CameraAttributes.svg index 459c64e11c..72d88ace7a 100644 --- a/editor/icons/CameraAttributes.svg +++ b/editor/icons/CameraAttributes.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M6 2a3 3 0 0 0-2 5.23V9L1 7v6l3-2v1a1 1 0 0 0 1 1h3a2 2 0 0 1 .73-1.24 1.83 1.83 0 0 1 1.828-3.143 1.8 1.8 0 0 1 3.313-.75 3 3 0 0 0-4.883-3.09A3 3 0 0 0 6 2z" fill="#e0e0e0"/><path d="M12.36 8.598a.533 3.2 0 0 0-.51 2.275 3.2.533 30 0 0-.515.887.533 3.2 60 0 0 .515.887.533 3.2 0 0 0 1.02 0 3.2.533 30 0 0 .515-.887.533 3.2 60 0 0-.515-.887.533 3.2 0 0 0-.51-2.275z" fill="#c38ef1"/></svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 2-2 4h-3s-1 .000002-1 1v6s.000002 1 1 1h6.1484375c-.1542249-.344387-.1544915-.707546-.1054687-.945312.0045588-.022112.0106058-.039779.015625-.060547a3 3 0 0 1 -.0585938.005859 3 3 0 0 1 -3-3 3 3 0 0 1 3-3 3 3 0 0 1 2.636719 1.5683594c.036555-.1169261.08189-.2370529.146484-.3671875.070405-.1418394.157935-.3026809.353516-.4960938.19558-.1934128.578437-.4459156 1.066406-.4472656h.003906.003907c.487968.0013542.872778.2538528 1.068359.4472656.195581.1934129.281158.3542544.351562.4960938.12919.2602692.183256.4788816.238282.7167969.01665-.005166.042083-.0204307.058593-.0253907.260877-.0784194.495713-.1397775.81836-.1484375.076759-.0020607.160042-.0000611.253906.0097656v-1.7539062s-.000002-1-1-1h-3l-2-4z" fill="#e0e0e0"/><path d="m2 3h2v2h-2z" fill="#e0e0e0"/><path d="m12.207341 8.6577609a.533 3.2 0 0 0 -.51 2.2750001 3.2.533 30 0 0 -.515.887.533 3.2 60 0 0 .515.887.533 3.2 0 0 0 1.02 0 3.2.533 30 0 0 .515-.887.533 3.2 60 0 0 -.515-.887.533 3.2 0 0 0 -.51-2.2750001z" fill="#c38ef1"/></svg> diff --git a/editor/icons/CameraAttributesPhysical.svg b/editor/icons/CameraAttributesPhysical.svg new file mode 100644 index 0000000000..f75cd0dc44 --- /dev/null +++ b/editor/icons/CameraAttributesPhysical.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 2-2 4h-3s-1 .000002-1 1v6s.000002 1 1 1h6.1484375c-.1542249-.344387-.1544915-.707546-.1054687-.945312.0045588-.022112.0106058-.039779.015625-.060547a3 3 0 0 1 -.0585938.005859 3 3 0 0 1 -3-3 3 3 0 0 1 3-3 3 3 0 0 1 2.636719 1.5683594c.036555-.1169261.08189-.2370529.146484-.3671875.070405-.1418394.157935-.3026809.353516-.4960938.19558-.1934128.578437-.4459156 1.066406-.4472656h.003906.003907c.487968.0013542.872778.2538528 1.068359.4472656.195581.1934129.281158.3542544.351562.4960938.12919.2602692.183256.4788816.238282.7167969.01665-.005166.042083-.0204307.058593-.0253907.260877-.0784194.495713-.1397775.81836-.1484375.076759-.0020607.160042-.0000611.253906.0097656v-1.7539062s-.000002-1-1-1h-3l-2-4z" fill="#e0e0e0"/><path d="m2 3h2v2h-2z" fill="#e0e0e0"/><path d="m12.207341 8.6577609a.533 3.2 0 0 0 -.51 2.2750001 3.2.533 30 0 0 -.515.887.533 3.2 60 0 0 .515.887.533 3.2 0 0 0 1.02 0 3.2.533 30 0 0 .515-.887.533 3.2 60 0 0 -.515-.887.533 3.2 0 0 0 -.51-2.2750001z" fill="#ffca5f"/></svg> diff --git a/editor/icons/CameraAttributesPractical.svg b/editor/icons/CameraAttributesPractical.svg new file mode 100644 index 0000000000..0aed99056d --- /dev/null +++ b/editor/icons/CameraAttributesPractical.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 2-2 4h-3s-1 .000002-1 1v6s.000002 1 1 1h6.1484375c-.1542249-.344387-.1544915-.707546-.1054687-.945312.0045588-.022112.0106058-.039779.015625-.060547a3 3 0 0 1 -.0585938.005859 3 3 0 0 1 -3-3 3 3 0 0 1 3-3 3 3 0 0 1 2.636719 1.5683594c.036555-.1169261.08189-.2370529.146484-.3671875.070405-.1418394.157935-.3026809.353516-.4960938.19558-.1934128.578437-.4459156 1.066406-.4472656h.003906.003907c.487968.0013542.872778.2538528 1.068359.4472656.195581.1934129.281158.3542544.351562.4960938.12919.2602692.183256.4788816.238282.7167969.01665-.005166.042083-.0204307.058593-.0253907.260877-.0784194.495713-.1397775.81836-.1484375.076759-.0020607.160042-.0000611.253906.0097656v-1.7539062s-.000002-1-1-1h-3l-2-4z" fill="#e0e0e0"/><path d="m2 3h2v2h-2z" fill="#e0e0e0"/><path d="m12.207341 8.6577609a.533 3.2 0 0 0 -.51 2.2750001 3.2.533 30 0 0 -.515.887.533 3.2 60 0 0 .515.887.533 3.2 0 0 0 1.02 0 3.2.533 30 0 0 .515-.887.533 3.2 60 0 0 -.515-.887.533 3.2 0 0 0 -.51-2.2750001z" fill="#5fb2ff"/></svg> diff --git a/editor/icons/CameraTexture.svg b/editor/icons/CameraTexture.svg index 145a817b7e..5a050b900e 100644 --- a/editor/icons/CameraTexture.svg +++ b/editor/icons/CameraTexture.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zm1 2h10v8H3zm5.818 1A1.65 1.65 0 0 0 7.19 5.514a1.65 1.65 0 1 0-1.641 2.752v1.19a.55.55 0 0 0 .545.544h3.27a.55.55 0 0 0 .544-.545V8.91L11.545 10V6.728L9.908 7.82v-.965a1.65 1.65 0 0 0-1.09-2.851z" fill="#e0e0e0"/></svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0"><path d="m2 1c-.5522847 0-1 .4477153-1 1v12c0 .552285.4477153 1 1 1h12c.552285 0 1-.447715 1-1v-12c0-.5522847-.447715-1-1-1zm1 2h10v8h-10z"/><g stroke-width="1.97307" transform="matrix(.50682329 0 0 .50682329 3.994604 2.971119)"><path d="m5.9298703 2.0300582-1.9730742 3.934252-2.95288.01033s-1.00000003.000001-1.00000003 1v5.9004198s-.00049262 1.000376.99950633 1.000351l13.7985506-.000351s1-.000001 1-1v-5.9004195s-.000008-.9979028-1-1l-2.95288-.01033-1.9730741-3.934252zm2.0701297 4.8922972c1.6568542 0 3 1.3431458 3 3 0 1.6568536-1.3431458 2.9999996-3 2.9999996s-3-1.343146-3-2.9999996c0-1.6568542 1.3431458-3 3-3z"/><path d="m.0106475 2.0300582 3.9461486-.0000002v1.9730745h-3.9461486z"/></g></g></svg> diff --git a/editor/icons/CompressedTexture2D.svg b/editor/icons/CompressedTexture2D.svg index 54ff10b3c1..aa65696396 100644 --- a/editor/icons/CompressedTexture2D.svg +++ b/editor/icons/CompressedTexture2D.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 1a1 1 0 0 0 -1 1v12a1 1 0 0 0 1 1h6v-2h2v-2h-2-5v-8h5v-2zm6 2v2h2v-2zm2 0h2v-2h-2zm2 0v2h2v-2zm0 2h-2v2h2zm0 2v2h2v-2zm0 2h-2v2h2zm0 2v2h2v-2zm0 2h-2v2h2zm-2-4v-2h-2v-1h-1v1h-1v1h-1v1h-1v1h2 2v-1z" fill="#e0e0e0"/></svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h6v-2h2v-2H3V3h5V1zm6 2v2h2V3zm2 0h2V1h-2zm2 0v2h2V3zm0 2h-2v2h2zm0 2v2h2V7zm0 2h-2v2h2zm0 2v2h2v-2zm0 2h-2v2h2zm-2-4V7H8V6H7v1H6v1H5v1H4v1h4V9z" fill="#e0e0e0"/></svg> diff --git a/editor/icons/CompressedTexture3D.svg b/editor/icons/CompressedTexture3D.svg new file mode 100644 index 0000000000..0f027ad71b --- /dev/null +++ b/editor/icons/CompressedTexture3D.svg @@ -0,0 +1 @@ +<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M1 14a1 1 0 0 0 1 1h6v-2h2v-2H8v1H2.25V5H10V3H8v.75H3.25L5 2h3V1H4.5a1 1 0 0 0-.707.293l-2.5 2.5A1 1 0 0 0 1 4.5Zm6.675-8L8 6.5V7h2v2H8v1.75l-.325.25-1.5-.5-1.75.75-1.75-.75L4.25 8l.875 1.25ZM10 3h2V1h-2zm2 0v2h2V3zm0 2h-2v2h2zm0 2v2h2V7zm0 2h-2v2h2zm0 2v2h2v-2zm0 2h-2v2h2z" fill="#e0e0e0"/><path d="M7.675 6v5L6 10.5l-.875-1.25zM4.25 8v3.25L2.5 10.5" fill="#000" fill-opacity=".4"/></svg> diff --git a/editor/icons/CurveTexture.svg b/editor/icons/CurveTexture.svg index 50232fdc4a..a5524f145c 100644 --- a/editor/icons/CurveTexture.svg +++ b/editor/icons/CurveTexture.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v9.16A3 3 0 0 1 2 11c.331 0 .666-.008 1-.014V3h10v1.135a3 3 0 0 1 2-.006V2a1 1 0 0 0-1-1H2zm7 4v1H8v1H6v1H5v1H4v1h4.39c1.113-.567 1.968-1.454 2.61-3.473V6h-1V5H9zm4.967.988a1 1 0 0 0-.928.739c-.927 3.246-2.636 4.682-4.652 5.466C6.37 12.978 4 13 2 13a1 1 0 1 0 0 2c2 0 4.63.024 7.113-.941 2.484-.966 4.775-3.03 5.848-6.784a1 1 0 0 0-.994-1.287z" fill="#e0e0e0"/></svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v9.16A3 3 0 0 1 2 11h1V3h10v1.135a3 3 0 0 1 2 0V2a1 1 0 0 0-1-1zm7 4v1H8v1H6v1H5v1H4v1h4.39c1.113-.567 1.968-1.454 2.61-3.473V6h-1V5H9zm4.039 1.727c-.927 3.246-2.636 4.682-4.652 5.466C6.37 12.978 4 13 2 13a1 1 0 1 0 0 2c2 0 4.63.024 7.113-.941 2.484-.966 4.775-3.03 5.848-6.784a1 1 0 0 0-1.922-.548z" fill="#e0e0e0"/></svg> diff --git a/editor/icons/CurveXYZTexture.svg b/editor/icons/CurveXYZTexture.svg new file mode 100644 index 0000000000..e376dd434b --- /dev/null +++ b/editor/icons/CurveXYZTexture.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v10a2 2 0 0 1 .75-.1Q3 11.9 3 10.62V3h10v6.5a2 2 0 0 1 2 0V2a1 1 0 0 0-1-1zm7 4v1H8v1H6v1H5v1H4v.5a2 2 0 0 1 2.7.5h1.2a2 2 0 0 1 3.2 0h.9V8h-1V6h-1V5z" fill="#e0e0e0"/><g stroke-width="1.6" stroke-linecap="round" fill="none"><path d="M2 14.2q2.5 0 3-3" stroke="#ff5f5f"/><path d="M6.5 14.2q2.5 0 3-3" stroke="#5fff97"/><path d="M11 14.2q2.5 0 3-3" stroke="#5fb2ff"/></g></svg> diff --git a/editor/icons/GizmoCamera3D.svg b/editor/icons/GizmoCamera3D.svg new file mode 100644 index 0000000000..c22fa18ee4 --- /dev/null +++ b/editor/icons/GizmoCamera3D.svg @@ -0,0 +1 @@ +<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="m52.789062 12.285156-.068359.001953s-2.40509.20501-4.607422 1.613282c-2.202331 1.408271-4.620558 4.01711-6.382812 8.287109l-6.40625 15.527344h-.966797v-3.986328s.05404-2.69271-1.351563-5.503907c-1.405598-2.811197-5.028556-5.923828-10.076171-5.923828-5.047616 0-8.672527 3.112631-10.078126 5.923828s-1.351562 5.503907-1.351562 5.503907v4.796875c-2.8348528 1.102579-4.8904632 3.167645-5.8632812 5.113281-1.4055984 2.811197-1.3515624 5.503906-1.3515626 5.503906v59.427732c0 5.04762 3.1145841 8.67253 5.9257818 10.07813 2.811195 1.4056 5.503906 1.35156 5.503906 1.35156h96.820316c5.04761 0 8.67252-3.11458 10.07812-5.92578s1.35156-5.50391 1.35156-5.50391v-59.427732c0-5.047615-3.11458-8.672526-5.92578-10.078125-2.81119-1.405599-5.5039-1.349609-5.5039-1.349609h-19.859379l-6.40625-15.527344c-1.693114-4.102471-4.120301-6.670605-6.261719-8.09375-2.141416-1.423145-4.378906-1.779297-4.378906-1.779297l-.242187-.029297zm.201172 8h21.722657c.009-.0059.05394-.06976.867187.470703.864378.57445 2.152133 1.720094 3.292969 4.484375l8.451172 20.47461h25.210941s1.02269.05432 1.92578.505859c.90308.451543 1.5039.540926 1.5039 2.921875v59.427732s-.0563 1.0227-.50781 1.92578c-.45154.90309-.54093 1.50391-2.92187 1.50391h-96.820316s-1.022696-.0543-1.925782-.50586c-.903086-.45154-1.503906-.54288-1.503906-2.92383v-59.427732s.05627-1.022696.507813-1.925781c.451542-.903085.540927-1.501953 2.921875-1.501953h24.960937l8.451172-20.47461c1.241133-3.007304 2.535771-4.112924 3.296875-4.599609.756188-.483541.568815-.357095.566406-.355469zm12.009766 24c-18.572491 0-33.714844 15.142353-33.714844 33.714844s15.142352 33.71484 33.714844 33.71484 33.714844-15.142349 33.714844-33.71484-15.142353-33.714844-33.714844-33.714844zm0 8c14.24897 0 25.714844 11.465874 25.714844 25.714844s-11.465875 25.71484-25.714844 25.71484-25.714844-11.46587-25.714844-25.71484 11.465874-25.714844 25.714844-25.714844z" fill-opacity=".3"/><g fill="#f7f5cf" stroke-width="8.00001"><path d="m52.857144 16.285714s-4.425182.151262-7.428572 7.428572l-7.428571 17.999668h-22.285715c-7.4285711 0-7.4285711 7.428572-7.4285711 7.428572v59.428574c0 7.42857 7.4285711 7.42857 7.4285711 7.42857h96.821434c7.42857 0 7.42857-7.42857 7.42857-7.42857v-59.428574c0-7.428572-7.42857-7.428572-7.42857-7.428572h-22.535719l-7.428571-17.999668c-2.833953-6.866759-7.428572-7.428572-7.428572-7.428572zm12.142856 31.999999c16.410747 0 29.714286 13.303539 29.714286 29.714286s-13.303539 29.714291-29.714286 29.714291-29.714286-13.303544-29.714286-29.714291 13.303539-29.714286 29.714286-29.714286z"/><path d="m15.500291 33.728577h14.857143s0-7.428572-7.428571-7.428572c-7.428572 0-7.428572 7.428572-7.428572 7.428572z"/></g></svg> diff --git a/editor/icons/ImageTexture.svg b/editor/icons/ImageTexture.svg index 25d4b53d00..17fc57d31d 100644 --- a/editor/icons/ImageTexture.svg +++ b/editor/icons/ImageTexture.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 1a1 1 0 0 0 -1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-12a1 1 0 0 0 -1-1zm1 2h10v8h-10zm6 2v1h-1v1h-2v1h-1v1h-1v1h2 2 2 2v-2h-1v-2h-1v-1z" fill="#e0e0e0"/></svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zm1 2h10v8H3zm6 2v1H8v1H6v1H5v1H4v1h8V8h-1V6h-1V5z" fill="#e0e0e0"/></svg> diff --git a/editor/icons/ImageTexture3D.svg b/editor/icons/ImageTexture3D.svg new file mode 100644 index 0000000000..7cb4d46f8d --- /dev/null +++ b/editor/icons/ImageTexture3D.svg @@ -0,0 +1 @@ +<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M1 14a1 1 0 0 0 1 1h9.5a1 1 0 0 0 .707-.293l2.5-2.5A1 1 0 0 0 15 11.5V2a1 1 0 0 0-1-1H4.5a1 1 0 0 0-.707.293l-2.5 2.5A1 1 0 0 0 1 4.5zm1.25-9H11v7H2.25zm10 6V4.75L14 3v6.25zm-1-7.25h-8L5 2h8zM7.675 6l3 4-3 1-1.5-.5-1.75.75-1.75-.75L4.25 8l.875 1.25z" fill="#e0e0e0"/><path d="M7.675 6v5L6 10.5l-.875-1.25zM4.25 8v3.25L2.5 10.5" fill="#000" fill-opacity=".4"/></svg> diff --git a/editor/icons/MaterialPreviewCube.svg b/editor/icons/MaterialPreviewCube.svg index e7e56d02aa..68d4595bc2 100644 --- a/editor/icons/MaterialPreviewCube.svg +++ b/editor/icons/MaterialPreviewCube.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1 1 4v8l7 3 7-3V4z" fill="#d6d6d6"/><path d="m1 4 7 3 7-3-7-3z" fill="#fff"/><path d="m8 15-7-3V4l7 3z" fill="#e0e0e0"/><path d="m8 15 7-3V4L8 7z" fill="#d6d6d6"/></svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1 7 3v8l-7 3-7-3V4z" fill="none" stroke="#000" stroke-opacity=".8" stroke-width="2" stroke-linejoin="round"/><path d="M8 1 1 4v8l7 3 7-3V4z" fill="#d6d6d6"/><path d="m1 4 7 3 7-3-7-3z" fill="#f9f9f9"/><path d="m8 15-7-3V4l7 3z" fill="#e0e0e0"/></svg> diff --git a/editor/icons/MaterialPreviewCubeOff.svg b/editor/icons/MaterialPreviewCubeOff.svg deleted file mode 100644 index ccf8a1e8f6..0000000000 --- a/editor/icons/MaterialPreviewCubeOff.svg +++ /dev/null @@ -1 +0,0 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1 1 4v8l7 3 7-3V4z" fill="#d6d6d6"/><path d="m1 4 7 3 7-3-7-3z" fill="#fff"/><path d="m8 15-7-3V4l7 3z" fill="#e0e0e0"/><path d="m8 15 7-3V4L8 7z" fill="#d6d6d6"/><path d="M8 1 1 4v8l7 3 7-3V4z" fill-opacity=".235"/></svg> diff --git a/editor/icons/MaterialPreviewLight1.svg b/editor/icons/MaterialPreviewLight1.svg index 5e0f7539c4..ef21a4ecd7 100644 --- a/editor/icons/MaterialPreviewLight1.svg +++ b/editor/icons/MaterialPreviewLight1.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M7 1v2h2V1zM3.758 2.344 2.344 3.758l1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM8 4a4 4 0 0 0 0 8 4 4 0 0 0 0-8zM7 5h2v6H8V6H7zM1 7v2h2V7zm12 0v2h2V7zm-9.242 3.828-1.414 1.414 1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM7 13v2h2v-2z" fill="#e0e0e0"/></svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#000" stroke-width="2" stroke-opacity=".8" stroke-linejoin="round"><path d="M7 1v2h2V1zM3.758 2.344 2.344 3.758l1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM8 4a4 4 0 0 0 0 8 4 4 0 0 0 0-8zM1 7v2h2V7zm12 0v2h2V7zm-9.242 3.828-1.414 1.414 1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM7 13v2h2v-2z"/><circle cx="8" cy="8" r="3.5" fill="#000"/></g><path d="M7 1v2h2V1zM3.758 2.344 2.344 3.758l1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM8 4a4 4 0 0 0 0 8 4 4 0 0 0 0-8zM1 7v2h2V7zm12 0v2h2V7zm-9.242 3.828-1.414 1.414 1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM7 13v2h2v-2zm1-7.875a.75.75 0 0 1 1.145.625V11h-1.5V7.152l-1 .667-.75-1.2z" fill="#f9f9f9"/></svg> diff --git a/editor/icons/MaterialPreviewLight1Off.svg b/editor/icons/MaterialPreviewLight1Off.svg deleted file mode 100644 index 47f9ee83c4..0000000000 --- a/editor/icons/MaterialPreviewLight1Off.svg +++ /dev/null @@ -1 +0,0 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M7 1v2h2V1zM3.758 2.344 2.344 3.758l1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM8 4a4 4 0 0 0 0 8 4 4 0 0 0 0-8zM7 5h2v6H8V6H7zM1 7v2h2V7zm12 0v2h2V7zm-9.242 3.828-1.414 1.414 1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM7 13v2h2v-2z" fill="#e0e0e0" fill-opacity=".69"/></svg> diff --git a/editor/icons/MaterialPreviewLight2.svg b/editor/icons/MaterialPreviewLight2.svg index 7370caf17f..7714864840 100644 --- a/editor/icons/MaterialPreviewLight2.svg +++ b/editor/icons/MaterialPreviewLight2.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M7 1v2h2V1zM3.758 2.344 2.344 3.758l1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM8 4a4 4 0 0 0 0 8 4 4 0 0 0 0-8zM7 5h2v3H7v1h2v1H6V7h2V6H7zM1 7v2h2V7zm12 0v2h2V7zm-9.242 3.828-1.414 1.414 1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM7 13v2h2v-2z" fill="#e0e0e0"/></svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#000" stroke-width="2" stroke-opacity=".8" stroke-linejoin="round"><path d="M7 1v2h2V1zM3.758 2.344 2.344 3.758l1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM8 4a4 4 0 0 0 0 8 4 4 0 0 0 0-8zM1 7v2h2V7zm12 0v2h2V7zm-9.242 3.828-1.414 1.414 1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM7 13v2h2v-2z"/><circle cx="8" cy="8" r="3.5" fill="#000"/></g><path d="M7 1v2h2V1zM3.758 2.344 2.344 3.758l1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM8 4a4 4 0 0 0 0 8 4 4 0 0 0 0-8zM1 7v2h2V7zm12 0v2h2V7zm-9.242 3.828-1.414 1.414 1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM7 13v2h2v-2zM5.9 6.45a1.98 1.98 0 1 1 3.4 1.8L8.05 9.5h2V11H6.8a.9.9 0 0 1-.75-1.5L8.3 7.25a.62.62 0 1 0-1.1-.45z" fill="#f9f9f9"/></svg> diff --git a/editor/icons/MaterialPreviewLight2Off.svg b/editor/icons/MaterialPreviewLight2Off.svg deleted file mode 100644 index 2cbfa4f176..0000000000 --- a/editor/icons/MaterialPreviewLight2Off.svg +++ /dev/null @@ -1 +0,0 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M7 1v2h2V1zM3.758 2.344 2.344 3.758l1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM8 4a4 4 0 0 0 0 8 4 4 0 0 0 0-8zM7 5h2v3H7v1h2v1H6V7h2V6H7zM1 7v2h2V7zm12 0v2h2V7zm-9.242 3.828-1.414 1.414 1.414 1.414 1.414-1.414zm8.484 0-1.414 1.414 1.414 1.414 1.414-1.414zM7 13v2h2v-2z" fill="#e0e0e0" fill-opacity=".69"/></svg> diff --git a/editor/icons/MaterialPreviewSphere.svg b/editor/icons/MaterialPreviewSphere.svg index 949d1ef3a7..e3c58fefe3 100644 --- a/editor/icons/MaterialPreviewSphere.svg +++ b/editor/icons/MaterialPreviewSphere.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1a7 7 0 0 0 0 14A7 7 0 0 0 8 1zM6 3a2 2 0 0 1 0 4 2 2 0 0 1 0-4z" fill="#e0e0e0"/></svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><circle cx="8" cy="8" r="8" fill="#000" fill-opacity=".8"/><path d="M8 1a7 7 0 0 0 0 14A7 7 0 0 0 8 1zM6 7a2 2 0 0 1 0-4 2 2 0 0 1 0 4" fill="#e0e0e0"/></svg> diff --git a/editor/icons/MaterialPreviewSphereOff.svg b/editor/icons/MaterialPreviewSphereOff.svg deleted file mode 100644 index 5ebd36f85f..0000000000 --- a/editor/icons/MaterialPreviewSphereOff.svg +++ /dev/null @@ -1 +0,0 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1a7 7 0 0 0 0 14A7 7 0 0 0 8 1zM6 3a2 2 0 0 1 0 4 2 2 0 0 1 0-4z" fill="#e0e0e0"/><path d="M8 1a7 7 0 0 0 0 14A7 7 0 0 0 8 1zM6 3a2 2 0 0 1 0 4 2 2 0 0 1 0-4z" fill-opacity=".23529"/></svg> diff --git a/editor/icons/NavigationAgent2D.svg b/editor/icons/NavigationAgent2D.svg index 09b9b35262..ad11096afc 100644 --- a/editor/icons/NavigationAgent2D.svg +++ b/editor/icons/NavigationAgent2D.svg @@ -1 +1 @@ -<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1v2.5a2.5 2.5 0 0 1 0 5V15c2-3 5.007-6.03 5-9s-2-5-5-5z" fill="#8da5f3"/><path d="M8 1C5 1 3 3 3 6s3 6 5 9V8.5a1 1 0 0 1 0-5z" fill="#e0e0e0"/></svg> +<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1v2.5a2.5 2.5 0 0 1 0 5V15c2-3 5-6 5-9s-2-5-5-5z" fill="#8da5f3"/><path d="M8 1C5 1 3 3 3 6s3 6 5 9V8.5a1 1 0 0 1 0-5z" fill="#e0e0e0"/></svg> diff --git a/editor/icons/NavigationAgent3D.svg b/editor/icons/NavigationAgent3D.svg index cc34201f9b..6f887611a9 100644 --- a/editor/icons/NavigationAgent3D.svg +++ b/editor/icons/NavigationAgent3D.svg @@ -1 +1 @@ -<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1.086c-.845.156-1.476.514-1.94.988L4.05 4.059Q4 8 7 9.879V15l1-1z" fill="#e0e0e0" fill-opacity=".5"/><path d="M7 3C4 3 3 5 3 7s2 5 4 8c.338-.507.672-1.012 1-1.514v-4.76a2 2 0 1 1 0-3.453V3.084A5.663 5.663 0 0 0 7 3z" fill="#e0e0e0"/><g fill="#fc7f7f"><path d="M9 1c-.363 0-.695.03-1 .086V14l1-1c2-3 4-6 4-8s-1-4-4-4z" fill-opacity=".5"/><path d="M8 3.084v2.19a2 2 0 0 1 0 3.453v4.76c1.615-2.47 3-4.825 3-6.487 0-1.759-.774-3.517-3-3.916z"/></g></svg> +<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1C5 1 3 3 3 6s3 6 5 9V8.5a1 1 0 0 1 0-5z" fill="#e0e0e0"/><path d="M8 1v2.5a2.5 2.5 0 0 1 0 5V15c2-3 5-6 5-9s-2-5-5-5z" fill="#fc7f7f"/></svg> diff --git a/editor/icons/NavigationObstacle3D.svg b/editor/icons/NavigationObstacle3D.svg index 2f6f198aab..52080bf3fc 100644 --- a/editor/icons/NavigationObstacle3D.svg +++ b/editor/icons/NavigationObstacle3D.svg @@ -1 +1 @@ -<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0"><path d="M4.607 8.379C2.81 9.307 1 10.45 1 11c0 1 6 4 7 4v-2c-2.5 0-5-1-4-3z" fill-opacity=".5"/><path d="M8 .875c-.375 0-.75.375-1 1.125l-3 8c-1 2 1.5 3 4 3V9.25c-.875 0-1.75-.25-2.5-.75l1-3.5c.5.25 1 .375 1.5.375z"/></g><g fill="#fc7f7f"><path d="M11.393 8.379 12 10c1.002 2.005-1.512 3.005-4.018 3v1.998L8 15c1 0 7-3 7-4 0-.549-1.81-1.693-3.607-2.621z" fill-opacity=".5"/><path d="m8 .875-.018.002v4.498A3.323 3.323 0 0 0 9.5 5l1 3.5a4.508 4.508 0 0 1-2.518.75V13c2.506.005 5.02-.995 4.018-3L9 2C8.75 1.25 8.375.875 8 .875z"/></g></svg> +<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8 .875C7.375.875 6.75 1.25 6.5 2l-3 10H8V9H5.5l1-4H8zM2 13c-1 0-1 2 0 2h6v-2z" fill="#e0e0e0"/><path d="M8 .875V5h1.5l1 4H8v3h4.5l-3-10C9.25 1.25 8.625.875 8 .875zM8 13v2h6c1 0 1-2 0-2z" fill="#fc7f7f"/></svg> diff --git a/editor/icons/PlaceholderMaterial.svg b/editor/icons/PlaceholderMaterial.svg new file mode 100644 index 0000000000..3b5a803e05 --- /dev/null +++ b/editor/icons/PlaceholderMaterial.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><linearGradient x2="0" y2="16" gradientUnits="userSpaceOnUse" id="a"><stop offset=".188" stop-color="#ff4545"/><stop stop-color="#ffe345"/><stop offset=".313" stop-color="#ffe345"/><stop stop-color="#80ff45"/><stop offset=".438" stop-color="#80ff45"/><stop stop-color="#45ffa2"/><stop offset=".563" stop-color="#45ffa2"/><stop stop-color="#45d7ff"/><stop offset=".688" stop-color="#45d7ff"/><stop stop-color="#8045ff"/><stop offset=".813" stop-color="#8045ff"/><stop stop-color="#ff4596"/></linearGradient><g stroke="url(#a)"><path stroke-linejoin="round" fill="none" stroke-width="2" d="M3 3h10v10H3z"/><path d="M7 5.5 5.5 7m5-1.5-5 5m5-1.5L9 10.5" stroke-linecap="round" stroke-width="1.5"/></g></svg> diff --git a/editor/icons/PlaceholderMesh.svg b/editor/icons/PlaceholderMesh.svg new file mode 100644 index 0000000000..c36156eb6f --- /dev/null +++ b/editor/icons/PlaceholderMesh.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M4.729 2A2 2 0 1 0 2 4.73v6.541A2 2 0 1 0 4.73 14h6.541a2 2 0 1 0 2.698-2.75H14V4.729A2 2 0 1 0 11.27 2H4.729zm6.542 2a2 2 0 0 0 .729.729v6.542a2 2 0 0 0-.729.729H4.729A2 2 0 0 0 4 11.271V4.729A2 2 0 0 0 4.729 4z" fill="#ffca5f"/><path d="m7.25 5.25-2 2M10.5 5.5l-5 5m5.25-1.75-2 2" stroke-width="1.5" stroke-linecap="round" stroke="#ffca5f"/></svg> diff --git a/editor/icons/PlaceholderTexture2D.svg b/editor/icons/PlaceholderTexture2D.svg new file mode 100644 index 0000000000..3e7a3754fe --- /dev/null +++ b/editor/icons/PlaceholderTexture2D.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zm1 2h10v8H3z" fill="#e0e0e0"/><path d="M7 4.25 4.25 7m6.5-2.75-5.5 5.5M11.75 7 9 9.75" stroke-width="1.5" stroke-linecap="round" stroke="#e0e0e0"/></svg> diff --git a/editor/icons/PlaceholderTexture3D.svg b/editor/icons/PlaceholderTexture3D.svg new file mode 100644 index 0000000000..93014b92ac --- /dev/null +++ b/editor/icons/PlaceholderTexture3D.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M1 14a1 1 0 0 0 1 1h9.5a1 1 0 0 0 .707-.293l2.5-2.5A1 1 0 0 0 15 11.5V2a1 1 0 0 0-1-1H4.5a1 1 0 0 0-.707.293l-2.5 2.5A1 1 0 0 0 1 4.5zm1.25-9H11v7H2.25zm10 6.25v-6.5L14 3v6.5zm-1-7.5H3L4.75 2H13z" fill="#e0e0e0"/><path d="M5.25 6.25 3.5 8m5.375-1.75-4.5 4.5M9.75 9 8 10.75" stroke-width="1.5" stroke-linecap="round" stroke="#e0e0e0"/></svg> diff --git a/editor/icons/PortableCompressedTexture2D.svg b/editor/icons/PortableCompressedTexture2D.svg new file mode 100644 index 0000000000..3f63ddae51 --- /dev/null +++ b/editor/icons/PortableCompressedTexture2D.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h7v-4H3V3h6V1zm6 5v1H6v1H5v1H4v1h5V6zm3-5h4v4h-4Zm0 5h4v4h-4Zm0 5h4v4h-4Z" fill="#e0e0e0"/></svg> diff --git a/editor/icons/ProxyTexture.svg b/editor/icons/ProxyTexture.svg deleted file mode 100644 index 5fe39f4da8..0000000000 --- a/editor/icons/ProxyTexture.svg +++ /dev/null @@ -1 +0,0 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M1 1v4h4V1zm6 0v2h6v8H7v4h7a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zm2 4v1H8v1H7v3h5V8h-1V6h-1V5zM1 6v4h4V6zm0 5v4h4v-4z" fill="#e0e0e0"/></svg> diff --git a/editor/icons/Texture3D.svg b/editor/icons/Texture3D.svg deleted file mode 100644 index ca80bcc76c..0000000000 --- a/editor/icons/Texture3D.svg +++ /dev/null @@ -1 +0,0 @@ -<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zm1 2h10v8H3zm1.973 7.075c-.134 0-.275-.012-.424-.036a3.3 3.3 0 0 1-.432-.08 4.066 4.066 0 0 1-.381-.115 2.025 2.025 0 0 1-.268-.115l.216-1.1c.129.065.293.136.492.213.204.071.455.106.753.106.342 0 .593-.076.752-.23s.239-.361.239-.621a.806.806 0 0 0-.09-.399.643.643 0 0 0-.231-.266.851.851 0 0 0-.357-.142 2.047 2.047 0 0 0-.432-.044h-.433V6.182h.492c.109 0 .214-.012.313-.036a.813.813 0 0 0 .276-.115.611.611 0 0 0 .186-.23.835.835 0 0 0 .075-.373c0-.112-.02-.21-.06-.292a.586.586 0 0 0-.157-.204.524.524 0 0 0-.216-.116.709.709 0 0 0-.246-.044 1.53 1.53 0 0 0-.596.115 2.401 2.401 0 0 0-.491.284l-.395-.966a2.774 2.774 0 0 1 1.043-.497c.149-.035.305-.053.469-.053.303 0 .564.044.783.133.223.083.407.204.551.363.144.154.251.337.321.55.069.207.104.435.104.683 0 .242-.057.479-.172.709a1.248 1.248 0 0 1-.462.515c.269.13.475.325.619.585.149.254.223.561.223.922a2.5 2.5 0 0 1-.119.789 1.645 1.645 0 0 1-.372.621 1.801 1.801 0 0 1-.649.408 2.703 2.703 0 0 1-.924.142zm4.295-1.26.186.018h.261c.581 0 1.011-.174 1.289-.523.284-.349.425-.831.425-1.445 0-.645-.134-1.132-.402-1.463-.269-.331-.693-.497-1.274-.497-.08 0-.162.003-.246.009-.085 0-.164.006-.239.018zm3.361-1.95c0 .532-.07.996-.209 1.392s-.338.724-.596.984c-.253.26-.564.455-.931.585-.368.13-.78.195-1.237.195-.209 0-.452-.011-.731-.035a4.777 4.777 0 0 1-.819-.124V3.876c.268-.059.546-.097.834-.115.293-.023.544-.035.753-.035.442 0 .842.059 1.2.177.362.118.673.305.931.559s.457.579.596.975.209.872.209 1.428z" fill="#e0e0e0"/></svg> diff --git a/editor/icons/UndoRedo.svg b/editor/icons/UndoRedo.svg new file mode 100644 index 0000000000..459efeba39 --- /dev/null +++ b/editor/icons/UndoRedo.svg @@ -0,0 +1 @@ +<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8 9H1V2l2 2a3.875 5 30 0 1 9 11 3.5 5 10 0 0-6-8z" fill="#e0e0e0"/></svg> diff --git a/editor/icons/XRCamera3D.svg b/editor/icons/XRCamera3D.svg index 501a86a9f7..88e11cfe34 100644 --- a/editor/icons/XRCamera3D.svg +++ b/editor/icons/XRCamera3D.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M9.5 0a3 3 0 0 0-3 2.777 3 3 0 1 0-3 5.047V10a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V9l3 2V5l-3 2V5.23A3 3 0 0 0 9.5 0zM4 12v1a1 1 0 0 0 1 1 1 1 0 0 0-1 1v1h1v-1h1v1h1v-1a1 1 0 0 0-1-1 1 1 0 0 0 1-1v-1H6v1H5v-1zm5 0v4h1v-1h1v1h1v-1a1 1 0 0 0-.137-.5A1 1 0 0 0 12 14v-1a1 1 0 0 0-1-1zm1 1h1v1h-1z" fill="#fc7f7f"/></svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#fc7f7f"><g fill-opacity=".996078" transform="translate(0 -2)"><path d="m7 2-2 3h-3s-1 .000001-1 1v6s.000001 1 1 1h12s1-.000001 1-1v-6s-.000001-1-1-1h-3l-2-3zm1 4c1.6568542 0 3 1.3431458 3 3 0 1.656854-1.3431458 3-3 3s-3-1.343146-3-3c0-1.6568542 1.3431458-3 3-3z"/><path d="m2 2h2v2h-2z"/></g><path d="m4 12v1c0 .552285.4477153 1 1 1-.5522847 0-1 .447715-1 1v1h1v-1h1v1h1v-1c0-.552285-.4477153-1-1-1 .5522847 0 1-.447715 1-1v-1h-1v1h-1v-1zm5 0v4h1v-1h1v1h1v-1c-.000919-.175812-.04817-.348275-.137-.5.08883-.151725.136081-.324188.137-.5v-1c0-.552285-.447715-1-1-1zm1 1h1v1h-1z"/></g></svg> diff --git a/editor/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp index 3c27864eff..46882e95cb 100644 --- a/editor/import/resource_importer_layered_texture.cpp +++ b/editor/import/resource_importer_layered_texture.cpp @@ -474,12 +474,19 @@ bool ResourceImporterLayeredTexture::are_import_settings_valid(const String &p_p ResourceImporterLayeredTexture *ResourceImporterLayeredTexture::singleton = nullptr; -ResourceImporterLayeredTexture::ResourceImporterLayeredTexture() { - singleton = this; +ResourceImporterLayeredTexture::ResourceImporterLayeredTexture(bool p_singleton) { + // This should only be set through the EditorNode. + if (p_singleton) { + singleton = this; + } + mode = MODE_CUBEMAP; } ResourceImporterLayeredTexture::~ResourceImporterLayeredTexture() { + if (singleton == this) { + singleton = nullptr; + } } void ResourceImporterLayeredTexture::_check_compress_ctex(const String &p_source_file, Ref<LayeredTextureImport> r_texture_import) { diff --git a/editor/import/resource_importer_layered_texture.h b/editor/import/resource_importer_layered_texture.h index 52fd37639d..5a21651de3 100644 --- a/editor/import/resource_importer_layered_texture.h +++ b/editor/import/resource_importer_layered_texture.h @@ -119,7 +119,7 @@ public: void set_mode(Mode p_mode) { mode = p_mode; } - ResourceImporterLayeredTexture(); + ResourceImporterLayeredTexture(bool p_singleton = false); ~ResourceImporterLayeredTexture(); }; diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index b0291f3a93..2e422d8c27 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -2607,15 +2607,28 @@ void ResourceImporterScene::ResourceImporterScene::show_advanced_options(const S SceneImportSettings::get_singleton()->open_settings(p_path, animation_importer); } -ResourceImporterScene::ResourceImporterScene(bool p_animation_import) { - if (p_animation_import) { - animation_singleton = this; - } else { - scene_singleton = this; +ResourceImporterScene::ResourceImporterScene(bool p_animation_import, bool p_singleton) { + // This should only be set through the EditorNode. + if (p_singleton) { + if (p_animation_import) { + animation_singleton = this; + } else { + scene_singleton = this; + } } + animation_importer = p_animation_import; } +ResourceImporterScene::~ResourceImporterScene() { + if (animation_singleton == this) { + animation_singleton = nullptr; + } + if (scene_singleton == this) { + scene_singleton = nullptr; + } +} + void ResourceImporterScene::add_importer(Ref<EditorSceneFormatImporter> p_importer, bool p_first_priority) { ERR_FAIL_COND(p_importer.is_null()); if (p_first_priority) { diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h index a66fd034f8..7a07a5f646 100644 --- a/editor/import/resource_importer_scene.h +++ b/editor/import/resource_importer_scene.h @@ -296,7 +296,8 @@ public: virtual bool can_import_threaded() const override { return false; } - ResourceImporterScene(bool p_animation_import = false); + ResourceImporterScene(bool p_animation_import = false, bool p_singleton = false); + ~ResourceImporterScene(); template <class M> static Vector<Ref<Shape3D>> get_collision_shapes(const Ref<ImporterMesh> &p_mesh, const M &p_options, float p_applied_root_scale); diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index 114ba5653a..8eac5ec323 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -789,10 +789,12 @@ bool ResourceImporterTexture::are_import_settings_valid(const String &p_path) co ResourceImporterTexture *ResourceImporterTexture::singleton = nullptr; -ResourceImporterTexture::ResourceImporterTexture() { - if (!singleton) { +ResourceImporterTexture::ResourceImporterTexture(bool p_singleton) { + // This should only be set through the EditorNode. + if (p_singleton) { singleton = this; } + CompressedTexture2D::request_3d_callback = _texture_reimport_3d; CompressedTexture2D::request_roughness_callback = _texture_reimport_roughness; CompressedTexture2D::request_normal_callback = _texture_reimport_normal; diff --git a/editor/import/resource_importer_texture.h b/editor/import/resource_importer_texture.h index c2bdbb6fa2..f2539a8f52 100644 --- a/editor/import/resource_importer_texture.h +++ b/editor/import/resource_importer_texture.h @@ -107,7 +107,7 @@ public: virtual bool are_import_settings_valid(const String &p_path) const override; virtual String get_import_settings_string() const override; - ResourceImporterTexture(); + ResourceImporterTexture(bool p_singleton = false); ~ResourceImporterTexture(); }; diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp index a17f497725..fcd2d8f908 100644 --- a/editor/import_dock.cpp +++ b/editor/import_dock.cpp @@ -324,6 +324,21 @@ void ImportDock::set_edit_multiple_paths(const Vector<String> &p_paths) { } } +void ImportDock::reimport_resources(const Vector<String> &p_paths) { + switch (p_paths.size()) { + case 0: + ERR_FAIL_MSG("You need to select files to reimport them."); + case 1: + set_edit_path(p_paths[0]); + break; + default: + set_edit_multiple_paths(p_paths); + break; + } + + _reimport_attempt(); +} + void ImportDock::_update_preset_menu() { preset->get_popup()->clear(); diff --git a/editor/import_dock.h b/editor/import_dock.h index 07c54f8beb..78cd6a556c 100644 --- a/editor/import_dock.h +++ b/editor/import_dock.h @@ -102,6 +102,7 @@ protected: public: void set_edit_path(const String &p_path); void set_edit_multiple_paths(const Vector<String> &p_paths); + void reimport_resources(const Vector<String> &p_paths); void initialize_import_options() const; void clear(); diff --git a/editor/plugin_config_dialog.cpp b/editor/plugin_config_dialog.cpp index 97398b8b69..7d7b156bd0 100644 --- a/editor/plugin_config_dialog.cpp +++ b/editor/plugin_config_dialog.cpp @@ -35,6 +35,7 @@ #include "editor/editor_node.h" #include "editor/editor_plugin.h" #include "editor/editor_scale.h" +#include "editor/gui/editor_validation_panel.h" #include "editor/project_settings_editor.h" #include "scene/gui/grid_container.h" @@ -96,52 +97,28 @@ void PluginConfigDialog::_on_canceled() { _clear_fields(); } -void PluginConfigDialog::_on_language_changed(const int) { - _on_required_text_changed(String()); -} - -void PluginConfigDialog::_on_required_text_changed(const String &) { +void PluginConfigDialog::_on_required_text_changed() { int lang_idx = script_option_edit->get_selected(); String ext = ScriptServer::get_language(lang_idx)->get_extension(); - Ref<Texture2D> valid_icon = get_theme_icon(SNAME("StatusSuccess"), SNAME("EditorIcons")); - Ref<Texture2D> invalid_icon = get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons")); - - // Set variables to assume all is valid - bool is_valid = true; - name_validation->set_texture(valid_icon); - subfolder_validation->set_texture(valid_icon); - script_validation->set_texture(valid_icon); - name_validation->set_tooltip_text(""); - subfolder_validation->set_tooltip_text(""); - script_validation->set_tooltip_text(""); - - // Change valid status to invalid depending on conditions. - Vector<String> errors; if (name_edit->get_text().is_empty()) { - is_valid = false; - name_validation->set_texture(invalid_icon); - name_validation->set_tooltip_text(TTR("Plugin name cannot be blank.")); + validation_panel->set_message(MSG_ID_PLUGIN, TTR("Plugin name cannot be blank."), EditorValidationPanel::MSG_ERROR); } if ((!script_edit->get_text().get_extension().is_empty() && script_edit->get_text().get_extension() != ext) || script_edit->get_text().ends_with(".")) { - is_valid = false; - script_validation->set_texture(invalid_icon); - script_validation->set_tooltip_text(vformat(TTR("Script extension must match chosen language extension (.%s)."), ext)); + validation_panel->set_message(MSG_ID_SCRIPT, vformat(TTR("Script extension must match chosen language extension (.%s)."), ext), EditorValidationPanel::MSG_ERROR); } - if (!subfolder_edit->get_text().is_empty() && !subfolder_edit->get_text().is_valid_filename()) { - is_valid = false; - subfolder_validation->set_texture(invalid_icon); - subfolder_validation->set_tooltip_text(TTR("Subfolder name is not a valid folder name.")); - } else { - String path = "res://addons/" + _get_subfolder(); - if (!_edit_mode && DirAccess::exists(path)) { // Only show this error if in "create" mode. - is_valid = false; - subfolder_validation->set_texture(invalid_icon); - subfolder_validation->set_tooltip_text(TTR("Subfolder cannot be one which already exists.")); + if (subfolder_edit->is_visible()) { + if (!subfolder_edit->get_text().is_empty() && !subfolder_edit->get_text().is_valid_filename()) { + validation_panel->set_message(MSG_ID_SUBFOLDER, TTR("Subfolder name is not a valid folder name."), EditorValidationPanel::MSG_ERROR); + } else { + String path = "res://addons/" + _get_subfolder(); + if (!_edit_mode && DirAccess::exists(path)) { // Only show this error if in "create" mode. + validation_panel->set_message(MSG_ID_SUBFOLDER, TTR("Subfolder cannot be one which already exists."), EditorValidationPanel::MSG_ERROR); + } } + } else { + validation_panel->set_message(MSG_ID_SUBFOLDER, "", EditorValidationPanel::MSG_OK); } - - get_ok_button()->set_disabled(!is_valid); } String PluginConfigDialog::_get_subfolder() { @@ -182,23 +159,20 @@ void PluginConfigDialog::config(const String &p_config_path) { _edit_mode = true; active_edit->hide(); - Object::cast_to<Label>(active_edit->get_parent()->get_child(active_edit->get_index() - 2))->hide(); + Object::cast_to<Label>(active_edit->get_parent()->get_child(active_edit->get_index() - 1))->hide(); subfolder_edit->hide(); - subfolder_validation->hide(); - Object::cast_to<Label>(subfolder_edit->get_parent()->get_child(subfolder_edit->get_index() - 2))->hide(); + Object::cast_to<Label>(subfolder_edit->get_parent()->get_child(subfolder_edit->get_index() - 1))->hide(); set_title(TTR("Edit a Plugin")); } else { _clear_fields(); _edit_mode = false; active_edit->show(); - Object::cast_to<Label>(active_edit->get_parent()->get_child(active_edit->get_index() - 2))->show(); + Object::cast_to<Label>(active_edit->get_parent()->get_child(active_edit->get_index() - 1))->show(); subfolder_edit->show(); - subfolder_validation->show(); - Object::cast_to<Label>(subfolder_edit->get_parent()->get_child(subfolder_edit->get_index() - 2))->show(); + Object::cast_to<Label>(subfolder_edit->get_parent()->get_child(subfolder_edit->get_index() - 1))->show(); set_title(TTR("Create a Plugin")); } - // Simulate text changing so the errors populate. - _on_required_text_changed(""); + validation_panel->update(); get_ok_button()->set_disabled(!_edit_mode); set_ok_button_text(_edit_mode ? TTR("Update") : TTR("Create")); @@ -218,7 +192,7 @@ PluginConfigDialog::PluginConfigDialog() { add_child(vbox); GridContainer *grid = memnew(GridContainer); - grid->set_columns(3); + grid->set_columns(2); grid->set_v_size_flags(Control::SIZE_EXPAND_FILL); vbox->add_child(grid); @@ -228,12 +202,7 @@ PluginConfigDialog::PluginConfigDialog() { name_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); grid->add_child(name_lb); - name_validation = memnew(TextureRect); - name_validation->set_v_size_flags(Control::SIZE_SHRINK_CENTER); - grid->add_child(name_validation); - name_edit = memnew(LineEdit); - name_edit->connect("text_changed", callable_mp(this, &PluginConfigDialog::_on_required_text_changed)); name_edit->set_placeholder("MyPlugin"); name_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); grid->add_child(name_edit); @@ -244,14 +213,9 @@ PluginConfigDialog::PluginConfigDialog() { subfolder_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); grid->add_child(subfolder_lb); - subfolder_validation = memnew(TextureRect); - subfolder_validation->set_v_size_flags(Control::SIZE_SHRINK_CENTER); - grid->add_child(subfolder_validation); - subfolder_edit = memnew(LineEdit); subfolder_edit->set_placeholder("\"my_plugin\" -> res://addons/my_plugin"); subfolder_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); - subfolder_edit->connect("text_changed", callable_mp(this, &PluginConfigDialog::_on_required_text_changed)); grid->add_child(subfolder_edit); // Description @@ -260,9 +224,6 @@ PluginConfigDialog::PluginConfigDialog() { desc_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); grid->add_child(desc_lb); - Control *desc_spacer = memnew(Control); - grid->add_child(desc_spacer); - desc_edit = memnew(TextEdit); desc_edit->set_custom_minimum_size(Size2(400, 80) * EDSCALE); desc_edit->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY); @@ -276,9 +237,6 @@ PluginConfigDialog::PluginConfigDialog() { author_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); grid->add_child(author_lb); - Control *author_spacer = memnew(Control); - grid->add_child(author_spacer); - author_edit = memnew(LineEdit); author_edit->set_placeholder("Godette"); author_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); @@ -290,9 +248,6 @@ PluginConfigDialog::PluginConfigDialog() { version_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); grid->add_child(version_lb); - Control *version_spacer = memnew(Control); - grid->add_child(version_spacer); - version_edit = memnew(LineEdit); version_edit->set_placeholder("1.0"); version_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); @@ -304,9 +259,6 @@ PluginConfigDialog::PluginConfigDialog() { script_option_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); grid->add_child(script_option_lb); - Control *script_opt_spacer = memnew(Control); - grid->add_child(script_opt_spacer); - script_option_edit = memnew(OptionButton); int default_lang = 0; for (int i = 0; i < ScriptServer::get_language_count(); i++) { @@ -318,7 +270,6 @@ PluginConfigDialog::PluginConfigDialog() { } script_option_edit->select(default_lang); grid->add_child(script_option_edit); - script_option_edit->connect("item_selected", callable_mp(this, &PluginConfigDialog::_on_language_changed)); // Plugin Script Name Label *script_lb = memnew(Label); @@ -326,12 +277,7 @@ PluginConfigDialog::PluginConfigDialog() { script_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); grid->add_child(script_lb); - script_validation = memnew(TextureRect); - script_validation->set_v_size_flags(Control::SIZE_SHRINK_CENTER); - grid->add_child(script_validation); - script_edit = memnew(LineEdit); - script_edit->connect("text_changed", callable_mp(this, &PluginConfigDialog::_on_required_text_changed)); script_edit->set_placeholder("\"plugin.gd\" -> res://addons/my_plugin/plugin.gd"); script_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); grid->add_child(script_edit); @@ -343,12 +289,26 @@ PluginConfigDialog::PluginConfigDialog() { active_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); grid->add_child(active_lb); - Control *active_spacer = memnew(Control); - grid->add_child(active_spacer); - active_edit = memnew(CheckBox); active_edit->set_pressed(true); grid->add_child(active_edit); + + Control *spacing = memnew(Control); + vbox->add_child(spacing); + spacing->set_custom_minimum_size(Size2(0, 10 * EDSCALE)); + + validation_panel = memnew(EditorValidationPanel); + vbox->add_child(validation_panel); + validation_panel->add_line(MSG_ID_PLUGIN, TTR("Plugin name is valid.")); + validation_panel->add_line(MSG_ID_SCRIPT, TTR("Script extension is valid.")); + validation_panel->add_line(MSG_ID_SUBFOLDER, TTR("Subfolder name is valid.")); + validation_panel->set_update_callback(callable_mp(this, &PluginConfigDialog::_on_required_text_changed)); + validation_panel->set_accept_button(get_ok_button()); + + script_option_edit->connect("item_selected", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1)); + name_edit->connect("text_changed", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1)); + subfolder_edit->connect("text_changed", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1)); + script_edit->connect("text_changed", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1)); } PluginConfigDialog::~PluginConfigDialog() { diff --git a/editor/plugin_config_dialog.h b/editor/plugin_config_dialog.h index 50b901a39e..1221d347a7 100644 --- a/editor/plugin_config_dialog.h +++ b/editor/plugin_config_dialog.h @@ -35,12 +35,21 @@ #include "scene/gui/dialogs.h" #include "scene/gui/line_edit.h" #include "scene/gui/option_button.h" +#include "scene/gui/panel_container.h" #include "scene/gui/text_edit.h" #include "scene/gui/texture_rect.h" +class EditorValidationPanel; + class PluginConfigDialog : public ConfirmationDialog { GDCLASS(PluginConfigDialog, ConfirmationDialog); + enum { + MSG_ID_PLUGIN, + MSG_ID_SUBFOLDER, + MSG_ID_SCRIPT, + }; + LineEdit *name_edit = nullptr; LineEdit *subfolder_edit = nullptr; TextEdit *desc_edit = nullptr; @@ -50,17 +59,14 @@ class PluginConfigDialog : public ConfirmationDialog { LineEdit *script_edit = nullptr; CheckBox *active_edit = nullptr; - TextureRect *name_validation = nullptr; - TextureRect *subfolder_validation = nullptr; - TextureRect *script_validation = nullptr; + EditorValidationPanel *validation_panel = nullptr; bool _edit_mode = false; void _clear_fields(); void _on_confirmed(); void _on_canceled(); - void _on_language_changed(const int p_language); - void _on_required_text_changed(const String &p_text); + void _on_required_text_changed(); String _get_subfolder(); static String _to_absolute_plugin_path(const String &p_plugin_name); diff --git a/editor/plugins/animation_tree_editor_plugin.cpp b/editor/plugins/animation_tree_editor_plugin.cpp index 9fa1e63dcb..7b5f8aa7f7 100644 --- a/editor/plugins/animation_tree_editor_plugin.cpp +++ b/editor/plugins/animation_tree_editor_plugin.cpp @@ -241,7 +241,8 @@ bool AnimationTreeEditor::can_edit(const Ref<AnimationNode> &p_node) const { } Vector<String> AnimationTreeEditor::get_animation_list() { - if (!singleton->is_visible()) { + if (!singleton->tree || !singleton->is_visible()) { + // When tree is empty, singleton not in the main thread. return Vector<String>(); } diff --git a/editor/plugins/bit_map_editor_plugin.cpp b/editor/plugins/bit_map_editor_plugin.cpp index 3388cab006..f2423cb803 100644 --- a/editor/plugins/bit_map_editor_plugin.cpp +++ b/editor/plugins/bit_map_editor_plugin.cpp @@ -37,7 +37,7 @@ void BitMapEditor::setup(const Ref<BitMap> &p_bitmap) { texture_rect->set_texture(ImageTexture::create_from_image(p_bitmap->convert_to_image())); - size_label->set_text(vformat(String::utf8("%s×%s"), p_bitmap->get_size().width, p_bitmap->get_size().height)); + size_label->set_text(vformat(U"%s×%s", p_bitmap->get_size().width, p_bitmap->get_size().height)); } BitMapEditor::BitMapEditor() { diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 0386a05cc2..c9c67f3d6b 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -1289,7 +1289,7 @@ void CanvasItemEditor::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_eve void CanvasItemEditor::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) { Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid()) { - // Special behvior for scroll events, as the zoom_by_increment method can smartly end up on powers of two. + // Special behavior for scroll events, as the zoom_by_increment method can smartly end up on powers of two. int increment = p_zoom_factor > 1.0 ? 1 : -1; bool by_integer = mb->is_alt_pressed(); @@ -2282,8 +2282,8 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) { if (b.is_valid() && b->is_pressed() && b->get_button_index() == MouseButton::RIGHT) { add_node_menu->clear(); - add_node_menu->add_icon_item(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")), TTR("Add Node Here"), ADD_NODE); - add_node_menu->add_icon_item(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")), TTR("Instantiate Scene Here"), ADD_INSTANCE); + add_node_menu->add_icon_item(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")), TTR("Add Node Here..."), ADD_NODE); + add_node_menu->add_icon_item(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")), TTR("Instantiate Scene Here..."), ADD_INSTANCE); for (Node *node : SceneTreeDock::get_singleton()->get_node_clipboard()) { if (Object::cast_to<CanvasItem>(node)) { add_node_menu->add_icon_item(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons")), TTR("Paste Node(s) Here"), ADD_PASTE); @@ -3027,8 +3027,8 @@ void CanvasItemEditor::_draw_ruler_tool() { Point2 v_angle_text_pos; v_angle_text_pos.x = CLAMP(begin.x - angle_text_width / 2, angle_text_width / 2, viewport->get_rect().size.x - angle_text_width); v_angle_text_pos.y = begin.y < end.y ? MIN(text_pos2.y - 2 * text_height, begin.y - text_height * 0.5) : MAX(text_pos2.y + text_height * 3, begin.y + text_height * 1.5); - viewport->draw_string_outline(font, v_angle_text_pos, TS->format_number(vformat(String::utf8("%d°"), vertical_angle)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, outline_size, outline_color); - viewport->draw_string(font, v_angle_text_pos, TS->format_number(vformat(String::utf8("%d°"), vertical_angle)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_secondary_color); + viewport->draw_string_outline(font, v_angle_text_pos, TS->format_number(vformat(U"%d°", vertical_angle)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, outline_size, outline_color); + viewport->draw_string(font, v_angle_text_pos, TS->format_number(vformat(U"%d°", vertical_angle)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_secondary_color); text_pos2 = text_pos; text_pos2.y = end.y < text_pos.y ? MIN(text_pos.y - text_height * 2, end.y - text_height / 2) : MAX(text_pos.y + text_height * 2, end.y - text_height / 2); @@ -3050,8 +3050,8 @@ void CanvasItemEditor::_draw_ruler_tool() { h_angle_text_pos.y = MIN(text_pos.y - height_multiplier * text_height, MIN(end.y - text_height * 0.5, text_pos2.y - height_multiplier * text_height)); } } - viewport->draw_string_outline(font, h_angle_text_pos, TS->format_number(vformat(String::utf8("%d°"), horizontal_angle)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, outline_size, outline_color); - viewport->draw_string(font, h_angle_text_pos, TS->format_number(vformat(String::utf8("%d°"), horizontal_angle)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_secondary_color); + viewport->draw_string_outline(font, h_angle_text_pos, TS->format_number(vformat(U"%d°", horizontal_angle)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, outline_size, outline_color); + viewport->draw_string(font, h_angle_text_pos, TS->format_number(vformat(U"%d°", horizontal_angle)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_secondary_color); } if (grid_snap_active) { @@ -5079,7 +5079,7 @@ CanvasItemEditor::CanvasItemEditor() { viewport->add_child(controls_vb); - // Add some margin to the left for better aesthetics. + // Add some margin to the left for better esthetics. // This prevents the first button's hover/pressed effect from "touching" the panel's border, // which looks ugly. Control *margin_left = memnew(Control); diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp index 6228faaa72..6b5b0f9214 100644 --- a/editor/plugins/curve_editor_plugin.cpp +++ b/editor/plugins/curve_editor_plugin.cpp @@ -105,7 +105,7 @@ void CurveEdit::set_snap_count(int p_snap_count) { } Size2 CurveEdit::get_minimum_size() const { - return Vector2(64, 135) * EDSCALE; + return Vector2(64, MAX(135, get_size().x * ASPECT_RATIO)) * EDSCALE; } void CurveEdit::_notification(int p_what) { @@ -986,6 +986,9 @@ void CurveEditor::_notification(int p_what) { snap_count_edit->set_value(curve->get_meta("_snap_count", DEFAULT_SNAP)); } } break; + case NOTIFICATION_RESIZED: + curve_editor_rect->update_minimum_size(); + break; } } diff --git a/editor/plugins/curve_editor_plugin.h b/editor/plugins/curve_editor_plugin.h index b8d24c5cbc..b6a74d9b93 100644 --- a/editor/plugins/curve_editor_plugin.h +++ b/editor/plugins/curve_editor_plugin.h @@ -104,6 +104,8 @@ private: void _redraw(); private: + const float ASPECT_RATIO = 6.f / 13.f; + Transform2D _world_to_view; Ref<Curve> curve; diff --git a/editor/plugins/debugger_editor_plugin.cpp b/editor/plugins/debugger_editor_plugin.cpp index 3c9bc991d3..15829a55de 100644 --- a/editor/plugins/debugger_editor_plugin.cpp +++ b/editor/plugins/debugger_editor_plugin.cpp @@ -54,7 +54,6 @@ DebuggerEditorPlugin::DebuggerEditorPlugin(PopupMenu *p_debug_menu) { EditorDebuggerNode *debugger = memnew(EditorDebuggerNode); Button *db = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Debugger"), debugger); - db->set_theme_type_variation("BottomPanelButton"); debugger->set_tool_button(db); // Main editor debug menu. diff --git a/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp index e4503b9c6f..2ef2e3a666 100644 --- a/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp @@ -41,6 +41,7 @@ Camera3DGizmoPlugin::Camera3DGizmoPlugin() { Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/camera", Color(0.8, 0.4, 0.8)); create_material("camera_material", gizmo_color); + create_icon_material("camera_icon", Node3DEditor::get_singleton()->get_theme_icon("GizmoCamera3D", "EditorIcons")); create_handle_material("handles"); } @@ -159,6 +160,7 @@ void Camera3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { Vector<Vector3> handles; Ref<Material> material = get_material("camera_material", p_gizmo); + Ref<Material> icon = get_material("camera_icon", p_gizmo); const Size2i viewport_size = _get_viewport_size(camera); const real_t viewport_aspect = viewport_size.x > 0 && viewport_size.y > 0 ? viewport_size.aspect() : 1.0; @@ -256,6 +258,7 @@ void Camera3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { #undef ADD_QUAD p_gizmo->add_lines(lines, material); + p_gizmo->add_unscaled_billboard(icon, 0.05); p_gizmo->add_collision_segments(lines); if (!handles.is_empty()) { diff --git a/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp index 79f46f7bf7..b967cb7ccd 100644 --- a/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp @@ -134,6 +134,11 @@ Variant CollisionShape3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p return Variant(); } +void CollisionShape3DGizmoPlugin::begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) { + initial_transform = p_gizmo->get_node_3d()->get_global_transform(); + initial_value = get_handle_value(p_gizmo, p_id, p_secondary); +} + void CollisionShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) { CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(p_gizmo->get_node_3d()); @@ -142,7 +147,7 @@ void CollisionShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, i return; } - Transform3D gt = cs->get_global_transform(); + Transform3D gt = initial_transform; Transform3D gi = gt.affine_inverse(); Vector3 ray_from = p_camera->project_ray_origin(p_point); @@ -184,22 +189,37 @@ void CollisionShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, i if (Object::cast_to<BoxShape3D>(*s)) { Vector3 axis; - axis[p_id] = 1.0; + axis[p_id / 2] = 1.0; Ref<BoxShape3D> bs = s; Vector3 ra, rb; - Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); - float d = ra[p_id] * 2; - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); - } + int sign = p_id % 2 * -2 + 1; + Vector3 initial_size = initial_value; - if (d < 0.001) { - d = 0.001; + Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096 * sign, sg[0], sg[1], ra, rb); + if (ra[p_id / 2] == 0) { + // Point before half of the shape. Needs to be calculated in opposite direction. + Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096 * -sign, sg[0], sg[1], ra, rb); } + float d = ra[p_id / 2] * sign; + Vector3 he = bs->get_size(); - he[p_id] = d; - bs->set_size(he); + he[p_id / 2] = d * 2; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + he[p_id / 2] = Math::snapped(he[p_id / 2], Node3DEditor::get_singleton()->get_translate_snap()); + } + + if (Input::get_singleton()->is_key_pressed(Key::ALT)) { + he[p_id / 2] = MAX(he[p_id / 2], 0.001); + bs->set_size(he); + cs->set_global_position(initial_transform.get_origin()); + } else { + he[p_id / 2] = MAX(he[p_id / 2], -initial_size[p_id / 2] + 0.002); + bs->set_size((initial_size + (he - initial_size) * 0.5).abs()); + Vector3 pos = initial_transform.affine_inverse().xform(initial_transform.get_origin()); + pos += (bs->get_size() - initial_size) * 0.5 * sign; + cs->set_global_position(initial_transform.xform(pos)); + } } if (Object::cast_to<CapsuleShape3D>(*s)) { @@ -273,6 +293,7 @@ void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo if (Object::cast_to<BoxShape3D>(*s)) { Ref<BoxShape3D> ss = s; if (p_cancel) { + cs->set_global_position(initial_transform.get_origin()); ss->set_size(p_restore); return; } @@ -280,7 +301,9 @@ void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); ur->create_action(TTR("Change Box Shape Size")); ur->add_do_method(ss.ptr(), "set_size", ss->get_size()); + ur->add_do_method(cs, "set_position", cs->get_global_position()); ur->add_undo_method(ss.ptr(), "set_size", p_restore); + ur->add_undo_method(cs, "set_global_position", initial_transform.get_origin()); ur->commit_action(); } @@ -429,6 +452,7 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { Vector3 ax; ax[i] = bs->get_size()[i] / 2; handles.push_back(ax); + handles.push_back(-ax); } p_gizmo->add_lines(lines, material); diff --git a/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.h b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.h index 520295a522..6b7740de2f 100644 --- a/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.h +++ b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.h @@ -36,6 +36,9 @@ class CollisionShape3DGizmoPlugin : public EditorNode3DGizmoPlugin { GDCLASS(CollisionShape3DGizmoPlugin, EditorNode3DGizmoPlugin); + Transform3D initial_transform; + Variant initial_value; + public: bool has_gizmo(Node3D *p_spatial) override; String get_gizmo_name() const override; @@ -44,6 +47,7 @@ public: String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override; Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override; + void begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) override; void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) override; void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel = false) override; diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.cpp b/editor/plugins/gpu_particles_2d_editor_plugin.cpp index 03d3ac1732..78bbc1484b 100644 --- a/editor/plugins/gpu_particles_2d_editor_plugin.cpp +++ b/editor/plugins/gpu_particles_2d_editor_plugin.cpp @@ -116,11 +116,8 @@ void GPUParticles2DEditorPlugin::_menu_callback(int p_idx) { EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); ur->create_action(TTR("Convert to CPUParticles2D")); - ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", particles, cpu_particles, true, false); - ur->add_do_reference(cpu_particles); - ur->add_undo_method(SceneTreeDock::get_singleton(), "replace_node", cpu_particles, particles, false, false); - ur->add_undo_reference(particles); - ur->commit_action(); + SceneTreeDock::get_singleton()->replace_node(particles, cpu_particles); + ur->commit_action(false); } break; case MENU_RESTART: { diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.cpp b/editor/plugins/gpu_particles_3d_editor_plugin.cpp index f0b2e32c72..108f85152f 100644 --- a/editor/plugins/gpu_particles_3d_editor_plugin.cpp +++ b/editor/plugins/gpu_particles_3d_editor_plugin.cpp @@ -277,11 +277,8 @@ void GPUParticles3DEditor::_menu_option(int p_option) { EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); ur->create_action(TTR("Convert to CPUParticles3D")); - ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, cpu_particles, true, false); - ur->add_do_reference(cpu_particles); - ur->add_undo_method(SceneTreeDock::get_singleton(), "replace_node", cpu_particles, node, false, false); - ur->add_undo_reference(node); - ur->commit_action(); + SceneTreeDock::get_singleton()->replace_node(node, cpu_particles); + ur->commit_action(false); } break; case MENU_OPTION_RESTART: { diff --git a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp index 9582f0caa5..938a541a22 100644 --- a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp +++ b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp @@ -93,8 +93,8 @@ void GPUParticlesCollisionSDF3DEditorPlugin::_notification(int p_what) { } String text; - text += vformat(TTR("Subdivisions: %s"), vformat(String::utf8("%d × %d × %d"), size.x, size.y, size.z)) + "\n"; - text += vformat(TTR("Cell size: %s"), vformat(String::utf8("%.3f × %.3f × %.3f"), extents.x / size.x, extents.y / size.y, extents.z / size.z)) + "\n"; + text += vformat(TTR("Subdivisions: %s"), vformat(U"%d × %d × %d", size.x, size.y, size.z)) + "\n"; + text += vformat(TTR("Cell size: %s"), vformat(U"%.3f × %.3f × %.3f", extents.x / size.x, extents.y / size.y, extents.z / size.z)) + "\n"; text += vformat(TTR("Video RAM size: %s MB (%s)"), String::num(size_mb, 2), size_quality); // Only update the tooltip when needed to avoid constant redrawing. diff --git a/editor/plugins/material_editor_plugin.cpp b/editor/plugins/material_editor_plugin.cpp index 404711e074..e24172e761 100644 --- a/editor/plugins/material_editor_plugin.cpp +++ b/editor/plugins/material_editor_plugin.cpp @@ -39,9 +39,9 @@ #include "scene/3d/light_3d.h" #include "scene/3d/mesh_instance_3d.h" #include "scene/gui/box_container.h" +#include "scene/gui/button.h" #include "scene/gui/color_rect.h" #include "scene/gui/subviewport_container.h" -#include "scene/gui/texture_button.h" #include "scene/main/viewport.h" #include "scene/resources/fog_material.h" #include "scene/resources/particle_process_material.h" @@ -63,15 +63,11 @@ void MaterialEditor::gui_input(const Ref<InputEvent> &p_event) { void MaterialEditor::_update_theme_item_cache() { Control::_update_theme_item_cache(); - theme_cache.light_1_on = get_theme_icon(SNAME("MaterialPreviewLight1"), SNAME("EditorIcons")); - theme_cache.light_1_off = get_theme_icon(SNAME("MaterialPreviewLight1Off"), SNAME("EditorIcons")); - theme_cache.light_2_on = get_theme_icon(SNAME("MaterialPreviewLight2"), SNAME("EditorIcons")); - theme_cache.light_2_off = get_theme_icon(SNAME("MaterialPreviewLight2Off"), SNAME("EditorIcons")); + theme_cache.light_1_icon = get_theme_icon(SNAME("MaterialPreviewLight1"), SNAME("EditorIcons")); + theme_cache.light_2_icon = get_theme_icon(SNAME("MaterialPreviewLight2"), SNAME("EditorIcons")); - theme_cache.sphere_on = get_theme_icon(SNAME("MaterialPreviewSphere"), SNAME("EditorIcons")); - theme_cache.sphere_off = get_theme_icon(SNAME("MaterialPreviewSphereOff"), SNAME("EditorIcons")); - theme_cache.box_on = get_theme_icon(SNAME("MaterialPreviewCube"), SNAME("EditorIcons")); - theme_cache.box_off = get_theme_icon(SNAME("MaterialPreviewCubeOff"), SNAME("EditorIcons")); + theme_cache.sphere_icon = get_theme_icon(SNAME("MaterialPreviewSphere"), SNAME("EditorIcons")); + theme_cache.box_icon = get_theme_icon(SNAME("MaterialPreviewCube"), SNAME("EditorIcons")); theme_cache.checkerboard = get_theme_icon(SNAME("Checkerboard"), SNAME("EditorIcons")); } @@ -79,15 +75,11 @@ void MaterialEditor::_update_theme_item_cache() { void MaterialEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { - light_1_switch->set_texture_normal(theme_cache.light_1_on); - light_1_switch->set_texture_pressed(theme_cache.light_1_off); - light_2_switch->set_texture_normal(theme_cache.light_2_on); - light_2_switch->set_texture_pressed(theme_cache.light_2_off); - - sphere_switch->set_texture_normal(theme_cache.sphere_off); - sphere_switch->set_texture_pressed(theme_cache.sphere_on); - box_switch->set_texture_normal(theme_cache.box_off); - box_switch->set_texture_pressed(theme_cache.box_on); + light_1_switch->set_icon(theme_cache.light_1_icon); + light_2_switch->set_icon(theme_cache.light_2_icon); + + sphere_switch->set_icon(theme_cache.sphere_icon); + box_switch->set_icon(theme_cache.box_icon); } break; case NOTIFICATION_DRAW: { @@ -135,34 +127,32 @@ void MaterialEditor::edit(Ref<Material> p_material, const Ref<Environment> &p_en _update_rotation(); } -void MaterialEditor::_button_pressed(Node *p_button) { - if (p_button == light_1_switch) { - light1->set_visible(!light_1_switch->is_pressed()); - } +void MaterialEditor::_on_light_1_switch_pressed() { + light1->set_visible(light_1_switch->is_pressed()); +} - if (p_button == light_2_switch) { - light2->set_visible(!light_2_switch->is_pressed()); - } +void MaterialEditor::_on_light_2_switch_pressed() { + light2->set_visible(light_2_switch->is_pressed()); +} - if (p_button == box_switch) { - box_instance->show(); - sphere_instance->hide(); - box_switch->set_pressed(true); - sphere_switch->set_pressed(false); - EditorSettings::get_singleton()->set_project_metadata("inspector_options", "material_preview_on_sphere", false); - } +void MaterialEditor::_on_sphere_switch_pressed() { + box_instance->hide(); + sphere_instance->show(); + box_switch->set_pressed(false); + sphere_switch->set_pressed(true); + EditorSettings::get_singleton()->set_project_metadata("inspector_options", "material_preview_on_sphere", true); +} - if (p_button == sphere_switch) { - box_instance->hide(); - sphere_instance->show(); - box_switch->set_pressed(false); - sphere_switch->set_pressed(true); - EditorSettings::get_singleton()->set_project_metadata("inspector_options", "material_preview_on_sphere", true); - } +void MaterialEditor::_on_box_switch_pressed() { + box_instance->show(); + sphere_instance->hide(); + box_switch->set_pressed(true); + sphere_switch->set_pressed(false); + EditorSettings::get_singleton()->set_project_metadata("inspector_options", "material_preview_on_sphere", false); } MaterialEditor::MaterialEditor() { - // canvas item + // Canvas item vc_2d = memnew(SubViewportContainer); vc_2d->set_stretch(true); @@ -185,7 +175,7 @@ MaterialEditor::MaterialEditor() { layout_2d->set_visible(false); - // spatial + // Spatial vc = memnew(SubViewportContainer); vc->set_stretch(true); @@ -247,32 +237,38 @@ MaterialEditor::MaterialEditor() { VBoxContainer *vb_shape = memnew(VBoxContainer); layout_3d->add_child(vb_shape); - sphere_switch = memnew(TextureButton); + sphere_switch = memnew(Button); + sphere_switch->set_theme_type_variation("PreviewLightButton"); sphere_switch->set_toggle_mode(true); sphere_switch->set_pressed(true); vb_shape->add_child(sphere_switch); - sphere_switch->connect("pressed", callable_mp(this, &MaterialEditor::_button_pressed).bind(sphere_switch)); + sphere_switch->connect("pressed", callable_mp(this, &MaterialEditor::_on_sphere_switch_pressed)); - box_switch = memnew(TextureButton); + box_switch = memnew(Button); + box_switch->set_theme_type_variation("PreviewLightButton"); box_switch->set_toggle_mode(true); box_switch->set_pressed(false); vb_shape->add_child(box_switch); - box_switch->connect("pressed", callable_mp(this, &MaterialEditor::_button_pressed).bind(box_switch)); + box_switch->connect("pressed", callable_mp(this, &MaterialEditor::_on_box_switch_pressed)); layout_3d->add_spacer(); VBoxContainer *vb_light = memnew(VBoxContainer); layout_3d->add_child(vb_light); - light_1_switch = memnew(TextureButton); + light_1_switch = memnew(Button); + light_1_switch->set_theme_type_variation("PreviewLightButton"); light_1_switch->set_toggle_mode(true); + light_1_switch->set_pressed(true); vb_light->add_child(light_1_switch); - light_1_switch->connect("pressed", callable_mp(this, &MaterialEditor::_button_pressed).bind(light_1_switch)); + light_1_switch->connect("pressed", callable_mp(this, &MaterialEditor::_on_light_1_switch_pressed)); - light_2_switch = memnew(TextureButton); + light_2_switch = memnew(Button); + light_2_switch->set_theme_type_variation("PreviewLightButton"); light_2_switch->set_toggle_mode(true); + light_2_switch->set_pressed(true); vb_light->add_child(light_2_switch); - light_2_switch->connect("pressed", callable_mp(this, &MaterialEditor::_button_pressed).bind(light_2_switch)); + light_2_switch->connect("pressed", callable_mp(this, &MaterialEditor::_on_light_2_switch_pressed)); if (EditorSettings::get_singleton()->get_project_metadata("inspector_options", "material_preview_on_sphere", true)) { box_instance->hide(); diff --git a/editor/plugins/material_editor_plugin.h b/editor/plugins/material_editor_plugin.h index ac81bdc7c7..5530592a81 100644 --- a/editor/plugins/material_editor_plugin.h +++ b/editor/plugins/material_editor_plugin.h @@ -44,7 +44,7 @@ class HBoxContainer; class MeshInstance3D; class SubViewport; class SubViewportContainer; -class TextureButton; +class Button; class MaterialEditor : public Control { GDCLASS(MaterialEditor, Control); @@ -71,27 +71,25 @@ class MaterialEditor : public Control { HBoxContainer *layout_3d = nullptr; - TextureButton *sphere_switch = nullptr; - TextureButton *box_switch = nullptr; - - TextureButton *light_1_switch = nullptr; - TextureButton *light_2_switch = nullptr; - Ref<Material> material; + Button *sphere_switch = nullptr; + Button *box_switch = nullptr; + Button *light_1_switch = nullptr; + Button *light_2_switch = nullptr; + struct ThemeCache { - Ref<Texture2D> light_1_on; - Ref<Texture2D> light_1_off; - Ref<Texture2D> light_2_on; - Ref<Texture2D> light_2_off; - Ref<Texture2D> sphere_on; - Ref<Texture2D> sphere_off; - Ref<Texture2D> box_on; - Ref<Texture2D> box_off; + Ref<Texture2D> light_1_icon; + Ref<Texture2D> light_2_icon; + Ref<Texture2D> sphere_icon; + Ref<Texture2D> box_icon; Ref<Texture2D> checkerboard; } theme_cache; - void _button_pressed(Node *p_button); + void _on_light_1_switch_pressed(); + void _on_light_2_switch_pressed(); + void _on_sphere_switch_pressed(); + void _on_box_switch_pressed(); protected: virtual void _update_theme_item_cache() override; diff --git a/editor/plugins/mesh_editor_plugin.cpp b/editor/plugins/mesh_editor_plugin.cpp index ee555ae8d4..b6be971370 100644 --- a/editor/plugins/mesh_editor_plugin.cpp +++ b/editor/plugins/mesh_editor_plugin.cpp @@ -32,7 +32,7 @@ #include "core/config/project_settings.h" #include "editor/editor_scale.h" -#include "scene/gui/texture_button.h" +#include "scene/gui/button.h" #include "scene/main/viewport.h" void MeshEditor::gui_input(const Ref<InputEvent> &p_event) { @@ -42,11 +42,8 @@ void MeshEditor::gui_input(const Ref<InputEvent> &p_event) { if (mm.is_valid() && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) { rot_x -= mm->get_relative().y * 0.01; rot_y -= mm->get_relative().x * 0.01; - if (rot_x < -Math_PI / 2) { - rot_x = -Math_PI / 2; - } else if (rot_x > Math_PI / 2) { - rot_x = Math_PI / 2; - } + + rot_x = CLAMP(rot_x, -Math_PI / 2, Math_PI / 2); _update_rotation(); } } @@ -54,19 +51,15 @@ void MeshEditor::gui_input(const Ref<InputEvent> &p_event) { void MeshEditor::_update_theme_item_cache() { SubViewportContainer::_update_theme_item_cache(); - theme_cache.light_1_on = get_theme_icon(SNAME("MaterialPreviewLight1"), SNAME("EditorIcons")); - theme_cache.light_1_off = get_theme_icon(SNAME("MaterialPreviewLight1Off"), SNAME("EditorIcons")); - theme_cache.light_2_on = get_theme_icon(SNAME("MaterialPreviewLight2"), SNAME("EditorIcons")); - theme_cache.light_2_off = get_theme_icon(SNAME("MaterialPreviewLight2Off"), SNAME("EditorIcons")); + theme_cache.light_1_icon = get_theme_icon(SNAME("MaterialPreviewLight1"), SNAME("EditorIcons")); + theme_cache.light_2_icon = get_theme_icon(SNAME("MaterialPreviewLight2"), SNAME("EditorIcons")); } void MeshEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { - light_1_switch->set_texture_normal(theme_cache.light_1_on); - light_1_switch->set_texture_pressed(theme_cache.light_1_off); - light_2_switch->set_texture_normal(theme_cache.light_2_on); - light_2_switch->set_texture_pressed(theme_cache.light_2_off); + light_1_switch->set_icon(theme_cache.light_1_icon); + light_2_switch->set_icon(theme_cache.light_2_icon); } break; } } @@ -100,21 +93,19 @@ void MeshEditor::edit(Ref<Mesh> p_mesh) { } } -void MeshEditor::_button_pressed(Node *p_button) { - if (p_button == light_1_switch) { - light1->set_visible(!light_1_switch->is_pressed()); - } +void MeshEditor::_on_light_1_switch_pressed() { + light1->set_visible(light_1_switch->is_pressed()); +} - if (p_button == light_2_switch) { - light2->set_visible(!light_2_switch->is_pressed()); - } +void MeshEditor::_on_light_2_switch_pressed() { + light2->set_visible(light_2_switch->is_pressed()); } MeshEditor::MeshEditor() { viewport = memnew(SubViewport); Ref<World3D> world_3d; world_3d.instantiate(); - viewport->set_world_3d(world_3d); //use own world + viewport->set_world_3d(world_3d); // Use own world. add_child(viewport); viewport->set_disable_input(true); viewport->set_msaa_3d(Viewport::MSAA_4X); @@ -154,15 +145,19 @@ MeshEditor::MeshEditor() { VBoxContainer *vb_light = memnew(VBoxContainer); hb->add_child(vb_light); - light_1_switch = memnew(TextureButton); + light_1_switch = memnew(Button); + light_1_switch->set_theme_type_variation("PreviewLightButton"); light_1_switch->set_toggle_mode(true); + light_1_switch->set_pressed(true); vb_light->add_child(light_1_switch); - light_1_switch->connect("pressed", callable_mp(this, &MeshEditor::_button_pressed).bind(light_1_switch)); + light_1_switch->connect("pressed", callable_mp(this, &MeshEditor::_on_light_1_switch_pressed)); - light_2_switch = memnew(TextureButton); + light_2_switch = memnew(Button); + light_2_switch->set_theme_type_variation("PreviewLightButton"); light_2_switch->set_toggle_mode(true); + light_2_switch->set_pressed(true); vb_light->add_child(light_2_switch); - light_2_switch->connect("pressed", callable_mp(this, &MeshEditor::_button_pressed).bind(light_2_switch)); + light_2_switch->connect("pressed", callable_mp(this, &MeshEditor::_on_light_2_switch_pressed)); rot_x = 0; rot_y = 0; diff --git a/editor/plugins/mesh_editor_plugin.h b/editor/plugins/mesh_editor_plugin.h index 335244ffd2..a8ef476f84 100644 --- a/editor/plugins/mesh_editor_plugin.h +++ b/editor/plugins/mesh_editor_plugin.h @@ -41,7 +41,7 @@ #include "scene/resources/material.h" class SubViewport; -class TextureButton; +class Button; class MeshEditor : public SubViewportContainer { GDCLASS(MeshEditor, SubViewportContainer); @@ -59,17 +59,16 @@ class MeshEditor : public SubViewportContainer { Ref<Mesh> mesh; - TextureButton *light_1_switch = nullptr; - TextureButton *light_2_switch = nullptr; + Button *light_1_switch = nullptr; + Button *light_2_switch = nullptr; struct ThemeCache { - Ref<Texture2D> light_1_on; - Ref<Texture2D> light_1_off; - Ref<Texture2D> light_2_on; - Ref<Texture2D> light_2_off; + Ref<Texture2D> light_1_icon; + Ref<Texture2D> light_2_icon; } theme_cache; - void _button_pressed(Node *p_button); + void _on_light_1_switch_pressed(); + void _on_light_2_switch_pressed(); void _update_rotation(); protected: diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp index 0da6fd3c41..2a91d9f108 100644 --- a/editor/plugins/node_3d_editor_gizmos.cpp +++ b/editor/plugins/node_3d_editor_gizmos.cpp @@ -115,6 +115,15 @@ Variant EditorNode3DGizmo::get_handle_value(int p_id, bool p_secondary) const { return gizmo_plugin->get_handle_value(this, p_id, p_secondary); } +void EditorNode3DGizmo::begin_handle_action(int p_id, bool p_secondary) { + if (GDVIRTUAL_CALL(_begin_handle_action, p_id, p_secondary)) { + return; + } + + ERR_FAIL_COND(!gizmo_plugin); + gizmo_plugin->begin_handle_action(this, p_id, p_secondary); +} + void EditorNode3DGizmo::set_handle(int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) { if (GDVIRTUAL_CALL(_set_handle, p_id, p_secondary, p_camera, p_point)) { return; @@ -1095,6 +1104,10 @@ Variant EditorNode3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_giz return ret; } +void EditorNode3DGizmoPlugin::begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) { + GDVIRTUAL_CALL(_begin_handle_action, Ref<EditorNode3DGizmo>(p_gizmo), p_id, p_secondary); +} + void EditorNode3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) { GDVIRTUAL_CALL(_set_handle, Ref<EditorNode3DGizmo>(p_gizmo), p_id, p_secondary, p_camera, p_point); } diff --git a/editor/plugins/node_3d_editor_gizmos.h b/editor/plugins/node_3d_editor_gizmos.h index ae2214de98..d7c368d5d0 100644 --- a/editor/plugins/node_3d_editor_gizmos.h +++ b/editor/plugins/node_3d_editor_gizmos.h @@ -83,6 +83,7 @@ protected: GDVIRTUAL2RC(String, _get_handle_name, int, bool) GDVIRTUAL2RC(bool, _is_handle_highlighted, int, bool) GDVIRTUAL2RC(Variant, _get_handle_value, int, bool) + GDVIRTUAL2(_begin_handle_action, int, bool) GDVIRTUAL4(_set_handle, int, bool, const Camera3D *, Vector2) GDVIRTUAL4(_commit_handle, int, bool, Variant, bool) @@ -104,6 +105,7 @@ public: virtual bool is_handle_highlighted(int p_id, bool p_secondary) const; virtual String get_handle_name(int p_id, bool p_secondary) const; virtual Variant get_handle_value(int p_id, bool p_secondary) const; + virtual void begin_handle_action(int p_id, bool p_secondary); virtual void set_handle(int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point); virtual void commit_handle(int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel = false); @@ -170,6 +172,7 @@ protected: GDVIRTUAL3RC(bool, _is_handle_highlighted, Ref<EditorNode3DGizmo>, int, bool) GDVIRTUAL3RC(Variant, _get_handle_value, Ref<EditorNode3DGizmo>, int, bool) + GDVIRTUAL3(_begin_handle_action, Ref<EditorNode3DGizmo>, int, bool) GDVIRTUAL5(_set_handle, Ref<EditorNode3DGizmo>, int, bool, const Camera3D *, Vector2) GDVIRTUAL5(_commit_handle, Ref<EditorNode3DGizmo>, int, bool, Variant, bool) @@ -196,6 +199,7 @@ public: virtual bool is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const; virtual String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const; virtual Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const; + virtual void begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary); virtual void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point); virtual void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel = false); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 68d3661d10..65563bd1a3 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -1675,6 +1675,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { if (_edit.mode != TRANSFORM_NONE && b->is_pressed()) { cancel_transform(); + break; } if (b->is_pressed()) { @@ -1775,6 +1776,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { seg->handles_intersect_ray(camera, _edit.mouse_pos, b->is_shift_pressed(), gizmo_handle, gizmo_secondary); if (gizmo_handle != -1) { _edit.gizmo = seg; + seg->begin_handle_action(gizmo_handle, gizmo_secondary); _edit.gizmo_handle = gizmo_handle; _edit.gizmo_handle_secondary = gizmo_secondary; _edit.gizmo_initial_value = seg->get_handle_value(gizmo_handle, gizmo_secondary); @@ -2006,7 +2008,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { _edit.mode = TRANSFORM_TRANSLATE; } - if (_edit.mode == TRANSFORM_NONE) { + if (_edit.mode == TRANSFORM_NONE || _edit.numeric_input != 0 || _edit.numeric_next_decimal != 0) { return; } @@ -2144,6 +2146,43 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { return; } + if (_edit.instant) { + // In a Blender-style transform, numbers set the magnitude of the transform. + // E.g. pressing g4.5x means "translate 4.5 units along the X axis". + // Use the Unicode value because we care about the text, not the actual keycode. + // This ensures numbers work consistently across different keyboard language layouts. + bool processed = true; + Key key = k->get_physical_keycode(); + char32_t unicode = k->get_unicode(); + if (unicode >= '0' && unicode <= '9') { + uint32_t value = uint32_t(unicode - Key::KEY_0); + if (_edit.numeric_next_decimal < 0) { + _edit.numeric_input = _edit.numeric_input + value * Math::pow(10.0, _edit.numeric_next_decimal--); + } else { + _edit.numeric_input = _edit.numeric_input * 10 + value; + } + update_transform_numeric(); + } else if (unicode == '-') { + _edit.numeric_negate = !_edit.numeric_negate; + update_transform_numeric(); + } else if (unicode == '.') { + if (_edit.numeric_next_decimal == 0) { + _edit.numeric_next_decimal = -1; + } + } else if (key == Key::ENTER || key == Key::KP_ENTER || key == Key::SPACE) { + commit_transform(); + } else { + processed = false; + } + + if (processed) { + // Ignore mouse inputs once we receive a numeric input. + set_process_input(false); + accept_event(); + return; + } + } + if (EDITOR_GET("editors/3d/navigation/emulate_numpad")) { const Key code = k->get_physical_keycode(); if (code >= Key::KEY_0 && code <= Key::KEY_9) { @@ -2152,7 +2191,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { } if (_edit.mode == TRANSFORM_NONE) { - if (_edit.gizmo.is_valid()) { + if (_edit.gizmo.is_valid() && (k->get_keycode() == Key::ESCAPE || k->get_keycode() == Key::BACKSPACE)) { // Restore. _edit.gizmo->commit_handle(_edit.gizmo_handle, _edit.gizmo_handle_secondary, _edit.gizmo_initial_value, true); _edit.gizmo = Ref<EditorNode3DGizmo>(); @@ -2164,26 +2203,19 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { } else { // We're actively transforming, handle keys specially TransformPlane new_plane = TRANSFORM_VIEW; - String new_message; if (ED_IS_SHORTCUT("spatial_editor/lock_transform_x", p_event)) { new_plane = TRANSFORM_X_AXIS; - new_message = TTR("X-Axis Transform."); } else if (ED_IS_SHORTCUT("spatial_editor/lock_transform_y", p_event)) { new_plane = TRANSFORM_Y_AXIS; - new_message = TTR("Y-Axis Transform."); } else if (ED_IS_SHORTCUT("spatial_editor/lock_transform_z", p_event)) { new_plane = TRANSFORM_Z_AXIS; - new_message = TTR("Z-Axis Transform."); } else if (_edit.mode != TRANSFORM_ROTATE) { // rotating on a plane doesn't make sense if (ED_IS_SHORTCUT("spatial_editor/lock_transform_yz", p_event)) { new_plane = TRANSFORM_YZ; - new_message = TTR("YZ-Plane Transform."); } else if (ED_IS_SHORTCUT("spatial_editor/lock_transform_xz", p_event)) { new_plane = TRANSFORM_XZ; - new_message = TTR("XZ-Plane Transform."); } else if (ED_IS_SHORTCUT("spatial_editor/lock_transform_xy", p_event)) { new_plane = TRANSFORM_XY; - new_message = TTR("XY-Plane Transform."); } } @@ -2200,8 +2232,11 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { _edit.plane = TRANSFORM_VIEW; spatial_editor->set_local_coords_enabled(false); } - update_transform(Input::get_singleton()->is_key_pressed(Key::SHIFT)); - set_message(new_message, 2); + if (_edit.numeric_input != 0 || _edit.numeric_next_decimal != 0) { + update_transform_numeric(); + } else { + update_transform(Input::get_singleton()->is_key_pressed(Key::SHIFT)); + } accept_event(); return; } @@ -2658,6 +2693,9 @@ void Node3DEditorViewport::_project_settings_changed() { const bool transparent_background = GLOBAL_GET("rendering/viewport/transparent_background"); viewport->set_transparent_background(transparent_background); + const bool use_hdr_2d = GLOBAL_GET("rendering/viewport/hdr_2d"); + viewport->set_use_hdr_2d(use_hdr_2d); + const bool use_debanding = GLOBAL_GET("rendering/anti_aliasing/quality/use_debanding"); viewport->set_use_debanding(use_debanding); @@ -2821,7 +2859,7 @@ void Node3DEditorViewport::_notification(int p_what) { } if (show_info) { - const String viewport_size = vformat(String::utf8("%d × %d"), viewport->get_size().x, viewport->get_size().y); + const String viewport_size = vformat(U"%d × %d", viewport->get_size().x, viewport->get_size().y); String text; text += vformat(TTR("X: %s\n"), rtos(current_camera->get_position().x).pad_decimals(1)); text += vformat(TTR("Y: %s\n"), rtos(current_camera->get_position().y).pad_decimals(1)); @@ -4571,6 +4609,43 @@ void Node3DEditorViewport::commit_transform() { set_message(""); } +void Node3DEditorViewport::apply_transform(Vector3 p_motion, double p_snap) { + bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW); + List<Node *> &selection = editor_selection->get_selected_node_list(); + for (Node *E : selection) { + Node3D *sp = Object::cast_to<Node3D>(E); + if (!sp) { + continue; + } + + Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp); + if (!se) { + continue; + } + + if (sp->has_meta("_edit_lock_")) { + continue; + } + + if (se->gizmo.is_valid()) { + for (KeyValue<int, Transform3D> &GE : se->subgizmos) { + Transform3D xform = GE.value; + Transform3D new_xform = _compute_transform(_edit.mode, se->original * xform, xform, p_motion, p_snap, local_coords, _edit.plane != TRANSFORM_VIEW); // Force orthogonal with subgizmo. + if (!local_coords) { + new_xform = se->original.affine_inverse() * new_xform; + } + se->gizmo->set_subgizmo_transform(GE.key, new_xform); + } + } else { + Transform3D new_xform = _compute_transform(_edit.mode, se->original, se->original_local, p_motion, p_snap, local_coords, sp->get_rotation_edit_mode() != Node3D::ROTATION_EDIT_MODE_BASIS && _edit.plane != TRANSFORM_VIEW); + _transform_gizmo_apply(se->sp, new_xform, local_coords); + } + } + + spatial_editor->update_transform_gizmo(); + surface->queue_redraw(); +} + // Update the current transform operation in response to an input. void Node3DEditorViewport::update_transform(bool p_shift) { Vector3 ray_pos = _get_ray_pos(_edit.mouse_pos); @@ -4666,43 +4741,11 @@ void Node3DEditorViewport::update_transform(bool p_shift) { set_message(TTR("Scaling:") + " (" + String::num(motion_snapped.x, snap_step_decimals) + ", " + String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")"); if (local_coords) { + // TODO: needed? motion = _edit.original.basis.inverse().xform(motion); } - List<Node *> &selection = editor_selection->get_selected_node_list(); - for (Node *E : selection) { - Node3D *sp = Object::cast_to<Node3D>(E); - if (!sp) { - continue; - } - - Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp); - if (!se) { - continue; - } - - if (sp->has_meta("_edit_lock_")) { - continue; - } - - if (se->gizmo.is_valid()) { - for (KeyValue<int, Transform3D> &GE : se->subgizmos) { - Transform3D xform = GE.value; - Transform3D new_xform = _compute_transform(TRANSFORM_SCALE, se->original * xform, xform, motion, snap, local_coords, _edit.plane != TRANSFORM_VIEW); // Force orthogonal with subgizmo. - if (!local_coords) { - new_xform = se->original.affine_inverse() * new_xform; - } - se->gizmo->set_subgizmo_transform(GE.key, new_xform); - } - } else { - Transform3D new_xform = _compute_transform(TRANSFORM_SCALE, se->original, se->original_local, motion, snap, local_coords, sp->get_rotation_edit_mode() != Node3D::ROTATION_EDIT_MODE_BASIS && _edit.plane != TRANSFORM_VIEW); - _transform_gizmo_apply(se->sp, new_xform, local_coords); - } - } - - spatial_editor->update_transform_gizmo(); - surface->queue_redraw(); - + apply_transform(motion, snap); } break; case TRANSFORM_TRANSLATE: { @@ -4772,38 +4815,7 @@ void Node3DEditorViewport::update_transform(bool p_shift) { motion = spatial_editor->get_gizmo_transform().basis.inverse().xform(motion); } - List<Node *> &selection = editor_selection->get_selected_node_list(); - for (Node *E : selection) { - Node3D *sp = Object::cast_to<Node3D>(E); - if (!sp) { - continue; - } - - Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp); - if (!se) { - continue; - } - - if (sp->has_meta("_edit_lock_")) { - continue; - } - - if (se->gizmo.is_valid()) { - for (KeyValue<int, Transform3D> &GE : se->subgizmos) { - Transform3D xform = GE.value; - Transform3D new_xform = _compute_transform(TRANSFORM_TRANSLATE, se->original * xform, xform, motion, snap, local_coords, true); // Force orthogonal with subgizmo. - new_xform = se->original.affine_inverse() * new_xform; - se->gizmo->set_subgizmo_transform(GE.key, new_xform); - } - } else { - Transform3D new_xform = _compute_transform(TRANSFORM_TRANSLATE, se->original, se->original_local, motion, snap, local_coords, sp->get_rotation_edit_mode() != Node3D::ROTATION_EDIT_MODE_BASIS); - _transform_gizmo_apply(se->sp, new_xform, false); - } - } - - spatial_editor->update_transform_gizmo(); - surface->queue_redraw(); - + apply_transform(motion, snap); } break; case TRANSFORM_ROTATE: { @@ -4872,53 +4884,85 @@ void Node3DEditorViewport::update_transform(bool p_shift) { bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW); // Disable local transformation for TRANSFORM_VIEW - List<Node *> &selection = editor_selection->get_selected_node_list(); - for (Node *E : selection) { - Node3D *sp = Object::cast_to<Node3D>(E); - if (!sp) { - continue; - } - - Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp); - if (!se) { - continue; - } - - if (sp->has_meta("_edit_lock_")) { - continue; - } - - Vector3 compute_axis = local_coords ? local_axis : global_axis; - if (se->gizmo.is_valid()) { - for (KeyValue<int, Transform3D> &GE : se->subgizmos) { - Transform3D xform = GE.value; - - Transform3D new_xform = _compute_transform(TRANSFORM_ROTATE, se->original * xform, xform, compute_axis, angle, local_coords, true); // Force orthogonal with subgizmo. - if (!local_coords) { - new_xform = se->original.affine_inverse() * new_xform; - } - se->gizmo->set_subgizmo_transform(GE.key, new_xform); - } - } else { - Transform3D new_xform = _compute_transform(TRANSFORM_ROTATE, se->original, se->original_local, compute_axis, angle, local_coords, sp->get_rotation_edit_mode() != Node3D::ROTATION_EDIT_MODE_BASIS); - _transform_gizmo_apply(se->sp, new_xform, local_coords); - } - } - - spatial_editor->update_transform_gizmo(); - surface->queue_redraw(); - + Vector3 compute_axis = local_coords ? local_axis : global_axis; + apply_transform(compute_axis, angle); } break; default: { } } } +void Node3DEditorViewport::update_transform_numeric() { + Vector3 motion; + switch (_edit.plane) { + case TRANSFORM_VIEW: { + switch (_edit.mode) { + case TRANSFORM_TRANSLATE: + motion = Vector3(1, 0, 0); + break; + case TRANSFORM_ROTATE: + motion = spatial_editor->get_gizmo_transform().basis.xform_inv(_get_camera_normal()).normalized(); + break; + case TRANSFORM_SCALE: + motion = Vector3(1, 1, 1); + break; + case TRANSFORM_NONE: + ERR_FAIL_MSG("_edit.mode cannot be TRANSFORM_NONE in update_transform_numeric."); + } + break; + } + case TRANSFORM_X_AXIS: + motion = Vector3(1, 0, 0); + break; + case TRANSFORM_Y_AXIS: + motion = Vector3(0, 1, 0); + break; + case TRANSFORM_Z_AXIS: + motion = Vector3(0, 0, 1); + break; + case TRANSFORM_XY: + motion = Vector3(1, 1, 0); + break; + case TRANSFORM_XZ: + motion = Vector3(1, 0, 1); + break; + case TRANSFORM_YZ: + motion = Vector3(0, 1, 1); + break; + } + + double value = _edit.numeric_input * (_edit.numeric_negate ? -1 : 1); + double extra = 0.0; + switch (_edit.mode) { + case TRANSFORM_TRANSLATE: + motion *= value; + set_message(vformat(TTR("Translating %s."), motion)); + break; + case TRANSFORM_ROTATE: + extra = Math::deg_to_rad(value); + set_message(vformat(TTR("Rotating %f degrees."), value)); + break; + case TRANSFORM_SCALE: + // To halve the size of an object in Blender, you scale it by 0.5. + // Doing the same in Godot is considered scaling it by -0.5. + motion *= (value - 1.0); + set_message(vformat(TTR("Scaling %s."), motion)); + break; + case TRANSFORM_NONE: + ERR_FAIL_MSG("_edit.mode cannot be TRANSFORM_NONE in update_transform_numeric."); + } + + apply_transform(motion, extra); +} + // Perform cleanup after a transform operation is committed or cancelled. void Node3DEditorViewport::finish_transform() { - spatial_editor->set_local_coords_enabled(_edit.original_local); _edit.mode = TRANSFORM_NONE; _edit.instant = false; + _edit.numeric_input = 0; + _edit.numeric_next_decimal = 0; + _edit.numeric_negate = false; + spatial_editor->set_local_coords_enabled(_edit.original_local); spatial_editor->update_transform_gizmo(); surface->queue_redraw(); set_process_input(false); @@ -8125,7 +8169,7 @@ Node3DEditor::Node3DEditor() { String sct; - // Add some margin to the left for better aesthetics. + // Add some margin to the left for better esthetics. // This prevents the first button's hover/pressed effect from "touching" the panel's border, // which looks ugly. Control *margin_left = memnew(Control); diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index 79674bdd64..e58e224ff4 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -349,6 +349,15 @@ private: Variant gizmo_initial_value; bool original_local; bool instant; + + // Numeric blender-style transforms (e.g. 'g5x'). + // numeric_input tracks the current input value, e.g. 1.23. + // numeric_negate indicates whether '-' has been pressed to negate the value + // while numeric_next_decimal is 0, numbers are input before the decimal point + // after pressing '.', numeric next decimal changes to -1, and decrements after each press. + double numeric_input = 0.0; + bool numeric_negate = false; + int numeric_next_decimal = 0; } _edit; struct Cursor { @@ -445,7 +454,9 @@ private: void begin_transform(TransformMode p_mode, bool instant); void commit_transform(); + void apply_transform(Vector3 p_motion, double p_snap); void update_transform(bool p_shift); + void update_transform_numeric(); void finish_transform(); void register_shortcut_action(const String &p_path, const String &p_name, Key p_keycode, bool p_physical = false); diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index 4e5a3eb095..0a3de70e2a 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -52,10 +52,8 @@ #include "scene/scene_string_names.h" void BoneTransformEditor::create_editors() { - const Color section_color = get_theme_color(SNAME("prop_subsection"), SNAME("Editor")); - section = memnew(EditorInspectorSection); - section->setup("trf_properties", label, this, section_color, true); + section->setup("trf_properties", label, this, Color(0.0f, 0.0f, 0.0f), true); section->unfold(); add_child(section); @@ -94,7 +92,7 @@ void BoneTransformEditor::create_editors() { // Transform/Matrix section. rest_section = memnew(EditorInspectorSection); - rest_section->setup("trf_properties_transform", "Rest", this, section_color, true); + rest_section->setup("trf_properties_transform", "Rest", this, Color(0.0f, 0.0f, 0.0f), true); section->get_vbox()->add_child(rest_section); // Transform/Matrix property. @@ -107,8 +105,10 @@ void BoneTransformEditor::create_editors() { void BoneTransformEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - create_editors(); + case NOTIFICATION_THEME_CHANGED: { + const Color section_color = get_theme_color(SNAME("prop_subsection"), SNAME("Editor")); + section->set_bg_color(section_color); + rest_section->set_bg_color(section_color); } break; } } @@ -128,6 +128,7 @@ void BoneTransformEditor::_value_changed(const String &p_property, Variant p_val BoneTransformEditor::BoneTransformEditor(Skeleton3D *p_skeleton) : skeleton(p_skeleton) { + create_editors(); } void BoneTransformEditor::set_keyable(const bool p_keyable) { diff --git a/editor/plugins/sprite_2d_editor_plugin.cpp b/editor/plugins/sprite_2d_editor_plugin.cpp index 0d00bfa8fd..f647bded95 100644 --- a/editor/plugins/sprite_2d_editor_plugin.cpp +++ b/editor/plugins/sprite_2d_editor_plugin.cpp @@ -346,11 +346,8 @@ void Sprite2DEditor::_convert_to_mesh_2d_node() { EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); ur->create_action(TTR("Convert to MeshInstance2D")); - ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, mesh_instance, true, false); - ur->add_do_reference(mesh_instance); - ur->add_undo_method(SceneTreeDock::get_singleton(), "replace_node", mesh_instance, node, false, false); - ur->add_undo_reference(node); - ur->commit_action(); + SceneTreeDock::get_singleton()->replace_node(node, mesh_instance); + ur->commit_action(false); } void Sprite2DEditor::_convert_to_polygon_2d_node() { @@ -404,11 +401,8 @@ void Sprite2DEditor::_convert_to_polygon_2d_node() { EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); ur->create_action(TTR("Convert to Polygon2D")); - ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, polygon_2d_instance, true, false); - ur->add_do_reference(polygon_2d_instance); - ur->add_undo_method(SceneTreeDock::get_singleton(), "replace_node", polygon_2d_instance, node, false, false); - ur->add_undo_reference(node); - ur->commit_action(); + SceneTreeDock::get_singleton()->replace_node(node, polygon_2d_instance); + ur->commit_action(false); } void Sprite2DEditor::_create_collision_polygon_2d_node() { diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index 7b35351457..720cfb5928 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -1282,7 +1282,7 @@ void SpriteFramesEditor::_update_library(bool p_skip_selector) { // Frame is often saved as an AtlasTexture subresource within a scene/resource file, // thus its path might be not what the user is looking for. So we're also showing // subsequent source texture paths. - String prefix = String::utf8("┖╴"); + String prefix = U"┖╴"; Ref<AtlasTexture> at = texture; while (at.is_valid() && at->get_atlas().is_valid()) { tooltip += "\n" + prefix + at->get_atlas()->get_path(); diff --git a/editor/plugins/style_box_editor_plugin.cpp b/editor/plugins/style_box_editor_plugin.cpp index 1d14f5e60b..9364b96c90 100644 --- a/editor/plugins/style_box_editor_plugin.cpp +++ b/editor/plugins/style_box_editor_plugin.cpp @@ -110,14 +110,11 @@ StyleBoxPreview::StyleBoxPreview() { set_anchors_and_offsets_preset(PRESET_FULL_RECT); grid_preview = memnew(Button); + // This theme variation works better than the normal theme because there's no focus highlight. + grid_preview->set_theme_type_variation("PreviewLightButton"); grid_preview->set_toggle_mode(true); grid_preview->connect("toggled", callable_mp(this, &StyleBoxPreview::_grid_preview_toggled)); grid_preview->set_pressed(grid_preview_enabled); - grid_preview->set_flat(true); - grid_preview->add_theme_style_override("normal", memnew(StyleBoxEmpty)); - grid_preview->add_theme_style_override("hover", memnew(StyleBoxEmpty)); - grid_preview->add_theme_style_override("focus", memnew(StyleBoxEmpty)); - grid_preview->add_theme_style_override("pressed", memnew(StyleBoxEmpty)); add_child(grid_preview); } diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index c98d9086d1..b1111be006 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -2655,6 +2655,12 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { TileSetAtlasSourceEditor::~TileSetAtlasSourceEditor() { memdelete(tile_proxy_object); memdelete(atlas_source_proxy_object); + + // Remove listener for old objects, so the TileSet doesn't + // try to call the destroyed TileSetAtlasSourceEditor. + if (tile_set.is_valid()) { + tile_set->disconnect_changed(callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_changed)); + } } ////// EditorPropertyTilePolygon ////// diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp index 911316822a..e432704702 100644 --- a/editor/plugins/tiles/tiles_editor_plugin.cpp +++ b/editor/plugins/tiles/tiles_editor_plugin.cpp @@ -38,7 +38,6 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" -#include "editor/inspector_dock.h" #include "editor/plugins/canvas_item_editor_plugin.h" #include "scene/2d/tile_map.h" @@ -326,8 +325,18 @@ void TileMapEditorPlugin::_tile_map_changed() { } void TileMapEditorPlugin::_update_tile_map() { - if (tile_map && tile_map->get_tileset().is_valid()) { - EditorNode::get_singleton()->edit_item(tile_map->get_tileset().ptr(), InspectorDock::get_inspector_singleton()); + TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); + if (tile_map) { + Ref<TileSet> tile_set = tile_map->get_tileset(); + if (tile_set.is_valid() && edited_tileset != tile_set->get_instance_id()) { + tile_set_plugin_singleton->edit(tile_map->get_tileset().ptr()); + tile_set_plugin_singleton->make_visible(true); + edited_tileset = tile_set->get_instance_id(); + } else if (tile_set.is_null()) { + tile_set_plugin_singleton->edit(nullptr); + tile_set_plugin_singleton->make_visible(false); + edited_tileset = ObjectID(); + } } tile_map_changed_needs_update = false; } @@ -339,18 +348,26 @@ void TileMapEditorPlugin::_notification(int p_notification) { } void TileMapEditorPlugin::edit(Object *p_object) { + TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); if (tile_map) { tile_map->disconnect("changed", callable_mp(this, &TileMapEditorPlugin::_tile_map_changed)); } tile_map = Object::cast_to<TileMap>(p_object); + if (tile_map) { + tile_map_id = tile_map->get_instance_id(); + } else { + tile_map_id = ObjectID(); + } editor->edit(tile_map); if (tile_map) { tile_map->connect("changed", callable_mp(this, &TileMapEditorPlugin::_tile_map_changed)); if (tile_map->get_tileset().is_valid()) { - EditorNode::get_singleton()->edit_item(tile_map->get_tileset().ptr(), InspectorDock::get_inspector_singleton()); + tile_set_plugin_singleton->edit(tile_map->get_tileset().ptr()); + tile_set_plugin_singleton->make_visible(true); + edited_tileset = tile_map->get_tileset()->get_instance_id(); } } } diff --git a/editor/plugins/tiles/tiles_editor_plugin.h b/editor/plugins/tiles/tiles_editor_plugin.h index 0bb45b746d..81cb48eb00 100644 --- a/editor/plugins/tiles/tiles_editor_plugin.h +++ b/editor/plugins/tiles/tiles_editor_plugin.h @@ -115,9 +115,11 @@ class TileMapEditorPlugin : public EditorPlugin { TileMapEditor *editor = nullptr; Button *button = nullptr; - TileMap *tile_map = nullptr; + ObjectID tile_map_id; bool tile_map_changed_needs_update = false; + ObjectID edited_tileset; + void _tile_map_changed(); void _update_tile_map(); diff --git a/editor/plugins/voxel_gi_editor_plugin.cpp b/editor/plugins/voxel_gi_editor_plugin.cpp index 43b133f4b5..af4a027f78 100644 --- a/editor/plugins/voxel_gi_editor_plugin.cpp +++ b/editor/plugins/voxel_gi_editor_plugin.cpp @@ -119,8 +119,8 @@ void VoxelGIEditorPlugin::_notification(int p_what) { } String text; - text += vformat(TTR("Subdivisions: %s"), vformat(String::utf8("%d × %d × %d"), cell_size.x, cell_size.y, cell_size.z)) + "\n"; - text += vformat(TTR("Cell size: %s"), vformat(String::utf8("%.3f × %.3f × %.3f"), half_size.x / cell_size.x, half_size.y / cell_size.y, half_size.z / cell_size.z)) + "\n"; + text += vformat(TTR("Subdivisions: %s"), vformat(U"%d × %d × %d", cell_size.x, cell_size.y, cell_size.z)) + "\n"; + text += vformat(TTR("Cell size: %s"), vformat(U"%.3f × %.3f × %.3f", half_size.x / cell_size.x, half_size.y / cell_size.y, half_size.z / cell_size.z)) + "\n"; text += vformat(TTR("Video RAM size: %s MB (%s)"), String::num(size_mb, 2), size_quality); // Only update the tooltip when needed to avoid constant redrawing. diff --git a/editor/register_editor_types.cpp b/editor/register_editor_types.cpp index fbb724906b..849a6cc097 100644 --- a/editor/register_editor_types.cpp +++ b/editor/register_editor_types.cpp @@ -275,6 +275,7 @@ void register_editor_types() { GLOBAL_DEF("editor/version_control/autoload_on_startup", false); EditorInterface::create(); + Engine::get_singleton()->add_singleton(Engine::Singleton("EditorInterface", EditorInterface::get_singleton())); OS::get_singleton()->benchmark_end_measure("register_editor_types"); } diff --git a/editor/scene_create_dialog.cpp b/editor/scene_create_dialog.cpp index 986f6bb87a..8f56267123 100644 --- a/editor/scene_create_dialog.cpp +++ b/editor/scene_create_dialog.cpp @@ -34,6 +34,7 @@ #include "editor/create_dialog.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/gui/editor_validation_panel.h" #include "scene/2d/node_2d.h" #include "scene/3d/node_3d.h" #include "scene/gui/box_container.h" @@ -41,7 +42,6 @@ #include "scene/gui/grid_container.h" #include "scene/gui/line_edit.h" #include "scene/gui/option_button.h" -#include "scene/gui/panel_container.h" #include "scene/resources/packed_scene.h" void SceneCreateDialog::_notification(int p_what) { @@ -53,7 +53,6 @@ void SceneCreateDialog::_notification(int p_what) { node_type_3d->set_icon(get_theme_icon(SNAME("Node3D"), SNAME("EditorIcons"))); node_type_gui->set_icon(get_theme_icon(SNAME("Control"), SNAME("EditorIcons"))); node_type_other->add_theme_icon_override(SNAME("icon"), get_theme_icon(SNAME("Node"), SNAME("EditorIcons"))); - status_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree"))); } break; } } @@ -63,7 +62,7 @@ void SceneCreateDialog::config(const String &p_dir) { root_name_edit->set_text(""); scene_name_edit->set_text(""); scene_name_edit->call_deferred(SNAME("grab_focus")); - update_dialog(); + validation_panel->update(); } void SceneCreateDialog::accept_create() { @@ -82,40 +81,35 @@ void SceneCreateDialog::browse_types() { void SceneCreateDialog::on_type_picked() { other_type_display->set_text(select_node_dialog->get_selected_type().get_slice(" ", 0)); if (node_type_other->is_pressed()) { - update_dialog(); + validation_panel->update(); } else { - node_type_other->set_pressed(true); // Calls update_dialog() via group. + node_type_other->set_pressed(true); // Calls validation_panel->update() via group. } } void SceneCreateDialog::update_dialog() { scene_name = scene_name_edit->get_text().strip_edges(); - update_error(file_error_label, MSG_OK, TTR("Scene name is valid.")); - bool is_valid = true; if (scene_name.is_empty()) { - update_error(file_error_label, MSG_ERROR, TTR("Scene name is empty.")); - is_valid = false; + validation_panel->set_message(MSG_ID_PATH, TTR("Scene name is empty."), EditorValidationPanel::MSG_ERROR); } - if (is_valid) { + if (validation_panel->is_valid()) { if (!scene_name.ends_with(".")) { scene_name += "."; } scene_name += scene_extension_picker->get_selected_metadata().operator String(); } - if (is_valid && !scene_name.is_valid_filename()) { - update_error(file_error_label, MSG_ERROR, TTR("File name invalid.")); - is_valid = false; + if (validation_panel->is_valid() && !scene_name.is_valid_filename()) { + validation_panel->set_message(MSG_ID_PATH, TTR("File name invalid."), EditorValidationPanel::MSG_ERROR); } - if (is_valid) { + if (validation_panel->is_valid()) { scene_name = directory.path_join(scene_name); Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); if (da->file_exists(scene_name)) { - update_error(file_error_label, MSG_ERROR, TTR("File already exists.")); - is_valid = false; + validation_panel->set_message(MSG_ID_PATH, TTR("File already exists."), EditorValidationPanel::MSG_ERROR); } } @@ -126,8 +120,6 @@ void SceneCreateDialog::update_dialog() { node_type_other->set_icon(nullptr); } - update_error(node_error_label, MSG_OK, TTR("Root node valid.")); - root_name = root_name_edit->get_text().strip_edges(); if (root_name.is_empty()) { root_name = scene_name_edit->get_text().strip_edges(); @@ -135,39 +127,16 @@ void SceneCreateDialog::update_dialog() { if (root_name.is_empty()) { root_name_edit->set_placeholder(TTR("Leave empty to derive from scene name")); } else { - // Respect the desired root node casing from ProjectSettings and ensure it's a valid node name. - String adjusted_root_name = Node::adjust_name_casing(root_name); - root_name = adjusted_root_name.validate_node_name(); - - bool has_invalid_characters = root_name != adjusted_root_name; - if (has_invalid_characters) { - update_error(node_error_label, MSG_WARNING, TTR("Invalid root node name characters have been replaced.")); - } - - root_name_edit->set_placeholder(root_name); + // Respect the desired root node casing from ProjectSettings. + root_name = Node::adjust_name_casing(root_name); + root_name_edit->set_placeholder(root_name.validate_node_name()); } } - if (root_name.is_empty() || root_name.validate_node_name() != root_name) { - update_error(node_error_label, MSG_ERROR, TTR("Invalid root node name.")); - is_valid = false; - } - - get_ok_button()->set_disabled(!is_valid); -} - -void SceneCreateDialog::update_error(Label *p_label, MsgType p_type, const String &p_msg) { - p_label->set_text(String::utf8("• ") + p_msg); - switch (p_type) { - case MSG_OK: - p_label->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor"))); - break; - case MSG_ERROR: - p_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor"))); - break; - case MSG_WARNING: - p_label->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor"))); - break; + if (root_name.is_empty()) { + validation_panel->set_message(MSG_ID_ROOT, TTR("Invalid root node name."), EditorValidationPanel::MSG_ERROR); + } else if (root_name != root_name.validate_node_name()) { + validation_panel->set_message(MSG_ID_ROOT, TTR("Invalid root node name characters have been replaced."), EditorValidationPanel::MSG_WARNING); } } @@ -268,8 +237,6 @@ SceneCreateDialog::SceneCreateDialog() { select_node_button = memnew(Button); hb->add_child(select_node_button); select_node_button->connect("pressed", callable_mp(this, &SceneCreateDialog::browse_types)); - - node_type_group->connect("pressed", callable_mp(this, &SceneCreateDialog::update_dialog).unbind(1)); } { @@ -282,7 +249,6 @@ SceneCreateDialog::SceneCreateDialog() { scene_name_edit = memnew(LineEdit); hb->add_child(scene_name_edit); scene_name_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); - scene_name_edit->connect("text_changed", callable_mp(this, &SceneCreateDialog::update_dialog).unbind(1)); scene_name_edit->connect("text_submitted", callable_mp(this, &SceneCreateDialog::accept_create).unbind(1)); List<String> extensions; @@ -305,7 +271,6 @@ SceneCreateDialog::SceneCreateDialog() { gc->add_child(root_name_edit); root_name_edit->set_tooltip_text(TTR("When empty, the root node name is derived from the scene name based on the \"editor/naming/node_name_casing\" project setting.")); root_name_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); - root_name_edit->connect("text_changed", callable_mp(this, &SceneCreateDialog::update_dialog).unbind(1)); root_name_edit->connect("text_submitted", callable_mp(this, &SceneCreateDialog::accept_create).unbind(1)); } @@ -313,19 +278,16 @@ SceneCreateDialog::SceneCreateDialog() { main_vb->add_child(spacing); spacing->set_custom_minimum_size(Size2(0, 10 * EDSCALE)); - status_panel = memnew(PanelContainer); - main_vb->add_child(status_panel); - status_panel->set_h_size_flags(Control::SIZE_FILL); - status_panel->set_v_size_flags(Control::SIZE_EXPAND_FILL); - - VBoxContainer *status_vb = memnew(VBoxContainer); - status_panel->add_child(status_vb); - - file_error_label = memnew(Label); - status_vb->add_child(file_error_label); + validation_panel = memnew(EditorValidationPanel); + main_vb->add_child(validation_panel); + validation_panel->add_line(MSG_ID_PATH, TTR("Scene name is valid.")); + validation_panel->add_line(MSG_ID_ROOT, TTR("Root node valid.")); + validation_panel->set_update_callback(callable_mp(this, &SceneCreateDialog::update_dialog)); + validation_panel->set_accept_button(get_ok_button()); - node_error_label = memnew(Label); - status_vb->add_child(node_error_label); + node_type_group->connect("pressed", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1)); + scene_name_edit->connect("text_changed", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1)); + root_name_edit->connect("text_changed", callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1)); set_title(TTR("Create New Scene")); set_min_size(Size2i(400 * EDSCALE, 0)); diff --git a/editor/scene_create_dialog.h b/editor/scene_create_dialog.h index c6f40b928e..c7a4a09850 100644 --- a/editor/scene_create_dialog.h +++ b/editor/scene_create_dialog.h @@ -37,18 +37,17 @@ class ButtonGroup; class CheckBox; class CreateDialog; class EditorFileDialog; +class EditorValidationPanel; class Label; class LineEdit; class OptionButton; -class PanelContainer; class SceneCreateDialog : public ConfirmationDialog { GDCLASS(SceneCreateDialog, ConfirmationDialog); - enum MsgType { - MSG_OK, - MSG_ERROR, - MSG_WARNING, + enum { + MSG_ID_PATH, + MSG_ID_ROOT, }; const StringName type_meta = StringName("type"); @@ -80,15 +79,12 @@ private: OptionButton *scene_extension_picker = nullptr; LineEdit *root_name_edit = nullptr; - PanelContainer *status_panel = nullptr; - Label *file_error_label = nullptr; - Label *node_error_label = nullptr; + EditorValidationPanel *validation_panel = nullptr; void accept_create(); void browse_types(); void on_type_picked(); void update_dialog(); - void update_error(Label *p_label, MsgType p_type, const String &p_msg); protected: void _notification(int p_what); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 6adfbef06f..3096d20c19 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -121,6 +121,8 @@ void SceneTreeDock::shortcut_input(const Ref<InputEvent> &p_event) { _tool_selected(TOOL_COPY); } else if (ED_IS_SHORTCUT("scene_tree/paste_node", p_event)) { _tool_selected(TOOL_PASTE); + } else if (ED_IS_SHORTCUT("scene_tree/paste_node_as_sibling", p_event)) { + _tool_selected(TOOL_PASTE_AS_SIBLING); } else if (ED_IS_SHORTCUT("scene_tree/change_node_type", p_event)) { _tool_selected(TOOL_REPLACE); } else if (ED_IS_SHORTCUT("scene_tree/duplicate", p_event)) { @@ -262,6 +264,9 @@ void SceneTreeDock::_perform_instantiate_scenes(const Vector<String> &p_files, N } void SceneTreeDock::_replace_with_branch_scene(const String &p_file, Node *base) { + // `move_child` + `get_index` doesn't really work for internal nodes. + ERR_FAIL_COND_MSG(base->get_internal_mode() != INTERNAL_MODE_DISABLED, "Trying to replace internal node, this is not supported."); + Ref<PackedScene> sdata = ResourceLoader::load(p_file); if (!sdata.is_valid()) { accept->set_text(vformat(TTR("Error loading scene from %s"), p_file)); @@ -282,7 +287,7 @@ void SceneTreeDock::_replace_with_branch_scene(const String &p_file, Node *base) undo_redo->create_action(TTR("Replace with Branch Scene")); Node *parent = base->get_parent(); - int pos = base->get_index(); + int pos = base->get_index(false); undo_redo->add_do_method(parent, "remove_child", base); undo_redo->add_undo_method(parent, "remove_child", instantiated_scene); undo_redo->add_do_method(parent, "add_child", instantiated_scene, true); @@ -511,7 +516,10 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } } break; case TOOL_PASTE: { - paste_nodes(); + paste_nodes(false); + } break; + case TOOL_PASTE_AS_SIBLING: { + paste_nodes(true); } break; case TOOL_REPLACE: { if (!profile_allow_editing) { @@ -607,10 +615,12 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { selection.reverse(); } - int lowest_id = common_parent->get_child_count() - 1; + int lowest_id = common_parent->get_child_count(false) - 1; int highest_id = 0; for (Node *E : selection) { - int index = E->get_index(); + // `move_child` + `get_index` doesn't really work for internal nodes. + ERR_FAIL_COND_MSG(E->get_internal_mode() != INTERNAL_MODE_DISABLED, "Trying to move internal node, this is not supported."); + int index = E->get_index(false); if (index > highest_id) { highest_id = index; @@ -624,7 +634,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } } - if (!common_parent || (MOVING_DOWN && highest_id >= common_parent->get_child_count() - MOVING_DOWN) || (MOVING_UP && lowest_id == 0)) { + if (!common_parent || (MOVING_DOWN && highest_id >= common_parent->get_child_count(false) - MOVING_DOWN) || (MOVING_UP && lowest_id == 0)) { break; // one or more nodes can not be moved } @@ -643,8 +653,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { ERR_FAIL_COND(!top_node->get_parent()); ERR_FAIL_COND(!bottom_node->get_parent()); - int bottom_node_pos = bottom_node->get_index(); - int top_node_pos_next = top_node->get_index() + (MOVING_DOWN ? 1 : -1); + int bottom_node_pos = bottom_node->get_index(false); + int top_node_pos_next = top_node->get_index(false) + (MOVING_DOWN ? 1 : -1); undo_redo->add_do_method(top_node->get_parent(), "move_child", top_node, top_node_pos_next); undo_redo->add_undo_method(bottom_node->get_parent(), "move_child", bottom_node, bottom_node_pos); @@ -775,6 +785,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { return; } + // `move_child` + `get_index` doesn't really work for internal nodes. + ERR_FAIL_COND_MSG(node->get_internal_mode() != INTERNAL_MODE_DISABLED, "Trying to set internal node as scene root, this is not supported."); + //check that from node to root, all owners are right if (root->get_scene_inherited_state().is_valid()) { @@ -811,7 +824,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { undo_redo->add_undo_method(node, "remove_child", root); undo_redo->add_undo_method(EditorNode::get_singleton(), "set_edited_scene", root); undo_redo->add_undo_method(node->get_parent(), "add_child", node, true); - undo_redo->add_undo_method(node->get_parent(), "move_child", node, node->get_index()); + undo_redo->add_undo_method(node->get_parent(), "move_child", node, node->get_index(false)); undo_redo->add_undo_method(root, "set_owner", (Object *)nullptr); undo_redo->add_undo_method(node, "set_owner", root); _node_replace_owner(root, root, root, MODE_UNDO); @@ -1903,8 +1916,10 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V if (p_nodes[ni] == p_new_parent) { return; // Attempt to reparent to itself. } + // `move_child` + `get_index` doesn't really work for internal nodes. + ERR_FAIL_COND_MSG(p_nodes[ni]->get_internal_mode() != INTERNAL_MODE_DISABLED, "Trying to move internal node, this is not supported."); - if (p_nodes[ni]->get_parent() != p_new_parent || p_position_in_parent + ni != p_nodes[ni]->get_index()) { + if (p_nodes[ni]->get_parent() != p_new_parent || p_position_in_parent + ni != p_nodes[ni]->get_index(false)) { no_change = false; } } @@ -1945,7 +1960,7 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V } bool same_parent = new_parent == node->get_parent(); - if (same_parent && node->get_index() < p_position_in_parent + ni) { + if (same_parent && node->get_index(false) < p_position_in_parent + ni) { inc--; // If the child will generate a gap when moved, adjust. } @@ -1987,7 +2002,7 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V } undo_redo->add_do_method(ed, "live_debug_reparent_node", edited_scene->get_path_to(node), edited_scene->get_path_to(new_parent), new_name, new_position_in_parent); - undo_redo->add_undo_method(ed, "live_debug_reparent_node", NodePath(String(edited_scene->get_path_to(new_parent)).path_join(new_name)), edited_scene->get_path_to(node->get_parent()), node->get_name(), node->get_index()); + undo_redo->add_undo_method(ed, "live_debug_reparent_node", NodePath(String(edited_scene->get_path_to(new_parent)).path_join(new_name)), edited_scene->get_path_to(node->get_parent()), node->get_name(), node->get_index(false)); if (p_keep_global_xform) { if (Object::cast_to<Node2D>(node)) { @@ -2024,7 +2039,7 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V owners.push_back(E); } - int child_pos = node->get_index(); + int child_pos = node->get_index(false); undo_redo->add_undo_method(node->get_parent(), "add_child", node, true); undo_redo->add_undo_method(node->get_parent(), "move_child", node, child_pos); @@ -2172,6 +2187,10 @@ void SceneTreeDock::_delete_confirm(bool p_cut) { undo_redo->add_undo_method(scene_tree, "update_tree"); undo_redo->add_undo_reference(edited_scene); } else { + for (const Node *E : remove_list) { + // `move_child` + `get_index` doesn't really work for internal nodes. + ERR_FAIL_COND_MSG(E->get_internal_mode() != INTERNAL_MODE_DISABLED, "Trying to remove internal node, this is not supported."); + } if (delete_tracks_checkbox->is_pressed() || p_cut) { remove_list.sort_custom<Node::Comparator>(); // Sort nodes to keep positions. HashMap<Node *, NodePath> path_renames; @@ -2203,7 +2222,7 @@ void SceneTreeDock::_delete_confirm(bool p_cut) { undo_redo->add_do_method(n->get_parent(), "remove_child", n); undo_redo->add_undo_method(n->get_parent(), "add_child", n, true); - undo_redo->add_undo_method(n->get_parent(), "move_child", n, n->get_index()); + undo_redo->add_undo_method(n->get_parent(), "move_child", n, n->get_index(false)); if (AnimationPlayerEditor::get_singleton()->get_track_editor()->get_root() == n) { undo_redo->add_undo_method(AnimationPlayerEditor::get_singleton()->get_track_editor(), "set_root", n); } @@ -2212,7 +2231,7 @@ void SceneTreeDock::_delete_confirm(bool p_cut) { EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); undo_redo->add_do_method(ed, "live_debug_remove_and_keep_node", edited_scene->get_path_to(n), n->get_instance_id()); - undo_redo->add_undo_method(ed, "live_debug_restore_node", n->get_instance_id(), edited_scene->get_path_to(n->get_parent()), n->get_index()); + undo_redo->add_undo_method(ed, "live_debug_restore_node", n->get_instance_id(), edited_scene->get_path_to(n->get_parent()), n->get_index(false)); } } undo_redo->commit_action(); @@ -2363,16 +2382,12 @@ void SceneTreeDock::_create() { Variant c = create_dialog->instantiate_selected(); ERR_FAIL_COND(!c); - Node *newnode = Object::cast_to<Node>(c); - ERR_FAIL_COND(!newnode); - - ur->add_do_method(this, "replace_node", n, newnode, true, false); - ur->add_do_reference(newnode); - ur->add_undo_method(this, "replace_node", newnode, n, false, false); - ur->add_undo_reference(n); + Node *new_node = Object::cast_to<Node>(c); + ERR_FAIL_COND(!new_node); + replace_node(n, new_node); } - ur->commit_action(); + ur->commit_action(false); } else if (current_option == TOOL_REPARENT_TO_NEW_NODE) { List<Node *> selection = editor_selection->get_selected_node_list(); ERR_FAIL_COND(selection.size() <= 0); @@ -2426,7 +2441,25 @@ void SceneTreeDock::_create() { scene_tree->get_scene_tree()->grab_focus(); } -void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node, bool p_keep_properties, bool p_remove_old) { +void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node) { + EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); + ur->create_action(TTR("Change type of node(s)"), UndoRedo::MERGE_DISABLE, p_node); + + ur->add_do_method(this, "replace_node", p_node, p_by_node, true, false); + ur->add_do_reference(p_by_node); + + _replace_node(p_node, p_by_node, true, false); + + ur->add_undo_method(this, "replace_node", p_by_node, p_node, false, false); + ur->add_undo_reference(p_node); + + perform_node_replace(nullptr, p_node, p_by_node); + + ur->commit_action(false); +} + +void SceneTreeDock::_replace_node(Node *p_node, Node *p_by_node, bool p_keep_properties, bool p_remove_old) { + ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "_replace_node() can't be called on a node outside of tree. You might have called it twice."); Node *n = p_node; Node *newnode = p_by_node; @@ -2499,6 +2532,84 @@ void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node, bool p_keep_prop } } +void SceneTreeDock::perform_node_replace(Node *p_base, Node *p_node, Node *p_by_node) { + if (!p_base) { + p_base = edited_scene; + } + + if (!p_base) { + return; + } + + // Renaming node used in node properties. + List<PropertyInfo> properties; + p_base->get_property_list(&properties); + + for (const PropertyInfo &E : properties) { + if (!(E.usage & (PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR))) { + continue; + } + String propertyname = E.name; + Variant old_variant = p_base->get(propertyname); + Variant updated_variant = old_variant; + String warn_message; + + if (_check_node_recursive(updated_variant, p_node, p_by_node, E.hint_string, warn_message)) { + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + undo_redo->add_do_property(p_base, propertyname, updated_variant); + undo_redo->add_undo_property(p_base, propertyname, old_variant); + p_base->set(propertyname, updated_variant); + if (!warn_message.is_empty()) { + String node_path = (String(edited_scene->get_name()) + "/" + String(edited_scene->get_path_to(p_base))).trim_suffix("/."); + WARN_PRINT(warn_message + vformat(TTR("Removing the node from variable \"%s\" on node \"%s\"."), propertyname, node_path)); + } + } + } + + for (int i = 0; i < p_base->get_child_count(); i++) { + perform_node_replace(p_base->get_child(i), p_node, p_by_node); + } +} + +bool SceneTreeDock::_check_node_recursive(Variant &r_variant, Node *p_node, Node *p_by_node, const String type_hint, String &r_warn_message) { + switch (r_variant.get_type()) { + case Variant::OBJECT: { + if (p_node == r_variant) { + if (p_by_node->is_class(type_hint) || EditorNode::get_singleton()->is_object_of_custom_type(p_by_node, type_hint)) { + r_variant = p_by_node; + } else { + r_variant = memnew(Object); + r_warn_message = vformat(TTR("The node's new type is incompatible with an exported variable (expected %s, but type is %s)."), type_hint, p_by_node->get_class()); + } + return true; + } + } break; + + case Variant::ARRAY: { + Array a = r_variant; + bool updated = false; + for (int i = 0; i < a.size(); i++) { + Variant value = a[i]; + if (_check_node_recursive(value, p_node, p_by_node, type_hint.get_slice(":", 1), r_warn_message)) { + if (!updated) { + a = a.duplicate(); // Need to duplicate for undo-redo to work. + updated = true; + } + a[i] = value; + } + } + if (updated) { + r_variant = a; + return true; + } + } break; + default: { + } + } + + return false; +} + void SceneTreeDock::set_edited_scene(Node *p_scene) { edited_scene = p_scene; } @@ -2613,7 +2724,7 @@ void SceneTreeDock::_normalize_drop(Node *&to_node, int &to_pos, int p_type) { ERR_FAIL_MSG("Cannot perform drop above the root node!"); } - to_pos = to_node->get_index(); + to_pos = to_node->get_index(false); to_node = to_node->get_parent(); } else if (p_type == 1) { @@ -2629,15 +2740,15 @@ void SceneTreeDock::_normalize_drop(Node *&to_node, int &to_pos, int p_type) { if (_has_visible_children(to_node)) { to_pos = 0; } else { - for (int i = to_node->get_index() + 1; i < to_node->get_parent()->get_child_count(); i++) { - Node *c = to_node->get_parent()->get_child(i); + for (int i = to_node->get_index(false) + 1; i < to_node->get_parent()->get_child_count(false); i++) { + Node *c = to_node->get_parent()->get_child(i, false); if (_is_node_visible(c)) { lower_sibling = c; break; } } if (lower_sibling) { - to_pos = lower_sibling->get_index(); + to_pos = lower_sibling->get_index(false); } to_node = to_node->get_parent(); @@ -2865,6 +2976,7 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { menu->add_icon_shortcut(get_theme_icon(SNAME("ActionCopy"), SNAME("EditorIcons")), ED_GET_SHORTCUT("scene_tree/copy_node"), TOOL_COPY); if (selection.size() == 1 && !node_clipboard.is_empty()) { menu->add_icon_shortcut(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons")), ED_GET_SHORTCUT("scene_tree/paste_node"), TOOL_PASTE); + menu->add_icon_shortcut(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons")), ED_GET_SHORTCUT("scene_tree/paste_node_as_sibling"), TOOL_PASTE); } menu->add_separator(); } @@ -3238,7 +3350,7 @@ void SceneTreeDock::open_instance_child_dialog() { _tool_selected(TOOL_INSTANTIATE, true); } -List<Node *> SceneTreeDock::paste_nodes() { +List<Node *> SceneTreeDock::paste_nodes(bool p_paste_as_sibling) { List<Node *> pasted_nodes; if (node_clipboard.is_empty()) { @@ -3268,6 +3380,10 @@ List<Node *> SceneTreeDock::paste_nodes() { paste_parent = selection.back()->get(); } + if (p_paste_as_sibling) { + paste_parent = paste_parent->get_parent(); + } + Node *owner = nullptr; if (paste_parent) { owner = paste_parent->get_owner(); @@ -3277,7 +3393,7 @@ List<Node *> SceneTreeDock::paste_nodes() { } EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); - ur->create_action(TTR("Paste Node(s)"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene()); + ur->create_action(vformat(p_paste_as_sibling ? TTR("Paste Node(s) as Sibling of %s") : TTR("Paste Node(s) as Child of %s"), paste_parent->get_name()), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene()); ur->add_do_method(editor_selection, "clear"); HashMap<Ref<Resource>, Ref<Resource>> resource_remap; @@ -3635,7 +3751,7 @@ void SceneTreeDock::_bind_methods() { ClassDB::bind_method(D_METHOD("instantiate"), &SceneTreeDock::instantiate); ClassDB::bind_method(D_METHOD("get_tree_editor"), &SceneTreeDock::get_tree_editor); - ClassDB::bind_method(D_METHOD("replace_node"), &SceneTreeDock::replace_node); + ClassDB::bind_method(D_METHOD("replace_node"), &SceneTreeDock::_replace_node); ADD_SIGNAL(MethodInfo("remote_tree_selected")); ADD_SIGNAL(MethodInfo("add_node_used")); @@ -3669,15 +3785,16 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec ED_SHORTCUT("scene_tree/batch_rename", TTR("Batch Rename"), KeyModifierMask::SHIFT | Key::F2); ED_SHORTCUT_OVERRIDE("scene_tree/batch_rename", "macos", KeyModifierMask::SHIFT | Key::ENTER); - ED_SHORTCUT("scene_tree/add_child_node", TTR("Add Child Node"), KeyModifierMask::CMD_OR_CTRL | Key::A); - ED_SHORTCUT("scene_tree/instantiate_scene", TTR("Instantiate Child Scene"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::A); + ED_SHORTCUT("scene_tree/add_child_node", TTR("Add Child Node..."), KeyModifierMask::CMD_OR_CTRL | Key::A); + ED_SHORTCUT("scene_tree/instantiate_scene", TTR("Instantiate Child Scene..."), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::A); ED_SHORTCUT("scene_tree/expand_collapse_all", TTR("Expand/Collapse Branch")); ED_SHORTCUT("scene_tree/cut_node", TTR("Cut"), KeyModifierMask::CMD_OR_CTRL | Key::X); ED_SHORTCUT("scene_tree/copy_node", TTR("Copy"), KeyModifierMask::CMD_OR_CTRL | Key::C); ED_SHORTCUT("scene_tree/paste_node", TTR("Paste"), KeyModifierMask::CMD_OR_CTRL | Key::V); - ED_SHORTCUT("scene_tree/change_node_type", TTR("Change Type")); - ED_SHORTCUT("scene_tree/attach_script", TTR("Attach Script")); - ED_SHORTCUT("scene_tree/extend_script", TTR("Extend Script")); + ED_SHORTCUT("scene_tree/paste_node_as_sibling", TTR("Paste as Sibling"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::V); + ED_SHORTCUT("scene_tree/change_node_type", TTR("Change Type...")); + ED_SHORTCUT("scene_tree/attach_script", TTR("Attach Script...")); + ED_SHORTCUT("scene_tree/extend_script", TTR("Extend Script...")); ED_SHORTCUT("scene_tree/detach_script", TTR("Detach Script")); ED_SHORTCUT("scene_tree/move_up", TTR("Move Up"), KeyModifierMask::CMD_OR_CTRL | Key::UP); ED_SHORTCUT("scene_tree/move_down", TTR("Move Down"), KeyModifierMask::CMD_OR_CTRL | Key::DOWN); diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index 1db73df4c7..9c111bce71 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -60,6 +60,7 @@ class SceneTreeDock : public VBoxContainer { TOOL_CUT, TOOL_COPY, TOOL_PASTE, + TOOL_PASTE_AS_SIBLING, TOOL_RENAME, #ifdef MODULE_REGEX_ENABLED TOOL_BATCH_RENAME, @@ -281,6 +282,8 @@ class SceneTreeDock : public VBoxContainer { bool _update_node_path(Node *p_root_node, NodePath &r_node_path, HashMap<Node *, NodePath> *p_renames) const; bool _check_node_path_recursive(Node *p_root_node, Variant &r_variant, HashMap<Node *, NodePath> *p_renames) const; + bool _check_node_recursive(Variant &r_variant, Node *p_node, Node *p_by_node, const String type_hint, String &r_warn_message); + void _replace_node(Node *p_node, Node *p_by_node, bool p_keep_properties = true, bool p_remove_old = true); private: static SceneTreeDock *singleton; @@ -306,6 +309,7 @@ public: void set_selected(Node *p_node, bool p_emit_selected = false); void fill_path_renames(Node *p_node, Node *p_new_parent, HashMap<Node *, NodePath> *p_renames); void perform_node_renames(Node *p_base, HashMap<Node *, NodePath> *p_renames, HashMap<Ref<Animation>, HashSet<int>> *r_rem_anims = nullptr); + void perform_node_replace(Node *p_base, Node *p_node, Node *p_by_node); SceneTreeEditor *get_tree_editor() { return scene_tree; } EditorData *get_editor_data() { return editor_data; } @@ -315,7 +319,7 @@ public: void show_tab_buttons(); void hide_tab_buttons(); - void replace_node(Node *p_node, Node *p_by_node, bool p_keep_properties = true, bool p_remove_old = true); + void replace_node(Node *p_node, Node *p_by_node); void attach_script_to_selected(bool p_extend); void open_script_dialog(Node *p_for_node, bool p_extend); @@ -326,7 +330,7 @@ public: void open_add_child_dialog(); void open_instance_child_dialog(); - List<Node *> paste_nodes(); + List<Node *> paste_nodes(bool p_paste_as_sibling = false); List<Node *> get_node_clipboard() const; ScriptCreateDialog *get_script_create_dialog() { diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp index a4eabf409a..daac755529 100644 --- a/editor/script_create_dialog.cpp +++ b/editor/script_create_dialog.cpp @@ -41,6 +41,7 @@ #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "editor/gui/editor_file_dialog.h" +#include "editor/gui/editor_validation_panel.h" static String _get_parent_class_of_script(String p_path) { if (!ResourceLoader::exists(p_path, "Script")) { @@ -136,7 +137,6 @@ void ScriptCreateDialog::_notification(int p_what) { path_button->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons"))); parent_browse_button->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons"))); parent_search_button->set_icon(get_theme_icon(SNAME("ClassList"), SNAME("EditorIcons"))); - status_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree"))); } break; } } @@ -295,13 +295,7 @@ String ScriptCreateDialog::_validate_path(const String &p_path, bool p_file_must } // Let ScriptLanguage do custom validation. - String path_error = ScriptServer::get_language(language_menu->get_selected())->validate_path(p); - if (!path_error.is_empty()) { - return path_error; - } - - // All checks passed. - return ""; + return ScriptServer::get_language(language_menu->get_selected())->validate_path(p); } String ScriptCreateDialog::_get_class_name() const { @@ -314,12 +308,12 @@ String ScriptCreateDialog::_get_class_name() const { void ScriptCreateDialog::_class_name_changed(const String &p_name) { is_class_name_valid = _validate_class(class_name->get_text()); - _update_dialog(); + validation_panel->update(); } void ScriptCreateDialog::_parent_name_changed(const String &p_parent) { is_parent_name_valid = _validate_parent(parent_name->get_text()); - _update_dialog(); + validation_panel->update(); } void ScriptCreateDialog::_template_changed(int p_template) { @@ -347,15 +341,15 @@ void ScriptCreateDialog::_template_changed(int p_template) { } } } + // Update template label information. - String template_info = String::utf8("• "); + String template_info = U"• "; template_info += TTR("Template:"); template_info += " " + sinfo.name; if (!sinfo.description.is_empty()) { template_info += " - " + sinfo.description; } - template_info_label->set_text(template_info); - template_info_label->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor"))); + validation_panel->set_message(MSG_ID_TEMPLATE, template_info, EditorValidationPanel::MSG_INFO, false); } void ScriptCreateDialog::ok_pressed() { @@ -367,7 +361,7 @@ void ScriptCreateDialog::ok_pressed() { EditorSettings::get_singleton()->save(); is_new_script_created = true; - _update_dialog(); + validation_panel->update(); } void ScriptCreateDialog::_create_new() { @@ -471,7 +465,7 @@ void ScriptCreateDialog::_language_changed(int l) { EditorSettings::get_singleton()->set_project_metadata("script_setup", "last_selected_language", language_menu->get_item_text(language_menu->get_selected())); _parent_name_changed(parent_name->get_text()); - _update_dialog(); + validation_panel->update(); } void ScriptCreateDialog::_built_in_pressed() { @@ -482,13 +476,13 @@ void ScriptCreateDialog::_built_in_pressed() { is_built_in = false; _path_changed(file_path->get_text()); } - _update_dialog(); + validation_panel->update(); } void ScriptCreateDialog::_use_template_pressed() { is_using_templates = use_templates->is_pressed(); EditorSettings::get_singleton()->set_meta("script_setup_use_script_templates", is_using_templates); - _update_dialog(); + validation_panel->update(); } void ScriptCreateDialog::_browse_path(bool browse_parent, bool p_save) { @@ -555,10 +549,9 @@ void ScriptCreateDialog::_path_changed(const String &p_path) { is_path_valid = false; is_new_script_created = true; - String path_error = _validate_path(p_path, false); + path_error = _validate_path(p_path, false); if (!path_error.is_empty()) { - _msg_path_valid(false, path_error); - _update_dialog(); + validation_panel->update(); return; } @@ -567,32 +560,15 @@ void ScriptCreateDialog::_path_changed(const String &p_path) { String p = ProjectSettings::get_singleton()->localize_path(p_path.strip_edges()); if (da->file_exists(p)) { is_new_script_created = false; - _msg_path_valid(true, TTR("File exists, it will be reused.")); } is_path_valid = true; - _update_dialog(); + validation_panel->update(); } void ScriptCreateDialog::_path_submitted(const String &p_path) { - ok_pressed(); -} - -void ScriptCreateDialog::_msg_script_valid(bool valid, const String &p_msg) { - error_label->set_text(String::utf8("• ") + p_msg); - if (valid) { - error_label->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor"))); - } else { - error_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor"))); - } -} - -void ScriptCreateDialog::_msg_path_valid(bool valid, const String &p_msg) { - path_error_label->set_text(String::utf8("• ") + p_msg); - if (valid) { - path_error_label->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor"))); - } else { - path_error_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor"))); + if (!get_ok_button()->is_disabled()) { + ok_pressed(); } } @@ -688,25 +664,25 @@ void ScriptCreateDialog::_update_template_menu() { void ScriptCreateDialog::_update_dialog() { // "Add Script Dialog" GUI logic and script checks. _update_template_menu(); - bool script_ok = true; // Is script path/name valid (order from top to bottom)? if (!is_built_in && !is_path_valid) { - _msg_script_valid(false, TTR("Invalid path.")); - script_ok = false; + validation_panel->set_message(MSG_ID_SCRIPT, TTR("Invalid path."), EditorValidationPanel::MSG_ERROR); } if (has_named_classes && (is_new_script_created && !is_class_name_valid)) { - _msg_script_valid(false, TTR("Invalid class name.")); - script_ok = false; + validation_panel->set_message(MSG_ID_SCRIPT, TTR("Invalid class name."), EditorValidationPanel::MSG_ERROR); } if (!is_parent_name_valid && is_new_script_created) { - _msg_script_valid(false, TTR("Invalid inherited parent name or path.")); - script_ok = false; + validation_panel->set_message(MSG_ID_SCRIPT, TTR("Invalid inherited parent name or path."), EditorValidationPanel::MSG_ERROR); } - if (script_ok) { - _msg_script_valid(true, TTR("Script path/name is valid.")); + if (validation_panel->is_valid() && !is_new_script_created) { + validation_panel->set_message(MSG_ID_SCRIPT, TTR("File exists, it will be reused."), EditorValidationPanel::MSG_OK); + } + + if (!path_error.is_empty()) { + validation_panel->set_message(MSG_ID_PATH, path_error, EditorValidationPanel::MSG_ERROR); } // Does script have named classes? @@ -752,7 +728,11 @@ void ScriptCreateDialog::_update_dialog() { // Is Script created or loaded from existing file? - builtin_warning_label->set_visible(is_built_in); + if (is_built_in) { + validation_panel->set_message(MSG_ID_BUILT_IN, TTR("Note: Built-in scripts have some limitations and can't be edited using an external editor."), EditorValidationPanel::MSG_INFO, false); + } else if (_get_class_name() == parent_name->get_text()) { + validation_panel->set_message(MSG_ID_BUILT_IN, TTR("Warning: Having the script name be the same as a built-in type is usually not desired."), EditorValidationPanel::MSG_WARNING, false); + } path_controls[0]->set_visible(!is_built_in); path_controls[1]->set_visible(!is_built_in); @@ -761,7 +741,6 @@ void ScriptCreateDialog::_update_dialog() { // Check if the script name is the same as the parent class. // This warning isn't relevant if the script is built-in. - script_name_warning_label->set_visible(!is_built_in && _get_class_name() == parent_name->get_text()); bool is_new_file = is_built_in || is_new_script_created; @@ -774,21 +753,16 @@ void ScriptCreateDialog::_update_dialog() { if (is_new_file) { if (is_built_in) { - _msg_path_valid(true, TTR("Built-in script (into scene file).")); - } - if (is_new_script_created && is_path_valid) { - _msg_path_valid(true, TTR("Will create a new script file.")); + validation_panel->set_message(MSG_ID_PATH, TTR("Built-in script (into scene file)."), EditorValidationPanel::MSG_OK); } } else { + template_inactive_message = TTR("Using existing script file."); if (load_enabled) { - template_inactive_message = TTR("Using existing script file."); if (is_path_valid) { - _msg_path_valid(true, TTR("Will load an existing script file.")); + validation_panel->set_message(MSG_ID_PATH, TTR("Will load an existing script file."), EditorValidationPanel::MSG_OK); } } else { - template_inactive_message = TTR("Using existing script file."); - _msg_path_valid(false, TTR("Script file already exists.")); - script_ok = false; + validation_panel->set_message(MSG_ID_PATH, TTR("Script file already exists."), EditorValidationPanel::MSG_ERROR); } } @@ -806,18 +780,7 @@ void ScriptCreateDialog::_update_dialog() { template_menu->set_disabled(true); template_menu->clear(); template_menu->add_item(template_inactive_message); - } - template_info_label->set_visible(!template_menu->is_disabled()); - - get_ok_button()->set_disabled(!script_ok); - - Callable entered_call = callable_mp(this, &ScriptCreateDialog::_path_submitted); - if (script_ok) { - if (!file_path->is_connected("text_submitted", entered_call)) { - file_path->connect("text_submitted", entered_call); - } - } else if (file_path->is_connected("text_submitted", entered_call)) { - file_path->disconnect("text_submitted", entered_call); + validation_panel->set_message(MSG_ID_TEMPLATE, "", EditorValidationPanel::MSG_INFO); } } @@ -967,47 +930,23 @@ ScriptCreateDialog::ScriptCreateDialog() { /* Information Messages Field */ - VBoxContainer *vb = memnew(VBoxContainer); - - error_label = memnew(Label); - vb->add_child(error_label); - - path_error_label = memnew(Label); - vb->add_child(path_error_label); - - builtin_warning_label = memnew(Label); - builtin_warning_label->set_text( - TTR("Note: Built-in scripts have some limitations and can't be edited using an external editor.")); - vb->add_child(builtin_warning_label); - builtin_warning_label->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART); - builtin_warning_label->hide(); - - script_name_warning_label = memnew(Label); - script_name_warning_label->set_text( - TTR("Warning: Having the script name be the same as a built-in type is usually not desired.")); - vb->add_child(script_name_warning_label); - script_name_warning_label->add_theme_color_override("font_color", Color(1, 0.85, 0.4)); - script_name_warning_label->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART); - script_name_warning_label->hide(); - - template_info_label = memnew(Label); - vb->add_child(template_info_label); - template_info_label->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART); - - status_panel = memnew(PanelContainer); - status_panel->set_h_size_flags(Control::SIZE_FILL); - status_panel->set_v_size_flags(Control::SIZE_EXPAND_FILL); - status_panel->add_child(vb); + validation_panel = memnew(EditorValidationPanel); + validation_panel->add_line(MSG_ID_SCRIPT, TTR("Script path/name is valid.")); + validation_panel->add_line(MSG_ID_PATH, TTR("Will create a new script file.")); + validation_panel->add_line(MSG_ID_BUILT_IN); + validation_panel->add_line(MSG_ID_TEMPLATE); + validation_panel->set_update_callback(callable_mp(this, &ScriptCreateDialog::_update_dialog)); + validation_panel->set_accept_button(get_ok_button()); /* Spacing */ Control *spacing = memnew(Control); spacing->set_custom_minimum_size(Size2(0, 10 * EDSCALE)); - vb = memnew(VBoxContainer); + VBoxContainer *vb = memnew(VBoxContainer); vb->add_child(gc); vb->add_child(spacing); - vb->add_child(status_panel); + vb->add_child(validation_panel); add_child(vb); /* Language */ diff --git a/editor/script_create_dialog.h b/editor/script_create_dialog.h index fa42b96746..ea2f480c57 100644 --- a/editor/script_create_dialog.h +++ b/editor/script_create_dialog.h @@ -41,17 +41,20 @@ class CreateDialog; class EditorFileDialog; +class EditorValidationPanel; class ScriptCreateDialog : public ConfirmationDialog { GDCLASS(ScriptCreateDialog, ConfirmationDialog); + enum { + MSG_ID_SCRIPT, + MSG_ID_PATH, + MSG_ID_BUILT_IN, + MSG_ID_TEMPLATE, + }; + LineEdit *class_name = nullptr; - Label *error_label = nullptr; - Label *path_error_label = nullptr; - Label *builtin_warning_label = nullptr; - Label *script_name_warning_label = nullptr; - Label *template_info_label = nullptr; - PanelContainer *status_panel = nullptr; + EditorValidationPanel *validation_panel = nullptr; LineEdit *parent_name = nullptr; Button *parent_browse_button = nullptr; Button *parent_search_button = nullptr; @@ -67,6 +70,7 @@ class ScriptCreateDialog : public ConfirmationDialog { AcceptDialog *alert = nullptr; CreateDialog *select_class = nullptr; bool is_browsing_parent = false; + String path_error; String template_inactive_message; String initial_bp; bool is_new_script_created = true; @@ -113,8 +117,6 @@ class ScriptCreateDialog : public ConfirmationDialog { virtual void ok_pressed() override; void _create_new(); void _load_exist(); - void _msg_script_valid(bool valid, const String &p_msg = String()); - void _msg_path_valid(bool valid, const String &p_msg = String()); void _update_template_menu(); void _update_dialog(); ScriptLanguage::ScriptTemplate _get_current_template() const; diff --git a/editor/shader_create_dialog.cpp b/editor/shader_create_dialog.cpp index 607b53718b..9a7b9bc84d 100644 --- a/editor/shader_create_dialog.cpp +++ b/editor/shader_create_dialog.cpp @@ -33,6 +33,7 @@ #include "core/config/project_settings.h" #include "editor/editor_scale.h" #include "editor/gui/editor_file_dialog.h" +#include "editor/gui/editor_validation_panel.h" #include "scene/resources/shader_include.h" #include "scene/resources/visual_shader.h" #include "servers/rendering/shader_types.h" @@ -89,7 +90,6 @@ void ShaderCreateDialog::_update_theme() { } path_button->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons"))); - status_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree"))); } void ShaderCreateDialog::_update_language_info() { @@ -147,7 +147,7 @@ void ShaderCreateDialog::ok_pressed() { } is_new_shader_created = true; - _update_dialog(); + validation_panel->update(); } void ShaderCreateDialog::_create_new() { @@ -327,7 +327,7 @@ void ShaderCreateDialog::_type_changed(int p_language) { } EditorSettings::get_singleton()->set_project_metadata("shader_setup", "last_selected_language", type_menu->get_item_text(type_menu->get_selected())); - _update_dialog(); + validation_panel->update(); } void ShaderCreateDialog::_built_in_toggled(bool p_enabled) { @@ -337,7 +337,7 @@ void ShaderCreateDialog::_built_in_toggled(bool p_enabled) { } else { _path_changed(file_path->get_text()); } - _update_dialog(); + validation_panel->update(); } void ShaderCreateDialog::_browse_path() { @@ -378,10 +378,9 @@ void ShaderCreateDialog::_path_changed(const String &p_path) { is_path_valid = false; is_new_shader_created = true; - String path_error = _validate_path(p_path); + path_error = _validate_path(p_path); if (!path_error.is_empty()) { - _msg_path_valid(false, path_error); - _update_dialog(); + validation_panel->update(); return; } @@ -389,15 +388,16 @@ void ShaderCreateDialog::_path_changed(const String &p_path) { String p = ProjectSettings::get_singleton()->localize_path(p_path.strip_edges()); if (f->file_exists(p)) { is_new_shader_created = false; - _msg_path_valid(true, TTR("File exists, it will be reused.")); } is_path_valid = true; - _update_dialog(); + validation_panel->update(); } void ShaderCreateDialog::_path_submitted(const String &p_path) { - ok_pressed(); + if (!get_ok_button()->is_disabled()) { + ok_pressed(); + } } void ShaderCreateDialog::config(const String &p_base_path, bool p_built_in_enabled, bool p_load_enabled, int p_preferred_type, int p_preferred_mode) { @@ -490,33 +490,14 @@ String ShaderCreateDialog::_validate_path(const String &p_path) { return ""; } -void ShaderCreateDialog::_msg_script_valid(bool valid, const String &p_msg) { - error_label->set_text(String::utf8("• ") + p_msg); - if (valid) { - error_label->add_theme_color_override("font_color", gc->get_theme_color(SNAME("success_color"), SNAME("Editor"))); - } else { - error_label->add_theme_color_override("font_color", gc->get_theme_color(SNAME("error_color"), SNAME("Editor"))); - } -} - -void ShaderCreateDialog::_msg_path_valid(bool valid, const String &p_msg) { - path_error_label->set_text(String::utf8("• ") + p_msg); - if (valid) { - path_error_label->add_theme_color_override("font_color", gc->get_theme_color(SNAME("success_color"), SNAME("Editor"))); - } else { - path_error_label->add_theme_color_override("font_color", gc->get_theme_color(SNAME("error_color"), SNAME("Editor"))); - } -} - void ShaderCreateDialog::_update_dialog() { - bool shader_ok = true; - if (!is_built_in && !is_path_valid) { - _msg_script_valid(false, TTR("Invalid path.")); - shader_ok = false; + validation_panel->set_message(MSG_ID_SHADER, TTR("Invalid path."), EditorValidationPanel::MSG_ERROR); } - if (shader_ok) { - _msg_script_valid(true, TTR("Shader path/name is valid.")); + if (!path_error.is_empty()) { + validation_panel->set_message(MSG_ID_PATH, path_error, EditorValidationPanel::MSG_ERROR); + } else if (validation_panel->is_valid() && !is_new_shader_created) { + validation_panel->set_message(MSG_ID_SHADER, TTR("File exists, it will be reused."), EditorValidationPanel::MSG_OK); } if (!built_in_enabled) { internal->set_pressed(false); @@ -537,37 +518,23 @@ void ShaderCreateDialog::_update_dialog() { internal->set_disabled(!built_in_enabled); - builtin_warning_label->set_visible(is_built_in); + if (is_built_in) { + validation_panel->set_message(MSG_ID_BUILT_IN, TTR("Note: Built-in shaders can't be edited using an external editor."), EditorValidationPanel::MSG_INFO, false); + } if (is_built_in) { set_ok_button_text(TTR("Create")); - _msg_path_valid(true, TTR("Built-in shader (into scene file).")); + validation_panel->set_message(MSG_ID_PATH, TTR("Built-in shader (into scene file)."), EditorValidationPanel::MSG_OK); } else if (is_new_shader_created) { set_ok_button_text(TTR("Create")); - if (is_path_valid) { - _msg_path_valid(true, TTR("Will create a new shader file.")); - } } else if (load_enabled) { set_ok_button_text(TTR("Load")); if (is_path_valid) { - _msg_path_valid(true, TTR("Will load an existing shader file.")); + validation_panel->set_message(MSG_ID_PATH, TTR("Will load an existing shader file."), EditorValidationPanel::MSG_OK); } } else { set_ok_button_text(TTR("Create")); - _msg_path_valid(false, TTR("Shader file already exists.")); - - shader_ok = false; - } - - get_ok_button()->set_disabled(!shader_ok); - - Callable entered_call = callable_mp(this, &ShaderCreateDialog::_path_submitted); - if (shader_ok) { - if (!file_path->is_connected("text_submitted", entered_call)) { - file_path->connect("text_submitted", entered_call); - } - } else if (file_path->is_connected("text_submitted", entered_call)) { - file_path->disconnect("text_submitted", entered_call); + validation_panel->set_message(MSG_ID_PATH, TTR("Shader file already exists."), EditorValidationPanel::MSG_ERROR); } } @@ -588,35 +555,22 @@ ShaderCreateDialog::ShaderCreateDialog() { // Error Fields. - VBoxContainer *vb = memnew(VBoxContainer); - - error_label = memnew(Label); - vb->add_child(error_label); - - path_error_label = memnew(Label); - vb->add_child(path_error_label); - - builtin_warning_label = memnew(Label); - builtin_warning_label->set_text( - TTR("Note: Built-in shaders can't be edited using an external editor.")); - vb->add_child(builtin_warning_label); - builtin_warning_label->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART); - builtin_warning_label->hide(); - - status_panel = memnew(PanelContainer); - status_panel->set_h_size_flags(Control::SIZE_FILL); - status_panel->set_v_size_flags(Control::SIZE_EXPAND_FILL); - status_panel->add_child(vb); + validation_panel = memnew(EditorValidationPanel); + validation_panel->add_line(MSG_ID_SHADER, TTR("Shader path/name is valid.")); + validation_panel->add_line(MSG_ID_PATH, TTR("Will create a new shader file.")); + validation_panel->add_line(MSG_ID_BUILT_IN); + validation_panel->set_update_callback(callable_mp(this, &ShaderCreateDialog::_update_dialog)); + validation_panel->set_accept_button(get_ok_button()); // Spacing. Control *spacing = memnew(Control); spacing->set_custom_minimum_size(Size2(0, 10 * EDSCALE)); - vb = memnew(VBoxContainer); + VBoxContainer *vb = memnew(VBoxContainer); vb->add_child(gc); vb->add_child(spacing); - vb->add_child(status_panel); + vb->add_child(validation_panel); add_child(vb); // Type. diff --git a/editor/shader_create_dialog.h b/editor/shader_create_dialog.h index 729a7b5bd4..d6d9f10020 100644 --- a/editor/shader_create_dialog.h +++ b/editor/shader_create_dialog.h @@ -40,10 +40,17 @@ #include "scene/gui/panel_container.h" class EditorFileDialog; +class EditorValidationPanel; class ShaderCreateDialog : public ConfirmationDialog { GDCLASS(ShaderCreateDialog, ConfirmationDialog); + enum { + MSG_ID_SHADER, + MSG_ID_PATH, + MSG_ID_BUILT_IN, + }; + struct ShaderTypeData { List<String> extensions; String default_extension; @@ -53,10 +60,7 @@ class ShaderCreateDialog : public ConfirmationDialog { List<ShaderTypeData> type_data; GridContainer *gc = nullptr; - Label *error_label = nullptr; - Label *path_error_label = nullptr; - Label *builtin_warning_label = nullptr; - PanelContainer *status_panel = nullptr; + EditorValidationPanel *validation_panel = nullptr; OptionButton *type_menu = nullptr; OptionButton *mode_menu = nullptr; OptionButton *template_menu = nullptr; @@ -67,6 +71,7 @@ class ShaderCreateDialog : public ConfirmationDialog { AcceptDialog *alert = nullptr; String initial_base_path; + String path_error; bool is_new_shader_created = true; bool is_path_valid = false; bool is_built_in = false; @@ -93,8 +98,6 @@ class ShaderCreateDialog : public ConfirmationDialog { virtual void ok_pressed() override; void _create_new(); void _load_exist(); - void _msg_script_valid(bool valid, const String &p_msg = String()); - void _msg_path_valid(bool valid, const String &p_msg = String()); void _update_dialog(); protected: diff --git a/main/main.cpp b/main/main.cpp index 7e2741648d..e6dd576b8a 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -318,6 +318,7 @@ void finalize_display() { void initialize_navigation_server() { ERR_FAIL_COND(navigation_server_3d != nullptr); + ERR_FAIL_COND(navigation_server_2d != nullptr); // Init 3D Navigation Server navigation_server_3d = NavigationServer3DManager::new_default_server(); @@ -330,6 +331,7 @@ void initialize_navigation_server() { // Should be impossible, but make sure it's not null. ERR_FAIL_NULL_MSG(navigation_server_3d, "Failed to initialize NavigationServer3D."); + navigation_server_3d->init(); // Init 2D Navigation Server navigation_server_2d = memnew(NavigationServer2D); @@ -337,9 +339,12 @@ void initialize_navigation_server() { } void finalize_navigation_server() { + ERR_FAIL_NULL(navigation_server_3d); + navigation_server_3d->finish(); memdelete(navigation_server_3d); navigation_server_3d = nullptr; + ERR_FAIL_NULL(navigation_server_2d); memdelete(navigation_server_2d); navigation_server_2d = nullptr; } @@ -453,6 +458,7 @@ void Main::print_help(const char *p_binary) { #if DEBUG_ENABLED OS::get_singleton()->print(" --gpu-abort Abort on graphics API usage errors (usually validation layer errors). May help see the problem if your system freezes.\n"); #endif + OS::get_singleton()->print(" --generate-spirv-debug-info Generate SPIR-V debug information. This allows source-level shader debugging with RenderDoc.\n"); OS::get_singleton()->print(" --remote-debug <uri> Remote debug (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007).\n"); OS::get_singleton()->print(" --single-threaded-scene Scene tree runs in single-threaded mode. Sub-thread groups are disabled and run on the main thread.\n"); #if defined(DEBUG_ENABLED) @@ -581,6 +587,8 @@ Error Main::test_setup() { theme_db->initialize_theme(); register_scene_singletons(); + initialize_navigation_server(); + ERR_FAIL_COND_V(TextServerManager::get_singleton()->get_interface_count() == 0, ERR_CANT_CREATE); /* Use one with the most features available. */ @@ -639,6 +647,8 @@ void Main::test_cleanup() { finalize_theme_db(); + finalize_navigation_server(); + GDExtensionManager::get_singleton()->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_SERVERS); uninitialize_modules(MODULE_INITIALIZATION_LEVEL_SERVERS); unregister_server_types(); @@ -857,6 +867,15 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(I->next()->get()); } } + // If gpu is specified, both editor and debug instances started from editor will inherit. + if (I->get() == "--gpu-index") { + if (I->next()) { + forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(I->get()); + forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(I->next()->get()); + forwardable_cli_arguments[CLI_SCOPE_PROJECT].push_back(I->get()); + forwardable_cli_arguments[CLI_SCOPE_PROJECT].push_back(I->next()->get()); + } + } #endif if (adding_user_args) { @@ -1001,6 +1020,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } else if (I->get() == "--gpu-abort") { Engine::singleton->abort_on_gpu_errors = true; #endif + } else if (I->get() == "--generate-spirv-debug-info") { + Engine::singleton->generate_spirv_debug_info = true; } else if (I->get() == "--tablet-driver") { if (I->next()) { tablet_driver = I->next()->get(); @@ -1522,6 +1543,12 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } } +#ifdef TOOLS_ENABLED + if (editor) { + Engine::get_singleton()->set_editor_hint(true); + } +#endif + // Initialize user data dir. OS::get_singleton()->ensure_user_data_dir(); @@ -1549,7 +1576,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph #ifdef TOOLS_ENABLED if (editor) { packed_data->set_disabled(true); - Engine::get_singleton()->set_editor_hint(true); main_args.push_back("--editor"); if (!init_windowed) { init_maximized = true; @@ -3104,6 +3130,7 @@ bool Main::start() { Size2i stretch_size = Size2i(GLOBAL_GET("display/window/size/viewport_width"), GLOBAL_GET("display/window/size/viewport_height")); real_t stretch_scale = GLOBAL_GET("display/window/stretch/scale"); + String stretch_scale_mode = GLOBAL_GET("display/window/stretch/scale_mode"); Window::ContentScaleMode cs_sm = Window::CONTENT_SCALE_MODE_DISABLED; if (stretch_mode == "canvas_items") { @@ -3123,8 +3150,14 @@ bool Main::start() { cs_aspect = Window::CONTENT_SCALE_ASPECT_EXPAND; } + Window::ContentScaleStretch cs_stretch = Window::CONTENT_SCALE_STRETCH_FRACTIONAL; + if (stretch_scale_mode == "integer") { + cs_stretch = Window::CONTENT_SCALE_STRETCH_INTEGER; + } + sml->get_root()->set_content_scale_mode(cs_sm); sml->get_root()->set_content_scale_aspect(cs_aspect); + sml->get_root()->set_content_scale_stretch(cs_stretch); sml->get_root()->set_content_scale_size(stretch_size); sml->get_root()->set_content_scale_factor(stretch_scale); diff --git a/methods.py b/methods.py index 571a3f739e..7c1781d699 100644 --- a/methods.py +++ b/methods.py @@ -868,6 +868,9 @@ def generate_vs_project(env, num_jobs, project_name="godot"): if env["custom_modules"]: common_build_postfix.append("custom_modules=%s" % env["custom_modules"]) + if env["windows_subsystem"] == "console": + common_build_postfix.append("windows_subsystem=console") + if env["precision"] == "double": common_build_postfix.append("precision=double") diff --git a/misc/error_suppressions/tsan.txt b/misc/error_suppressions/tsan.txt new file mode 100644 index 0000000000..7c3d836f6c --- /dev/null +++ b/misc/error_suppressions/tsan.txt @@ -0,0 +1,8 @@ +# See the below link for an explanation of this file's format +# https://github.com/google/sanitizers/wiki/ThreadSanitizerSuppressions + +deadlock:tests/core/templates/test_command_queue.h +deadlock:modules/text_server_adv/text_server_adv.cpp +deadlock:modules/text_server_fb/text_server_fb.cpp +race:modules/navigation/nav_map.cpp + diff --git a/misc/extension_api_validation/4.0-stable.expected b/misc/extension_api_validation/4.0-stable.expected index 380c1d5193..5f982cbff6 100644 --- a/misc/extension_api_validation/4.0-stable.expected +++ b/misc/extension_api_validation/4.0-stable.expected @@ -4,8 +4,21 @@ This file contains the expected output of --validate-extension-api when run agai Only lines that start with "Validate extension JSON:" matter, everything else is considered a comment and ignored. They should instead be used to justify these changes and describe how users should work around these changes. +Add new entries at the end of the file. + ======================================================================================================================== +Misc +---- +Validate extension JSON: API was removed: classes/FramebufferCacheRD +Validate extension JSON: API was removed: classes/UniformSetCacheRD + +FIXME: These aren't written when dumping the interface with a headless build +(since there's no RD backend in use). We need to fix this inconsistency somehow. + + +## Changes between 4.0-stable and 4.1-stable + GH-78517 -------- Validate extension JSON: Error: Field 'classes/DisplayServer/methods/global_menu_add_check_item/arguments/2': default_value changed value in new API, from "" to "Callable()". @@ -36,10 +49,12 @@ Validate extension JSON: Error: Field 'classes/RenderingServer/methods/instances The previous argument was a serialization bug, there's no actual API change. + GH-78237 -------- Validate extension JSON: Error: Field 'classes/WebRTCPeerConnectionExtension/methods/_create_data_channel/return_value': type changed value in new API, from "Object" to "WebRTCDataChannel". + GH-77757 -------- Validate extension JSON: Error: Field 'classes/Viewport/methods/gui_get_focus_owner': is_const changed value in new API, from false to true. @@ -136,13 +151,6 @@ Navigation avoidance was reworked entirely. Migration: TODO -GH-????? --------- -Validate extension JSON: API was removed: classes/FramebufferCacheRD -Validate extension JSON: API was removed: classes/UniformSetCacheRD - -Unsure where these come from; when dumping the interface, these do actually still exist - GH-76176 -------- Validate extension JSON: Error: Hash changed for 'classes/EditorInterface/methods/get_base_control', from 31757941 to A5E188F5. This means that the function has changed and no compatibility function was provided. @@ -260,7 +268,6 @@ Validate extension JSON: Error: Field 'classes/SyntaxHighlighter/methods/get_tex Function was made `const`. No adjustments should be necessary. - GH-75250 & GH-76401 ------------------- Validate extension JSON: Error: Hash changed for 'classes/RichTextLabel/methods/push_paragraph', from 3DD1D1C2 to BFDC71FE. This means that the function has changed and no compatibility function was provided. @@ -359,6 +366,9 @@ Validate extension JSON: Error: Hash changed for 'classes/UndoRedo/methods/creat Added a optional parameters with default values. No adjustments should be necessary. + +## Changes between 4.1-stable and 4.2-stable + GH-79911 -------- Validate extension JSON: Error: Field 'classes/RenderingDevice/enums/BarrierMask/values/BARRIER_MASK_RASTER': value changed value in new API, from 1.0 to 9. @@ -379,3 +389,65 @@ Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/barrier/a Validate extension JSON: Error: Hash changed for 'classes/RenderingDevice/methods/barrier', from 0FE50041 to DD9E8DAB. This means that the function has changed and no compatibility function was provided. Raster barrier was split into vertex and fragment barriers for use in mobile renderer. + + +GH-79308 +-------- +Validate extension JSON: API was removed: classes/GraphEdit/methods/get_scroll_ofs +Validate extension JSON: API was removed: classes/GraphEdit/methods/get_snap +Validate extension JSON: API was removed: classes/GraphEdit/methods/get_zoom_hbox +Validate extension JSON: API was removed: classes/GraphEdit/methods/is_using_snap +Validate extension JSON: API was removed: classes/GraphEdit/methods/set_scroll_ofs +Validate extension JSON: API was removed: classes/GraphEdit/methods/set_snap +Validate extension JSON: API was removed: classes/GraphEdit/methods/set_use_snap +Validate extension JSON: API was removed: classes/GraphEdit/properties/snap_distance +Validate extension JSON: API was removed: classes/GraphEdit/properties/use_snap +Validate extension JSON: API was removed: classes/GraphNode/methods/is_comment +Validate extension JSON: API was removed: classes/GraphNode/methods/set_comment +Validate extension JSON: API was removed: classes/GraphNode/properties/comment +Validate extension JSON: Error: Field 'classes/GraphEdit/properties/scroll_offset': getter changed value in new API, from "get_scroll_ofs" to &"get_scroll_offset". +Validate extension JSON: Error: Field 'classes/GraphEdit/properties/scroll_offset': setter changed value in new API, from "set_scroll_ofs" to &"set_scroll_offset". + +Intentional compatibility breakage during refactoring of API marked as experimental. + +FIXME: Still a WIP, review this list once the work is completed, especially if compatibility +code is added. + + +GH-73196 +-------- +Validate extension JSON: Error: Field 'classes/CodeEdit/methods/get_text_for_symbol_lookup': is_const changed value in new API, from false to true. + +Function was made `const`. No adjustments should be necessary. + + +GH-78328 +-------- +Validate extension JSON: Error: Field 'classes/TileMap/methods/get_used_rect': is_const changed value in new API, from false to true. + +Function was made `const`. No adjustments should be necessary. + + +GH-79606 +-------- +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/shader_create_from_bytecode/arguments': size changed value in new API, from 1 to 2. + +Added optional argument. Compatibility method registered. + + +GH-78266 +-------- +Validate extension JSON: API was removed: classes/FontFile/properties/fallbacks +Validate extension JSON: API was removed: classes/FontVariation/properties/fallbacks +Validate extension JSON: API was removed: classes/SystemFont/properties/fallbacks + +The property was moved to their common base class Font. +The setters and getters were already in Font, so this shouldn't affect compatibility. + + +GH-36493 +-------- +Validate extension JSON: Error: Field 'classes/PopupMenu/methods/add_icon_shortcut/arguments': size changed value in new API, from 4 to 5. +Validate extension JSON: Error: Field 'classes/PopupMenu/methods/add_shortcut/arguments': size changed value in new API, from 3 to 4. + +Added optional argument. Compatibility methods registered. diff --git a/misc/scripts/clang_format.sh b/misc/scripts/clang_format.sh index 74a8e1a8a3..40d94d4276 100755 --- a/misc/scripts/clang_format.sh +++ b/misc/scripts/clang_format.sh @@ -21,7 +21,7 @@ fi # Fix copyright headers, but not all files get them. for f in $files; do - if [[ "$f" == *"inc" ]]; then + if [[ "$f" == *"inc" && "$f" != *"compat.inc" ]]; then continue elif [[ "$f" == *"glsl" ]]; then continue diff --git a/misc/scripts/validate_extension_api.sh b/misc/scripts/validate_extension_api.sh index e06d52115a..f2f7c28e70 100755 --- a/misc/scripts/validate_extension_api.sh +++ b/misc/scripts/validate_extension_api.sh @@ -1,5 +1,5 @@ #!/bin/bash -set -uo pipefail +set -o pipefail if [ ! -f "version.py" ]; then echo "Warning: This script is intended to be run from the root of the Godot repository." @@ -18,7 +18,7 @@ make_annotation() local body=$2 local type=$3 local file=$4 - if [ ! -v GITHUB_OUTPUT ]; then + if [[ "$GITHUB_OUTPUT" == "" ]]; then echo "$title" echo "$body" else @@ -43,8 +43,8 @@ while read -r file; do awk '/^Validate extension JSON:/' - < "$file" | sort > "$allowed_errors" # Differences between the expected and actual errors - new_validation_error="$(comm "$validation_output" "$allowed_errors" -23)" - obsolete_validation_error="$(comm "$validation_output" "$allowed_errors" -13)" + new_validation_error="$(comm -23 "$validation_output" "$allowed_errors")" + obsolete_validation_error="$(comm -13 "$validation_output" "$allowed_errors")" if [ -n "$obsolete_validation_error" ]; then make_annotation "The following validation errors no longer occur (compared to $reference_tag):" "$obsolete_validation_error" warning "$file" diff --git a/modules/csg/doc_classes/CSGMesh3D.xml b/modules/csg/doc_classes/CSGMesh3D.xml index 9a0f121e19..96d89ff486 100644 --- a/modules/csg/doc_classes/CSGMesh3D.xml +++ b/modules/csg/doc_classes/CSGMesh3D.xml @@ -16,7 +16,8 @@ </member> <member name="mesh" type="Mesh" setter="set_mesh" getter="get_mesh"> The [Mesh] resource to use as a CSG shape. - [b]Note:[/b] When using an [ArrayMesh], avoid meshes with vertex normals unless a flat shader is required. By default, CSGMesh will ignore the mesh's vertex normals and use a smooth shader calculated using the faces' normals. If a flat shader is required, ensure that all faces' vertex normals are parallel. + [b]Note:[/b] When using an [ArrayMesh], all vertex attributes except [constant Mesh.ARRAY_VERTEX], [constant Mesh.ARRAY_NORMAL] and [constant Mesh.ARRAY_TEX_UV] are left unused. Only [constant Mesh.ARRAY_VERTEX] and [constant Mesh.ARRAY_TEX_UV] will be passed to the GPU. + [constant Mesh.ARRAY_NORMAL] is only used to determine which faces require the use of flat shading. By default, CSGMesh will ignore the mesh's vertex normals, recalculate them for each vertex and use a smooth shader. If a flat shader is required for a face, ensure that all vertex normals of the face are approximately equal. </member> </members> </class> diff --git a/modules/denoise/SCsub b/modules/denoise/SCsub index 779ce165d2..967a511e1e 100644 --- a/modules/denoise/SCsub +++ b/modules/denoise/SCsub @@ -109,6 +109,15 @@ env_oidn.AppendUnique(CPPDEFINES=["NDEBUG"]) # No assert() even in debug builds env_thirdparty = env_oidn.Clone() env_thirdparty.disable_warnings() + +if env["disable_exceptions"]: + # OIDN hard-requires exceptions, so we re-enable them here. + if env.msvc and ("_HAS_EXCEPTIONS", 0) in env_thirdparty["CPPDEFINES"]: + env_thirdparty["CPPDEFINES"].remove(("_HAS_EXCEPTIONS", 0)) + env_thirdparty.AppendUnique(CCFLAGS=["/EHsc"]) + elif not env.msvc and "-fno-exceptions" in env_thirdparty["CCFLAGS"]: + env_thirdparty["CCFLAGS"].remove("-fno-exceptions") + env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) env.modules_sources += thirdparty_obj diff --git a/modules/enet/enet_connection.cpp b/modules/enet/enet_connection.cpp index 0ace89caa5..88aaa006b5 100644 --- a/modules/enet/enet_connection.cpp +++ b/modules/enet/enet_connection.cpp @@ -142,7 +142,7 @@ ENetConnection::EventType ENetConnection::_parse_event(const ENetEvent &p_event, return EVENT_ERROR; } break; case ENET_EVENT_TYPE_RECEIVE: { - // Packet reveived. + // Packet received. if (p_event.peer->data != nullptr) { Ref<ENetPacketPeer> pp = Ref<ENetPacketPeer>((ENetPacketPeer *)p_event.peer->data); r_event.peer = Ref<ENetPacketPeer>((ENetPacketPeer *)p_event.peer->data); diff --git a/modules/enet/enet_multiplayer_peer.cpp b/modules/enet/enet_multiplayer_peer.cpp index af5bd2929c..63f12ea1c1 100644 --- a/modules/enet/enet_multiplayer_peer.cpp +++ b/modules/enet/enet_multiplayer_peer.cpp @@ -343,23 +343,22 @@ Error ENetMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size int packet_flags = 0; int channel = SYSCH_RELIABLE; int tr_channel = get_transfer_channel(); + switch (get_transfer_mode()) { + case TRANSFER_MODE_UNRELIABLE: { + packet_flags = ENET_PACKET_FLAG_UNSEQUENCED | ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT; + channel = SYSCH_UNRELIABLE; + } break; + case TRANSFER_MODE_UNRELIABLE_ORDERED: { + packet_flags = ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT; + channel = SYSCH_UNRELIABLE; + } break; + case TRANSFER_MODE_RELIABLE: { + packet_flags = ENET_PACKET_FLAG_RELIABLE; + channel = SYSCH_RELIABLE; + } break; + } if (tr_channel > 0) { channel = SYSCH_MAX + tr_channel - 1; - } else { - switch (get_transfer_mode()) { - case TRANSFER_MODE_UNRELIABLE: { - packet_flags = ENET_PACKET_FLAG_UNSEQUENCED | ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT; - channel = SYSCH_UNRELIABLE; - } break; - case TRANSFER_MODE_UNRELIABLE_ORDERED: { - packet_flags = ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT; - channel = SYSCH_UNRELIABLE; - } break; - case TRANSFER_MODE_RELIABLE: { - packet_flags = ENET_PACKET_FLAG_RELIABLE; - channel = SYSCH_RELIABLE; - } break; - } } #ifdef DEBUG_ENABLED diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index b54dc502ae..862799a6ec 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -145,6 +145,10 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l // Check if it's the whole line. if (end_key_length == 0 || color_regions[c].line_only || from + end_key_length > line_length) { + // Don't skip comments, for highlighting markers. + if (color_regions[in_region].start_key == "#") { + break; + } if (from + end_key_length > line_length) { // If it's key length and there is a '\', dont skip to highlight esc chars. if (str.find("\\", from) >= 0) { @@ -163,7 +167,8 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l break; } - if (j == line_length) { + // Don't skip comments, for highlighting markers. + if (j == line_length && color_regions[in_region].start_key != "#") { continue; } } @@ -185,56 +190,83 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l highlighter_info["color"] = region_color; color_map[j] = highlighter_info; - // Search the line. - int region_end_index = -1; - int end_key_length = color_regions[in_region].end_key.length(); - const char32_t *end_key = color_regions[in_region].end_key.get_data(); - for (; from < line_length; from++) { - if (line_length - from < end_key_length) { - // Don't break if '\' to highlight esc chars. - if (str.find("\\", from) < 0) { - break; + if (color_regions[in_region].start_key == "#") { + int marker_start_pos = from; + int marker_len = 0; + while (from <= line_length) { + if (from < line_length && is_unicode_identifier_continue(str[from])) { + marker_len++; + } else { + if (marker_len > 0) { + HashMap<String, CommentMarkerLevel>::ConstIterator E = comment_markers.find(str.substr(marker_start_pos, marker_len)); + if (E) { + Dictionary marker_highlighter_info; + marker_highlighter_info["color"] = comment_marker_colors[E->value]; + color_map[marker_start_pos] = marker_highlighter_info; + + Dictionary marker_continue_highlighter_info; + marker_continue_highlighter_info["color"] = region_color; + color_map[from] = marker_continue_highlighter_info; + } + } + marker_start_pos = from + 1; + marker_len = 0; } + from++; } + from = line_length - 1; + j = from; + } else { + // Search the line. + int region_end_index = -1; + int end_key_length = color_regions[in_region].end_key.length(); + const char32_t *end_key = color_regions[in_region].end_key.get_data(); + for (; from < line_length; from++) { + if (line_length - from < end_key_length) { + // Don't break if '\' to highlight esc chars. + if (str.find("\\", from) < 0) { + break; + } + } - if (!is_symbol(str[from])) { - continue; - } + if (!is_symbol(str[from])) { + continue; + } - if (str[from] == '\\') { - Dictionary escape_char_highlighter_info; - escape_char_highlighter_info["color"] = symbol_color; - color_map[from] = escape_char_highlighter_info; + if (str[from] == '\\') { + Dictionary escape_char_highlighter_info; + escape_char_highlighter_info["color"] = symbol_color; + color_map[from] = escape_char_highlighter_info; - from++; + from++; - Dictionary region_continue_highlighter_info; - prev_color = region_color; - region_continue_highlighter_info["color"] = region_color; - color_map[from + 1] = region_continue_highlighter_info; - continue; - } + Dictionary region_continue_highlighter_info; + region_continue_highlighter_info["color"] = region_color; + color_map[from + 1] = region_continue_highlighter_info; + continue; + } - region_end_index = from; - for (int k = 0; k < end_key_length; k++) { - if (end_key[k] != str[from + k]) { - region_end_index = -1; + region_end_index = from; + for (int k = 0; k < end_key_length; k++) { + if (end_key[k] != str[from + k]) { + region_end_index = -1; + break; + } + } + + if (region_end_index != -1) { break; } } - - if (region_end_index != -1) { - break; + j = from + (end_key_length - 1); + if (region_end_index == -1) { + color_region_cache[p_line] = in_region; } } prev_type = REGION; prev_text = ""; prev_column = j; - j = from + (end_key_length - 1); - if (region_end_index == -1) { - color_region_cache[p_line] = in_region; - } in_region = -1; prev_is_char = false; @@ -706,6 +738,9 @@ void GDScriptSyntaxHighlighter::_update_cache() { node_ref_color = Color(0.39, 0.76, 0.35); annotation_color = Color(1.0, 0.7, 0.45); string_name_color = Color(1.0, 0.76, 0.65); + comment_marker_colors[COMMENT_MARKER_CRITICAL] = Color(0.77, 0.35, 0.35); + comment_marker_colors[COMMENT_MARKER_WARNING] = Color(0.72, 0.61, 0.48); + comment_marker_colors[COMMENT_MARKER_NOTICE] = Color(0.56, 0.67, 0.51); } else { function_definition_color = Color(0, 0.6, 0.6); global_function_color = Color(0.36, 0.18, 0.72); @@ -713,6 +748,9 @@ void GDScriptSyntaxHighlighter::_update_cache() { node_ref_color = Color(0.0, 0.5, 0); annotation_color = Color(0.8, 0.37, 0); string_name_color = Color(0.8, 0.56, 0.45); + comment_marker_colors[COMMENT_MARKER_CRITICAL] = Color(0.8, 0.14, 0.14); + comment_marker_colors[COMMENT_MARKER_WARNING] = Color(0.75, 0.39, 0.03); + comment_marker_colors[COMMENT_MARKER_NOTICE] = Color(0.24, 0.54, 0.09); } EDITOR_DEF("text_editor/theme/highlighting/gdscript/function_definition_color", function_definition_color); @@ -721,6 +759,14 @@ void GDScriptSyntaxHighlighter::_update_cache() { EDITOR_DEF("text_editor/theme/highlighting/gdscript/node_reference_color", node_ref_color); EDITOR_DEF("text_editor/theme/highlighting/gdscript/annotation_color", annotation_color); EDITOR_DEF("text_editor/theme/highlighting/gdscript/string_name_color", string_name_color); + EDITOR_DEF("text_editor/theme/highlighting/comment_markers/critical_color", comment_marker_colors[COMMENT_MARKER_CRITICAL]); + EDITOR_DEF("text_editor/theme/highlighting/comment_markers/warning_color", comment_marker_colors[COMMENT_MARKER_WARNING]); + EDITOR_DEF("text_editor/theme/highlighting/comment_markers/notice_color", comment_marker_colors[COMMENT_MARKER_NOTICE]); + // The list is based on <https://github.com/KDE/syntax-highlighting/blob/master/data/syntax/alert.xml>. + EDITOR_DEF("text_editor/theme/highlighting/comment_markers/critical_list", "ALERT,ATTENTION,CAUTION,CRITICAL,DANGER,SECURITY"); + EDITOR_DEF("text_editor/theme/highlighting/comment_markers/warning_list", "BUG,DEPRECATED,FIXME,HACK,TASK,TBD,TODO,WARNING"); + EDITOR_DEF("text_editor/theme/highlighting/comment_markers/notice_list", "INFO,NOTE,NOTICE,TEST,TESTING"); + if (text_edit_color_theme == "Default" || godot_2_theme) { EditorSettings::get_singleton()->set_initial_value( "text_editor/theme/highlighting/gdscript/function_definition_color", @@ -746,6 +792,18 @@ void GDScriptSyntaxHighlighter::_update_cache() { "text_editor/theme/highlighting/gdscript/string_name_color", string_name_color, true); + EditorSettings::get_singleton()->set_initial_value( + "text_editor/theme/highlighting/comment_markers/critical_color", + comment_marker_colors[COMMENT_MARKER_CRITICAL], + true); + EditorSettings::get_singleton()->set_initial_value( + "text_editor/theme/highlighting/comment_markers/warning_color", + comment_marker_colors[COMMENT_MARKER_WARNING], + true); + EditorSettings::get_singleton()->set_initial_value( + "text_editor/theme/highlighting/comment_markers/notice_color", + comment_marker_colors[COMMENT_MARKER_NOTICE], + true); } function_definition_color = EDITOR_GET("text_editor/theme/highlighting/gdscript/function_definition_color"); @@ -755,6 +813,23 @@ void GDScriptSyntaxHighlighter::_update_cache() { annotation_color = EDITOR_GET("text_editor/theme/highlighting/gdscript/annotation_color"); string_name_color = EDITOR_GET("text_editor/theme/highlighting/gdscript/string_name_color"); type_color = EDITOR_GET("text_editor/theme/highlighting/base_type_color"); + comment_marker_colors[COMMENT_MARKER_CRITICAL] = EDITOR_GET("text_editor/theme/highlighting/comment_markers/critical_color"); + comment_marker_colors[COMMENT_MARKER_WARNING] = EDITOR_GET("text_editor/theme/highlighting/comment_markers/warning_color"); + comment_marker_colors[COMMENT_MARKER_NOTICE] = EDITOR_GET("text_editor/theme/highlighting/comment_markers/notice_color"); + + comment_markers.clear(); + Vector<String> critical_list = EDITOR_GET("text_editor/theme/highlighting/comment_markers/critical_list").operator String().split(",", false); + for (int i = 0; i < critical_list.size(); i++) { + comment_markers[critical_list[i]] = COMMENT_MARKER_CRITICAL; + } + Vector<String> warning_list = EDITOR_GET("text_editor/theme/highlighting/comment_markers/warning_list").operator String().split(",", false); + for (int i = 0; i < warning_list.size(); i++) { + comment_markers[warning_list[i]] = COMMENT_MARKER_WARNING; + } + Vector<String> notice_list = EDITOR_GET("text_editor/theme/highlighting/comment_markers/notice_list").operator String().split(",", false); + for (int i = 0; i < notice_list.size(); i++) { + comment_markers[notice_list[i]] = COMMENT_MARKER_NOTICE; + } } void GDScriptSyntaxHighlighter::add_color_region(const String &p_start_key, const String &p_end_key, const Color &p_color, bool p_line_only) { diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h index aceb644658..fe3b63d713 100644 --- a/modules/gdscript/editor/gdscript_highlighter.h +++ b/modules/gdscript/editor/gdscript_highlighter.h @@ -84,6 +84,15 @@ private: Color string_name_color; Color type_color; + enum CommentMarkerLevel { + COMMENT_MARKER_CRITICAL, + COMMENT_MARKER_WARNING, + COMMENT_MARKER_NOTICE, + COMMENT_MARKER_MAX, + }; + Color comment_marker_colors[COMMENT_MARKER_MAX]; + HashMap<String, CommentMarkerLevel> comment_markers; + void add_color_region(const String &p_start_key, const String &p_end_key, const Color &p_color, bool p_line_only = false); public: diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 0bf9f72a2c..42b08f8a68 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -2695,6 +2695,11 @@ Ref<Resource> ResourceFormatLoaderGDScript::load(const String &p_path, const Str Error err; Ref<GDScript> scr = GDScriptCache::get_full_script(p_path, err, "", p_cache_mode == CACHE_MODE_IGNORE); + if (err && scr.is_valid()) { + // If !scr.is_valid(), the error was likely from scr->load_source_code(), which already generates an error. + ERR_PRINT_ED(vformat(R"(Failed to load script "%s" with error "%s".)", p_path, error_names[err])); + } + if (r_error) { // Don't fail loading because of parsing error. *r_error = scr.is_valid() ? OK : err; diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index cb04913620..9f9accf507 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -3301,17 +3301,26 @@ void GDScriptAnalyzer::reduce_dictionary(GDScriptParser::DictionaryNode *p_dicti void GDScriptAnalyzer::reduce_get_node(GDScriptParser::GetNodeNode *p_get_node) { GDScriptParser::DataType result; - result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; - result.kind = GDScriptParser::DataType::NATIVE; - result.native_type = SNAME("Node"); - result.builtin_type = Variant::OBJECT; + result.kind = GDScriptParser::DataType::VARIANT; - if (!ClassDB::is_parent_class(parser->current_class->base_type.native_type, result.native_type)) { - push_error(R"*(Cannot use shorthand "get_node()" notation ("$") on a class that isn't a node.)*", p_get_node); + if (!ClassDB::is_parent_class(parser->current_class->base_type.native_type, SNAME("Node"))) { + push_error(vformat(R"*(Cannot use shorthand "get_node()" notation ("%c") on a class that isn't a node.)*", p_get_node->use_dollar ? '$' : '%'), p_get_node); + p_get_node->set_datatype(result); + return; + } + + if (static_context) { + push_error(vformat(R"*(Cannot use shorthand "get_node()" notation ("%c") in a static function.)*", p_get_node->use_dollar ? '$' : '%'), p_get_node); + p_get_node->set_datatype(result); + return; } mark_lambda_use_self(); + result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; + result.kind = GDScriptParser::DataType::NATIVE; + result.builtin_type = Variant::OBJECT; + result.native_type = SNAME("Node"); p_get_node->set_datatype(result); } @@ -3469,6 +3478,9 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod for (GDScriptParser::ClassNode *script_class : script_classes) { if (p_base == nullptr && script_class->identifier && script_class->identifier->name == name) { reduce_identifier_from_base_set_class(p_identifier, script_class->get_datatype()); + if (script_class->outer != nullptr) { + p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CLASS; + } return; } diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 2a52db4158..3366fa2eec 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -245,6 +245,8 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code // MEMBERS. case GDScriptParser::IdentifierNode::MEMBER_VARIABLE: + case GDScriptParser::IdentifierNode::MEMBER_FUNCTION: + case GDScriptParser::IdentifierNode::MEMBER_SIGNAL: case GDScriptParser::IdentifierNode::INHERITED_VARIABLE: { // Try class members. if (_is_class_member_property(codegen, identifier)) { @@ -271,45 +273,44 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code } } } - } break; - case GDScriptParser::IdentifierNode::MEMBER_FUNCTION: - case GDScriptParser::IdentifierNode::MEMBER_SIGNAL: { - // Try methods and signals (can be Callable and Signal). - // Search upwards through parent classes: - const GDScriptParser::ClassNode *base_class = codegen.class_node; - while (base_class != nullptr) { - if (base_class->has_member(identifier)) { - const GDScriptParser::ClassNode::Member &member = base_class->get_member(identifier); - if (member.type == GDScriptParser::ClassNode::Member::FUNCTION || member.type == GDScriptParser::ClassNode::Member::SIGNAL) { - // Get like it was a property. - GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here. - GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF); - - gen->write_get_named(temp, identifier, self); - return temp; + // Try methods and signals (can be Callable and Signal). + { + // Search upwards through parent classes: + const GDScriptParser::ClassNode *base_class = codegen.class_node; + while (base_class != nullptr) { + if (base_class->has_member(identifier)) { + const GDScriptParser::ClassNode::Member &member = base_class->get_member(identifier); + if (member.type == GDScriptParser::ClassNode::Member::FUNCTION || member.type == GDScriptParser::ClassNode::Member::SIGNAL) { + // Get like it was a property. + GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here. + GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF); + + gen->write_get_named(temp, identifier, self); + return temp; + } } + base_class = base_class->base_type.class_type; } - base_class = base_class->base_type.class_type; - } - // Try in native base. - GDScript *scr = codegen.script; - GDScriptNativeClass *nc = nullptr; - while (scr) { - if (scr->native.is_valid()) { - nc = scr->native.ptr(); + // Try in native base. + GDScript *scr = codegen.script; + GDScriptNativeClass *nc = nullptr; + while (scr) { + if (scr->native.is_valid()) { + nc = scr->native.ptr(); + } + scr = scr->_base; } - scr = scr->_base; - } - if (nc && (ClassDB::has_signal(nc->get_name(), identifier) || ClassDB::has_method(nc->get_name(), identifier))) { - // Get like it was a property. - GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here. - GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF); + if (nc && (ClassDB::has_signal(nc->get_name(), identifier) || ClassDB::has_method(nc->get_name(), identifier))) { + // Get like it was a property. + GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here. + GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF); - gen->write_get_named(temp, identifier, self); - return temp; + gen->write_get_named(temp, identifier, self); + return temp; + } } } break; case GDScriptParser::IdentifierNode::MEMBER_CONSTANT: @@ -319,6 +320,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code while (owner) { GDScript *scr = owner; GDScriptNativeClass *nc = nullptr; + while (scr) { if (scr->constants.has(identifier)) { return codegen.add_constant(scr->constants[identifier]); // TODO: Get type here. diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index d27ea974e3..3019354d93 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -1076,7 +1076,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base List<PropertyInfo> members; scr->get_script_property_list(&members); for (const PropertyInfo &E : members) { - int location = p_recursion_depth + _get_property_location(scr->get_class_name(), E.class_name); + int location = p_recursion_depth + _get_property_location(scr->get_class_name(), E.name); ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location); r_result.insert(option.display, option); } @@ -1150,7 +1150,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base if (E.name.contains("/")) { continue; } - int location = p_recursion_depth + _get_property_location(type, E.class_name); + int location = p_recursion_depth + _get_property_location(type, E.name); ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location); r_result.insert(option.display, option); } diff --git a/modules/gdscript/gdscript_lambda_callable.cpp b/modules/gdscript/gdscript_lambda_callable.cpp index 9e14e43a58..3b89f077bd 100644 --- a/modules/gdscript/gdscript_lambda_callable.cpp +++ b/modules/gdscript/gdscript_lambda_callable.cpp @@ -67,6 +67,10 @@ ObjectID GDScriptLambdaCallable::get_object() const { return script->get_instance_id(); } +StringName GDScriptLambdaCallable::get_method() const { + return function->get_name(); +} + void GDScriptLambdaCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { int captures_amount = captures.size(); diff --git a/modules/gdscript/gdscript_lambda_callable.h b/modules/gdscript/gdscript_lambda_callable.h index 33bdf6dfc1..1c7a18fb9d 100644 --- a/modules/gdscript/gdscript_lambda_callable.h +++ b/modules/gdscript/gdscript_lambda_callable.h @@ -56,6 +56,7 @@ public: CompareEqualFunc get_compare_equal_func() const override; CompareLessFunc get_compare_less_func() const override; ObjectID get_object() const override; + StringName get_method() const override; void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override; GDScriptLambdaCallable(Ref<GDScript> p_script, GDScriptFunction *p_function, const Vector<Variant> &p_captures); diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index debc85ebbf..b76ceea11f 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -2446,7 +2446,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_operator(Expression complete_extents(operation); if (operation->right_operand == nullptr) { - push_error(vformat(R"(Expected expression after "%s" operator.")", op.get_name())); + push_error(vformat(R"(Expected expression after "%s" operator.)", op.get_name())); } // TODO: Also for unary, ternary, and assignment. @@ -3035,10 +3035,8 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p if (previous.type == GDScriptTokenizer::Token::DOLLAR) { // Detect initial slash, which will be handled in the loop if it matches. match(GDScriptTokenizer::Token::SLASH); -#ifdef DEBUG_ENABLED } else { get_node->use_dollar = false; -#endif } int context_argument = 0; diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 20f5dcf06d..71660d8f60 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -848,9 +848,7 @@ public: struct GetNodeNode : public ExpressionNode { String full_path; -#ifdef DEBUG_ENABLED bool use_dollar = true; -#endif GetNodeNode() { type = GET_NODE; diff --git a/modules/gdscript/gdscript_rpc_callable.cpp b/modules/gdscript/gdscript_rpc_callable.cpp index a4dd8a8d3c..265e624b6c 100644 --- a/modules/gdscript/gdscript_rpc_callable.cpp +++ b/modules/gdscript/gdscript_rpc_callable.cpp @@ -63,6 +63,10 @@ ObjectID GDScriptRPCCallable::get_object() const { return object->get_instance_id(); } +StringName GDScriptRPCCallable::get_method() const { + return method; +} + void GDScriptRPCCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { r_return_value = object->callp(method, p_arguments, p_argcount, r_call_error); } diff --git a/modules/gdscript/gdscript_rpc_callable.h b/modules/gdscript/gdscript_rpc_callable.h index c1007b18b0..66052157be 100644 --- a/modules/gdscript/gdscript_rpc_callable.h +++ b/modules/gdscript/gdscript_rpc_callable.h @@ -51,6 +51,7 @@ public: CompareEqualFunc get_compare_equal_func() const override; CompareLessFunc get_compare_less_func() const override; ObjectID get_object() const override; + StringName get_method() const override; void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override; Error rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const override; diff --git a/modules/gdscript/language_server/godot_lsp.h b/modules/gdscript/language_server/godot_lsp.h index b9a54cf818..3782945e07 100644 --- a/modules/gdscript/language_server/godot_lsp.h +++ b/modules/gdscript/language_server/godot_lsp.h @@ -1566,7 +1566,7 @@ struct SignatureHelp { /** * The active signature. If omitted or the value lies outside the * range of `signatures` the value defaults to zero or is ignored if - * `signatures.length === 0`. Whenever possible implementors should + * `signatures.length === 0`. Whenever possible implementers should * make an active decision about the active signature and shouldn't * rely on a default value. * In future version of the protocol this property might become diff --git a/modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_in_static_function.gd b/modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_in_static_function.gd new file mode 100644 index 0000000000..caeea46977 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_in_static_function.gd @@ -0,0 +1,9 @@ +# GH-75645 + +extends Node + +static func static_func(): + var a = $Node + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_in_static_function.out b/modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_in_static_function.out new file mode 100644 index 0000000000..1910b3e66b --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_in_static_function.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot use shorthand "get_node()" notation ("$") in a static function. diff --git a/modules/gdscript/tests/scripts/analyzer/features/inner_class_access_from_inside.gd b/modules/gdscript/tests/scripts/analyzer/features/inner_class_access_from_inside.gd new file mode 100644 index 0000000000..51c589b8e0 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/inner_class_access_from_inside.gd @@ -0,0 +1,21 @@ +# GH-80508 + +class A: + func a(): + return A.new() + func b(): + return B.new() + +class B: + func a(): + return A.new() + func b(): + return B.new() + +func test(): + var a := A.new() + var b := B.new() + print(a.a() is A) + print(a.b() is B) + print(b.a() is A) + print(b.b() is B) diff --git a/modules/gdscript/tests/scripts/analyzer/features/inner_class_access_from_inside.out b/modules/gdscript/tests/scripts/analyzer/features/inner_class_access_from_inside.out new file mode 100644 index 0000000000..f9783e4362 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/inner_class_access_from_inside.out @@ -0,0 +1,5 @@ +GDTEST_OK +true +true +true +true diff --git a/modules/gdscript/tests/scripts/runtime/features/first_class_callable_and_signal.gd b/modules/gdscript/tests/scripts/runtime/features/first_class_callable_and_signal.gd new file mode 100644 index 0000000000..f17fb9823d --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/first_class_callable_and_signal.gd @@ -0,0 +1,14 @@ +# GH-80157 + +extends Node + +func f(): + pass + +signal s() + +func test(): + print(f) + print(s) + print(get_child) + print(ready) diff --git a/modules/gdscript/tests/scripts/runtime/features/first_class_callable_and_signal.out b/modules/gdscript/tests/scripts/runtime/features/first_class_callable_and_signal.out new file mode 100644 index 0000000000..e5e9ff7043 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/first_class_callable_and_signal.out @@ -0,0 +1,5 @@ +GDTEST_OK +Node::f +Node::[signal]s +Node::get_child +Node::[signal]ready diff --git a/modules/glslang/register_types.cpp b/modules/glslang/register_types.cpp index 622910761d..2b070c24b8 100644 --- a/modules/glslang/register_types.cpp +++ b/modules/glslang/register_types.cpp @@ -32,6 +32,7 @@ #include "glslang_resource_limits.h" +#include "core/config/engine.h" #include "servers/rendering/rendering_device.h" #include <glslang/Include/Types.h> @@ -56,7 +57,6 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage glslang::EShTargetClientVersion ClientVersion = glslang::EShTargetVulkan_1_2; glslang::EShTargetLanguageVersion TargetVersion = glslang::EShTargetSpv_1_5; - glslang::TShader::ForbidIncluder includer; if (capabilities->device_family == RenderingDevice::DeviceFamily::DEVICE_VULKAN) { if (capabilities->version_major == 1 && capabilities->version_minor == 0) { @@ -127,23 +127,10 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage } EShMessages messages = (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules); - const int DefaultVersion = 100; - std::string pre_processed_code; - - //preprocess - if (!shader.preprocess(&DefaultTBuiltInResource, DefaultVersion, ENoProfile, false, false, messages, &pre_processed_code, includer)) { - if (r_error) { - (*r_error) = "Failed pre-process:\n"; - (*r_error) += shader.getInfoLog(); - (*r_error) += "\n"; - (*r_error) += shader.getInfoDebugLog(); - } - - return ret; + if (Engine::get_singleton()->is_generate_spirv_debug_info_enabled()) { + messages = (EShMessages)(messages | EShMsgDebugInfo); } - //set back.. - cs_strings = pre_processed_code.c_str(); - shader.setStrings(&cs_strings, 1); + const int DefaultVersion = 100; //parse if (!shader.parse(&DefaultTBuiltInResource, DefaultVersion, false, messages)) { @@ -174,6 +161,13 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage std::vector<uint32_t> SpirV; spv::SpvBuildLogger logger; glslang::SpvOptions spvOptions; + + if (Engine::get_singleton()->is_generate_spirv_debug_info_enabled()) { + spvOptions.generateDebugInfo = true; + spvOptions.emitNonSemanticShaderDebugInfo = true; + spvOptions.emitNonSemanticShaderDebugSource = true; + } + glslang::GlslangToSpv(*program.getIntermediate(stages[p_stage]), SpirV, &logger, &spvOptions); ret.resize(SpirV.size() * sizeof(uint32_t)); @@ -188,7 +182,7 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage static String _get_cache_key_function_glsl(const RenderingDevice *p_render_device) { const RD::Capabilities *capabilities = p_render_device->get_device_capabilities(); String version; - version = "SpirVGen=" + itos(glslang::GetSpirvGeneratorVersion()) + ", major=" + itos(capabilities->version_major) + ", minor=" + itos(capabilities->version_minor) + " , subgroup_size=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_SIZE)) + " , subgroup_ops=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_OPERATIONS)) + " , subgroup_in_shaders=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_IN_SHADERS)); + version = "SpirVGen=" + itos(glslang::GetSpirvGeneratorVersion()) + ", major=" + itos(capabilities->version_major) + ", minor=" + itos(capabilities->version_minor) + " , subgroup_size=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_SIZE)) + " , subgroup_ops=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_OPERATIONS)) + " , subgroup_in_shaders=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_IN_SHADERS)) + " , debug=" + itos(Engine::get_singleton()->is_generate_spirv_debug_info_enabled()); return version; } diff --git a/modules/gltf/doc_classes/GLTFDocument.xml b/modules/gltf/doc_classes/GLTFDocument.xml index 5bc6081803..9b760a997a 100644 --- a/modules/gltf/doc_classes/GLTFDocument.xml +++ b/modules/gltf/doc_classes/GLTFDocument.xml @@ -1,11 +1,16 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GLTFDocument" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> + Class for importing and exporting glTF files in and out of Godot. </brief_description> <description> - Append a glTF2 3d format from a file, buffer or scene and then write to the filesystem, buffer or scene. + GLTFDocument supports reading data from a glTF file, buffer, or Godot scene. This data can then be written to the filesystem, buffer, or used to create a Godot scene. + All of the data in a GLTF scene is stored in the [GLTFState] class. GLTFDocument processes state objects, but does not contain any scene data itself. + GLTFDocument can be extended with arbitrary functionality by extending the [GLTFDocumentExtension] class and registering it with GLTFDocument via [method register_gltf_document_extension]. This allows for custom data to be imported and exported. </description> <tutorials> + <link title="glTF 'What the duck?' guide">https://www.khronos.org/files/gltf20-reference-guide.pdf</link> + <link title="Khronos glTF specification">https://registry.khronos.org/glTF/</link> </tutorials> <methods> <method name="append_from_buffer"> diff --git a/modules/gltf/doc_classes/GLTFDocumentExtension.xml b/modules/gltf/doc_classes/GLTFDocumentExtension.xml index 927ffb6aae..bae980fb56 100644 --- a/modules/gltf/doc_classes/GLTFDocumentExtension.xml +++ b/modules/gltf/doc_classes/GLTFDocumentExtension.xml @@ -17,7 +17,7 @@ <param index="1" name="gltf_node" type="GLTFNode" /> <param index="2" name="scene_node" type="Node" /> <description> - Part of the export process. This method is run after [method _export_preflight] and before [method _export_node]. + Part of the export process. This method is run after [method _export_preflight] and before [method _export_preserialize]. Runs when converting the data from a Godot scene node. This method can be used to process the Godot scene node data into a format that can be used by [method _export_node]. </description> </method> @@ -28,7 +28,7 @@ <param index="2" name="json" type="Dictionary" /> <param index="3" name="node" type="Node" /> <description> - Part of the export process. This method is run after [method _convert_scene_node] and before [method _export_post]. + Part of the export process. This method is run after [method _export_preserialize] and before [method _export_post]. This method can be used to modify the final JSON of each node. </description> </method> @@ -49,6 +49,14 @@ The return value is used to determine if this [GLTFDocumentExtension] instance should be used for exporting a given GLTF file. If [constant OK], the export will use this [GLTFDocumentExtension] instance. If not overridden, [constant OK] is returned. </description> </method> + <method name="_export_preserialize" qualifiers="virtual"> + <return type="int" enum="Error" /> + <param index="0" name="state" type="GLTFState" /> + <description> + Part of the export process. This method is run after [method _convert_scene_node] and before [method _export_node]. + This method can be used to alter the state before performing serialization. It runs every time when generating a buffer with [method GLTFDocument.generate_buffer] or writing to the file system with [method GLTFDocument.write_to_filesystem]. + </description> + </method> <method name="_generate_scene_node" qualifiers="virtual"> <return type="Node3D" /> <param index="0" name="state" type="GLTFState" /> @@ -59,6 +67,12 @@ Runs when generating a Godot scene node from a GLTFNode. The returned node will be added to the scene tree. Multiple nodes can be generated in this step if they are added as a child of the returned node. </description> </method> + <method name="_get_image_file_extension" qualifiers="virtual"> + <return type="String" /> + <description> + Returns the file extension to use for saving image data into, for example, [code]".png"[/code]. If defined, when this extension is used to handle images, and the images are saved to a separate file, the image bytes will be copied to a file with this extension. If this is set, there should be a [ResourceImporter] class able to import the file. If not defined or empty, Godot will save the image into a PNG file. + </description> + </method> <method name="_get_supported_extensions" qualifiers="virtual"> <return type="PackedStringArray" /> <description> diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml index 44b0d124e7..ba1c531283 100644 --- a/modules/gltf/doc_classes/GLTFState.xml +++ b/modules/gltf/doc_classes/GLTFState.xml @@ -267,6 +267,7 @@ </methods> <members> <member name="base_path" type="String" setter="set_base_path" getter="get_base_path" default=""""> + The folder path associated with this GLTF data. This is used to find other files the GLTF file references, like images or binary buffers. This will be set during import when appending from a file, and will be set during export when writing to a file. </member> <member name="buffers" type="PackedByteArray[]" setter="set_buffers" getter="get_buffers" default="[]"> </member> @@ -275,6 +276,9 @@ </member> <member name="create_animations" type="bool" setter="set_create_animations" getter="get_create_animations" default="true"> </member> + <member name="filename" type="String" setter="set_filename" getter="get_filename" default=""""> + The file name associated with this GLTF data. If it ends with [code].gltf[/code], this is text-based GLTF, otherwise this is binary GLB. This will be set during import when appending from a file, and will be set during export when writing to a file. If writing to a buffer, this will be an empty string. + </member> <member name="glb_data" type="PackedByteArray" setter="set_glb_data" getter="get_glb_data" default="PackedByteArray()"> </member> <member name="json" type="Dictionary" setter="set_json" getter="get_json" default="{}"> diff --git a/modules/gltf/extensions/gltf_document_extension.cpp b/modules/gltf/extensions/gltf_document_extension.cpp index 2804a8b0a2..11718ba78a 100644 --- a/modules/gltf/extensions/gltf_document_extension.cpp +++ b/modules/gltf/extensions/gltf_document_extension.cpp @@ -36,6 +36,7 @@ void GLTFDocumentExtension::_bind_methods() { GDVIRTUAL_BIND(_get_supported_extensions); GDVIRTUAL_BIND(_parse_node_extensions, "state", "gltf_node", "extensions"); GDVIRTUAL_BIND(_parse_image_data, "state", "image_data", "mime_type", "ret_image"); + GDVIRTUAL_BIND(_get_image_file_extension); GDVIRTUAL_BIND(_parse_texture_json, "state", "texture_json", "ret_gltf_texture"); GDVIRTUAL_BIND(_generate_scene_node, "state", "gltf_node", "scene_parent"); GDVIRTUAL_BIND(_import_post_parse, "state"); @@ -44,6 +45,7 @@ void GLTFDocumentExtension::_bind_methods() { // Export process. GDVIRTUAL_BIND(_export_preflight, "state", "root"); GDVIRTUAL_BIND(_convert_scene_node, "state", "gltf_node", "scene_node"); + GDVIRTUAL_BIND(_export_preserialize, "state"); GDVIRTUAL_BIND(_export_node, "state", "gltf_node", "json", "node"); GDVIRTUAL_BIND(_export_post, "state"); } @@ -78,6 +80,12 @@ Error GLTFDocumentExtension::parse_image_data(Ref<GLTFState> p_state, const Pack return err; } +String GLTFDocumentExtension::get_image_file_extension() { + String ret; + GDVIRTUAL_CALL(_get_image_file_extension, ret); + return ret; +} + Error GLTFDocumentExtension::parse_texture_json(Ref<GLTFState> p_state, const Dictionary &p_texture_json, Ref<GLTFTexture> r_gltf_texture) { ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); ERR_FAIL_NULL_V(r_gltf_texture, ERR_INVALID_PARAMETER); @@ -134,6 +142,13 @@ void GLTFDocumentExtension::convert_scene_node(Ref<GLTFState> p_state, Ref<GLTFN GDVIRTUAL_CALL(_convert_scene_node, p_state, p_gltf_node, p_scene_node); } +Error GLTFDocumentExtension::export_preserialize(Ref<GLTFState> p_state) { + ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); + Error err = OK; + GDVIRTUAL_CALL(_export_preserialize, p_state, err); + return err; +} + Error GLTFDocumentExtension::export_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_dict, Node *p_node) { ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); ERR_FAIL_NULL_V(p_gltf_node, ERR_INVALID_PARAMETER); diff --git a/modules/gltf/extensions/gltf_document_extension.h b/modules/gltf/extensions/gltf_document_extension.h index d922588a29..0a631bb6c5 100644 --- a/modules/gltf/extensions/gltf_document_extension.h +++ b/modules/gltf/extensions/gltf_document_extension.h @@ -45,6 +45,7 @@ public: virtual Vector<String> get_supported_extensions(); virtual Error parse_node_extensions(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &p_extensions); virtual Error parse_image_data(Ref<GLTFState> p_state, const PackedByteArray &p_image_data, const String &p_mime_type, Ref<Image> r_image); + virtual String get_image_file_extension(); virtual Error parse_texture_json(Ref<GLTFState> p_state, const Dictionary &p_texture_json, Ref<GLTFTexture> r_gltf_texture); virtual Node3D *generate_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_parent); virtual Error import_post_parse(Ref<GLTFState> p_state); @@ -53,6 +54,7 @@ public: // Export process. virtual Error export_preflight(Ref<GLTFState> p_state, Node *p_root); virtual void convert_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_node); + virtual Error export_preserialize(Ref<GLTFState> p_state); virtual Error export_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_json, Node *p_node); virtual Error export_post(Ref<GLTFState> p_state); @@ -61,6 +63,7 @@ public: GDVIRTUAL0R(Vector<String>, _get_supported_extensions); GDVIRTUAL3R(Error, _parse_node_extensions, Ref<GLTFState>, Ref<GLTFNode>, Dictionary); GDVIRTUAL4R(Error, _parse_image_data, Ref<GLTFState>, PackedByteArray, String, Ref<Image>); + GDVIRTUAL0R(String, _get_image_file_extension); GDVIRTUAL3R(Error, _parse_texture_json, Ref<GLTFState>, Dictionary, Ref<GLTFTexture>); GDVIRTUAL3R(Node3D *, _generate_scene_node, Ref<GLTFState>, Ref<GLTFNode>, Node *); GDVIRTUAL1R(Error, _import_post_parse, Ref<GLTFState>); @@ -69,6 +72,7 @@ public: // Export process. GDVIRTUAL2R(Error, _export_preflight, Ref<GLTFState>, Node *); GDVIRTUAL3(_convert_scene_node, Ref<GLTFState>, Ref<GLTFNode>, Node *); + GDVIRTUAL1R(Error, _export_preserialize, Ref<GLTFState>); GDVIRTUAL4R(Error, _export_node, Ref<GLTFState>, Ref<GLTFNode>, Dictionary, Node *); GDVIRTUAL1R(Error, _export_post, Ref<GLTFState>); }; diff --git a/modules/gltf/extensions/gltf_document_extension_texture_webp.cpp b/modules/gltf/extensions/gltf_document_extension_texture_webp.cpp index ded4970968..73c869be3b 100644 --- a/modules/gltf/extensions/gltf_document_extension_texture_webp.cpp +++ b/modules/gltf/extensions/gltf_document_extension_texture_webp.cpp @@ -30,8 +30,6 @@ #include "gltf_document_extension_texture_webp.h" -#include "scene/3d/area_3d.h" - // Import process. Error GLTFDocumentExtensionTextureWebP::import_preflight(Ref<GLTFState> p_state, Vector<String> p_extensions) { if (!p_extensions.has("EXT_texture_webp")) { @@ -53,6 +51,10 @@ Error GLTFDocumentExtensionTextureWebP::parse_image_data(Ref<GLTFState> p_state, return OK; } +String GLTFDocumentExtensionTextureWebP::get_image_file_extension() { + return ".webp"; +} + Error GLTFDocumentExtensionTextureWebP::parse_texture_json(Ref<GLTFState> p_state, const Dictionary &p_texture_json, Ref<GLTFTexture> r_gltf_texture) { if (!p_texture_json.has("extensions")) { return OK; diff --git a/modules/gltf/extensions/gltf_document_extension_texture_webp.h b/modules/gltf/extensions/gltf_document_extension_texture_webp.h index 9abf09a41f..d2654aae8c 100644 --- a/modules/gltf/extensions/gltf_document_extension_texture_webp.h +++ b/modules/gltf/extensions/gltf_document_extension_texture_webp.h @@ -41,6 +41,7 @@ public: Error import_preflight(Ref<GLTFState> p_state, Vector<String> p_extensions) override; Vector<String> get_supported_extensions() override; Error parse_image_data(Ref<GLTFState> p_state, const PackedByteArray &p_image_data, const String &p_mime_type, Ref<Image> r_image) override; + String get_image_file_extension() override; Error parse_texture_json(Ref<GLTFState> p_state, const Dictionary &p_texture_json, Ref<GLTFTexture> r_gltf_texture) override; }; diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 4381a0e00d..9e7611bc5e 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -110,11 +110,17 @@ static Ref<ImporterMesh> _mesh_to_importer_mesh(Ref<Mesh> p_mesh) { return importer_mesh; } -Error GLTFDocument::_serialize(Ref<GLTFState> p_state, const String &p_path) { +Error GLTFDocument::_serialize(Ref<GLTFState> p_state) { if (!p_state->buffers.size()) { p_state->buffers.push_back(Vector<uint8_t>()); } + for (Ref<GLTFDocumentExtension> ext : document_extensions) { + ERR_CONTINUE(ext.is_null()); + Error err = ext->export_preserialize(p_state); + ERR_CONTINUE(err != OK); + } + /* STEP CONVERT MESH INSTANCES */ _convert_mesh_instances(p_state); @@ -161,7 +167,7 @@ Error GLTFDocument::_serialize(Ref<GLTFState> p_state, const String &p_path) { } /* STEP SERIALIZE IMAGES */ - err = _serialize_images(p_state, p_path); + err = _serialize_images(p_state); if (err != OK) { return Error::FAILED; } @@ -2995,17 +3001,32 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) { return OK; } -Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state, const String &p_path) { +Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state) { Array images; for (int i = 0; i < p_state->images.size(); i++) { - Dictionary d; + Dictionary image_dict; ERR_CONTINUE(p_state->images[i].is_null()); Ref<Image> image = p_state->images[i]->get_image(); ERR_CONTINUE(image.is_null()); - if (p_path.to_lower().ends_with("glb") || p_path.is_empty()) { + if (p_state->filename.to_lower().ends_with("gltf")) { + String img_name = p_state->images[i]->get_name(); + if (img_name.is_empty()) { + img_name = itos(i); + } + img_name = _gen_unique_name(p_state, img_name); + img_name = img_name.pad_zeros(3) + ".png"; + String relative_texture_dir = "textures"; + String full_texture_dir = p_state->base_path.path_join(relative_texture_dir); + Ref<DirAccess> da = DirAccess::open(p_state->base_path); + if (!da->dir_exists(full_texture_dir)) { + da->make_dir(full_texture_dir); + } + image->save_png(full_texture_dir.path_join(img_name)); + image_dict["uri"] = relative_texture_dir.path_join(img_name).uri_encode(); + } else { GLTFBufferViewIndex bvi; Ref<GLTFBufferView> bv; @@ -3031,27 +3052,10 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state, const String &p_pa p_state->buffer_views.push_back(bv); bvi = p_state->buffer_views.size() - 1; - d["bufferView"] = bvi; - d["mimeType"] = "image/png"; - } else { - ERR_FAIL_COND_V(p_path.is_empty(), ERR_INVALID_PARAMETER); - String img_name = p_state->images[i]->get_name(); - if (img_name.is_empty()) { - img_name = itos(i); - } - img_name = _gen_unique_name(p_state, img_name); - img_name = img_name.pad_zeros(3) + ".png"; - String texture_dir = "textures"; - String path = p_path.get_base_dir(); - String new_texture_dir = path + "/" + texture_dir; - Ref<DirAccess> da = DirAccess::open(path); - if (!da->dir_exists(new_texture_dir)) { - da->make_dir(new_texture_dir); - } - image->save_png(new_texture_dir.path_join(img_name)); - d["uri"] = texture_dir.path_join(img_name).uri_encode(); + image_dict["bufferView"] = bvi; + image_dict["mimeType"] = "image/png"; } - images.push_back(d); + images.push_back(image_dict); } print_verbose("Total images: " + itos(p_state->images.size())); @@ -3064,7 +3068,7 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state, const String &p_pa return OK; } -Ref<Image> GLTFDocument::_parse_image_bytes_into_image(Ref<GLTFState> p_state, const Vector<uint8_t> &p_bytes, const String &p_mime_type, int p_index) { +Ref<Image> GLTFDocument::_parse_image_bytes_into_image(Ref<GLTFState> p_state, const Vector<uint8_t> &p_bytes, const String &p_mime_type, int p_index, String &r_file_extension) { Ref<Image> r_image; r_image.instantiate(); // Check if any GLTFDocumentExtensions want to import this data as an image. @@ -3073,6 +3077,7 @@ Ref<Image> GLTFDocument::_parse_image_bytes_into_image(Ref<GLTFState> p_state, c Error err = ext->parse_image_data(p_state, p_bytes, p_mime_type, r_image); ERR_CONTINUE_MSG(err != OK, "GLTF: Encountered error " + itos(err) + " when parsing image " + itos(p_index) + " in file " + p_state->filename + ". Continuing."); if (!r_image->is_empty()) { + r_file_extension = ext->get_image_file_extension(); return r_image; } } @@ -3080,8 +3085,10 @@ Ref<Image> GLTFDocument::_parse_image_bytes_into_image(Ref<GLTFState> p_state, c // First we honor the mime types if they were defined. if (p_mime_type == "image/png") { // Load buffer as PNG. r_image->load_png_from_buffer(p_bytes); + r_file_extension = ".png"; } else if (p_mime_type == "image/jpeg") { // Loader buffer as JPEG. r_image->load_jpg_from_buffer(p_bytes); + r_file_extension = ".jpg"; } // If we didn't pass the above tests, we attempt loading as PNG and then JPEG directly. // This covers URIs with base64-encoded data with application/* type but @@ -3102,7 +3109,7 @@ Ref<Image> GLTFDocument::_parse_image_bytes_into_image(Ref<GLTFState> p_state, c return r_image; } -void GLTFDocument::_parse_image_save_image(Ref<GLTFState> p_state, const String &p_mime_type, int p_index, Ref<Image> p_image) { +void GLTFDocument::_parse_image_save_image(Ref<GLTFState> p_state, const Vector<uint8_t> &p_bytes, const String &p_file_extension, int p_index, Ref<Image> p_image) { GLTFState::GLTFHandleBinary handling = GLTFState::GLTFHandleBinary(p_state->handle_binary_image); if (p_image->is_empty() || handling == GLTFState::GLTFHandleBinary::HANDLE_BINARY_DISCARD_TEXTURES) { p_state->images.push_back(Ref<Texture2D>()); @@ -3119,11 +3126,11 @@ void GLTFDocument::_parse_image_save_image(Ref<GLTFState> p_state, const String p_state->images.push_back(Ref<Texture2D>()); p_state->source_images.push_back(Ref<Image>()); } else { - Error err = OK; bool must_import = true; Vector<uint8_t> img_data = p_image->get_data(); Dictionary generator_parameters; - String file_path = p_state->get_base_path() + "/" + p_state->filename.get_basename() + "_" + p_image->get_name() + ".png"; + String file_path = p_state->get_base_path() + "/" + p_state->filename.get_basename() + "_" + p_image->get_name(); + file_path += p_file_extension.is_empty() ? ".png" : p_file_extension; if (FileAccess::exists(file_path + ".import")) { Ref<ConfigFile> config; config.instantiate(); @@ -3144,8 +3151,18 @@ void GLTFDocument::_parse_image_save_image(Ref<GLTFState> p_state, const String } } if (must_import) { - err = p_image->save_png(file_path); - ERR_FAIL_COND(err != OK); + Error err = OK; + if (p_file_extension.is_empty()) { + // If a file extension was not specified, save the image data to a PNG file. + err = p_image->save_png(file_path); + ERR_FAIL_COND(err != OK); + } else { + // If a file extension was specified, save the original bytes to a file with that extension. + Ref<FileAccess> file = FileAccess::open(file_path, FileAccess::WRITE, &err); + ERR_FAIL_COND(err != OK); + file->store_buffer(p_bytes); + file->close(); + } // ResourceLoader::import will crash if not is_editor_hint(), so this case is protected above and will fall through to uncompressed. HashMap<StringName, Variant> custom_options; custom_options[SNAME("mipmaps/generate")] = true; @@ -3295,9 +3312,10 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p continue; } // Parse the image data from bytes into an Image resource and save if needed. - Ref<Image> img = _parse_image_bytes_into_image(p_state, data, mime_type, i); + String file_extension; + Ref<Image> img = _parse_image_bytes_into_image(p_state, data, mime_type, i, file_extension); img->set_name(image_name); - _parse_image_save_image(p_state, mime_type, i, img); + _parse_image_save_image(p_state, data, file_extension, i, img); } print_verbose("glTF: Total images: " + itos(p_state->images.size())); @@ -3312,16 +3330,16 @@ Error GLTFDocument::_serialize_textures(Ref<GLTFState> p_state) { Array textures; for (int32_t i = 0; i < p_state->textures.size(); i++) { - Dictionary d; - Ref<GLTFTexture> t = p_state->textures[i]; - ERR_CONTINUE(t->get_src_image() == -1); - d["source"] = t->get_src_image(); + Dictionary texture_dict; + Ref<GLTFTexture> gltf_texture = p_state->textures[i]; + ERR_CONTINUE(gltf_texture->get_src_image() == -1); + texture_dict["source"] = gltf_texture->get_src_image(); - GLTFTextureSamplerIndex sampler_index = t->get_sampler(); + GLTFTextureSamplerIndex sampler_index = gltf_texture->get_sampler(); if (sampler_index != -1) { - d["sampler"] = sampler_index; + texture_dict["sampler"] = sampler_index; } - textures.push_back(d); + textures.push_back(texture_dict); } p_state->json["textures"] = textures; @@ -3335,28 +3353,28 @@ Error GLTFDocument::_parse_textures(Ref<GLTFState> p_state) { const Array &textures = p_state->json["textures"]; for (GLTFTextureIndex i = 0; i < textures.size(); i++) { - const Dictionary &dict = textures[i]; - Ref<GLTFTexture> texture; - texture.instantiate(); + const Dictionary &texture_dict = textures[i]; + Ref<GLTFTexture> gltf_texture; + gltf_texture.instantiate(); // Check if any GLTFDocumentExtensions want to handle this texture JSON. for (Ref<GLTFDocumentExtension> ext : document_extensions) { ERR_CONTINUE(ext.is_null()); - Error err = ext->parse_texture_json(p_state, dict, texture); - ERR_CONTINUE_MSG(err != OK, "GLTF: Encountered error " + itos(err) + " when parsing texture JSON " + String(Variant(dict)) + " in file " + p_state->filename + ". Continuing."); - if (texture->get_src_image() != -1) { + Error err = ext->parse_texture_json(p_state, texture_dict, gltf_texture); + ERR_CONTINUE_MSG(err != OK, "GLTF: Encountered error " + itos(err) + " when parsing texture JSON " + String(Variant(texture_dict)) + " in file " + p_state->filename + ". Continuing."); + if (gltf_texture->get_src_image() != -1) { break; } } - if (texture->get_src_image() == -1) { + if (gltf_texture->get_src_image() == -1) { // No extensions handled it, so use the base GLTF source. // This may be the fallback, or the only option anyway. - ERR_FAIL_COND_V(!dict.has("source"), ERR_PARSE_ERROR); - texture->set_src_image(dict["source"]); + ERR_FAIL_COND_V(!texture_dict.has("source"), ERR_PARSE_ERROR); + gltf_texture->set_src_image(texture_dict["source"]); } - if (texture->get_sampler() == -1 && dict.has("sampler")) { - texture->set_sampler(dict["sampler"]); + if (gltf_texture->get_sampler() == -1 && texture_dict.has("sampler")) { + gltf_texture->set_sampler(texture_dict["sampler"]); } - p_state->textures.push_back(texture); + p_state->textures.push_back(gltf_texture); } return OK; @@ -3382,10 +3400,11 @@ Ref<Texture2D> GLTFDocument::_get_texture(Ref<GLTFState> p_state, const GLTFText const GLTFImageIndex image = p_state->textures[p_texture]->get_src_image(); ERR_FAIL_INDEX_V(image, p_state->images.size(), Ref<Texture2D>()); if (GLTFState::GLTFHandleBinary(p_state->handle_binary_image) == GLTFState::GLTFHandleBinary::HANDLE_BINARY_EMBED_AS_BASISU) { + ERR_FAIL_INDEX_V(image, p_state->source_images.size(), Ref<Texture2D>()); Ref<PortableCompressedTexture2D> portable_texture; portable_texture.instantiate(); portable_texture->set_keep_compressed_buffer(true); - Ref<Image> new_img = p_state->source_images[p_texture]->duplicate(); + Ref<Image> new_img = p_state->source_images[image]->duplicate(); ERR_FAIL_COND_V(new_img.is_null(), Ref<Texture2D>()); new_img->generate_mipmaps(); if (p_texture_types) { @@ -7221,7 +7240,10 @@ PackedByteArray GLTFDocument::_serialize_glb_buffer(Ref<GLTFState> p_state, Erro PackedByteArray GLTFDocument::generate_buffer(Ref<GLTFState> p_state) { ERR_FAIL_NULL_V(p_state, PackedByteArray()); - Error err = _serialize(p_state, ""); + // For buffers, set the state filename to an empty string, but + // don't touch the base path, in case the user set it manually. + p_state->filename = ""; + Error err = _serialize(p_state); ERR_FAIL_COND_V(err != OK, PackedByteArray()); PackedByteArray bytes = _serialize_glb_buffer(p_state, &err); return bytes; @@ -7229,7 +7251,9 @@ PackedByteArray GLTFDocument::generate_buffer(Ref<GLTFState> p_state) { Error GLTFDocument::write_to_filesystem(Ref<GLTFState> p_state, const String &p_path) { ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); - Error err = _serialize(p_state, p_path); + p_state->base_path = p_path.get_base_dir(); + p_state->filename = p_path.get_file(); + Error err = _serialize(p_state); if (err != OK) { return err; } @@ -7280,44 +7304,44 @@ Node *GLTFDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, boo return root; } -Error GLTFDocument::append_from_scene(Node *p_node, Ref<GLTFState> r_state, uint32_t p_flags) { - ERR_FAIL_COND_V(r_state.is_null(), FAILED); - r_state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS; - r_state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS; - if (!r_state->buffers.size()) { - r_state->buffers.push_back(Vector<uint8_t>()); +Error GLTFDocument::append_from_scene(Node *p_node, Ref<GLTFState> p_state, uint32_t p_flags) { + ERR_FAIL_COND_V(p_state.is_null(), FAILED); + p_state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS; + p_state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS; + if (!p_state->buffers.size()) { + p_state->buffers.push_back(Vector<uint8_t>()); } // Perform export preflight for document extensions. Only extensions that // return OK will be used for the rest of the export steps. document_extensions.clear(); for (Ref<GLTFDocumentExtension> ext : all_document_extensions) { ERR_CONTINUE(ext.is_null()); - Error err = ext->export_preflight(r_state, p_node); + Error err = ext->export_preflight(p_state, p_node); if (err == OK) { document_extensions.push_back(ext); } } // Add the root node(s) and their descendants to the state. - _convert_scene_node(r_state, p_node, -1, -1); + _convert_scene_node(p_state, p_node, -1, -1); return OK; } -Error GLTFDocument::append_from_buffer(PackedByteArray p_bytes, String p_base_path, Ref<GLTFState> r_state, uint32_t p_flags) { - ERR_FAIL_COND_V(r_state.is_null(), FAILED); +Error GLTFDocument::append_from_buffer(PackedByteArray p_bytes, String p_base_path, Ref<GLTFState> p_state, uint32_t p_flags) { + ERR_FAIL_COND_V(p_state.is_null(), FAILED); // TODO Add missing texture and missing .bin file paths to r_missing_deps 2021-09-10 fire Error err = FAILED; - r_state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS; - r_state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS; + p_state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS; + p_state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS; Ref<FileAccessMemory> file_access; file_access.instantiate(); file_access->open_custom(p_bytes.ptr(), p_bytes.size()); - r_state->base_path = p_base_path.get_base_dir(); - err = _parse(r_state, r_state->base_path, file_access); + p_state->base_path = p_base_path.get_base_dir(); + err = _parse(p_state, p_state->base_path, file_access); ERR_FAIL_COND_V(err != OK, err); for (Ref<GLTFDocumentExtension> ext : document_extensions) { ERR_CONTINUE(ext.is_null()); - err = ext->import_post_parse(r_state); + err = ext->import_post_parse(p_state); ERR_FAIL_COND_V(err != OK, err); } return OK; @@ -7436,14 +7460,14 @@ Error GLTFDocument::_parse_gltf_state(Ref<GLTFState> p_state, const String &p_se return OK; } -Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> r_state, uint32_t p_flags, String p_base_path) { +Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> p_state, uint32_t p_flags, String p_base_path) { // TODO Add missing texture and missing .bin file paths to r_missing_deps 2021-09-10 fire - if (r_state == Ref<GLTFState>()) { - r_state.instantiate(); + if (p_state == Ref<GLTFState>()) { + p_state.instantiate(); } - r_state->filename = p_path.get_file().get_basename(); - r_state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS; - r_state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS; + p_state->filename = p_path.get_file().get_basename(); + p_state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS; + p_state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS; Error err; Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::READ, &err); ERR_FAIL_COND_V(err != OK, ERR_FILE_CANT_OPEN); @@ -7452,12 +7476,12 @@ Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> r_state, uint if (base_path.is_empty()) { base_path = p_path.get_base_dir(); } - r_state->base_path = base_path; - err = _parse(r_state, base_path, file); + p_state->base_path = base_path; + err = _parse(p_state, base_path, file); ERR_FAIL_COND_V(err != OK, err); for (Ref<GLTFDocumentExtension> ext : document_extensions) { ERR_CONTINUE(ext.is_null()); - err = ext->import_post_parse(r_state); + err = ext->import_post_parse(p_state); ERR_FAIL_COND_V(err != OK, err); } return OK; diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h index dfde53c9fb..f2e36a0457 100644 --- a/modules/gltf/gltf_document.h +++ b/modules/gltf/gltf_document.h @@ -149,10 +149,10 @@ private: Error _parse_meshes(Ref<GLTFState> p_state); Error _serialize_textures(Ref<GLTFState> p_state); Error _serialize_texture_samplers(Ref<GLTFState> p_state); - Error _serialize_images(Ref<GLTFState> p_state, const String &p_path); + Error _serialize_images(Ref<GLTFState> p_state); Error _serialize_lights(Ref<GLTFState> p_state); - Ref<Image> _parse_image_bytes_into_image(Ref<GLTFState> p_state, const Vector<uint8_t> &p_bytes, const String &p_mime_type, int p_index); - void _parse_image_save_image(Ref<GLTFState> p_state, const String &p_mime_type, int p_index, Ref<Image> p_image); + Ref<Image> _parse_image_bytes_into_image(Ref<GLTFState> p_state, const Vector<uint8_t> &p_bytes, const String &p_mime_type, int p_index, String &r_file_extension); + void _parse_image_save_image(Ref<GLTFState> p_state, const Vector<uint8_t> &p_bytes, const String &p_file_extension, int p_index, Ref<Image> p_image); Error _parse_images(Ref<GLTFState> p_state, const String &p_base_path); Error _parse_textures(Ref<GLTFState> p_state); Error _parse_texture_samplers(Ref<GLTFState> p_state); @@ -293,9 +293,9 @@ private: static float get_max_component(const Color &p_color); public: - Error append_from_file(String p_path, Ref<GLTFState> r_state, uint32_t p_flags = 0, String p_base_path = String()); - Error append_from_buffer(PackedByteArray p_bytes, String p_base_path, Ref<GLTFState> r_state, uint32_t p_flags = 0); - Error append_from_scene(Node *p_node, Ref<GLTFState> r_state, uint32_t p_flags = 0); + Error append_from_file(String p_path, Ref<GLTFState> p_state, uint32_t p_flags = 0, String p_base_path = String()); + Error append_from_buffer(PackedByteArray p_bytes, String p_base_path, Ref<GLTFState> p_state, uint32_t p_flags = 0); + Error append_from_scene(Node *p_node, Ref<GLTFState> p_state, uint32_t p_flags = 0); public: Node *generate_scene(Ref<GLTFState> p_state, float p_bake_fps = 30.0f, bool p_trimming = false, bool p_remove_immutable_tracks = true); @@ -366,7 +366,7 @@ public: GLTFMeshIndex _convert_mesh_to_gltf(Ref<GLTFState> p_state, MeshInstance3D *p_mesh_instance); void _convert_animation(Ref<GLTFState> p_state, AnimationPlayer *p_animation_player, String p_animation_track_name); - Error _serialize(Ref<GLTFState> p_state, const String &p_path); + Error _serialize(Ref<GLTFState> p_state); Error _parse(Ref<GLTFState> p_state, String p_path, Ref<FileAccess> p_file); }; diff --git a/modules/gltf/gltf_state.cpp b/modules/gltf/gltf_state.cpp index 87d15066f0..c0ec004fd6 100644 --- a/modules/gltf/gltf_state.cpp +++ b/modules/gltf/gltf_state.cpp @@ -64,6 +64,8 @@ void GLTFState::_bind_methods() { ClassDB::bind_method(D_METHOD("set_scene_name", "scene_name"), &GLTFState::set_scene_name); ClassDB::bind_method(D_METHOD("get_base_path"), &GLTFState::get_base_path); ClassDB::bind_method(D_METHOD("set_base_path", "base_path"), &GLTFState::set_base_path); + ClassDB::bind_method(D_METHOD("get_filename"), &GLTFState::get_filename); + ClassDB::bind_method(D_METHOD("set_filename", "filename"), &GLTFState::set_filename); ClassDB::bind_method(D_METHOD("get_root_nodes"), &GLTFState::get_root_nodes); ClassDB::bind_method(D_METHOD("set_root_nodes", "root_nodes"), &GLTFState::set_root_nodes); ClassDB::bind_method(D_METHOD("get_textures"), &GLTFState::get_textures); @@ -109,6 +111,7 @@ void GLTFState::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_materials", "get_materials"); // Vector<Ref<Material> ADD_PROPERTY(PropertyInfo(Variant::STRING, "scene_name"), "set_scene_name", "get_scene_name"); // String ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_path"), "set_base_path", "get_base_path"); // String + ADD_PROPERTY(PropertyInfo(Variant::STRING, "filename"), "set_filename", "get_filename"); // String ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "root_nodes"), "set_root_nodes", "get_root_nodes"); // Vector<int> ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "textures", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_textures", "get_textures"); // Vector<Ref<GLTFTexture>> ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "texture_samplers", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_texture_samplers", "get_texture_samplers"); //Vector<Ref<GLTFTextureSampler>> @@ -164,11 +167,11 @@ void GLTFState::set_minor_version(int p_minor_version) { minor_version = p_minor_version; } -String GLTFState::get_copyright() { +String GLTFState::get_copyright() const { return copyright; } -void GLTFState::set_copyright(String p_copyright) { +void GLTFState::set_copyright(const String &p_copyright) { copyright = p_copyright; } @@ -381,6 +384,14 @@ void GLTFState::set_base_path(String p_base_path) { base_path = p_base_path; } +String GLTFState::get_filename() const { + return filename; +} + +void GLTFState::set_filename(const String &p_filename) { + filename = p_filename; +} + Variant GLTFState::get_additional_data(const StringName &p_extension_name) { return additional_data[p_extension_name]; } diff --git a/modules/gltf/gltf_state.h b/modules/gltf/gltf_state.h index d122049c0b..91af8f91a4 100644 --- a/modules/gltf/gltf_state.h +++ b/modules/gltf/gltf_state.h @@ -47,8 +47,8 @@ class GLTFState : public Resource { GDCLASS(GLTFState, Resource); friend class GLTFDocument; - String filename; String base_path; + String filename; Dictionary json; int major_version = 0; int minor_version = 0; @@ -126,8 +126,8 @@ public: int get_minor_version(); void set_minor_version(int p_minor_version); - String get_copyright(); - void set_copyright(String p_copyright); + String get_copyright() const; + void set_copyright(const String &p_copyright); Vector<uint8_t> get_glb_data(); void set_glb_data(Vector<uint8_t> p_glb_data); @@ -171,6 +171,9 @@ public: String get_base_path(); void set_base_path(String p_base_path); + String get_filename() const; + void set_filename(const String &p_filename); + PackedInt32Array get_root_nodes(); void set_root_nodes(PackedInt32Array p_root_nodes); diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp index a300f7d2e2..d7ecf4ef1a 100644 --- a/modules/gridmap/editor/grid_map_editor_plugin.cpp +++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp @@ -743,6 +743,17 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D } } + // Consume input to avoid conflicts with other plugins. + if (k.is_valid() && k->is_pressed() && !k->is_echo()) { + for (int i = 0; i < options->get_popup()->get_item_count(); ++i) { + const Ref<Shortcut> &shortcut = options->get_popup()->get_item_shortcut(i); + if (shortcut.is_valid() && shortcut->matches_event(p_event)) { + _menu_option(options->get_popup()->get_item_id(i)); + return EditorPlugin::AFTER_GUI_INPUT_STOP; + } + } + } + if (k->is_shift_pressed() && selection.active && input_action != INPUT_PASTE) { if (k->get_keycode() == (Key)options->get_popup()->get_item_accelerator(options->get_popup()->get_item_index(MENU_OPTION_PREV_LEVEL))) { selection.click[edit_axis]--; @@ -1154,6 +1165,24 @@ void GridMapEditor::_bind_methods() { } GridMapEditor::GridMapEditor() { + ED_SHORTCUT("grid_map/previous_floor", TTR("Previous Floor"), Key::Q, true); + ED_SHORTCUT("grid_map/next_floor", TTR("Next Floor"), Key::E, true); + ED_SHORTCUT("grid_map/edit_x_axis", TTR("Edit X Axis"), Key::Z, true); + ED_SHORTCUT("grid_map/edit_y_axis", TTR("Edit Y Axis"), Key::X, true); + ED_SHORTCUT("grid_map/edit_z_axis", TTR("Edit Z Axis"), Key::C, true); + ED_SHORTCUT("grid_map/cursor_rotate_x", TTR("Cursor Rotate X"), Key::A, true); + ED_SHORTCUT("grid_map/cursor_rotate_y", TTR("Cursor Rotate Y"), Key::S, true); + ED_SHORTCUT("grid_map/cursor_rotate_z", TTR("Cursor Rotate Z"), Key::D, true); + ED_SHORTCUT("grid_map/cursor_back_rotate_x", TTR("Cursor Back Rotate X"), KeyModifierMask::SHIFT + Key::A, true); + ED_SHORTCUT("grid_map/cursor_back_rotate_y", TTR("Cursor Back Rotate Y"), KeyModifierMask::SHIFT + Key::S, true); + ED_SHORTCUT("grid_map/cursor_back_rotate_z", TTR("Cursor Back Rotate Z"), KeyModifierMask::SHIFT + Key::D, true); + ED_SHORTCUT("grid_map/cursor_clear_rotation", TTR("Cursor Clear Rotation"), Key::W, true); + ED_SHORTCUT("grid_map/paste_selects", TTR("Paste Selects")); + ED_SHORTCUT("grid_map/duplicate_selection", TTR("Duplicate Selection"), KeyModifierMask::CTRL + Key::C, true); + ED_SHORTCUT("grid_map/cut_selection", TTR("Cut Selection"), KeyModifierMask::CTRL + Key::X, true); + ED_SHORTCUT("grid_map/clear_selection", TTR("Clear Selection"), Key::KEY_DELETE); + ED_SHORTCUT("grid_map/fill_selection", TTR("Fill Selection"), KeyModifierMask::CTRL + Key::F); + int mw = EDITOR_DEF("editors/grid_map/palette_min_width", 230); Control *ec = memnew(Control); ec->set_custom_minimum_size(Size2(mw, 0) * EDSCALE); @@ -1186,29 +1215,29 @@ GridMapEditor::GridMapEditor() { spatial_editor_hb->hide(); options->set_text(TTR("Grid Map")); - options->get_popup()->add_item(TTR("Previous Floor"), MENU_OPTION_PREV_LEVEL, Key::Q); - options->get_popup()->add_item(TTR("Next Floor"), MENU_OPTION_NEXT_LEVEL, Key::E); + options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/previous_floor"), MENU_OPTION_PREV_LEVEL); + options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/next_floor"), MENU_OPTION_NEXT_LEVEL); options->get_popup()->add_separator(); - options->get_popup()->add_radio_check_item(TTR("Edit X Axis"), MENU_OPTION_X_AXIS, Key::Z); - options->get_popup()->add_radio_check_item(TTR("Edit Y Axis"), MENU_OPTION_Y_AXIS, Key::X); - options->get_popup()->add_radio_check_item(TTR("Edit Z Axis"), MENU_OPTION_Z_AXIS, Key::C); + options->get_popup()->add_radio_check_shortcut(ED_GET_SHORTCUT("grid_map/edit_x_axis"), MENU_OPTION_X_AXIS); + options->get_popup()->add_radio_check_shortcut(ED_GET_SHORTCUT("grid_map/edit_y_axis"), MENU_OPTION_Y_AXIS); + options->get_popup()->add_radio_check_shortcut(ED_GET_SHORTCUT("grid_map/edit_z_axis"), MENU_OPTION_Z_AXIS); options->get_popup()->set_item_checked(options->get_popup()->get_item_index(MENU_OPTION_Y_AXIS), true); options->get_popup()->add_separator(); - options->get_popup()->add_item(TTR("Cursor Rotate X"), MENU_OPTION_CURSOR_ROTATE_X, Key::A); - options->get_popup()->add_item(TTR("Cursor Rotate Y"), MENU_OPTION_CURSOR_ROTATE_Y, Key::S); - options->get_popup()->add_item(TTR("Cursor Rotate Z"), MENU_OPTION_CURSOR_ROTATE_Z, Key::D); - options->get_popup()->add_item(TTR("Cursor Back Rotate X"), MENU_OPTION_CURSOR_BACK_ROTATE_X, KeyModifierMask::SHIFT + Key::A); - options->get_popup()->add_item(TTR("Cursor Back Rotate Y"), MENU_OPTION_CURSOR_BACK_ROTATE_Y, KeyModifierMask::SHIFT + Key::S); - options->get_popup()->add_item(TTR("Cursor Back Rotate Z"), MENU_OPTION_CURSOR_BACK_ROTATE_Z, KeyModifierMask::SHIFT + Key::D); - options->get_popup()->add_item(TTR("Cursor Clear Rotation"), MENU_OPTION_CURSOR_CLEAR_ROTATION, Key::W); + options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_rotate_x"), MENU_OPTION_CURSOR_ROTATE_X); + options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_rotate_y"), MENU_OPTION_CURSOR_ROTATE_Y); + options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_rotate_z"), MENU_OPTION_CURSOR_ROTATE_Z); + options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_back_rotate_x"), MENU_OPTION_CURSOR_BACK_ROTATE_X); + options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_back_rotate_y"), MENU_OPTION_CURSOR_BACK_ROTATE_Y); + options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_back_rotate_z"), MENU_OPTION_CURSOR_BACK_ROTATE_Z); + options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_clear_rotation"), MENU_OPTION_CURSOR_CLEAR_ROTATION); options->get_popup()->add_separator(); // TRANSLATORS: This is a toggle to select after pasting the new content. - options->get_popup()->add_check_item(TTR("Paste Selects"), MENU_OPTION_PASTE_SELECTS); + options->get_popup()->add_check_shortcut(ED_GET_SHORTCUT("grid_map/paste_selects"), MENU_OPTION_PASTE_SELECTS); options->get_popup()->add_separator(); - options->get_popup()->add_item(TTR("Duplicate Selection"), MENU_OPTION_SELECTION_DUPLICATE, KeyModifierMask::CTRL + Key::C); - options->get_popup()->add_item(TTR("Cut Selection"), MENU_OPTION_SELECTION_CUT, KeyModifierMask::CTRL + Key::X); - options->get_popup()->add_item(TTR("Clear Selection"), MENU_OPTION_SELECTION_CLEAR, Key::KEY_DELETE); - options->get_popup()->add_item(TTR("Fill Selection"), MENU_OPTION_SELECTION_FILL, KeyModifierMask::CTRL + Key::F); + options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/duplicate_selection"), MENU_OPTION_SELECTION_DUPLICATE); + options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cut_selection"), MENU_OPTION_SELECTION_CUT); + options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/clear_selection"), MENU_OPTION_SELECTION_CLEAR); + options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/fill_selection"), MENU_OPTION_SELECTION_FILL); options->get_popup()->add_separator(); options->get_popup()->add_item(TTR("Settings..."), MENU_OPTION_GRIDMAP_SETTINGS); diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 1ed495943f..2971706c75 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -72,7 +72,7 @@ // Types that will be skipped over (in favor of their base types) when setting up instance bindings. // This must be a superset of `ignored_types` in bindings_generator.cpp. -const Vector<String> ignored_types = { "PhysicsServer2DExtension", "PhysicsServer3DExtension" }; +const Vector<String> ignored_types = {}; #ifdef TOOLS_ENABLED static bool _create_project_solution_if_needed() { @@ -1197,8 +1197,6 @@ void CSharpLanguage::_editor_init_callback() { // Add plugin to EditorNode and enable it EditorNode::add_editor_plugin(godotsharp_editor); - ED_SHORTCUT("mono/build_solution", TTR("Build Solution"), KeyModifierMask::ALT | Key::B); - ED_SHORTCUT_OVERRIDE("mono/build_solution", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::B); godotsharp_editor->enable_plugin(); get_singleton()->godotsharp_editor = godotsharp_editor; diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj index a03c9bc06c..23879e0e53 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj @@ -18,6 +18,7 @@ <GeneratePackageOnBuild>true</GeneratePackageOnBuild> <!-- Do not include the generator as a lib dependency --> <IncludeBuildOutput>false</IncludeBuildOutput> + <SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" PrivateAssets="all" /> diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs index d7d484d166..8a292fd73a 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs @@ -23,7 +23,7 @@ namespace GodotTools.Build if (dotnetPath == null) throw new FileNotFoundException("Cannot find the dotnet executable."); - var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings(); + var editorSettings = EditorInterface.Singleton.GetEditorSettings(); var startInfo = new ProcessStartInfo(dotnetPath); @@ -94,7 +94,7 @@ namespace GodotTools.Build if (dotnetPath == null) throw new FileNotFoundException("Cannot find the dotnet executable."); - var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings(); + var editorSettings = EditorInterface.Singleton.GetEditorSettings(); var startInfo = new ProcessStartInfo(dotnetPath); diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs index 1bb1b3227e..cc11132a55 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs @@ -114,7 +114,7 @@ namespace GodotTools.Build var toolBarHBox = new HBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill }; AddChild(toolBarHBox); - _buildMenuBtn = new MenuButton { Text = "Build", Icon = GetThemeIcon("Play", "EditorIcons") }; + _buildMenuBtn = new MenuButton { Text = "Build", Icon = GetThemeIcon("BuildCSharp", "EditorIcons") }; toolBarHBox.AddChild(_buildMenuBtn); var buildMenu = _buildMenuBtn.GetPopup(); @@ -184,7 +184,7 @@ namespace GodotTools.Build if (what == NotificationThemeChanged) { if (_buildMenuBtn != null) - _buildMenuBtn.Icon = GetThemeIcon("Play", "EditorIcons"); + _buildMenuBtn.Icon = GetThemeIcon("BuildCSharp", "EditorIcons"); if (_errorsBtn != null) _errorsBtn.Icon = GetThemeIcon("StatusError", "EditorIcons"); if (_warningsBtn != null) diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs index 4d61372ab0..b98df190ca 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs @@ -170,7 +170,7 @@ namespace GodotTools.Export string ridOS = DetermineRuntimeIdentifierOS(platform); string ridArch = DetermineRuntimeIdentifierArch(arch); string runtimeIdentifier = $"{ridOS}-{ridArch}"; - string projectDataDirName = $"data_{GodotSharpDirs.CSharpProjectName}_{arch}"; + string projectDataDirName = $"data_{GodotSharpDirs.CSharpProjectName}_{platform}_{arch}"; if (platform == OS.Platforms.MacOS) { projectDataDirName = Path.Combine("Contents", "Resources", projectDataDirName); diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index 622a155d37..ec28658557 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -280,7 +280,7 @@ namespace GodotTools case ExternalEditorId.Rider: { string scriptPath = ProjectSettings.GlobalizePath(script.ResourcePath); - RiderPathManager.OpenFile(GodotSharpDirs.ProjectSlnPath, scriptPath, line); + RiderPathManager.OpenFile(GodotSharpDirs.ProjectSlnPath, scriptPath, line + 1, col); return Error.Ok; } case ExternalEditorId.MonoDevelop: @@ -473,10 +473,9 @@ namespace GodotTools } } - var editorInterface = GetEditorInterface(); - var editorBaseControl = editorInterface.GetBaseControl(); + var editorBaseControl = EditorInterface.Singleton.GetBaseControl(); - _editorSettings = editorInterface.GetEditorSettings(); + _editorSettings = EditorInterface.Singleton.GetEditorSettings(); _errorDialog = new AcceptDialog(); editorBaseControl.AddChild(_errorDialog); @@ -497,18 +496,20 @@ namespace GodotTools AddToolSubmenuItem("C#", _menuPopup); - var buildSolutionShortcut = (Shortcut)EditorShortcut("mono/build_solution"); - _toolBarBuildButton = new Button { - Text = "Build", - TooltipText = "Build Solution".TTR(), + Flat = true, + Icon = editorBaseControl.GetThemeIcon("BuildCSharp", "EditorIcons"), FocusMode = Control.FocusModeEnum.None, - Shortcut = buildSolutionShortcut, - ShortcutInTooltip = true + Shortcut = EditorDefShortcut("mono/build_solution", "Build Project".TTR(), (Key)KeyModifierMask.MaskAlt | Key.B), + ShortcutInTooltip = true, }; + EditorShortcutOverride("mono/build_solution", "macos", (Key)KeyModifierMask.MaskMeta | (Key)KeyModifierMask.MaskCtrl | Key.B); + _toolBarBuildButton.Pressed += BuildProjectPressed; - AddControlToContainer(CustomControlContainer.Toolbar, _toolBarBuildButton); + Internal.EditorPlugin_AddControlToEditorRunBar(_toolBarBuildButton); + // Move Build button so it appears to the left of the Play button. + _toolBarBuildButton.GetParent().MoveChild(_toolBarBuildButton, 0); if (File.Exists(GodotSharpDirs.ProjectCsProjPath)) { @@ -538,7 +539,7 @@ namespace GodotTools settingsHintStr += $",Visual Studio:{(int)ExternalEditorId.VisualStudio}" + $",MonoDevelop:{(int)ExternalEditorId.MonoDevelop}" + $",Visual Studio Code:{(int)ExternalEditorId.VsCode}" + - $",JetBrains Rider:{(int)ExternalEditorId.Rider}" + + $",JetBrains Rider and Fleet:{(int)ExternalEditorId.Rider}" + $",Custom:{(int)ExternalEditorId.CustomEditor}"; } else if (OS.IsMacOS) @@ -546,14 +547,14 @@ namespace GodotTools settingsHintStr += $",Visual Studio:{(int)ExternalEditorId.VisualStudioForMac}" + $",MonoDevelop:{(int)ExternalEditorId.MonoDevelop}" + $",Visual Studio Code:{(int)ExternalEditorId.VsCode}" + - $",JetBrains Rider:{(int)ExternalEditorId.Rider}" + + $",JetBrains Rider and Fleet:{(int)ExternalEditorId.Rider}" + $",Custom:{(int)ExternalEditorId.CustomEditor}"; } else if (OS.IsUnixLike) { settingsHintStr += $",MonoDevelop:{(int)ExternalEditorId.MonoDevelop}" + $",Visual Studio Code:{(int)ExternalEditorId.VsCode}" + - $",JetBrains Rider:{(int)ExternalEditorId.Rider}" + + $",JetBrains Rider and Fleet:{(int)ExternalEditorId.Rider}" + $",Custom:{(int)ExternalEditorId.CustomEditor}"; } diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj index 4a0b7f9bed..56ae37b4dd 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj +++ b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj @@ -28,7 +28,7 @@ </PropertyGroup> <ItemGroup> <PackageReference Include="JetBrains.Annotations" Version="2019.1.3.0" ExcludeAssets="runtime" PrivateAssets="all" /> - <PackageReference Include="JetBrains.Rider.PathLocator" Version="1.0.1" /> + <PackageReference Include="JetBrains.Rider.PathLocator" Version="1.0.4" /> <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <Reference Include="GodotSharp"> diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs index 5d1a2277f9..65b77112aa 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs @@ -79,7 +79,7 @@ namespace GodotTools.Ides public async Task<EditorPick?> LaunchIdeAsync(int millisecondsTimeout = 10000) { - var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings(); + var editorSettings = EditorInterface.Singleton.GetEditorSettings(); var editorId = editorSettings.GetSetting(GodotSharpEditor.Settings.ExternalEditor).As<ExternalEditorId>(); string editorIdentity = GetExternalEditorIdentity(editorId); diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderLocatorEnvironment.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderLocatorEnvironment.cs index 7e08d8c01d..61c1581281 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderLocatorEnvironment.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderLocatorEnvironment.cs @@ -48,4 +48,9 @@ public class RiderLocatorEnvironment : IRiderLocatorEnvironment else GD.PushError(message, e); } + + public void Verbose(string message, Exception e = null) + { + // do nothing, since IDK how to write only to the log, without spamming the output + } } diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs index 5c09f1f83a..fbbd01dafd 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.IO; using System.Linq; using Godot; @@ -11,17 +10,20 @@ namespace GodotTools.Ides.Rider public static class RiderPathManager { private static readonly RiderPathLocator RiderPathLocator; + private static readonly RiderFileOpener RiderFileOpener; static RiderPathManager() { - RiderPathLocator = new RiderPathLocator(new RiderLocatorEnvironment()); + var riderLocatorEnvironment = new RiderLocatorEnvironment(); + RiderPathLocator = new RiderPathLocator(riderLocatorEnvironment); + RiderFileOpener = new RiderFileOpener(riderLocatorEnvironment); } public static readonly string EditorPathSettingName = "dotnet/editor/editor_path_optional"; private static string GetRiderPathFromSettings() { - var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings(); + var editorSettings = EditorInterface.Singleton.GetEditorSettings(); if (editorSettings.HasSetting(EditorPathSettingName)) return (string)editorSettings.GetSetting(EditorPathSettingName); return null; @@ -29,7 +31,7 @@ namespace GodotTools.Ides.Rider public static void Initialize() { - var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings(); + var editorSettings = EditorInterface.Singleton.GetEditorSettings(); var editor = editorSettings.GetSetting(GodotSharpEditor.Settings.ExternalEditor).As<ExternalEditorId>(); if (editor == ExternalEditorId.Rider) { @@ -46,7 +48,7 @@ namespace GodotTools.Ides.Rider } var riderPath = (string)editorSettings.GetSetting(EditorPathSettingName); - if (IsRiderAndExists(riderPath)) + if (File.Exists(riderPath)) { Globals.EditorDef(EditorPathSettingName, riderPath); return; @@ -63,12 +65,6 @@ namespace GodotTools.Ides.Rider } } - public static bool IsExternalEditorSetToRider(EditorSettings editorSettings) - { - return editorSettings.HasSetting(EditorPathSettingName) && - IsRider((string)editorSettings.GetSetting(EditorPathSettingName)); - } - public static bool IsRider(string path) { if (string.IsNullOrEmpty(path)) @@ -84,49 +80,29 @@ namespace GodotTools.Ides.Rider private static string CheckAndUpdatePath(string riderPath) { - if (IsRiderAndExists(riderPath)) + if (File.Exists(riderPath)) { return riderPath; } - var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings(); - var paths = RiderPathLocator.GetAllRiderPaths(); - - if (!paths.Any()) + var allInfos = RiderPathLocator.GetAllRiderPaths(); + if (allInfos.Length == 0) return null; - - string newPath = paths.Last().Path; + var riderInfos = allInfos.Where(info => IsRider(info.Path)).ToArray(); + string newPath = riderInfos.Length > 0 + ? riderInfos[riderInfos.Length - 1].Path + : allInfos[allInfos.Length - 1].Path; + var editorSettings = EditorInterface.Singleton.GetEditorSettings(); editorSettings.SetSetting(EditorPathSettingName, newPath); Globals.EditorDef(EditorPathSettingName, newPath); return newPath; } - private static bool IsRiderAndExists(string riderPath) - { - return !string.IsNullOrEmpty(riderPath) && IsRider(riderPath) && new FileInfo(riderPath).Exists; - } - - public static void OpenFile(string slnPath, string scriptPath, int line) + public static void OpenFile(string slnPath, string scriptPath, int line, int column) { string pathFromSettings = GetRiderPathFromSettings(); string path = CheckAndUpdatePath(pathFromSettings); - - var args = new List<string>(); - args.Add(slnPath); - if (line >= 0) - { - args.Add("--line"); - args.Add((line + 1).ToString()); // https://github.com/JetBrains/godot-support/issues/61 - } - args.Add(scriptPath); - try - { - Utils.OS.RunProcess(path, args); - } - catch (Exception e) - { - GD.PushError($"Error when trying to run code editor: JetBrains Rider. Exception message: '{e.Message}'"); - } + RiderFileOpener.OpenFile(path, slnPath, scriptPath, line, column); } } } diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs index 45ae7eb86b..a6718e8fd5 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs @@ -29,11 +29,26 @@ namespace GodotTools.Internals return Variant.CreateTakingOwnershipOfDisposableValue(result); } - public static Variant EditorShortcut(string setting) + public static Shortcut EditorDefShortcut(string setting, string name, Key keycode = Key.None, bool physical = false) { using godot_string settingIn = Marshaling.ConvertStringToNative(setting); - Internal.godot_icall_Globals_EditorShortcut(settingIn, out godot_variant result); - return Variant.CreateTakingOwnershipOfDisposableValue(result); + using godot_string nameIn = Marshaling.ConvertStringToNative(name); + Internal.godot_icall_Globals_EditorDefShortcut(settingIn, nameIn, keycode, physical.ToGodotBool(), out godot_variant result); + return (Shortcut)Variant.CreateTakingOwnershipOfDisposableValue(result); + } + + public static Shortcut EditorGetShortcut(string setting) + { + using godot_string settingIn = Marshaling.ConvertStringToNative(setting); + Internal.godot_icall_Globals_EditorGetShortcut(settingIn, out godot_variant result); + return (Shortcut)Variant.CreateTakingOwnershipOfDisposableValue(result); + } + + public static void EditorShortcutOverride(string setting, string feature, Key keycode = Key.None, bool physical = false) + { + using godot_string settingIn = Marshaling.ConvertStringToNative(setting); + using godot_string featureIn = Marshaling.ConvertStringToNative(feature); + Internal.godot_icall_Globals_EditorShortcutOverride(settingIn, featureIn, keycode, physical.ToGodotBool()); } [SuppressMessage("ReSharper", "InconsistentNaming")] diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs index 3ea11750b7..90c443ebb8 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs @@ -54,6 +54,9 @@ namespace GodotTools.Internals public static void EditorRunStop() => godot_icall_Internal_EditorRunStop(); + public static void EditorPlugin_AddControlToEditorRunBar(Control control) => + godot_icall_Internal_EditorPlugin_AddControlToEditorRunBar(control.NativeInstance); + public static void ScriptEditorDebugger_ReloadScripts() => godot_icall_Internal_ScriptEditorDebugger_ReloadScripts(); @@ -137,6 +140,8 @@ namespace GodotTools.Internals private static partial void godot_icall_Internal_EditorRunStop(); + private static partial void godot_icall_Internal_EditorPlugin_AddControlToEditorRunBar(IntPtr p_control); + private static partial void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts(); private static partial void godot_icall_Internal_CodeCompletionRequest(int kind, in godot_string scriptFile, @@ -151,7 +156,13 @@ namespace GodotTools.Internals bool restartIfChanged, out godot_variant result); public static partial void - godot_icall_Globals_EditorShortcut(in godot_string setting, out godot_variant result); + godot_icall_Globals_EditorDefShortcut(in godot_string setting, in godot_string name, Key keycode, godot_bool physical, out godot_variant result); + + public static partial void + godot_icall_Globals_EditorGetShortcut(in godot_string setting, out godot_variant result); + + public static partial void + godot_icall_Globals_EditorShortcutOverride(in godot_string setting, in godot_string feature, Key keycode, godot_bool physical); public static partial void godot_icall_Globals_TTR(in godot_string text, out godot_string dest); diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index bed93cd69e..006aca6c73 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -82,6 +82,7 @@ StringBuilder &operator<<(StringBuilder &r_sb, const char *p_cstring) { #define CS_STATIC_METHOD_GETINSTANCE "GetPtr" #define CS_METHOD_CALL "Call" #define CS_PROPERTY_SINGLETON "Singleton" +#define CS_SINGLETON_INSTANCE_SUFFIX "Instance" #define CS_METHOD_INVOKE_GODOT_CLASS_METHOD "InvokeGodotClassMethod" #define CS_METHOD_HAS_GODOT_CLASS_METHOD "HasGodotClassMethod" #define CS_METHOD_HAS_GODOT_CLASS_SIGNAL "HasGodotClassSignal" @@ -116,7 +117,7 @@ StringBuilder &operator<<(StringBuilder &r_sb, const char *p_cstring) { // Types that will be ignored by the generator and won't be available in C#. // This must be kept in sync with `ignored_types` in csharp_script.cpp -const Vector<String> ignored_types = { "PhysicsServer2DExtension", "PhysicsServer3DExtension" }; +const Vector<String> ignored_types = {}; void BindingsGenerator::TypeInterface::postsetup_enum_type(BindingsGenerator::TypeInterface &r_enum_itype) { // C interface for enums is the same as that of 'uint32_t'. Remember to apply @@ -147,7 +148,7 @@ static String fix_doc_description(const String &p_bbcode) { .strip_edges(); } -String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype) { +String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype, bool p_is_signal) { // Based on the version in EditorHelp if (p_bbcode.is_empty()) { @@ -304,11 +305,11 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf _append_xml_enum(xml_output, target_itype, target_cname, link_target, link_target_parts); } else if (link_tag == "constant") { _append_xml_constant(xml_output, target_itype, target_cname, link_target, link_target_parts); + } else if (link_tag == "param") { + _append_xml_param(xml_output, link_target, p_is_signal); } else if (link_tag == "theme_item") { // We do not declare theme_items in any way in C#, so there is nothing to reference _append_xml_undeclared(xml_output, link_target); - } else if (link_tag == "param") { - _append_xml_undeclared(xml_output, snake_to_camel_case(link_target, false)); } pos = brk_end + 1; @@ -653,6 +654,11 @@ void BindingsGenerator::_append_xml_constant(StringBuilder &p_xml_output, const _append_xml_undeclared(p_xml_output, p_link_target); } else { // Try to find the constant in the current class + if (p_target_itype->is_singleton_instance) { + // Constants and enums are declared in the static singleton class. + p_target_itype = &obj_types[p_target_itype->cname]; + } + const ConstantInterface *target_iconst = find_constant_by_name(p_target_cname, p_target_itype->constants); if (target_iconst) { @@ -678,7 +684,7 @@ void BindingsGenerator::_append_xml_constant(StringBuilder &p_xml_output, const p_xml_output.append("<see cref=\"" BINDINGS_NAMESPACE "."); p_xml_output.append(p_target_itype->proxy_name); p_xml_output.append("."); - p_xml_output.append(target_ienum->cname); + p_xml_output.append(target_ienum->proxy_name); p_xml_output.append("."); p_xml_output.append(target_iconst->proxy_name); p_xml_output.append("\"/>"); @@ -719,7 +725,7 @@ void BindingsGenerator::_append_xml_constant_in_global_scope(StringBuilder &p_xm if (target_iconst) { p_xml_output.append("<see cref=\"" BINDINGS_NAMESPACE "."); - p_xml_output.append(target_ienum->cname); + p_xml_output.append(target_ienum->proxy_name); p_xml_output.append("."); p_xml_output.append(target_iconst->proxy_name); p_xml_output.append("\"/>"); @@ -730,6 +736,21 @@ void BindingsGenerator::_append_xml_constant_in_global_scope(StringBuilder &p_xm } } +void BindingsGenerator::_append_xml_param(StringBuilder &p_xml_output, const String &p_link_target, bool p_is_signal) { + const String link_target = snake_to_camel_case(p_link_target); + + if (!p_is_signal) { + p_xml_output.append("<paramref name=\""); + p_xml_output.append(link_target); + p_xml_output.append("\"/>"); + } else { + // Documentation in C# is added to an event, not the delegate itself; + // as such, we treat these parameters as codeblocks instead. + // See: https://github.com/godotengine/godot/pull/65529 + _append_xml_undeclared(p_xml_output, link_target); + } +} + void BindingsGenerator::_append_xml_undeclared(StringBuilder &p_xml_output, const String &p_link_target) { p_xml_output.append("<c>"); p_xml_output.append(p_link_target); @@ -975,7 +996,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) { for (const EnumInterface &ienum : global_enums) { CRASH_COND(ienum.constants.is_empty()); - String enum_proxy_name = ienum.cname.operator String(); + String enum_proxy_name = ienum.proxy_name; bool enum_in_static_class = false; @@ -989,7 +1010,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) { _log("Declaring global enum '%s' inside struct '%s'\n", enum_proxy_name.utf8().get_data(), enum_class_name.utf8().get_data()); p_output.append("\npublic partial struct "); - p_output.append(pascal_to_pascal_case(enum_class_name)); + p_output.append(enum_class_name); p_output.append("\n" OPEN_BLOCK); } @@ -998,7 +1019,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) { } p_output.append("\npublic enum "); - p_output.append(pascal_to_pascal_case(enum_proxy_name)); + p_output.append(enum_proxy_name); p_output.append(" : long"); p_output.append("\n" OPEN_BLOCK); @@ -1403,8 +1424,13 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str if (is_derived_type && !itype.is_singleton) { if (obj_types.has(itype.base_name)) { + TypeInterface base_type = obj_types[itype.base_name]; output.append(" : "); - output.append(obj_types[itype.base_name].proxy_name); + output.append(base_type.proxy_name); + if (base_type.is_singleton) { + // If the type is a singleton, use the instance type. + output.append(CS_SINGLETON_INSTANCE_SUFFIX); + } } else { ERR_PRINT("Base type '" + itype.base_name.operator String() + "' does not exist, for class '" + itype.name + "'."); return ERR_INVALID_DATA; @@ -1458,7 +1484,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str } output.append(MEMBER_BEGIN "public enum "); - output.append(pascal_to_pascal_case(ienum.cname.operator String())); + output.append(ienum.proxy_name); output.append(" : long"); output.append(MEMBER_BEGIN OPEN_BLOCK); @@ -1504,37 +1530,44 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str "' for class '" + itype.name + "'."); } - if (itype.is_singleton) { - // Add the type name and the singleton pointer as static fields + // Add native name static field and cached type. + + if (is_derived_type && !itype.is_singleton) { + output << MEMBER_BEGIN "private static readonly System.Type CachedType = typeof(" << itype.proxy_name << ");\n"; + } - output.append(MEMBER_BEGIN "private static GodotObject singleton;\n"); + output.append(MEMBER_BEGIN "private static readonly StringName " BINDINGS_NATIVE_NAME_FIELD " = \""); + output.append(itype.name); + output.append("\";\n"); - output << MEMBER_BEGIN "public static GodotObject " CS_PROPERTY_SINGLETON "\n" INDENT1 "{\n" - << INDENT2 "get\n" INDENT2 "{\n" INDENT3 "if (singleton == null)\n" - << INDENT4 "singleton = " C_METHOD_ENGINE_GET_SINGLETON "(\"" - << itype.name - << "\");\n" INDENT3 "return singleton;\n" INDENT2 "}\n" INDENT1 "}\n"; + if (itype.is_singleton || itype.is_compat_singleton) { + // Add the Singleton static property. - output.append(MEMBER_BEGIN "private static readonly StringName " BINDINGS_NATIVE_NAME_FIELD " = \""); - output.append(itype.name); - output.append("\";\n"); - } else { + String instance_type_name; + + if (itype.is_singleton) { + StringName instance_name = itype.name + CS_SINGLETON_INSTANCE_SUFFIX; + instance_type_name = obj_types.has(instance_name) + ? obj_types[instance_name].proxy_name + : "GodotObject"; + } else { + instance_type_name = itype.proxy_name; + } + + output.append(MEMBER_BEGIN "private static " + instance_type_name + " singleton;\n"); + + output << MEMBER_BEGIN "public static " + instance_type_name + " " CS_PROPERTY_SINGLETON " =>\n" + << INDENT2 "singleton \?\?= (" + instance_type_name + ")" + << C_METHOD_ENGINE_GET_SINGLETON "(\"" << itype.name << "\");\n"; + } + + if (!itype.is_singleton) { // IMPORTANT: We also generate the static fields for GodotObject instead of declaring // them manually in the `GodotObject.base.cs` partial class declaration, because they're // required by other static fields in this generated partial class declaration. // Static fields are initialized in order of declaration, but when they're in different // partial class declarations then it becomes harder to tell (Rider warns about this). - // Add native name static field - - if (is_derived_type) { - output << MEMBER_BEGIN "private static readonly System.Type CachedType = typeof(" << itype.proxy_name << ");\n"; - } - - output.append(MEMBER_BEGIN "private static readonly StringName " BINDINGS_NATIVE_NAME_FIELD " = \""); - output.append(itype.name); - output.append("\";\n"); - if (itype.is_instantiable) { // Add native constructor static field @@ -1749,7 +1782,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str << INDENT1 "/// This method is used by Godot to check if a signal exists before raising it.\n" << INDENT1 "/// Do not call or override this method.\n" << INDENT1 "/// </summary>\n" - << INDENT1 "/// <param name=\"method\">Name of the method to check for.</param>\n"; + << INDENT1 "/// <param name=\"signal\">Name of the signal to check for.</param>\n"; output << MEMBER_BEGIN "protected internal " << (is_derived_type ? "override" : "virtual") << " bool " CS_METHOD_HAS_GODOT_CLASS_SIGNAL "(in godot_string_name signal)\n" @@ -1894,7 +1927,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte const TypeReference &proptype_name = getter ? getter->return_type : setter->arguments.back()->get().type; - const TypeInterface *prop_itype = _get_type_or_null(proptype_name); + const TypeInterface *prop_itype = _get_type_or_singleton_or_null(proptype_name); ERR_FAIL_NULL_V(prop_itype, ERR_BUG); // Property type not found ERR_FAIL_COND_V_MSG(prop_itype->is_singleton, ERR_BUG, @@ -1983,7 +2016,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte } Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, int &p_method_bind_count, StringBuilder &p_output) { - const TypeInterface *return_type = _get_type_or_null(p_imethod.return_type); + const TypeInterface *return_type = _get_type_or_singleton_or_null(p_imethod.return_type); ERR_FAIL_NULL_V(return_type, ERR_BUG); // Return type not found ERR_FAIL_COND_V_MSG(return_type->is_singleton, ERR_BUG, @@ -2004,12 +2037,17 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf String icall_params = method_bind_field; if (!p_imethod.is_static) { + String self_reference = "this"; + if (p_itype.is_singleton) { + self_reference = CS_PROPERTY_SINGLETON; + } + if (p_itype.cs_in.size()) { - cs_in_statements << sformat(p_itype.cs_in, p_itype.c_type, "this", + cs_in_statements << sformat(p_itype.cs_in, p_itype.c_type, self_reference, String(), String(), String(), INDENT2); } - icall_params += ", " + sformat(p_itype.cs_in_expr, "this"); + icall_params += ", " + sformat(p_itype.cs_in_expr, self_reference); } StringBuilder default_args_doc; @@ -2017,7 +2055,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf // Retrieve information from the arguments const ArgumentInterface &first = p_imethod.arguments.front()->get(); for (const ArgumentInterface &iarg : p_imethod.arguments) { - const TypeInterface *arg_type = _get_type_or_null(iarg.type); + const TypeInterface *arg_type = _get_type_or_singleton_or_null(iarg.type); ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Argument type not found ERR_FAIL_COND_V_MSG(arg_type->is_singleton, ERR_BUG, @@ -2275,7 +2313,7 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf // Retrieve information from the arguments const ArgumentInterface &first = p_isignal.arguments.front()->get(); for (const ArgumentInterface &iarg : p_isignal.arguments) { - const TypeInterface *arg_type = _get_type_or_null(iarg.type); + const TypeInterface *arg_type = _get_type_or_singleton_or_null(iarg.type); ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Argument type not found ERR_FAIL_COND_V_MSG(arg_type->is_singleton, ERR_BUG, @@ -2307,31 +2345,31 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf // Generate signal { - p_output.append(MEMBER_BEGIN "/// <summary>\n"); - p_output.append(INDENT1 "/// "); - p_output.append("Represents the method that handles the "); - p_output.append("<see cref=\"" BINDINGS_NAMESPACE "." + p_itype.proxy_name + "." + p_isignal.proxy_name + "\"/>"); - p_output.append(" event of a "); - p_output.append("<see cref=\"" BINDINGS_NAMESPACE "." + p_itype.proxy_name + "\"/>"); - p_output.append(" class.\n"); - p_output.append(INDENT1 "/// </summary>"); - - if (p_isignal.is_deprecated) { - if (p_isignal.deprecation_message.is_empty()) { - WARN_PRINT("An empty deprecation message is discouraged. Signal: '" + p_isignal.proxy_name + "'."); - } - - p_output.append(MEMBER_BEGIN "[Obsolete(\""); - p_output.append(p_isignal.deprecation_message); - p_output.append("\")]"); - } - bool is_parameterless = p_isignal.arguments.size() == 0; // Delegate name is [SignalName]EventHandler String delegate_name = is_parameterless ? "Action" : p_isignal.proxy_name + "EventHandler"; if (!is_parameterless) { + p_output.append(MEMBER_BEGIN "/// <summary>\n"); + p_output.append(INDENT1 "/// "); + p_output.append("Represents the method that handles the "); + p_output.append("<see cref=\"" BINDINGS_NAMESPACE "." + p_itype.proxy_name + "." + p_isignal.proxy_name + "\"/>"); + p_output.append(" event of a "); + p_output.append("<see cref=\"" BINDINGS_NAMESPACE "." + p_itype.proxy_name + "\"/>"); + p_output.append(" class.\n"); + p_output.append(INDENT1 "/// </summary>"); + + if (p_isignal.is_deprecated) { + if (p_isignal.deprecation_message.is_empty()) { + WARN_PRINT("An empty deprecation message is discouraged. Signal: '" + p_isignal.proxy_name + "'."); + } + + p_output.append(MEMBER_BEGIN "[Obsolete(\""); + p_output.append(p_isignal.deprecation_message); + p_output.append("\")]"); + } + // Generate delegate p_output.append(MEMBER_BEGIN "public delegate void "); p_output.append(delegate_name); @@ -2367,7 +2405,7 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf } if (p_isignal.method_doc && p_isignal.method_doc->description.size()) { - String xml_summary = bbcode_to_xml(fix_doc_description(p_isignal.method_doc->description), &p_itype); + String xml_summary = bbcode_to_xml(fix_doc_description(p_isignal.method_doc->description), &p_itype, true); Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>(); if (summary_lines.size()) { @@ -2702,6 +2740,20 @@ const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_null(con return nullptr; } +const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_singleton_or_null(const TypeReference &p_typeref) { + const TypeInterface *itype = _get_type_or_null(p_typeref); + if (itype == nullptr) { + return nullptr; + } + + if (itype->is_singleton) { + StringName instance_type_name = itype->name + CS_SINGLETON_INSTANCE_SUFFIX; + itype = &obj_types.find(instance_type_name)->value; + } + + return itype; +} + const String BindingsGenerator::_get_generic_type_parameters(const TypeInterface &p_itype, const List<TypeReference> &p_generic_type_parameters) { if (p_generic_type_parameters.is_empty()) { return ""; @@ -2715,8 +2767,8 @@ const String BindingsGenerator::_get_generic_type_parameters(const TypeInterface int i = 0; String params = "<"; for (const TypeReference ¶m_type : p_generic_type_parameters) { - const TypeInterface *param_itype = _get_type_or_null(param_type); - ERR_FAIL_NULL_V(param_itype, ""); + const TypeInterface *param_itype = _get_type_or_singleton_or_null(param_type); + ERR_FAIL_NULL_V(param_itype, ""); // Parameter type not found ERR_FAIL_COND_V_MSG(param_itype->is_singleton, "", "Generic type parameter is a singleton: '" + param_itype->name + "'."); @@ -2932,17 +2984,18 @@ bool BindingsGenerator::_populate_object_type_interfaces() { itype.is_ref_counted = ClassDB::is_parent_class(type_cname, name_cache.type_RefCounted); itype.memory_own = itype.is_ref_counted; + if (itype.is_singleton && compat_singletons.has(itype.cname)) { + itype.is_singleton = false; + itype.is_compat_singleton = true; + } + itype.c_out = "%5return "; itype.c_out += C_METHOD_UNMANAGED_GET_MANAGED; itype.c_out += itype.is_ref_counted ? "(%1.Reference);\n" : "(%1);\n"; itype.cs_type = itype.proxy_name; - if (itype.is_singleton) { - itype.cs_in_expr = "GodotObject." CS_STATIC_METHOD_GETINSTANCE "(" CS_PROPERTY_SINGLETON ")"; - } else { - itype.cs_in_expr = "GodotObject." CS_STATIC_METHOD_GETINSTANCE "(%0)"; - } + itype.cs_in_expr = "GodotObject." CS_STATIC_METHOD_GETINSTANCE "(%0)"; itype.cs_out = "%5return (%2)%0(%1);"; @@ -3278,8 +3331,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() { enum_proxy_name += "Enum"; enum_proxy_cname = StringName(enum_proxy_name); } - EnumInterface ienum(enum_proxy_cname); - ienum.is_flags = E.value.is_bitfield; + EnumInterface ienum(enum_proxy_cname, enum_proxy_name, E.value.is_bitfield); const List<StringName> &enum_constants = E.value.constants; for (const StringName &constant_cname : enum_constants) { String constant_name = constant_cname.operator String(); @@ -3346,6 +3398,19 @@ bool BindingsGenerator::_populate_object_type_interfaces() { obj_types.insert(itype.cname, itype); + if (itype.is_singleton) { + // Add singleton instance type. + itype.proxy_name += CS_SINGLETON_INSTANCE_SUFFIX; + itype.is_singleton = false; + itype.is_singleton_instance = true; + + // Remove constants and enums, those will remain in the static class. + itype.constants.clear(); + itype.enums.clear(); + + obj_types.insert(itype.name + CS_SINGLETON_INSTANCE_SUFFIX, itype); + } + class_list.pop_front(); } @@ -3821,7 +3886,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { builtin_types.insert(itype.cname, itype); // Array_@generic - // Re-use Array's itype + // Reuse Array's itype itype.name = "Array_@generic"; itype.cname = itype.name; itype.cs_out = "%5return new %2(%0(%1));"; @@ -3848,7 +3913,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { builtin_types.insert(itype.cname, itype); // Dictionary_@generic - // Re-use Dictionary's itype + // Reuse Dictionary's itype itype.name = "Dictionary_@generic"; itype.cname = itype.name; itype.cs_out = "%5return new %2(%0(%1));"; @@ -3899,8 +3964,7 @@ void BindingsGenerator::_populate_global_constants() { iconstant.const_doc = const_doc; if (enum_name != StringName()) { - EnumInterface ienum(enum_name); - ienum.is_flags = CoreConstants::is_global_constant_bitfield(i); + EnumInterface ienum(enum_name, pascal_to_pascal_case(enum_name.operator String()), CoreConstants::is_global_constant_bitfield(i)); List<EnumInterface>::Element *enum_match = global_enums.find(ienum); if (enum_match) { enum_match->get().constants.push_back(iconstant); @@ -3918,7 +3982,7 @@ void BindingsGenerator::_populate_global_constants() { enum_itype.is_enum = true; enum_itype.name = ienum.cname.operator String(); enum_itype.cname = ienum.cname; - enum_itype.proxy_name = pascal_to_pascal_case(enum_itype.name); + enum_itype.proxy_name = ienum.proxy_name; TypeInterface::postsetup_enum_type(enum_itype); enum_types.insert(enum_itype.cname, enum_itype); @@ -3962,6 +4026,10 @@ void BindingsGenerator::_initialize_blacklisted_methods() { blacklisted_methods["Object"].push_back("_init"); // never called in C# (TODO: implement it) } +void BindingsGenerator::_initialize_compat_singletons() { + compat_singletons.insert("EditorInterface"); +} + void BindingsGenerator::_log(const char *p_format, ...) { if (log_print_enabled) { va_list list; @@ -3981,6 +4049,8 @@ void BindingsGenerator::_initialize() { _initialize_blacklisted_methods(); + _initialize_compat_singletons(); + bool obj_type_ok = _populate_object_type_interfaces(); ERR_FAIL_COND_MSG(!obj_type_ok, "Failed to generate object type interfaces"); diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index 38347a5181..6118576bb6 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -60,6 +60,7 @@ class BindingsGenerator { struct EnumInterface { StringName cname; + String proxy_name; List<ConstantInterface> constants; bool is_flags = false; @@ -69,8 +70,10 @@ class BindingsGenerator { EnumInterface() {} - EnumInterface(const StringName &p_cname) { + EnumInterface(const StringName &p_cname, const String &p_proxy_name, bool p_is_flags) { cname = p_cname; + proxy_name = p_proxy_name; + is_flags = p_is_flags; } }; @@ -227,9 +230,18 @@ class BindingsGenerator { bool is_enum = false; bool is_object_type = false; bool is_singleton = false; + bool is_singleton_instance = false; bool is_ref_counted = false; /** + * Class is a singleton, but can't be declared as a static class as that would + * break backwards compatibility. As such, instead of going with a static class, + * we use the actual singleton pattern (private constructor with instance property), + * which doesn't break compatibility. + */ + bool is_compat_singleton = false; + + /** * Determines whether the native return value of this type must be disposed * by the generated internal call (think of `godot_string`, whose destructor * must be called). Some structs that are disposable may still disable this @@ -614,8 +626,10 @@ class BindingsGenerator { HashMap<const MethodInterface *, const InternalCall *> method_icalls_map; HashMap<StringName, List<StringName>> blacklisted_methods; + HashSet<StringName> compat_singletons; void _initialize_blacklisted_methods(); + void _initialize_compat_singletons(); struct NameCache { StringName type_void = StaticCString::create("void"); @@ -740,7 +754,7 @@ class BindingsGenerator { return p_type->name; } - String bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype); + String bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype, bool p_is_signal = false); void _append_xml_method(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector<String> &p_link_target_parts); void _append_xml_member(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector<String> &p_link_target_parts); @@ -748,6 +762,7 @@ class BindingsGenerator { void _append_xml_enum(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector<String> &p_link_target_parts); void _append_xml_constant(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector<String> &p_link_target_parts); void _append_xml_constant_in_global_scope(StringBuilder &p_xml_output, const String &p_target_cname, const String &p_link_target); + void _append_xml_param(StringBuilder &p_xml_output, const String &p_link_target, bool p_is_signal); void _append_xml_undeclared(StringBuilder &p_xml_output, const String &p_link_target); int _determine_enum_prefix(const EnumInterface &p_ienum); @@ -756,6 +771,7 @@ class BindingsGenerator { Error _populate_method_icalls_table(const TypeInterface &p_itype); const TypeInterface *_get_type_or_null(const TypeReference &p_typeref); + const TypeInterface *_get_type_or_singleton_or_null(const TypeReference &p_typeref); const String _get_generic_type_parameters(const TypeInterface &p_itype, const List<TypeReference> &p_generic_type_parameters); diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp index ba6b91b704..fc99f3ceda 100644 --- a/modules/mono/editor/editor_internal_calls.cpp +++ b/modules/mono/editor/editor_internal_calls.cpp @@ -168,6 +168,10 @@ void godot_icall_Internal_EditorRunStop() { EditorRunBar::get_singleton()->stop_playing(); } +void godot_icall_Internal_EditorPlugin_AddControlToEditorRunBar(Control *p_control) { + EditorRunBar::get_singleton()->get_buttons_container()->add_child(p_control); +} + void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts() { EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); if (ed) { @@ -199,12 +203,25 @@ void godot_icall_Globals_EditorDef(const godot_string *p_setting, const godot_va memnew_placement(r_result, Variant(result)); } -void godot_icall_Globals_EditorShortcut(const godot_string *p_setting, godot_variant *r_result) { +void godot_icall_Globals_EditorDefShortcut(const godot_string *p_setting, const godot_string *p_name, Key p_keycode, bool p_physical, godot_variant *r_result) { + String setting = *reinterpret_cast<const String *>(p_setting); + String name = *reinterpret_cast<const String *>(p_name); + Ref<Shortcut> result = ED_SHORTCUT(setting, name, p_keycode, p_physical); + memnew_placement(r_result, Variant(result)); +} + +void godot_icall_Globals_EditorGetShortcut(const godot_string *p_setting, Ref<Shortcut> *r_result) { String setting = *reinterpret_cast<const String *>(p_setting); Ref<Shortcut> result = ED_GET_SHORTCUT(setting); memnew_placement(r_result, Variant(result)); } +void godot_icall_Globals_EditorShortcutOverride(const godot_string *p_setting, const godot_string *p_feature, Key p_keycode, bool p_physical) { + String setting = *reinterpret_cast<const String *>(p_setting); + String feature = *reinterpret_cast<const String *>(p_feature); + ED_SHORTCUT_OVERRIDE(setting, feature, p_keycode, p_physical); +} + void godot_icall_Globals_TTR(const godot_string *p_text, godot_string *r_dest) { String text = *reinterpret_cast<const String *>(p_text); memnew_placement(r_dest, String(TTR(text))); @@ -251,12 +268,15 @@ static const void *unmanaged_callbacks[]{ (void *)godot_icall_Internal_EditorNodeShowScriptScreen, (void *)godot_icall_Internal_EditorRunPlay, (void *)godot_icall_Internal_EditorRunStop, + (void *)godot_icall_Internal_EditorPlugin_AddControlToEditorRunBar, (void *)godot_icall_Internal_ScriptEditorDebugger_ReloadScripts, (void *)godot_icall_Internal_CodeCompletionRequest, (void *)godot_icall_Globals_EditorScale, (void *)godot_icall_Globals_GlobalDef, (void *)godot_icall_Globals_EditorDef, - (void *)godot_icall_Globals_EditorShortcut, + (void *)godot_icall_Globals_EditorDefShortcut, + (void *)godot_icall_Globals_EditorGetShortcut, + (void *)godot_icall_Globals_EditorShortcutOverride, (void *)godot_icall_Globals_TTR, (void *)godot_icall_Utils_OS_GetPlatformName, (void *)godot_icall_Utils_OS_UnixFileHasExecutableAccess, diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs index dfae85b667..0fe4bcdfce 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs @@ -276,8 +276,13 @@ namespace Godot.Bridge if (wrapperType != null && IsStatic(wrapperType)) { - // A static class means this is a Godot singleton class. If an instance is needed we use GodotObject. - return typeof(GodotObject); + // A static class means this is a Godot singleton class. Try to get the Instance proxy type. + wrapperType = TypeGetProxyClass($"{nativeTypeNameStr}Instance"); + if (wrapperType == null) + { + // Otherwise, fallback to GodotObject. + return typeof(GodotObject); + } } return wrapperType; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs index c4161d2ded..57b292793a 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs @@ -14,14 +14,46 @@ namespace Godot { private static void AppendTypeName(this StringBuilder sb, Type type) { - if (type.IsPrimitive) - sb.Append(type.Name); - else if (type == typeof(void)) + // Use the C# type keyword for built-in types. + // https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/built-in-types + if (type == typeof(void)) sb.Append("void"); + else if (type == typeof(bool)) + sb.Append("bool"); + else if (type == typeof(byte)) + sb.Append("byte"); + else if (type == typeof(sbyte)) + sb.Append("sbyte"); + else if (type == typeof(char)) + sb.Append("char"); + else if (type == typeof(decimal)) + sb.Append("decimal"); + else if (type == typeof(double)) + sb.Append("double"); + else if (type == typeof(float)) + sb.Append("float"); + else if (type == typeof(int)) + sb.Append("int"); + else if (type == typeof(uint)) + sb.Append("uint"); + else if (type == typeof(nint)) + sb.Append("nint"); + else if (type == typeof(nuint)) + sb.Append("nuint"); + else if (type == typeof(long)) + sb.Append("long"); + else if (type == typeof(ulong)) + sb.Append("ulong"); + else if (type == typeof(short)) + sb.Append("short"); + else if (type == typeof(ushort)) + sb.Append("ushort"); + else if (type == typeof(object)) + sb.Append("object"); + else if (type == typeof(string)) + sb.Append("string"); else sb.Append(type); - - sb.Append(' '); } internal static void InstallTraceListener() @@ -70,13 +102,26 @@ namespace Godot } } + internal static unsafe StackFrame? GetCurrentStackFrame(int skipFrames = 0) + { + // We skip 2 frames: + // The first skipped frame is the current method. + // The second skipped frame is a method in NativeInterop.NativeFuncs. + var stackTrace = new StackTrace(skipFrames: 2 + skipFrames, fNeedFileInfo: true); + return stackTrace.GetFrame(0); + } + [UnmanagedCallersOnly] internal static unsafe void GetCurrentStackInfo(void* destVector) { try { var vector = (godot_stack_info_vector*)destVector; - var stackTrace = new StackTrace(skipFrames: 1, fNeedFileInfo: true); + + // We skip 2 frames: + // The first skipped frame is the current method. + // The second skipped frame is a method in NativeInterop.NativeFuncs. + var stackTrace = new StackTrace(skipFrames: 2, fNeedFileInfo: true); int frameCount = stackTrace.FrameCount; if (frameCount == 0) @@ -87,6 +132,14 @@ namespace Godot int i = 0; foreach (StackFrame frame in stackTrace.GetFrames()) { + var method = frame.GetMethod(); + + if (method is MethodInfo methodInfo && methodInfo.IsDefined(typeof(StackTraceHiddenAttribute))) + { + // Skip methods marked hidden from the stack trace. + continue; + } + string? fileName = frame.GetFileName(); int fileLineNumber = frame.GetFileLineNumber(); @@ -102,6 +155,9 @@ namespace Godot i++; } + + // Resize the vector again in case we skipped some frames. + vector->Resize(i); } catch (Exception e) { @@ -122,7 +178,10 @@ namespace Godot var sb = new StringBuilder(); if (methodBase is MethodInfo methodInfo) + { sb.AppendTypeName(methodInfo.ReturnType); + sb.Append(' '); + } sb.Append(methodBase.DeclaringType?.FullName ?? "<unknown>"); sb.Append('.'); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs index 9425b7424c..33ebb8171e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Text; using Godot.NativeInterop; @@ -334,6 +335,21 @@ namespace Godot NativeFuncs.godotsharp_printt(godotStr); } + [StackTraceHidden] + private static void ErrPrintError(string message, godot_error_handler_type type = godot_error_handler_type.ERR_HANDLER_ERROR) + { + // Skip 1 frame to avoid current method. + var stackFrame = DebuggingUtils.GetCurrentStackFrame(skipFrames: 1); + string callerFilePath = ProjectSettings.LocalizePath(stackFrame.GetFileName()); + DebuggingUtils.GetStackFrameMethodDecl(stackFrame, out string callerName); + int callerLineNumber = stackFrame.GetFileLineNumber(); + + using godot_string messageStr = Marshaling.ConvertStringToNative(message); + using godot_string callerNameStr = Marshaling.ConvertStringToNative(callerName); + using godot_string callerFilePathStr = Marshaling.ConvertStringToNative(callerFilePath); + NativeFuncs.godotsharp_err_print_error(callerNameStr, callerFilePathStr, callerLineNumber, messageStr, p_type: type); + } + /// <summary> /// Pushes an error message to Godot's built-in debugger and to the OS terminal. /// @@ -347,8 +363,7 @@ namespace Godot /// <param name="message">Error message.</param> public static void PushError(string message) { - using var godotStr = Marshaling.ConvertStringToNative(message); - NativeFuncs.godotsharp_pusherror(godotStr); + ErrPrintError(message); } /// <summary> @@ -364,7 +379,7 @@ namespace Godot /// <param name="what">Arguments that form the error message.</param> public static void PushError(params object[] what) { - PushError(AppendPrintParams(what)); + ErrPrintError(AppendPrintParams(what)); } /// <summary> @@ -378,8 +393,7 @@ namespace Godot /// <param name="message">Warning message.</param> public static void PushWarning(string message) { - using var godotStr = Marshaling.ConvertStringToNative(message); - NativeFuncs.godotsharp_pushwarning(godotStr); + ErrPrintError(message, type: godot_error_handler_type.ERR_HANDLER_WARNING); } /// <summary> @@ -393,7 +407,7 @@ namespace Godot /// <param name="what">Arguments that form the warning message.</param> public static void PushWarning(params object[] what) { - PushWarning(AppendPrintParams(what)); + ErrPrintError(AppendPrintParams(what), type: godot_error_handler_type.ERR_HANDLER_WARNING); } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs index ba2c232580..a656c5de90 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs @@ -95,7 +95,7 @@ namespace Godot.NativeInterop } NativeFuncs.godotsharp_internal_script_debugger_send_error(nFunc, nFile, line, - nErrorMsg, nExcMsg, p_warning: godot_bool.False, stackInfoVector); + nErrorMsg, nExcMsg, godot_error_handler_type.ERR_HANDLER_ERROR, stackInfoVector); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs index 1dddc82e85..d5d9404ed1 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs @@ -1134,4 +1134,13 @@ namespace Godot.NativeInterop get => _ptr != null ? *((int*)_ptr - 1) : 0; } } + + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum godot_error_handler_type + { + ERR_HANDLER_ERROR = 0, + ERR_HANDLER_WARNING, + ERR_HANDLER_SCRIPT, + ERR_HANDLER_SHADER, + } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index 3ec3d1e530..d42ee15657 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -58,7 +58,7 @@ namespace Godot.NativeInterop internal static partial void godotsharp_internal_script_debugger_send_error(in godot_string p_func, in godot_string p_file, int p_line, in godot_string p_err, in godot_string p_descr, - godot_bool p_warning, in DebuggingUtils.godot_stack_info_vector p_stack_info_vector); + godot_error_handler_type p_type, in DebuggingUtils.godot_stack_info_vector p_stack_info_vector); internal static partial godot_bool godotsharp_internal_script_debugger_is_active(); @@ -540,9 +540,7 @@ namespace Godot.NativeInterop internal static partial void godotsharp_var_to_str(in godot_variant p_var, out godot_string r_ret); - internal static partial void godotsharp_pusherror(in godot_string p_str); - - internal static partial void godotsharp_pushwarning(in godot_string p_str); + internal static partial void godotsharp_err_print_error(in godot_string p_function, in godot_string p_file, int p_line, in godot_string p_error, in godot_string p_message = default, godot_bool p_editor_notify = godot_bool.False, godot_error_handler_type p_type = godot_error_handler_type.ERR_HANDLER_ERROR); // Object diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp index ee4de4e9f5..24a9d4030a 100644 --- a/modules/mono/glue/runtime_interop.cpp +++ b/modules/mono/glue/runtime_interop.cpp @@ -92,10 +92,10 @@ void godotsharp_stack_info_vector_destroy( void godotsharp_internal_script_debugger_send_error(const String *p_func, const String *p_file, int32_t p_line, const String *p_err, const String *p_descr, - bool p_warning, const Vector<ScriptLanguage::StackInfo> *p_stack_info_vector) { + ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> *p_stack_info_vector) { const String file = ProjectSettings::get_singleton()->localize_path(p_file->simplify_path()); EngineDebugger::get_script_debugger()->send_error(*p_func, file, p_line, *p_err, *p_descr, - true, p_warning ? ERR_HANDLER_WARNING : ERR_HANDLER_ERROR, *p_stack_info_vector); + true, p_type, *p_stack_info_vector); } bool godotsharp_internal_script_debugger_is_active() { @@ -1320,12 +1320,14 @@ void godotsharp_printraw(const godot_string *p_what) { OS::get_singleton()->print("%s", reinterpret_cast<const String *>(p_what)->utf8().get_data()); } -void godotsharp_pusherror(const godot_string *p_str) { - ERR_PRINT(*reinterpret_cast<const String *>(p_str)); -} - -void godotsharp_pushwarning(const godot_string *p_str) { - WARN_PRINT(*reinterpret_cast<const String *>(p_str)); +void godotsharp_err_print_error(const godot_string *p_function, const godot_string *p_file, int32_t p_line, const godot_string *p_error, const godot_string *p_message, bool p_editor_notify, ErrorHandlerType p_type) { + _err_print_error( + reinterpret_cast<const String *>(p_function)->utf8().get_data(), + reinterpret_cast<const String *>(p_file)->utf8().get_data(), + p_line, + reinterpret_cast<const String *>(p_error)->utf8().get_data(), + reinterpret_cast<const String *>(p_message)->utf8().get_data(), + p_editor_notify, p_type); } void godotsharp_var_to_str(const godot_variant *p_var, godot_string *r_ret) { @@ -1611,8 +1613,7 @@ static const void *unmanaged_callbacks[]{ (void *)godotsharp_str_to_var, (void *)godotsharp_var_to_bytes, (void *)godotsharp_var_to_str, - (void *)godotsharp_pusherror, - (void *)godotsharp_pushwarning, + (void *)godotsharp_err_print_error, (void *)godotsharp_object_to_string, }; diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp index 279b5cfed2..c84ecf4ceb 100644 --- a/modules/mono/godotsharp_dirs.cpp +++ b/modules/mono/godotsharp_dirs.cpp @@ -33,10 +33,6 @@ #include "mono_gd/gd_mono.h" #include "utils/path_utils.h" -#ifdef ANDROID_ENABLED -#include "mono_gd/support/android_support.h" -#endif - #include "core/config/project_settings.h" #include "core/io/dir_access.h" #include "core/os/os.h" @@ -95,6 +91,38 @@ String _get_mono_user_dir() { #endif } +#if !TOOLS_ENABLED +// This should be the equivalent of GodotTools.Utils.OS.PlatformNameMap. +static const char *platform_name_map[][2] = { + { "Windows", "windows" }, + { "macOS", "macos" }, + { "Linux", "linuxbsd" }, + { "FreeBSD", "linuxbsd" }, + { "NetBSD", "linuxbsd" }, + { "BSD", "linuxbsd" }, + { "UWP", "uwp" }, + { "Haiku", "haiku" }, + { "Android", "android" }, + { "iOS", "ios" }, + { "Web", "web" }, + { nullptr, nullptr } +}; + +String _get_platform_name() { + String platform_name = OS::get_singleton()->get_name(); + + int idx = 0; + while (platform_name_map[idx][0] != nullptr) { + if (platform_name_map[idx][0] == platform_name) { + return platform_name_map[idx][1]; + } + idx++; + } + + return ""; +} +#endif + class _GodotSharpDirs { public: String res_metadata_dir; @@ -139,12 +167,13 @@ private: #endif api_assemblies_dir = api_assemblies_base_dir.path_join(GDMono::get_expected_api_build_config()); #else // TOOLS_ENABLED + String platform = _get_platform_name(); String arch = Engine::get_singleton()->get_architecture_name(); String appname_safe = path::get_csharp_project_name(); String packed_path = "res://.godot/mono/publish/" + arch; if (DirAccess::exists(packed_path)) { // The dotnet publish data is packed in the pck/zip. - String data_dir_root = OS::get_singleton()->get_cache_path().path_join("data_" + appname_safe + "_" + arch); + String data_dir_root = OS::get_singleton()->get_cache_path().path_join("data_" + appname_safe + "_" + platform + "_" + arch); bool has_data = false; if (!has_data) { // 1. Try to access the data directly. @@ -173,16 +202,10 @@ private: api_assemblies_dir = data_dir_root; } else { // The dotnet publish data is in a directory next to the executable. - String data_dir_root = exe_dir.path_join("data_" + appname_safe + "_" + arch); - if (!DirAccess::exists(data_dir_root)) { - data_dir_root = exe_dir.path_join("data_Godot_" + arch); - } + String data_dir_root = exe_dir.path_join("data_" + appname_safe + "_" + platform + "_" + arch); #ifdef MACOS_ENABLED if (!DirAccess::exists(data_dir_root)) { - data_dir_root = res_dir.path_join("data_" + appname_safe + "_" + arch); - } - if (!DirAccess::exists(data_dir_root)) { - data_dir_root = res_dir.path_join("data_Godot_" + arch); + data_dir_root = res_dir.path_join("data_" + appname_safe + "_" + platform + "_" + arch); } #endif api_assemblies_dir = data_dir_root; diff --git a/modules/mono/icons/BuildCSharp.svg b/modules/mono/icons/BuildCSharp.svg new file mode 100644 index 0000000000..9d0102c35d --- /dev/null +++ b/modules/mono/icons/BuildCSharp.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M 9.6060193,0.78346667 C 8.6741914,0.96303367 7.6708299,1.5334576 6.9028943,1.9768256 l -2.1523438,1.244141 0.082031,0.138672 -0.3105469,-0.00781 -2.5839844,1.492188 1.9101563,3.308593 2.5820312,-1.490234 0.1425781,-0.255859 4.1875002,7.2539054 0.5,0.867187 c 0.415803,0.720194 1.331398,0.964165 2.050782,0.548829 0.719286,-0.415279 0.963839,-1.33001 0.548828,-2.048829 l -2,-3.4648424 -2.8808602,-4.990235 3.7070322,-2.101562 -0.265626,-0.439453 C 11.697382,0.83561667 10.650124,0.58226267 9.6060193,0.78346667 Z" fill="#e0e0e0"/></svg> diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 5e52f25cf4..247968e251 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -376,6 +376,12 @@ void GDMono::initialize() { godot_plugins_initialize_fn godot_plugins_initialize = nullptr; + // Check that the .NET assemblies directory exists before trying to use it. + if (!DirAccess::exists(GodotSharpDirs::get_api_assemblies_dir())) { + OS::get_singleton()->alert(vformat(RTR("Unable to find the .NET assemblies directory.\nMake sure the '%s' directory exists and contains the .NET assemblies."), GodotSharpDirs::get_api_assemblies_dir()), RTR(".NET assemblies not found")); + ERR_FAIL_MSG(".NET: Assemblies not found"); + } + if (!load_hostfxr(hostfxr_dll_handle)) { #if !defined(TOOLS_ENABLED) godot_plugins_initialize = try_load_native_aot_library(hostfxr_dll_handle); diff --git a/modules/navigation/editor/navigation_mesh_editor_plugin.cpp b/modules/navigation/editor/navigation_mesh_editor_plugin.cpp index 634d70d3bd..535091e5b6 100644 --- a/modules/navigation/editor/navigation_mesh_editor_plugin.cpp +++ b/modules/navigation/editor/navigation_mesh_editor_plugin.cpp @@ -32,12 +32,11 @@ #ifdef TOOLS_ENABLED -#include "../navigation_mesh_generator.h" - #include "core/io/marshalls.h" #include "core/io/resource_saver.h" #include "editor/editor_node.h" #include "scene/3d/mesh_instance_3d.h" +#include "scene/3d/navigation_region_3d.h" #include "scene/gui/box_container.h" #include "scene/gui/button.h" #include "scene/gui/dialogs.h" @@ -99,18 +98,16 @@ void NavigationMeshEditor::_bake_pressed() { } } - NavigationMeshGenerator::get_singleton()->clear(node->get_navigation_mesh()); - Ref<NavigationMeshSourceGeometryData3D> source_geometry_data; - source_geometry_data.instantiate(); - NavigationMeshGenerator::get_singleton()->parse_source_geometry_data(node->get_navigation_mesh(), source_geometry_data, node); - NavigationMeshGenerator::get_singleton()->bake_from_source_geometry_data(node->get_navigation_mesh(), source_geometry_data); + node->bake_navigation_mesh(false); node->update_gizmos(); } void NavigationMeshEditor::_clear_pressed() { if (node) { - NavigationMeshGenerator::get_singleton()->clear(node->get_navigation_mesh()); + if (node->get_navigation_mesh().is_valid()) { + node->get_navigation_mesh()->clear(); + } } button_bake->set_pressed(false); @@ -139,14 +136,15 @@ NavigationMeshEditor::NavigationMeshEditor() { button_bake->set_flat(true); bake_hbox->add_child(button_bake); button_bake->set_toggle_mode(true); - button_bake->set_text(TTR("Bake NavMesh")); + button_bake->set_text(TTR("Bake NavigationMesh")); + button_bake->set_tooltip_text(TTR("Bakes the NavigationMesh by first parsing the scene for source geometry and then creating the navigation mesh vertices and polygons.")); button_bake->connect("pressed", callable_mp(this, &NavigationMeshEditor::_bake_pressed)); button_reset = memnew(Button); button_reset->set_flat(true); bake_hbox->add_child(button_reset); - // No button text, we only use a revert icon which is set when entering the tree. - button_reset->set_tooltip_text(TTR("Clear the navigation mesh.")); + button_reset->set_text(TTR("Clear NavigationMesh")); + button_reset->set_tooltip_text(TTR("Clears the internal NavigationMesh vertices and polygons.")); button_reset->connect("pressed", callable_mp(this, &NavigationMeshEditor::_clear_pressed)); bake_info = memnew(Label); diff --git a/modules/navigation/godot_navigation_server.cpp b/modules/navigation/godot_navigation_server.cpp index c0fa6eef9e..9162fcf171 100644 --- a/modules/navigation/godot_navigation_server.cpp +++ b/modules/navigation/godot_navigation_server.cpp @@ -31,8 +31,8 @@ #include "godot_navigation_server.h" #ifndef _3D_DISABLED -#include "navigation_mesh_generator.h" -#endif +#include "nav_mesh_generator_3d.h" +#endif // _3D_DISABLED #include "core/os/mutex.h" @@ -468,11 +468,11 @@ void GodotNavigationServer::region_bake_navigation_mesh(Ref<NavigationMesh> p_na WARN_PRINT_ONCE("NavigationServer3D::region_bake_navigation_mesh() is deprecated due to core threading changes. To upgrade existing code, first create a NavigationMeshSourceGeometryData3D resource. Use this resource with method parse_source_geometry_data() to parse the SceneTree for nodes that should contribute to the navigation mesh baking. The SceneTree parsing needs to happen on the main thread. After the parsing is finished use the resource with method bake_from_source_geometry_data() to bake a navigation mesh.."); #ifndef _3D_DISABLED - NavigationMeshGenerator::get_singleton()->clear(p_navigation_mesh); + p_navigation_mesh->clear(); Ref<NavigationMeshSourceGeometryData3D> source_geometry_data; source_geometry_data.instantiate(); - NavigationMeshGenerator::get_singleton()->parse_source_geometry_data(p_navigation_mesh, source_geometry_data, p_root_node); - NavigationMeshGenerator::get_singleton()->bake_from_source_geometry_data(p_navigation_mesh, source_geometry_data); + parse_source_geometry_data(p_navigation_mesh, source_geometry_data, p_root_node); + bake_from_source_geometry_data(p_navigation_mesh, source_geometry_data); #endif } #endif // DISABLE_DEPRECATED @@ -930,16 +930,36 @@ COMMAND_2(obstacle_set_avoidance_layers, RID, p_obstacle, uint32_t, p_layers) { obstacle->set_avoidance_layers(p_layers); } -void GodotNavigationServer::parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback) { +void GodotNavigationServer::parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback) { #ifndef _3D_DISABLED - NavigationMeshGenerator::get_singleton()->parse_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_root_node, p_callback); -#endif + ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "The SceneTree can only be parsed on the main thread. Call this function from the main thread or use call_deferred()."); + ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh."); + ERR_FAIL_COND_MSG(p_root_node == nullptr, "No parsing root node specified."); + ERR_FAIL_COND_MSG(!p_root_node->is_inside_tree(), "The root node needs to be inside the SceneTree."); + + ERR_FAIL_NULL(NavMeshGenerator3D::get_singleton()); + NavMeshGenerator3D::get_singleton()->parse_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_root_node, p_callback); +#endif // _3D_DISABLED } -void GodotNavigationServer::bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback) { +void GodotNavigationServer::bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback) { #ifndef _3D_DISABLED - NavigationMeshGenerator::get_singleton()->bake_from_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_callback); -#endif + ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh."); + ERR_FAIL_COND_MSG(!p_source_geometry_data.is_valid(), "Invalid NavigationMeshSourceGeometryData3D."); + + ERR_FAIL_NULL(NavMeshGenerator3D::get_singleton()); + NavMeshGenerator3D::get_singleton()->bake_from_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_callback); +#endif // _3D_DISABLED +} + +void GodotNavigationServer::bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback) { +#ifndef _3D_DISABLED + ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh."); + ERR_FAIL_COND_MSG(!p_source_geometry_data.is_valid(), "Invalid NavigationMeshSourceGeometryData3D."); + + ERR_FAIL_NULL(NavMeshGenerator3D::get_singleton()); + NavMeshGenerator3D::get_singleton()->bake_from_source_geometry_data_async(p_navigation_mesh, p_source_geometry_data, p_callback); +#endif // _3D_DISABLED } COMMAND_1(free, RID, p_object) { @@ -1073,6 +1093,16 @@ void GodotNavigationServer::process(real_t p_delta_time) { return; } +#ifndef _3D_DISABLED + // Sync finished navmesh bakes before doing NavMap updates. + if (navmesh_generator_3d) { + navmesh_generator_3d->sync(); + // Finished bakes emit callbacks and users might have reacted to those. + // Flush queue again so users do not have to wait for the next sync. + flush_queries(); + } +#endif // _3D_DISABLED + int _new_pm_region_count = 0; int _new_pm_agent_count = 0; int _new_pm_link_count = 0; @@ -1117,6 +1147,23 @@ void GodotNavigationServer::process(real_t p_delta_time) { pm_edge_free_count = _new_pm_edge_free_count; } +void GodotNavigationServer::init() { +#ifndef _3D_DISABLED + navmesh_generator_3d = memnew(NavMeshGenerator3D); +#endif // _3D_DISABLED +} + +void GodotNavigationServer::finish() { + flush_queries(); +#ifndef _3D_DISABLED + if (navmesh_generator_3d) { + navmesh_generator_3d->finish(); + memdelete(navmesh_generator_3d); + navmesh_generator_3d = nullptr; + } +#endif // _3D_DISABLED +} + PathQueryResult GodotNavigationServer::_query_path(const PathQueryParameters &p_parameters) const { PathQueryResult r_query_result; diff --git a/modules/navigation/godot_navigation_server.h b/modules/navigation/godot_navigation_server.h index 0b3789102c..40893bada6 100644 --- a/modules/navigation/godot_navigation_server.h +++ b/modules/navigation/godot_navigation_server.h @@ -56,6 +56,7 @@ void MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1) class GodotNavigationServer; +class NavMeshGenerator3D; struct SetCommand { virtual ~SetCommand() {} @@ -79,6 +80,8 @@ class GodotNavigationServer : public NavigationServer3D { LocalVector<NavMap *> active_maps; LocalVector<uint32_t> active_maps_update_id; + NavMeshGenerator3D *navmesh_generator_3d = nullptr; + // Performance Monitor int pm_region_count = 0; int pm_agent_count = 0; @@ -225,8 +228,9 @@ public: virtual void obstacle_set_vertices(RID p_obstacle, const Vector<Vector3> &p_vertices) override; COMMAND_2(obstacle_set_avoidance_layers, RID, p_obstacle, uint32_t, p_layers); - virtual void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override; - virtual void bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override; + virtual void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override; + virtual void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override; + virtual void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override; COMMAND_1(free, RID, p_object); @@ -234,6 +238,8 @@ public: void flush_queries(); virtual void process(real_t p_delta_time) override; + virtual void init() override; + virtual void finish() override; virtual NavigationUtilities::PathQueryResult _query_path(const NavigationUtilities::PathQueryParameters &p_parameters) const override; diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp index 745c227fe5..737ccaf3cd 100644 --- a/modules/navigation/nav_map.cpp +++ b/modules/navigation/nav_map.cpp @@ -431,6 +431,17 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p if (p_optimize) { // Set the apex poly/point to the end point gd::NavigationPoly *apex_poly = &navigation_polys[least_cost_id]; + + Vector3 back_pathway[2] = { apex_poly->back_navigation_edge_pathway_start, apex_poly->back_navigation_edge_pathway_end }; + const Vector3 back_edge_closest_point = Geometry3D::get_closest_point_to_segment(end_point, back_pathway); + if (end_point.is_equal_approx(back_edge_closest_point)) { + // The end point is basically on top of the last crossed edge, funneling around the corners would at best do nothing. + // At worst it would add an unwanted path point before the last point due to precision issues so skip to the next polygon. + if (apex_poly->back_navigation_poly_id != -1) { + apex_poly = &navigation_polys[apex_poly->back_navigation_poly_id]; + } + } + Vector3 apex_point = end_point; gd::NavigationPoly *left_poly = apex_poly; @@ -1049,7 +1060,8 @@ void NavMap::sync() { } // Update the update ID. - map_update_id = (map_update_id + 1) % 9999999; + // Some code treats 0 as a failure case, so we avoid returning 0. + map_update_id = map_update_id % 9999999 + 1; } // Do we have modified obstacle positions? diff --git a/modules/navigation/nav_mesh_generator_3d.cpp b/modules/navigation/nav_mesh_generator_3d.cpp new file mode 100644 index 0000000000..7c27417e5f --- /dev/null +++ b/modules/navigation/nav_mesh_generator_3d.cpp @@ -0,0 +1,838 @@ +/**************************************************************************/ +/* nav_mesh_generator_3d.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef _3D_DISABLED + +#include "nav_mesh_generator_3d.h" + +#include "core/config/project_settings.h" +#include "core/math/convex_hull.h" +#include "core/os/thread.h" +#include "scene/3d/mesh_instance_3d.h" +#include "scene/3d/multimesh_instance_3d.h" +#include "scene/3d/physics_body_3d.h" +#include "scene/resources/box_shape_3d.h" +#include "scene/resources/capsule_shape_3d.h" +#include "scene/resources/concave_polygon_shape_3d.h" +#include "scene/resources/convex_polygon_shape_3d.h" +#include "scene/resources/cylinder_shape_3d.h" +#include "scene/resources/height_map_shape_3d.h" +#include "scene/resources/navigation_mesh.h" +#include "scene/resources/navigation_mesh_source_geometry_data_3d.h" +#include "scene/resources/primitive_meshes.h" +#include "scene/resources/shape_3d.h" +#include "scene/resources/sphere_shape_3d.h" +#include "scene/resources/world_boundary_shape_3d.h" + +#include "modules/modules_enabled.gen.h" // For csg, gridmap. + +#ifdef MODULE_CSG_ENABLED +#include "modules/csg/csg_shape.h" +#endif +#ifdef MODULE_GRIDMAP_ENABLED +#include "modules/gridmap/grid_map.h" +#endif + +#include <Recast.h> + +NavMeshGenerator3D *NavMeshGenerator3D::singleton = nullptr; +Mutex NavMeshGenerator3D::baking_navmesh_mutex; +Mutex NavMeshGenerator3D::generator_task_mutex; +bool NavMeshGenerator3D::use_threads = true; +bool NavMeshGenerator3D::baking_use_multiple_threads = true; +bool NavMeshGenerator3D::baking_use_high_priority_threads = true; +HashSet<Ref<NavigationMesh>> NavMeshGenerator3D::baking_navmeshes; +HashMap<WorkerThreadPool::TaskID, NavMeshGenerator3D::NavMeshGeneratorTask3D *> NavMeshGenerator3D::generator_tasks; + +NavMeshGenerator3D *NavMeshGenerator3D::get_singleton() { + return singleton; +} + +NavMeshGenerator3D::NavMeshGenerator3D() { + ERR_FAIL_COND(singleton != nullptr); + singleton = this; + + baking_use_multiple_threads = GLOBAL_GET("navigation/baking/thread_model/baking_use_multiple_threads"); + baking_use_high_priority_threads = GLOBAL_GET("navigation/baking/thread_model/baking_use_high_priority_threads"); + + // Using threads might cause problems on certain exports or with the Editor on certain devices. + // This is the main switch to turn threaded navmesh baking off should the need arise. + use_threads = baking_use_multiple_threads && !Engine::get_singleton()->is_editor_hint(); +} + +NavMeshGenerator3D::~NavMeshGenerator3D() { + cleanup(); +} + +void NavMeshGenerator3D::sync() { + if (generator_tasks.size() == 0) { + return; + } + + baking_navmesh_mutex.lock(); + generator_task_mutex.lock(); + + LocalVector<WorkerThreadPool::TaskID> finished_task_ids; + + for (KeyValue<WorkerThreadPool::TaskID, NavMeshGeneratorTask3D *> &E : generator_tasks) { + if (WorkerThreadPool::get_singleton()->is_task_completed(E.key)) { + WorkerThreadPool::get_singleton()->wait_for_task_completion(E.key); + finished_task_ids.push_back(E.key); + + NavMeshGeneratorTask3D *generator_task = E.value; + DEV_ASSERT(generator_task->status == NavMeshGeneratorTask3D::TaskStatus::BAKING_FINISHED); + + baking_navmeshes.erase(generator_task->navigation_mesh); + if (generator_task->callback.is_valid()) { + generator_emit_callback(generator_task->callback); + } + memdelete(generator_task); + } + } + + for (WorkerThreadPool::TaskID finished_task_id : finished_task_ids) { + generator_tasks.erase(finished_task_id); + } + + generator_task_mutex.unlock(); + baking_navmesh_mutex.unlock(); +} + +void NavMeshGenerator3D::cleanup() { + baking_navmesh_mutex.lock(); + generator_task_mutex.lock(); + + baking_navmeshes.clear(); + + for (KeyValue<WorkerThreadPool::TaskID, NavMeshGeneratorTask3D *> &E : generator_tasks) { + WorkerThreadPool::get_singleton()->wait_for_task_completion(E.key); + NavMeshGeneratorTask3D *generator_task = E.value; + memdelete(generator_task); + } + generator_tasks.clear(); + + generator_task_mutex.unlock(); + baking_navmesh_mutex.unlock(); +} + +void NavMeshGenerator3D::finish() { + cleanup(); +} + +void NavMeshGenerator3D::parse_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback) { + ERR_FAIL_COND(!Thread::is_main_thread()); + ERR_FAIL_COND(!p_navigation_mesh.is_valid()); + ERR_FAIL_COND(p_root_node == nullptr); + ERR_FAIL_COND(!p_root_node->is_inside_tree()); + ERR_FAIL_COND(!p_source_geometry_data.is_valid()); + + generator_parse_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_root_node); + + if (p_callback.is_valid()) { + generator_emit_callback(p_callback); + } +} + +void NavMeshGenerator3D::bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback) { + ERR_FAIL_COND(!p_navigation_mesh.is_valid()); + ERR_FAIL_COND(!p_source_geometry_data.is_valid()); + + if (!p_source_geometry_data->has_data()) { + p_navigation_mesh->clear(); + if (p_callback.is_valid()) { + generator_emit_callback(p_callback); + } + return; + } + + baking_navmesh_mutex.lock(); + if (baking_navmeshes.has(p_navigation_mesh)) { + baking_navmesh_mutex.unlock(); + ERR_FAIL_MSG("NavigationMesh is already baking. Wait for current bake to finish."); + } + baking_navmeshes.insert(p_navigation_mesh); + baking_navmesh_mutex.unlock(); + + generator_bake_from_source_geometry_data(p_navigation_mesh, p_source_geometry_data); + + baking_navmesh_mutex.lock(); + baking_navmeshes.erase(p_navigation_mesh); + baking_navmesh_mutex.unlock(); + + if (p_callback.is_valid()) { + generator_emit_callback(p_callback); + } +} + +void NavMeshGenerator3D::bake_from_source_geometry_data_async(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback) { + ERR_FAIL_COND(!p_navigation_mesh.is_valid()); + ERR_FAIL_COND(!p_source_geometry_data.is_valid()); + + if (!p_source_geometry_data->has_data()) { + p_navigation_mesh->clear(); + if (p_callback.is_valid()) { + generator_emit_callback(p_callback); + } + return; + } + + if (!use_threads) { + bake_from_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_callback); + return; + } + + baking_navmesh_mutex.lock(); + if (baking_navmeshes.has(p_navigation_mesh)) { + baking_navmesh_mutex.unlock(); + ERR_FAIL_MSG("NavigationMesh is already baking. Wait for current bake to finish."); + return; + } + baking_navmeshes.insert(p_navigation_mesh); + baking_navmesh_mutex.unlock(); + + generator_task_mutex.lock(); + NavMeshGeneratorTask3D *generator_task = memnew(NavMeshGeneratorTask3D); + generator_task->navigation_mesh = p_navigation_mesh; + generator_task->source_geometry_data = p_source_geometry_data; + generator_task->callback = p_callback; + generator_task->status = NavMeshGeneratorTask3D::TaskStatus::BAKING_STARTED; + generator_task->thread_task_id = WorkerThreadPool::get_singleton()->add_native_task(&NavMeshGenerator3D::generator_thread_bake, generator_task, NavMeshGenerator3D::baking_use_high_priority_threads, SNAME("NavMeshGeneratorBake3D")); + generator_tasks.insert(generator_task->thread_task_id, generator_task); + generator_task_mutex.unlock(); +} + +void NavMeshGenerator3D::generator_thread_bake(void *p_arg) { + NavMeshGeneratorTask3D *generator_task = static_cast<NavMeshGeneratorTask3D *>(p_arg); + + generator_bake_from_source_geometry_data(generator_task->navigation_mesh, generator_task->source_geometry_data); + + generator_task->status = NavMeshGeneratorTask3D::TaskStatus::BAKING_FINISHED; +} + +void NavMeshGenerator3D::generator_parse_geometry_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node, bool p_recurse_children) { + generator_parse_meshinstance3d_node(p_navigation_mesh, p_source_geometry_data, p_node); + generator_parse_multimeshinstance3d_node(p_navigation_mesh, p_source_geometry_data, p_node); + generator_parse_staticbody3d_node(p_navigation_mesh, p_source_geometry_data, p_node); +#ifdef MODULE_CSG_ENABLED + generator_parse_csgshape3d_node(p_navigation_mesh, p_source_geometry_data, p_node); +#endif +#ifdef MODULE_GRIDMAP_ENABLED + generator_parse_gridmap_node(p_navigation_mesh, p_source_geometry_data, p_node); +#endif + + if (p_recurse_children) { + for (int i = 0; i < p_node->get_child_count(); i++) { + generator_parse_geometry_node(p_navigation_mesh, p_source_geometry_data, p_node->get_child(i), p_recurse_children); + } + } +} + +void NavMeshGenerator3D::generator_parse_meshinstance3d_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node) { + MeshInstance3D *mesh_instance = Object::cast_to<MeshInstance3D>(p_node); + + if (mesh_instance) { + NavigationMesh::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type(); + + if (parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_MESH_INSTANCES || parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_BOTH) { + Ref<Mesh> mesh = mesh_instance->get_mesh(); + if (mesh.is_valid()) { + p_source_geometry_data->add_mesh(mesh, mesh_instance->get_global_transform()); + } + } + } +} + +void NavMeshGenerator3D::generator_parse_multimeshinstance3d_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node) { + MultiMeshInstance3D *multimesh_instance = Object::cast_to<MultiMeshInstance3D>(p_node); + + if (multimesh_instance) { + NavigationMesh::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type(); + + if (parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_MESH_INSTANCES || parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_BOTH) { + Ref<MultiMesh> multimesh = multimesh_instance->get_multimesh(); + if (multimesh.is_valid()) { + Ref<Mesh> mesh = multimesh->get_mesh(); + if (mesh.is_valid()) { + int n = multimesh->get_visible_instance_count(); + if (n == -1) { + n = multimesh->get_instance_count(); + } + for (int i = 0; i < n; i++) { + p_source_geometry_data->add_mesh(mesh, multimesh_instance->get_global_transform() * multimesh->get_instance_transform(i)); + } + } + } + } + } +} + +void NavMeshGenerator3D::generator_parse_staticbody3d_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node) { + StaticBody3D *static_body = Object::cast_to<StaticBody3D>(p_node); + + if (static_body) { + NavigationMesh::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type(); + uint32_t parsed_collision_mask = p_navigation_mesh->get_collision_mask(); + + if ((parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS || parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_BOTH) && (static_body->get_collision_layer() & parsed_collision_mask)) { + List<uint32_t> shape_owners; + static_body->get_shape_owners(&shape_owners); + for (uint32_t shape_owner : shape_owners) { + if (static_body->is_shape_owner_disabled(shape_owner)) { + continue; + } + const int shape_count = static_body->shape_owner_get_shape_count(shape_owner); + for (int shape_index = 0; shape_index < shape_count; shape_index++) { + Ref<Shape3D> s = static_body->shape_owner_get_shape(shape_owner, shape_index); + if (s.is_null()) { + continue; + } + + const Transform3D transform = static_body->get_global_transform() * static_body->shape_owner_get_transform(shape_owner); + + BoxShape3D *box = Object::cast_to<BoxShape3D>(*s); + if (box) { + Array arr; + arr.resize(RS::ARRAY_MAX); + BoxMesh::create_mesh_array(arr, box->get_size()); + p_source_geometry_data->add_mesh_array(arr, transform); + } + + CapsuleShape3D *capsule = Object::cast_to<CapsuleShape3D>(*s); + if (capsule) { + Array arr; + arr.resize(RS::ARRAY_MAX); + CapsuleMesh::create_mesh_array(arr, capsule->get_radius(), capsule->get_height()); + p_source_geometry_data->add_mesh_array(arr, transform); + } + + CylinderShape3D *cylinder = Object::cast_to<CylinderShape3D>(*s); + if (cylinder) { + Array arr; + arr.resize(RS::ARRAY_MAX); + CylinderMesh::create_mesh_array(arr, cylinder->get_radius(), cylinder->get_radius(), cylinder->get_height()); + p_source_geometry_data->add_mesh_array(arr, transform); + } + + SphereShape3D *sphere = Object::cast_to<SphereShape3D>(*s); + if (sphere) { + Array arr; + arr.resize(RS::ARRAY_MAX); + SphereMesh::create_mesh_array(arr, sphere->get_radius(), sphere->get_radius() * 2.0); + p_source_geometry_data->add_mesh_array(arr, transform); + } + + ConcavePolygonShape3D *concave_polygon = Object::cast_to<ConcavePolygonShape3D>(*s); + if (concave_polygon) { + p_source_geometry_data->add_faces(concave_polygon->get_faces(), transform); + } + + ConvexPolygonShape3D *convex_polygon = Object::cast_to<ConvexPolygonShape3D>(*s); + if (convex_polygon) { + Vector<Vector3> varr = Variant(convex_polygon->get_points()); + Geometry3D::MeshData md; + + Error err = ConvexHullComputer::convex_hull(varr, md); + + if (err == OK) { + PackedVector3Array faces; + + for (const Geometry3D::MeshData::Face &face : md.faces) { + for (uint32_t k = 2; k < face.indices.size(); ++k) { + faces.push_back(md.vertices[face.indices[0]]); + faces.push_back(md.vertices[face.indices[k - 1]]); + faces.push_back(md.vertices[face.indices[k]]); + } + } + + p_source_geometry_data->add_faces(faces, transform); + } + } + + HeightMapShape3D *heightmap_shape = Object::cast_to<HeightMapShape3D>(*s); + if (heightmap_shape) { + int heightmap_depth = heightmap_shape->get_map_depth(); + int heightmap_width = heightmap_shape->get_map_width(); + + if (heightmap_depth >= 2 && heightmap_width >= 2) { + const Vector<real_t> &map_data = heightmap_shape->get_map_data(); + + Vector2 heightmap_gridsize(heightmap_width - 1, heightmap_depth - 1); + Vector2 start = heightmap_gridsize * -0.5; + + Vector<Vector3> vertex_array; + vertex_array.resize((heightmap_depth - 1) * (heightmap_width - 1) * 6); + int map_data_current_index = 0; + + for (int d = 0; d < heightmap_depth; d++) { + for (int w = 0; w < heightmap_width; w++) { + if (map_data_current_index + 1 + heightmap_depth < map_data.size()) { + float top_left_height = map_data[map_data_current_index]; + float top_right_height = map_data[map_data_current_index + 1]; + float bottom_left_height = map_data[map_data_current_index + heightmap_depth]; + float bottom_right_height = map_data[map_data_current_index + 1 + heightmap_depth]; + + Vector3 top_left = Vector3(start.x + w, top_left_height, start.y + d); + Vector3 top_right = Vector3(start.x + w + 1.0, top_right_height, start.y + d); + Vector3 bottom_left = Vector3(start.x + w, bottom_left_height, start.y + d + 1.0); + Vector3 bottom_right = Vector3(start.x + w + 1.0, bottom_right_height, start.y + d + 1.0); + + vertex_array.push_back(top_right); + vertex_array.push_back(bottom_left); + vertex_array.push_back(top_left); + vertex_array.push_back(top_right); + vertex_array.push_back(bottom_right); + vertex_array.push_back(bottom_left); + } + map_data_current_index += 1; + } + } + if (vertex_array.size() > 0) { + p_source_geometry_data->add_faces(vertex_array, transform); + } + } + } + } + } + } + } +} + +#ifdef MODULE_CSG_ENABLED +void NavMeshGenerator3D::generator_parse_csgshape3d_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node) { + CSGShape3D *csgshape3d = Object::cast_to<CSGShape3D>(p_node); + + if (csgshape3d) { + NavigationMesh::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type(); + uint32_t parsed_collision_mask = p_navigation_mesh->get_collision_mask(); + + if (parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_MESH_INSTANCES || (parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS && csgshape3d->is_using_collision() && (csgshape3d->get_collision_layer() & parsed_collision_mask)) || parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_BOTH) { + CSGShape3D *csg_shape = Object::cast_to<CSGShape3D>(p_node); + Array meshes = csg_shape->get_meshes(); + if (!meshes.is_empty()) { + Ref<Mesh> mesh = meshes[1]; + if (mesh.is_valid()) { + p_source_geometry_data->add_mesh(mesh, csg_shape->get_global_transform()); + } + } + } + } +} +#endif // MODULE_CSG_ENABLED + +#ifdef MODULE_GRIDMAP_ENABLED +void NavMeshGenerator3D::generator_parse_gridmap_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node) { + GridMap *gridmap = Object::cast_to<GridMap>(p_node); + + if (gridmap) { + NavigationMesh::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type(); + uint32_t parsed_collision_mask = p_navigation_mesh->get_collision_mask(); + + if (parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_MESH_INSTANCES || parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_BOTH) { + Array meshes = gridmap->get_meshes(); + Transform3D xform = gridmap->get_global_transform(); + for (int i = 0; i < meshes.size(); i += 2) { + Ref<Mesh> mesh = meshes[i + 1]; + if (mesh.is_valid()) { + p_source_geometry_data->add_mesh(mesh, xform * (Transform3D)meshes[i]); + } + } + } + + else if ((parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS || parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_BOTH) && (gridmap->get_collision_layer() & parsed_collision_mask)) { + Array shapes = gridmap->get_collision_shapes(); + for (int i = 0; i < shapes.size(); i += 2) { + RID shape = shapes[i + 1]; + PhysicsServer3D::ShapeType type = PhysicsServer3D::get_singleton()->shape_get_type(shape); + Variant data = PhysicsServer3D::get_singleton()->shape_get_data(shape); + + switch (type) { + case PhysicsServer3D::SHAPE_SPHERE: { + real_t radius = data; + Array arr; + arr.resize(RS::ARRAY_MAX); + SphereMesh::create_mesh_array(arr, radius, radius * 2.0); + p_source_geometry_data->add_mesh_array(arr, shapes[i]); + } break; + case PhysicsServer3D::SHAPE_BOX: { + Vector3 extents = data; + Array arr; + arr.resize(RS::ARRAY_MAX); + BoxMesh::create_mesh_array(arr, extents * 2.0); + p_source_geometry_data->add_mesh_array(arr, shapes[i]); + } break; + case PhysicsServer3D::SHAPE_CAPSULE: { + Dictionary dict = data; + real_t radius = dict["radius"]; + real_t height = dict["height"]; + Array arr; + arr.resize(RS::ARRAY_MAX); + CapsuleMesh::create_mesh_array(arr, radius, height); + p_source_geometry_data->add_mesh_array(arr, shapes[i]); + } break; + case PhysicsServer3D::SHAPE_CYLINDER: { + Dictionary dict = data; + real_t radius = dict["radius"]; + real_t height = dict["height"]; + Array arr; + arr.resize(RS::ARRAY_MAX); + CylinderMesh::create_mesh_array(arr, radius, radius, height); + p_source_geometry_data->add_mesh_array(arr, shapes[i]); + } break; + case PhysicsServer3D::SHAPE_CONVEX_POLYGON: { + PackedVector3Array vertices = data; + Geometry3D::MeshData md; + + Error err = ConvexHullComputer::convex_hull(vertices, md); + + if (err == OK) { + PackedVector3Array faces; + + for (const Geometry3D::MeshData::Face &face : md.faces) { + for (uint32_t k = 2; k < face.indices.size(); ++k) { + faces.push_back(md.vertices[face.indices[0]]); + faces.push_back(md.vertices[face.indices[k - 1]]); + faces.push_back(md.vertices[face.indices[k]]); + } + } + + p_source_geometry_data->add_faces(faces, shapes[i]); + } + } break; + case PhysicsServer3D::SHAPE_CONCAVE_POLYGON: { + Dictionary dict = data; + PackedVector3Array faces = Variant(dict["faces"]); + p_source_geometry_data->add_faces(faces, shapes[i]); + } break; + case PhysicsServer3D::SHAPE_HEIGHTMAP: { + Dictionary dict = data; + ///< dict( int:"width", int:"depth",float:"cell_size", float_array:"heights" + int heightmap_depth = dict["depth"]; + int heightmap_width = dict["width"]; + + if (heightmap_depth >= 2 && heightmap_width >= 2) { + const Vector<real_t> &map_data = dict["heights"]; + + Vector2 heightmap_gridsize(heightmap_width - 1, heightmap_depth - 1); + Vector2 start = heightmap_gridsize * -0.5; + + Vector<Vector3> vertex_array; + vertex_array.resize((heightmap_depth - 1) * (heightmap_width - 1) * 6); + int map_data_current_index = 0; + + for (int d = 0; d < heightmap_depth; d++) { + for (int w = 0; w < heightmap_width; w++) { + if (map_data_current_index + 1 + heightmap_depth < map_data.size()) { + float top_left_height = map_data[map_data_current_index]; + float top_right_height = map_data[map_data_current_index + 1]; + float bottom_left_height = map_data[map_data_current_index + heightmap_depth]; + float bottom_right_height = map_data[map_data_current_index + 1 + heightmap_depth]; + + Vector3 top_left = Vector3(start.x + w, top_left_height, start.y + d); + Vector3 top_right = Vector3(start.x + w + 1.0, top_right_height, start.y + d); + Vector3 bottom_left = Vector3(start.x + w, bottom_left_height, start.y + d + 1.0); + Vector3 bottom_right = Vector3(start.x + w + 1.0, bottom_right_height, start.y + d + 1.0); + + vertex_array.push_back(top_right); + vertex_array.push_back(bottom_left); + vertex_array.push_back(top_left); + vertex_array.push_back(top_right); + vertex_array.push_back(bottom_right); + vertex_array.push_back(bottom_left); + } + map_data_current_index += 1; + } + } + if (vertex_array.size() > 0) { + p_source_geometry_data->add_faces(vertex_array, shapes[i]); + } + } + } break; + default: { + WARN_PRINT("Unsupported collision shape type."); + } break; + } + } + } + } +} +#endif // MODULE_GRIDMAP_ENABLED + +void NavMeshGenerator3D::generator_parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node) { + List<Node *> parse_nodes; + + if (p_navigation_mesh->get_source_geometry_mode() == NavigationMesh::SOURCE_GEOMETRY_ROOT_NODE_CHILDREN) { + parse_nodes.push_back(p_root_node); + } else { + p_root_node->get_tree()->get_nodes_in_group(p_navigation_mesh->get_source_group_name(), &parse_nodes); + } + + Transform3D root_node_transform = Transform3D(); + if (Object::cast_to<Node3D>(p_root_node)) { + root_node_transform = Object::cast_to<Node3D>(p_root_node)->get_global_transform().affine_inverse(); + } + + p_source_geometry_data->clear(); + p_source_geometry_data->root_node_transform = root_node_transform; + + bool recurse_children = p_navigation_mesh->get_source_geometry_mode() != NavigationMesh::SOURCE_GEOMETRY_GROUPS_EXPLICIT; + + for (Node *parse_node : parse_nodes) { + generator_parse_geometry_node(p_navigation_mesh, p_source_geometry_data, parse_node, recurse_children); + } +}; + +void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data) { + if (p_navigation_mesh.is_null() || p_source_geometry_data.is_null()) { + return; + } + + const Vector<float> &vertices = p_source_geometry_data->get_vertices(); + const Vector<int> &indices = p_source_geometry_data->get_indices(); + + if (vertices.size() < 3 || indices.size() < 3) { + return; + } + + rcHeightfield *hf = nullptr; + rcCompactHeightfield *chf = nullptr; + rcContourSet *cset = nullptr; + rcPolyMesh *poly_mesh = nullptr; + rcPolyMeshDetail *detail_mesh = nullptr; + rcContext ctx; + + // added to keep track of steps, no functionality right now + String bake_state = ""; + + bake_state = "Setting up Configuration..."; // step #1 + + const float *verts = vertices.ptr(); + const int nverts = vertices.size() / 3; + const int *tris = indices.ptr(); + const int ntris = indices.size() / 3; + + float bmin[3], bmax[3]; + rcCalcBounds(verts, nverts, bmin, bmax); + + rcConfig cfg; + memset(&cfg, 0, sizeof(cfg)); + + cfg.cs = p_navigation_mesh->get_cell_size(); + cfg.ch = p_navigation_mesh->get_cell_height(); + cfg.walkableSlopeAngle = p_navigation_mesh->get_agent_max_slope(); + cfg.walkableHeight = (int)Math::ceil(p_navigation_mesh->get_agent_height() / cfg.ch); + cfg.walkableClimb = (int)Math::floor(p_navigation_mesh->get_agent_max_climb() / cfg.ch); + cfg.walkableRadius = (int)Math::ceil(p_navigation_mesh->get_agent_radius() / cfg.cs); + cfg.maxEdgeLen = (int)(p_navigation_mesh->get_edge_max_length() / p_navigation_mesh->get_cell_size()); + cfg.maxSimplificationError = p_navigation_mesh->get_edge_max_error(); + cfg.minRegionArea = (int)(p_navigation_mesh->get_region_min_size() * p_navigation_mesh->get_region_min_size()); + cfg.mergeRegionArea = (int)(p_navigation_mesh->get_region_merge_size() * p_navigation_mesh->get_region_merge_size()); + cfg.maxVertsPerPoly = (int)p_navigation_mesh->get_vertices_per_polygon(); + cfg.detailSampleDist = MAX(p_navigation_mesh->get_cell_size() * p_navigation_mesh->get_detail_sample_distance(), 0.1f); + cfg.detailSampleMaxError = p_navigation_mesh->get_cell_height() * p_navigation_mesh->get_detail_sample_max_error(); + + if (!Math::is_equal_approx((float)cfg.walkableHeight * cfg.ch, p_navigation_mesh->get_agent_height())) { + WARN_PRINT("Property agent_height is ceiled to cell_height voxel units and loses precision."); + } + if (!Math::is_equal_approx((float)cfg.walkableClimb * cfg.ch, p_navigation_mesh->get_agent_max_climb())) { + WARN_PRINT("Property agent_max_climb is floored to cell_height voxel units and loses precision."); + } + if (!Math::is_equal_approx((float)cfg.walkableRadius * cfg.cs, p_navigation_mesh->get_agent_radius())) { + WARN_PRINT("Property agent_radius is ceiled to cell_size voxel units and loses precision."); + } + if (!Math::is_equal_approx((float)cfg.maxEdgeLen * cfg.cs, p_navigation_mesh->get_edge_max_length())) { + WARN_PRINT("Property edge_max_length is rounded to cell_size voxel units and loses precision."); + } + if (!Math::is_equal_approx((float)cfg.minRegionArea, p_navigation_mesh->get_region_min_size() * p_navigation_mesh->get_region_min_size())) { + WARN_PRINT("Property region_min_size is converted to int and loses precision."); + } + if (!Math::is_equal_approx((float)cfg.mergeRegionArea, p_navigation_mesh->get_region_merge_size() * p_navigation_mesh->get_region_merge_size())) { + WARN_PRINT("Property region_merge_size is converted to int and loses precision."); + } + if (!Math::is_equal_approx((float)cfg.maxVertsPerPoly, p_navigation_mesh->get_vertices_per_polygon())) { + WARN_PRINT("Property vertices_per_polygon is converted to int and loses precision."); + } + if (p_navigation_mesh->get_cell_size() * p_navigation_mesh->get_detail_sample_distance() < 0.1f) { + WARN_PRINT("Property detail_sample_distance is clamped to 0.1 world units as the resulting value from multiplying with cell_size is too low."); + } + + cfg.bmin[0] = bmin[0]; + cfg.bmin[1] = bmin[1]; + cfg.bmin[2] = bmin[2]; + cfg.bmax[0] = bmax[0]; + cfg.bmax[1] = bmax[1]; + cfg.bmax[2] = bmax[2]; + + AABB baking_aabb = p_navigation_mesh->get_filter_baking_aabb(); + if (baking_aabb.has_volume()) { + Vector3 baking_aabb_offset = p_navigation_mesh->get_filter_baking_aabb_offset(); + cfg.bmin[0] = baking_aabb.position[0] + baking_aabb_offset.x; + cfg.bmin[1] = baking_aabb.position[1] + baking_aabb_offset.y; + cfg.bmin[2] = baking_aabb.position[2] + baking_aabb_offset.z; + cfg.bmax[0] = cfg.bmin[0] + baking_aabb.size[0]; + cfg.bmax[1] = cfg.bmin[1] + baking_aabb.size[1]; + cfg.bmax[2] = cfg.bmin[2] + baking_aabb.size[2]; + } + + bake_state = "Calculating grid size..."; // step #2 + rcCalcGridSize(cfg.bmin, cfg.bmax, cfg.cs, &cfg.width, &cfg.height); + + // ~30000000 seems to be around sweetspot where Editor baking breaks + if ((cfg.width * cfg.height) > 30000000) { + WARN_PRINT("NavigationMesh baking process will likely fail." + "\nSource geometry is suspiciously big for the current Cell Size and Cell Height in the NavMesh Resource bake settings." + "\nIf baking does not fail, the resulting NavigationMesh will create serious pathfinding performance issues." + "\nIt is advised to increase Cell Size and/or Cell Height in the NavMesh Resource bake settings or reduce the size / scale of the source geometry."); + } + + bake_state = "Creating heightfield..."; // step #3 + hf = rcAllocHeightfield(); + + ERR_FAIL_COND(!hf); + ERR_FAIL_COND(!rcCreateHeightfield(&ctx, *hf, cfg.width, cfg.height, cfg.bmin, cfg.bmax, cfg.cs, cfg.ch)); + + bake_state = "Marking walkable triangles..."; // step #4 + { + Vector<unsigned char> tri_areas; + tri_areas.resize(ntris); + + ERR_FAIL_COND(tri_areas.size() == 0); + + memset(tri_areas.ptrw(), 0, ntris * sizeof(unsigned char)); + rcMarkWalkableTriangles(&ctx, cfg.walkableSlopeAngle, verts, nverts, tris, ntris, tri_areas.ptrw()); + + ERR_FAIL_COND(!rcRasterizeTriangles(&ctx, verts, nverts, tris, tri_areas.ptr(), ntris, *hf, cfg.walkableClimb)); + } + + if (p_navigation_mesh->get_filter_low_hanging_obstacles()) { + rcFilterLowHangingWalkableObstacles(&ctx, cfg.walkableClimb, *hf); + } + if (p_navigation_mesh->get_filter_ledge_spans()) { + rcFilterLedgeSpans(&ctx, cfg.walkableHeight, cfg.walkableClimb, *hf); + } + if (p_navigation_mesh->get_filter_walkable_low_height_spans()) { + rcFilterWalkableLowHeightSpans(&ctx, cfg.walkableHeight, *hf); + } + + bake_state = "Constructing compact heightfield..."; // step #5 + + chf = rcAllocCompactHeightfield(); + + ERR_FAIL_COND(!chf); + ERR_FAIL_COND(!rcBuildCompactHeightfield(&ctx, cfg.walkableHeight, cfg.walkableClimb, *hf, *chf)); + + rcFreeHeightField(hf); + hf = nullptr; + + bake_state = "Eroding walkable area..."; // step #6 + + ERR_FAIL_COND(!rcErodeWalkableArea(&ctx, cfg.walkableRadius, *chf)); + + bake_state = "Partitioning..."; // step #7 + + if (p_navigation_mesh->get_sample_partition_type() == NavigationMesh::SAMPLE_PARTITION_WATERSHED) { + ERR_FAIL_COND(!rcBuildDistanceField(&ctx, *chf)); + ERR_FAIL_COND(!rcBuildRegions(&ctx, *chf, 0, cfg.minRegionArea, cfg.mergeRegionArea)); + } else if (p_navigation_mesh->get_sample_partition_type() == NavigationMesh::SAMPLE_PARTITION_MONOTONE) { + ERR_FAIL_COND(!rcBuildRegionsMonotone(&ctx, *chf, 0, cfg.minRegionArea, cfg.mergeRegionArea)); + } else { + ERR_FAIL_COND(!rcBuildLayerRegions(&ctx, *chf, 0, cfg.minRegionArea)); + } + + bake_state = "Creating contours..."; // step #8 + + cset = rcAllocContourSet(); + + ERR_FAIL_COND(!cset); + ERR_FAIL_COND(!rcBuildContours(&ctx, *chf, cfg.maxSimplificationError, cfg.maxEdgeLen, *cset)); + + bake_state = "Creating polymesh..."; // step #9 + + poly_mesh = rcAllocPolyMesh(); + ERR_FAIL_COND(!poly_mesh); + ERR_FAIL_COND(!rcBuildPolyMesh(&ctx, *cset, cfg.maxVertsPerPoly, *poly_mesh)); + + detail_mesh = rcAllocPolyMeshDetail(); + ERR_FAIL_COND(!detail_mesh); + ERR_FAIL_COND(!rcBuildPolyMeshDetail(&ctx, *poly_mesh, *chf, cfg.detailSampleDist, cfg.detailSampleMaxError, *detail_mesh)); + + rcFreeCompactHeightfield(chf); + chf = nullptr; + rcFreeContourSet(cset); + cset = nullptr; + + bake_state = "Converting to native navigation mesh..."; // step #10 + + Vector<Vector3> nav_vertices; + + for (int i = 0; i < detail_mesh->nverts; i++) { + const float *v = &detail_mesh->verts[i * 3]; + nav_vertices.push_back(Vector3(v[0], v[1], v[2])); + } + p_navigation_mesh->set_vertices(nav_vertices); + p_navigation_mesh->clear_polygons(); + + for (int i = 0; i < detail_mesh->nmeshes; i++) { + const unsigned int *detail_mesh_m = &detail_mesh->meshes[i * 4]; + const unsigned int detail_mesh_bverts = detail_mesh_m[0]; + const unsigned int detail_mesh_m_btris = detail_mesh_m[2]; + const unsigned int detail_mesh_ntris = detail_mesh_m[3]; + const unsigned char *detail_mesh_tris = &detail_mesh->tris[detail_mesh_m_btris * 4]; + for (unsigned int j = 0; j < detail_mesh_ntris; j++) { + Vector<int> nav_indices; + nav_indices.resize(3); + // Polygon order in recast is opposite than godot's + nav_indices.write[0] = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 0])); + nav_indices.write[1] = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 2])); + nav_indices.write[2] = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 1])); + p_navigation_mesh->add_polygon(nav_indices); + } + } + + bake_state = "Cleanup..."; // step #11 + + rcFreePolyMesh(poly_mesh); + poly_mesh = nullptr; + rcFreePolyMeshDetail(detail_mesh); + detail_mesh = nullptr; + + bake_state = "Baking finished."; // step #12 +} + +bool NavMeshGenerator3D::generator_emit_callback(const Callable &p_callback) { + ERR_FAIL_COND_V(!p_callback.is_valid(), false); + + Callable::CallError ce; + Variant result; + p_callback.callp(nullptr, 0, result, ce); + + return ce.error == Callable::CallError::CALL_OK; +} + +#endif // _3D_DISABLED diff --git a/modules/navigation/nav_mesh_generator_3d.h b/modules/navigation/nav_mesh_generator_3d.h new file mode 100644 index 0000000000..4220927641 --- /dev/null +++ b/modules/navigation/nav_mesh_generator_3d.h @@ -0,0 +1,109 @@ +/**************************************************************************/ +/* nav_mesh_generator_3d.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef NAV_MESH_GENERATOR_3D_H +#define NAV_MESH_GENERATOR_3D_H + +#ifndef _3D_DISABLED + +#include "core/object/class_db.h" +#include "core/object/worker_thread_pool.h" +#include "modules/modules_enabled.gen.h" // For csg, gridmap. + +class Node; +class NavigationMesh; +class NavigationMeshSourceGeometryData3D; + +class NavMeshGenerator3D : public Object { + static NavMeshGenerator3D *singleton; + + static Mutex baking_navmesh_mutex; + static Mutex generator_task_mutex; + + static bool use_threads; + static bool baking_use_multiple_threads; + static bool baking_use_high_priority_threads; + + struct NavMeshGeneratorTask3D { + enum TaskStatus { + BAKING_STARTED, + BAKING_FINISHED, + BAKING_FAILED, + CALLBACK_DISPATCHED, + CALLBACK_FAILED, + }; + + Ref<NavigationMesh> navigation_mesh; + Ref<NavigationMeshSourceGeometryData3D> source_geometry_data; + Callable callback; + WorkerThreadPool::TaskID thread_task_id = WorkerThreadPool::INVALID_TASK_ID; + NavMeshGeneratorTask3D::TaskStatus status = NavMeshGeneratorTask3D::TaskStatus::BAKING_STARTED; + }; + + static HashMap<WorkerThreadPool::TaskID, NavMeshGeneratorTask3D *> generator_tasks; + + static void generator_thread_bake(void *p_arg); + + static HashSet<Ref<NavigationMesh>> baking_navmeshes; + + static void generator_parse_geometry_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node, bool p_recurse_children); + static void generator_parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node); + static void generator_bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data); + + static void generator_parse_meshinstance3d_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node); + static void generator_parse_multimeshinstance3d_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node); + static void generator_parse_staticbody3d_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node); +#ifdef MODULE_CSG_ENABLED + static void generator_parse_csgshape3d_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node); +#endif // MODULE_CSG_ENABLED +#ifdef MODULE_GRIDMAP_ENABLED + static void generator_parse_gridmap_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node); +#endif // MODULE_GRIDMAP_ENABLED + + static bool generator_emit_callback(const Callable &p_callback); + +public: + static NavMeshGenerator3D *get_singleton(); + + static void sync(); + static void cleanup(); + static void finish(); + + static void parse_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()); + static void bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback = Callable()); + static void bake_from_source_geometry_data_async(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback = Callable()); + + NavMeshGenerator3D(); + ~NavMeshGenerator3D(); +}; + +#endif // _3D_DISABLED + +#endif // NAV_MESH_GENERATOR_3D_H diff --git a/modules/navigation/navigation_mesh_generator.cpp b/modules/navigation/navigation_mesh_generator.cpp index 89afb4a8ea..8393896db1 100644 --- a/modules/navigation/navigation_mesh_generator.cpp +++ b/modules/navigation/navigation_mesh_generator.cpp @@ -32,451 +32,11 @@ #include "navigation_mesh_generator.h" -#include "core/math/convex_hull.h" -#include "core/os/thread.h" -#include "scene/3d/mesh_instance_3d.h" -#include "scene/3d/multimesh_instance_3d.h" -#include "scene/3d/physics_body_3d.h" -#include "scene/resources/box_shape_3d.h" -#include "scene/resources/capsule_shape_3d.h" -#include "scene/resources/concave_polygon_shape_3d.h" -#include "scene/resources/convex_polygon_shape_3d.h" -#include "scene/resources/cylinder_shape_3d.h" -#include "scene/resources/height_map_shape_3d.h" #include "scene/resources/navigation_mesh_source_geometry_data_3d.h" -#include "scene/resources/primitive_meshes.h" -#include "scene/resources/shape_3d.h" -#include "scene/resources/sphere_shape_3d.h" -#include "scene/resources/world_boundary_shape_3d.h" - -#ifdef TOOLS_ENABLED -#include "editor/editor_node.h" -#endif - -#include "modules/modules_enabled.gen.h" // For csg, gridmap. - -#ifdef MODULE_CSG_ENABLED -#include "modules/csg/csg_shape.h" -#endif -#ifdef MODULE_GRIDMAP_ENABLED -#include "modules/gridmap/grid_map.h" -#endif +#include "servers/navigation_server_3d.h" NavigationMeshGenerator *NavigationMeshGenerator::singleton = nullptr; -void NavigationMeshGenerator::_add_vertex(const Vector3 &p_vec3, Vector<float> &p_vertices) { - p_vertices.push_back(p_vec3.x); - p_vertices.push_back(p_vec3.y); - p_vertices.push_back(p_vec3.z); -} - -void NavigationMeshGenerator::_add_mesh(const Ref<Mesh> &p_mesh, const Transform3D &p_xform, Vector<float> &p_vertices, Vector<int> &p_indices) { - int current_vertex_count; - - for (int i = 0; i < p_mesh->get_surface_count(); i++) { - current_vertex_count = p_vertices.size() / 3; - - if (p_mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) { - continue; - } - - int index_count = 0; - if (p_mesh->surface_get_format(i) & Mesh::ARRAY_FORMAT_INDEX) { - index_count = p_mesh->surface_get_array_index_len(i); - } else { - index_count = p_mesh->surface_get_array_len(i); - } - - ERR_CONTINUE((index_count == 0 || (index_count % 3) != 0)); - - int face_count = index_count / 3; - - Array a = p_mesh->surface_get_arrays(i); - ERR_CONTINUE(a.is_empty() || (a.size() != Mesh::ARRAY_MAX)); - - Vector<Vector3> mesh_vertices = a[Mesh::ARRAY_VERTEX]; - ERR_CONTINUE(mesh_vertices.is_empty()); - const Vector3 *vr = mesh_vertices.ptr(); - - if (p_mesh->surface_get_format(i) & Mesh::ARRAY_FORMAT_INDEX) { - Vector<int> mesh_indices = a[Mesh::ARRAY_INDEX]; - ERR_CONTINUE(mesh_indices.is_empty() || (mesh_indices.size() != index_count)); - const int *ir = mesh_indices.ptr(); - - for (int j = 0; j < mesh_vertices.size(); j++) { - _add_vertex(p_xform.xform(vr[j]), p_vertices); - } - - for (int j = 0; j < face_count; j++) { - // CCW - p_indices.push_back(current_vertex_count + (ir[j * 3 + 0])); - p_indices.push_back(current_vertex_count + (ir[j * 3 + 2])); - p_indices.push_back(current_vertex_count + (ir[j * 3 + 1])); - } - } else { - ERR_CONTINUE(mesh_vertices.size() != index_count); - face_count = mesh_vertices.size() / 3; - for (int j = 0; j < face_count; j++) { - _add_vertex(p_xform.xform(vr[j * 3 + 0]), p_vertices); - _add_vertex(p_xform.xform(vr[j * 3 + 2]), p_vertices); - _add_vertex(p_xform.xform(vr[j * 3 + 1]), p_vertices); - - p_indices.push_back(current_vertex_count + (j * 3 + 0)); - p_indices.push_back(current_vertex_count + (j * 3 + 1)); - p_indices.push_back(current_vertex_count + (j * 3 + 2)); - } - } - } -} - -void NavigationMeshGenerator::_add_mesh_array(const Array &p_array, const Transform3D &p_xform, Vector<float> &p_vertices, Vector<int> &p_indices) { - ERR_FAIL_COND(p_array.size() != Mesh::ARRAY_MAX); - - Vector<Vector3> mesh_vertices = p_array[Mesh::ARRAY_VERTEX]; - ERR_FAIL_COND(mesh_vertices.is_empty()); - const Vector3 *vr = mesh_vertices.ptr(); - - Vector<int> mesh_indices = p_array[Mesh::ARRAY_INDEX]; - ERR_FAIL_COND(mesh_indices.is_empty()); - const int *ir = mesh_indices.ptr(); - - const int face_count = mesh_indices.size() / 3; - const int current_vertex_count = p_vertices.size() / 3; - - for (int j = 0; j < mesh_vertices.size(); j++) { - _add_vertex(p_xform.xform(vr[j]), p_vertices); - } - - for (int j = 0; j < face_count; j++) { - // CCW - p_indices.push_back(current_vertex_count + (ir[j * 3 + 0])); - p_indices.push_back(current_vertex_count + (ir[j * 3 + 2])); - p_indices.push_back(current_vertex_count + (ir[j * 3 + 1])); - } -} - -void NavigationMeshGenerator::_add_faces(const PackedVector3Array &p_faces, const Transform3D &p_xform, Vector<float> &p_vertices, Vector<int> &p_indices) { - ERR_FAIL_COND(p_faces.is_empty()); - ERR_FAIL_COND(p_faces.size() % 3 != 0); - int face_count = p_faces.size() / 3; - int current_vertex_count = p_vertices.size() / 3; - - for (int j = 0; j < face_count; j++) { - _add_vertex(p_xform.xform(p_faces[j * 3 + 0]), p_vertices); - _add_vertex(p_xform.xform(p_faces[j * 3 + 1]), p_vertices); - _add_vertex(p_xform.xform(p_faces[j * 3 + 2]), p_vertices); - - p_indices.push_back(current_vertex_count + (j * 3 + 0)); - p_indices.push_back(current_vertex_count + (j * 3 + 2)); - p_indices.push_back(current_vertex_count + (j * 3 + 1)); - } -} - -void NavigationMeshGenerator::_parse_geometry(const Transform3D &p_navmesh_transform, Node *p_node, Vector<float> &p_vertices, Vector<int> &p_indices, NavigationMesh::ParsedGeometryType p_generate_from, uint32_t p_collision_mask, bool p_recurse_children) { - if (Object::cast_to<MeshInstance3D>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) { - MeshInstance3D *mesh_instance = Object::cast_to<MeshInstance3D>(p_node); - Ref<Mesh> mesh = mesh_instance->get_mesh(); - if (mesh.is_valid()) { - _add_mesh(mesh, p_navmesh_transform * mesh_instance->get_global_transform(), p_vertices, p_indices); - } - } - - if (Object::cast_to<MultiMeshInstance3D>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) { - MultiMeshInstance3D *multimesh_instance = Object::cast_to<MultiMeshInstance3D>(p_node); - Ref<MultiMesh> multimesh = multimesh_instance->get_multimesh(); - if (multimesh.is_valid()) { - Ref<Mesh> mesh = multimesh->get_mesh(); - if (mesh.is_valid()) { - int n = multimesh->get_visible_instance_count(); - if (n == -1) { - n = multimesh->get_instance_count(); - } - for (int i = 0; i < n; i++) { - _add_mesh(mesh, p_navmesh_transform * multimesh_instance->get_global_transform() * multimesh->get_instance_transform(i), p_vertices, p_indices); - } - } - } - } - -#ifdef MODULE_CSG_ENABLED - if (Object::cast_to<CSGShape3D>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) { - CSGShape3D *csg_shape = Object::cast_to<CSGShape3D>(p_node); - Array meshes = csg_shape->get_meshes(); - if (!meshes.is_empty()) { - Ref<Mesh> mesh = meshes[1]; - if (mesh.is_valid()) { - _add_mesh(mesh, p_navmesh_transform * csg_shape->get_global_transform(), p_vertices, p_indices); - } - } - } -#endif - - if (Object::cast_to<StaticBody3D>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_MESH_INSTANCES) { - StaticBody3D *static_body = Object::cast_to<StaticBody3D>(p_node); - - if (static_body->get_collision_layer() & p_collision_mask) { - List<uint32_t> shape_owners; - static_body->get_shape_owners(&shape_owners); - for (uint32_t shape_owner : shape_owners) { - if (static_body->is_shape_owner_disabled(shape_owner)) { - continue; - } - const int shape_count = static_body->shape_owner_get_shape_count(shape_owner); - for (int i = 0; i < shape_count; i++) { - Ref<Shape3D> s = static_body->shape_owner_get_shape(shape_owner, i); - if (s.is_null()) { - continue; - } - - const Transform3D transform = p_navmesh_transform * static_body->get_global_transform() * static_body->shape_owner_get_transform(shape_owner); - - BoxShape3D *box = Object::cast_to<BoxShape3D>(*s); - if (box) { - Array arr; - arr.resize(RS::ARRAY_MAX); - BoxMesh::create_mesh_array(arr, box->get_size()); - _add_mesh_array(arr, transform, p_vertices, p_indices); - } - - CapsuleShape3D *capsule = Object::cast_to<CapsuleShape3D>(*s); - if (capsule) { - Array arr; - arr.resize(RS::ARRAY_MAX); - CapsuleMesh::create_mesh_array(arr, capsule->get_radius(), capsule->get_height()); - _add_mesh_array(arr, transform, p_vertices, p_indices); - } - - CylinderShape3D *cylinder = Object::cast_to<CylinderShape3D>(*s); - if (cylinder) { - Array arr; - arr.resize(RS::ARRAY_MAX); - CylinderMesh::create_mesh_array(arr, cylinder->get_radius(), cylinder->get_radius(), cylinder->get_height()); - _add_mesh_array(arr, transform, p_vertices, p_indices); - } - - SphereShape3D *sphere = Object::cast_to<SphereShape3D>(*s); - if (sphere) { - Array arr; - arr.resize(RS::ARRAY_MAX); - SphereMesh::create_mesh_array(arr, sphere->get_radius(), sphere->get_radius() * 2.0); - _add_mesh_array(arr, transform, p_vertices, p_indices); - } - - ConcavePolygonShape3D *concave_polygon = Object::cast_to<ConcavePolygonShape3D>(*s); - if (concave_polygon) { - _add_faces(concave_polygon->get_faces(), transform, p_vertices, p_indices); - } - - ConvexPolygonShape3D *convex_polygon = Object::cast_to<ConvexPolygonShape3D>(*s); - if (convex_polygon) { - Vector<Vector3> varr = Variant(convex_polygon->get_points()); - Geometry3D::MeshData md; - - Error err = ConvexHullComputer::convex_hull(varr, md); - - if (err == OK) { - PackedVector3Array faces; - - for (const Geometry3D::MeshData::Face &face : md.faces) { - for (uint32_t k = 2; k < face.indices.size(); ++k) { - faces.push_back(md.vertices[face.indices[0]]); - faces.push_back(md.vertices[face.indices[k - 1]]); - faces.push_back(md.vertices[face.indices[k]]); - } - } - - _add_faces(faces, transform, p_vertices, p_indices); - } - } - - HeightMapShape3D *heightmap_shape = Object::cast_to<HeightMapShape3D>(*s); - if (heightmap_shape) { - int heightmap_depth = heightmap_shape->get_map_depth(); - int heightmap_width = heightmap_shape->get_map_width(); - - if (heightmap_depth >= 2 && heightmap_width >= 2) { - const Vector<real_t> &map_data = heightmap_shape->get_map_data(); - - Vector2 heightmap_gridsize(heightmap_width - 1, heightmap_depth - 1); - Vector2 start = heightmap_gridsize * -0.5; - - Vector<Vector3> vertex_array; - vertex_array.resize((heightmap_depth - 1) * (heightmap_width - 1) * 6); - int map_data_current_index = 0; - - for (int d = 0; d < heightmap_depth; d++) { - for (int w = 0; w < heightmap_width; w++) { - if (map_data_current_index + 1 + heightmap_depth < map_data.size()) { - float top_left_height = map_data[map_data_current_index]; - float top_right_height = map_data[map_data_current_index + 1]; - float bottom_left_height = map_data[map_data_current_index + heightmap_depth]; - float bottom_right_height = map_data[map_data_current_index + 1 + heightmap_depth]; - - Vector3 top_left = Vector3(start.x + w, top_left_height, start.y + d); - Vector3 top_right = Vector3(start.x + w + 1.0, top_right_height, start.y + d); - Vector3 bottom_left = Vector3(start.x + w, bottom_left_height, start.y + d + 1.0); - Vector3 bottom_right = Vector3(start.x + w + 1.0, bottom_right_height, start.y + d + 1.0); - - vertex_array.push_back(top_right); - vertex_array.push_back(bottom_left); - vertex_array.push_back(top_left); - vertex_array.push_back(top_right); - vertex_array.push_back(bottom_right); - vertex_array.push_back(bottom_left); - } - map_data_current_index += 1; - } - } - if (vertex_array.size() > 0) { - _add_faces(vertex_array, transform, p_vertices, p_indices); - } - } - } - } - } - } - } - -#ifdef MODULE_GRIDMAP_ENABLED - GridMap *gridmap = Object::cast_to<GridMap>(p_node); - - if (gridmap) { - if (p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) { - Array meshes = gridmap->get_meshes(); - Transform3D xform = gridmap->get_global_transform(); - for (int i = 0; i < meshes.size(); i += 2) { - Ref<Mesh> mesh = meshes[i + 1]; - if (mesh.is_valid()) { - _add_mesh(mesh, p_navmesh_transform * xform * (Transform3D)meshes[i], p_vertices, p_indices); - } - } - } - - if (p_generate_from != NavigationMesh::PARSED_GEOMETRY_MESH_INSTANCES && (gridmap->get_collision_layer() & p_collision_mask)) { - Array shapes = gridmap->get_collision_shapes(); - for (int i = 0; i < shapes.size(); i += 2) { - RID shape = shapes[i + 1]; - PhysicsServer3D::ShapeType type = PhysicsServer3D::get_singleton()->shape_get_type(shape); - Variant data = PhysicsServer3D::get_singleton()->shape_get_data(shape); - - switch (type) { - case PhysicsServer3D::SHAPE_SPHERE: { - real_t radius = data; - Array arr; - arr.resize(RS::ARRAY_MAX); - SphereMesh::create_mesh_array(arr, radius, radius * 2.0); - _add_mesh_array(arr, shapes[i], p_vertices, p_indices); - } break; - case PhysicsServer3D::SHAPE_BOX: { - Vector3 extents = data; - Array arr; - arr.resize(RS::ARRAY_MAX); - BoxMesh::create_mesh_array(arr, extents * 2.0); - _add_mesh_array(arr, shapes[i], p_vertices, p_indices); - } break; - case PhysicsServer3D::SHAPE_CAPSULE: { - Dictionary dict = data; - real_t radius = dict["radius"]; - real_t height = dict["height"]; - Array arr; - arr.resize(RS::ARRAY_MAX); - CapsuleMesh::create_mesh_array(arr, radius, height); - _add_mesh_array(arr, shapes[i], p_vertices, p_indices); - } break; - case PhysicsServer3D::SHAPE_CYLINDER: { - Dictionary dict = data; - real_t radius = dict["radius"]; - real_t height = dict["height"]; - Array arr; - arr.resize(RS::ARRAY_MAX); - CylinderMesh::create_mesh_array(arr, radius, radius, height); - _add_mesh_array(arr, shapes[i], p_vertices, p_indices); - } break; - case PhysicsServer3D::SHAPE_CONVEX_POLYGON: { - PackedVector3Array vertices = data; - Geometry3D::MeshData md; - - Error err = ConvexHullComputer::convex_hull(vertices, md); - - if (err == OK) { - PackedVector3Array faces; - - for (const Geometry3D::MeshData::Face &face : md.faces) { - for (uint32_t k = 2; k < face.indices.size(); ++k) { - faces.push_back(md.vertices[face.indices[0]]); - faces.push_back(md.vertices[face.indices[k - 1]]); - faces.push_back(md.vertices[face.indices[k]]); - } - } - - _add_faces(faces, shapes[i], p_vertices, p_indices); - } - } break; - case PhysicsServer3D::SHAPE_CONCAVE_POLYGON: { - Dictionary dict = data; - PackedVector3Array faces = Variant(dict["faces"]); - _add_faces(faces, shapes[i], p_vertices, p_indices); - } break; - case PhysicsServer3D::SHAPE_HEIGHTMAP: { - Dictionary dict = data; - ///< dict( int:"width", int:"depth",float:"cell_size", float_array:"heights" - int heightmap_depth = dict["depth"]; - int heightmap_width = dict["width"]; - - if (heightmap_depth >= 2 && heightmap_width >= 2) { - const Vector<real_t> &map_data = dict["heights"]; - - Vector2 heightmap_gridsize(heightmap_width - 1, heightmap_depth - 1); - Vector2 start = heightmap_gridsize * -0.5; - - Vector<Vector3> vertex_array; - vertex_array.resize((heightmap_depth - 1) * (heightmap_width - 1) * 6); - int map_data_current_index = 0; - - for (int d = 0; d < heightmap_depth; d++) { - for (int w = 0; w < heightmap_width; w++) { - if (map_data_current_index + 1 + heightmap_depth < map_data.size()) { - float top_left_height = map_data[map_data_current_index]; - float top_right_height = map_data[map_data_current_index + 1]; - float bottom_left_height = map_data[map_data_current_index + heightmap_depth]; - float bottom_right_height = map_data[map_data_current_index + 1 + heightmap_depth]; - - Vector3 top_left = Vector3(start.x + w, top_left_height, start.y + d); - Vector3 top_right = Vector3(start.x + w + 1.0, top_right_height, start.y + d); - Vector3 bottom_left = Vector3(start.x + w, bottom_left_height, start.y + d + 1.0); - Vector3 bottom_right = Vector3(start.x + w + 1.0, bottom_right_height, start.y + d + 1.0); - - vertex_array.push_back(top_right); - vertex_array.push_back(bottom_left); - vertex_array.push_back(top_left); - vertex_array.push_back(top_right); - vertex_array.push_back(bottom_right); - vertex_array.push_back(bottom_left); - } - map_data_current_index += 1; - } - } - if (vertex_array.size() > 0) { - _add_faces(vertex_array, shapes[i], p_vertices, p_indices); - } - } - } break; - default: { - WARN_PRINT("Unsupported collision shape type."); - } break; - } - } - } - } -#endif - - if (p_recurse_children) { - for (int i = 0; i < p_node->get_child_count(); i++) { - _parse_geometry(p_navmesh_transform, p_node->get_child(i), p_vertices, p_indices, p_generate_from, p_collision_mask, p_recurse_children); - } - } -} - NavigationMeshGenerator *NavigationMeshGenerator::get_singleton() { return singleton; } @@ -500,285 +60,11 @@ void NavigationMeshGenerator::clear(Ref<NavigationMesh> p_navigation_mesh) { } void NavigationMeshGenerator::parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback) { - ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "The SceneTree can only be parsed on the main thread. Call this function from the main thread or use call_deferred()."); - ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh."); - ERR_FAIL_COND_MSG(p_root_node == nullptr, "No parsing root node specified."); - ERR_FAIL_COND_MSG(!p_root_node->is_inside_tree(), "The root node needs to be inside the SceneTree."); - - Vector<float> vertices; - Vector<int> indices; - - List<Node *> parse_nodes; - - if (p_navigation_mesh->get_source_geometry_mode() == NavigationMesh::SOURCE_GEOMETRY_ROOT_NODE_CHILDREN) { - parse_nodes.push_back(p_root_node); - } else { - p_root_node->get_tree()->get_nodes_in_group(p_navigation_mesh->get_source_group_name(), &parse_nodes); - } - - Transform3D navmesh_xform = Transform3D(); - if (Object::cast_to<Node3D>(p_root_node)) { - navmesh_xform = Object::cast_to<Node3D>(p_root_node)->get_global_transform().affine_inverse(); - } - for (Node *E : parse_nodes) { - NavigationMesh::ParsedGeometryType geometry_type = p_navigation_mesh->get_parsed_geometry_type(); - uint32_t collision_mask = p_navigation_mesh->get_collision_mask(); - bool recurse_children = p_navigation_mesh->get_source_geometry_mode() != NavigationMesh::SOURCE_GEOMETRY_GROUPS_EXPLICIT; - _parse_geometry(navmesh_xform, E, vertices, indices, geometry_type, collision_mask, recurse_children); - } - - p_source_geometry_data->set_vertices(vertices); - p_source_geometry_data->set_indices(indices); - - if (p_callback.is_valid()) { - Callable::CallError ce; - Variant result; - p_callback.callp(nullptr, 0, result, ce); - if (ce.error == Callable::CallError::CALL_OK) { - // - } - } + NavigationServer3D::get_singleton()->parse_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_root_node, p_callback); } void NavigationMeshGenerator::bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback) { - ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh."); - ERR_FAIL_COND_MSG(!p_source_geometry_data.is_valid(), "Invalid NavigationMeshSourceGeometryData3D."); - ERR_FAIL_COND_MSG(!p_source_geometry_data->has_data(), "NavigationMeshSourceGeometryData3D is empty. Parse source geometry first."); - - generator_mutex.lock(); - if (baking_navmeshes.has(p_navigation_mesh)) { - generator_mutex.unlock(); - ERR_FAIL_MSG("NavigationMesh is already baking. Wait for current bake to finish."); - } else { - baking_navmeshes.insert(p_navigation_mesh); - generator_mutex.unlock(); - } - -#ifndef _3D_DISABLED - const Vector<float> vertices = p_source_geometry_data->get_vertices(); - const Vector<int> indices = p_source_geometry_data->get_indices(); - - if (vertices.size() < 3 || indices.size() < 3) { - return; - } - - rcHeightfield *hf = nullptr; - rcCompactHeightfield *chf = nullptr; - rcContourSet *cset = nullptr; - rcPolyMesh *poly_mesh = nullptr; - rcPolyMeshDetail *detail_mesh = nullptr; - rcContext ctx; - - // added to keep track of steps, no functionality right now - String bake_state = ""; - - bake_state = "Setting up Configuration..."; // step #1 - - const float *verts = vertices.ptr(); - const int nverts = vertices.size() / 3; - const int *tris = indices.ptr(); - const int ntris = indices.size() / 3; - - float bmin[3], bmax[3]; - rcCalcBounds(verts, nverts, bmin, bmax); - - rcConfig cfg; - memset(&cfg, 0, sizeof(cfg)); - - cfg.cs = p_navigation_mesh->get_cell_size(); - cfg.ch = p_navigation_mesh->get_cell_height(); - cfg.walkableSlopeAngle = p_navigation_mesh->get_agent_max_slope(); - cfg.walkableHeight = (int)Math::ceil(p_navigation_mesh->get_agent_height() / cfg.ch); - cfg.walkableClimb = (int)Math::floor(p_navigation_mesh->get_agent_max_climb() / cfg.ch); - cfg.walkableRadius = (int)Math::ceil(p_navigation_mesh->get_agent_radius() / cfg.cs); - cfg.maxEdgeLen = (int)(p_navigation_mesh->get_edge_max_length() / p_navigation_mesh->get_cell_size()); - cfg.maxSimplificationError = p_navigation_mesh->get_edge_max_error(); - cfg.minRegionArea = (int)(p_navigation_mesh->get_region_min_size() * p_navigation_mesh->get_region_min_size()); - cfg.mergeRegionArea = (int)(p_navigation_mesh->get_region_merge_size() * p_navigation_mesh->get_region_merge_size()); - cfg.maxVertsPerPoly = (int)p_navigation_mesh->get_vertices_per_polygon(); - cfg.detailSampleDist = MAX(p_navigation_mesh->get_cell_size() * p_navigation_mesh->get_detail_sample_distance(), 0.1f); - cfg.detailSampleMaxError = p_navigation_mesh->get_cell_height() * p_navigation_mesh->get_detail_sample_max_error(); - - if (!Math::is_equal_approx((float)cfg.walkableHeight * cfg.ch, p_navigation_mesh->get_agent_height())) { - WARN_PRINT("Property agent_height is ceiled to cell_height voxel units and loses precision."); - } - if (!Math::is_equal_approx((float)cfg.walkableClimb * cfg.ch, p_navigation_mesh->get_agent_max_climb())) { - WARN_PRINT("Property agent_max_climb is floored to cell_height voxel units and loses precision."); - } - if (!Math::is_equal_approx((float)cfg.walkableRadius * cfg.cs, p_navigation_mesh->get_agent_radius())) { - WARN_PRINT("Property agent_radius is ceiled to cell_size voxel units and loses precision."); - } - if (!Math::is_equal_approx((float)cfg.maxEdgeLen * cfg.cs, p_navigation_mesh->get_edge_max_length())) { - WARN_PRINT("Property edge_max_length is rounded to cell_size voxel units and loses precision."); - } - if (!Math::is_equal_approx((float)cfg.minRegionArea, p_navigation_mesh->get_region_min_size() * p_navigation_mesh->get_region_min_size())) { - WARN_PRINT("Property region_min_size is converted to int and loses precision."); - } - if (!Math::is_equal_approx((float)cfg.mergeRegionArea, p_navigation_mesh->get_region_merge_size() * p_navigation_mesh->get_region_merge_size())) { - WARN_PRINT("Property region_merge_size is converted to int and loses precision."); - } - if (!Math::is_equal_approx((float)cfg.maxVertsPerPoly, p_navigation_mesh->get_vertices_per_polygon())) { - WARN_PRINT("Property vertices_per_polygon is converted to int and loses precision."); - } - if (p_navigation_mesh->get_cell_size() * p_navigation_mesh->get_detail_sample_distance() < 0.1f) { - WARN_PRINT("Property detail_sample_distance is clamped to 0.1 world units as the resulting value from multiplying with cell_size is too low."); - } - - cfg.bmin[0] = bmin[0]; - cfg.bmin[1] = bmin[1]; - cfg.bmin[2] = bmin[2]; - cfg.bmax[0] = bmax[0]; - cfg.bmax[1] = bmax[1]; - cfg.bmax[2] = bmax[2]; - - AABB baking_aabb = p_navigation_mesh->get_filter_baking_aabb(); - if (baking_aabb.has_volume()) { - Vector3 baking_aabb_offset = p_navigation_mesh->get_filter_baking_aabb_offset(); - cfg.bmin[0] = baking_aabb.position[0] + baking_aabb_offset.x; - cfg.bmin[1] = baking_aabb.position[1] + baking_aabb_offset.y; - cfg.bmin[2] = baking_aabb.position[2] + baking_aabb_offset.z; - cfg.bmax[0] = cfg.bmin[0] + baking_aabb.size[0]; - cfg.bmax[1] = cfg.bmin[1] + baking_aabb.size[1]; - cfg.bmax[2] = cfg.bmin[2] + baking_aabb.size[2]; - } - - bake_state = "Calculating grid size..."; // step #2 - rcCalcGridSize(cfg.bmin, cfg.bmax, cfg.cs, &cfg.width, &cfg.height); - - // ~30000000 seems to be around sweetspot where Editor baking breaks - if ((cfg.width * cfg.height) > 30000000) { - WARN_PRINT("NavigationMesh baking process will likely fail." - "\nSource geometry is suspiciously big for the current Cell Size and Cell Height in the NavMesh Resource bake settings." - "\nIf baking does not fail, the resulting NavigationMesh will create serious pathfinding performance issues." - "\nIt is advised to increase Cell Size and/or Cell Height in the NavMesh Resource bake settings or reduce the size / scale of the source geometry."); - } - - bake_state = "Creating heightfield..."; // step #3 - hf = rcAllocHeightfield(); - - ERR_FAIL_COND(!hf); - ERR_FAIL_COND(!rcCreateHeightfield(&ctx, *hf, cfg.width, cfg.height, cfg.bmin, cfg.bmax, cfg.cs, cfg.ch)); - - bake_state = "Marking walkable triangles..."; // step #4 - { - Vector<unsigned char> tri_areas; - tri_areas.resize(ntris); - - ERR_FAIL_COND(tri_areas.size() == 0); - - memset(tri_areas.ptrw(), 0, ntris * sizeof(unsigned char)); - rcMarkWalkableTriangles(&ctx, cfg.walkableSlopeAngle, verts, nverts, tris, ntris, tri_areas.ptrw()); - - ERR_FAIL_COND(!rcRasterizeTriangles(&ctx, verts, nverts, tris, tri_areas.ptr(), ntris, *hf, cfg.walkableClimb)); - } - - if (p_navigation_mesh->get_filter_low_hanging_obstacles()) { - rcFilterLowHangingWalkableObstacles(&ctx, cfg.walkableClimb, *hf); - } - if (p_navigation_mesh->get_filter_ledge_spans()) { - rcFilterLedgeSpans(&ctx, cfg.walkableHeight, cfg.walkableClimb, *hf); - } - if (p_navigation_mesh->get_filter_walkable_low_height_spans()) { - rcFilterWalkableLowHeightSpans(&ctx, cfg.walkableHeight, *hf); - } - - bake_state = "Constructing compact heightfield..."; // step #5 - - chf = rcAllocCompactHeightfield(); - - ERR_FAIL_COND(!chf); - ERR_FAIL_COND(!rcBuildCompactHeightfield(&ctx, cfg.walkableHeight, cfg.walkableClimb, *hf, *chf)); - - rcFreeHeightField(hf); - hf = nullptr; - - bake_state = "Eroding walkable area..."; // step #6 - - ERR_FAIL_COND(!rcErodeWalkableArea(&ctx, cfg.walkableRadius, *chf)); - - bake_state = "Partitioning..."; // step #7 - - if (p_navigation_mesh->get_sample_partition_type() == NavigationMesh::SAMPLE_PARTITION_WATERSHED) { - ERR_FAIL_COND(!rcBuildDistanceField(&ctx, *chf)); - ERR_FAIL_COND(!rcBuildRegions(&ctx, *chf, 0, cfg.minRegionArea, cfg.mergeRegionArea)); - } else if (p_navigation_mesh->get_sample_partition_type() == NavigationMesh::SAMPLE_PARTITION_MONOTONE) { - ERR_FAIL_COND(!rcBuildRegionsMonotone(&ctx, *chf, 0, cfg.minRegionArea, cfg.mergeRegionArea)); - } else { - ERR_FAIL_COND(!rcBuildLayerRegions(&ctx, *chf, 0, cfg.minRegionArea)); - } - - bake_state = "Creating contours..."; // step #8 - - cset = rcAllocContourSet(); - - ERR_FAIL_COND(!cset); - ERR_FAIL_COND(!rcBuildContours(&ctx, *chf, cfg.maxSimplificationError, cfg.maxEdgeLen, *cset)); - - bake_state = "Creating polymesh..."; // step #9 - - poly_mesh = rcAllocPolyMesh(); - ERR_FAIL_COND(!poly_mesh); - ERR_FAIL_COND(!rcBuildPolyMesh(&ctx, *cset, cfg.maxVertsPerPoly, *poly_mesh)); - - detail_mesh = rcAllocPolyMeshDetail(); - ERR_FAIL_COND(!detail_mesh); - ERR_FAIL_COND(!rcBuildPolyMeshDetail(&ctx, *poly_mesh, *chf, cfg.detailSampleDist, cfg.detailSampleMaxError, *detail_mesh)); - - rcFreeCompactHeightfield(chf); - chf = nullptr; - rcFreeContourSet(cset); - cset = nullptr; - - bake_state = "Converting to native navigation mesh..."; // step #10 - - Vector<Vector3> nav_vertices; - - for (int i = 0; i < detail_mesh->nverts; i++) { - const float *v = &detail_mesh->verts[i * 3]; - nav_vertices.push_back(Vector3(v[0], v[1], v[2])); - } - p_navigation_mesh->set_vertices(nav_vertices); - p_navigation_mesh->clear_polygons(); - - for (int i = 0; i < detail_mesh->nmeshes; i++) { - const unsigned int *detail_mesh_m = &detail_mesh->meshes[i * 4]; - const unsigned int detail_mesh_bverts = detail_mesh_m[0]; - const unsigned int detail_mesh_m_btris = detail_mesh_m[2]; - const unsigned int detail_mesh_ntris = detail_mesh_m[3]; - const unsigned char *detail_mesh_tris = &detail_mesh->tris[detail_mesh_m_btris * 4]; - for (unsigned int j = 0; j < detail_mesh_ntris; j++) { - Vector<int> nav_indices; - nav_indices.resize(3); - // Polygon order in recast is opposite than godot's - nav_indices.write[0] = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 0])); - nav_indices.write[1] = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 2])); - nav_indices.write[2] = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 1])); - p_navigation_mesh->add_polygon(nav_indices); - } - } - - bake_state = "Cleanup..."; // step #11 - - rcFreePolyMesh(poly_mesh); - poly_mesh = nullptr; - rcFreePolyMeshDetail(detail_mesh); - detail_mesh = nullptr; - - bake_state = "Baking finished."; // step #12 -#endif // _3D_DISABLED - - generator_mutex.lock(); - baking_navmeshes.erase(p_navigation_mesh); - generator_mutex.unlock(); - - if (p_callback.is_valid()) { - Callable::CallError ce; - Variant result; - p_callback.callp(nullptr, 0, result, ce); - if (ce.error == Callable::CallError::CALL_OK) { - // - } - } + NavigationServer3D::get_singleton()->bake_from_source_geometry_data(p_navigation_mesh, p_source_geometry_data, p_callback); } void NavigationMeshGenerator::_bind_methods() { diff --git a/modules/navigation/navigation_mesh_generator.h b/modules/navigation/navigation_mesh_generator.h index 4bf2b64f44..08fe9f9142 100644 --- a/modules/navigation/navigation_mesh_generator.h +++ b/modules/navigation/navigation_mesh_generator.h @@ -36,27 +36,16 @@ #include "scene/3d/navigation_region_3d.h" #include "scene/resources/navigation_mesh.h" -#include <Recast.h> - class NavigationMeshSourceGeometryData3D; class NavigationMeshGenerator : public Object { GDCLASS(NavigationMeshGenerator, Object); - Mutex generator_mutex; static NavigationMeshGenerator *singleton; - HashSet<Ref<NavigationMesh>> baking_navmeshes; - protected: static void _bind_methods(); - static void _add_vertex(const Vector3 &p_vec3, Vector<float> &p_vertices); - static void _add_mesh(const Ref<Mesh> &p_mesh, const Transform3D &p_xform, Vector<float> &p_vertices, Vector<int> &p_indices); - static void _add_mesh_array(const Array &p_array, const Transform3D &p_xform, Vector<float> &p_vertices, Vector<int> &p_indices); - static void _add_faces(const PackedVector3Array &p_faces, const Transform3D &p_xform, Vector<float> &p_vertices, Vector<int> &p_indices); - static void _parse_geometry(const Transform3D &p_navmesh_transform, Node *p_node, Vector<float> &p_vertices, Vector<int> &p_indices, NavigationMesh::ParsedGeometryType p_generate_from, uint32_t p_collision_mask, bool p_recurse_children); - public: static NavigationMeshGenerator *get_singleton(); diff --git a/modules/navigation/register_types.cpp b/modules/navigation/register_types.cpp index 1401833d0e..1548ff4b9c 100644 --- a/modules/navigation/register_types.cpp +++ b/modules/navigation/register_types.cpp @@ -32,9 +32,11 @@ #include "godot_navigation_server.h" +#ifndef DISABLE_DEPRECATED #ifndef _3D_DISABLED #include "navigation_mesh_generator.h" #endif +#endif // DISABLE_DEPRECATED #ifdef TOOLS_ENABLED #include "editor/navigation_mesh_editor_plugin.h" @@ -43,9 +45,11 @@ #include "core/config/engine.h" #include "servers/navigation_server_3d.h" +#ifndef DISABLE_DEPRECATED #ifndef _3D_DISABLED NavigationMeshGenerator *_nav_mesh_generator = nullptr; #endif +#endif // DISABLE_DEPRECATED NavigationServer3D *new_server() { return memnew(GodotNavigationServer); @@ -55,11 +59,13 @@ void initialize_navigation_module(ModuleInitializationLevel p_level) { if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) { NavigationServer3DManager::set_default_server(new_server); +#ifndef DISABLE_DEPRECATED #ifndef _3D_DISABLED _nav_mesh_generator = memnew(NavigationMeshGenerator); GDREGISTER_CLASS(NavigationMeshGenerator); Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationMeshGenerator", NavigationMeshGenerator::get_singleton())); #endif +#endif // DISABLE_DEPRECATED } #ifdef TOOLS_ENABLED @@ -74,9 +80,11 @@ void uninitialize_navigation_module(ModuleInitializationLevel p_level) { return; } +#ifndef DISABLE_DEPRECATED #ifndef _3D_DISABLED if (_nav_mesh_generator) { memdelete(_nav_mesh_generator); } #endif +#endif // DISABLE_DEPRECATED } diff --git a/modules/noise/doc_classes/FastNoiseLite.xml b/modules/noise/doc_classes/FastNoiseLite.xml index 4c6cdfbf12..1ab2552547 100644 --- a/modules/noise/doc_classes/FastNoiseLite.xml +++ b/modules/noise/doc_classes/FastNoiseLite.xml @@ -5,7 +5,7 @@ </brief_description> <description> This class generates noise using the FastNoiseLite library, which is a collection of several noise algorithms including Cellular, Perlin, Value, and more. - Most generated noise values are in the range of [code][-1,1][/code], however not always. Some of the cellular noise algorithms return results above [code]1[/code]. + Most generated noise values are in the range of [code][-1, 1][/code], but not always. Some of the cellular noise algorithms return results above [code]1[/code]. </description> <tutorials> </tutorials> diff --git a/modules/noise/doc_classes/Noise.xml b/modules/noise/doc_classes/Noise.xml index dd232af1cc..7d74c84f93 100644 --- a/modules/noise/doc_classes/Noise.xml +++ b/modules/noise/doc_classes/Noise.xml @@ -5,7 +5,7 @@ </brief_description> <description> This class defines the interface for noise generation libraries to inherit from. - A default get_seamless_noise() implementation is provided for libraries that do not provide seamless noise. This function requests a larger image from get_image(), reverses the quadrants of the image, then uses the strips of extra width to blend over the seams. + A default [method get_seamless_image] implementation is provided for libraries that do not provide seamless noise. This function requests a larger image from the [method get_image] method, reverses the quadrants of the image, then uses the strips of extra width to blend over the seams. Inheriting noise classes can optionally override this function to provide a more optimal algorithm. </description> <tutorials> diff --git a/modules/noise/doc_classes/NoiseTexture2D.xml b/modules/noise/doc_classes/NoiseTexture2D.xml index e25af794d4..3f4afc5141 100644 --- a/modules/noise/doc_classes/NoiseTexture2D.xml +++ b/modules/noise/doc_classes/NoiseTexture2D.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="NoiseTexture2D" inherits="Texture2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> - A texture filled with noise generated by a [Noise] object. + A 2D texture filled with noise generated by a [Noise] object. </brief_description> <description> - Uses [FastNoiseLite] or other libraries to fill the texture data of your desired size. [NoiseTexture2D] can also generate normal map textures. + Uses the [FastNoiseLite] library or other noise generators to fill the texture data of your desired size. [NoiseTexture2D] can also generate normal map textures. The class uses [Thread]s to generate the texture data internally, so [method Texture2D.get_image] may return [code]null[/code] if the generation process has not completed yet. In that case, you need to wait for the texture to be generated before accessing the image and the generated byte data: [codeblock] var texture = NoiseTexture2D.new() diff --git a/modules/noise/doc_classes/NoiseTexture3D.xml b/modules/noise/doc_classes/NoiseTexture3D.xml index 0ada6942ad..e8e205bc68 100644 --- a/modules/noise/doc_classes/NoiseTexture3D.xml +++ b/modules/noise/doc_classes/NoiseTexture3D.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="NoiseTexture3D" inherits="Texture3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> - A texture filled with noise generated by a [Noise] object. + A 3D texture filled with noise generated by a [Noise] object. </brief_description> <description> - Uses [FastNoiseLite] or other libraries to fill the texture data of your desired size. + Uses the [FastNoiseLite] library or other noise generators to fill the texture data of your desired size. The class uses [Thread]s to generate the texture data internally, so [method Texture3D.get_data] may return [code]null[/code] if the generation process has not completed yet. In that case, you need to wait for the texture to be generated before accessing the image: [codeblock] var texture = NoiseTexture3D.new() diff --git a/modules/noise/icons/NoiseTexture3D.svg b/modules/noise/icons/NoiseTexture3D.svg new file mode 100644 index 0000000000..92da633dce --- /dev/null +++ b/modules/noise/icons/NoiseTexture3D.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M1 14a1 1 0 0 0 1 1h9.5a1 1 0 0 0 .707-.293l2.5-2.5A1 1 0 0 0 15 11.5V2a1 1 0 0 0-1-1H4.5a1 1 0 0 0-.707.293l-2.5 2.5A1 1 0 0 0 1 4.5zm1.25-9H11v7H2.25zm10 6.25v-6.5L14 3v6.5zm-1-7.5H3L4.75 2H13zM3 11h4l1.25-1.25V9H9l1.25-1.25v-2h-2L7 7v.75h-.75v-2h-2L3 7z" fill="#e0e0e0"/><path d="M3 7h2l1.25-1.25h-2zm2 2h2V7.75h-.75zm2-2h2l1.25-1.25H8z" fill="#000" fill-opacity=".4"/><path d="M5 7v2l1.25-1.25v-2zm2 2v2l1.25-1.25V9zm2 0V7l1.25-1.25v2z" fill="#000" fill-opacity=".2"/></svg> diff --git a/modules/noise/noise_texture_3d.cpp b/modules/noise/noise_texture_3d.cpp index ed242e7faa..33e257a5c2 100644 --- a/modules/noise/noise_texture_3d.cpp +++ b/modules/noise/noise_texture_3d.cpp @@ -110,6 +110,7 @@ void NoiseTexture3D::_set_texture_data(const TypedArray<Image> &p_data) { } else { texture = RS::get_singleton()->texture_3d_create(data[0]->get_format(), data[0]->get_width(), data[0]->get_height(), data.size(), false, data); } + format = data[0]->get_format(); } emit_changed(); } @@ -346,6 +347,5 @@ Vector<Ref<Image>> NoiseTexture3D::get_data() const { } Image::Format NoiseTexture3D::get_format() const { - ERR_FAIL_COND_V(!texture.is_valid(), Image::FORMAT_L8); - return RS::get_singleton()->texture_3d_get(texture)[0]->get_format(); + return format; } diff --git a/modules/noise/noise_texture_3d.h b/modules/noise/noise_texture_3d.h index 397711ca98..13125efe7f 100644 --- a/modules/noise/noise_texture_3d.h +++ b/modules/noise/noise_texture_3d.h @@ -60,6 +60,8 @@ private: Ref<Gradient> color_ramp; Ref<Noise> noise; + Image::Format format = Image::FORMAT_L8; + void _thread_done(const TypedArray<Image> &p_data); static void _thread_function(void *p_ud); diff --git a/modules/openxr/SCsub b/modules/openxr/SCsub index f49dc390de..1bd10f1009 100644 --- a/modules/openxr/SCsub +++ b/modules/openxr/SCsub @@ -52,10 +52,11 @@ if env["builtin_openxr"]: env_thirdparty = env_openxr.Clone() env_thirdparty.disable_warnings() + env_thirdparty.AppendUnique(CPPDEFINES=["DISABLE_STD_FILESYSTEM"]) + if env["disable_exceptions"]: + env_thirdparty.AppendUnique(CPPDEFINES=["XRLOADER_DISABLE_EXCEPTION_HANDLING", ("JSON_USE_EXCEPTION", 0)]) - if "-fno-exceptions" in env_thirdparty["CXXFLAGS"]: - env_thirdparty["CXXFLAGS"].remove("-fno-exceptions") env_thirdparty.Append(CPPPATH=[thirdparty_dir + "/src/loader"]) # add in external jsoncpp dependency diff --git a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp index 81ba9c56b8..4829f713d2 100644 --- a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp +++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp @@ -126,7 +126,7 @@ void OpenXRExtensionWrapperExtension::on_before_instance_created() { } void OpenXRExtensionWrapperExtension::on_instance_created(const XrInstance p_instance) { - uint64_t instance = reinterpret_cast<uint64_t>(p_instance); + uint64_t instance = (uint64_t)p_instance; GDVIRTUAL_CALL(_on_instance_created, instance); } @@ -135,7 +135,7 @@ void OpenXRExtensionWrapperExtension::on_instance_destroyed() { } void OpenXRExtensionWrapperExtension::on_session_created(const XrSession p_session) { - uint64_t session = reinterpret_cast<uint64_t>(p_session); + uint64_t session = (uint64_t)p_session; GDVIRTUAL_CALL(_on_session_created, session); } diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp index 91684b55b9..9885190cb1 100644 --- a/modules/openxr/openxr_api.cpp +++ b/modules/openxr/openxr_api.cpp @@ -795,7 +795,7 @@ bool OpenXRAPI::create_swapchains() { Also Godot only creates a swapchain for the main output. OpenXR will require us to create swapchains as the render target for additional viewports if we want to use the layer system - to optimize text rendering and background rendering as OpenXR may choose to re-use the results for reprojection while we're + to optimize text rendering and background rendering as OpenXR may choose to reuse the results for reprojection while we're already rendering the next frame. Finally an area we need to expand upon is that Foveated rendering is only enabled for the swap chain we create, @@ -1668,7 +1668,7 @@ bool OpenXRAPI::process() { } bool OpenXRAPI::acquire_image(OpenXRSwapChainInfo &p_swapchain) { - ERR_FAIL_COND_V(p_swapchain.image_acquired, true); // this was not released when it should be, error out and re-use... + ERR_FAIL_COND_V(p_swapchain.image_acquired, true); // This was not released when it should be, error out and reuse... XrResult result; XrSwapchainImageAcquireInfo swapchain_image_acquire_info = { diff --git a/modules/text_server_adv/gdextension_build/SConstruct b/modules/text_server_adv/gdextension_build/SConstruct index af9dae84e3..4b12a4e41d 100644 --- a/modules/text_server_adv/gdextension_build/SConstruct +++ b/modules/text_server_adv/gdextension_build/SConstruct @@ -262,7 +262,6 @@ if env["freetype_enabled"]: CPPDEFINES=[ "FT2_BUILD_LIBRARY", "FT_CONFIG_OPTION_USE_PNG", - ("PNG_ARM_NEON_OPT", 0), "FT_CONFIG_OPTION_SYSTEM_ZLIB", ] ) diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index 13d8a2c17a..2a502d081e 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -2113,12 +2113,10 @@ Dictionary TextServerAdvanced::_font_get_ot_name_strings(const RID &p_font_rid) name = vformat("unknown_%d", names[i].name_id); } break; } + String text; unsigned int text_size = hb_ot_name_get_utf32(hb_face, names[i].name_id, names[i].language, nullptr, nullptr) + 1; - // @todo After godot-cpp#1141 is fixed, use text.resize() and write directly to text.wptr() instead of using a temporary buffer. - char32_t *buffer = memnew_arr(char32_t, text_size); - hb_ot_name_get_utf32(hb_face, names[i].name_id, names[i].language, &text_size, (uint32_t *)buffer); - String text(buffer); - memdelete_arr(buffer); + text.resize(text_size); + hb_ot_name_get_utf32(hb_face, names[i].name_id, names[i].language, &text_size, (uint32_t *)text.ptrw()); if (!text.is_empty()) { Dictionary &id_string = names_for_lang[String(hb_language_to_string(names[i].language))]; id_string[name] = text; @@ -4771,7 +4769,7 @@ void TextServerAdvanced::_shaped_text_overrun_trim_to_width(const RID &p_shaped_ if ((trim_pos >= 0 && sd->width > p_width) || enforce_ellipsis) { if (add_ellipsis && (ellipsis_pos > 0 || enforce_ellipsis)) { - // Insert an additional space when cutting word bound for aesthetics. + // Insert an additional space when cutting word bound for esthetics. if (cut_per_word && (ellipsis_pos > 0)) { Glyph gl; gl.count = 1; diff --git a/modules/text_server_adv/thorvg_svg_in_ot.cpp b/modules/text_server_adv/thorvg_svg_in_ot.cpp index 2f48f1564c..951ca15503 100644 --- a/modules/text_server_adv/thorvg_svg_in_ot.cpp +++ b/modules/text_server_adv/thorvg_svg_in_ot.cpp @@ -155,21 +155,10 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin } String xml_code_str = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"" + rtos(min_x) + " " + rtos(min_y) + " " + rtos(new_w) + " " + rtos(new_h) + "\">" + xml_body; -#ifndef GDEXTENSION gl_state.xml_code = xml_code_str.utf8(); -#else - CharString xml_code = xml_code_str.utf8(); - gl_state.xml_code_length = xml_code.length(); - gl_state.xml_code = memnew_arr(char, gl_state.xml_code_length); - memcpy(gl_state.xml_code, xml_code.get_data(), gl_state.xml_code_length); -#endif picture = tvg::Picture::gen(); -#ifndef GDEXTENSION result = picture->load(gl_state.xml_code.get_data(), gl_state.xml_code.length(), "svg+xml", false); -#else - result = picture->load(gl_state.xml_code, gl_state.xml_code_length, "svg+xml", false); -#endif if (result != tvg::Result::Success) { ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (glyph metrics)."); } @@ -257,11 +246,7 @@ FT_Error tvg_svg_in_ot_render(FT_GlyphSlot p_slot, FT_Pointer *p_state) { ERR_FAIL_COND_V_MSG(!gl_state.ready, FT_Err_Invalid_SVG_Document, "SVG glyph not ready."); std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen(); -#ifndef GDEXTENSION tvg::Result res = picture->load(gl_state.xml_code.get_data(), gl_state.xml_code.length(), "svg+xml", false); -#else - tvg::Result res = picture->load(gl_state.xml_code, gl_state.xml_code_length, "svg+xml", false); -#endif if (res != tvg::Result::Success) { ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (glyph rendering)."); } diff --git a/modules/text_server_adv/thorvg_svg_in_ot.h b/modules/text_server_adv/thorvg_svg_in_ot.h index 5e79c8e444..034fffb5e6 100644 --- a/modules/text_server_adv/thorvg_svg_in_ot.h +++ b/modules/text_server_adv/thorvg_svg_in_ot.h @@ -66,22 +66,8 @@ struct GL_State { float y = 0; float w = 0; float h = 0; -#ifndef GDEXTENSION CharString xml_code; -#else - // @todo After godot-cpp#1142 is fixed, use CharString just like when compiled as a Godot module. - char *xml_code = nullptr; - int xml_code_length = 0; -#endif tvg::Matrix m; - -#ifdef GDEXTENSION - ~GL_State() { - if (xml_code) { - memdelete_arr(xml_code); - } - } -#endif }; struct TVG_State { diff --git a/modules/text_server_fb/gdextension_build/SConstruct b/modules/text_server_fb/gdextension_build/SConstruct index 51a6ee06be..fcf8976019 100644 --- a/modules/text_server_fb/gdextension_build/SConstruct +++ b/modules/text_server_fb/gdextension_build/SConstruct @@ -257,7 +257,6 @@ if env["freetype_enabled"]: CPPDEFINES=[ "FT2_BUILD_LIBRARY", "FT_CONFIG_OPTION_USE_PNG", - ("PNG_ARM_NEON_OPT", 0), "FT_CONFIG_OPTION_SYSTEM_ZLIB", ] ) diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index dc0a7df81a..70da5e2782 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -3563,7 +3563,7 @@ void TextServerFallback::_shaped_text_overrun_trim_to_width(const RID &p_shaped_ if ((trim_pos >= 0 && sd->width > p_width) || enforce_ellipsis) { if (add_ellipsis && (ellipsis_pos > 0 || enforce_ellipsis)) { - // Insert an additional space when cutting word bound for aesthetics. + // Insert an additional space when cutting word bound for esthetics. if (cut_per_word && (ellipsis_pos > 0)) { Glyph gl; gl.count = 1; diff --git a/modules/zip/doc_classes/ZIPReader.xml b/modules/zip/doc_classes/ZIPReader.xml index 0fa2672044..bf596806b2 100644 --- a/modules/zip/doc_classes/ZIPReader.xml +++ b/modules/zip/doc_classes/ZIPReader.xml @@ -25,6 +25,15 @@ Closes the underlying resources used by this instance. </description> </method> + <method name="file_exists"> + <return type="bool" /> + <param index="0" name="path" type="String" /> + <param index="1" name="case_sensitive" type="bool" default="true" /> + <description> + Returns [code]true[/code] if the file exists in the loaded zip archive. + Must be called after [method open]. + </description> + </method> <method name="get_files"> <return type="PackedStringArray" /> <description> diff --git a/modules/zip/zip_reader.cpp b/modules/zip/zip_reader.cpp index 2684875e1c..5752b829ef 100644 --- a/modules/zip/zip_reader.cpp +++ b/modules/zip/zip_reader.cpp @@ -118,6 +118,21 @@ PackedByteArray ZIPReader::read_file(String p_path, bool p_case_sensitive) { return data; } +bool ZIPReader::file_exists(String p_path, bool p_case_sensitive) { + ERR_FAIL_COND_V_MSG(fa.is_null(), false, "ZIPReader must be opened before use."); + + int cs = p_case_sensitive ? 1 : 2; + if (unzLocateFile(uzf, p_path.utf8().get_data(), cs) != UNZ_OK) { + return false; + } + if (unzOpenCurrentFile(uzf) != UNZ_OK) { + return false; + } + + unzCloseCurrentFile(uzf); + return true; +} + ZIPReader::ZIPReader() {} ZIPReader::~ZIPReader() { @@ -131,4 +146,5 @@ void ZIPReader::_bind_methods() { ClassDB::bind_method(D_METHOD("close"), &ZIPReader::close); ClassDB::bind_method(D_METHOD("get_files"), &ZIPReader::get_files); ClassDB::bind_method(D_METHOD("read_file", "path", "case_sensitive"), &ZIPReader::read_file, DEFVAL(Variant(true))); + ClassDB::bind_method(D_METHOD("file_exists", "path", "case_sensitive"), &ZIPReader::file_exists, DEFVAL(Variant(true))); } diff --git a/modules/zip/zip_reader.h b/modules/zip/zip_reader.h index c074197eb4..0f78352e3f 100644 --- a/modules/zip/zip_reader.h +++ b/modules/zip/zip_reader.h @@ -51,6 +51,7 @@ public: PackedStringArray get_files(); PackedByteArray read_file(String p_path, bool p_case_sensitive); + bool file_exists(String p_path, bool p_case_sensitive); ZIPReader(); ~ZIPReader(); diff --git a/platform/android/detect.py b/platform/android/detect.py index 2860898e5c..33c6565789 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -170,10 +170,6 @@ def configure(env: "Environment"): env["RANLIB"] = compiler_path + "/llvm-ranlib" env["AS"] = compiler_path + "/clang" - # Disable exceptions on template builds - if not env.editor_build: - env.Append(CXXFLAGS=["-fno-exceptions"]) - env.Append( CCFLAGS=( "-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -fvisibility=hidden -fno-strict-aliasing".split() diff --git a/platform/android/doc_classes/EditorExportPlatformAndroid.xml b/platform/android/doc_classes/EditorExportPlatformAndroid.xml index d61d63d242..d270980d72 100644 --- a/platform/android/doc_classes/EditorExportPlatformAndroid.xml +++ b/platform/android/doc_classes/EditorExportPlatformAndroid.xml @@ -110,6 +110,10 @@ <member name="package/show_in_android_tv" type="bool" setter="" getter=""> If [code]true[/code], this app will show in Android TV launcher UI. </member> + <member name="package/show_in_app_library" type="bool" setter="" getter=""> + If [code]true[/code], this app will show in the device's app library. + [b]Note:[/b] This is [code]true[/code] by default. + </member> <member name="package/signed" type="bool" setter="" getter=""> If [code]true[/code], package signing is enabled. </member> @@ -150,7 +154,7 @@ Allows an application to act as an AccountAuthenticator for the AccountManager. </member> <member name="permissions/battery_stats" type="bool" setter="" getter=""> - Allows an application to collect battery statistics. Sett [url=https://developer.android.com/reference/android/Manifest.permission#BATTERY_STATS]BATTERY_STATS[/url]. + Allows an application to collect battery statistics. See [url=https://developer.android.com/reference/android/Manifest.permission#BATTERY_STATS]BATTERY_STATS[/url]. </member> <member name="permissions/bind_accessibility_service" type="bool" setter="" getter=""> Must be required by an AccessibilityService, to ensure that only the system can bind to it. See [url=https://developer.android.com/reference/android/Manifest.permission#BIND_ACCESSIBILITY_SERVICE]BIND_ACCESSIBILITY_SERVICE[/url]. @@ -579,10 +583,10 @@ If [code]true[/code], allows the application to participate in the backup and restore infrastructure. </member> <member name="version/code" type="int" setter="" getter=""> - Machine-readable application version. + Machine-readable application version. This must be incremented for every new release pushed to the Play Store. </member> <member name="version/name" type="String" setter="" getter=""> - Application version visible to the user. + Application version visible to the user. Falls back to [member ProjectSettings.application/config/version] if left empty. </member> <member name="xr_features/xr_mode" type="int" setter="" getter=""> </member> diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index 4eb516fb63..21de46f4fc 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -47,6 +47,7 @@ #include "editor/editor_paths.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/import/resource_importer_texture_settings.h" #include "main/splash.gen.h" #include "scene/resources/image_texture.h" @@ -904,7 +905,7 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p uint32_t string_table_ends = 0; Vector<uint8_t> stable_extra; - String version_name = p_preset->get("version/name"); + String version_name = p_preset->get_version("version/name"); int version_code = p_preset->get("version/code"); String package_name = p_preset->get("package/unique_name"); @@ -1829,7 +1830,7 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name"), "1.0")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Leave empty to use project version"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "ext.domain.name"), "org.godotengine.$genname", false, true)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name [default if blank]"), "")); @@ -1838,6 +1839,7 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/retain_data_on_uninstall"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/exclude_from_recents"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/show_in_android_tv"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/show_in_app_library"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/show_as_launcher_app"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_icon_option, PROPERTY_HINT_FILE, "*.png"), "")); @@ -1980,7 +1982,7 @@ Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset, String output; bool remove_prev = EDITOR_GET("export/android/one_click_deploy_clear_previous_install"); - String version_name = p_preset->get("version/name"); + String version_name = p_preset->get_version("version/name"); String package_name = p_preset->get("package/unique_name"); if (remove_prev) { @@ -2225,17 +2227,15 @@ String EditorExportPlatformAndroid::get_apksigner_path(int p_target_sdk, bool p_ } bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const { -#ifdef MODULE_MONO_ENABLED - // Don't check for additional errors, as this particular error cannot be resolved. - r_error += TTR("Exporting to Android is currently not supported in Godot 4 when using C#/.NET. Use Godot 3 to target Android with C#/Mono instead.") + "\n"; - r_error += TTR("If this project does not use C#, use a non-C# editor build to export the project.") + "\n"; - return false; -#else - String err; bool valid = false; const bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build"); +#ifdef MODULE_MONO_ENABLED + // Android export is still a work in progress, keep a message as a warning. + err += TTR("Exporting to Android when using C#/.NET is experimental.") + "\n"; +#endif + // Look for export templates (first official, and if defined custom templates). if (!gradle_build_enabled) { @@ -2365,7 +2365,6 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito } return valid; -#endif // !MODULE_MONO_ENABLED } bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const { @@ -2386,10 +2385,8 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit } } - String etc_error = test_etc2(); - if (!etc_error.is_empty()) { + if (!ResourceImporterTextureSettings::should_import_etc2_astc()) { valid = false; - err += etc_error; } String min_sdk_str = p_preset->get("gradle_build/min_sdk"); @@ -2893,7 +2890,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP String package_name = get_package_name(p_preset->get("package/unique_name")); String version_code = itos(p_preset->get("version/code")); - String version_name = p_preset->get("version/name"); + String version_name = p_preset->get_version("version/name"); String min_sdk_version = p_preset->get("gradle_build/min_sdk"); if (!min_sdk_version.is_valid_int()) { min_sdk_version = itos(VULKAN_MIN_SDK_VERSION); @@ -3111,7 +3108,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP String cmdline = p_preset->get("command_line/extra_args"); - String version_name = p_preset->get("version/name"); + String version_name = p_preset->get_version("version/name"); String package_name = p_preset->get("package/unique_name"); String apk_expansion_pkey = p_preset->get("apk_expansion/public_key"); diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp index d0d0c34bb4..0915009235 100644 --- a/platform/android/export/gradle_export_util.cpp +++ b/platform/android/export/gradle_export_util.cpp @@ -269,7 +269,12 @@ String _get_activity_tag(const Ref<EditorExportPlatform> &p_export_platform, con manifest_activity_text += " <intent-filter>\n" " <action android:name=\"android.intent.action.MAIN\" />\n" - " <category android:name=\"android.intent.category.LAUNCHER\" />\n"; + " <category android:name=\"android.intent.category.DEFAULT\" />\n"; + + bool show_in_app_library = p_preset->get("package/show_in_app_library"); + if (show_in_app_library) { + manifest_activity_text += " <category android:name=\"android.intent.category.LAUNCHER\" />\n"; + } bool uses_leanback_category = p_preset->get("package/show_in_android_tv"); if (uses_leanback_category) { @@ -279,7 +284,6 @@ String _get_activity_tag(const Ref<EditorExportPlatform> &p_export_platform, con bool uses_home_category = p_preset->get("package/show_as_launcher_app"); if (uses_home_category) { manifest_activity_text += " <category android:name=\"android.intent.category.HOME\" />\n"; - manifest_activity_text += " <category android:name=\"android.intent.category.DEFAULT\" />\n"; } manifest_activity_text += " </intent-filter>\n"; diff --git a/platform/android/file_access_android.h b/platform/android/file_access_android.h index 7ff5c4d117..3aa4ca98fc 100644 --- a/platform/android/file_access_android.h +++ b/platform/android/file_access_android.h @@ -76,8 +76,13 @@ public: virtual bool file_exists(const String &p_path) override; // return true if a file exists virtual uint64_t _get_modified_time(const String &p_file) override { return 0; } - virtual uint32_t _get_unix_permissions(const String &p_file) override { return 0; } - virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override { return FAILED; } + virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; } + virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return FAILED; } + + virtual bool _get_hidden_attribute(const String &p_file) override { return false; } + virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; } + virtual bool _get_read_only_attribute(const String &p_file) override { return false; } + virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; } virtual void close() override; diff --git a/platform/android/file_access_filesystem_jandroid.h b/platform/android/file_access_filesystem_jandroid.h index 739c8a0925..0c3f8d7259 100644 --- a/platform/android/file_access_filesystem_jandroid.h +++ b/platform/android/file_access_filesystem_jandroid.h @@ -91,8 +91,13 @@ public: static void setup(jobject p_file_access_handler); virtual uint64_t _get_modified_time(const String &p_file) override; - virtual uint32_t _get_unix_permissions(const String &p_file) override { return 0; } - virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override { return FAILED; } + virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; } + virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return FAILED; } + + virtual bool _get_hidden_attribute(const String &p_file) override { return false; } + virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; } + virtual bool _get_read_only_attribute(const String &p_file) override { return false; } + virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; } virtual void close() override; diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml index 542ab51660..56d403a263 100644 --- a/platform/android/java/app/AndroidManifest.xml +++ b/platform/android/java/app/AndroidManifest.xml @@ -45,6 +45,7 @@ <intent-filter> <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> diff --git a/platform/android/java/editor/src/main/AndroidManifest.xml b/platform/android/java/editor/src/main/AndroidManifest.xml index c7b2c8ad67..cb89d6e1b0 100644 --- a/platform/android/java/editor/src/main/AndroidManifest.xml +++ b/platform/android/java/editor/src/main/AndroidManifest.xml @@ -45,6 +45,7 @@ <intent-filter> <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> diff --git a/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java b/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java index 8449c08b88..56397bb2c2 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java @@ -867,7 +867,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback */ public interface EGLConfigChooser { /** - * Choose a configuration from the list. Implementors typically + * Choose a configuration from the list. Implementers typically * implement this method by calling * {@link EGL10#eglChooseConfig} and iterating through the results. Please consult the * EGL specification available from The Khronos Group to learn how to call eglChooseConfig. diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java index 185d03fe39..1a25be0460 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java @@ -306,7 +306,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { return; } - // Assign first available number. Re-use numbers where possible. + // Assign first available number. Reuse numbers where possible. final int id = assignJoystickIdNumber(deviceId); final Joystick joystick = new Joystick(); diff --git a/platform/ios/detect.py b/platform/ios/detect.py index f5501e3d85..e11c0b7d91 100644 --- a/platform/ios/detect.py +++ b/platform/ios/detect.py @@ -30,7 +30,6 @@ def get_opts(): ), ("IOS_SDK_PATH", "Path to the iOS SDK", ""), BoolVariable("ios_simulator", "Build for iOS Simulator", False), - BoolVariable("ios_exceptions", "Enable exceptions", False), ("ios_triple", "Triple for ios toolchain", ""), ] @@ -85,19 +84,18 @@ def configure(env: "Environment"): env["ENV"]["PATH"] = env["IOS_TOOLCHAIN_PATH"] + "/Developer/usr/bin/:" + env["ENV"]["PATH"] compiler_path = "$IOS_TOOLCHAIN_PATH/usr/bin/${ios_triple}" - s_compiler_path = "$IOS_TOOLCHAIN_PATH/Developer/usr/bin/" ccache_path = os.environ.get("CCACHE") if ccache_path is None: env["CC"] = compiler_path + "clang" env["CXX"] = compiler_path + "clang++" - env["S_compiler"] = s_compiler_path + "gcc" + env["S_compiler"] = compiler_path + "clang" else: # there aren't any ccache wrappers available for iOS, # to enable caching we need to prepend the path to the ccache binary env["CC"] = ccache_path + " " + compiler_path + "clang" env["CXX"] = ccache_path + " " + compiler_path + "clang++" - env["S_compiler"] = ccache_path + " " + s_compiler_path + "gcc" + env["S_compiler"] = ccache_path + " " + compiler_path + "clang" env["AR"] = compiler_path + "ar" env["RANLIB"] = compiler_path + "ranlib" @@ -139,11 +137,6 @@ def configure(env: "Environment"): env.Append(ASFLAGS=["-arch", "arm64"]) env.Append(CPPDEFINES=["NEED_LONG_INT"]) - if env["ios_exceptions"]: - env.Append(CCFLAGS=["-fexceptions"]) - else: - env.Append(CCFLAGS=["-fno-exceptions"]) - # Temp fix for ABS/MAX/MIN macros in iOS SDK blocking compilation env.Append(CCFLAGS=["-Wno-ambiguous-macro"]) diff --git a/platform/ios/doc_classes/EditorExportPlatformIOS.xml b/platform/ios/doc_classes/EditorExportPlatformIOS.xml index 84bc0e1277..563c057266 100644 --- a/platform/ios/doc_classes/EditorExportPlatformIOS.xml +++ b/platform/ios/doc_classes/EditorExportPlatformIOS.xml @@ -45,7 +45,7 @@ Can be overridden with the environment variable [code]GODOT_IOS_PROVISIONING_PROFILE_UUID_RELEASE[/code]. </member> <member name="application/short_version" type="String" setter="" getter=""> - Application version visible to the user, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]). + Application version visible to the user, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]). Falls back to [member ProjectSettings.application/config/version] if left empty. </member> <member name="application/signature" type="String" setter="" getter=""> A four-character creator code that is specific to the bundle. Optional. @@ -54,7 +54,7 @@ Supported device family. </member> <member name="application/version" type="String" setter="" getter=""> - Machine-readable application version, in the [code]major.minor.patch[/code] format, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]). + Machine-readable application version, in the [code]major.minor.patch[/code] format, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]). This must be incremented on every new release pushed to the App Store. </member> <member name="architectures/arm64" type="bool" setter="" getter=""> If [code]true[/code], [code]arm64[/code] binaries are included into exported project. diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index 544bfb71e0..b6320fb22b 100644 --- a/platform/ios/export/export_plugin.cpp +++ b/platform/ios/export/export_plugin.cpp @@ -39,6 +39,7 @@ #include "editor/editor_paths.h" #include "editor/editor_scale.h" #include "editor/export/editor_export.h" +#include "editor/import/resource_importer_texture_settings.h" #include "editor/plugins/script_editor_plugin.h" #include "modules/modules_enabled.gen.h" // For mono and svg. @@ -177,7 +178,7 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/bundle_identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), "", false, true)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version", PROPERTY_HINT_PLACEHOLDER_TEXT, "Leave empty to use project version"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/icon_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4)); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/launch_screens_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4)); @@ -284,7 +285,7 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_ } else if (lines[i].find("$short_version") != -1) { strnew += lines[i].replace("$short_version", p_preset->get("application/short_version")) + "\n"; } else if (lines[i].find("$version") != -1) { - strnew += lines[i].replace("$version", p_preset->get("application/version")) + "\n"; + strnew += lines[i].replace("$version", p_preset->get_version("application/version")) + "\n"; } else if (lines[i].find("$signature") != -1) { strnew += lines[i].replace("$signature", p_preset->get("application/signature")) + "\n"; } else if (lines[i].find("$team_id") != -1) { @@ -1984,10 +1985,8 @@ bool EditorExportPlatformIOS::has_valid_project_configuration(const Ref<EditorEx } } - const String etc_error = test_etc2(); - if (!etc_error.is_empty()) { + if (!ResourceImporterTextureSettings::should_import_etc2_astc()) { valid = false; - err += etc_error; } if (!err.is_empty()) { diff --git a/platform/linuxbsd/freedesktop_portal_desktop.cpp b/platform/linuxbsd/freedesktop_portal_desktop.cpp index c3cb0f411d..91c14e0e91 100644 --- a/platform/linuxbsd/freedesktop_portal_desktop.cpp +++ b/platform/linuxbsd/freedesktop_portal_desktop.cpp @@ -32,6 +32,7 @@ #ifdef DBUS_ENABLED +#include "core/crypto/crypto_core.h" #include "core/error/error_macros.h" #include "core/os/os.h" #include "core/string/ustring.h" @@ -43,12 +44,15 @@ #include <dbus/dbus.h> #endif +#include <unistd.h> + #define BUS_OBJECT_NAME "org.freedesktop.portal.Desktop" #define BUS_OBJECT_PATH "/org/freedesktop/portal/desktop" #define BUS_INTERFACE_SETTINGS "org.freedesktop.portal.Settings" +#define BUS_INTERFACE_FILE_CHOOSER "org.freedesktop.portal.FileChooser" -static bool try_parse_variant(DBusMessage *p_reply_message, int p_type, void *r_value) { +bool FreeDesktopPortalDesktop::try_parse_variant(DBusMessage *p_reply_message, int p_type, void *r_value) { DBusMessageIter iter[3]; dbus_message_iter_init(p_reply_message, &iter[0]); @@ -80,11 +84,11 @@ bool FreeDesktopPortalDesktop::read_setting(const char *p_namespace, const char DBusConnection *bus = dbus_bus_get(DBUS_BUS_SESSION, &error); if (dbus_error_is_set(&error)) { - dbus_error_free(&error); - unsupported = true; if (OS::get_singleton()->is_stdout_verbose()) { - ERR_PRINT(String() + "Error opening D-Bus connection: " + error.message); + ERR_PRINT(vformat("Error opening D-Bus connection: %s", error.message)); } + dbus_error_free(&error); + unsupported = true; return false; } @@ -100,11 +104,11 @@ bool FreeDesktopPortalDesktop::read_setting(const char *p_namespace, const char DBusMessage *reply = dbus_connection_send_with_reply_and_block(bus, message, 50, &error); dbus_message_unref(message); if (dbus_error_is_set(&error)) { - dbus_error_free(&error); - dbus_connection_unref(bus); if (OS::get_singleton()->is_stdout_verbose()) { - ERR_PRINT(String() + "Error on D-Bus communication: " + error.message); + ERR_PRINT(vformat("Error on D-Bus communication: %s", error.message)); } + dbus_error_free(&error); + dbus_connection_unref(bus); return false; } @@ -126,6 +130,317 @@ uint32_t FreeDesktopPortalDesktop::get_appearance_color_scheme() { return value; } +static const char *cs_empty = ""; + +void FreeDesktopPortalDesktop::append_dbus_string(DBusMessageIter *p_iter, const String &p_string) { + CharString cs = p_string.utf8(); + const char *cs_ptr = cs.ptr(); + if (cs_ptr) { + dbus_message_iter_append_basic(p_iter, DBUS_TYPE_STRING, &cs_ptr); + } else { + dbus_message_iter_append_basic(p_iter, DBUS_TYPE_STRING, &cs_empty); + } +} + +void FreeDesktopPortalDesktop::append_dbus_dict_filters(DBusMessageIter *p_iter, const Vector<String> &p_filters) { + DBusMessageIter dict_iter; + DBusMessageIter var_iter; + DBusMessageIter arr_iter; + const char *filters_key = "filters"; + + dbus_message_iter_open_container(p_iter, DBUS_TYPE_DICT_ENTRY, nullptr, &dict_iter); + dbus_message_iter_append_basic(&dict_iter, DBUS_TYPE_STRING, &filters_key); + dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, "a(sa(us))", &var_iter); + dbus_message_iter_open_container(&var_iter, DBUS_TYPE_ARRAY, "(sa(us))", &arr_iter); + for (int i = 0; i < p_filters.size(); i++) { + Vector<String> tokens = p_filters[i].split(";"); + if (tokens.size() == 2) { + DBusMessageIter struct_iter; + DBusMessageIter array_iter; + DBusMessageIter array_struct_iter; + dbus_message_iter_open_container(&arr_iter, DBUS_TYPE_STRUCT, nullptr, &struct_iter); + append_dbus_string(&struct_iter, tokens[0]); + + dbus_message_iter_open_container(&struct_iter, DBUS_TYPE_ARRAY, "(us)", &array_iter); + dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, nullptr, &array_struct_iter); + { + const unsigned nil = 0; + dbus_message_iter_append_basic(&array_struct_iter, DBUS_TYPE_UINT32, &nil); + } + append_dbus_string(&array_struct_iter, tokens[1]); + dbus_message_iter_close_container(&array_iter, &array_struct_iter); + dbus_message_iter_close_container(&struct_iter, &array_iter); + dbus_message_iter_close_container(&arr_iter, &struct_iter); + } + } + dbus_message_iter_close_container(&var_iter, &arr_iter); + dbus_message_iter_close_container(&dict_iter, &var_iter); + dbus_message_iter_close_container(p_iter, &dict_iter); +} + +void FreeDesktopPortalDesktop::append_dbus_dict_string(DBusMessageIter *p_iter, const String &p_key, const String &p_value, bool p_as_byte_array) { + DBusMessageIter dict_iter; + DBusMessageIter var_iter; + dbus_message_iter_open_container(p_iter, DBUS_TYPE_DICT_ENTRY, nullptr, &dict_iter); + append_dbus_string(&dict_iter, p_key); + + if (p_as_byte_array) { + DBusMessageIter arr_iter; + dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, "ay", &var_iter); + dbus_message_iter_open_container(&var_iter, DBUS_TYPE_ARRAY, "y", &arr_iter); + CharString cs = p_value.utf8(); + const char *cs_ptr = cs.get_data(); + do { + dbus_message_iter_append_basic(&arr_iter, DBUS_TYPE_BYTE, cs_ptr); + } while (*cs_ptr++); + dbus_message_iter_close_container(&var_iter, &arr_iter); + } else { + dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, "s", &var_iter); + append_dbus_string(&var_iter, p_value); + } + + dbus_message_iter_close_container(&dict_iter, &var_iter); + dbus_message_iter_close_container(p_iter, &dict_iter); +} + +void FreeDesktopPortalDesktop::append_dbus_dict_bool(DBusMessageIter *p_iter, const String &p_key, bool p_value) { + DBusMessageIter dict_iter; + DBusMessageIter var_iter; + dbus_message_iter_open_container(p_iter, DBUS_TYPE_DICT_ENTRY, nullptr, &dict_iter); + append_dbus_string(&dict_iter, p_key); + + dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, "b", &var_iter); + { + int val = p_value; + dbus_message_iter_append_basic(&var_iter, DBUS_TYPE_BOOLEAN, &val); + } + + dbus_message_iter_close_container(&dict_iter, &var_iter); + dbus_message_iter_close_container(p_iter, &dict_iter); +} + +bool FreeDesktopPortalDesktop::file_chooser_parse_response(DBusMessageIter *p_iter, bool &r_cancel, Vector<String> &r_urls) { + ERR_FAIL_COND_V(dbus_message_iter_get_arg_type(p_iter) != DBUS_TYPE_UINT32, false); + + dbus_uint32_t resp_code; + dbus_message_iter_get_basic(p_iter, &resp_code); + if (resp_code != 0) { + r_cancel = true; + } else { + r_cancel = false; + ERR_FAIL_COND_V(!dbus_message_iter_next(p_iter), false); + ERR_FAIL_COND_V(dbus_message_iter_get_arg_type(p_iter) != DBUS_TYPE_ARRAY, false); + + DBusMessageIter dict_iter; + dbus_message_iter_recurse(p_iter, &dict_iter); + while (dbus_message_iter_get_arg_type(&dict_iter) == DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter iter; + dbus_message_iter_recurse(&dict_iter, &iter); + if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING) { + const char *key; + dbus_message_iter_get_basic(&iter, &key); + dbus_message_iter_next(&iter); + + DBusMessageIter var_iter; + dbus_message_iter_recurse(&iter, &var_iter); + if (strcmp(key, "uris") == 0) { + if (dbus_message_iter_get_arg_type(&var_iter) == DBUS_TYPE_ARRAY) { + DBusMessageIter uri_iter; + dbus_message_iter_recurse(&var_iter, &uri_iter); + while (dbus_message_iter_get_arg_type(&uri_iter) == DBUS_TYPE_STRING) { + const char *value; + dbus_message_iter_get_basic(&uri_iter, &value); + r_urls.push_back(String::utf8(value).trim_prefix("file://").uri_decode()); + if (!dbus_message_iter_next(&uri_iter)) { + break; + } + } + } + } + } + if (!dbus_message_iter_next(&dict_iter)) { + break; + } + } + } + return true; +} + +Error FreeDesktopPortalDesktop::file_dialog_show(const String &p_xid, const String &p_title, const String &p_current_directory, const String &p_filename, DisplayServer::FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) { + if (unsupported) { + return FAILED; + } + + DBusError err; + dbus_error_init(&err); + + // Open connection and add signal handler. + FileDialogData fd; + fd.callback = p_callback; + + CryptoCore::RandomGenerator rng; + ERR_FAIL_COND_V_MSG(rng.init(), FAILED, "Failed to initialize random number generator."); + uint8_t uuid[64]; + Error rng_err = rng.get_random_bytes(uuid, 64); + ERR_FAIL_COND_V_MSG(rng_err, rng_err, "Failed to generate unique token."); + + fd.connection = dbus_bus_get(DBUS_BUS_SESSION, &err); + if (dbus_error_is_set(&err)) { + ERR_PRINT(vformat("Failed to open DBus connection: %s", err.message)); + dbus_error_free(&err); + unsupported = true; + return FAILED; + } + + String dbus_unique_name = String::utf8(dbus_bus_get_unique_name(fd.connection)); + String token = String::hex_encode_buffer(uuid, 64); + String path = vformat("/org/freedesktop/portal/desktop/request/%s/%s", dbus_unique_name.replace(".", "_").replace(":", ""), token); + + fd.path = vformat("type='signal',sender='org.freedesktop.portal.Desktop',path='%s',interface='org.freedesktop.portal.Request',member='Response',destination='%s'", path, dbus_unique_name); + dbus_bus_add_match(fd.connection, fd.path.utf8().get_data(), &err); + if (dbus_error_is_set(&err)) { + ERR_PRINT(vformat("Failed to add DBus match: %s", err.message)); + dbus_error_free(&err); + dbus_connection_unref(fd.connection); + return FAILED; + } + + // Generate FileChooser message. + const char *method = nullptr; + switch (p_mode) { + case DisplayServer::FILE_DIALOG_MODE_SAVE_FILE: { + method = "SaveFile"; + } break; + case DisplayServer::FILE_DIALOG_MODE_OPEN_ANY: + case DisplayServer::FILE_DIALOG_MODE_OPEN_FILE: + case DisplayServer::FILE_DIALOG_MODE_OPEN_DIR: + case DisplayServer::FILE_DIALOG_MODE_OPEN_FILES: { + method = "OpenFile"; + } break; + } + + DBusMessage *message = dbus_message_new_method_call(BUS_OBJECT_NAME, BUS_OBJECT_PATH, BUS_INTERFACE_FILE_CHOOSER, method); + { + DBusMessageIter iter; + dbus_message_iter_init_append(message, &iter); + + append_dbus_string(&iter, p_xid); + append_dbus_string(&iter, p_title); + + DBusMessageIter arr_iter; + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &arr_iter); + + append_dbus_dict_string(&arr_iter, "handle_token", token); + append_dbus_dict_bool(&arr_iter, "multiple", p_mode == DisplayServer::FILE_DIALOG_MODE_OPEN_FILES); + append_dbus_dict_bool(&arr_iter, "directory", p_mode == DisplayServer::FILE_DIALOG_MODE_OPEN_DIR); + append_dbus_dict_filters(&arr_iter, p_filters); + append_dbus_dict_string(&arr_iter, "current_folder", p_current_directory, true); + if (p_mode == DisplayServer::FILE_DIALOG_MODE_SAVE_FILE) { + append_dbus_dict_string(&arr_iter, "current_name", p_filename); + } + + dbus_message_iter_close_container(&iter, &arr_iter); + } + + DBusMessage *reply = dbus_connection_send_with_reply_and_block(fd.connection, message, DBUS_TIMEOUT_INFINITE, &err); + dbus_message_unref(message); + + if (!reply || dbus_error_is_set(&err)) { + ERR_PRINT(vformat("Failed to send DBus message: %s", err.message)); + dbus_error_free(&err); + dbus_bus_remove_match(fd.connection, fd.path.utf8().get_data(), &err); + dbus_connection_unref(fd.connection); + return FAILED; + } + + // Update signal path. + { + DBusMessageIter iter; + if (dbus_message_iter_init(reply, &iter)) { + if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_OBJECT_PATH) { + const char *new_path = nullptr; + dbus_message_iter_get_basic(&iter, &new_path); + if (String::utf8(new_path) != path) { + dbus_bus_remove_match(fd.connection, fd.path.utf8().get_data(), &err); + if (dbus_error_is_set(&err)) { + ERR_PRINT(vformat("Failed to remove DBus match: %s", err.message)); + dbus_error_free(&err); + dbus_connection_unref(fd.connection); + return FAILED; + } + fd.path = String::utf8(new_path); + dbus_bus_add_match(fd.connection, fd.path.utf8().get_data(), &err); + if (dbus_error_is_set(&err)) { + ERR_PRINT(vformat("Failed to add DBus match: %s", err.message)); + dbus_error_free(&err); + dbus_connection_unref(fd.connection); + return FAILED; + } + } + } + } + } + dbus_message_unref(reply); + + MutexLock lock(file_dialog_mutex); + file_dialogs.push_back(fd); + + return OK; +} + +void FreeDesktopPortalDesktop::_thread_file_dialog_monitor(void *p_ud) { + FreeDesktopPortalDesktop *portal = (FreeDesktopPortalDesktop *)p_ud; + + while (!portal->file_dialog_thread_abort.is_set()) { + { + MutexLock lock(portal->file_dialog_mutex); + for (int i = portal->file_dialogs.size() - 1; i >= 0; i--) { + bool remove = false; + { + FreeDesktopPortalDesktop::FileDialogData &fd = portal->file_dialogs.write[i]; + if (fd.connection) { + while (true) { + DBusMessage *msg = dbus_connection_pop_message(fd.connection); + if (!msg) { + break; + } else if (dbus_message_is_signal(msg, "org.freedesktop.portal.Request", "Response")) { + DBusMessageIter iter; + if (dbus_message_iter_init(msg, &iter)) { + bool cancel = false; + Vector<String> uris; + file_chooser_parse_response(&iter, cancel, uris); + + if (fd.callback.is_valid()) { + Variant v_status = !cancel; + Variant v_files = uris; + Variant *v_args[2] = { &v_status, &v_files }; + fd.callback.call_deferredp((const Variant **)&v_args, 2); + } + } + dbus_message_unref(msg); + + DBusError err; + dbus_error_init(&err); + dbus_bus_remove_match(fd.connection, fd.path.utf8().get_data(), &err); + dbus_error_free(&err); + dbus_connection_unref(fd.connection); + remove = true; + break; + } + dbus_message_unref(msg); + } + dbus_connection_read_write(fd.connection, 0); + } + } + if (remove) { + portal->file_dialogs.remove_at(i); + } + } + } + usleep(50000); + } +} + FreeDesktopPortalDesktop::FreeDesktopPortalDesktop() { #ifdef SOWRAP_ENABLED #ifdef DEBUG_ENABLED @@ -153,6 +468,27 @@ FreeDesktopPortalDesktop::FreeDesktopPortalDesktop() { print_verbose("PortalDesktop: Unsupported DBus library version!"); unsupported = true; } + + if (!unsupported) { + file_dialog_thread_abort.clear(); + file_dialog_thread.start(FreeDesktopPortalDesktop::_thread_file_dialog_monitor, this); + } +} + +FreeDesktopPortalDesktop::~FreeDesktopPortalDesktop() { + file_dialog_thread_abort.set(); + if (file_dialog_thread.is_started()) { + file_dialog_thread.wait_to_finish(); + } + for (FreeDesktopPortalDesktop::FileDialogData &fd : file_dialogs) { + if (fd.connection) { + DBusError err; + dbus_error_init(&err); + dbus_bus_remove_match(fd.connection, fd.path.utf8().get_data(), &err); + dbus_error_free(&err); + dbus_connection_unref(fd.connection); + } + } } #endif // DBUS_ENABLED diff --git a/platform/linuxbsd/freedesktop_portal_desktop.h b/platform/linuxbsd/freedesktop_portal_desktop.h index 1520ab3a1f..a9b83b3844 100644 --- a/platform/linuxbsd/freedesktop_portal_desktop.h +++ b/platform/linuxbsd/freedesktop_portal_desktop.h @@ -33,20 +33,48 @@ #ifdef DBUS_ENABLED -#include <stdint.h> +#include "core/os/thread.h" +#include "servers/display_server.h" + +struct DBusMessage; +struct DBusConnection; +struct DBusMessageIter; class FreeDesktopPortalDesktop { private: bool unsupported = false; + static bool try_parse_variant(DBusMessage *p_reply_message, int p_type, void *r_value); // Read a setting from org.freekdesktop.portal.Settings bool read_setting(const char *p_namespace, const char *p_key, int p_type, void *r_value); + static void append_dbus_string(DBusMessageIter *p_iter, const String &p_string); + static void append_dbus_dict_filters(DBusMessageIter *p_iter, const Vector<String> &p_filters); + static void append_dbus_dict_string(DBusMessageIter *p_iter, const String &p_key, const String &p_value, bool p_as_byte_array = false); + static void append_dbus_dict_bool(DBusMessageIter *p_iter, const String &p_key, bool p_value); + static bool file_chooser_parse_response(DBusMessageIter *p_iter, bool &r_cancel, Vector<String> &r_urls); + + struct FileDialogData { + DBusConnection *connection = nullptr; + Callable callback; + String path; + }; + + Mutex file_dialog_mutex; + Vector<FileDialogData> file_dialogs; + Thread file_dialog_thread; + SafeFlag file_dialog_thread_abort; + + static void _thread_file_dialog_monitor(void *p_ud); + public: FreeDesktopPortalDesktop(); + ~FreeDesktopPortalDesktop(); bool is_supported() { return !unsupported; } + Error file_dialog_show(const String &p_xid, const String &p_title, const String &p_current_directory, const String &p_filename, DisplayServer::FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback); + // Retrieve the system's preferred color scheme. // 0: No preference or unknown. // 1: Prefer dark appearance. diff --git a/platform/linuxbsd/joypad_linux.cpp b/platform/linuxbsd/joypad_linux.cpp index 342cff82e9..827c567785 100644 --- a/platform/linuxbsd/joypad_linux.cpp +++ b/platform/linuxbsd/joypad_linux.cpp @@ -56,6 +56,14 @@ static const char *ignore_str = "/dev/input/js"; #endif +// On Linux with Steam Input Xbox 360 devices have an index appended to their device name, this index is +// the Steam Input gamepad index +#define VALVE_GAMEPAD_NAME_PREFIX "Microsoft X-Box 360 pad " +// IDs used by Steam Input virtual controllers. +// See https://partner.steamgames.com/doc/features/steam_controller/steam_input_gamepad_emulation_bestpractices +#define VALVE_GAMEPAD_VID 0x28DE +#define VALVE_GAMEPAD_PID 0x11FF + JoypadLinux::Joypad::~Joypad() { for (int i = 0; i < MAX_ABS; i++) { if (abs_info[i]) { @@ -74,31 +82,9 @@ void JoypadLinux::Joypad::reset() { events.clear(); } -#ifdef UDEV_ENABLED -// This function is derived from SDL: -// https://github.com/libsdl-org/SDL/blob/main/src/core/linux/SDL_sandbox.c#L28-L45 -static bool detect_sandbox() { - if (access("/.flatpak-info", F_OK) == 0) { - return true; - } - - // For Snap, we check multiple variables because they might be set for - // unrelated reasons. This is the same thing WebKitGTK does. - if (OS::get_singleton()->has_environment("SNAP") && OS::get_singleton()->has_environment("SNAP_NAME") && OS::get_singleton()->has_environment("SNAP_REVISION")) { - return true; - } - - if (access("/run/host/container-manager", F_OK) == 0) { - return true; - } - - return false; -} -#endif // UDEV_ENABLED - JoypadLinux::JoypadLinux(Input *in) { #ifdef UDEV_ENABLED - if (detect_sandbox()) { + if (OS::get_singleton()->is_sandboxed()) { // Linux binaries in sandboxes / containers need special handling because // libudev doesn't work there. So we need to fallback to manual parsing // of /dev/input in such case. @@ -411,8 +397,23 @@ void JoypadLinux::open_joypad(const char *p_path) { setup_joypad_properties(joypad); sprintf(uid, "%04x%04x", BSWAP16(inpid.bustype), 0); if (inpid.vendor && inpid.product && inpid.version) { + Dictionary joypad_info; + joypad_info["vendor_id"] = inpid.vendor; + joypad_info["product_id"] = inpid.product; + joypad_info["raw_name"] = name; + sprintf(uid + String(uid).length(), "%04x%04x%04x%04x%04x%04x", vendor, 0, product, 0, version, 0); - input->joy_connection_changed(joy_num, true, name, uid); + + if (inpid.vendor == VALVE_GAMEPAD_VID && inpid.product == VALVE_GAMEPAD_PID) { + if (name.begins_with(VALVE_GAMEPAD_NAME_PREFIX)) { + String idx_str = name.substr(strlen(VALVE_GAMEPAD_NAME_PREFIX)); + if (idx_str.is_valid_int()) { + joypad_info["steam_input_index"] = idx_str.to_int(); + } + } + } + + input->joy_connection_changed(joy_num, true, name, uid, joypad_info); } else { String uidname = uid; int uidlen = MIN(name.length(), 11); diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp index 14d02a73c8..d22d398a67 100644 --- a/platform/linuxbsd/os_linuxbsd.cpp +++ b/platform/linuxbsd/os_linuxbsd.cpp @@ -164,6 +164,27 @@ String OS_LinuxBSD::get_processor_name() const { ERR_FAIL_V_MSG("", String("Couldn't get the CPU model name from `/proc/cpuinfo`. Returning an empty string.")); } +bool OS_LinuxBSD::is_sandboxed() const { + // This function is derived from SDL: + // https://github.com/libsdl-org/SDL/blob/main/src/core/linux/SDL_sandbox.c#L28-L45 + + if (access("/.flatpak-info", F_OK) == 0) { + return true; + } + + // For Snap, we check multiple variables because they might be set for + // unrelated reasons. This is the same thing WebKitGTK does. + if (has_environment("SNAP") && has_environment("SNAP_NAME") && has_environment("SNAP_REVISION")) { + return true; + } + + if (access("/run/host/container-manager", F_OK) == 0) { + return true; + } + + return false; +} + void OS_LinuxBSD::finalize() { if (main_loop) { memdelete(main_loop); diff --git a/platform/linuxbsd/os_linuxbsd.h b/platform/linuxbsd/os_linuxbsd.h index 007b90b82b..6917ea5ae7 100644 --- a/platform/linuxbsd/os_linuxbsd.h +++ b/platform/linuxbsd/os_linuxbsd.h @@ -123,6 +123,8 @@ public: virtual String get_unique_id() const override; virtual String get_processor_name() const override; + virtual bool is_sandboxed() const override; + virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override; virtual bool _check_internal_feature_support(const String &p_feature) override; diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index 2643cd3b1a..f38a9dd278 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -122,6 +122,9 @@ bool DisplayServerX11::has_feature(Feature p_feature) const { case FEATURE_WINDOW_TRANSPARENCY: //case FEATURE_HIDPI: case FEATURE_ICON: +#ifdef DBUS_ENABLED + case FEATURE_NATIVE_DIALOG: +#endif //case FEATURE_NATIVE_ICON: case FEATURE_SWAP_BUFFERS: #ifdef DBUS_ENABLED @@ -360,6 +363,17 @@ bool DisplayServerX11::is_dark_mode() const { } } +Error DisplayServerX11::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) { + WindowID window_id = _get_focused_window_or_popup(); + + if (!windows.has(window_id)) { + window_id = MAIN_WINDOW_ID; + } + + String xid = vformat("x11:%x", (uint64_t)windows[window_id].x11_window); + return portal_desktop->file_dialog_show(xid, p_title, p_current_directory, p_filename, p_mode, p_filters, p_callback); +} + #endif void DisplayServerX11::mouse_set_mode(MouseMode p_mode) { @@ -379,7 +393,11 @@ void DisplayServerX11::mouse_set_mode(MouseMode p_mode) { if (show_cursor && !previously_shown) { WindowID window_id = get_window_at_screen_position(mouse_get_position()); - if (window_id != INVALID_WINDOW_ID) { + if (window_id != INVALID_WINDOW_ID && window_mouseover_id != window_id) { + if (window_mouseover_id != INVALID_WINDOW_ID) { + _send_window_event(windows[window_mouseover_id], WINDOW_EVENT_MOUSE_EXIT); + } + window_mouseover_id = window_id; _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER); } } @@ -744,7 +762,7 @@ int DisplayServerX11::get_screen_count() const { // Using Xinerama Extension int event_base, error_base; - if (XineramaQueryExtension(x11_display, &event_base, &error_base)) { + if (xinerama_ext_ok && XineramaQueryExtension(x11_display, &event_base, &error_base)) { XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &count); XFree(xsi); } else { @@ -756,7 +774,7 @@ int DisplayServerX11::get_screen_count() const { int DisplayServerX11::get_primary_screen() const { int event_base, error_base; - if (XineramaQueryExtension(x11_display, &event_base, &error_base)) { + if (xinerama_ext_ok && XineramaQueryExtension(x11_display, &event_base, &error_base)) { return 0; } else { return XDefaultScreen(x11_display); @@ -809,7 +827,7 @@ Rect2i DisplayServerX11::_screen_get_rect(int p_screen) const { // Using Xinerama Extension. int event_base, error_base; - if (XineramaQueryExtension(x11_display, &event_base, &error_base)) { + if (xinerama_ext_ok && XineramaQueryExtension(x11_display, &event_base, &error_base)) { int count; XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &count); @@ -1244,7 +1262,7 @@ Ref<Image> DisplayServerX11::screen_get_image(int p_screen) const { XImage *image = nullptr; int event_base, error_base; - if (XineramaQueryExtension(x11_display, &event_base, &error_base)) { + if (xinerama_ext_ok && XineramaQueryExtension(x11_display, &event_base, &error_base)) { int xin_count; XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &xin_count); if (p_screen < xin_count) { @@ -1449,6 +1467,11 @@ void DisplayServerX11::delete_sub_window(WindowID p_id) { DEBUG_LOG_X11("delete_sub_window: %lu (%u) \n", wd.x11_window, p_id); + if (window_mouseover_id == p_id) { + window_mouseover_id = INVALID_WINDOW_ID; + _send_window_event(windows[p_id], WINDOW_EVENT_MOUSE_EXIT); + } + window_set_rect_changed_callback(Callable(), p_id); window_set_window_event_callback(Callable(), p_id); window_set_input_event_callback(Callable(), p_id); @@ -1586,6 +1609,7 @@ void DisplayServerX11::window_set_mouse_passthrough(const Vector<Vector2> &p_reg void DisplayServerX11::_update_window_mouse_passthrough(WindowID p_window) { ERR_FAIL_COND(!windows.has(p_window)); + ERR_FAIL_COND(!xshaped_ext_ok); const Vector<Vector2> region_path = windows[p_window].mpath; @@ -2102,9 +2126,10 @@ bool DisplayServerX11::_window_maximize_check(WindowID p_window, const char *p_a bool DisplayServerX11::_window_minimize_check(WindowID p_window) const { const WindowData &wd = windows[p_window]; - // Using ICCCM -- Inter-Client Communication Conventions Manual - Atom property = XInternAtom(x11_display, "WM_STATE", True); - if (property == None) { + // Using EWMH instead of ICCCM, might work better for Wayland users. + Atom property = XInternAtom(x11_display, "_NET_WM_STATE", True); + Atom hidden = XInternAtom(x11_display, "_NET_WM_STATE_HIDDEN", True); + if (property == None || hidden == None) { return false; } @@ -2112,7 +2137,7 @@ bool DisplayServerX11::_window_minimize_check(WindowID p_window) const { int format; unsigned long len; unsigned long remaining; - unsigned char *data = nullptr; + Atom *atoms = nullptr; int result = XGetWindowProperty( x11_display, @@ -2121,20 +2146,21 @@ bool DisplayServerX11::_window_minimize_check(WindowID p_window) const { 0, 32, False, - AnyPropertyType, + XA_ATOM, &type, &format, &len, &remaining, - &data); + (unsigned char **)&atoms); - if (result == Success && data) { - long *state = (long *)data; - if (state[0] == WM_IconicState) { - XFree(data); - return true; + if (result == Success && atoms) { + for (unsigned int i = 0; i < len; i++) { + if (atoms[i] == hidden) { + XFree(atoms); + return true; + } } - XFree(data); + XFree(atoms); } return false; @@ -3957,7 +3983,7 @@ bool DisplayServerX11::mouse_process_popups() { // Find top popup to close. while (E) { // Popup window area. - Rect2i win_rect = Rect2i(window_get_position(E->get()), window_get_size(E->get())); + Rect2i win_rect = Rect2i(window_get_position_with_decorations(E->get()), window_get_size_with_decorations(E->get())); // Area of the parent window, which responsible for opening sub-menu. Rect2i safe_rect = window_get_popup_safe_rect(E->get()); if (win_rect.has_point(pos)) { @@ -4291,7 +4317,8 @@ void DisplayServerX11::process_events() { break; } - if (!mouse_mode_grab) { + if (!mouse_mode_grab && window_mouseover_id == window_id) { + window_mouseover_id = INVALID_WINDOW_ID; _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT); } @@ -4303,7 +4330,11 @@ void DisplayServerX11::process_events() { break; } - if (!mouse_mode_grab) { + if (!mouse_mode_grab && window_mouseover_id != window_id) { + if (window_mouseover_id != INVALID_WINDOW_ID) { + _send_window_event(windows[window_mouseover_id], WINDOW_EVENT_MOUSE_EXIT); + } + window_mouseover_id = window_id; _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER); } } break; @@ -5461,13 +5492,11 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode } if (initialize_xinerama(dylibloader_verbose) != 0) { - r_error = ERR_UNAVAILABLE; - ERR_FAIL_MSG("Can't load Xinerama dynamically."); + xinerama_ext_ok = false; } if (initialize_xrandr(dylibloader_verbose) != 0) { - r_error = ERR_UNAVAILABLE; - ERR_FAIL_MSG("Can't load Xrandr dynamically."); + xrandr_ext_ok = false; } if (initialize_xrender(dylibloader_verbose) != 0) { @@ -5537,42 +5566,36 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode return; } - { + if (xshaped_ext_ok) { int version_major = 0; int version_minor = 0; int rc = XShapeQueryVersion(x11_display, &version_major, &version_minor); print_verbose(vformat("Xshape %d.%d detected.", version_major, version_minor)); if (rc != 1 || version_major < 1) { - ERR_PRINT("Unsupported Xshape library version."); - r_error = ERR_UNAVAILABLE; - XCloseDisplay(x11_display); - return; + xshaped_ext_ok = false; + print_verbose("Unsupported Xshape library version."); } } - { + if (xinerama_ext_ok) { int version_major = 0; int version_minor = 0; int rc = XineramaQueryVersion(x11_display, &version_major, &version_minor); print_verbose(vformat("Xinerama %d.%d detected.", version_major, version_minor)); if (rc != 1 || version_major < 1) { - ERR_PRINT("Unsupported Xinerama library version."); - r_error = ERR_UNAVAILABLE; - XCloseDisplay(x11_display); - return; + xinerama_ext_ok = false; + print_verbose("Unsupported Xinerama library version."); } } - { + if (xrandr_ext_ok) { int version_major = 0; int version_minor = 0; int rc = XRRQueryVersion(x11_display, &version_major, &version_minor); print_verbose(vformat("Xrandr %d.%d detected.", version_major, version_minor)); if (rc != 1 || (version_major == 1 && version_minor < 3) || (version_major < 1)) { - ERR_PRINT("Unsupported Xrandr library version."); - r_error = ERR_UNAVAILABLE; - XCloseDisplay(x11_display); - return; + xrandr_ext_ok = false; + print_verbose("Unsupported Xrandr library version."); } } @@ -5638,7 +5661,9 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode if (!xrandr_handle) { fprintf(stderr, "could not load libXrandr.so.2, Error: %s\n", err); } - } else { + } + + if (xrandr_handle) { XRRQueryVersion(x11_display, &xrandr_major, &xrandr_minor); if (((xrandr_major << 8) | xrandr_minor) >= 0x0105) { xrr_get_monitors = (xrr_get_monitors_t)dlsym(xrandr_handle, "XRRGetMonitors"); diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h index 70703d42c3..71beddce76 100644 --- a/platform/linuxbsd/x11/display_server_x11.h +++ b/platform/linuxbsd/x11/display_server_x11.h @@ -225,6 +225,7 @@ class DisplayServerX11 : public DisplayServer { List<WindowID> popup_list; + WindowID window_mouseover_id = INVALID_WINDOW_ID; WindowID last_focused_window = INVALID_WINDOW_ID; WindowID window_id_counter = MAIN_WINDOW_ID; @@ -322,7 +323,9 @@ class DisplayServerX11 : public DisplayServer { xrr_get_monitors_t xrr_get_monitors = nullptr; xrr_free_monitors_t xrr_free_monitors = nullptr; void *xrandr_handle = nullptr; - Bool xrandr_ext_ok; + bool xrandr_ext_ok = true; + bool xinerama_ext_ok = true; + bool xshaped_ext_ok = true; struct Property { unsigned char *data; @@ -390,6 +393,8 @@ public: #if defined(DBUS_ENABLED) virtual bool is_dark_mode_supported() const override; virtual bool is_dark_mode() const override; + + virtual Error file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) override; #endif virtual void mouse_set_mode(MouseMode p_mode) override; diff --git a/platform/linuxbsd/x11/gl_manager_x11.h b/platform/linuxbsd/x11/gl_manager_x11.h index 59e20fec45..d3a25506a8 100644 --- a/platform/linuxbsd/x11/gl_manager_x11.h +++ b/platform/linuxbsd/x11/gl_manager_x11.h @@ -74,17 +74,17 @@ private: }; struct GLDisplay { - GLDisplay() { context = nullptr; } + GLDisplay() {} ~GLDisplay(); GLManager_X11_Private *context = nullptr; - ::Display *x11_display; - XVisualInfo x_vi; + ::Display *x11_display = nullptr; + XVisualInfo x_vi = {}; }; // just for convenience, window and display struct struct XWinDisp { ::Window x11_window; - ::Display *x11_display; + ::Display *x11_display = nullptr; } _x_windisp; LocalVector<GLWindow> _windows; diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h index e5e0e53bfb..69f6008043 100644 --- a/platform/macos/display_server_macos.h +++ b/platform/macos/display_server_macos.h @@ -171,6 +171,7 @@ private: int current_layout = 0; bool keyboard_layout_dirty = true; + WindowID window_mouseover_id = INVALID_WINDOW_ID; WindowID last_focused_window = INVALID_WINDOW_ID; WindowID window_id_counter = MAIN_WINDOW_ID; float display_max_scale = 1.f; @@ -240,6 +241,8 @@ public: bool get_is_resizing() const; void reparent_check(WindowID p_window); WindowID _get_focused_window_or_popup() const; + void mouse_enter_window(WindowID p_window); + void mouse_exit_window(WindowID p_window); void window_update(WindowID p_window); void window_destroy(WindowID p_window); diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index d64bb5211e..e79d6acc3f 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -366,6 +366,25 @@ DisplayServer::WindowID DisplayServerMacOS::_get_focused_window_or_popup() const return last_focused_window; } +void DisplayServerMacOS::mouse_enter_window(WindowID p_window) { + if (window_mouseover_id != p_window) { + if (window_mouseover_id != INVALID_WINDOW_ID) { + send_window_event(windows[window_mouseover_id], WINDOW_EVENT_MOUSE_EXIT); + } + window_mouseover_id = p_window; + if (p_window != INVALID_WINDOW_ID) { + send_window_event(windows[p_window], WINDOW_EVENT_MOUSE_ENTER); + } + } +} + +void DisplayServerMacOS::mouse_exit_window(WindowID p_window) { + if (window_mouseover_id == p_window && p_window != INVALID_WINDOW_ID) { + send_window_event(windows[p_window], WINDOW_EVENT_MOUSE_EXIT); + } + window_mouseover_id = INVALID_WINDOW_ID; +} + void DisplayServerMacOS::_dispatch_input_events(const Ref<InputEvent> &p_event) { ((DisplayServerMacOS *)(get_singleton()))->_dispatch_input_event(p_event); } @@ -2069,9 +2088,7 @@ void DisplayServerMacOS::mouse_set_mode(MouseMode p_mode) { if (show_cursor && !previously_shown) { window_id = get_window_at_screen_position(mouse_get_position()); - if (window_id != INVALID_WINDOW_ID) { - send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER); - } + mouse_enter_window(window_id); } if (p_mode == MOUSE_MODE_CAPTURED) { @@ -4026,7 +4043,7 @@ bool DisplayServerMacOS::mouse_process_popups(bool p_close) { // Find top popup to close. while (E) { // Popup window area. - Rect2i win_rect = Rect2i(window_get_position(E->get()), window_get_size(E->get())); + Rect2i win_rect = Rect2i(window_get_position_with_decorations(E->get()), window_get_size_with_decorations(E->get())); // Area of the parent window, which responsible for opening sub-menu. Rect2i safe_rect = window_get_popup_safe_rect(E->get()); if (win_rect.has_point(pos)) { diff --git a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml index 6af816989d..c24ff4cb73 100644 --- a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml +++ b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml @@ -23,7 +23,7 @@ Copyright notice for the bundle visible to the user (localized). </member> <member name="application/icon" type="String" setter="" getter=""> - Application icon file. If left empty, it will fallback to [member ProjectSettings.application/config/macos_native_icon], and then to [member ProjectSettings.application/config/icon]. + Application icon file. If left empty, it will fallback to [member ProjectSettings.application/config/macos_native_icon], and then to [member ProjectSettings.application/config/icon]. </member> <member name="application/icon_interpolation" type="int" setter="" getter=""> Interpolation method used to resize application icon. @@ -32,13 +32,13 @@ Minimum version of macOS required for this application to run in the [code]major.minor.patch[/code] or [code]major.minor[/code] format, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]). </member> <member name="application/short_version" type="String" setter="" getter=""> - Application version visible to the user, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]). + Application version visible to the user, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]). Falls back to [member ProjectSettings.application/config/version] if left empty. </member> <member name="application/signature" type="String" setter="" getter=""> A four-character creator code that is specific to the bundle. Optional. </member> <member name="application/version" type="String" setter="" getter=""> - Machine-readable application version, in the [code]major.minor.patch[/code] format, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]). + Machine-readable application version, in the [code]major.minor.patch[/code] format, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]). This must be incremented on every new release pushed to the App Store. </member> <member name="binary_format/architecture" type="String" setter="" getter=""> Application executable architecture. @@ -63,7 +63,7 @@ Array of the additional command line arguments passed to the code signing tool. </member> <member name="codesign/entitlements/address_book" type="bool" setter="" getter=""> - Enable to allow access to contacts in the user's address book, if it's enabled you should also provide usage message in the [code]privacy/address_book_usage_description[/code] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_addressbook]com.apple.security.personal-information.addressbook[/url]. + Enable to allow access to contacts in the user's address book, if it's enabled you should also provide usage message in the [code]privacy/address_book_usage_description[/code] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_addressbook]com.apple.security.personal-information.addressbook[/url]. </member> <member name="codesign/entitlements/allow_dyld_environment_variables" type="bool" setter="" getter=""> Allows app to use dynamic linker environment variables to inject code. If you are using add-ons with dynamic or self-modifying native code, enable them according to the add-on documentation. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_cs_allow-dyld-environment-variables]com.apple.security.cs.allow-dyld-environment-variables[/url]. diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp index 81f9707f6b..6586fe7f82 100644 --- a/platform/macos/export/export_plugin.cpp +++ b/platform/macos/export/export_plugin.cpp @@ -41,6 +41,7 @@ #include "editor/editor_node.h" #include "editor/editor_paths.h" #include "editor/editor_scale.h" +#include "editor/import/resource_importer_texture_settings.h" #include "scene/resources/image_texture.h" #include "modules/modules_enabled.gen.h" // For svg and regex. @@ -342,7 +343,9 @@ List<String> EditorExportPlatformMacOS::get_binary_extensions(const Ref<EditorEx list.push_back("dmg"); #endif list.push_back("zip"); +#ifndef WINDOWS_ENABLED list.push_back("app"); +#endif } else if (dist_type == 1) { #ifdef MACOS_ENABLED list.push_back("dmg"); @@ -376,7 +379,7 @@ void EditorExportPlatformMacOS::get_export_options(List<ExportOption> *r_options r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/app_category", PROPERTY_HINT_ENUM, "Business,Developer-tools,Education,Entertainment,Finance,Games,Action-games,Adventure-games,Arcade-games,Board-games,Card-games,Casino-games,Dice-games,Educational-games,Family-games,Kids-games,Music-games,Puzzle-games,Racing-games,Role-playing-games,Simulation-games,Sports-games,Strategy-games,Trivia-games,Word-games,Graphics-design,Healthcare-fitness,Lifestyle,Medical,Music,News,Photography,Productivity,Reference,Social-networking,Sports,Travel,Utilities,Video,Weather"), "Games")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version", PROPERTY_HINT_PLACEHOLDER_TEXT, "Leave empty to use project version"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "application/copyright_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary())); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/min_macos_version"), "10.12")); @@ -670,7 +673,7 @@ void EditorExportPlatformMacOS::_fix_plist(const Ref<EditorExportPreset> &p_pres } else if (lines[i].find("$short_version") != -1) { strnew += lines[i].replace("$short_version", p_preset->get("application/short_version")) + "\n"; } else if (lines[i].find("$version") != -1) { - strnew += lines[i].replace("$version", p_preset->get("application/version")) + "\n"; + strnew += lines[i].replace("$version", p_preset->get_version("application/version")) + "\n"; } else if (lines[i].find("$signature") != -1) { strnew += lines[i].replace("$signature", p_preset->get("application/signature")) + "\n"; } else if (lines[i].find("$app_category") != -1) { @@ -2122,16 +2125,12 @@ bool EditorExportPlatformMacOS::has_valid_export_configuration(const Ref<EditorE // Check the texture formats, which vary depending on the target architecture. String architecture = p_preset->get("binary_format/architecture"); if (architecture == "universal" || architecture == "x86_64") { - const String bc_error = test_bc(); - if (!bc_error.is_empty()) { + if (!ResourceImporterTextureSettings::should_import_s3tc_bptc()) { valid = false; - err += bc_error; } } else if (architecture == "arm64") { - const String etc_error = test_etc2(); - if (!etc_error.is_empty()) { + if (!ResourceImporterTextureSettings::should_import_etc2_astc()) { valid = false; - err += etc_error; } } else { ERR_PRINT("Invalid architecture"); diff --git a/platform/macos/godot_content_view.mm b/platform/macos/godot_content_view.mm index 3467bf90a1..231be83a03 100644 --- a/platform/macos/godot_content_view.mm +++ b/platform/macos/godot_content_view.mm @@ -505,9 +505,8 @@ return; } - DisplayServerMacOS::WindowData &wd = ds->get_window(window_id); if (ds->mouse_get_mode() != DisplayServer::MOUSE_MODE_CAPTURED) { - ds->send_window_event(wd, DisplayServerMacOS::WINDOW_EVENT_MOUSE_EXIT); + ds->mouse_exit_window(window_id); } } @@ -517,9 +516,8 @@ return; } - DisplayServerMacOS::WindowData &wd = ds->get_window(window_id); if (ds->mouse_get_mode() != DisplayServer::MOUSE_MODE_CAPTURED) { - ds->send_window_event(wd, DisplayServerMacOS::WINDOW_EVENT_MOUSE_ENTER); + ds->mouse_enter_window(window_id); } ds->cursor_update_shape(); diff --git a/platform/macos/godot_window_delegate.mm b/platform/macos/godot_window_delegate.mm index 1c6dbb1981..46355b4ae8 100644 --- a/platform/macos/godot_window_delegate.mm +++ b/platform/macos/godot_window_delegate.mm @@ -67,6 +67,7 @@ ds->window_set_transient(window_id, DisplayServerMacOS::INVALID_WINDOW_ID); } + ds->mouse_exit_window(window_id); ds->window_destroy(window_id); } diff --git a/platform/web/SCsub b/platform/web/SCsub index e44e59bfb9..1af0642554 100644 --- a/platform/web/SCsub +++ b/platform/web/SCsub @@ -70,6 +70,9 @@ if env["dlink_enabled"]: sys_env.Append(LINKFLAGS=["-s", "WARN_ON_UNDEFINED_SYMBOLS=0"]) # Force exporting the standard library (printf, malloc, etc.) sys_env["ENV"]["EMCC_FORCE_STDLIBS"] = "libc,libc++,libc++abi" + sys_env["CCFLAGS"].remove("-fvisibility=hidden") + sys_env["LINKFLAGS"].remove("-fvisibility=hidden") + # The main emscripten runtime, with exported standard libraries. sys = sys_env.Program(build_targets, ["web_runtime.cpp"]) diff --git a/platform/web/detect.py b/platform/web/detect.py index 2685cbcd63..4015c8ff16 100644 --- a/platform/web/detect.py +++ b/platform/web/detect.py @@ -97,12 +97,9 @@ def configure(env: "Environment"): if env["use_assertions"]: env.Append(LINKFLAGS=["-s", "ASSERTIONS=1"]) - if env.editor_build: - if env["initial_memory"] < 64: - print('Note: Forcing "initial_memory=64" as it is required for the web editor.') - env["initial_memory"] = 64 - else: - env.Append(CPPFLAGS=["-fno-exceptions"]) + if env.editor_build and env["initial_memory"] < 64: + print('Note: Forcing "initial_memory=64" as it is required for the web editor.') + env["initial_memory"] = 64 env.Append(LINKFLAGS=["-s", "INITIAL_MEMORY=%sMB" % env["initial_memory"]]) @@ -211,6 +208,8 @@ def configure(env: "Environment"): env.Append(CCFLAGS=["-s", "SIDE_MODULE=2"]) env.Append(LINKFLAGS=["-s", "SIDE_MODULE=2"]) + env.Append(CCFLAGS=["-fvisibility=hidden"]) + env.Append(LINKFLAGS=["-fvisibility=hidden"]) env.extra_suffix = ".dlink" + env.extra_suffix # Reduce code size by generating less support code (e.g. skip NodeJS support). diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp index 38e2714d9f..993abd2cee 100644 --- a/platform/web/export/export_plugin.cpp +++ b/platform/web/export/export_plugin.cpp @@ -37,6 +37,7 @@ #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "editor/export/editor_export.h" +#include "editor/import/resource_importer_texture_settings.h" #include "scene/resources/image_texture.h" #include "modules/modules_enabled.gen.h" // For mono and svg. @@ -406,10 +407,8 @@ bool EditorExportPlatformWeb::has_valid_project_configuration(const Ref<EditorEx // Validate the project configuration. if (p_preset->get("vram_texture_compression/for_mobile")) { - String etc_error = test_etc2(); - if (!etc_error.is_empty()) { + if (!ResourceImporterTextureSettings::should_import_etc2_astc()) { valid = false; - err += etc_error; } } diff --git a/platform/web/js/libs/library_godot_audio.js b/platform/web/js/libs/library_godot_audio.js index 1993d66310..cc86c81096 100644 --- a/platform/web/js/libs/library_godot_audio.js +++ b/platform/web/js/libs/library_godot_audio.js @@ -88,7 +88,7 @@ const GodotAudio = { GodotAudio.input = GodotAudio.ctx.createMediaStreamSource(stream); callback(GodotAudio.input); } catch (e) { - GodotRuntime.error('Failed creaating input.', e); + GodotRuntime.error('Failed creating input.', e); } } if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { diff --git a/platform/web/js/libs/library_godot_display.js b/platform/web/js/libs/library_godot_display.js index 746f858923..c60e6899f2 100644 --- a/platform/web/js/libs/library_godot_display.js +++ b/platform/web/js/libs/library_godot_display.js @@ -289,11 +289,11 @@ const GodotDisplayScreen = { const isFullscreen = GodotDisplayScreen.isFullscreen(); const wantsFullWindow = GodotConfig.canvas_resize_policy === 2; const noResize = GodotConfig.canvas_resize_policy === 0; - const wwidth = GodotDisplayScreen.desired_size[0]; - const wheight = GodotDisplayScreen.desired_size[1]; + const dWidth = GodotDisplayScreen.desired_size[0]; + const dHeight = GodotDisplayScreen.desired_size[1]; const canvas = GodotConfig.canvas; - let width = wwidth; - let height = wheight; + let width = dWidth; + let height = dHeight; if (noResize) { // Don't resize canvas, just update GL if needed. if (canvas.width !== width || canvas.height !== height) { diff --git a/platform/web/js/libs/library_godot_webgl2.js b/platform/web/js/libs/library_godot_webgl2.js index 8afbdf9e78..3c6de4a071 100644 --- a/platform/web/js/libs/library_godot_webgl2.js +++ b/platform/web/js/libs/library_godot_webgl2.js @@ -40,7 +40,7 @@ const GodotWebGL2 = { if (typeof context.multiviewExt === 'undefined') { const /** OVR_multiview2 */ ext = context.GLctx.getExtension('OVR_multiview2'); if (!ext) { - console.error('Trying to call glFramebufferTextureMultiviewOVR() without the OVR_multiview2 extension'); + GodotRuntime.error('Trying to call glFramebufferTextureMultiviewOVR() without the OVR_multiview2 extension'); return; } context.multiviewExt = ext; diff --git a/platform/web/package-lock.json b/platform/web/package-lock.json index 4399e8243c..a1615fe591 100644 --- a/platform/web/package-lock.json +++ b/platform/web/package-lock.json @@ -1,157 +1,205 @@ { "name": "godot", "version": "1.0.0", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "godot", "version": "1.0.0", "license": "MIT", - "dependencies": { - "eslint-plugin-html": "^7.1.0" - }, "devDependencies": { - "@html-eslint/eslint-plugin": "^0.15.0", - "@html-eslint/parser": "^0.15.0", - "eslint": "^7.32.0", - "eslint-config-airbnb-base": "^14.2.1", - "eslint-plugin-import": "^2.23.4", - "jsdoc": "^3.6.7" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.10.4" + "@html-eslint/eslint-plugin": "^0.19.1", + "@html-eslint/parser": "^0.19.1", + "eslint": "^8.46.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-plugin-html": "^7.1.0", + "eslint-plugin-import": "^2.28.0", + "jsdoc": "^4.0.2" } }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", - "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", "dev": true, "engines": { - "node": ">=6.9.0" + "node": ">=0.10.0" } }, - "node_modules/@babel/highlight": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", - "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "node_modules/@babel/parser": { + "version": "7.22.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz", + "integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==", "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.14.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "bin": { + "parser": "bin/babel-parser.js" }, "engines": { - "node": ">=6.9.0" + "node": ">=6.0.0" } }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "dev": true, "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "eslint-visitor-keys": "^3.3.0" }, "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true, - "engines": { - "node": ">=0.8.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@babel/parser": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.5.tgz", - "integrity": "sha512-TM8C+xtH/9n1qzX+JNHi7AN2zHMTiPUtspO0ZdHflW8KaskkALhMmuMHb4bCmNdv9VAPzJX3/bXqkVLnAvsPfg==", + "node_modules/@eslint-community/regexpp": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", + "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, "engines": { - "node": ">=6.0.0" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", - "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.1.tgz", + "integrity": "sha512-9t7ZA7NGGK8ckelF0PQCfcxIUzs1Md5rrO6U/c+FIQNanea5UZC0wqKXH4vHBccmu4ZJgZ2idtPeW7+Q2npOEA==", "dev": true, "dependencies": { "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.46.0.tgz", + "integrity": "sha512-a8TLtmPi8xzPkCbp/OGFUo5yhRkHM2Ko9kOWP4znJr0WAhWyThaw3PnwX4vOTWOAMsV2uRt32PPDcEz63esSaA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@html-eslint/eslint-plugin": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@html-eslint/eslint-plugin/-/eslint-plugin-0.15.0.tgz", - "integrity": "sha512-6DUb2ZN1PUlzlNzNj4aBhoObBp3Kl/+YbZ6CnkgFAsQSW0tSFAu7p8WwESkz9RZLZZN9gCUlcaYKJnQjTkmnDA==", + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@html-eslint/eslint-plugin/-/eslint-plugin-0.19.1.tgz", + "integrity": "sha512-Tn+/GWLtNM6NiZFLbfM+vTK0d7gKaDgnw4Pp+DsZi09lFimi4bOPOgy8dSVnLeyFIfP6LkeuwVm4pfBZZM2qbA==", "dev": true, "engines": { "node": ">=8.10.0" } }, "node_modules/@html-eslint/parser": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@html-eslint/parser/-/parser-0.15.0.tgz", - "integrity": "sha512-fA+HQtWnODhOIK6j1p4XWqltINx7hM0WNNTM2RvlH/2glzeRDCcYq3vEmeQhnytvGocidu4ofTzNk80cLnnyiw==", + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@html-eslint/parser/-/parser-0.19.1.tgz", + "integrity": "sha512-dpAw6UX0ZSVNnsAzl9ULHZX7CvAGKF5uta4iebbhSDvGE1o9NX6BoOofD/6WucTvs/qnoKojc3Y2LG6vy4afiQ==", "dev": true, "dependencies": { - "es-html-parser": "^0.0.8" + "es-html-parser": "^0.0.9" }, "engines": { "node": ">=8.10.0" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", - "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.0", + "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", - "minimatch": "^3.0.4" + "minimatch": "^3.0.5" }, "engines": { "node": ">=10.10.0" } }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@humanwhocodes/object-schema": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "node_modules/@jsdoc/salty": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.5.tgz", + "integrity": "sha512-TfRP53RqunNe2HBobVBJ0VLhK1HbfvBYeTC1ahnN64PWvyYyGebmMiPkuwvD9fpw2ZbkoPb8Q7mwy0aR8Z9rvw==", + "dev": true, + "dependencies": { + "lodash": "^4.17.21" + }, + "engines": { + "node": ">=v12.0.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, "node_modules/@types/linkify-it": { @@ -177,9 +225,9 @@ "dev": true }, "node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -213,15 +261,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -232,37 +271,69 @@ } }, "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", "dev": true, "dependencies": { - "sprintf-js": "~1.0.2" + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/array-includes": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", - "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", + "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", - "get-intrinsic": "^1.1.1", - "is-string": "^1.0.5" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.2.tgz", + "integrity": "sha512-tb5thFFlUcp7NdNF6/MpDk/1r/4awWG1FIz3YqDf+/zJSTezBb+/5WViH41obXULHVpDzoiCLpJ/ZO9YbJMsdw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.1.3" }, "engines": { "node": ">= 0.4" @@ -272,14 +343,15 @@ } }, "node_modules/array.prototype.flat": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", - "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", + "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1" + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -288,13 +360,54 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "node_modules/array.prototype.flatmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", + "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz", + "integrity": "sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/balanced-match": { @@ -354,9 +467,9 @@ } }, "node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { "ansi-styles": "^4.1.0", @@ -369,22 +482,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chalk/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/chalk/node_modules/color-convert": { + "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", @@ -396,58 +494,22 @@ "node": ">=7.0.0" } }, - "node_modules/chalk/node_modules/color-name": { + "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/chalk/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "node_modules/confusing-browser-globals": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz", - "integrity": "sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", "dev": true }, "node_modules/cross-spawn": { @@ -482,21 +544,25 @@ } }, "node_modules/deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, "node_modules/define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", "dev": true, "dependencies": { - "object-keys": "^1.0.12" + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/doctrine": { @@ -515,6 +581,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", @@ -524,21 +591,11 @@ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, - "node_modules/dom-serializer/node_modules/entities": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", - "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/domelementtype": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, "funding": [ { "type": "github", @@ -550,6 +607,7 @@ "version": "5.0.3", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, "dependencies": { "domelementtype": "^2.3.0" }, @@ -561,76 +619,76 @@ } }, "node_modules/domutils": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", - "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dev": true, "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", - "domhandler": "^5.0.1" + "domhandler": "^5.0.3" }, "funding": { "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "dependencies": { - "ansi-colors": "^4.1.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "dev": true, + "engines": { + "node": ">=0.12" + }, "funding": { "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, "node_modules/es-abstract": { - "version": "1.18.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.3.tgz", - "integrity": "sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw==", + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz", + "integrity": "sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==", "dev": true, "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.1", + "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.2.1", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", "has": "^1.0.3", - "has-symbols": "^1.0.2", - "is-callable": "^1.2.3", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.3", - "is-string": "^1.0.6", - "object-inspect": "^1.10.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.10", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.3", "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.0", + "safe-array-concat": "^1.0.0", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.7", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.10" }, "engines": { "node": ">= 0.4" @@ -640,11 +698,34 @@ } }, "node_modules/es-html-parser": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/es-html-parser/-/es-html-parser-0.0.8.tgz", - "integrity": "sha512-kjMH23xhvTBw/7Ve1Dtb/7yZdFajfvwOpdsgRHmnyt8yvTsDJnkFjUgEEaMZFW+e1OhN/eoZrvF9wehq+waTGg==", + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/es-html-parser/-/es-html-parser-0.0.9.tgz", + "integrity": "sha512-oniQMi+466VFsDzcdron9Ry/sqUJpDJg1bbDn0jFJKDdxXhwIOYDr4DgBnO5/yPLGj2xv+n5yy4L1Q0vAC5TYQ==", "dev": true }, + "node_modules/es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + } + }, "node_modules/es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", @@ -675,116 +756,113 @@ } }, "node_modules/eslint": { - "version": "7.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", - "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.3", - "@humanwhocodes/config-array": "^0.5.0", - "ajv": "^6.10.0", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.46.0.tgz", + "integrity": "sha512-cIO74PvbW0qU8e0mIvk5IV3ToWdCq5FYG6gWPHHkx6gNdjlbAYvtfHmlCMXxjcoVaIdwy/IAt3+mDkZkfvb2Dg==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.1", + "@eslint/js": "^8.46.0", + "@humanwhocodes/config-array": "^0.11.10", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", - "debug": "^4.0.1", + "debug": "^4.3.2", "doctrine": "^3.0.0", - "enquirer": "^2.3.5", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.2", + "espree": "^9.6.1", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.9", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-config-airbnb-base": { - "version": "14.2.1", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz", - "integrity": "sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA==", + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", + "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", "dev": true, "dependencies": { "confusing-browser-globals": "^1.0.10", "object.assign": "^4.1.2", - "object.entries": "^1.1.2" + "object.entries": "^1.1.5", + "semver": "^6.3.0" }, "engines": { - "node": ">= 6" + "node": "^10.12.0 || >=12.0.0" }, "peerDependencies": { - "eslint": "^5.16.0 || ^6.8.0 || ^7.2.0", - "eslint-plugin-import": "^2.22.1" + "eslint": "^7.32.0 || ^8.2.0", + "eslint-plugin-import": "^2.25.2" } }, "node_modules/eslint-import-resolver-node": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", - "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", + "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", "dev": true, "dependencies": { - "debug": "^2.6.9", - "resolve": "^1.13.1" + "debug": "^3.2.7", + "is-core-module": "^2.11.0", + "resolve": "^1.22.1" } }, "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "dependencies": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, - "node_modules/eslint-import-resolver-node/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, "node_modules/eslint-module-utils": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz", - "integrity": "sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", + "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", "dev": true, "dependencies": { - "debug": "^3.2.7", - "pkg-dir": "^2.0.0" + "debug": "^3.2.7" }, "engines": { "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, "node_modules/eslint-module-utils/node_modules/debug": { @@ -800,46 +878,50 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/eslint-plugin-html/-/eslint-plugin-html-7.1.0.tgz", "integrity": "sha512-fNLRraV/e6j8e3XYOC9xgND4j+U7b1Rq+OygMlLcMg+wI/IpVbF+ubQa3R78EjKB9njT6TQOlcK5rFKBVVtdfg==", + "dev": true, "dependencies": { "htmlparser2": "^8.0.1" } }, "node_modules/eslint-plugin-import": { - "version": "2.23.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.23.4.tgz", - "integrity": "sha512-6/wP8zZRsnQFiR3iaPFgh5ImVRM1WN5NUWfTIRqwOdeiGJlBcSk82o1FEVq8yXmy4lkIzTo7YhHCIxlU/2HyEQ==", + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.28.0.tgz", + "integrity": "sha512-B8s/n+ZluN7sxj9eUf7/pRFERX0r5bnFA2dCaLHy2ZeaQEAz0k+ZZkFWRFHJAqxfxQDx6KLv9LeIki7cFdwW+Q==", "dev": true, "dependencies": { - "array-includes": "^3.1.3", - "array.prototype.flat": "^1.2.4", - "debug": "^2.6.9", + "array-includes": "^3.1.6", + "array.prototype.findlastindex": "^1.2.2", + "array.prototype.flat": "^1.3.1", + "array.prototype.flatmap": "^1.3.1", + "debug": "^3.2.7", "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.4", - "eslint-module-utils": "^2.6.1", - "find-up": "^2.0.0", + "eslint-import-resolver-node": "^0.3.7", + "eslint-module-utils": "^2.8.0", "has": "^1.0.3", - "is-core-module": "^2.4.0", - "minimatch": "^3.0.4", - "object.values": "^1.1.3", - "pkg-up": "^2.0.0", - "read-pkg-up": "^3.0.0", - "resolve": "^1.20.0", - "tsconfig-paths": "^3.9.0" + "is-core-module": "^2.12.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.6", + "object.groupby": "^1.0.0", + "object.values": "^1.1.6", + "resolve": "^1.22.3", + "semver": "^6.3.1", + "tsconfig-paths": "^3.14.2" }, "engines": { "node": ">=4" }, "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0" + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" } }, "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "dependencies": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "node_modules/eslint-plugin-import/node_modules/doctrine": { @@ -854,98 +936,55 @@ "node": ">=0.10.0" } }, - "node_modules/eslint-plugin-import/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^1.1.0" + "estraverse": "^5.2.0" }, "engines": { - "node": ">=6" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.2.tgz", + "integrity": "sha512-8drBzUEyZ2llkpCA67iYrgEssKDUu68V8ChqqOfFupIaG/LCVPUT+CoGJpT77zJprs4T/W7p07LP7zAIMuweVw==", "dev": true, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "engines": { - "node": ">=4" + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -954,15 +993,6 @@ "node": ">=0.10" } }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", @@ -975,19 +1005,10 @@ "node": ">=4.0" } }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { "node": ">=4.0" @@ -1017,9 +1038,18 @@ "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -1033,15 +1063,19 @@ } }, "node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "dependencies": { - "locate-path": "^2.0.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/flat-cache": { @@ -1058,15 +1092,24 @@ } }, "node_modules/flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, "node_modules/function-bind": { @@ -1075,36 +1118,74 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", "dev": true, "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.1" + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, @@ -1116,21 +1197,21 @@ } }, "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "dependencies": { - "is-glob": "^4.0.1" + "is-glob": "^4.0.3" }, "engines": { - "node": ">= 6" + "node": ">=10.13.0" } }, "node_modules/globals": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -1142,10 +1223,43 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, "node_modules/has": { @@ -1161,27 +1275,51 @@ } }, "node_modules/has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { - "node": ">=4" + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true, "engines": { "node": ">= 0.4" @@ -1190,16 +1328,26 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/htmlparser2": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz", - "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "dev": true, "funding": [ "https://github.com/fb55/htmlparser2?sponsor=1", { @@ -1209,26 +1357,15 @@ ], "dependencies": { "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", + "domhandler": "^5.0.3", "domutils": "^3.0.1", - "entities": "^4.3.0" - } - }, - "node_modules/htmlparser2/node_modules/entities": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", - "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "entities": "^4.4.0" } }, "node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true, "engines": { "node": ">= 4" @@ -1253,7 +1390,7 @@ "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "engines": { "node": ">=0.8.19" @@ -1262,7 +1399,7 @@ "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "dependencies": { "once": "^1.3.0", @@ -1275,28 +1412,54 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true + "node_modules/internal-slot": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/is-bigint": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz", - "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-boolean-object": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz", - "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "dev": true, "dependencies": { - "call-bind": "^1.0.2" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -1306,9 +1469,9 @@ } }, "node_modules/is-callable": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", - "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, "engines": { "node": ">= 0.4" @@ -1318,9 +1481,9 @@ } }, "node_modules/is-core-module": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", - "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -1330,10 +1493,13 @@ } }, "node_modules/is-date-object": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz", - "integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -1344,25 +1510,16 @@ "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "dependencies": { "is-extglob": "^2.1.1" @@ -1372,9 +1529,9 @@ } }, "node_modules/is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", "dev": true, "engines": { "node": ">= 0.4" @@ -1384,10 +1541,13 @@ } }, "node_modules/is-number-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz", - "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -1395,14 +1555,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-regex": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz", - "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "has-symbols": "^1.0.2" + "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -1411,11 +1580,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-string": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz", - "integrity": "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -1438,26 +1622,52 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-typed-array": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.11" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" @@ -1473,32 +1683,32 @@ } }, "node_modules/jsdoc": { - "version": "3.6.10", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.10.tgz", - "integrity": "sha512-IdQ8ppSo5LKZ9o3M+LKIIK8i00DIe5msDvG3G81Km+1dhy0XrOWD0Ji8H61ElgyEj/O9KRLokgKbAM9XX9CJAg==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.2.tgz", + "integrity": "sha512-e8cIg2z62InH7azBBi3EsSEqrKx+nUtAS5bBcYTSpZFA+vhNPyhv8PTFZ0WsjOPDj04/dOLlm08EDcQJDqaGQg==", "dev": true, "dependencies": { - "@babel/parser": "^7.9.4", + "@babel/parser": "^7.20.15", + "@jsdoc/salty": "^0.2.1", "@types/markdown-it": "^12.2.3", "bluebird": "^3.7.2", "catharsis": "^0.9.0", "escape-string-regexp": "^2.0.0", "js2xmlparser": "^4.0.2", - "klaw": "^4.0.1", + "klaw": "^3.0.0", "markdown-it": "^12.3.2", "markdown-it-anchor": "^8.4.1", "marked": "^4.0.10", "mkdirp": "^1.0.4", "requizzle": "^0.2.3", "strip-json-comments": "^3.1.0", - "taffydb": "2.6.2", "underscore": "~1.13.2" }, "bin": { "jsdoc": "jsdoc.js" }, "engines": { - "node": ">=8.15.0" + "node": ">=12.0.0" } }, "node_modules/jsdoc/node_modules/escape-string-regexp": { @@ -1510,12 +1720,6 @@ "node": ">=8" } }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -1525,7 +1729,7 @@ "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, "node_modules/json5": { @@ -1541,12 +1745,12 @@ } }, "node_modules/klaw": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-4.0.1.tgz", - "integrity": "sha512-pgsE40/SvC7st04AHiISNewaIMUbY5V/K8b21ekiPiFoYs/EYSdsGa+FJArB1d441uq4Q8zZyIxvAzkGNlBdRw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", "dev": true, - "engines": { - "node": ">=14.14.0" + "dependencies": { + "graceful-fs": "^4.1.9" } }, "node_modules/levn": { @@ -1571,32 +1775,19 @@ "uc.micro": "^1.0.1" } }, - "node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "p-locate": "^5.0.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lodash": { @@ -1605,36 +1796,12 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, - "node_modules/lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "node_modules/lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", - "dev": true - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/markdown-it": { "version": "12.3.2", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", @@ -1652,25 +1819,28 @@ } }, "node_modules/markdown-it-anchor": { - "version": "8.6.4", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.4.tgz", - "integrity": "sha512-Ul4YVYZNxMJYALpKtu+ZRdrryYt/GlQ5CK+4l1bp/gWXOG2QWElt6AqF3Mih/wfUKdZbNAZVXGR73/n6U/8img==", + "version": "8.6.7", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", + "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", "dev": true, "peerDependencies": { "@types/markdown-it": "*", "markdown-it": "*" } }, - "node_modules/markdown-it/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "node_modules/markdown-it/node_modules/entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } }, "node_modules/marked": { - "version": "4.0.16", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.16.tgz", - "integrity": "sha512-wahonIQ5Jnyatt2fn8KqF/nIqZM8mh3oRu2+l5EANGMhu6RFjiSG52QNE2eWzFMI94HqYSgN184NurgNG6CztA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", "dev": true, "bin": { "marked": "bin/marked.js" @@ -1682,7 +1852,7 @@ "node_modules/mdurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", "dev": true }, "node_modules/minimatch": { @@ -1698,10 +1868,13 @@ } }, "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/mkdirp": { "version": "1.0.4", @@ -1724,34 +1897,13 @@ "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/normalize-package-data/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, "node_modules/object-inspect": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz", - "integrity": "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==", + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1767,14 +1919,14 @@ } }, "node_modules/object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", "object-keys": "^1.1.1" }, "engines": { @@ -1785,28 +1937,57 @@ } }, "node_modules/object.entries": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.4.tgz", - "integrity": "sha512-h4LWKWE+wKQGhtMjZEBud7uLGhqyLwj8fpHOarZhD2uY3C9cRtk57VQ89ke3moByLXMedqs3XCHzyb4AmA2DjA==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz", + "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.2" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" }, "engines": { "node": ">= 0.4" } }, + "node_modules/object.fromentries": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", + "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.0.tgz", + "integrity": "sha512-70MWG6NfRH9GnbZOikuhPPYzpUpof9iW2J9E4dW7FXTqPNb6rllE6u39SKwwiNh8lCwX3DDb5OgcKGiEBrTTyw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.21.2", + "get-intrinsic": "^1.2.1" + } + }, "node_modules/object.values": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.4.tgz", - "integrity": "sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", + "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.2" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" }, "engines": { "node": ">= 0.4" @@ -1818,60 +1999,57 @@ "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "dependencies": { "wrappy": "1" } }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "type-check": "^0.4.0" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "dependencies": { - "p-try": "^1.0.0" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "dependencies": { - "p-limit": "^1.1.0" + "p-limit": "^3.0.2" }, "engines": { - "node": ">=4" - } - }, - "node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true, - "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/parent-module": { @@ -1886,32 +2064,19 @@ "node": ">=6" } }, - "node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "engines": { "node": ">=0.10.0" @@ -1932,51 +2097,6 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, - "node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "dependencies": { - "pify": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "dependencies": { - "find-up": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", - "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", - "dev": true, - "dependencies": { - "find-up": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -1986,89 +2106,73 @@ "node": ">= 0.8.0" } }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", "dev": true, "engines": { "node": ">=6" } }, - "node_modules/read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, - "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - }, - "engines": { - "node": ">=4" - } + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "node_modules/read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "node_modules/regexp.prototype.flags": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", + "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", "dev": true, "dependencies": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "functions-have-names": "^1.2.3" }, "engines": { - "node": ">=4" - } - }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/requizzle": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", - "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", + "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", "dev": true, "dependencies": { - "lodash": "^4.17.14" + "lodash": "^4.17.21" } }, "node_modules/resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.3.tgz", + "integrity": "sha512-P8ur/gp/AmbEzjr729bZnLjXK5Z+4P0zhIJgBgzqRih7hL7BOukHGtSTA3ACMY467GRFz3duQsi0bDZdR7DKdw==", "dev": true, "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "is-core-module": "^2.12.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2083,6 +2187,16 @@ "node": ">=4" } }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -2098,19 +2212,68 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", + "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==", "dev": true, "dependencies": { - "lru-cache": "^6.0.0" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, "bin": { "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" } }, "node_modules/shebang-command": { @@ -2134,141 +2297,72 @@ "node": ">=8" } }, - "node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" }, "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/string.prototype.trim": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", + "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/slice-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz", - "integrity": "sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ==", - "dev": true - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" @@ -2277,7 +2371,7 @@ "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, "engines": { "node": ">=4" @@ -2296,77 +2390,44 @@ } }, "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/table": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", - "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, - "dependencies": { - "ajv": "^8.0.1", - "lodash.clonedeep": "^4.5.0", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0" - }, "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/table/node_modules/ajv": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.0.tgz", - "integrity": "sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "node": ">= 0.4" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/table/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/taffydb": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", - "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=", - "dev": true - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, "node_modules/tsconfig-paths": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", - "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", + "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", "dev": true, "dependencies": { "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.0", + "json5": "^1.0.2", + "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, @@ -2394,6 +2455,71 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/uc.micro": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", @@ -2401,14 +2527,14 @@ "dev": true }, "node_modules/unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", "which-boxed-primitive": "^1.0.2" }, "funding": { @@ -2416,9 +2542,9 @@ } }, "node_modules/underscore": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.3.tgz", - "integrity": "sha512-QvjkYpiD+dJJraRA8+dGAU4i7aBbb2s0S3jA45TFOvg2VgqvdCDd/3N6CqA8gluk1W91GLoXg5enMUx560QzuA==", + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", "dev": true }, "node_modules/uri-js": { @@ -2430,22 +2556,6 @@ "punycode": "^2.1.0" } }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -2477,19 +2587,29 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "node_modules/which-typed-array": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", + "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, "node_modules/xmlcreate": { @@ -2498,1932 +2618,17 @@ "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", "dev": true }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", - "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", - "dev": true - }, - "@babel/highlight": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", - "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.14.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - } - } - }, - "@babel/parser": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.5.tgz", - "integrity": "sha512-TM8C+xtH/9n1qzX+JNHi7AN2zHMTiPUtspO0ZdHflW8KaskkALhMmuMHb4bCmNdv9VAPzJX3/bXqkVLnAvsPfg==", - "dev": true - }, - "@eslint/eslintrc": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", - "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - } - }, - "@html-eslint/eslint-plugin": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@html-eslint/eslint-plugin/-/eslint-plugin-0.15.0.tgz", - "integrity": "sha512-6DUb2ZN1PUlzlNzNj4aBhoObBp3Kl/+YbZ6CnkgFAsQSW0tSFAu7p8WwESkz9RZLZZN9gCUlcaYKJnQjTkmnDA==", - "dev": true - }, - "@html-eslint/parser": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@html-eslint/parser/-/parser-0.15.0.tgz", - "integrity": "sha512-fA+HQtWnODhOIK6j1p4XWqltINx7hM0WNNTM2RvlH/2glzeRDCcYq3vEmeQhnytvGocidu4ofTzNk80cLnnyiw==", - "dev": true, - "requires": { - "es-html-parser": "^0.0.8" - } - }, - "@humanwhocodes/config-array": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", - "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.0", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - } - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", - "dev": true - }, - "@types/linkify-it": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", - "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", - "dev": true - }, - "@types/markdown-it": { - "version": "12.2.3", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", - "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", - "dev": true, - "requires": { - "@types/linkify-it": "*", - "@types/mdurl": "*" - } - }, - "@types/mdurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", - "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", - "dev": true - }, - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "array-includes": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", - "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", - "get-intrinsic": "^1.1.1", - "is-string": "^1.0.5" - } - }, - "array.prototype.flat": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", - "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1" - } - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "catharsis": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", - "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", - "dev": true, - "requires": { - "lodash": "^4.17.15" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "confusing-browser-globals": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz", - "integrity": "sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA==", - "dev": true - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "requires": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "dependencies": { - "entities": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", - "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==" - } - } - }, - "domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" - }, - "domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "requires": { - "domelementtype": "^2.3.0" - } - }, - "domutils": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", - "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", - "requires": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.1" - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, - "entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", - "dev": true - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.18.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.3.tgz", - "integrity": "sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "is-callable": "^1.2.3", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.3", - "is-string": "^1.0.6", - "object-inspect": "^1.10.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" - } - }, - "es-html-parser": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/es-html-parser/-/es-html-parser-0.0.8.tgz", - "integrity": "sha512-kjMH23xhvTBw/7Ve1Dtb/7yZdFajfvwOpdsgRHmnyt8yvTsDJnkFjUgEEaMZFW+e1OhN/eoZrvF9wehq+waTGg==", - "dev": true - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "eslint": { - "version": "7.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", - "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", - "dev": true, - "requires": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.3", - "@humanwhocodes/config-array": "^0.5.0", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.9", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - } - }, - "eslint-config-airbnb-base": { - "version": "14.2.1", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz", - "integrity": "sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA==", - "dev": true, - "requires": { - "confusing-browser-globals": "^1.0.10", - "object.assign": "^4.1.2", - "object.entries": "^1.1.2" - } - }, - "eslint-import-resolver-node": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", - "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", - "dev": true, - "requires": { - "debug": "^2.6.9", - "resolve": "^1.13.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "eslint-module-utils": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz", - "integrity": "sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A==", - "dev": true, - "requires": { - "debug": "^3.2.7", - "pkg-dir": "^2.0.0" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "eslint-plugin-html": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-html/-/eslint-plugin-html-7.1.0.tgz", - "integrity": "sha512-fNLRraV/e6j8e3XYOC9xgND4j+U7b1Rq+OygMlLcMg+wI/IpVbF+ubQa3R78EjKB9njT6TQOlcK5rFKBVVtdfg==", - "requires": { - "htmlparser2": "^8.0.1" - } - }, - "eslint-plugin-import": { - "version": "2.23.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.23.4.tgz", - "integrity": "sha512-6/wP8zZRsnQFiR3iaPFgh5ImVRM1WN5NUWfTIRqwOdeiGJlBcSk82o1FEVq8yXmy4lkIzTo7YhHCIxlU/2HyEQ==", - "dev": true, - "requires": { - "array-includes": "^3.1.3", - "array.prototype.flat": "^1.2.4", - "debug": "^2.6.9", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.4", - "eslint-module-utils": "^2.6.1", - "find-up": "^2.0.0", - "has": "^1.0.3", - "is-core-module": "^2.4.0", - "minimatch": "^3.0.4", - "object.values": "^1.1.3", - "pkg-up": "^2.0.0", - "read-pkg-up": "^3.0.0", - "resolve": "^1.20.0", - "tsconfig-paths": "^3.9.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } - } - }, - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - }, - "espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", - "dev": true, - "requires": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - } - }, - "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "globals": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true - }, - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "htmlparser2": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz", - "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==", - "requires": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "entities": "^4.3.0" - }, - "dependencies": { - "entities": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", - "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==" - } - } - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-bigint": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz", - "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==", - "dev": true - }, - "is-boolean-object": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz", - "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-callable": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", - "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", - "dev": true - }, - "is-core-module": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", - "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-date-object": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz", - "integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", - "dev": true - }, - "is-number-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz", - "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==", - "dev": true - }, - "is-regex": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz", - "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-symbols": "^1.0.2" - } - }, - "is-string": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz", - "integrity": "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==", - "dev": true - }, - "is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "js2xmlparser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", - "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", - "dev": true, - "requires": { - "xmlcreate": "^2.0.4" - } - }, - "jsdoc": { - "version": "3.6.10", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.10.tgz", - "integrity": "sha512-IdQ8ppSo5LKZ9o3M+LKIIK8i00DIe5msDvG3G81Km+1dhy0XrOWD0Ji8H61ElgyEj/O9KRLokgKbAM9XX9CJAg==", - "dev": true, - "requires": { - "@babel/parser": "^7.9.4", - "@types/markdown-it": "^12.2.3", - "bluebird": "^3.7.2", - "catharsis": "^0.9.0", - "escape-string-regexp": "^2.0.0", - "js2xmlparser": "^4.0.2", - "klaw": "^4.0.1", - "markdown-it": "^12.3.2", - "markdown-it-anchor": "^8.4.1", - "marked": "^4.0.10", - "mkdirp": "^1.0.4", - "requizzle": "^0.2.3", - "strip-json-comments": "^3.1.0", - "taffydb": "2.6.2", - "underscore": "~1.13.2" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - } - } - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "klaw": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-4.0.1.tgz", - "integrity": "sha512-pgsE40/SvC7st04AHiISNewaIMUbY5V/K8b21ekiPiFoYs/EYSdsGa+FJArB1d441uq4Q8zZyIxvAzkGNlBdRw==", - "dev": true - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", - "dev": true, - "requires": { - "uc.micro": "^1.0.1" - } - }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", - "dev": true - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", - "dev": true, - "requires": { - "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - }, - "dependencies": { - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - } - } - }, - "markdown-it-anchor": { - "version": "8.6.4", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.4.tgz", - "integrity": "sha512-Ul4YVYZNxMJYALpKtu+ZRdrryYt/GlQ5CK+4l1bp/gWXOG2QWElt6AqF3Mih/wfUKdZbNAZVXGR73/n6U/8img==", - "dev": true, - "requires": {} - }, - "marked": { - "version": "4.0.16", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.16.tgz", - "integrity": "sha512-wahonIQ5Jnyatt2fn8KqF/nIqZM8mh3oRu2+l5EANGMhu6RFjiSG52QNE2eWzFMI94HqYSgN184NurgNG6CztA==", - "dev": true - }, - "mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "object-inspect": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz", - "integrity": "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - } - }, - "object.entries": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.4.tgz", - "integrity": "sha512-h4LWKWE+wKQGhtMjZEBud7uLGhqyLwj8fpHOarZhD2uY3C9cRtk57VQ89ke3moByLXMedqs3XCHzyb4AmA2DjA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.2" - } - }, - "object.values": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.4.tgz", - "integrity": "sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.2" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - } - }, - "pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", - "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - } - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - } - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, - "requizzle": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", - "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } - }, - "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dev": true, - "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } - } - }, - "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz", - "integrity": "sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ==", - "dev": true - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "table": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", - "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", - "dev": true, - "requires": { - "ajv": "^8.0.1", - "lodash.clonedeep": "^4.5.0", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0" + "engines": { + "node": ">=10" }, - "dependencies": { - "ajv": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.0.tgz", - "integrity": "sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - } - } - }, - "taffydb": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", - "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=", - "dev": true - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "tsconfig-paths": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", - "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", - "dev": true, - "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.0", - "strip-bom": "^3.0.0" - } - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - }, - "uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", - "dev": true - }, - "unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", - "which-boxed-primitive": "^1.0.2" - } - }, - "underscore": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.3.tgz", - "integrity": "sha512-QvjkYpiD+dJJraRA8+dGAU4i7aBbb2s0S3jA45TFOvg2VgqvdCDd/3N6CqA8gluk1W91GLoXg5enMUx560QzuA==", - "dev": true - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "xmlcreate": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", - "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true } } } diff --git a/platform/web/package.json b/platform/web/package.json index e1dd7b1a22..5da0e2a76b 100644 --- a/platform/web/package.json +++ b/platform/web/package.json @@ -21,14 +21,12 @@ "author": "Godot Engine contributors", "license": "MIT", "devDependencies": { - "@html-eslint/eslint-plugin": "^0.15.0", - "@html-eslint/parser": "^0.15.0", - "eslint": "^7.32.0", - "eslint-config-airbnb-base": "^14.2.1", - "eslint-plugin-import": "^2.23.4", - "jsdoc": "^3.6.7" - }, - "dependencies": { + "@html-eslint/eslint-plugin": "^0.19.1", + "@html-eslint/parser": "^0.19.1", + "eslint": "^8.46.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-plugin-import": "^2.28.0", + "jsdoc": "^4.0.2", "eslint-plugin-html": "^7.1.0" } } diff --git a/platform/windows/detect.py b/platform/windows/detect.py index bec1fd2cb6..9548939695 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -355,6 +355,9 @@ def configure_msvc(env, vcvars_msvc_config): else: env.AppendUnique(CCFLAGS=["/MD"]) + # MSVC incremental linking is broken and _increases_ link time (GH-77968). + env.Append(LINKFLAGS=["/INCREMENTAL:NO"]) + if env["arch"] == "x86_32": env["x86_libtheora_opt_vc"] = True diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index b1dccdcefe..5863a75324 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -278,7 +278,12 @@ Error DisplayServerWindows::file_dialog_show(const String &p_title, const String pfd->SetFileTypes(filters.size(), filters.ptr()); pfd->SetFileTypeIndex(0); - hr = pfd->Show(nullptr); + WindowID window_id = _get_focused_window_or_popup(); + if (!windows.has(window_id)) { + window_id = MAIN_WINDOW_ID; + } + + hr = pfd->Show(windows[window_id].hWnd); if (SUCCEEDED(hr)) { Vector<String> file_names; @@ -2735,7 +2740,7 @@ LRESULT DisplayServerWindows::MouseProc(int code, WPARAM wParam, LPARAM lParam) // Find top popup to close. while (E) { // Popup window area. - Rect2i win_rect = Rect2i(window_get_position(E->get()), window_get_size(E->get())); + Rect2i win_rect = Rect2i(window_get_position_with_decorations(E->get()), window_get_size_with_decorations(E->get())); // Area of the parent window, which responsible for opening sub-menu. Rect2i safe_rect = window_get_popup_safe_rect(E->get()); if (win_rect.has_point(pos)) { diff --git a/platform/windows/doc_classes/EditorExportPlatformWindows.xml b/platform/windows/doc_classes/EditorExportPlatformWindows.xml index 80928d7418..fc068efc75 100644 --- a/platform/windows/doc_classes/EditorExportPlatformWindows.xml +++ b/platform/windows/doc_classes/EditorExportPlatformWindows.xml @@ -22,10 +22,10 @@ File description to be presented to users. Required. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url]. </member> <member name="application/file_version" type="String" setter="" getter=""> - Version number of the file. Required. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url]. + Version number of the file. Falls back to [member ProjectSettings.application/config/version] if left empty. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url]. </member> <member name="application/icon" type="String" setter="" getter=""> - Application icon file. If left empty, it will fallback to [member ProjectSettings.application/config/windows_native_icon], and then to [member ProjectSettings.application/config/icon]. + Application icon file. If left empty, it will fallback to [member ProjectSettings.application/config/windows_native_icon], and then to [member ProjectSettings.application/config/icon]. </member> <member name="application/icon_interpolation" type="int" setter="" getter=""> Interpolation method used to resize application icon. @@ -37,7 +37,7 @@ Name of the application. Required. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url]. </member> <member name="application/product_version" type="String" setter="" getter=""> - Application version visible to the user. Required. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url]. + Application version visible to the user. Falls back to [member ProjectSettings.application/config/version] if left empty. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url]. </member> <member name="application/trademarks" type="String" setter="" getter=""> Trademarks and registered trademarks that apply to the file. Optional. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url]. diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp index 0ef07c3275..c4be1821bd 100644 --- a/platform/windows/export/export_plugin.cpp +++ b/platform/windows/export/export_plugin.cpp @@ -168,10 +168,10 @@ Error EditorExportPlatformWindows::sign_shared_object(const Ref<EditorExportPres Error EditorExportPlatformWindows::modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { if (p_preset->get("application/modify_resources")) { - _rcedit_add_data(p_preset, p_path, true); + _rcedit_add_data(p_preset, p_path, false); String wrapper_path = p_path.get_basename() + ".console.exe"; if (FileAccess::exists(wrapper_path)) { - _rcedit_add_data(p_preset, wrapper_path, false); + _rcedit_add_data(p_preset, wrapper_path, true); } } return OK; @@ -342,8 +342,8 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.ico,*.png,*.webp,*.svg"), "", false, true)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/console_wrapper_icon", PROPERTY_HINT_FILE, "*.ico,*.png,*.webp,*.svg"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/icon_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0.0"), "", false, true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0.0"), "", false, true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "Leave empty to use project version"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "Leave empty to use project version"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/company_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Company Name"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_description"), "")); @@ -425,8 +425,8 @@ Error EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset } } - String file_verion = p_preset->get("application/file_version"); - String product_version = p_preset->get("application/product_version"); + String file_verion = p_preset->get_version("application/file_version", true); + String product_version = p_preset->get_version("application/product_version", true); String company_name = p_preset->get("application/company_name"); String product_name = p_preset->get("application/product_name"); String file_description = p_preset->get("application/file_description"); diff --git a/platform/windows/joypad_windows.cpp b/platform/windows/joypad_windows.cpp index 487cb56ba0..0ac6c2c8b0 100644 --- a/platform/windows/joypad_windows.cpp +++ b/platform/windows/joypad_windows.cpp @@ -318,7 +318,9 @@ void JoypadWindows::probe_joypads() { x_joypads[i].ff_end_timestamp = 0; x_joypads[i].vibrating = false; attached_joypads[id] = true; - input->joy_connection_changed(id, true, "XInput Gamepad", "__XINPUT_DEVICE__"); + Dictionary joypad_info; + joypad_info["xinput_index"] = (int)i; + input->joy_connection_changed(id, true, "XInput Gamepad", "__XINPUT_DEVICE__", joypad_info); } } else if (x_joypads[i].attached) { x_joypads[i].attached = false; diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index df93631ef0..7c94e38e14 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -1714,7 +1714,7 @@ String OS_Windows::get_system_ca_certificates() { OS_Windows::OS_Windows(HINSTANCE _hInstance) { hInstance = _hInstance; - CoInitializeEx(nullptr, COINIT_MULTITHREADED); + CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); #ifdef WASAPI_ENABLED AudioDriverManager::add_driver(&driver_wasapi); diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp index 2ea7a8d6ae..3aa2a71a2c 100644 --- a/scene/2d/area_2d.cpp +++ b/scene/2d/area_2d.cpp @@ -510,7 +510,7 @@ StringName Area2D::get_audio_bus_name() const { return audio_bus; } } - return "Master"; + return SceneStringNames::get_singleton()->Master; } void Area2D::_validate_property(PropertyInfo &p_property) const { diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp index 343dd82b87..dac31058bd 100644 --- a/scene/2d/audio_stream_player_2d.cpp +++ b/scene/2d/audio_stream_player_2d.cpp @@ -116,10 +116,10 @@ StringName AudioStreamPlayer2D::_get_actual_bus() { //check if any area is diverting sound into a bus Ref<World2D> world_2d = get_world_2d(); - ERR_FAIL_COND_V(world_2d.is_null(), SNAME("Master")); + ERR_FAIL_COND_V(world_2d.is_null(), SceneStringNames::get_singleton()->Master); PhysicsDirectSpaceState2D *space_state = PhysicsServer2D::get_singleton()->space_get_direct_state(world_2d->get_space()); - ERR_FAIL_COND_V(space_state == nullptr, SNAME("Master")); + ERR_FAIL_COND_V(space_state == nullptr, SceneStringNames::get_singleton()->Master); PhysicsDirectSpaceState2D::ShapeResult sr[MAX_INTERSECT_AREAS]; PhysicsDirectSpaceState2D::PointParameters point_params; @@ -316,7 +316,7 @@ StringName AudioStreamPlayer2D::get_bus() const { return default_bus; } } - return SNAME("Master"); + return SceneStringNames::get_singleton()->Master; } void AudioStreamPlayer2D::set_autoplay(bool p_enable) { diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h index 9b23fd3943..a4677bef36 100644 --- a/scene/2d/audio_stream_player_2d.h +++ b/scene/2d/audio_stream_player_2d.h @@ -32,6 +32,7 @@ #define AUDIO_STREAM_PLAYER_2D_H #include "scene/2d/node_2d.h" +#include "scene/scene_string_names.h" #include "servers/audio/audio_stream.h" #include "servers/audio_server.h" @@ -66,7 +67,7 @@ private: float volume_db = 0.0; float pitch_scale = 1.0; bool autoplay = false; - StringName default_bus = SNAME("Master"); + StringName default_bus = SceneStringNames::get_singleton()->Master; int max_polyphony = 1; void _set_playing(bool p_enable); diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp index e7003ab98b..78987738a5 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -33,20 +33,21 @@ #include "core/config/project_settings.h" #include "scene/main/window.h" +bool Camera2D::_is_editing_in_editor() const { +#ifdef TOOLS_ENABLED + return is_part_of_edited_scene(); +#else + return false; +#endif // TOOLS_ENABLED +} + void Camera2D::_update_scroll() { - if (!is_inside_tree()) { + if (!is_inside_tree() || !viewport) { return; } - if (Engine::get_singleton()->is_editor_hint()) { + if (_is_editing_in_editor()) { queue_redraw(); - // Only set viewport transform when not bound to the main viewport. - if (get_tree()->get_edited_scene_root() && get_viewport() == get_tree()->get_edited_scene_root()->get_viewport()) { - return; - } - } - - if (!viewport) { return; } @@ -65,7 +66,7 @@ void Camera2D::_update_scroll() { } void Camera2D::_update_process_callback() { - if (Engine::get_singleton()->is_editor_hint()) { + if (_is_editing_in_editor()) { set_process_internal(false); set_physics_process_internal(false); } else if (process_callback == CAMERA2D_PROCESS_IDLE) { @@ -106,7 +107,7 @@ Transform2D Camera2D::get_camera_transform() { if (!first) { if (anchor_mode == ANCHOR_MODE_DRAG_CENTER) { - if (drag_horizontal_enabled && !Engine::get_singleton()->is_editor_hint() && !drag_horizontal_offset_changed) { + if (drag_horizontal_enabled && !_is_editing_in_editor() && !drag_horizontal_offset_changed) { camera_pos.x = MIN(camera_pos.x, (new_camera_pos.x + screen_size.x * 0.5 * zoom_scale.x * drag_margin[SIDE_LEFT])); camera_pos.x = MAX(camera_pos.x, (new_camera_pos.x - screen_size.x * 0.5 * zoom_scale.x * drag_margin[SIDE_RIGHT])); } else { @@ -119,7 +120,7 @@ Transform2D Camera2D::get_camera_transform() { drag_horizontal_offset_changed = false; } - if (drag_vertical_enabled && !Engine::get_singleton()->is_editor_hint() && !drag_vertical_offset_changed) { + if (drag_vertical_enabled && !_is_editing_in_editor() && !drag_vertical_offset_changed) { camera_pos.y = MIN(camera_pos.y, (new_camera_pos.y + screen_size.y * 0.5 * zoom_scale.y * drag_margin[SIDE_TOP])); camera_pos.y = MAX(camera_pos.y, (new_camera_pos.y - screen_size.y * 0.5 * zoom_scale.y * drag_margin[SIDE_BOTTOM])); @@ -158,7 +159,7 @@ Transform2D Camera2D::get_camera_transform() { } } - if (position_smoothing_enabled && !Engine::get_singleton()->is_editor_hint()) { + if (position_smoothing_enabled && !_is_editing_in_editor()) { real_t c = position_smoothing_speed * (process_callback == CAMERA2D_PROCESS_PHYSICS ? get_physics_process_delta_time() : get_process_delta_time()); smoothed_camera_pos = ((camera_pos - smoothed_camera_pos) * c) + smoothed_camera_pos; ret_camera_pos = smoothed_camera_pos; @@ -175,7 +176,7 @@ Transform2D Camera2D::get_camera_transform() { Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5 * zoom_scale) : Point2()); if (!ignore_rotation) { - if (rotation_smoothing_enabled && !Engine::get_singleton()->is_editor_hint()) { + if (rotation_smoothing_enabled && !_is_editing_in_editor()) { real_t step = rotation_smoothing_speed * (process_callback == CAMERA2D_PROCESS_PHYSICS ? get_physics_process_delta_time() : get_process_delta_time()); camera_angle = Math::lerp_angle(camera_angle, get_global_rotation(), step); } else { @@ -250,7 +251,7 @@ void Camera2D::_notification(int p_what) { add_to_group(group_name); add_to_group(canvas_group_name); - if (!Engine::get_singleton()->is_editor_hint() && enabled && !viewport->get_camera_2d()) { + if (!_is_editing_in_editor() && enabled && !viewport->get_camera_2d()) { make_current(); } @@ -272,7 +273,7 @@ void Camera2D::_notification(int p_what) { #ifdef TOOLS_ENABLED case NOTIFICATION_DRAW: { - if (!is_inside_tree() || !Engine::get_singleton()->is_editor_hint()) { + if (!is_inside_tree() || !_is_editing_in_editor()) { break; } @@ -398,7 +399,11 @@ void Camera2D::set_process_callback(Camera2DProcessCallback p_mode) { void Camera2D::set_enabled(bool p_enabled) { enabled = p_enabled; - if (enabled && is_inside_tree() && !viewport->get_camera_2d()) { + if (!is_inside_tree()) { + return; + } + + if (enabled && !viewport->get_camera_2d()) { make_current(); } else if (!enabled && is_current()) { clear_current(); @@ -414,27 +419,27 @@ Camera2D::Camera2DProcessCallback Camera2D::get_process_callback() const { } void Camera2D::_make_current(Object *p_which) { - if (!viewport || (custom_viewport && !ObjectDB::get_instance(custom_viewport_id))) { + if (!is_inside_tree() || !viewport) { return; } + if (custom_viewport && !ObjectDB::get_instance(custom_viewport_id)) { + return; + } + + queue_redraw(); + if (p_which == this) { - if (is_inside_tree()) { - viewport->_camera_2d_set(this); - queue_redraw(); - } + viewport->_camera_2d_set(this); } else { - if (is_inside_tree()) { - if (viewport->get_camera_2d() == this) { - viewport->_camera_2d_set(nullptr); - } - queue_redraw(); + if (viewport->get_camera_2d() == this) { + viewport->_camera_2d_set(nullptr); } } } void Camera2D::_update_process_internal_for_smoothing() { - bool is_not_in_scene_or_editor = !(is_inside_tree() && Engine::get_singleton()->is_editor_hint()); + bool is_not_in_scene_or_editor = !(is_inside_tree() && _is_editing_in_editor()); bool is_any_smoothing_valid = position_smoothing_speed > 0 || rotation_smoothing_speed > 0; bool enable = is_any_smoothing_valid && is_not_in_scene_or_editor; @@ -453,13 +458,22 @@ void Camera2D::make_current() { void Camera2D::clear_current() { ERR_FAIL_COND(!is_current()); - if (viewport && !(custom_viewport && !ObjectDB::get_instance(custom_viewport_id)) && viewport->is_inside_tree()) { + + if (!viewport || !viewport->is_inside_tree()) { + return; + } + + if (!custom_viewport || ObjectDB::get_instance(custom_viewport_id)) { viewport->assign_next_enabled_camera_2d(group_name); } } bool Camera2D::is_current() const { - if (viewport && !(custom_viewport && !ObjectDB::get_instance(custom_viewport_id))) { + if (!viewport) { + return false; + } + + if (!custom_viewport || ObjectDB::get_instance(custom_viewport_id)) { return viewport->get_camera_2d() == this; } return false; @@ -567,8 +581,7 @@ Point2 Camera2D::get_camera_screen_center() const { } Size2 Camera2D::_get_camera_screen_size() const { - // special case if the camera2D is in the root viewport - if (Engine::get_singleton()->is_editor_hint() && get_viewport()->get_parent_viewport() == get_tree()->get_root()) { + if (_is_editing_in_editor()) { return Size2(GLOBAL_GET("display/window/size/viewport_width"), GLOBAL_GET("display/window/size/viewport_height")); } return get_viewport_rect().size; diff --git a/scene/2d/camera_2d.h b/scene/2d/camera_2d.h index 808529b0fb..5693d05ee5 100644 --- a/scene/2d/camera_2d.h +++ b/scene/2d/camera_2d.h @@ -85,6 +85,7 @@ protected: bool drag_vertical_offset_changed = false; Point2 camera_screen_center; + bool _is_editing_in_editor() const; void _update_process_callback(); void _update_scroll(); diff --git a/scene/2d/canvas_modulate.cpp b/scene/2d/canvas_modulate.cpp index 7dd2e75f09..2c5c6a1a16 100644 --- a/scene/2d/canvas_modulate.cpp +++ b/scene/2d/canvas_modulate.cpp @@ -30,32 +30,67 @@ #include "canvas_modulate.h" +void CanvasModulate::_on_in_canvas_visibility_changed(bool p_new_visibility) { + RID canvas = get_canvas(); + StringName group_name = "_canvas_modulate_" + itos(canvas.get_id()); + + ERR_FAIL_COND_MSG(p_new_visibility == is_in_group(group_name), vformat("CanvasModulate becoming %s in the canvas already %s in the modulate group. Buggy logic, please report.", p_new_visibility ? "visible" : "invisible", p_new_visibility ? "was" : "was not")); + + if (p_new_visibility) { + bool has_active_canvas_modulate = get_tree()->has_group(group_name); // Group would be removed if empty; otherwise one CanvasModulate within must be active. + add_to_group(group_name); + if (!has_active_canvas_modulate) { + is_active = true; + RS::get_singleton()->canvas_set_modulate(canvas, color); + } + } else { + remove_from_group(group_name); + if (is_active) { + is_active = false; + CanvasModulate *new_active = Object::cast_to<CanvasModulate>(get_tree()->get_first_node_in_group(group_name)); + if (new_active) { + new_active->is_active = true; + RS::get_singleton()->canvas_set_modulate(canvas, new_active->color); + } else { + RS::get_singleton()->canvas_set_modulate(canvas, Color(1, 1, 1, 1)); + } + } + } + + update_configuration_warnings(); +} + void CanvasModulate::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_CANVAS: { - if (is_visible_in_tree()) { - RS::get_singleton()->canvas_set_modulate(get_canvas(), color); - add_to_group("_canvas_modulate_" + itos(get_canvas().get_id())); + is_in_canvas = true; + bool visible_in_tree = is_visible_in_tree(); + if (visible_in_tree) { + _on_in_canvas_visibility_changed(true); } + was_visible_in_tree = visible_in_tree; } break; case NOTIFICATION_EXIT_CANVAS: { - if (is_visible_in_tree()) { - RS::get_singleton()->canvas_set_modulate(get_canvas(), Color(1, 1, 1, 1)); - remove_from_group("_canvas_modulate_" + itos(get_canvas().get_id())); + is_in_canvas = false; + if (was_visible_in_tree) { + _on_in_canvas_visibility_changed(false); } } break; case NOTIFICATION_VISIBILITY_CHANGED: { - if (is_visible_in_tree()) { - RS::get_singleton()->canvas_set_modulate(get_canvas(), color); - add_to_group("_canvas_modulate_" + itos(get_canvas().get_id())); - } else { - RS::get_singleton()->canvas_set_modulate(get_canvas(), Color(1, 1, 1, 1)); - remove_from_group("_canvas_modulate_" + itos(get_canvas().get_id())); + if (!is_in_canvas) { + return; } - update_configuration_warnings(); + bool visible_in_tree = is_visible_in_tree(); + if (visible_in_tree == was_visible_in_tree) { + return; + } + + _on_in_canvas_visibility_changed(visible_in_tree); + + was_visible_in_tree = visible_in_tree; } break; } } @@ -69,7 +104,7 @@ void CanvasModulate::_bind_methods() { void CanvasModulate::set_color(const Color &p_color) { color = p_color; - if (is_visible_in_tree()) { + if (is_active) { RS::get_singleton()->canvas_set_modulate(get_canvas(), color); } } @@ -81,12 +116,12 @@ Color CanvasModulate::get_color() const { PackedStringArray CanvasModulate::get_configuration_warnings() const { PackedStringArray warnings = Node::get_configuration_warnings(); - if (is_visible_in_tree() && is_inside_tree()) { + if (is_in_canvas && is_visible_in_tree()) { List<Node *> nodes; get_tree()->get_nodes_in_group("_canvas_modulate_" + itos(get_canvas().get_id()), &nodes); if (nodes.size() > 1) { - warnings.push_back(RTR("Only one visible CanvasModulate is allowed per scene (or set of instantiated scenes). The first created one will work, while the rest will be ignored.")); + warnings.push_back(RTR("Only one visible CanvasModulate is allowed per canvas.\nWhen there are more than one, only one of them will be active. Which one is undefined.")); } } diff --git a/scene/2d/canvas_modulate.h b/scene/2d/canvas_modulate.h index 3b11cf71f1..08ded52e23 100644 --- a/scene/2d/canvas_modulate.h +++ b/scene/2d/canvas_modulate.h @@ -38,6 +38,14 @@ class CanvasModulate : public Node2D { Color color = Color(1, 1, 1, 1); + // CanvasModulate is in canvas-specific modulate group when both in canvas and visible in tree. + // Exactly one CanvasModulate in each such non-empty group is active. + bool is_in_canvas = false; + bool was_visible_in_tree = false; // Relevant only when in canvas. + bool is_active = false; + + void _on_in_canvas_visibility_changed(bool p_new_visibility); + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/scene/2d/tile_map.compat.inc b/scene/2d/tile_map.compat.inc index 49e2bf6f0b..c7786ecced 100644 --- a/scene/2d/tile_map.compat.inc +++ b/scene/2d/tile_map.compat.inc @@ -1,5 +1,5 @@ /**************************************************************************/ -/* object.compat.inc */ +/* tile_map.compat.inc */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -30,10 +30,6 @@ #ifndef DISABLE_DEPRECATED -#include "core/object/object.h" - -#include "core/object/class_db.h" - Rect2i TileMap::_get_used_rect_bind_compat_78328() { return get_used_rect(); } diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 1836cc20b6..09effe6596 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -1839,6 +1839,17 @@ void TileMapLayer::set_cell(const Vector2i &p_coords, int p_source_id, const Vec ERR_FAIL_COND(!Q); TileMapQuadrant &q = Q->value; + // Find node in scenes and remove it. + HashMap<Vector2i, String>::Iterator entry = q.scenes.find(pk); + if (entry != q.scenes.end()) { + String scene_name = entry->value; + Node *scene = tile_map_node->get_node_or_null(scene_name); + if (scene) { + scene->queue_free(); + instantiated_scenes.erase(Vector2i(pk.x, pk.y)); + } + } + q.cells.erase(pk); // Remove or make the quadrant dirty. diff --git a/scene/3d/area_3d.cpp b/scene/3d/area_3d.cpp index 29d151b726..beb6892435 100644 --- a/scene/3d/area_3d.cpp +++ b/scene/3d/area_3d.cpp @@ -573,7 +573,7 @@ StringName Area3D::get_audio_bus_name() const { return audio_bus; } } - return "Master"; + return SceneStringNames::get_singleton()->Master; } void Area3D::set_use_reverb_bus(bool p_enable) { @@ -594,7 +594,7 @@ StringName Area3D::get_reverb_bus_name() const { return reverb_bus; } } - return "Master"; + return SceneStringNames::get_singleton()->Master; } void Area3D::set_reverb_amount(float p_amount) { diff --git a/scene/3d/area_3d.h b/scene/3d/area_3d.h index 454c8c2960..86602d3192 100644 --- a/scene/3d/area_3d.h +++ b/scene/3d/area_3d.h @@ -33,6 +33,7 @@ #include "core/templates/vset.h" #include "scene/3d/collision_object_3d.h" +#include "scene/scene_string_names.h" class Area3D : public CollisionObject3D { GDCLASS(Area3D, CollisionObject3D); @@ -134,10 +135,10 @@ private: void _clear_monitoring(); bool audio_bus_override = false; - StringName audio_bus = "Master"; + StringName audio_bus = SceneStringNames::get_singleton()->Master; bool use_reverb_bus = false; - StringName reverb_bus = "Master"; + StringName reverb_bus = SceneStringNames::get_singleton()->Master; float reverb_amount = 0.0; float reverb_uniformity = 0.0; diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index ac626d0d2a..8147521a01 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -35,6 +35,7 @@ #include "scene/3d/audio_listener_3d.h" #include "scene/3d/camera_3d.h" #include "scene/main/viewport.h" +#include "scene/scene_string_names.h" // Based on "A Novel Multichannel Panning Method for Standard and Arbitrary Loudspeaker Configurations" by Ramy Sadek and Chris Kyriakakis (2004) // Speaker-Placement Correction Amplitude Panning (SPCAP) @@ -635,7 +636,7 @@ StringName AudioStreamPlayer3D::get_bus() const { return bus; } } - return SNAME("Master"); + return SceneStringNames::get_singleton()->Master; } void AudioStreamPlayer3D::set_autoplay(bool p_enable) { diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index b4df06a83e..a666dca658 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -552,7 +552,7 @@ int32_t LightmapGI::_compute_bsp_tree(const Vector<Vector3> &p_points, const Loc // Luckily, because we are using tetrahedrons, we can resort to // less precise but still working ways to generate the separating plane // this will most likely look bad when interpolating, but at least it will not crash. - // and the arctifact will most likely also be very small, so too difficult to notice. + // and the artifact will most likely also be very small, so too difficult to notice. //find the longest axis diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp index 8d66f7ebeb..e03640f6cb 100644 --- a/scene/3d/navigation_region_3d.cpp +++ b/scene/3d/navigation_region_3d.cpp @@ -179,10 +179,6 @@ void NavigationRegion3D::_notification(int p_what) { } void NavigationRegion3D::set_navigation_mesh(const Ref<NavigationMesh> &p_navigation_mesh) { - if (p_navigation_mesh == navigation_mesh) { - return; - } - if (navigation_mesh.is_valid()) { navigation_mesh->disconnect_changed(callable_mp(this, &NavigationRegion3D::_navigation_mesh_changed)); } @@ -240,62 +236,29 @@ RID NavigationRegion3D::get_navigation_map() const { return RID(); } -struct BakeThreadsArgs { - NavigationRegion3D *nav_region = nullptr; - Ref<NavigationMeshSourceGeometryData3D> source_geometry_data; -}; - -void _bake_navigation_mesh(void *p_user_data) { - BakeThreadsArgs *args = static_cast<BakeThreadsArgs *>(p_user_data); - - if (args->nav_region->get_navigation_mesh().is_valid()) { - Ref<NavigationMesh> nav_mesh = args->nav_region->get_navigation_mesh()->duplicate(); - Ref<NavigationMeshSourceGeometryData3D> source_geometry_data = args->source_geometry_data; - - NavigationServer3D::get_singleton()->bake_from_source_geometry_data(nav_mesh, source_geometry_data); - if (!Thread::is_main_thread()) { - args->nav_region->call_deferred(SNAME("_bake_finished"), nav_mesh); - } else { - args->nav_region->_bake_finished(nav_mesh); - } - memdelete(args); - } else { - ERR_PRINT("Can't bake the navigation mesh if the `NavigationMesh` resource doesn't exist"); - if (!Thread::is_main_thread()) { - args->nav_region->call_deferred(SNAME("_bake_finished"), Ref<NavigationMesh>()); - } else { - args->nav_region->_bake_finished(Ref<NavigationMesh>()); - } - memdelete(args); - } -} - void NavigationRegion3D::bake_navigation_mesh(bool p_on_thread) { ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "The SceneTree can only be parsed on the main thread. Call this function from the main thread or use call_deferred()."); ERR_FAIL_COND_MSG(!navigation_mesh.is_valid(), "Baking the navigation mesh requires a valid `NavigationMesh` resource."); - ERR_FAIL_COND_MSG(bake_thread.is_started(), "Unable to start another bake request. The navigation mesh bake thread is already baking a navigation mesh."); Ref<NavigationMeshSourceGeometryData3D> source_geometry_data; source_geometry_data.instantiate(); NavigationServer3D::get_singleton()->parse_source_geometry_data(navigation_mesh, source_geometry_data, this); - BakeThreadsArgs *args = memnew(BakeThreadsArgs); - args->nav_region = this; - args->source_geometry_data = source_geometry_data; - if (p_on_thread) { - bake_thread.start(_bake_navigation_mesh, args); + NavigationServer3D::get_singleton()->bake_from_source_geometry_data_async(navigation_mesh, source_geometry_data, callable_mp(this, &NavigationRegion3D::_bake_finished).bind(navigation_mesh)); } else { - _bake_navigation_mesh(args); + NavigationServer3D::get_singleton()->bake_from_source_geometry_data(navigation_mesh, source_geometry_data, callable_mp(this, &NavigationRegion3D::_bake_finished).bind(navigation_mesh)); } } -void NavigationRegion3D::_bake_finished(Ref<NavigationMesh> p_nav_mesh) { - set_navigation_mesh(p_nav_mesh); - if (bake_thread.is_started()) { - bake_thread.wait_to_finish(); +void NavigationRegion3D::_bake_finished(Ref<NavigationMesh> p_navigation_mesh) { + if (!Thread::is_main_thread()) { + call_deferred(SNAME("_bake_finished"), p_navigation_mesh); + return; } + + set_navigation_mesh(p_navigation_mesh); emit_signal(SNAME("bake_finished")); } @@ -456,10 +419,6 @@ NavigationRegion3D::NavigationRegion3D() { } NavigationRegion3D::~NavigationRegion3D() { - if (bake_thread.is_started()) { - bake_thread.wait_to_finish(); - } - if (navigation_mesh.is_valid()) { navigation_mesh->disconnect_changed(callable_mp(this, &NavigationRegion3D::_navigation_mesh_changed)); } diff --git a/scene/3d/navigation_region_3d.h b/scene/3d/navigation_region_3d.h index e41d07f4cf..02fe5524b2 100644 --- a/scene/3d/navigation_region_3d.h +++ b/scene/3d/navigation_region_3d.h @@ -49,8 +49,6 @@ class NavigationRegion3D : public Node3D { Transform3D current_global_transform; - Thread bake_thread; - void _navigation_mesh_changed(); #ifdef DEBUG_ENABLED diff --git a/scene/3d/ray_cast_3d.cpp b/scene/3d/ray_cast_3d.cpp index 7b04669dad..13519ecec7 100644 --- a/scene/3d/ray_cast_3d.cpp +++ b/scene/3d/ray_cast_3d.cpp @@ -104,6 +104,10 @@ Vector3 RayCast3D::get_collision_normal() const { return collision_normal; } +int RayCast3D::get_collision_face_index() const { + return collision_face_index; +} + void RayCast3D::set_enabled(bool p_enabled) { enabled = p_enabled; update_gizmos(); @@ -232,6 +236,7 @@ void RayCast3D::_update_raycast_state() { against_rid = rr.rid; collision_point = rr.position; collision_normal = rr.normal; + collision_face_index = rr.face_index; against_shape = rr.shape; } else { collided = false; @@ -321,6 +326,7 @@ void RayCast3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_collider_shape"), &RayCast3D::get_collider_shape); ClassDB::bind_method(D_METHOD("get_collision_point"), &RayCast3D::get_collision_point); ClassDB::bind_method(D_METHOD("get_collision_normal"), &RayCast3D::get_collision_normal); + ClassDB::bind_method(D_METHOD("get_collision_face_index"), &RayCast3D::get_collision_face_index); ClassDB::bind_method(D_METHOD("add_exception_rid", "rid"), &RayCast3D::add_exception_rid); ClassDB::bind_method(D_METHOD("add_exception", "node"), &RayCast3D::add_exception); diff --git a/scene/3d/ray_cast_3d.h b/scene/3d/ray_cast_3d.h index 1def7a0eca..7b7f698114 100644 --- a/scene/3d/ray_cast_3d.h +++ b/scene/3d/ray_cast_3d.h @@ -45,6 +45,7 @@ class RayCast3D : public Node3D { int against_shape = 0; Vector3 collision_point; Vector3 collision_normal; + int collision_face_index = -1; Vector3 target_position = Vector3(0, -1, 0); HashSet<RID> exclude; @@ -122,6 +123,7 @@ public: int get_collider_shape() const; Vector3 get_collision_point() const; Vector3 get_collision_normal() const; + int get_collision_face_index() const; void add_exception_rid(const RID &p_rid); void add_exception(const CollisionObject3D *p_node); diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp index 6c37d6f81d..ae68bd719b 100644 --- a/scene/audio/audio_stream_player.cpp +++ b/scene/audio/audio_stream_player.cpp @@ -200,7 +200,7 @@ StringName AudioStreamPlayer::get_bus() const { return bus; } } - return SNAME("Master"); + return SceneStringNames::get_singleton()->Master; } void AudioStreamPlayer::set_autoplay(bool p_enable) { diff --git a/scene/audio/audio_stream_player.h b/scene/audio/audio_stream_player.h index d1f6fca2ee..c0c25aa983 100644 --- a/scene/audio/audio_stream_player.h +++ b/scene/audio/audio_stream_player.h @@ -33,6 +33,7 @@ #include "core/templates/safe_refcount.h" #include "scene/main/node.h" +#include "scene/scene_string_names.h" #include "servers/audio/audio_stream.h" class AudioStreamPlayer : public Node { @@ -54,7 +55,7 @@ private: float pitch_scale = 1.0; float volume_db = 0.0; bool autoplay = false; - StringName bus = SNAME("Master"); + StringName bus = SceneStringNames::get_singleton()->Master; int max_polyphony = 1; MixTarget mix_target = MIX_TARGET_STEREO; diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index 3ac3ca7363..f57afb66b3 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -468,12 +468,12 @@ void BaseButton::_bind_methods() { ClassDB::bind_method(D_METHOD("get_button_group"), &BaseButton::get_button_group); GDVIRTUAL_BIND(_pressed); - GDVIRTUAL_BIND(_toggled, "button_pressed"); + GDVIRTUAL_BIND(_toggled, "toggled_on"); ADD_SIGNAL(MethodInfo("pressed")); ADD_SIGNAL(MethodInfo("button_up")); ADD_SIGNAL(MethodInfo("button_down")); - ADD_SIGNAL(MethodInfo("toggled", PropertyInfo(Variant::BOOL, "button_pressed"))); + ADD_SIGNAL(MethodInfo("toggled", PropertyInfo(Variant::BOOL, "toggled_on"))); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "toggle_mode"), "set_toggle_mode", "is_toggle_mode"); diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index 430569432a..738778b516 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -118,6 +118,7 @@ void Button::_notification(int p_what) { Ref<StyleBox> style = theme_cache.normal; bool rtl = is_layout_rtl(); + const bool is_clipped = clip_text || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING; switch (get_draw_mode()) { case DRAW_NORMAL: { @@ -283,7 +284,7 @@ void Button::_notification(int p_what) { Size2 _size = get_size() - style->get_offset() * 2; int icon_text_separation = text.is_empty() ? 0 : theme_cache.h_separation; _size.width -= icon_text_separation + icon_ofs_region; - if (!clip_text && icon_align_rtl_checked != HORIZONTAL_ALIGNMENT_CENTER) { + if (!is_clipped && icon_align_rtl_checked != HORIZONTAL_ALIGNMENT_CENTER) { _size.width -= text_buf->get_size().width; } if (vertical_icon_alignment != VERTICAL_ALIGNMENT_CENTER) { @@ -335,9 +336,9 @@ void Button::_notification(int p_what) { text_clip -= _internal_margin[SIDE_RIGHT] + theme_cache.h_separation; } - text_buf->set_width((clip_text || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) ? text_clip : -1); + text_buf->set_width(is_clipped ? text_clip : -1); - int text_width = MAX(1, (clip_text || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) ? MIN(text_clip, text_buf->get_size().x) : text_buf->get_size().x); + int text_width = MAX(1, is_clipped ? MIN(text_clip, text_buf->get_size().x) : text_buf->get_size().x); Point2 text_ofs = (size - style->get_minimum_size() - icon_ofs - text_buf->get_size() - Point2(_internal_margin[SIDE_RIGHT] - _internal_margin[SIDE_LEFT], 0)) / 2.0; diff --git a/scene/gui/code_edit.compat.inc b/scene/gui/code_edit.compat.inc new file mode 100644 index 0000000000..9107d6523f --- /dev/null +++ b/scene/gui/code_edit.compat.inc @@ -0,0 +1,41 @@ +/**************************************************************************/ +/* code_edit.compat.inc */ +/**************************************************************************/ +/* 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 DISABLE_DEPRECATED + +String CodeEdit::_get_text_for_symbol_lookup_bind_compat_73196() { + return get_text_for_symbol_lookup(); +} + +void CodeEdit::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("get_text_for_symbol_lookup"), &CodeEdit::_get_text_for_symbol_lookup_bind_compat_73196); +} + +#endif diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index eee59606e3..443a639ff2 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -29,6 +29,7 @@ /**************************************************************************/ #include "code_edit.h" +#include "code_edit.compat.inc" #include "core/os/keyboard.h" #include "core/string/string_builder.h" @@ -3127,6 +3128,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() { } String target_lower = option.display.to_lower(); + int long_option = target_lower.size() > 50; const char32_t *string_to_complete_char_lower = &string_to_complete_lower[0]; const char32_t *target_char_lower = &target_lower[0]; @@ -3141,27 +3143,34 @@ void CodeEdit::_filter_code_completion_candidates_impl() { for (int i = 1; *string_to_complete_char_lower && (all_possible_subsequence_matches.size() > 0); i++, string_to_complete_char_lower++) { // find all occurrences of ssq_lower to avoid looking everywhere each time Vector<int> all_ocurence; - for (int j = i; j < target_lower.length(); j++) { - if (target_lower[j] == *string_to_complete_char_lower) { - all_ocurence.push_back(j); + if (long_option) { + all_ocurence.push_back(target_lower.find_char(*string_to_complete_char_lower)); + } else { + for (int j = i; j < target_lower.length(); j++) { + if (target_lower[j] == *string_to_complete_char_lower) { + all_ocurence.push_back(j); + } } } Vector<Vector<Pair<int, int>>> next_subsequence_matches; - for (Vector<Pair<int, int>> &subsequence_matches : all_possible_subsequence_matches) { - Pair<int, int> match_last_segment = subsequence_matches[subsequence_matches.size() - 1]; + for (Vector<Pair<int, int>> &subsequence_match : all_possible_subsequence_matches) { + Pair<int, int> match_last_segment = subsequence_match[subsequence_match.size() - 1]; int next_index = match_last_segment.first + match_last_segment.second; // get the last index from current sequence // and look for next char starting from that index if (target_lower[next_index] == *string_to_complete_char_lower) { - Vector<Pair<int, int>> new_matches = subsequence_matches; - new_matches.write[new_matches.size() - 1].second++; - next_subsequence_matches.push_back(new_matches); + Vector<Pair<int, int>> new_match = subsequence_match; + new_match.write[new_match.size() - 1].second++; + next_subsequence_matches.push_back(new_match); + if (long_option) { + continue; + } } for (int index : all_ocurence) { if (index > next_index) { - Vector<Pair<int, int>> new_matches = subsequence_matches; - new_matches.push_back({ index, 1 }); - next_subsequence_matches.push_back(new_matches); + Vector<Pair<int, int>> new_match = subsequence_match; + new_match.push_back({ index, 1 }); + next_subsequence_matches.push_back(new_match); } } } diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h index a3c968da60..addbb6e468 100644 --- a/scene/gui/code_edit.h +++ b/scene/gui/code_edit.h @@ -287,6 +287,11 @@ protected: void _notification(int p_what); static void _bind_methods(); +#ifndef DISABLE_DEPRECATED + String _get_text_for_symbol_lookup_bind_compat_73196(); + static void _bind_compatibility_methods(); +#endif + virtual void _update_theme_item_cache() override; /* Text manipulation */ diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 2a0f85a1be..3c19766ca7 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -71,16 +71,14 @@ void ColorPicker::_notification(int p_what) { for (int i = 0; i < SLIDER_COUNT; i++) { labels[i]->set_custom_minimum_size(Size2(theme_cache.label_width, 0)); sliders[i]->add_theme_constant_override(SNAME("center_grabber"), theme_cache.center_slider_grabbers); - set_offset((Side)i, get_offset((Side)i) + theme_cache.content_margin); } alpha_label->set_custom_minimum_size(Size2(theme_cache.label_width, 0)); alpha_label->add_theme_constant_override(SNAME("center_grabber"), theme_cache.center_slider_grabbers); - set_offset((Side)0, get_offset((Side)0) + theme_cache.content_margin); for (int i = 0; i < MODE_BUTTON_COUNT; i++) { - mode_btns[i]->add_theme_style_override("pressed", theme_cache.mode_button_pressed); - mode_btns[i]->add_theme_style_override("normal", theme_cache.mode_button_normal); - mode_btns[i]->add_theme_style_override("hover", theme_cache.mode_button_hover); + mode_btns[i]->add_theme_style_override(SNAME("pressed"), theme_cache.mode_button_pressed); + mode_btns[i]->add_theme_style_override(SNAME("normal"), theme_cache.mode_button_normal); + mode_btns[i]->add_theme_style_override(SNAME("hover"), theme_cache.mode_button_hover); } shape_popup->set_item_icon(shape_popup->get_item_index(SHAPE_HSV_RECTANGLE), theme_cache.shape_rect); @@ -88,6 +86,11 @@ void ColorPicker::_notification(int p_what) { shape_popup->set_item_icon(shape_popup->get_item_index(SHAPE_VHS_CIRCLE), theme_cache.shape_circle); shape_popup->set_item_icon(shape_popup->get_item_index(SHAPE_OKHSL_CIRCLE), theme_cache.shape_circle); + internal_margin->add_theme_constant_override(SNAME("margin_bottom"), theme_cache.content_margin); + internal_margin->add_theme_constant_override(SNAME("margin_left"), theme_cache.content_margin); + internal_margin->add_theme_constant_override(SNAME("margin_right"), theme_cache.content_margin); + internal_margin->add_theme_constant_override(SNAME("margin_top"), theme_cache.content_margin); + _reset_sliders_theme(); if (Engine::get_singleton()->is_editor_hint()) { @@ -100,13 +103,6 @@ void ColorPicker::_notification(int p_what) { _update_controls(); } break; - case NOTIFICATION_VISIBILITY_CHANGED: { - Popup *p = Object::cast_to<Popup>(get_parent()); - if (p && is_visible_in_tree()) { - p->set_size(Size2(get_combined_minimum_size().width + theme_cache.content_margin * 2, get_combined_minimum_size().height + theme_cache.content_margin * 2)); - } - } break; - case NOTIFICATION_WM_CLOSE_REQUEST: { if (picker_window != nullptr && picker_window->is_visible()) { picker_window->hide(); @@ -1450,7 +1446,7 @@ void ColorPicker::_pick_button_pressed() { picker_window = memnew(Popup); picker_window->set_size(Vector2i(1, 1)); picker_window->connect("visibility_changed", callable_mp(this, &ColorPicker::_pick_finished)); - add_child(picker_window); + add_child(picker_window, false, INTERNAL_MODE_FRONT); } picker_window->popup(); } @@ -1479,7 +1475,7 @@ void ColorPicker::_pick_button_pressed_legacy() { picker_window = memnew(Popup); picker_window->hide(); picker_window->set_transient(true); - add_child(picker_window); + add_child(picker_window, false, INTERNAL_MODE_FRONT); picker_texture_rect = memnew(TextureRect); picker_texture_rect->set_anchors_preset(Control::PRESET_FULL_RECT); @@ -1715,8 +1711,14 @@ void ColorPicker::_bind_methods() { } ColorPicker::ColorPicker() { + internal_margin = memnew(MarginContainer); + add_child(internal_margin, false, INTERNAL_MODE_FRONT); + + VBoxContainer *real_vbox = memnew(VBoxContainer); + internal_margin->add_child(real_vbox); + HBoxContainer *hb_edit = memnew(HBoxContainer); - add_child(hb_edit, false, INTERNAL_MODE_FRONT); + real_vbox->add_child(hb_edit); hb_edit->set_v_size_flags(SIZE_SHRINK_BEGIN); uv_edit = memnew(Control); @@ -1728,7 +1730,7 @@ ColorPicker::ColorPicker() { uv_edit->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw).bind(0, uv_edit)); sample_hbc = memnew(HBoxContainer); - add_child(sample_hbc, false, INTERNAL_MODE_FRONT); + real_vbox->add_child(sample_hbc); btn_pick = memnew(Button); sample_hbc->add_child(btn_pick); @@ -1771,7 +1773,7 @@ ColorPicker::ColorPicker() { add_mode(new ColorModeOKHSL(this)); mode_hbc = memnew(HBoxContainer); - add_child(mode_hbc, false, INTERNAL_MODE_FRONT); + real_vbox->add_child(mode_hbc); mode_group.instantiate(); @@ -1806,11 +1808,11 @@ ColorPicker::ColorPicker() { mode_popup->set_item_checked(MODE_MAX + 1, true); mode_popup->connect("id_pressed", callable_mp(this, &ColorPicker::_set_mode_popup_value)); VBoxContainer *vbl = memnew(VBoxContainer); - add_child(vbl, false, INTERNAL_MODE_FRONT); + real_vbox->add_child(vbl); VBoxContainer *vbr = memnew(VBoxContainer); - add_child(vbr, false, INTERNAL_MODE_FRONT); + real_vbox->add_child(vbr); vbr->set_h_size_flags(SIZE_EXPAND_FILL); slider_gc = memnew(GridContainer); @@ -1900,9 +1902,9 @@ ColorPicker::ColorPicker() { btn_preset->set_focus_mode(FOCUS_NONE); btn_preset->set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT); btn_preset->connect("toggled", callable_mp(this, &ColorPicker::_show_hide_preset).bind(btn_preset, preset_container)); - add_child(btn_preset, false, INTERNAL_MODE_FRONT); + real_vbox->add_child(btn_preset); - add_child(preset_container, false, INTERNAL_MODE_FRONT); + real_vbox->add_child(preset_container); recent_preset_hbc = memnew(HBoxContainer); recent_preset_hbc->set_v_size_flags(SIZE_SHRINK_BEGIN); @@ -1917,9 +1919,9 @@ ColorPicker::ColorPicker() { btn_recent_preset->set_focus_mode(FOCUS_NONE); btn_recent_preset->set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT); btn_recent_preset->connect("toggled", callable_mp(this, &ColorPicker::_show_hide_preset).bind(btn_recent_preset, recent_preset_hbc)); - add_child(btn_recent_preset, false, INTERNAL_MODE_FRONT); + real_vbox->add_child(btn_recent_preset); - add_child(recent_preset_hbc, false, INTERNAL_MODE_FRONT); + real_vbox->add_child(recent_preset_hbc); set_pick_color(Color(1, 1, 1)); diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h index 1a4b1b6ee1..680b3f7d96 100644 --- a/scene/gui/color_picker.h +++ b/scene/gui/color_picker.h @@ -130,6 +130,7 @@ private: Ref<StyleBoxFlat> picker_preview_style_box; Color picker_color; + MarginContainer *internal_margin = nullptr; Control *uv_edit = nullptr; Control *w_edit = nullptr; AspectRatioContainer *wheel_edit = nullptr; diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp index 4f94f0d9f2..6e75be268d 100644 --- a/scene/gui/dialogs.cpp +++ b/scene/gui/dialogs.cpp @@ -38,8 +38,7 @@ // AcceptDialog void AcceptDialog::_input_from_window(const Ref<InputEvent> &p_event) { - Ref<InputEventKey> key = p_event; - if (close_on_escape && key.is_valid() && key->is_action_pressed(SNAME("ui_cancel"), false, true)) { + if (close_on_escape && p_event->is_action_pressed(SNAME("ui_cancel"), false, true)) { _cancel_pressed(); } } diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index 614e9b9695..d86cc4a5f9 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -227,7 +227,6 @@ private: void _zoom_plus(); void _update_zoom_label(); - PackedVector2Array get_connection_line(const Vector2 &p_from, const Vector2 &p_to); void _draw_connection_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color, float p_width, float p_zoom); void _graph_node_selected(Node *p_gn); @@ -243,8 +242,6 @@ private: virtual void gui_input(const Ref<InputEvent> &p_ev) override; void _top_layer_input(const Ref<InputEvent> &p_ev); - bool is_in_input_hotzone(GraphNode *p_graph_node, int p_port, const Vector2 &p_mouse_pos, const Vector2i &p_port_size); - bool is_in_output_hotzone(GraphNode *p_graph_node, int p_port, const Vector2 &p_mouse_pos, const Vector2i &p_port_size); bool is_in_port_hotzone(const Vector2 &p_pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left); void _top_layer_draw(); @@ -272,6 +269,9 @@ protected: void _notification(int p_what); + virtual bool is_in_input_hotzone(GraphNode *p_graph_node, int p_port, const Vector2 &p_mouse_pos, const Vector2i &p_port_size); + virtual bool is_in_output_hotzone(GraphNode *p_graph_node, int p_port, const Vector2 &p_mouse_pos, const Vector2i &p_port_size); + GDVIRTUAL2RC(Vector<Vector2>, _get_connection_line, Vector2, Vector2) GDVIRTUAL3R(bool, _is_in_input_hotzone, Object *, int, Vector2) GDVIRTUAL3R(bool, _is_in_output_hotzone, Object *, int, Vector2) @@ -287,6 +287,8 @@ public: void disconnect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port); void clear_connections(); void force_connection_drag_end(); + + virtual PackedVector2Array get_connection_line(const Vector2 &p_from, const Vector2 &p_to); virtual bool is_node_hover_valid(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port); void set_connection_activity(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, float p_activity); diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index b273f709f2..cff07c6e1c 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -675,11 +675,18 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { if (closest != -1 && (mb->get_button_index() == MouseButton::LEFT || (allow_rmb_select && mb->get_button_index() == MouseButton::RIGHT))) { int i = closest; + if (items[i].disabled) { + // Don't emit any signal or do any action with clicked item when disabled. + return; + } + if (select_mode == SELECT_MULTI && items[i].selected && mb->is_command_or_control_pressed()) { deselect(i); emit_signal(SNAME("multi_selected"), i, false); } else if (select_mode == SELECT_MULTI && mb->is_shift_pressed() && current >= 0 && current < items.size() && current != i) { + // Range selection. + int from = current; int to = i; if (i < current) { @@ -687,6 +694,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { } for (int j = from; j <= to; j++) { if (!CAN_SELECT(j)) { + // Item is not selectable during a range selection, so skip it. continue; } bool selected = !items[j].selected; @@ -698,12 +706,17 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { emit_signal(SNAME("item_clicked"), i, get_local_mouse_position(), mb->get_button_index()); } else { - if (!mb->is_double_click() && !mb->is_command_or_control_pressed() && select_mode == SELECT_MULTI && items[i].selectable && !items[i].disabled && items[i].selected && mb->get_button_index() == MouseButton::LEFT) { + if (!mb->is_double_click() && + !mb->is_command_or_control_pressed() && + select_mode == SELECT_MULTI && + items[i].selectable && + items[i].selected && + mb->get_button_index() == MouseButton::LEFT) { defer_select_single = i; return; } - if (!items[i].selected || allow_reselect) { + if (items[i].selectable && (!items[i].selected || allow_reselect)) { select(i, select_mode == SELECT_SINGLE || !mb->is_command_or_control_pressed()); if (select_mode == SELECT_SINGLE) { @@ -722,7 +735,9 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { return; } else if (closest != -1) { - emit_signal(SNAME("item_clicked"), closest, get_local_mouse_position(), mb->get_button_index()); + if (!items[closest].disabled) { + emit_signal(SNAME("item_clicked"), closest, get_local_mouse_position(), mb->get_button_index()); + } } else { // Since closest is null, more likely we clicked on empty space, so send signal to interested controls. Allows, for example, implement items deselecting. emit_signal(SNAME("empty_clicked"), get_local_mouse_position(), mb->get_button_index()); @@ -886,7 +901,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { search_string = ""; } else if (p_event->is_action("ui_select", true) && select_mode == SELECT_MULTI) { if (current >= 0 && current < items.size()) { - if (items[current].selectable && !items[current].disabled && !items[current].selected) { + if (CAN_SELECT(current) && !items[current].selected) { select(current, false); emit_signal(SNAME("multi_selected"), current, true); } else if (items[current].selected) { @@ -897,7 +912,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { } else if (p_event->is_action("ui_accept", true)) { search_string = ""; //any mousepress cancels - if (current >= 0 && current < items.size()) { + if (current >= 0 && current < items.size() && !items[current].disabled) { emit_signal(SNAME("item_activated"), current); } } else { diff --git a/scene/gui/menu_bar.cpp b/scene/gui/menu_bar.cpp index 85068ac862..0dd258d92c 100644 --- a/scene/gui/menu_bar.cpp +++ b/scene/gui/menu_bar.cpp @@ -152,7 +152,7 @@ void MenuBar::shortcut_input(const Ref<InputEvent> &p_event) { return; } - if (p_event->is_pressed() && !p_event->is_echo() && (Object::cast_to<InputEventKey>(p_event.ptr()) || Object::cast_to<InputEventJoypadButton>(p_event.ptr()) || Object::cast_to<InputEventAction>(*p_event) || Object::cast_to<InputEventShortcut>(*p_event))) { + if (p_event->is_pressed() && (Object::cast_to<InputEventKey>(p_event.ptr()) || Object::cast_to<InputEventJoypadButton>(p_event.ptr()) || Object::cast_to<InputEventAction>(*p_event) || Object::cast_to<InputEventShortcut>(*p_event))) { if (!get_parent() || !is_visible_in_tree()) { return; } diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp index 4e80d7a2d6..868383b141 100644 --- a/scene/gui/menu_button.cpp +++ b/scene/gui/menu_button.cpp @@ -40,7 +40,7 @@ void MenuButton::shortcut_input(const Ref<InputEvent> &p_event) { return; } - if (p_event->is_pressed() && !p_event->is_echo() && !is_disabled() && is_visible_in_tree() && popup->activate_item_by_event(p_event, false)) { + if (p_event->is_pressed() && !is_disabled() && is_visible_in_tree() && popup->activate_item_by_event(p_event, false)) { accept_event(); return; } diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index 8138a66f64..2b8c85c823 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -78,6 +78,7 @@ void OptionButton::_update_theme_item_cache() { void OptionButton::_notification(int p_what) { switch (p_what) { case NOTIFICATION_POSTINITIALIZE: { + _refresh_size_cache(); if (has_theme_icon(SNAME("arrow"))) { if (is_layout_rtl()) { _set_internal_margin(SIDE_LEFT, theme_cache.arrow_icon->get_width()); @@ -446,7 +447,7 @@ void OptionButton::_refresh_size_cache() { cache_refresh_pending = false; if (fit_to_longest_item) { - _cached_size = Vector2(); + _cached_size = theme_cache.normal->get_minimum_size(); for (int i = 0; i < get_item_count(); i++) { _cached_size = _cached_size.max(get_minimum_size_for_text_and_icon(popup->get_item_xl_text(i), get_item_icon(i))); } @@ -595,7 +596,6 @@ OptionButton::OptionButton(const String &p_text) : popup->connect("index_pressed", callable_mp(this, &OptionButton::_selected)); popup->connect("id_focused", callable_mp(this, &OptionButton::_focused)); popup->connect("popup_hide", callable_mp((BaseButton *)this, &BaseButton::set_pressed).bind(false)); - _refresh_size_cache(); } OptionButton::~OptionButton() { diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp index c0a2dc81d0..6915f3d242 100644 --- a/scene/gui/popup.cpp +++ b/scene/gui/popup.cpp @@ -35,8 +35,7 @@ #include "scene/gui/panel.h" void Popup::_input_from_window(const Ref<InputEvent> &p_event) { - Ref<InputEventKey> key = p_event; - if (get_flag(FLAG_POPUP) && key.is_valid() && key->is_action_pressed(SNAME("ui_cancel"), false, true)) { + if (get_flag(FLAG_POPUP) && p_event->is_action_pressed(SNAME("ui_cancel"), false, true)) { _close_pressed(); } } diff --git a/scene/gui/popup_menu.compat.inc b/scene/gui/popup_menu.compat.inc new file mode 100644 index 0000000000..ef74a17228 --- /dev/null +++ b/scene/gui/popup_menu.compat.inc @@ -0,0 +1,46 @@ +/**************************************************************************/ +/* popup_menu.compat.inc */ +/**************************************************************************/ +/* 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 DISABLE_DEPRECATED + +void PopupMenu::_add_shortcut_bind_compat_36493(const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) { + return add_shortcut(p_shortcut, p_id, p_global, false); +} + +void PopupMenu::_add_icon_shortcut_bind_compat_36493(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) { + return add_icon_shortcut(p_icon, p_shortcut, p_id, p_global, false); +} + +void PopupMenu::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("add_shortcut", "shortcut", "id", "global"), &PopupMenu::_add_shortcut_bind_compat_36493, DEFVAL(-1), DEFVAL(false)); + ClassDB::bind_compatibility_method(D_METHOD("add_icon_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::_add_icon_shortcut_bind_compat_36493, DEFVAL(-1), DEFVAL(false)); +} + +#endif diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 40db8deaac..4bba33f18e 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -29,6 +29,7 @@ /**************************************************************************/ #include "popup_menu.h" +#include "popup_menu.compat.inc" #include "core/config/project_settings.h" #include "core/input/input.h" @@ -1164,18 +1165,19 @@ void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int _menu_changed(); } -#define ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global) \ +#define ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global, p_allow_echo) \ ERR_FAIL_COND_MSG(p_shortcut.is_null(), "Cannot add item with invalid Shortcut."); \ _ref_shortcut(p_shortcut); \ item.text = p_shortcut->get_name(); \ item.xl_text = atr(item.text); \ item.id = p_id == -1 ? items.size() : p_id; \ item.shortcut = p_shortcut; \ - item.shortcut_is_global = p_global; + item.shortcut_is_global = p_global; \ + item.allow_echo = p_allow_echo; -void PopupMenu::add_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) { +void PopupMenu::add_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_global, bool p_allow_echo) { Item item; - ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global); + ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global, p_allow_echo); items.push_back(item); _shape_item(items.size() - 1); @@ -1185,9 +1187,9 @@ void PopupMenu::add_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_g _menu_changed(); } -void PopupMenu::add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) { +void PopupMenu::add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id, bool p_global, bool p_allow_echo) { Item item; - ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global); + ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global, p_allow_echo); item.icon = p_icon; items.push_back(item); @@ -1200,7 +1202,7 @@ void PopupMenu::add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortc void PopupMenu::add_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) { Item item; - ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global); + ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global, false); // Echo for check shortcuts doesn't make sense. item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); @@ -1213,7 +1215,7 @@ void PopupMenu::add_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bo void PopupMenu::add_icon_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) { Item item; - ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global); + ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global, false); item.icon = p_icon; item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); @@ -1227,7 +1229,7 @@ void PopupMenu::add_icon_check_shortcut(const Ref<Texture2D> &p_icon, const Ref< void PopupMenu::add_radio_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) { Item item; - ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global); + ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global, false); item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; items.push_back(item); @@ -1240,7 +1242,7 @@ void PopupMenu::add_radio_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_ void PopupMenu::add_icon_radio_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) { Item item; - ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global); + ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global, false); item.icon = p_icon; item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; items.push_back(item); @@ -1838,7 +1840,7 @@ bool PopupMenu::activate_item_by_event(const Ref<InputEvent> &p_event, bool p_fo } for (int i = 0; i < items.size(); i++) { - if (is_item_disabled(i) || items[i].shortcut_is_disabled) { + if (is_item_disabled(i) || items[i].shortcut_is_disabled || (!items[i].allow_echo && p_event->is_echo())) { continue; } @@ -2213,8 +2215,8 @@ void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("add_multistate_item", "label", "max_states", "default_state", "id", "accel"), &PopupMenu::add_multistate_item, DEFVAL(0), DEFVAL(-1), DEFVAL(0)); - ClassDB::bind_method(D_METHOD("add_shortcut", "shortcut", "id", "global"), &PopupMenu::add_shortcut, DEFVAL(-1), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("add_icon_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_shortcut, DEFVAL(-1), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("add_shortcut", "shortcut", "id", "global", "allow_echo"), &PopupMenu::add_shortcut, DEFVAL(-1), DEFVAL(false), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("add_icon_shortcut", "texture", "shortcut", "id", "global", "allow_echo"), &PopupMenu::add_icon_shortcut, DEFVAL(-1), DEFVAL(false), DEFVAL(false)); ClassDB::bind_method(D_METHOD("add_check_shortcut", "shortcut", "id", "global"), &PopupMenu::add_check_shortcut, DEFVAL(-1), DEFVAL(false)); ClassDB::bind_method(D_METHOD("add_icon_check_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_check_shortcut, DEFVAL(-1), DEFVAL(false)); ClassDB::bind_method(D_METHOD("add_radio_check_shortcut", "shortcut", "id", "global"), &PopupMenu::add_radio_check_shortcut, DEFVAL(-1), DEFVAL(false)); diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index 5ad9cd4303..ef754315f0 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -74,6 +74,7 @@ class PopupMenu : public Popup { Ref<Shortcut> shortcut; bool shortcut_is_global = false; bool shortcut_is_disabled = false; + bool allow_echo = false; // Returns (0,0) if icon is null. Size2 get_icon_size() const { @@ -199,6 +200,12 @@ protected: void _get_property_list(List<PropertyInfo> *p_list) const; static void _bind_methods(); +#ifndef DISABLE_DEPRECATED + void _add_shortcut_bind_compat_36493(const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false); + void _add_icon_shortcut_bind_compat_36493(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false); + static void _bind_compatibility_methods(); +#endif + public: // ATTENTION: This is used by the POT generator's scene parser. If the number of properties returned by `_get_items()` ever changes, // this value should be updated to reflect the new size. @@ -215,8 +222,8 @@ public: void add_multistate_item(const String &p_label, int p_max_states, int p_default_state = 0, int p_id = -1, Key p_accel = Key::NONE); - void add_shortcut(const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false); - void add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false); + void add_shortcut(const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false, bool p_allow_echo = false); + void add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false, bool p_allow_echo = false); void add_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false); void add_icon_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false); void add_radio_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false); diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 3a0fb6d89c..1e7e376fc8 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -2745,6 +2745,9 @@ void RichTextLabel::_thread_function(void *p_userdata) { void RichTextLabel::_thread_end() { set_physics_process_internal(false); + if (!scroll_visible) { + vscroll->hide(); + } if (is_visible_in_tree()) { queue_redraw(); } @@ -2814,7 +2817,6 @@ _FORCE_INLINE_ float RichTextLabel::_update_scroll_exceeds(float p_total_height, } else { scroll_visible = false; scroll_w = 0; - vscroll->hide(); } main->first_resized_line.store(0); @@ -2862,6 +2864,9 @@ bool RichTextLabel::_validate_line_caches() { if (main->first_resized_line.load() == (int)main->lines.size()) { vscroll->set_value(old_scroll); validating.store(false); + if (!scroll_visible) { + vscroll->hide(); + } return true; } @@ -2881,6 +2886,9 @@ bool RichTextLabel::_validate_line_caches() { update_minimum_size(); } validating.store(false); + if (!scroll_visible) { + vscroll->hide(); + } return true; } validating.store(false); @@ -2896,6 +2904,9 @@ bool RichTextLabel::_validate_line_caches() { updating.store(true); _process_line_caches(); updating.store(false); + if (!scroll_visible) { + vscroll->hide(); + } queue_redraw(); return true; } diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 46bf5641bc..0d0917a9d8 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -256,7 +256,7 @@ private: ListType list_type = LIST_DOTS; bool capitalize = false; int level = 0; - String bullet = String::utf8("•"); + String bullet = U"•"; ItemList() { type = ITEM_LIST; } }; diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp index fcf9302953..06b3882d25 100644 --- a/scene/gui/scroll_bar.cpp +++ b/scene/gui/scroll_bar.cpp @@ -428,6 +428,14 @@ void ScrollBar::_notification(int p_what) { } } break; + case NOTIFICATION_VISIBILITY_CHANGED: { + if (!is_visible()) { + incr_active = false; + decr_active = false; + drag.active = false; + } + } break; + case NOTIFICATION_MOUSE_EXIT: { highlight = HIGHLIGHT_NONE; queue_redraw(); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 7038145c80..3c1be2d5fe 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -1922,7 +1922,8 @@ void Tree::update_column(int p_col) { columns.write[p_col].text_buf->set_direction((TextServer::Direction)columns[p_col].text_direction); } - columns.write[p_col].text_buf->add_string(columns[p_col].title, theme_cache.font, theme_cache.font_size, columns[p_col].language); + columns.write[p_col].text_buf->add_string(columns[p_col].title, theme_cache.tb_font, theme_cache.tb_font_size, columns[p_col].language); + columns.write[p_col].cached_minimum_width_dirty = true; } void Tree::update_item_cell(TreeItem *p_item, int p_col) { @@ -2015,7 +2016,7 @@ void Tree::update_item_cache(TreeItem *p_item) { } } -int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 &p_draw_size, TreeItem *p_item, int *r_self_height) { +int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 &p_draw_size, TreeItem *p_item, int &r_self_height) { if (p_pos.y - theme_cache.offset.y > (p_draw_size.height)) { return -1; //draw no more! } @@ -2097,11 +2098,12 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 p_item->cells.write[i].text_buf->set_width(item_width); - label_h = compute_item_height(p_item); - if (r_self_height != nullptr) { - *r_self_height = label_h; + r_self_height = compute_item_height(p_item); + label_h = r_self_height + theme_cache.v_separation; + + if (p_pos.y + label_h - theme_cache.offset.y < 0) { + continue; // No need to draw. } - label_h += theme_cache.v_separation; Rect2i item_rect = Rect2i(Point2i(ofs, p_pos.y) - theme_cache.offset + p_draw_ofs, Size2i(item_width, label_h)); Rect2i cell_rect = item_rect; @@ -2450,7 +2452,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 int child_h = -1; int child_self_height = 0; if (htotal >= 0) { - child_h = draw_item(children_pos, p_draw_ofs, p_draw_size, c, &child_self_height); + child_h = draw_item(children_pos, p_draw_ofs, p_draw_size, c, child_self_height); child_self_height += theme_cache.v_separation; } @@ -4106,7 +4108,7 @@ void Tree::update_scrollbars() { } int Tree::_get_title_button_height() const { - ERR_FAIL_COND_V(theme_cache.font.is_null() || theme_cache.title_button.is_null(), 0); + ERR_FAIL_COND_V(theme_cache.tb_font.is_null() || theme_cache.title_button.is_null(), 0); int h = 0; if (show_column_titles) { for (int i = 0; i < columns.size(); i++) { @@ -4232,7 +4234,8 @@ void Tree::_notification(int p_what) { cache.rtl = is_layout_rtl(); if (root && get_size().x > 0 && get_size().y > 0) { - draw_item(Point2(), draw_ofs, draw_size, root); + int self_height = 0; // Just to pass a reference, we don't need the root's `self_height`. + draw_item(Point2(), draw_ofs, draw_size, root, self_height); } if (show_column_titles) { @@ -4240,7 +4243,6 @@ void Tree::_notification(int p_what) { int ofs2 = theme_cache.panel_style->get_margin(SIDE_LEFT); for (int i = 0; i < columns.size(); i++) { Ref<StyleBox> sb = (cache.click_type == Cache::CLICK_TITLE && cache.click_index == i) ? theme_cache.title_button_pressed : ((cache.hover_type == Cache::CLICK_TITLE && cache.hover_index == i) ? theme_cache.title_button_hover : theme_cache.title_button); - Ref<Font> f = theme_cache.tb_font; Rect2 tbrect = Rect2(ofs2 - theme_cache.offset.x, bg->get_margin(SIDE_TOP), get_column_width(i), tbh); if (cache.rtl) { tbrect.position.x = get_size().width - tbrect.size.x - tbrect.position.x; @@ -4250,6 +4252,7 @@ void Tree::_notification(int p_what) { //text int clip_w = tbrect.size.width - sb->get_minimum_size().width; columns.write[i].text_buf->set_width(clip_w); + columns.write[i].cached_minimum_width_dirty = true; Vector2 text_pos = Point2i(tbrect.position.x, tbrect.position.y + (tbrect.size.height - columns[i].text_buf->get_size().y) / 2); switch (columns[i].title_alignment) { @@ -4387,6 +4390,7 @@ void Tree::item_edited(int p_column, TreeItem *p_item, MouseButton p_custom_mous void Tree::item_changed(int p_column, TreeItem *p_item) { if (p_item != nullptr && p_column >= 0 && p_column < p_item->cells.size()) { p_item->cells.write[p_column].dirty = true; + columns.write[p_column].cached_minimum_width_dirty = true; } queue_redraw(); } @@ -4518,6 +4522,7 @@ void Tree::set_column_custom_minimum_width(int p_column, int p_min_width) { return; } columns.write[p_column].custom_min_width = p_min_width; + columns.write[p_column].cached_minimum_width_dirty = true; queue_redraw(); } @@ -4529,6 +4534,7 @@ void Tree::set_column_expand(int p_column, bool p_expand) { } columns.write[p_column].expand = p_expand; + columns.write[p_column].cached_minimum_width_dirty = true; queue_redraw(); } @@ -4540,6 +4546,7 @@ void Tree::set_column_expand_ratio(int p_column, int p_ratio) { } columns.write[p_column].expand_ratio = p_ratio; + columns.write[p_column].cached_minimum_width_dirty = true; queue_redraw(); } @@ -4551,6 +4558,7 @@ void Tree::set_column_clip_content(int p_column, bool p_fit) { } columns.write[p_column].clip_content = p_fit; + columns.write[p_column].cached_minimum_width_dirty = true; queue_redraw(); } @@ -4633,46 +4641,51 @@ TreeItem *Tree::get_next_selected(TreeItem *p_item) { int Tree::get_column_minimum_width(int p_column) const { ERR_FAIL_INDEX_V(p_column, columns.size(), -1); - // Use the custom minimum width. - int min_width = columns[p_column].custom_min_width; + if (columns[p_column].cached_minimum_width_dirty) { + // Use the custom minimum width. + int min_width = columns[p_column].custom_min_width; - // Check if the visible title of the column is wider. - if (show_column_titles) { - min_width = MAX(theme_cache.font->get_string_size(columns[p_column].title, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).width + theme_cache.panel_style->get_margin(SIDE_LEFT) + theme_cache.panel_style->get_margin(SIDE_RIGHT), min_width); - } - - if (!columns[p_column].clip_content) { - int depth = 0; - TreeItem *next; - for (TreeItem *item = get_root(); item; item = next) { - next = item->get_next_visible(); - // Compute the depth in tree. - if (next && p_column == 0) { - if (next->get_parent() == item) { - depth += 1; - } else { - TreeItem *common_parent = item->get_parent(); - while (common_parent != next->get_parent() && common_parent) { - common_parent = common_parent->get_parent(); - depth -= 1; + // Check if the visible title of the column is wider. + if (show_column_titles) { + min_width = MAX(theme_cache.font->get_string_size(columns[p_column].title, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).width + theme_cache.panel_style->get_margin(SIDE_LEFT) + theme_cache.panel_style->get_margin(SIDE_RIGHT), min_width); + } + + if (!columns[p_column].clip_content) { + int depth = 0; + TreeItem *next; + for (TreeItem *item = get_root(); item; item = next) { + next = item->get_next_visible(); + // Compute the depth in tree. + if (next && p_column == 0) { + if (next->get_parent() == item) { + depth += 1; + } else { + TreeItem *common_parent = item->get_parent(); + while (common_parent != next->get_parent() && common_parent) { + common_parent = common_parent->get_parent(); + depth -= 1; + } } } - } - // 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; - } + // 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; + } - // Check if the item is wider. - min_width = MAX(min_width, item_size.width); + // Check if the item is wider. + min_width = MAX(min_width, item_size.width); + } } + + columns.get(p_column).cached_minimum_width = min_width; + columns.get(p_column).cached_minimum_width_dirty = false; } - return min_width; + return columns[p_column].cached_minimum_width; } int Tree::get_column_width(int p_column) const { diff --git a/scene/gui/tree.h b/scene/gui/tree.h index ce4379d7fe..cb00889cb9 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -452,6 +452,10 @@ private: Ref<TextParagraph> text_buf; String language; Control::TextDirection text_direction = Control::TEXT_DIRECTION_INHERITED; + + mutable int cached_minimum_width = 0; + mutable bool cached_minimum_width_dirty = true; + ColumnInfo() { text_buf.instantiate(); } @@ -484,7 +488,7 @@ private: void update_item_cache(TreeItem *p_item); //void draw_item_text(String p_text,const Ref<Texture2D>& p_icon,int p_icon_max_w,bool p_tool,Rect2i p_rect,const Color& p_color); void draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Color &p_color, const Color &p_icon_color, int p_ol_size, const Color &p_ol_color); - int draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 &p_draw_size, TreeItem *p_item, int *r_self_height = nullptr); + int draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 &p_draw_size, TreeItem *p_item, int &r_self_height); void select_single_item(TreeItem *p_selected, TreeItem *p_current, int p_col, TreeItem *p_prev = nullptr, bool *r_in_range = nullptr, bool p_force_deselect = false); int propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int x_limit, bool p_double_click, TreeItem *p_item, MouseButton p_button, const Ref<InputEventWithModifiers> &p_mod); void _line_editor_submit(String p_text); diff --git a/scene/gui/video_stream_player.cpp b/scene/gui/video_stream_player.cpp index f6e558fe57..ac09844128 100644 --- a/scene/gui/video_stream_player.cpp +++ b/scene/gui/video_stream_player.cpp @@ -451,7 +451,7 @@ StringName VideoStreamPlayer::get_bus() const { return bus; } } - return "Master"; + return SceneStringNames::get_singleton()->Master; } void VideoStreamPlayer::_validate_property(PropertyInfo &p_property) const { diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 8d5133311a..3500b2201a 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -1347,6 +1347,10 @@ void Node::_generate_serial_child_name(const Node *p_child, StringName &name) co } } +Node::InternalMode Node::get_internal_mode() const { + return data.internal_mode; +} + void Node::_add_child_nocheck(Node *p_child, const StringName &p_name, InternalMode p_internal_mode) { //add a child node quickly, without name validation diff --git a/scene/main/node.h b/scene/main/node.h index ed8f699d7b..feda200b24 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -386,6 +386,8 @@ public: String get_description() const; void set_name(const String &p_name); + InternalMode get_internal_mode() const; + void add_child(Node *p_child, bool p_force_readable_name = false, InternalMode p_internal = INTERNAL_MODE_DISABLED); void add_sibling(Node *p_sibling, bool p_force_readable_name = false); void remove_child(Node *p_child); diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index db9c1efa68..d8c5e9c007 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -1733,6 +1733,9 @@ SceneTree::SceneTree() { const bool transparent_background = GLOBAL_DEF("rendering/viewport/transparent_background", false); root->set_transparent_background(transparent_background); + const bool use_hdr_2d = GLOBAL_DEF_RST_BASIC("rendering/viewport/hdr_2d", false); + root->set_use_hdr_2d(use_hdr_2d); + const int ssaa_mode = GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "rendering/anti_aliasing/quality/screen_space_aa", PROPERTY_HINT_ENUM, "Disabled (Fastest),FXAA (Fast)"), 0); root->set_screen_space_aa(Viewport::ScreenSpaceAA(ssaa_mode)); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 94e5c07552..41034466f9 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1085,7 +1085,7 @@ void Viewport::enable_canvas_transform_override(bool p_enable) { } } -bool Viewport::is_canvas_transform_override_enbled() const { +bool Viewport::is_canvas_transform_override_enabled() const { ERR_READ_THREAD_GUARD_V(false); return override_canvas_transform; } @@ -1177,6 +1177,17 @@ bool Viewport::has_transparent_background() const { return transparent_bg; } +void Viewport::set_use_hdr_2d(bool p_enable) { + ERR_MAIN_THREAD_GUARD; + use_hdr_2d = p_enable; + RS::get_singleton()->viewport_set_use_hdr_2d(viewport, p_enable); +} + +bool Viewport::is_using_hdr_2d() const { + ERR_READ_THREAD_GUARD_V(false); + return use_hdr_2d; +} + void Viewport::set_world_2d(const Ref<World2D> &p_world_2d) { ERR_MAIN_THREAD_GUARD; if (world_2d == p_world_2d) { @@ -2184,6 +2195,18 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { return; } + if (p_event->is_action_pressed("ui_cancel")) { + // Cancel tooltip timer or hide tooltip when pressing Escape (this is standard behavior in most applications). + _gui_cancel_tooltip(); + if (gui.tooltip_popup) { + // If a tooltip was hidden, prevent other actions associated with `ui_cancel` from occurring. + // For instance, this prevents the node from being deselected when pressing Escape + // to hide a documentation tooltip in the inspector. + set_input_as_handled(); + return; + } + } + if (gui.key_focus && !gui.key_focus->is_visible_in_tree()) { gui.key_focus->release_focus(); } @@ -2416,6 +2439,7 @@ Window *Viewport::get_base_window() const { return w; } + void Viewport::_gui_remove_focus_for_window(Node *p_window) { if (get_base_window() == p_window) { gui_release_focus(); @@ -2932,7 +2956,7 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) { void Viewport::_update_mouse_over() { // Update gui.mouse_over and gui.subwindow_over in all Viewports. - // Send necessary mouse_enter/mouse_exit signals and the NOTIFICATION_VP_MOUSE_ENTER/NOTIFICATION_VP_MOUSE_EXIT notifications for every Viewport in the SceneTree. + // Send necessary mouse_enter/mouse_exit signals and the MOUSE_ENTER/MOUSE_EXIT notifications for every Viewport in the SceneTree. if (is_attached_in_viewport()) { // Execute this function only, when it is processed by a native Window or a SubViewport, that has no SubViewportContainer as parent. @@ -2986,7 +3010,7 @@ void Viewport::_update_mouse_over(Vector2 p_pos) { } gui.subwindow_over = sw; if (!sw->is_input_disabled()) { - sw->notification(NOTIFICATION_VP_MOUSE_ENTER); + sw->_propagate_window_notification(sw, NOTIFICATION_WM_MOUSE_ENTER); } } if (!sw->is_input_disabled()) { @@ -3633,12 +3657,20 @@ bool Viewport::is_embedding_subwindows() const { return gui.embed_subwindows_hint; } +TypedArray<Window> Viewport::get_embedded_subwindows() const { + TypedArray<Window> windows; + for (int i = 0; i < gui.sub_windows.size(); i++) { + windows.append(gui.sub_windows[i].window); + } + + return windows; +} + void Viewport::subwindow_set_popup_safe_rect(Window *p_window, const Rect2i &p_rect) { int index = _sub_window_find(p_window); ERR_FAIL_COND(index == -1); - SubWindow sw = gui.sub_windows[index]; - sw.parent_safe_rect = p_rect; + gui.sub_windows.write[index].parent_safe_rect = p_rect; } Rect2i Viewport::subwindow_get_popup_safe_rect(Window *p_window) const { @@ -4272,6 +4304,8 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("get_visible_rect"), &Viewport::get_visible_rect); ClassDB::bind_method(D_METHOD("set_transparent_background", "enable"), &Viewport::set_transparent_background); ClassDB::bind_method(D_METHOD("has_transparent_background"), &Viewport::has_transparent_background); + ClassDB::bind_method(D_METHOD("set_use_hdr_2d", "enable"), &Viewport::set_use_hdr_2d); + ClassDB::bind_method(D_METHOD("is_using_hdr_2d"), &Viewport::is_using_hdr_2d); ClassDB::bind_method(D_METHOD("set_msaa_2d", "msaa"), &Viewport::set_msaa_2d); ClassDB::bind_method(D_METHOD("get_msaa_2d"), &Viewport::get_msaa_2d); @@ -4360,6 +4394,7 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("set_embedding_subwindows", "enable"), &Viewport::set_embedding_subwindows); ClassDB::bind_method(D_METHOD("is_embedding_subwindows"), &Viewport::is_embedding_subwindows); + ClassDB::bind_method(D_METHOD("get_embedded_subwindows"), &Viewport::get_embedded_subwindows); ClassDB::bind_method(D_METHOD("set_canvas_cull_mask", "mask"), &Viewport::set_canvas_cull_mask); ClassDB::bind_method(D_METHOD("get_canvas_cull_mask"), &Viewport::get_canvas_cull_mask); @@ -4436,6 +4471,8 @@ void Viewport::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_occlusion_culling"), "set_use_occlusion_culling", "is_using_occlusion_culling"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mesh_lod_threshold", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_mesh_lod_threshold", "get_mesh_lod_threshold"); ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_draw", PROPERTY_HINT_ENUM, "Disabled,Unshaded,Lighting,Overdraw,Wireframe"), "set_debug_draw", "get_debug_draw"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hdr_2d"), "set_use_hdr_2d", "is_using_hdr_2d"); + #ifndef _3D_DISABLED ADD_GROUP("Scaling 3D", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "scaling_3d_mode", PROPERTY_HINT_ENUM, "Bilinear (Fastest),FSR 1.0 (Fast)"), "set_scaling_3d_mode", "get_scaling_3d_mode"); diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 7cfad42119..346dc6af7e 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -243,6 +243,7 @@ private: Rect2 last_vp_rect; bool transparent_bg = false; + bool use_hdr_2d = false; bool gen_mipmaps = false; bool snap_controls_to_pixels = true; @@ -467,7 +468,8 @@ private: SubWindowResize _sub_window_get_resize_margin(Window *p_subwindow, const Point2 &p_point); void _update_mouse_over(); - void _update_mouse_over(Vector2 p_pos); + virtual void _update_mouse_over(Vector2 p_pos); + virtual void _mouse_leave_viewport(); virtual bool _can_consume_input_events() const { return true; } uint64_t event_count = 0; @@ -481,8 +483,6 @@ protected: Size2i _get_size_2d_override() const; bool _is_size_allocated() const; - void _mouse_leave_viewport(); - void _notification(int p_what); void _process_picking(); static void _bind_methods(); @@ -507,7 +507,7 @@ public: Ref<World2D> find_world_2d() const; void enable_canvas_transform_override(bool p_enable); - bool is_canvas_transform_override_enbled() const; + bool is_canvas_transform_override_enabled() const; void set_canvas_transform_override(const Transform2D &p_transform); Transform2D get_canvas_transform_override() const; @@ -526,6 +526,9 @@ public: void set_transparent_background(bool p_enable); bool has_transparent_background() const; + void set_use_hdr_2d(bool p_enable); + bool is_using_hdr_2d() const; + Ref<ViewportTexture> get_texture() const; void set_positional_shadow_atlas_size(int p_size); @@ -650,6 +653,7 @@ public: void set_embedding_subwindows(bool p_embed); bool is_embedding_subwindows() const; + TypedArray<Window> get_embedded_subwindows() const; void subwindow_set_popup_safe_rect(Window *p_window, const Rect2i &p_rect); Rect2i subwindow_get_popup_safe_rect(Window *p_window) const; diff --git a/scene/main/window.cpp b/scene/main/window.cpp index bd51f8eeaf..1af279d94c 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -670,18 +670,29 @@ void Window::_propagate_window_notification(Node *p_node, int p_notification) { void Window::_event_callback(DisplayServer::WindowEvent p_event) { switch (p_event) { case DisplayServer::WINDOW_EVENT_MOUSE_ENTER: { - _propagate_window_notification(this, NOTIFICATION_WM_MOUSE_ENTER); Window *root = get_tree()->get_root(); - DEV_ASSERT(!root->gui.windowmanager_window_over); // Entering a window while a window is hovered should never happen. + if (root->gui.windowmanager_window_over) { +#ifdef DEV_ENABLED + WARN_PRINT_ONCE("Entering a window while a window is hovered should never happen in DisplayServer."); +#endif // DEV_ENABLED + root->gui.windowmanager_window_over->_event_callback(DisplayServer::WINDOW_EVENT_MOUSE_EXIT); + } + _propagate_window_notification(this, NOTIFICATION_WM_MOUSE_ENTER); root->gui.windowmanager_window_over = this; - notification(NOTIFICATION_VP_MOUSE_ENTER); + mouse_in_window = true; if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE)) { DisplayServer::get_singleton()->cursor_set_shape(DisplayServer::CURSOR_ARROW); //restore cursor shape } } break; case DisplayServer::WINDOW_EVENT_MOUSE_EXIT: { Window *root = get_tree()->get_root(); - DEV_ASSERT(root->gui.windowmanager_window_over); // Exiting a window, while no window is hovered should never happen. + if (!root->gui.windowmanager_window_over) { +#ifdef DEV_ENABLED + WARN_PRINT_ONCE("Exiting a window while no window is hovered should never happen in DisplayServer."); +#endif // DEV_ENABLED + return; + } + mouse_in_window = false; root->gui.windowmanager_window_over->_mouse_leave_viewport(); root->gui.windowmanager_window_over = nullptr; _propagate_window_notification(this, NOTIFICATION_WM_MOUSE_EXIT); @@ -991,6 +1002,17 @@ void Window::_update_viewport_size() { float font_oversampling = 1.0; window_transform = Transform2D(); + if (content_scale_stretch == Window::CONTENT_SCALE_STRETCH_INTEGER) { + // We always want to make sure that the content scale factor is a whole + // number, else there will be pixel wobble no matter what. + content_scale_factor = Math::floor(content_scale_factor); + + // A content scale factor of zero is pretty useless. + if (content_scale_factor < 1) { + content_scale_factor = 1; + } + } + if (content_scale_mode == CONTENT_SCALE_MODE_DISABLED || content_scale_size.x == 0 || content_scale_size.y == 0) { font_oversampling = content_scale_factor; final_size = size; @@ -1044,13 +1066,26 @@ void Window::_update_viewport_size() { screen_size = screen_size.floor(); viewport_size = viewport_size.floor(); + if (content_scale_stretch == Window::CONTENT_SCALE_STRETCH_INTEGER) { + Size2i screen_scale = (screen_size / viewport_size).floor(); + int scale_factor = MIN(screen_scale.x, screen_scale.y); + + if (scale_factor < 1) { + scale_factor = 1; + } + + screen_size = viewport_size * scale_factor; + } + Size2 margin; Size2 offset; - if (content_scale_aspect != CONTENT_SCALE_ASPECT_EXPAND && screen_size.x < video_mode.x) { + if (screen_size.x < video_mode.x) { margin.x = Math::round((video_mode.x - screen_size.x) / 2.0); offset.x = Math::round(margin.x * viewport_size.y / screen_size.y); - } else if (content_scale_aspect != CONTENT_SCALE_ASPECT_EXPAND && screen_size.y < video_mode.y) { + } + + if (screen_size.y < video_mode.y) { margin.y = Math::round((video_mode.y - screen_size.y) / 2.0); offset.y = Math::round(margin.y * viewport_size.x / screen_size.x); } @@ -1327,6 +1362,15 @@ Window::ContentScaleAspect Window::get_content_scale_aspect() const { return content_scale_aspect; } +void Window::set_content_scale_stretch(ContentScaleStretch p_stretch) { + content_scale_stretch = p_stretch; + _update_viewport_size(); +} + +Window::ContentScaleStretch Window::get_content_scale_stretch() const { + return content_scale_stretch; +} + void Window::set_content_scale_factor(real_t p_factor) { ERR_MAIN_THREAD_GUARD; ERR_FAIL_COND(p_factor <= 0); @@ -1748,6 +1792,10 @@ Rect2i Window::fit_rect_in_parent(Rect2i p_rect, const Rect2i &p_parent_rect) co Size2 Window::get_contents_minimum_size() const { ERR_READ_THREAD_GUARD_V(Size2()); + Vector2 ms; + if (GDVIRTUAL_CALL(_get_contents_minimum_size, ms)) { + return ms; + } return _get_contents_minimum_size(); } @@ -2505,6 +2553,41 @@ bool Window::is_attached_in_viewport() const { return get_embedder(); } +void Window::_update_mouse_over(Vector2 p_pos) { + if (!mouse_in_window) { + if (is_embedded()) { + mouse_in_window = true; + _propagate_window_notification(this, NOTIFICATION_WM_MOUSE_ENTER); + } else { + // Prevent update based on delayed InputEvents from DisplayServer. + return; + } + } + + bool new_in = get_visible_rect().has_point(p_pos); + if (new_in == gui.mouse_in_viewport) { + if (new_in) { + Viewport::_update_mouse_over(p_pos); + } + return; + } + + if (new_in) { + notification(NOTIFICATION_VP_MOUSE_ENTER); + Viewport::_update_mouse_over(p_pos); + } else { + Viewport::_mouse_leave_viewport(); + } +} + +void Window::_mouse_leave_viewport() { + Viewport::_mouse_leave_viewport(); + if (is_embedded()) { + mouse_in_window = false; + _propagate_window_notification(this, NOTIFICATION_WM_MOUSE_EXIT); + } +} + void Window::_bind_methods() { ClassDB::bind_method(D_METHOD("set_title", "title"), &Window::set_title); ClassDB::bind_method(D_METHOD("get_title"), &Window::get_title); @@ -2579,6 +2662,9 @@ void Window::_bind_methods() { ClassDB::bind_method(D_METHOD("set_content_scale_aspect", "aspect"), &Window::set_content_scale_aspect); ClassDB::bind_method(D_METHOD("get_content_scale_aspect"), &Window::get_content_scale_aspect); + ClassDB::bind_method(D_METHOD("set_content_scale_stretch", "stretch"), &Window::set_content_scale_stretch); + ClassDB::bind_method(D_METHOD("get_content_scale_stretch"), &Window::get_content_scale_stretch); + ClassDB::bind_method(D_METHOD("set_content_scale_factor", "factor"), &Window::set_content_scale_factor); ClassDB::bind_method(D_METHOD("get_content_scale_factor"), &Window::get_content_scale_factor); @@ -2697,7 +2783,8 @@ void Window::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "content_scale_size"), "set_content_scale_size", "get_content_scale_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_mode", PROPERTY_HINT_ENUM, "Disabled,Canvas Items,Viewport"), "set_content_scale_mode", "get_content_scale_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_aspect", PROPERTY_HINT_ENUM, "Ignore,Keep,Keep Width,Keep Height,Expand"), "set_content_scale_aspect", "get_content_scale_aspect"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "content_scale_factor"), "set_content_scale_factor", "get_content_scale_factor"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_stretch", PROPERTY_HINT_ENUM, "Fractional,Integer"), "set_content_scale_stretch", "get_content_scale_stretch"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "content_scale_factor", PROPERTY_HINT_RANGE, "0.5,8.0,0.01"), "set_content_scale_factor", "get_content_scale_factor"); ADD_GROUP("Localization", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate"), "set_auto_translate", "is_auto_translating"); @@ -2749,6 +2836,9 @@ void Window::_bind_methods() { BIND_ENUM_CONSTANT(CONTENT_SCALE_ASPECT_KEEP_HEIGHT); BIND_ENUM_CONSTANT(CONTENT_SCALE_ASPECT_EXPAND); + BIND_ENUM_CONSTANT(CONTENT_SCALE_STRETCH_FRACTIONAL); + BIND_ENUM_CONSTANT(CONTENT_SCALE_STRETCH_INTEGER); + BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_INHERITED); BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_LOCALE); BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_LTR); @@ -2760,6 +2850,8 @@ void Window::_bind_methods() { BIND_ENUM_CONSTANT(WINDOW_INITIAL_POSITION_CENTER_OTHER_SCREEN); BIND_ENUM_CONSTANT(WINDOW_INITIAL_POSITION_CENTER_SCREEN_WITH_MOUSE_FOCUS); BIND_ENUM_CONSTANT(WINDOW_INITIAL_POSITION_CENTER_SCREEN_WITH_KEYBOARD_FOCUS); + + GDVIRTUAL_BIND(_get_contents_minimum_size); } Window::Window() { diff --git a/scene/main/window.h b/scene/main/window.h index 24142b8a91..c387ffa92a 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -78,6 +78,11 @@ public: CONTENT_SCALE_ASPECT_EXPAND, }; + enum ContentScaleStretch { + CONTENT_SCALE_STRETCH_FRACTIONAL, + CONTENT_SCALE_STRETCH_INTEGER, + }; + enum LayoutDirection { LAYOUT_DIRECTION_INHERITED, LAYOUT_DIRECTION_LOCALE, @@ -135,6 +140,7 @@ private: Size2i content_scale_size; ContentScaleMode content_scale_mode = CONTENT_SCALE_MODE_DISABLED; ContentScaleAspect content_scale_aspect = CONTENT_SCALE_ASPECT_IGNORE; + ContentScaleStretch content_scale_stretch = CONTENT_SCALE_STRETCH_FRACTIONAL; real_t content_scale_factor = 1.0; void _make_window(); @@ -197,6 +203,10 @@ private: void _event_callback(DisplayServer::WindowEvent p_event); virtual bool _can_consume_input_events() const override; + bool mouse_in_window = false; + void _update_mouse_over(Vector2 p_pos) override; + void _mouse_leave_viewport() override; + Ref<Shortcut> debugger_stop_shortcut; protected: @@ -205,7 +215,6 @@ protected: virtual void _update_theme_item_cache(); virtual void _post_popup() {} - virtual Size2 _get_contents_minimum_size() const; static void _bind_methods(); void _notification(int p_what); @@ -217,6 +226,8 @@ protected: virtual void add_child_notify(Node *p_child) override; virtual void remove_child_notify(Node *p_child) override; + GDVIRTUAL0RC(Vector2, _get_contents_minimum_size) + public: enum { NOTIFICATION_VISIBILITY_CHANGED = 30, @@ -298,6 +309,9 @@ public: void set_content_scale_aspect(ContentScaleAspect p_aspect); ContentScaleAspect get_content_scale_aspect() const; + void set_content_scale_stretch(ContentScaleStretch p_stretch); + ContentScaleStretch get_content_scale_stretch() const; + void set_content_scale_factor(real_t p_factor); real_t get_content_scale_factor() const; @@ -409,6 +423,8 @@ public: Rect2i get_parent_rect() const; virtual DisplayServer::WindowID get_window_id() const override; + virtual Size2 _get_contents_minimum_size() const; + Window(); ~Window(); }; @@ -417,6 +433,7 @@ VARIANT_ENUM_CAST(Window::Mode); VARIANT_ENUM_CAST(Window::Flags); VARIANT_ENUM_CAST(Window::ContentScaleMode); VARIANT_ENUM_CAST(Window::ContentScaleAspect); +VARIANT_ENUM_CAST(Window::ContentScaleStretch); VARIANT_ENUM_CAST(Window::LayoutDirection); VARIANT_ENUM_CAST(Window::WindowInitialPosition); diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index b6a1737acb..eef46a6798 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -183,7 +183,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("icon_focus_color", "Button", Color(1, 1, 1, 1)); theme->set_color("icon_disabled_color", "Button", Color(1, 1, 1, 0.4)); - theme->set_constant("h_separation", "Button", Math::round(2 * scale)); + theme->set_constant("h_separation", "Button", Math::round(4 * scale)); theme->set_constant("icon_max_width", "Button", 0); // MenuBar @@ -259,7 +259,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_disabled_color", "OptionButton", control_font_disabled_color); theme->set_color("font_outline_color", "OptionButton", Color(1, 1, 1)); - theme->set_constant("h_separation", "OptionButton", Math::round(2 * scale)); + theme->set_constant("h_separation", "OptionButton", Math::round(4 * scale)); theme->set_constant("arrow_margin", "OptionButton", Math::round(4 * scale)); theme->set_constant("outline_size", "OptionButton", 0); theme->set_constant("modulate_arrow", "OptionButton", false); @@ -282,7 +282,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_disabled_color", "MenuButton", Color(1, 1, 1, 0.3)); theme->set_color("font_outline_color", "MenuButton", Color(1, 1, 1)); - theme->set_constant("h_separation", "MenuButton", Math::round(3 * scale)); + theme->set_constant("h_separation", "MenuButton", Math::round(4 * scale)); theme->set_constant("outline_size", "MenuButton", 0); // CheckBox @@ -762,6 +762,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_font("title_button_font", "Tree", Ref<Font>()); theme->set_font("font", "Tree", Ref<Font>()); theme->set_font_size("font_size", "Tree", -1); + theme->set_font_size("title_button_font_size", "Tree", -1); theme->set_color("title_button_color", "Tree", control_font_color); theme->set_color("font_color", "Tree", control_font_low_color); @@ -788,7 +789,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_constant("children_hl_line_width", "Tree", 1); theme->set_constant("parent_hl_line_margin", "Tree", 0); theme->set_constant("draw_guides", "Tree", 1); - theme->set_constant("scroll_border", "Tree", 4); + theme->set_constant("scroll_border", "Tree", Math::round(4 * scale)); theme->set_constant("scroll_speed", "Tree", 12); theme->set_constant("outline_size", "Tree", 0); theme->set_constant("icon_max_width", "Tree", 0); @@ -803,9 +804,9 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("panel", "ItemList", make_flat_stylebox(style_normal_color)); theme->set_stylebox("focus", "ItemList", focus); - theme->set_constant("h_separation", "ItemList", 4); - theme->set_constant("v_separation", "ItemList", 2); - theme->set_constant("icon_margin", "ItemList", 4); + theme->set_constant("h_separation", "ItemList", Math::round(4 * scale)); + theme->set_constant("v_separation", "ItemList", Math::round(2 * scale)); + theme->set_constant("icon_margin", "ItemList", Math::round(4 * scale)); theme->set_constant("line_separation", "ItemList", Math::round(2 * scale)); theme->set_font("font", "ItemList", Ref<Font>()); @@ -1005,7 +1006,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_disabled_color", "ColorPickerButton", Color(0.9, 0.9, 0.9, 0.3)); theme->set_color("font_outline_color", "ColorPickerButton", Color(1, 1, 1)); - theme->set_constant("h_separation", "ColorPickerButton", Math::round(2 * scale)); + theme->set_constant("h_separation", "ColorPickerButton", Math::round(4 * scale)); theme->set_constant("outline_size", "ColorPickerButton", 0); // ColorPresetButton diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index e48f744c72..abe1561310 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -1446,7 +1446,7 @@ void Environment::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_emission_energy", PROPERTY_HINT_RANGE, "0,1024,0.01,or_greater"), "set_volumetric_fog_emission_energy", "get_volumetric_fog_emission_energy"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_gi_inject", PROPERTY_HINT_RANGE, "0.0,16,0.01,exp"), "set_volumetric_fog_gi_inject", "get_volumetric_fog_gi_inject"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_anisotropy", PROPERTY_HINT_RANGE, "-0.9,0.9,0.01"), "set_volumetric_fog_anisotropy", "get_volumetric_fog_anisotropy"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_length", PROPERTY_HINT_RANGE, "0,1024,0.01,or_greater"), "set_volumetric_fog_length", "get_volumetric_fog_length"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_length", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:m"), "set_volumetric_fog_length", "get_volumetric_fog_length"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_detail_spread", PROPERTY_HINT_EXP_EASING, "positive_only"), "set_volumetric_fog_detail_spread", "get_volumetric_fog_detail_spread"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_ambient_inject", PROPERTY_HINT_RANGE, "0.0,16,0.01,exp"), "set_volumetric_fog_ambient_inject", "get_volumetric_fog_ambient_inject"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_sky_affect", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_volumetric_fog_sky_affect", "get_volumetric_fog_sky_affect"); diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 09a835db1b..df0bd8bc2e 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -97,6 +97,8 @@ void Font::_bind_methods() { ClassDB::bind_method(D_METHOD("get_supported_feature_list"), &Font::get_supported_feature_list); ClassDB::bind_method(D_METHOD("get_supported_variation_list"), &Font::get_supported_variation_list); ClassDB::bind_method(D_METHOD("get_face_count"), &Font::get_face_count); + + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("Font")), "set_fallbacks", "get_fallbacks"); } void Font::_update_rids_fb(const Ref<Font> &p_f, int p_depth) const { @@ -1006,7 +1008,12 @@ void FontFile::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_oversampling", "get_oversampling"); ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_fixed_size", "get_fixed_size"); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "opentype_feature_overrides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_opentype_feature_overrides", "get_opentype_feature_overrides"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("Font"), PROPERTY_USAGE_STORAGE), "set_fallbacks", "get_fallbacks"); +} + +void FontFile::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "fallbacks") { + p_property.usage &= ~PROPERTY_USAGE_EDITOR; + } } bool FontFile::_set(const StringName &p_name, const Variant &p_value) { @@ -2634,7 +2641,6 @@ void FontVariation::_bind_methods() { ClassDB::bind_method(D_METHOD("set_spacing", "spacing", "value"), &FontVariation::set_spacing); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "base_font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_base_font", "get_base_font"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("Font")), "set_fallbacks", "get_fallbacks"); ADD_GROUP("Variation", "variation_"); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "variation_opentype"), "set_variation_opentype", "get_variation_opentype"); @@ -2924,7 +2930,6 @@ void SystemFont::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_pixel_range"), "set_msdf_pixel_range", "get_msdf_pixel_range"); ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_size"), "set_msdf_size", "get_msdf_size"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "0,10,0.1"), "set_oversampling", "get_oversampling"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("Font")), "set_fallbacks", "get_fallbacks"); } void SystemFont::_update_rids() const { diff --git a/scene/resources/font.h b/scene/resources/font.h index 5d600451a1..d8374c4447 100644 --- a/scene/resources/font.h +++ b/scene/resources/font.h @@ -210,6 +210,7 @@ class FontFile : public Font { protected: static void _bind_methods(); + void _validate_property(PropertyInfo &p_property) const; bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; diff --git a/scene/resources/particle_process_material.cpp b/scene/resources/particle_process_material.cpp index 745c71626c..72d38ec8ce 100644 --- a/scene/resources/particle_process_material.cpp +++ b/scene/resources/particle_process_material.cpp @@ -683,11 +683,13 @@ void ParticleProcessMaterial::_update_shader() { code += " pos.z = 0.0;\n"; } code += " // apply linear acceleration\n"; - code += " force += length(VELOCITY) > 0.0 ? normalize(VELOCITY) * tex_linear_accel * mix(linear_accel_min, linear_accel_max, rand_from_seed(alt_seed)) : vec3(0.0);\n"; + code += " float linear_accel_rand = rand_from_seed(alt_seed);\n"; + code += " force += length(VELOCITY) > 0.0 ? normalize(VELOCITY) * tex_linear_accel * mix(linear_accel_min, linear_accel_max, linear_accel_rand) : vec3(0.0);\n"; code += " // apply radial acceleration\n"; code += " vec3 org = EMISSION_TRANSFORM[3].xyz;\n"; code += " vec3 diff = pos - org;\n"; - code += " force += length(diff) > 0.0 ? normalize(diff) * tex_radial_accel * mix(radial_accel_min, radial_accel_max, rand_from_seed(alt_seed)) : vec3(0.0);\n"; + code += " float radial_accel_rand = rand_from_seed(alt_seed);\n"; + code += " force += length(diff) > 0.0 ? normalize(diff) * tex_radial_accel * mix(radial_accel_min, radial_accel_max, radial_accel_rand) : vec3(0.0);\n"; code += " // apply tangential acceleration;\n"; code += " float tangent_accel_val = tex_tangent_accel * mix(tangent_accel_min, tangent_accel_max, rand_from_seed(alt_seed));\n"; if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index 6430a1302d..a64ae07f05 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -2066,8 +2066,8 @@ void TorusMesh::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inner_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_inner_radius", "get_inner_radius"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "outer_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_outer_radius", "get_outer_radius"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "3,128,1"), "set_rings", "get_rings"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "ring_segments", PROPERTY_HINT_RANGE, "3,64,1"), "set_ring_segments", "get_ring_segments"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "3,128,1,or_greater"), "set_rings", "get_rings"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "ring_segments", PROPERTY_HINT_RANGE, "3,64,1,or_greater"), "set_ring_segments", "get_ring_segments"); } void TorusMesh::set_inner_radius(const float p_inner_radius) { diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp index 87835a9522..cf8ece0d4c 100644 --- a/scene/scene_string_names.cpp +++ b/scene/scene_string_names.cpp @@ -193,12 +193,16 @@ SceneStringNames::SceneStringNames() { path_pp = NodePath(".."); + // Audio bus name. + Master = StaticCString::create("Master"); + _default = StaticCString::create("default"); _window_group = StaticCString::create("_window_group"); _window_input = StaticCString::create("_window_input"); window_input = StaticCString::create("window_input"); _window_unhandled_input = StaticCString::create("_window_unhandled_input"); + _get_contents_minimum_size = StaticCString::create("_get_contents_minimum_size"); theme_changed = StaticCString::create("theme_changed"); parameters_base_path = "parameters/"; diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h index ad1135e24c..10b71e2a2a 100644 --- a/scene/scene_string_names.h +++ b/scene/scene_string_names.h @@ -204,12 +204,15 @@ public: StringName output; + StringName Master; + StringName parameters_base_path; StringName _window_group; StringName _window_input; StringName _window_unhandled_input; StringName window_input; + StringName _get_contents_minimum_size; StringName theme_changed; StringName shader_overrides_group; diff --git a/scu_builders.py b/scu_builders.py index 5f7821655b..e76a58bd88 100644 --- a/scu_builders.py +++ b/scu_builders.py @@ -8,8 +8,8 @@ from os.path import normpath, basename base_folder_path = str(Path(__file__).parent) + "/" base_folder_only = os.path.basename(os.path.normpath(base_folder_path)) _verbose = False -_is_release_build = False _scu_folders = set() +_max_includes_per_scu = 1024 def clear_out_existing_files(output_folder, extension): @@ -197,13 +197,14 @@ def process_folder(folders, sought_exceptions=[], includes_per_scu=0, extension= # adjust number of output files according to whether DEV or release num_output_files = 1 - if _is_release_build: - # always have a maximum in release - includes_per_scu = 8 - num_output_files = max(math.ceil(total_lines / float(includes_per_scu)), 1) + + if includes_per_scu == 0: + includes_per_scu = _max_includes_per_scu else: - if includes_per_scu > 0: - num_output_files = max(math.ceil(total_lines / float(includes_per_scu)), 1) + if includes_per_scu > _max_includes_per_scu: + includes_per_scu = _max_includes_per_scu + + num_output_files = max(math.ceil(total_lines / float(includes_per_scu)), 1) lines_per_file = math.ceil(total_lines / float(num_output_files)) lines_per_file = max(lines_per_file, 1) @@ -241,15 +242,15 @@ def process_folder(folders, sought_exceptions=[], includes_per_scu=0, extension= ) -def generate_scu_files(verbose, is_release_build): +def generate_scu_files(verbose, max_includes_per_scu): print("=============================") print("Single Compilation Unit Build") print("=============================") - print("Generating SCU build files") global _verbose _verbose = verbose - global _is_release_build - _is_release_build = is_release_build + global _max_includes_per_scu + _max_includes_per_scu = max_includes_per_scu + print("Generating SCU build files... (max includes per scu " + str(_max_includes_per_scu) + ")") curr_folder = os.path.abspath("./") diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp index 49991e41d3..2308fcca9e 100644 --- a/servers/audio_server.cpp +++ b/servers/audio_server.cpp @@ -40,6 +40,7 @@ #include "core/string/string_name.h" #include "core/templates/pair.h" #include "scene/resources/audio_stream_wav.h" +#include "scene/scene_string_names.h" #include "servers/audio/audio_driver_dummy.h" #include "servers/audio/effects/audio_effect_compressor.h" @@ -747,7 +748,7 @@ void AudioServer::set_bus_count(int p_count) { buses[i]->bypass = false; buses[i]->volume_db = 0; if (i > 0) { - buses[i]->send = "Master"; + buses[i]->send = SceneStringNames::get_singleton()->Master; } bus_map[attempt] = buses[i]; @@ -1582,7 +1583,7 @@ void AudioServer::set_bus_layout(const Ref<AudioBusLayout> &p_bus_layout) { for (int i = 0; i < p_bus_layout->buses.size(); i++) { Bus *bus = memnew(Bus); if (i == 0) { - bus->name = "Master"; + bus->name = SceneStringNames::get_singleton()->Master; } else { bus->name = p_bus_layout->buses[i].name; bus->send = p_bus_layout->buses[i].send; @@ -1891,5 +1892,5 @@ void AudioBusLayout::_get_property_list(List<PropertyInfo> *p_list) const { AudioBusLayout::AudioBusLayout() { buses.resize(1); - buses.write[0].name = "Master"; + buses.write[0].name = SceneStringNames::get_singleton()->Master; } diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp index 04facdb8d9..75036b935b 100644 --- a/servers/navigation_server_3d.cpp +++ b/servers/navigation_server_3d.cpp @@ -155,6 +155,7 @@ void NavigationServer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("parse_source_geometry_data", "navigation_mesh", "source_geometry_data", "root_node", "callback"), &NavigationServer3D::parse_source_geometry_data, DEFVAL(Callable())); ClassDB::bind_method(D_METHOD("bake_from_source_geometry_data", "navigation_mesh", "source_geometry_data", "callback"), &NavigationServer3D::bake_from_source_geometry_data, DEFVAL(Callable())); + ClassDB::bind_method(D_METHOD("bake_from_source_geometry_data_async", "navigation_mesh", "source_geometry_data", "callback"), &NavigationServer3D::bake_from_source_geometry_data_async, DEFVAL(Callable())); ClassDB::bind_method(D_METHOD("free_rid", "rid"), &NavigationServer3D::free); @@ -204,6 +205,9 @@ NavigationServer3D::NavigationServer3D() { GLOBAL_DEF("navigation/avoidance/thread_model/avoidance_use_multiple_threads", true); GLOBAL_DEF("navigation/avoidance/thread_model/avoidance_use_high_priority_threads", true); + GLOBAL_DEF("navigation/baking/thread_model/baking_use_multiple_threads", true); + GLOBAL_DEF("navigation/baking/thread_model/baking_use_high_priority_threads", true); + #ifdef DEBUG_ENABLED debug_navigation_edge_connection_color = GLOBAL_DEF("debug/shapes/navigation/edge_connection_color", Color(1.0, 0.0, 1.0, 1.0)); debug_navigation_geometry_edge_color = GLOBAL_DEF("debug/shapes/navigation/geometry_edge_color", Color(0.5, 1.0, 1.0, 1.0)); diff --git a/servers/navigation_server_3d.h b/servers/navigation_server_3d.h index 391730e18f..39f147357a 100644 --- a/servers/navigation_server_3d.h +++ b/servers/navigation_server_3d.h @@ -34,7 +34,7 @@ #include "core/object/class_db.h" #include "core/templates/rid.h" -#include "scene/3d/navigation_region_3d.h" +#include "scene/resources/navigation_mesh.h" #include "scene/resources/navigation_mesh_source_geometry_data_3d.h" #include "servers/navigation/navigation_path_query_parameters_3d.h" #include "servers/navigation/navigation_path_query_result_3d.h" @@ -301,14 +301,17 @@ public: /// so this must be called in the main thread. /// Note: This function is not thread safe. virtual void process(real_t delta_time) = 0; + virtual void init() = 0; + virtual void finish() = 0; /// Returns a customized navigation path using a query parameters object virtual void query_path(const Ref<NavigationPathQueryParameters3D> &p_query_parameters, Ref<NavigationPathQueryResult3D> p_query_result) const; virtual NavigationUtilities::PathQueryResult _query_path(const NavigationUtilities::PathQueryParameters &p_parameters) const = 0; - virtual void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) = 0; - virtual void bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0; + virtual void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) = 0; + virtual void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0; + virtual void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0; NavigationServer3D(); ~NavigationServer3D() override; diff --git a/servers/navigation_server_3d_dummy.h b/servers/navigation_server_3d_dummy.h index b2d452f67a..b1ec214bb0 100644 --- a/servers/navigation_server_3d_dummy.h +++ b/servers/navigation_server_3d_dummy.h @@ -145,11 +145,14 @@ public: void obstacle_set_position(RID p_obstacle, Vector3 p_position) override {} void obstacle_set_vertices(RID p_obstacle, const Vector<Vector3> &p_vertices) override {} void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) override {} - void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override {} - void bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {} + void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override {} + void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {} + void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {} void free(RID p_object) override {} void set_active(bool p_active) override {} void process(real_t delta_time) override {} + void init() override {} + void finish() override {} NavigationUtilities::PathQueryResult _query_path(const NavigationUtilities::PathQueryParameters &p_parameters) const override { return NavigationUtilities::PathQueryResult(); } int get_process_info(ProcessInfo p_info) const override { return 0; } void set_debug_enabled(bool p_enabled) {} diff --git a/servers/physics_3d/godot_body_pair_3d.cpp b/servers/physics_3d/godot_body_pair_3d.cpp index a78bd0f888..84fae73616 100644 --- a/servers/physics_3d/godot_body_pair_3d.cpp +++ b/servers/physics_3d/godot_body_pair_3d.cpp @@ -168,7 +168,6 @@ void GodotBodyPair3D::validate_contacts() { // adjust the velocity of A down so that it will just slightly intersect the collider instead of blowing right past it. bool GodotBodyPair3D::_test_ccd(real_t p_step, GodotBody3D *p_A, int p_shape_A, const Transform3D &p_xform_A, GodotBody3D *p_B, int p_shape_B, const Transform3D &p_xform_B) { GodotShape3D *shape_A_ptr = p_A->get_shape(p_shape_A); - GodotShape3D *shape_B_ptr = p_B->get_shape(p_shape_B); Vector3 motion = p_A->get_linear_velocity() * p_step; real_t mlen = motion.length(); @@ -221,7 +220,8 @@ bool GodotBodyPair3D::_test_ccd(real_t p_step, GodotBody3D *p_A, int p_shape_A, Vector3 local_to = from_inv.xform(to); Vector3 rpos, rnorm; - if (shape_B_ptr->intersect_segment(local_from, local_to, rpos, rnorm, true)) { + int fi = -1; + if (p_B->get_shape(p_shape_B)->intersect_segment(local_from, local_to, rpos, rnorm, fi, true)) { float hit_length = local_from.distance_to(rpos); if (hit_length < segment_hit_length) { segment_support_idx = i; diff --git a/servers/physics_3d/godot_collision_solver_3d.cpp b/servers/physics_3d/godot_collision_solver_3d.cpp index 2de1d86de1..db48111eea 100644 --- a/servers/physics_3d/godot_collision_solver_3d.cpp +++ b/servers/physics_3d/godot_collision_solver_3d.cpp @@ -106,7 +106,8 @@ bool GodotCollisionSolver3D::solve_separation_ray(const GodotShape3D *p_shape_A, to = ai.xform(to); Vector3 p, n; - if (!p_shape_B->intersect_segment(from, to, p, n, true)) { + int fi = -1; + if (!p_shape_B->intersect_segment(from, to, p, n, fi, true)) { return false; } diff --git a/servers/physics_3d/godot_shape_3d.cpp b/servers/physics_3d/godot_shape_3d.cpp index a2276d8dec..872d26aff6 100644 --- a/servers/physics_3d/godot_shape_3d.cpp +++ b/servers/physics_3d/godot_shape_3d.cpp @@ -126,7 +126,7 @@ Vector3 GodotWorldBoundaryShape3D::get_support(const Vector3 &p_normal) const { return p_normal * 1e15; } -bool GodotWorldBoundaryShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const { +bool GodotWorldBoundaryShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const { bool inters = plane.intersects_segment(p_begin, p_end, &r_result); if (inters) { r_normal = plane.normal; @@ -207,7 +207,7 @@ void GodotSeparationRayShape3D::get_supports(const Vector3 &p_normal, int p_max, } } -bool GodotSeparationRayShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const { +bool GodotSeparationRayShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const { return false; //simply not possible } @@ -275,7 +275,7 @@ void GodotSphereShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector r_type = FEATURE_POINT; } -bool GodotSphereShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const { +bool GodotSphereShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const { return Geometry3D::segment_intersects_sphere(p_begin, p_end, Vector3(), radius, &r_result, &r_normal); } @@ -417,7 +417,7 @@ void GodotBoxShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector3 * r_supports[0] = point; } -bool GodotBoxShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const { +bool GodotBoxShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const { AABB aabb_ext(-half_extents, half_extents * 2.0); return aabb_ext.intersects_segment(p_begin, p_end, &r_result, &r_normal); @@ -551,7 +551,7 @@ void GodotCapsuleShape3D::get_supports(const Vector3 &p_normal, int p_max, Vecto } } -bool GodotCapsuleShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const { +bool GodotCapsuleShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const { Vector3 norm = (p_end - p_begin).normalized(); real_t min_d = 1e20; @@ -743,7 +743,7 @@ void GodotCylinderShape3D::get_supports(const Vector3 &p_normal, int p_max, Vect } } -bool GodotCylinderShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const { +bool GodotCylinderShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const { return Geometry3D::segment_intersects_cylinder(p_begin, p_end, height, radius, &r_result, &r_normal, 1); } @@ -975,7 +975,7 @@ void GodotConvexPolygonShape3D::get_supports(const Vector3 &p_normal, int p_max, r_type = FEATURE_POINT; } -bool GodotConvexPolygonShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const { +bool GodotConvexPolygonShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const { const Geometry3D::MeshData::Face *faces = mesh.faces.ptr(); int fc = mesh.faces.size(); @@ -1252,7 +1252,7 @@ void GodotFaceShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector3 r_supports[0] = vertex[vert_support_idx]; } -bool GodotFaceShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const { +bool GodotFaceShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const { bool c = Geometry3D::segment_intersects_triangle(p_begin, p_end, vertex[0], vertex[1], vertex[2], &r_result); if (c) { r_normal = Plane(vertex[0], vertex[1], vertex[2]).normal; @@ -1362,12 +1362,14 @@ void GodotConcavePolygonShape3D::_cull_segment(int p_idx, _SegmentCullParams *p_ Vector3 res; Vector3 normal; - if (face->intersect_segment(p_params->from, p_params->to, res, normal, true)) { + int face_index = params_bvh->face_index; + if (face->intersect_segment(p_params->from, p_params->to, res, normal, face_index, true)) { real_t d = p_params->dir.dot(res) - p_params->dir.dot(p_params->from); if ((d > 0) && (d < p_params->min_d)) { p_params->min_d = d; p_params->result = res; p_params->normal = normal; + p_params->face_index = face_index; p_params->collisions++; } } @@ -1381,7 +1383,7 @@ void GodotConcavePolygonShape3D::_cull_segment(int p_idx, _SegmentCullParams *p_ } } -bool GodotConcavePolygonShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const { +bool GodotConcavePolygonShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const { if (faces.size() == 0) { return false; } @@ -1411,6 +1413,7 @@ bool GodotConcavePolygonShape3D::intersect_segment(const Vector3 &p_begin, const if (params.collisions > 0) { r_result = params.result; r_normal = params.normal; + r_face_index = params.face_index; return true; } else { return false; @@ -1734,9 +1737,11 @@ struct _HeightmapGridCullState { _FORCE_INLINE_ bool _heightmap_face_cull_segment(_HeightmapSegmentCullParams &p_params) { Vector3 res; Vector3 normal; - if (p_params.face->intersect_segment(p_params.from, p_params.to, res, normal, true)) { + int fi = -1; + if (p_params.face->intersect_segment(p_params.from, p_params.to, res, normal, fi, true)) { p_params.result = res; p_params.normal = normal; + return true; } @@ -1940,7 +1945,7 @@ bool GodotHeightMapShape3D::_intersect_grid_segment(ProcessFunction &p_process, return false; } -bool GodotHeightMapShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal, bool p_hit_back_faces) const { +bool GodotHeightMapShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const { if (heights.is_empty()) { return false; } diff --git a/servers/physics_3d/godot_shape_3d.h b/servers/physics_3d/godot_shape_3d.h index acc42f2fcb..dbd58ead68 100644 --- a/servers/physics_3d/godot_shape_3d.h +++ b/servers/physics_3d/godot_shape_3d.h @@ -80,7 +80,7 @@ public: virtual Vector3 get_support(const Vector3 &p_normal) const; virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const = 0; virtual Vector3 get_closest_point_to(const Vector3 &p_point) const = 0; - virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal, bool p_hit_back_faces) const = 0; + virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const = 0; virtual bool intersect_point(const Vector3 &p_point) const = 0; virtual Vector3 get_moment_of_inertia(real_t p_mass) const = 0; @@ -126,7 +126,7 @@ public: virtual Vector3 get_support(const Vector3 &p_normal) const override; virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override { r_amount = 0; } - virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override; + virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override; virtual bool intersect_point(const Vector3 &p_point) const override; virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override; virtual Vector3 get_moment_of_inertia(real_t p_mass) const override; @@ -153,7 +153,7 @@ public: virtual Vector3 get_support(const Vector3 &p_normal) const override; virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override; - virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override; + virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override; virtual bool intersect_point(const Vector3 &p_point) const override; virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override; @@ -180,7 +180,7 @@ public: virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const override; virtual Vector3 get_support(const Vector3 &p_normal) const override; virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override; - virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override; + virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override; virtual bool intersect_point(const Vector3 &p_point) const override; virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override; @@ -205,7 +205,7 @@ public: virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const override; virtual Vector3 get_support(const Vector3 &p_normal) const override; virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override; - virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override; + virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override; virtual bool intersect_point(const Vector3 &p_point) const override; virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override; @@ -234,7 +234,7 @@ public: virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const override; virtual Vector3 get_support(const Vector3 &p_normal) const override; virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override; - virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override; + virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override; virtual bool intersect_point(const Vector3 &p_point) const override; virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override; @@ -263,7 +263,7 @@ public: virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const override; virtual Vector3 get_support(const Vector3 &p_normal) const override; virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override; - virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override; + virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override; virtual bool intersect_point(const Vector3 &p_point) const override; virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override; @@ -290,7 +290,7 @@ public: virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const override; virtual Vector3 get_support(const Vector3 &p_normal) const override; virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override; - virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override; + virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override; virtual bool intersect_point(const Vector3 &p_point) const override; virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override; @@ -347,6 +347,7 @@ struct GodotConcavePolygonShape3D : public GodotConcaveShape3D { Vector3 result; Vector3 normal; + int face_index = -1; real_t min_d = 1e20; int collisions = 0; }; @@ -368,7 +369,7 @@ public: virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const override; virtual Vector3 get_support(const Vector3 &p_normal) const override; - virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override; + virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override; virtual bool intersect_point(const Vector3 &p_point) const override; virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override; @@ -431,7 +432,7 @@ public: virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const override; virtual Vector3 get_support(const Vector3 &p_normal) const override; - virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal, bool p_hit_back_faces) const override; + virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override; virtual bool intersect_point(const Vector3 &p_point) const override; virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override; @@ -459,7 +460,7 @@ struct GodotFaceShape3D : public GodotShape3D { virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const override; virtual Vector3 get_support(const Vector3 &p_normal) const override; virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override; - virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override; + virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override; virtual bool intersect_point(const Vector3 &p_point) const override; virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override; @@ -498,7 +499,7 @@ struct GodotMotionShape3D : public GodotShape3D { } virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override { r_amount = 0; } - virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override { return false; } + virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override { return false; } virtual bool intersect_point(const Vector3 &p_point) const override { return false; } virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override { return p_point; } diff --git a/servers/physics_3d/godot_soft_body_3d.cpp b/servers/physics_3d/godot_soft_body_3d.cpp index 5c67807c48..4b35dd1500 100644 --- a/servers/physics_3d/godot_soft_body_3d.cpp +++ b/servers/physics_3d/godot_soft_body_3d.cpp @@ -1266,7 +1266,7 @@ struct _SoftBodyIntersectSegmentInfo { } }; -bool GodotSoftBodyShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const { +bool GodotSoftBodyShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const { _SoftBodyIntersectSegmentInfo query_info; query_info.soft_body = soft_body; query_info.from = p_begin; diff --git a/servers/physics_3d/godot_soft_body_3d.h b/servers/physics_3d/godot_soft_body_3d.h index 20592f3411..e23f4bb9f5 100644 --- a/servers/physics_3d/godot_soft_body_3d.h +++ b/servers/physics_3d/godot_soft_body_3d.h @@ -259,7 +259,7 @@ public: virtual Vector3 get_support(const Vector3 &p_normal) const override { return Vector3(); } virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override { r_amount = 0; } - virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override; + virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override; virtual bool intersect_point(const Vector3 &p_point) const override; virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override; virtual Vector3 get_moment_of_inertia(real_t p_mass) const override { return Vector3(); } diff --git a/servers/physics_3d/godot_space_3d.cpp b/servers/physics_3d/godot_space_3d.cpp index 35f6fa023d..92b5f31e41 100644 --- a/servers/physics_3d/godot_space_3d.cpp +++ b/servers/physics_3d/godot_space_3d.cpp @@ -120,6 +120,7 @@ bool GodotPhysicsDirectSpaceState3D::intersect_ray(const RayParameters &p_parame bool collided = false; Vector3 res_point, res_normal; + int res_face_index = -1; int res_shape = -1; const GodotCollisionObject3D *res_obj = nullptr; real_t min_d = 1e10; @@ -148,6 +149,7 @@ bool GodotPhysicsDirectSpaceState3D::intersect_ray(const RayParameters &p_parame const GodotShape3D *shape = col_obj->get_shape(shape_idx); Vector3 shape_point, shape_normal; + int shape_face_index = -1; if (shape->intersect_point(local_from)) { if (p_parameters.hit_from_inside) { @@ -165,7 +167,7 @@ bool GodotPhysicsDirectSpaceState3D::intersect_ray(const RayParameters &p_parame } } - if (shape->intersect_segment(local_from, local_to, shape_point, shape_normal, p_parameters.hit_back_faces)) { + if (shape->intersect_segment(local_from, local_to, shape_point, shape_normal, shape_face_index, p_parameters.hit_back_faces)) { Transform3D xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx); shape_point = xform.xform(shape_point); @@ -175,6 +177,7 @@ bool GodotPhysicsDirectSpaceState3D::intersect_ray(const RayParameters &p_parame min_d = ld; res_point = shape_point; res_normal = inv_xform.basis.xform_inv(shape_normal).normalized(); + res_face_index = shape_face_index; res_shape = shape_idx; res_obj = col_obj; collided = true; @@ -194,6 +197,7 @@ bool GodotPhysicsDirectSpaceState3D::intersect_ray(const RayParameters &p_parame r_result.collider = nullptr; } r_result.normal = res_normal; + r_result.face_index = res_face_index; r_result.position = res_point; r_result.rid = res_obj->get_self(); r_result.shape = res_shape; diff --git a/servers/physics_server_3d.cpp b/servers/physics_server_3d.cpp index 6b8d3d1af6..8497bc78e2 100644 --- a/servers/physics_server_3d.cpp +++ b/servers/physics_server_3d.cpp @@ -362,6 +362,7 @@ Dictionary PhysicsDirectSpaceState3D::_intersect_ray(const Ref<PhysicsRayQueryPa Dictionary d; d["position"] = result.position; d["normal"] = result.normal; + d["face_index"] = result.face_index; d["collider_id"] = result.collider_id; d["collider"] = result.collider; d["shape"] = result.shape; diff --git a/servers/physics_server_3d.h b/servers/physics_server_3d.h index 33e9f8a7c9..cd96bf15fd 100644 --- a/servers/physics_server_3d.h +++ b/servers/physics_server_3d.h @@ -151,6 +151,7 @@ public: struct RayResult { Vector3 position; Vector3 normal; + int face_index = -1; RID rid; ObjectID collider_id; Object *collider = nullptr; diff --git a/servers/rendering/dummy/storage/texture_storage.h b/servers/rendering/dummy/storage/texture_storage.h index 71a1801de9..d9bde5903c 100644 --- a/servers/rendering/dummy/storage/texture_storage.h +++ b/servers/rendering/dummy/storage/texture_storage.h @@ -177,6 +177,8 @@ public: virtual void render_target_set_as_unused(RID p_render_target) override {} virtual void render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) override {} virtual RS::ViewportMSAA render_target_get_msaa(RID p_render_target) const override { return RS::VIEWPORT_MSAA_DISABLED; } + virtual void render_target_set_use_hdr(RID p_render_target, bool p_use_hdr_2d) override {} + virtual bool render_target_is_using_hdr(RID p_render_target) const override { return false; } virtual void render_target_request_clear(RID p_render_target, const Color &p_clear_color) override {} virtual bool render_target_is_clear_requested(RID p_render_target) override { return false; } diff --git a/servers/rendering/renderer_rd/effects/bokeh_dof.cpp b/servers/rendering/renderer_rd/effects/bokeh_dof.cpp index 971f8b7f6f..cc5031823e 100644 --- a/servers/rendering/renderer_rd/effects/bokeh_dof.cpp +++ b/servers/rendering/renderer_rd/effects/bokeh_dof.cpp @@ -359,11 +359,10 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[BOKEH_GEN_BLUR_SIZE].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_depth_texture), 0); - RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array()); RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant)); - RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u); RD::get_singleton()->draw_list_end(); } @@ -393,11 +392,10 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_base_texture), 0); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture0), 1); - RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array()); RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant)); - RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u); RD::get_singleton()->draw_list_end(); // Pass 2 @@ -418,11 +416,10 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, texture), 0); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, weight), 1); - RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array()); RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant)); - RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u); RD::get_singleton()->draw_list_end(); if (bokeh.push_constant.half_size) { @@ -438,11 +435,10 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_half_texture1), 0); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture3), 1); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 2, u_weight_texture0), 2); - RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array()); RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant)); - RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u); RD::get_singleton()->draw_list_end(); } @@ -471,11 +467,10 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_base_texture), 0); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture0), 1); - RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array()); RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant)); - RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u); RD::get_singleton()->draw_list_end(); if (bokeh.push_constant.half_size) { @@ -491,11 +486,10 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_half_texture0), 0); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture2), 1); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 2, u_weight_texture0), 2); - RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array()); RD::get_singleton()->draw_list_set_push_constant(draw_list, &bokeh.push_constant, sizeof(BokehPushConstant)); - RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u); RD::get_singleton()->draw_list_end(); } else { CopyEffects::get_singleton()->copy_raster(p_buffers.secondary_texture, p_buffers.base_fb); diff --git a/servers/rendering/renderer_rd/effects/copy_effects.cpp b/servers/rendering/renderer_rd/effects/copy_effects.cpp index eba1c145e3..834653f5c2 100644 --- a/servers/rendering/renderer_rd/effects/copy_effects.cpp +++ b/servers/rendering/renderer_rd/effects/copy_effects.cpp @@ -696,11 +696,9 @@ void CopyEffects::gaussian_blur_raster(RID p_source_rd_texture, RID p_dest_textu RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(dest_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0); - RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array()); - RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant)); - RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u); RD::get_singleton()->draw_list_end(); } @@ -803,12 +801,11 @@ void CopyEffects::gaussian_glow_raster(RID p_source_rd_texture, RID p_half_textu RD::Uniform u_auto_exposure(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_auto_exposure })); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_auto_exposure), 1); } - RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array()); blur_raster.push_constant.flags = base_flags | BLUR_FLAG_HORIZONTAL | (p_first_pass ? BLUR_FLAG_GLOW_FIRST_PASS : 0); RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant)); - RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u); RD::get_singleton()->draw_list_end(); blur_mode = BLUR_MODE_GAUSSIAN_GLOW; @@ -820,12 +817,11 @@ void CopyEffects::gaussian_glow_raster(RID p_source_rd_texture, RID p_half_textu draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(dest_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_half_texture), 0); - RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array()); blur_raster.push_constant.flags = base_flags; RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant)); - RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u); RD::get_singleton()->draw_list_end(); } @@ -1055,11 +1051,10 @@ void CopyEffects::cubemap_downsample_raster(RID p_source_cubemap, RID p_dest_fra RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, cubemap_downsampler.raster_pipeline.get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_cubemap), 0); - RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array()); RD::get_singleton()->draw_list_set_push_constant(draw_list, &cubemap_downsampler.push_constant, sizeof(CubemapDownsamplerPushConstant)); - RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u); RD::get_singleton()->draw_list_end(); } @@ -1136,11 +1131,10 @@ void CopyEffects::cubemap_filter_raster(RID p_source_cubemap, RID p_dest_framebu RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, filter.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_cubemap), 0); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, filter.uniform_set, 1); - RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array()); RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(CubemapFilterRasterPushConstant)); - RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u); RD::get_singleton()->draw_list_end(); } @@ -1214,11 +1208,10 @@ void CopyEffects::cubemap_roughness_raster(RID p_source_rd_texture, RID p_dest_f RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, roughness.raster_pipeline.get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0); - RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array()); RD::get_singleton()->draw_list_set_push_constant(draw_list, &roughness.push_constant, sizeof(CubemapRoughnessPushConstant)); - RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u); RD::get_singleton()->draw_list_end(); } @@ -1269,8 +1262,7 @@ void CopyEffects::merge_specular(RID p_dest_framebuffer, RID p_specular, RID p_b RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_reflection), 1); } - RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array()); - RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u); RD::get_singleton()->draw_list_end(); RD::get_singleton()->draw_command_end_label(); diff --git a/servers/rendering/renderer_rd/effects/luminance.cpp b/servers/rendering/renderer_rd/effects/luminance.cpp index 7462282932..3aa5f5706e 100644 --- a/servers/rendering/renderer_rd/effects/luminance.cpp +++ b/servers/rendering/renderer_rd/effects/luminance.cpp @@ -191,11 +191,10 @@ void Luminance::luminance_reduction(RID p_source_texture, const Size2i p_source_ RD::Uniform u_current_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_luminance_buffers->current })); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_current_texture), 1); } - RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array()); RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(LuminanceReduceRasterPushConstant)); - RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u); RD::get_singleton()->draw_list_end(); } } else { diff --git a/servers/rendering/renderer_rd/effects/ss_effects.cpp b/servers/rendering/renderer_rd/effects/ss_effects.cpp index b9346c0201..d123f24865 100644 --- a/servers/rendering/renderer_rd/effects/ss_effects.cpp +++ b/servers/rendering/renderer_rd/effects/ss_effects.cpp @@ -1429,7 +1429,7 @@ void SSEffects::screen_space_reflection(Ref<RenderSceneBuffersRD> p_render_buffe push_constant.camera_z_far = p_projections[v].get_z_far(); push_constant.camera_z_near = p_projections[v].get_z_near(); push_constant.orthogonal = p_projections[v].is_orthogonal(); - push_constant.filter = false; //enabling causes arctifacts + push_constant.filter = false; // Enabling causes artifacts. push_constant.screen_size[0] = p_ssr_buffers.size.x; push_constant.screen_size[1] = p_ssr_buffers.size.y; diff --git a/servers/rendering/renderer_rd/effects/tone_mapper.cpp b/servers/rendering/renderer_rd/effects/tone_mapper.cpp index 821960bb3b..48c6511408 100644 --- a/servers/rendering/renderer_rd/effects/tone_mapper.cpp +++ b/servers/rendering/renderer_rd/effects/tone_mapper.cpp @@ -89,12 +89,12 @@ void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Ton memset(&tonemap.push_constant, 0, sizeof(TonemapPushConstant)); - tonemap.push_constant.use_bcs = p_settings.use_bcs; + tonemap.push_constant.flags |= p_settings.use_bcs ? TONEMAP_FLAG_USE_BCS : 0; tonemap.push_constant.bcs[0] = p_settings.brightness; tonemap.push_constant.bcs[1] = p_settings.contrast; tonemap.push_constant.bcs[2] = p_settings.saturation; - tonemap.push_constant.use_glow = p_settings.use_glow; + tonemap.push_constant.flags |= p_settings.use_glow ? TONEMAP_FLAG_USE_GLOW : 0; tonemap.push_constant.glow_intensity = p_settings.glow_intensity; tonemap.push_constant.glow_map_strength = p_settings.glow_map_strength; tonemap.push_constant.glow_levels[0] = p_settings.glow_levels[0]; // clean this up to just pass by pointer or something @@ -114,19 +114,21 @@ void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Ton } tonemap.push_constant.tonemapper = p_settings.tonemap_mode; - tonemap.push_constant.use_auto_exposure = p_settings.use_auto_exposure; + tonemap.push_constant.flags |= p_settings.use_auto_exposure ? TONEMAP_FLAG_USE_AUTO_EXPOSURE : 0; tonemap.push_constant.exposure = p_settings.exposure; tonemap.push_constant.white = p_settings.white; tonemap.push_constant.auto_exposure_scale = p_settings.auto_exposure_scale; tonemap.push_constant.luminance_multiplier = p_settings.luminance_multiplier; - tonemap.push_constant.use_color_correction = p_settings.use_color_correction; + tonemap.push_constant.flags |= p_settings.use_color_correction ? TONEMAP_FLAG_USE_COLOR_CORRECTION : 0; - tonemap.push_constant.use_fxaa = p_settings.use_fxaa; - tonemap.push_constant.use_debanding = p_settings.use_debanding; + tonemap.push_constant.flags |= p_settings.use_fxaa ? TONEMAP_FLAG_USE_FXAA : 0; + tonemap.push_constant.flags |= p_settings.use_debanding ? TONEMAP_FLAG_USE_DEBANDING : 0; tonemap.push_constant.pixel_size[0] = 1.0 / p_settings.texture_size.x; tonemap.push_constant.pixel_size[1] = 1.0 / p_settings.texture_size.y; + tonemap.push_constant.flags |= p_settings.convert_to_srgb ? TONEMAP_FLAG_CONVERT_TO_SRGB : 0; + if (p_settings.view_count > 1) { // Use MULTIVIEW versions mode += 6; @@ -170,10 +172,9 @@ void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Ton RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_exposure_texture), 1); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 2, u_glow_texture, u_glow_map), 2); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 3, u_color_correction_texture), 3); - RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array()); RD::get_singleton()->draw_list_set_push_constant(draw_list, &tonemap.push_constant, sizeof(TonemapPushConstant)); - RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u); RD::get_singleton()->draw_list_end(); } @@ -185,13 +186,13 @@ void ToneMapper::tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_col memset(&tonemap.push_constant, 0, sizeof(TonemapPushConstant)); - tonemap.push_constant.use_bcs = p_settings.use_bcs; + tonemap.push_constant.flags |= p_settings.use_bcs ? TONEMAP_FLAG_USE_BCS : 0; tonemap.push_constant.bcs[0] = p_settings.brightness; tonemap.push_constant.bcs[1] = p_settings.contrast; tonemap.push_constant.bcs[2] = p_settings.saturation; ERR_FAIL_COND_MSG(p_settings.use_glow, "Glow is not supported when using subpasses."); - tonemap.push_constant.use_glow = p_settings.use_glow; + tonemap.push_constant.flags |= p_settings.use_glow ? TONEMAP_FLAG_USE_GLOW : 0; int mode = p_settings.use_1d_color_correction ? TONEMAP_MODE_SUBPASS_1D_LUT : TONEMAP_MODE_SUBPASS; if (p_settings.view_count > 1) { @@ -200,16 +201,18 @@ void ToneMapper::tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_col } tonemap.push_constant.tonemapper = p_settings.tonemap_mode; - tonemap.push_constant.use_auto_exposure = p_settings.use_auto_exposure; + tonemap.push_constant.flags |= p_settings.use_auto_exposure ? TONEMAP_FLAG_USE_AUTO_EXPOSURE : 0; tonemap.push_constant.exposure = p_settings.exposure; tonemap.push_constant.white = p_settings.white; tonemap.push_constant.auto_exposure_scale = p_settings.auto_exposure_scale; - tonemap.push_constant.use_color_correction = p_settings.use_color_correction; + tonemap.push_constant.flags |= p_settings.use_color_correction ? TONEMAP_FLAG_USE_COLOR_CORRECTION : 0; - tonemap.push_constant.use_debanding = p_settings.use_debanding; + tonemap.push_constant.flags |= p_settings.use_debanding ? TONEMAP_FLAG_USE_DEBANDING : 0; tonemap.push_constant.luminance_multiplier = p_settings.luminance_multiplier; + tonemap.push_constant.flags |= p_settings.convert_to_srgb ? TONEMAP_FLAG_CONVERT_TO_SRGB : 0; + RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); RID default_mipmap_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); @@ -250,8 +253,7 @@ void ToneMapper::tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_col RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, uniform_set_cache->get_cache(shader, 1, u_exposure_texture), 1); // should be set to a default texture, it's ignored RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, uniform_set_cache->get_cache(shader, 2, u_glow_texture, u_glow_map), 2); // should be set to a default texture, it's ignored RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, uniform_set_cache->get_cache(shader, 3, u_color_correction_texture), 3); - RD::get_singleton()->draw_list_bind_index_array(p_subpass_draw_list, material_storage->get_quad_index_array()); RD::get_singleton()->draw_list_set_push_constant(p_subpass_draw_list, &tonemap.push_constant, sizeof(TonemapPushConstant)); - RD::get_singleton()->draw_list_draw(p_subpass_draw_list, true); + RD::get_singleton()->draw_list_draw(p_subpass_draw_list, false, 1u, 3u); } diff --git a/servers/rendering/renderer_rd/effects/tone_mapper.h b/servers/rendering/renderer_rd/effects/tone_mapper.h index afd2f8e401..a1a99f931f 100644 --- a/servers/rendering/renderer_rd/effects/tone_mapper.h +++ b/servers/rendering/renderer_rd/effects/tone_mapper.h @@ -59,14 +59,23 @@ private: TONEMAP_MODE_MAX }; + enum { + TONEMAP_FLAG_USE_BCS = (1 << 0), + TONEMAP_FLAG_USE_GLOW = (1 << 1), + TONEMAP_FLAG_USE_AUTO_EXPOSURE = (1 << 2), + TONEMAP_FLAG_USE_COLOR_CORRECTION = (1 << 3), + TONEMAP_FLAG_USE_FXAA = (1 << 4), + TONEMAP_FLAG_USE_DEBANDING = (1 << 5), + TONEMAP_FLAG_CONVERT_TO_SRGB = (1 << 6), + }; + struct TonemapPushConstant { float bcs[3]; // 12 - 12 - uint32_t use_bcs; // 4 - 16 + uint32_t flags; // 4 - 16 - uint32_t use_glow; // 4 - 20 - uint32_t use_auto_exposure; // 4 - 24 - uint32_t use_color_correction; // 4 - 28 - uint32_t tonemapper; // 4 - 32 + float pixel_size[2]; // 8 - 24 + uint32_t tonemapper; // 4 - 28 + uint32_t pad; // 4 - 32 uint32_t glow_texture_size[2]; // 8 - 40 float glow_intensity; // 4 - 44 @@ -79,10 +88,6 @@ private: float white; // 4 - 88 float auto_exposure_scale; // 4 - 92 float luminance_multiplier; // 4 - 96 - - float pixel_size[2]; // 8 - 104 - uint32_t use_fxaa; // 4 - 108 - uint32_t use_debanding; // 4 - 112 }; /* tonemap actually writes to a framebuffer, which is @@ -141,6 +146,8 @@ public: bool use_debanding = false; Vector2i texture_size; uint32_t view_count = 1; + + bool convert_to_srgb = false; }; void tonemapper(RID p_source_color, RID p_dst_framebuffer, const TonemapSettings &p_settings); diff --git a/servers/rendering/renderer_rd/effects/vrs.cpp b/servers/rendering/renderer_rd/effects/vrs.cpp index 6ec8612029..63c99facdd 100644 --- a/servers/rendering/renderer_rd/effects/vrs.cpp +++ b/servers/rendering/renderer_rd/effects/vrs.cpp @@ -85,9 +85,8 @@ void VRS::copy_vrs(RID p_source_rd_texture, RID p_dest_framebuffer, bool p_multi RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD, Vector<Color>()); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, vrs_shader.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0); - RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array()); // RD::get_singleton()->draw_list_set_push_constant(draw_list, &vrs_shader.push_constant, sizeof(VRSPushConstant)); - RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u); RD::get_singleton()->draw_list_end(); } diff --git a/servers/rendering/renderer_rd/environment/fog.cpp b/servers/rendering/renderer_rd/environment/fog.cpp index 98d826b1f9..8402cc7444 100644 --- a/servers/rendering/renderer_rd/environment/fog.cpp +++ b/servers/rendering/renderer_rd/environment/fog.cpp @@ -143,7 +143,7 @@ Vector3 Fog::fog_volume_get_size(RID p_fog_volume) const { bool Fog::FogMaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) { uniform_set_updated = true; - return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, Fog::get_singleton()->volumetric_fog.shader.version_get_shader(shader_data->version, 0), VolumetricFogShader::FogSet::FOG_SET_MATERIAL, true); + return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, Fog::get_singleton()->volumetric_fog.shader.version_get_shader(shader_data->version, 0), VolumetricFogShader::FogSet::FOG_SET_MATERIAL, true, true); } Fog::FogMaterialData::~FogMaterialData() { diff --git a/servers/rendering/renderer_rd/environment/gi.cpp b/servers/rendering/renderer_rd/environment/gi.cpp index 964d4d9adf..991ccf984e 100644 --- a/servers/rendering/renderer_rd/environment/gi.cpp +++ b/servers/rendering/renderer_rd/environment/gi.cpp @@ -2215,7 +2215,7 @@ void GI::SDFGI::render_region(Ref<RenderSceneBuffersRD> p_render_buffers, int p_ RD::get_singleton()->compute_list_dispatch_threads(compute_list, cascade_size, cascade_size, cascade_size); RD::get_singleton()->compute_list_add_barrier(compute_list); - //run one pass of fullsize jumpflood to fix up half size arctifacts + //run one pass of fullsize jumpflood to fix up half size artifacts push_constant.half_size = false; push_constant.step_size = 1; @@ -2977,7 +2977,7 @@ void GI::VoxelGIInstance::update(bool p_update_light_instances, const Vector<RID push_constant.cell_offset = mipmaps[i].cell_offset; push_constant.cell_count = mipmaps[i].cell_count; - int64_t wg_todo = (mipmaps[i].cell_count - 1) / wg_size + 1; + int64_t wg_todo = (mipmaps[i].cell_count + wg_size - 1) / wg_size; while (wg_todo) { int64_t wg_count = MIN(wg_todo, wg_limit_x); RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(VoxelGIPushConstant)); @@ -2998,7 +2998,7 @@ void GI::VoxelGIInstance::update(bool p_update_light_instances, const Vector<RID push_constant.cell_offset = mipmaps[i].cell_offset; push_constant.cell_count = mipmaps[i].cell_count; - int64_t wg_todo = (mipmaps[i].cell_count - 1) / wg_size + 1; + int64_t wg_todo = (mipmaps[i].cell_count + wg_size - 1) / wg_size; while (wg_todo) { int64_t wg_count = MIN(wg_todo, wg_limit_x); RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(VoxelGIPushConstant)); diff --git a/servers/rendering/renderer_rd/environment/sky.cpp b/servers/rendering/renderer_rd/environment/sky.cpp index ebf3c5f619..f94c701ada 100644 --- a/servers/rendering/renderer_rd/environment/sky.cpp +++ b/servers/rendering/renderer_rd/environment/sky.cpp @@ -185,7 +185,7 @@ bool SkyRD::SkyMaterialData::update_parameters(const HashMap<StringName, Variant uniform_set_updated = true; - return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, scene_singleton->sky.sky_shader.shader.version_get_shader(shader_data->version, 0), SKY_SET_MATERIAL, true); + return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, scene_singleton->sky.sky_shader.shader.version_get_shader(shader_data->version, 0), SKY_SET_MATERIAL, true, true); } SkyRD::SkyMaterialData::~SkyMaterialData() { @@ -249,11 +249,9 @@ void SkyRD::_render_sky(RD::DrawListID p_list, float p_time, RID p_fb, PipelineC } } - RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array); - RD::get_singleton()->draw_list_set_push_constant(draw_list, &sky_push_constant, sizeof(SkyPushConstant)); - RD::get_singleton()->draw_list_draw(draw_list, true); + RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u); } //////////////////////////////////////////////////////////////////////////////// @@ -941,23 +939,6 @@ void sky() { sky_scene_state.fog_only_texture_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sky_shader.default_shader_rd, SKY_SET_TEXTURES); } - - { //create index array for copy shaders - Vector<uint8_t> pv; - pv.resize(6 * 4); - { - uint8_t *w = pv.ptrw(); - int *p32 = (int *)w; - p32[0] = 0; - p32[1] = 1; - p32[2] = 2; - p32[3] = 0; - p32[4] = 2; - p32[5] = 3; - } - index_buffer = RD::get_singleton()->index_buffer_create(6, RenderingDevice::INDEX_BUFFER_FORMAT_UINT32, pv); - index_array = RD::get_singleton()->index_array_create(index_buffer, 0, 6); - } } void SkyRD::set_texture_format(RD::DataFormat p_texture_format) { @@ -990,8 +971,6 @@ SkyRD::~SkyRD() { if (RD::get_singleton()->uniform_set_is_valid(sky_scene_state.fog_only_texture_uniform_set)) { RD::get_singleton()->free(sky_scene_state.fog_only_texture_uniform_set); } - - RD::get_singleton()->free(index_buffer); //array gets freed as dependency } void SkyRD::setup_sky(RID p_env, Ref<RenderSceneBuffersRD> p_render_buffers, const PagedArray<RID> &p_lights, RID p_camera_attributes, uint32_t p_view_count, const Projection *p_view_projections, const Vector3 *p_view_eye_offsets, const Transform3D &p_cam_transform, const Projection &p_cam_projection, const Size2i p_screen_size, RendererSceneRenderRD *p_scene_render) { diff --git a/servers/rendering/renderer_rd/environment/sky.h b/servers/rendering/renderer_rd/environment/sky.h index 7aee65fd67..ee2d81757c 100644 --- a/servers/rendering/renderer_rd/environment/sky.h +++ b/servers/rendering/renderer_rd/environment/sky.h @@ -70,9 +70,6 @@ public: private: RD::DataFormat texture_format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; - RID index_buffer; - RID index_array; - enum SkyTextureSetVersion { SKY_TEXTURE_SET_BACKGROUND, SKY_TEXTURE_SET_HALF_RES, diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index 7bca1dc3be..2974e9c4a3 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -1010,13 +1010,6 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_USES_DEPTH_TEXTURE) { scene_state.used_depth_texture = true; } - - if (p_color_pass_flags & COLOR_PASS_FLAG_MOTION_VECTORS) { - if ((flags & (INSTANCE_DATA_FLAG_MULTIMESH | INSTANCE_DATA_FLAG_PARTICLES)) == INSTANCE_DATA_FLAG_MULTIMESH && RendererRD::MeshStorage::get_singleton()->_multimesh_enable_motion_vectors(inst->data->base)) { - inst->transforms_uniform_set = mesh_storage->multimesh_get_3d_uniform_set(inst->data->base, scene_shader.default_shader_rd, TRANSFORMS_UNIFORM_SET); - } - } - } else if (p_pass_mode == PASS_MODE_SHADOW || p_pass_mode == PASS_MODE_SHADOW_DP) { if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_PASS_SHADOW) { rl->add_element(surf); @@ -1435,7 +1428,7 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo // Note: when rendering stereoscopic (multiview) we are using our combined frustum projection to create // our cluster data. We use reprojection in the shader to adjust for our left/right eye. // This only works as we don't filter our cluster by depth buffer. - // If we ever make this optimisation we should make it optional and only use it in mono. + // If we ever make this optimization we should make it optional and only use it in mono. // What we win by filtering out a few lights, we loose by having to do the work double for stereo. current_cluster_builder->begin(p_render_data->scene_data->cam_transform, p_render_data->scene_data->cam_projection, !p_render_data->reflection_probe.is_valid()); } @@ -1769,7 +1762,8 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co case RS::ENV_BG_CANVAS: { if (!is_reflection_probe) { RID texture = RendererRD::TextureStorage::get_singleton()->render_target_get_rd_texture(rb->get_render_target()); - copy_effects->copy_to_fb_rect(texture, color_only_framebuffer, Rect2i(), false, false, false, false, RID(), false, false, true); + bool convert_to_linear = !RendererRD::TextureStorage::get_singleton()->render_target_is_using_hdr(rb->get_render_target()); + copy_effects->copy_to_fb_rect(texture, color_only_framebuffer, Rect2i(), false, false, false, false, RID(), false, false, convert_to_linear); } keep_color = true; } break; @@ -1847,7 +1841,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_OPAQUE, nullptr, RID()); - bool finish_depth = using_ssao || using_sdfgi || using_voxelgi; + bool finish_depth = using_ssao || using_ssil || using_sdfgi || using_voxelgi; RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, depth_pass_mode, 0, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count); _render_list_with_threads(&render_list_params, depth_framebuffer, needs_pre_resolve ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, needs_pre_resolve ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_CLEAR, finish_depth ? RD::FINAL_ACTION_READ : RD::FINAL_ACTION_CONTINUE, needs_pre_resolve ? Vector<Color>() : depth_pass_clear); 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 298a6c0752..12f8f6a366 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 @@ -436,7 +436,7 @@ void SceneShaderForwardClustered::MaterialData::set_next_pass(RID p_pass) { bool SceneShaderForwardClustered::MaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) { SceneShaderForwardClustered *shader_singleton = (SceneShaderForwardClustered *)SceneShaderForwardClustered::singleton; - return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, shader_singleton->shader.version_get_shader(shader_data->version, 0), RenderForwardClustered::MATERIAL_UNIFORM_SET, true, RD::BARRIER_MASK_RASTER); + return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, shader_singleton->shader.version_get_shader(shader_data->version, 0), RenderForwardClustered::MATERIAL_UNIFORM_SET, true, true, RD::BARRIER_MASK_RASTER); } SceneShaderForwardClustered::MaterialData::~MaterialData() { diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index 2053c3a9a6..befb2c5504 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -722,16 +722,9 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color using_subpass_post_process = false; } - // We do this last because our get_color_fbs creates and caches the framebuffer if we need it. - RID four_subpasses = rb_data->get_color_fbs(RenderBufferDataForwardMobile::FB_CONFIG_FOUR_SUBPASSES); - if (using_subpass_post_process && four_subpasses.is_null()) { - // can't do blit subpass because we don't have all subpasses - using_subpass_post_process = false; - } - if (using_subpass_post_process) { // all as subpasses - framebuffer = four_subpasses; + framebuffer = rb_data->get_color_fbs(RenderBufferDataForwardMobile::FB_CONFIG_FOUR_SUBPASSES); } else if (using_subpass_transparent) { // our tonemap pass is separate framebuffer = rb_data->get_color_fbs(RenderBufferDataForwardMobile::FB_CONFIG_THREE_SUBPASSES); @@ -803,7 +796,8 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color if (rb_data.is_valid()) { RID dest_framebuffer = rb_data->get_color_fbs(RenderBufferDataForwardMobile::FB_CONFIG_ONE_PASS); RID texture = RendererRD::TextureStorage::get_singleton()->render_target_get_rd_texture(rb->get_render_target()); - copy_effects->copy_to_fb_rect(texture, dest_framebuffer, Rect2i(), false, false, false, false, RID(), false, false, true); + bool convert_to_linear = !RendererRD::TextureStorage::get_singleton()->render_target_is_using_hdr(rb->get_render_target()); + copy_effects->copy_to_fb_rect(texture, dest_framebuffer, Rect2i(), false, false, false, false, RID(), false, false, convert_to_linear); } keep_color = true; } break; 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 e4498ac533..32d2289f75 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 @@ -388,7 +388,7 @@ void SceneShaderForwardMobile::MaterialData::set_next_pass(RID p_pass) { bool SceneShaderForwardMobile::MaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) { SceneShaderForwardMobile *shader_singleton = (SceneShaderForwardMobile *)SceneShaderForwardMobile::singleton; - return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, shader_singleton->shader.version_get_shader(shader_data->version, 0), RenderForwardMobile::MATERIAL_UNIFORM_SET, true, RD::BARRIER_MASK_RASTER); + return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, shader_singleton->shader.version_get_shader(shader_data->version, 0), RenderForwardMobile::MATERIAL_UNIFORM_SET, true, true, RD::BARRIER_MASK_RASTER); } SceneShaderForwardMobile::MaterialData::~MaterialData() { diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index eb33f296d0..0ee76e0aa4 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -348,7 +348,7 @@ void RendererCanvasRenderRD::free_polygon(PolygonID p_polygon) { //////////////////// -void RendererCanvasRenderRD::_bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size) { +void RendererCanvasRenderRD::_bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size, bool p_texture_is_data) { if (p_texture == RID()) { p_texture = default_canvas_texture; } @@ -363,7 +363,7 @@ void RendererCanvasRenderRD::_bind_canvas_texture(RD::DrawListID p_draw_list, RI bool use_normal; bool use_specular; - bool success = RendererRD::TextureStorage::get_singleton()->canvas_texture_get_uniform_set(p_texture, p_base_filter, p_base_repeat, shader.default_version_rd_shader, CANVAS_TEXTURE_UNIFORM_SET, uniform_set, size, specular_shininess, use_normal, use_specular); + bool success = RendererRD::TextureStorage::get_singleton()->canvas_texture_get_uniform_set(p_texture, p_base_filter, p_base_repeat, shader.default_version_rd_shader, CANVAS_TEXTURE_UNIFORM_SET, bool(push_constant.flags & FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR), uniform_set, size, specular_shininess, use_normal, use_specular, p_texture_is_data); //something odd happened if (!success) { _bind_canvas_texture(p_draw_list, default_canvas_texture, p_base_filter, p_base_repeat, r_last_texture, push_constant, r_texpixel_size); @@ -421,6 +421,7 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend _update_transform_2d_to_mat2x3(base_transform, push_constant.world); Color base_color = p_item->final_modulate; + bool use_linear_colors = texture_storage->render_target_is_using_hdr(p_render_target); for (int i = 0; i < 4; i++) { push_constant.modulation[i] = 0; @@ -441,6 +442,7 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend push_constant.lights[3] = 0; uint32_t base_flags = 0; + base_flags |= use_linear_colors ? FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR : 0; uint16_t light_count = 0; PipelineLightMode light_mode; @@ -505,7 +507,7 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend //bind textures - _bind_canvas_texture(p_draw_list, rect->texture, current_filter, current_repeat, last_texture, push_constant, texpixel_size); + _bind_canvas_texture(p_draw_list, rect->texture, current_filter, current_repeat, last_texture, push_constant, texpixel_size, bool(rect->flags & CANVAS_RECT_MSDF)); Rect2 src_rect; Rect2 dst_rect; @@ -566,10 +568,15 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend push_constant.flags |= FLAGS_USE_LCD; } - push_constant.modulation[0] = rect->modulate.r * base_color.r; - push_constant.modulation[1] = rect->modulate.g * base_color.g; - push_constant.modulation[2] = rect->modulate.b * base_color.b; - push_constant.modulation[3] = rect->modulate.a * base_color.a; + Color modulated = rect->modulate * base_color; + if (use_linear_colors) { + modulated = modulated.srgb_to_linear(); + } + + push_constant.modulation[0] = modulated.r; + push_constant.modulation[1] = modulated.g; + push_constant.modulation[2] = modulated.b; + push_constant.modulation[3] = modulated.a; push_constant.src_rect[0] = src_rect.position.x; push_constant.src_rect[1] = src_rect.position.y; @@ -618,10 +625,15 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend } } - push_constant.modulation[0] = np->color.r * base_color.r; - push_constant.modulation[1] = np->color.g * base_color.g; - push_constant.modulation[2] = np->color.b * base_color.b; - push_constant.modulation[3] = np->color.a * base_color.a; + Color modulated = np->color * base_color; + if (use_linear_colors) { + modulated = modulated.srgb_to_linear(); + } + + push_constant.modulation[0] = modulated.r; + push_constant.modulation[1] = modulated.g; + push_constant.modulation[2] = modulated.b; + push_constant.modulation[3] = modulated.a; push_constant.src_rect[0] = src_rect.position.x; push_constant.src_rect[1] = src_rect.position.y; @@ -676,10 +688,15 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend _bind_canvas_texture(p_draw_list, polygon->texture, current_filter, current_repeat, last_texture, push_constant, texpixel_size); - push_constant.modulation[0] = base_color.r; - push_constant.modulation[1] = base_color.g; - push_constant.modulation[2] = base_color.b; - push_constant.modulation[3] = base_color.a; + Color color = base_color; + if (use_linear_colors) { + color = color.srgb_to_linear(); + } + + push_constant.modulation[0] = color.r; + push_constant.modulation[1] = color.g; + push_constant.modulation[2] = color.b; + push_constant.modulation[3] = color.a; for (int j = 0; j < 4; j++) { push_constant.src_rect[j] = 0; @@ -718,6 +735,9 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend push_constant.uvs[j * 2 + 0] = primitive->uvs[j].x; push_constant.uvs[j * 2 + 1] = primitive->uvs[j].y; Color col = primitive->colors[j] * base_color; + if (use_linear_colors) { + col = col.srgb_to_linear(); + } push_constant.colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r); push_constant.colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b); } @@ -732,6 +752,9 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend push_constant.uvs[j * 2 + 0] = primitive->uvs[j + 1].x; push_constant.uvs[j * 2 + 1] = primitive->uvs[j + 1].y; Color col = primitive->colors[j + 1] * base_color; + if (use_linear_colors) { + col = col.srgb_to_linear(); + } push_constant.colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r); push_constant.colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b); } @@ -849,10 +872,15 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend uint32_t surf_count = mesh_storage->mesh_get_surface_count(mesh); static const PipelineVariant variant[RS::PRIMITIVE_MAX] = { PIPELINE_VARIANT_ATTRIBUTE_POINTS, PIPELINE_VARIANT_ATTRIBUTE_LINES, PIPELINE_VARIANT_ATTRIBUTE_LINES_STRIP, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLES, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLE_STRIP }; - push_constant.modulation[0] = base_color.r * modulate.r; - push_constant.modulation[1] = base_color.g * modulate.g; - push_constant.modulation[2] = base_color.b * modulate.b; - push_constant.modulation[3] = base_color.a * modulate.a; + Color modulated = modulate * base_color; + if (use_linear_colors) { + modulated = modulated.srgb_to_linear(); + } + + push_constant.modulation[0] = modulated.r; + push_constant.modulation[1] = modulated.g; + push_constant.modulation[2] = modulated.b; + push_constant.modulation[3] = modulated.a; for (int j = 0; j < 4; j++) { push_constant.src_rect[j] = 0; @@ -1113,8 +1141,9 @@ void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_co if (material_data->shader_data->version.is_valid() && material_data->shader_data->valid) { pipeline_variants = &material_data->shader_data->pipeline_variants; // Update uniform set. - if (material_data->uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(material_data->uniform_set)) { // Material may not have a uniform set. - RD::get_singleton()->draw_list_bind_uniform_set(draw_list, material_data->uniform_set, MATERIAL_UNIFORM_SET); + RID uniform_set = texture_storage->render_target_is_using_hdr(p_to_render_target) ? material_data->uniform_set : material_data->uniform_set_srgb; + if (uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) { // Material may not have a uniform set. + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set, MATERIAL_UNIFORM_SET); material_data->set_as_used(); } } else { @@ -2235,12 +2264,14 @@ RendererRD::MaterialStorage::ShaderData *RendererCanvasRenderRD::_create_shader_ bool RendererCanvasRenderRD::CanvasMaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) { RendererCanvasRenderRD *canvas_singleton = static_cast<RendererCanvasRenderRD *>(RendererCanvasRender::singleton); - - return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, canvas_singleton->shader.canvas_shader.version_get_shader(shader_data->version, 0), MATERIAL_UNIFORM_SET, false); + bool uniform_set_changed = update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, canvas_singleton->shader.canvas_shader.version_get_shader(shader_data->version, 0), MATERIAL_UNIFORM_SET, true, false, RD::BARRIER_MASK_ALL_BARRIERS); + bool uniform_set_srgb_changed = update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set_srgb, canvas_singleton->shader.canvas_shader.version_get_shader(shader_data->version, 0), MATERIAL_UNIFORM_SET, false, false, RD::BARRIER_MASK_ALL_BARRIERS); + return uniform_set_changed || uniform_set_srgb_changed; } RendererCanvasRenderRD::CanvasMaterialData::~CanvasMaterialData() { free_parameters_uniform_set(uniform_set); + free_parameters_uniform_set(uniform_set_srgb); } RendererRD::MaterialStorage::MaterialData *RendererCanvasRenderRD::_create_material_func(CanvasShaderData *p_shader) { diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h index 1116bed6e4..4c8cbd1c9f 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h @@ -75,6 +75,8 @@ class RendererCanvasRenderRD : public RendererCanvasRender { FLAGS_CLIP_RECT_UV = (1 << 9), FLAGS_TRANSPOSE_RECT = (1 << 10), + FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR = (1 << 11), + FLAGS_NINEPACH_DRAW_CENTER = (1 << 12), FLAGS_USING_PARTICLES = (1 << 13), @@ -195,6 +197,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender { struct CanvasMaterialData : public RendererRD::MaterialStorage::MaterialData { CanvasShaderData *shader_data = nullptr; RID uniform_set; + RID uniform_set_srgb; virtual void set_render_priority(int p_priority) {} virtual void set_next_pass(RID p_pass) {} @@ -415,7 +418,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender { RID _create_base_uniform_set(RID p_to_render_target, bool p_backbuffer); - inline void _bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size); //recursive, so regular inline used instead. + inline void _bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size, bool p_texture_is_data = false); //recursive, so regular inline used instead. void _render_item(RenderingDevice::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RenderingDevice::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used); void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false); diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp index 870da3c321..4ccd2aa322 100644 --- a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp @@ -81,6 +81,7 @@ void RendererCompositorRD::blit_render_targets_to_screen(DisplayServer::WindowID blit.push_constant.k2 = p_render_targets[i].lens_distortion.k2; blit.push_constant.upscale = p_render_targets[i].lens_distortion.upscale; blit.push_constant.aspect_ratio = p_render_targets[i].lens_distortion.aspect_ratio; + blit.push_constant.convert_to_srgb = texture_storage->render_target_is_using_hdr(p_render_targets[i].render_target); RD::get_singleton()->draw_list_set_push_constant(draw_list, &blit.push_constant, sizeof(BlitPushConstant)); RD::get_singleton()->draw_list_draw(draw_list, true); @@ -171,7 +172,7 @@ void RendererCompositorRD::set_boot_image(const Ref<Image> &p_image, const Color RID texture = texture_storage->texture_allocate(); texture_storage->texture_2d_initialize(texture, p_image); - RID rd_texture = texture_storage->texture_get_rd_texture(texture); + RID rd_texture = texture_storage->texture_get_rd_texture(texture, false); RD::SamplerState sampler_state; sampler_state.min_filter = p_use_filter ? RD::SAMPLER_FILTER_LINEAR : RD::SAMPLER_FILTER_NEAREST; @@ -237,6 +238,7 @@ void RendererCompositorRD::set_boot_image(const Ref<Image> &p_image, const Color blit.push_constant.k2 = 0; blit.push_constant.upscale = 1.0; blit.push_constant.aspect_ratio = 1.0; + blit.push_constant.convert_to_srgb = false; RD::get_singleton()->draw_list_set_push_constant(draw_list, &blit.push_constant, sizeof(BlitPushConstant)); RD::get_singleton()->draw_list_draw(draw_list, true); diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.h b/servers/rendering/renderer_rd/renderer_compositor_rd.h index 14fb4e6340..705fb9e8e5 100644 --- a/servers/rendering/renderer_rd/renderer_compositor_rd.h +++ b/servers/rendering/renderer_rd/renderer_compositor_rd.h @@ -80,7 +80,7 @@ protected: float upscale; float aspect_ratio; uint32_t layer; - uint32_t pad1; + uint32_t convert_to_srgb; }; struct Blit { diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index 646bdc5436..9d4d266a7a 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -368,7 +368,7 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende buffers.base_texture = rb->get_internal_texture(i); buffers.depth_texture = rb->get_depth_texture(i); - // In stereo p_render_data->z_near and p_render_data->z_far can be offset for our combined frustrum + // In stereo p_render_data->z_near and p_render_data->z_far can be offset for our combined frustum. float z_near = p_render_data->scene_data->view_projection[i].get_z_near(); float z_far = p_render_data->scene_data->view_projection[i].get_z_far(); bokeh_dof->bokeh_dof_compute(buffers, p_render_data->camera_attributes, z_near, z_far, p_render_data->scene_data->cam_orthogonal); @@ -391,7 +391,7 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende buffers.depth_texture = rb->get_depth_texture(i); buffers.base_fb = FramebufferCacheRD::get_singleton()->get_cache(buffers.base_texture); // TODO move this into bokeh_dof_raster, we can do this internally - // In stereo p_render_data->z_near and p_render_data->z_far can be offset for our combined frustrum + // In stereo p_render_data->z_near and p_render_data->z_far can be offset for our combined frustum. float z_near = p_render_data->scene_data->view_projection[i].get_z_near(); float z_far = p_render_data->scene_data->view_projection[i].get_z_far(); bokeh_dof->bokeh_dof_raster(buffers, p_render_data->camera_attributes, z_near, z_far, p_render_data->scene_data->cam_orthogonal); @@ -552,6 +552,8 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende tonemap.luminance_multiplier = _render_buffers_get_luminance_multiplier(); tonemap.view_count = rb->get_view_count(); + tonemap.convert_to_srgb = !texture_storage->render_target_is_using_hdr(render_target); + RID dest_fb; if (fsr && can_use_effects && rb->get_scaling_3d_mode() == RS::VIEWPORT_SCALING_3D_MODE_FSR) { // If we use FSR to upscale we need to write our result into an intermediate buffer. @@ -647,6 +649,8 @@ void RendererSceneRenderRD::_post_process_subpass(RID p_source_texture, RID p_fr tonemap.luminance_multiplier = _render_buffers_get_luminance_multiplier(); tonemap.view_count = rb->get_view_count(); + tonemap.convert_to_srgb = !texture_storage->render_target_is_using_hdr(rb->get_render_target()); + tone_mapper->tonemapper(draw_list, p_source_texture, RD::get_singleton()->framebuffer_get_format(p_framebuffer), tonemap); RD::get_singleton()->draw_command_end_label(); @@ -697,7 +701,7 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(const RenderDataRD *p_ren copy_effects->copy_to_fb_rect(shadow_atlas_texture, dest_fb, Rect2i(Vector2(), size), false, true); - // Visualise our view frustum to show coverage. + // Visualize our view frustum to show coverage. for (int i = 0; i < p_render_data->render_shadow_count; i++) { RID light = p_render_data->render_shadows[i].light; RID base = light_storage->light_instance_get_base_light(light); diff --git a/servers/rendering/renderer_rd/shader_rd.cpp b/servers/rendering/renderer_rd/shader_rd.cpp index d3814f175f..46ae2d9551 100644 --- a/servers/rendering/renderer_rd/shader_rd.cpp +++ b/servers/rendering/renderer_rd/shader_rd.cpp @@ -161,7 +161,7 @@ void ShaderRD::_clear_version(Version *p_version) { // Clear versions if they exist. if (p_version->variants) { for (int i = 0; i < variant_defines.size(); i++) { - if (variants_enabled[i] && group_enabled[variant_defines[i].group]) { + if (p_version->variants[i].is_valid()) { RD::get_singleton()->free(p_version->variants[i]); } } diff --git a/servers/rendering/renderer_rd/shaders/blit.glsl b/servers/rendering/renderer_rd/shaders/blit.glsl index 14f190a49f..d451647bec 100644 --- a/servers/rendering/renderer_rd/shaders/blit.glsl +++ b/servers/rendering/renderer_rd/shaders/blit.glsl @@ -45,7 +45,7 @@ layout(push_constant, std140) uniform Pos { float upscale; float aspect_ratio; uint layer; - uint pad1; + bool convert_to_srgb; } data; @@ -59,6 +59,13 @@ layout(binding = 0) uniform sampler2DArray src_rt; layout(binding = 0) uniform sampler2D src_rt; #endif +vec3 linear_to_srgb(vec3 color) { + // If going to srgb, clamp from 0 to 1. + 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))); +} + void main() { #ifdef APPLY_LENS_DISTORTION vec2 coords = uv * 2.0 - 1.0; @@ -94,4 +101,8 @@ void main() { #else color = texture(src_rt, uv); #endif + + if (data.convert_to_srgb) { + color.rgb = linear_to_srgb(color.rgb); // Regular linear -> SRGB conversion. + } } diff --git a/servers/rendering/renderer_rd/shaders/canvas.glsl b/servers/rendering/renderer_rd/shaders/canvas.glsl index 6270450c15..31c5aadc88 100644 --- a/servers/rendering/renderer_rd/shaders/canvas.glsl +++ b/servers/rendering/renderer_rd/shaders/canvas.glsl @@ -36,6 +36,12 @@ layout(set = 1, binding = 0, std140) uniform MaterialUniforms{ #GLOBALS +#ifdef USE_ATTRIBUTES +vec3 srgb_to_linear(vec3 color) { + return mix(pow((color.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), color.rgb * (1.0 / 12.92), lessThan(color.rgb, vec3(0.04045))); +} +#endif + void main() { vec4 instance_custom = vec4(0.0); #ifdef USE_PRIMITIVE @@ -65,7 +71,11 @@ void main() { #elif defined(USE_ATTRIBUTES) vec2 vertex = vertex_attrib; - vec4 color = color_attrib * draw_data.modulation; + vec4 color = color_attrib; + if (bool(draw_data.flags & FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR)) { + color.rgb = srgb_to_linear(color.rgb); + } + color *= draw_data.modulation; vec2 uv = uv_attrib; uvec4 bones = bone_attrib; @@ -563,9 +573,6 @@ void main() { } vec4 base_color = color; - if (bool(draw_data.flags & FLAGS_USING_LIGHT_MASK)) { - color = vec4(0.0); //invisible by default due to using light mask - } #ifdef MODE_LIGHT_ONLY float light_only_alpha = 0.0; diff --git a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl index 2dd5abb75f..7ac7cf9c07 100644 --- a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl @@ -12,7 +12,7 @@ #define FLAGS_CLIP_RECT_UV (1 << 9) #define FLAGS_TRANSPOSE_RECT (1 << 10) -#define FLAGS_USING_LIGHT_MASK (1 << 11) +#define FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR (1 << 11) #define FLAGS_NINEPACH_DRAW_CENTER (1 << 12) #define FLAGS_USING_PARTICLES (1 << 13) diff --git a/servers/rendering/renderer_rd/shaders/effects/blur_raster.glsl b/servers/rendering/renderer_rd/shaders/effects/blur_raster.glsl index 31aabbe9d2..91a3b582b9 100644 --- a/servers/rendering/renderer_rd/shaders/effects/blur_raster.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/blur_raster.glsl @@ -11,10 +11,9 @@ layout(location = 0) out vec2 uv_interp; /* clang-format on */ void main() { - vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0)); - uv_interp = base_arr[gl_VertexIndex]; - - gl_Position = vec4(uv_interp * 2.0 - 1.0, 0.0, 1.0); + vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0)); + gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0); + uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0 } /* clang-format off */ diff --git a/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl index 1b487835d2..947aa793d9 100644 --- a/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl @@ -11,10 +11,9 @@ layout(location = 0) out vec2 uv_interp; /* clang-format on */ void main() { - vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0)); - uv_interp = base_arr[gl_VertexIndex]; - - gl_Position = vec4(uv_interp * 2.0 - 1.0, 0.0, 1.0); + vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0)); + gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0); + uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0 } /* clang-format off */ diff --git a/servers/rendering/renderer_rd/shaders/effects/copy.glsl b/servers/rendering/renderer_rd/shaders/effects/copy.glsl index 3a82861057..2b3d27b000 100644 --- a/servers/rendering/renderer_rd/shaders/effects/copy.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/copy.glsl @@ -57,7 +57,7 @@ layout(r32f, set = 3, binding = 0) uniform restrict writeonly image2D dest_buffe #elif defined(DST_IMAGE_8BIT) layout(rgba8, set = 3, binding = 0) uniform restrict writeonly image2D dest_buffer; #else -layout(rgba32f, set = 3, binding = 0) uniform restrict writeonly image2D dest_buffer; +layout(rgba16f, set = 3, binding = 0) uniform restrict writeonly image2D dest_buffer; #endif #ifdef MODE_GAUSSIAN_BLUR diff --git a/servers/rendering/renderer_rd/shaders/effects/cubemap_downsampler_raster.glsl b/servers/rendering/renderer_rd/shaders/effects/cubemap_downsampler_raster.glsl index b8c64d09f4..7b3c2f1c3b 100644 --- a/servers/rendering/renderer_rd/shaders/effects/cubemap_downsampler_raster.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/cubemap_downsampler_raster.glsl @@ -31,9 +31,9 @@ layout(location = 0) out vec2 uv_interp; /* clang-format on */ void main() { - vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0)); - uv_interp = base_arr[gl_VertexIndex] * float(params.face_size); - gl_Position = vec4(base_arr[gl_VertexIndex] * 2.0 - 1.0, 0.0, 1.0); + vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0)); + gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0); + uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0 } /* clang-format off */ diff --git a/servers/rendering/renderer_rd/shaders/effects/cubemap_filter_raster.glsl b/servers/rendering/renderer_rd/shaders/effects/cubemap_filter_raster.glsl index 0990dc7c2f..961ec96f00 100644 --- a/servers/rendering/renderer_rd/shaders/effects/cubemap_filter_raster.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/cubemap_filter_raster.glsl @@ -35,9 +35,9 @@ layout(location = 0) out vec2 uv_interp; /* clang-format on */ void main() { - vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0)); - uv_interp = base_arr[gl_VertexIndex]; - gl_Position = vec4(base_arr[gl_VertexIndex] * 2.0 - 1.0, 0.0, 1.0); + vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0)); + gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0); + uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0 } /* clang-format off */ diff --git a/servers/rendering/renderer_rd/shaders/effects/cubemap_roughness_raster.glsl b/servers/rendering/renderer_rd/shaders/effects/cubemap_roughness_raster.glsl index c29accd8a7..05a20c459b 100644 --- a/servers/rendering/renderer_rd/shaders/effects/cubemap_roughness_raster.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/cubemap_roughness_raster.glsl @@ -11,9 +11,9 @@ layout(location = 0) out vec2 uv_interp; /* clang-format on */ void main() { - vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0)); - uv_interp = base_arr[gl_VertexIndex]; - gl_Position = vec4(uv_interp * 2.0 - 1.0, 0.0, 1.0); + vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0)); + gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0); + uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0 } /* clang-format off */ diff --git a/servers/rendering/renderer_rd/shaders/effects/luminance_reduce_raster.glsl b/servers/rendering/renderer_rd/shaders/effects/luminance_reduce_raster.glsl index 29ebd74a90..05a84db6b6 100644 --- a/servers/rendering/renderer_rd/shaders/effects/luminance_reduce_raster.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/luminance_reduce_raster.glsl @@ -11,10 +11,9 @@ layout(location = 0) out vec2 uv_interp; /* clang-format on */ void main() { - vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0)); - uv_interp = base_arr[gl_VertexIndex]; - - gl_Position = vec4(uv_interp * 2.0 - 1.0, 0.0, 1.0); + vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0)); + gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0); + uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0 } /* clang-format off */ diff --git a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_scale.glsl b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_scale.glsl index a7da0812df..4df74b8626 100644 --- a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_scale.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_scale.glsl @@ -36,7 +36,7 @@ void main() { if (any(greaterThanEqual(ssC.xy, params.screen_size))) { //too large, do nothing return; } - //do not filter, SSR will generate arctifacts if this is done + //do not filter, SSR will generate artifacts if this is done float divisor = 0.0; vec4 color; diff --git a/servers/rendering/renderer_rd/shaders/effects/specular_merge.glsl b/servers/rendering/renderer_rd/shaders/effects/specular_merge.glsl index c62144fdaf..db710b7cdd 100644 --- a/servers/rendering/renderer_rd/shaders/effects/specular_merge.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/specular_merge.glsl @@ -27,16 +27,11 @@ layout(location = 0) out vec2 uv_interp; #endif //USE_MULTIVIEW void main() { - vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0)); - -#ifdef USE_MULTIVIEW - uv_interp = vec3(base_arr[gl_VertexIndex], ViewIndex); - - gl_Position = vec4(uv_interp.xy * 2.0 - 1.0, 0.0, 1.0); -#else - uv_interp = base_arr[gl_VertexIndex]; - - gl_Position = vec4(uv_interp * 2.0 - 1.0, 0.0, 1.0); + vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0)); + gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0); + uv_interp.xy = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0 +#ifdef MULTIVIEW + uv_interp.z = ViewIndex; #endif } diff --git a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl index 52aee8b648..33ec991107 100644 --- a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl @@ -13,9 +13,9 @@ layout(location = 0) out vec2 uv_interp; void main() { - vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0)); - uv_interp = base_arr[gl_VertexIndex]; - gl_Position = vec4(uv_interp * 2.0 - 1.0, 0.0, 1.0); + vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0)); + gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0); + uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0 } #[fragment] @@ -57,14 +57,21 @@ layout(set = 3, binding = 0) uniform sampler2D source_color_correction; layout(set = 3, binding = 0) uniform sampler3D source_color_correction; #endif +#define FLAG_USE_BCS (1 << 0) +#define FLAG_USE_GLOW (1 << 1) +#define FLAG_USE_AUTO_EXPOSURE (1 << 2) +#define FLAG_USE_COLOR_CORRECTION (1 << 3) +#define FLAG_USE_FXAA (1 << 4) +#define FLAG_USE_DEBANDING (1 << 5) +#define FLAG_CONVERT_TO_SRGB (1 << 6) + layout(push_constant, std430) uniform Params { vec3 bcs; - bool use_bcs; + uint flags; - bool use_glow; - bool use_auto_exposure; - bool use_color_correction; + vec2 pixel_size; uint tonemapper; + uint pad; uvec2 glow_texture_size; float glow_intensity; @@ -77,10 +84,6 @@ layout(push_constant, std430) uniform Params { float white; float auto_exposure_scale; float luminance_multiplier; - - vec2 pixel_size; - bool use_fxaa; - bool use_debanding; } params; @@ -318,10 +321,12 @@ vec3 apply_glow(vec3 color, vec3 glow) { // apply glow using the selected blendi if (params.glow_mode == GLOW_MODE_ADD) { return color + glow; } else if (params.glow_mode == GLOW_MODE_SCREEN) { - //need color clamping + // Needs color clamping. + glow.rgb = clamp(glow.rgb, vec3(0.0f), vec3(1.0f)); return max((color + glow) - (color * glow), vec3(0.0)); } else if (params.glow_mode == GLOW_MODE_SOFTLIGHT) { - //need color clamping + // Needs color clamping. + glow.rgb = clamp(glow.rgb, vec3(0.0f), vec3(1.0f)); glow = glow * vec3(0.5f) + vec3(0.5f); color.r = (glow.r <= 0.5f) ? (color.r - (1.0f - 2.0f * glow.r) * color.r * (1.0f - color.r)) : (((glow.r > 0.5f) && (color.r <= 0.25f)) ? (color.r + (2.0f * glow.r - 1.0f) * (4.0f * color.r * (4.0f * color.r + 1.0f) * (color.r - 1.0f) + 7.0f * color.r)) : (color.r + (2.0f * glow.r - 1.0f) * (sqrt(color.r) - color.r))); @@ -439,7 +444,7 @@ void main() { float exposure = params.exposure; #ifndef SUBPASS - if (params.use_auto_exposure) { + if (bool(params.flags & FLAG_USE_AUTO_EXPOSURE)) { exposure *= 1.0 / (texelFetch(source_auto_exposure, ivec2(0, 0), 0).r * params.luminance_multiplier / params.auto_exposure_scale); } #endif @@ -448,12 +453,12 @@ void main() { // Early Tonemap & SRGB Conversion #ifndef SUBPASS - if (params.use_fxaa) { + if (bool(params.flags & FLAG_USE_FXAA)) { // FXAA must be performed before glow to preserve the "bleed" effect of glow. color.rgb = do_fxaa(color.rgb, exposure, uv_interp); } - if (params.use_glow && params.glow_mode == GLOW_MODE_MIX) { + if (bool(params.flags & FLAG_USE_GLOW) && params.glow_mode == GLOW_MODE_MIX) { vec3 glow = gather_glow(source_glow, uv_interp) * params.luminance_multiplier; if (params.glow_map_strength > 0.001) { glow = mix(glow, texture(glow_map, uv_interp).rgb * glow, params.glow_map_strength); @@ -464,11 +469,12 @@ void main() { color.rgb = apply_tonemapping(color.rgb, params.white); - color.rgb = linear_to_srgb(color.rgb); // regular linear -> SRGB conversion - + if (bool(params.flags & FLAG_CONVERT_TO_SRGB)) { + color.rgb = linear_to_srgb(color.rgb); // Regular linear -> SRGB conversion. + } #ifndef SUBPASS // Glow - if (params.use_glow && params.glow_mode != GLOW_MODE_MIX) { + if (bool(params.flags & FLAG_USE_GLOW) && params.glow_mode != GLOW_MODE_MIX) { vec3 glow = gather_glow(source_glow, uv_interp) * params.glow_intensity * params.luminance_multiplier; if (params.glow_map_strength > 0.001) { glow = mix(glow, texture(glow_map, uv_interp).rgb * glow, params.glow_map_strength); @@ -476,7 +482,9 @@ void main() { // high dynamic range -> SRGB glow = apply_tonemapping(glow, params.white); - glow = linear_to_srgb(glow); + if (bool(params.flags & FLAG_CONVERT_TO_SRGB)) { + glow = linear_to_srgb(glow); + } color.rgb = apply_glow(color.rgb, glow); } @@ -484,15 +492,15 @@ void main() { // Additional effects - if (params.use_bcs) { + if (bool(params.flags & FLAG_USE_BCS)) { color.rgb = apply_bcs(color.rgb, params.bcs); } - if (params.use_color_correction) { + if (bool(params.flags & FLAG_USE_COLOR_CORRECTION)) { color.rgb = apply_color_correction(color.rgb); } - if (params.use_debanding) { + if (bool(params.flags & FLAG_USE_DEBANDING)) { // Debanding should be done at the end of tonemapping, but before writing to the LDR buffer. // Otherwise, we're adding noise to an already-quantized image. color.rgb += screen_space_dither(gl_FragCoord.xy); diff --git a/servers/rendering/renderer_rd/shaders/effects/vrs.glsl b/servers/rendering/renderer_rd/shaders/effects/vrs.glsl index b450bb9fe9..d3d39a8b92 100644 --- a/servers/rendering/renderer_rd/shaders/effects/vrs.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/vrs.glsl @@ -20,13 +20,12 @@ layout(location = 0) out vec2 uv_interp; #endif void main() { - vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0)); - uv_interp.xy = base_arr[gl_VertexIndex]; + vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0)); + gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0); + uv_interp.xy = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0 #ifdef MULTIVIEW uv_interp.z = ViewIndex; #endif - - gl_Position = vec4(uv_interp.xy * 2.0 - 1.0, 0.0, 1.0); } #[fragment] diff --git a/servers/rendering/renderer_rd/shaders/environment/sky.glsl b/servers/rendering/renderer_rd/shaders/environment/sky.glsl index d605917acc..4e5b11aed8 100644 --- a/servers/rendering/renderer_rd/shaders/environment/sky.glsl +++ b/servers/rendering/renderer_rd/shaders/environment/sky.glsl @@ -23,7 +23,7 @@ layout(push_constant, std430) uniform Params { params; void main() { - vec2 base_arr[4] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0), vec2(1.0, -1.0)); + vec2 base_arr[3] = vec2[](vec2(-1.0, -3.0), vec2(-1.0, 1.0), vec2(3.0, 1.0)); uv_interp = base_arr[gl_VertexIndex]; gl_Position = vec4(uv_interp, 1.0, 1.0); } 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 ba8d901772..aa90ad6876 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl @@ -584,7 +584,7 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 v { vec4 clamp_rect = omni_lights.data[idx].atlas_rect; - //redo shadowmapping, but shrink the model a bit to avoid arctifacts + //redo shadowmapping, but shrink the model a bit to avoid artifacts vec4 splane = (omni_lights.data[idx].shadow_matrix * vec4(vertex - normalize(normal_interp) * omni_lights.data[idx].transmittance_bias, 1.0)); float shadow_len = length(splane.xyz); diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp index d055f01009..fda341bbc9 100644 --- a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp @@ -751,7 +751,7 @@ MaterialStorage::MaterialData::~MaterialData() { } } -void MaterialStorage::MaterialData::update_textures(const HashMap<StringName, Variant> &p_parameters, const HashMap<StringName, HashMap<int, RID>> &p_default_textures, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, RID *p_textures, bool p_use_linear_color) { +void MaterialStorage::MaterialData::update_textures(const HashMap<StringName, Variant> &p_parameters, const HashMap<StringName, HashMap<int, RID>> &p_default_textures, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, RID *p_textures, bool p_use_linear_color, bool p_3d_material) { TextureStorage *texture_storage = TextureStorage::get_singleton(); MaterialStorage *material_storage = MaterialStorage::get_singleton(); @@ -917,7 +917,7 @@ void MaterialStorage::MaterialData::update_textures(const HashMap<StringName, Va if (tex) { rd_texture = (srgb && tex->rd_texture_srgb.is_valid()) ? tex->rd_texture_srgb : tex->rd_texture; #ifdef TOOLS_ENABLED - if (tex->detect_3d_callback && p_use_linear_color) { + if (tex->detect_3d_callback && p_3d_material) { tex->detect_3d_callback(tex->detect_3d_callback_ud); } if (tex->detect_normal_callback && (p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL || p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_ROUGHNESS_NORMAL)) { @@ -986,7 +986,7 @@ void MaterialStorage::MaterialData::free_parameters_uniform_set(RID p_uniform_se } } -bool MaterialStorage::MaterialData::update_parameters_uniform_set(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty, const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, const HashMap<StringName, HashMap<int, RID>> &p_default_texture_params, uint32_t p_ubo_size, RID &uniform_set, RID p_shader, uint32_t p_shader_uniform_set, bool p_use_linear_color, uint32_t p_barrier) { +bool MaterialStorage::MaterialData::update_parameters_uniform_set(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty, const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, const HashMap<StringName, HashMap<int, RID>> &p_default_texture_params, uint32_t p_ubo_size, RID &uniform_set, RID p_shader, uint32_t p_shader_uniform_set, bool p_use_linear_color, bool p_3d_material, uint32_t p_barrier) { if ((uint32_t)ubo_data.size() != p_ubo_size) { p_uniform_dirty = true; if (uniform_buffer.is_valid()) { @@ -1033,7 +1033,7 @@ bool MaterialStorage::MaterialData::update_parameters_uniform_set(const HashMap< } if (p_textures_dirty && tex_uniform_count) { - update_textures(p_parameters, p_default_texture_params, p_texture_uniforms, texture_cache.ptrw(), p_use_linear_color); + update_textures(p_parameters, p_default_texture_params, p_texture_uniforms, texture_cache.ptrw(), p_use_linear_color, p_3d_material); } if (p_ubo_size == 0 && (p_texture_uniforms.size() == 0)) { @@ -1821,14 +1821,16 @@ void MaterialStorage::global_shader_parameters_load_settings(bool p_load_texture if (gvtype >= RS::GLOBAL_VAR_TYPE_SAMPLER2D) { //textire if (!p_load_textures) { - value = RID(); continue; } String path = value; - Ref<Resource> resource = ResourceLoader::load(path); - ERR_CONTINUE(resource.is_null()); - value = resource; + if (path.is_empty()) { + value = RID(); + } else { + Ref<Resource> resource = ResourceLoader::load(path); + value = resource; + } } if (global_shader_uniforms.variables.has(name)) { diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.h b/servers/rendering/renderer_rd/storage_rd/material_storage.h index b6da3df783..ae97f43a3c 100644 --- a/servers/rendering/renderer_rd/storage_rd/material_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/material_storage.h @@ -78,7 +78,7 @@ public: struct MaterialData { Vector<RendererRD::TextureStorage::RenderTarget *> render_target_cache; void update_uniform_buffer(const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const HashMap<StringName, Variant> &p_parameters, uint8_t *p_buffer, uint32_t p_buffer_size, bool p_use_linear_color); - void update_textures(const HashMap<StringName, Variant> &p_parameters, const HashMap<StringName, HashMap<int, RID>> &p_default_textures, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, RID *p_textures, bool p_use_linear_color); + void update_textures(const HashMap<StringName, Variant> &p_parameters, const HashMap<StringName, HashMap<int, RID>> &p_default_textures, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, RID *p_textures, bool p_use_linear_color, bool p_3d_material); void set_as_used(); virtual void set_render_priority(int p_priority) = 0; @@ -87,7 +87,7 @@ public: virtual ~MaterialData(); //to be used internally by update_parameters, in the most common configuration of material parameters - bool update_parameters_uniform_set(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty, const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, const HashMap<StringName, HashMap<int, RID>> &p_default_texture_params, uint32_t p_ubo_size, RID &uniform_set, RID p_shader, uint32_t p_shader_uniform_set, bool p_use_linear_color, uint32_t p_barrier = RD::BARRIER_MASK_ALL_BARRIERS); + bool update_parameters_uniform_set(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty, const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, const HashMap<StringName, HashMap<int, RID>> &p_default_texture_params, uint32_t p_ubo_size, RID &r_uniform_set, RID p_shader, uint32_t p_shader_uniform_set, bool p_use_linear_color, bool p_3d_material, uint32_t p_barrier = RD::BARRIER_MASK_ALL_BARRIERS); void free_parameters_uniform_set(RID p_uniform_set); private: diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp index 67b5cdd291..56f2ea0b0c 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp @@ -1289,12 +1289,9 @@ void MeshStorage::multimesh_allocate_data(RID p_multimesh, int p_instances, RS:: multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MULTIMESH); } -bool MeshStorage::_multimesh_enable_motion_vectors(RID p_multimesh) { - MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); - ERR_FAIL_COND_V(!multimesh, false); - +void MeshStorage::_multimesh_enable_motion_vectors(MultiMesh *multimesh) { if (multimesh->motion_vectors_enabled) { - return false; + return; } multimesh->motion_vectors_enabled = true; @@ -1307,22 +1304,30 @@ bool MeshStorage::_multimesh_enable_motion_vectors(RID p_multimesh) { multimesh->data_cache.append_array(multimesh->data_cache); } - if (multimesh->buffer_set) { + uint32_t buffer_size = multimesh->instances * multimesh->stride_cache * sizeof(float); + uint32_t new_buffer_size = buffer_size * 2; + RID new_buffer = RD::get_singleton()->storage_buffer_create(new_buffer_size); + + if (multimesh->buffer_set && multimesh->data_cache.is_empty()) { + // If the buffer was set but there's no data cached in the CPU, we copy the buffer directly on the GPU. RD::get_singleton()->barrier(); - Vector<uint8_t> buffer_data = RD::get_singleton()->buffer_get_data(multimesh->buffer); - if (!multimesh->data_cache.is_empty()) { - memcpy(buffer_data.ptrw(), multimesh->data_cache.ptr(), buffer_data.size()); - } + RD::get_singleton()->buffer_copy(multimesh->buffer, new_buffer, 0, 0, buffer_size, RD::BARRIER_MASK_NO_BARRIER); + RD::get_singleton()->buffer_copy(multimesh->buffer, new_buffer, 0, buffer_size, buffer_size); + } else if (!multimesh->data_cache.is_empty()) { + // Simply upload the data cached in the CPU, which should already be doubled in size. + ERR_FAIL_COND(multimesh->data_cache.size() * sizeof(float) != size_t(new_buffer_size)); + RD::get_singleton()->buffer_update(new_buffer, 0, new_buffer_size, multimesh->data_cache.ptr()); + } + if (multimesh->buffer.is_valid()) { RD::get_singleton()->free(multimesh->buffer); - uint32_t buffer_size = multimesh->instances * multimesh->stride_cache * sizeof(float) * 2; - multimesh->buffer = RD::get_singleton()->storage_buffer_create(buffer_size); - RD::get_singleton()->buffer_update(multimesh->buffer, 0, buffer_data.size(), buffer_data.ptr(), RD::BARRIER_MASK_NO_BARRIER); - RD::get_singleton()->buffer_update(multimesh->buffer, buffer_data.size(), buffer_data.size(), buffer_data.ptr()); - multimesh->uniform_set_3d = RID(); // Cleared by dependency - return true; } - return false; // Update the transforms uniform set cache + + multimesh->buffer = new_buffer; + multimesh->uniform_set_3d = RID(); // Cleared by dependency. + + // Invalidate any references to the buffer that was released and the uniform set that was pointing to it. + multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MULTIMESH); } void MeshStorage::_multimesh_get_motion_vectors_offsets(RID p_multimesh, uint32_t &r_current_offset, uint32_t &r_prev_offset) { @@ -1531,6 +1536,12 @@ void MeshStorage::multimesh_instance_set_transform(RID p_multimesh, int p_index, ERR_FAIL_COND(multimesh->xform_format != RS::MULTIMESH_TRANSFORM_3D); _multimesh_make_local(multimesh); + + bool uses_motion_vectors = (RSG::viewport->get_num_viewports_with_motion_vectors() > 0); + if (uses_motion_vectors) { + _multimesh_enable_motion_vectors(multimesh); + } + _multimesh_update_motion_vectors_data_cache(multimesh); { @@ -1749,6 +1760,11 @@ void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_b ERR_FAIL_COND(!multimesh); ERR_FAIL_COND(p_buffer.size() != (multimesh->instances * (int)multimesh->stride_cache)); + bool uses_motion_vectors = (RSG::viewport->get_num_viewports_with_motion_vectors() > 0); + if (uses_motion_vectors) { + _multimesh_enable_motion_vectors(multimesh); + } + if (multimesh->motion_vectors_enabled) { uint32_t frame = RSG::rasterizer->get_frame_number(); diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h index 4d46a62a76..c23a5b1449 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h @@ -234,6 +234,7 @@ private: MultiMesh *multimesh_dirty_list = nullptr; _FORCE_INLINE_ void _multimesh_make_local(MultiMesh *multimesh) const; + _FORCE_INLINE_ void _multimesh_enable_motion_vectors(MultiMesh *multimesh); _FORCE_INLINE_ void _multimesh_update_motion_vectors_data_cache(MultiMesh *multimesh); _FORCE_INLINE_ void _multimesh_mark_dirty(MultiMesh *multimesh, int p_index, bool p_aabb); _FORCE_INLINE_ void _multimesh_mark_all_dirty(MultiMesh *multimesh, bool p_data, bool p_aabb); @@ -593,7 +594,6 @@ public: virtual AABB multimesh_get_aabb(RID p_multimesh) const override; void _update_dirty_multimeshes(); - bool _multimesh_enable_motion_vectors(RID p_multimesh); void _multimesh_get_motion_vectors_offsets(RID p_multimesh, uint32_t &r_current_offset, uint32_t &r_prev_offset); _FORCE_INLINE_ RS::MultimeshTransformFormat multimesh_get_transform_format(RID p_multimesh) const { diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp index 8f647be5c9..0a91672544 100644 --- a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp @@ -1629,7 +1629,7 @@ MaterialStorage::ShaderData *ParticlesStorage::_create_particles_shader_func() { } bool ParticlesStorage::ParticleProcessMaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) { - return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, ParticlesStorage::get_singleton()->particles_shader.shader.version_get_shader(shader_data->version, 0), 3, true); + return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, ParticlesStorage::get_singleton()->particles_shader.shader.version_get_shader(shader_data->version, 0), 3, true, false); } ParticlesStorage::ParticleProcessMaterialData::~ParticleProcessMaterialData() { diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp index a7b8f985d9..27c435eeba 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp @@ -234,6 +234,12 @@ void RenderSceneDataRD::update_ubo(RID p_uniform_buffer, RS::ViewportDebugDraw p RendererRD::MaterialStorage::store_transform(prev_cam_transform, prev_ubo.inv_view_matrix); RendererRD::MaterialStorage::store_transform(prev_cam_transform.affine_inverse(), prev_ubo.view_matrix); +#ifdef REAL_T_IS_DOUBLE + RendererRD::MaterialStorage::split_double(-prev_cam_transform.origin.x, &prev_ubo.inv_view_matrix[12], &prev_ubo.inv_view_matrix[3]); + RendererRD::MaterialStorage::split_double(-prev_cam_transform.origin.y, &prev_ubo.inv_view_matrix[13], &prev_ubo.inv_view_matrix[7]); + RendererRD::MaterialStorage::split_double(-prev_cam_transform.origin.z, &prev_ubo.inv_view_matrix[14], &prev_ubo.inv_view_matrix[11]); +#endif + for (uint32_t v = 0; v < view_count; v++) { prev_projection = prev_correction * view_projection[v]; RendererRD::MaterialStorage::store_camera(prev_projection, prev_ubo.projection_matrix_view[v]); diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp index d84f6e6850..286a9528fc 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp @@ -46,9 +46,11 @@ void TextureStorage::CanvasTexture::clear_sets() { } for (int i = 1; i < RS::CANVAS_ITEM_TEXTURE_FILTER_MAX; i++) { for (int j = 1; j < RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX; j++) { - if (RD::get_singleton()->uniform_set_is_valid(uniform_sets[i][j])) { - RD::get_singleton()->free(uniform_sets[i][j]); - uniform_sets[i][j] = RID(); + for (int k = 0; k < 2; k++) { + if (RD::get_singleton()->uniform_set_is_valid(uniform_sets[i][j][k])) { + RD::get_singleton()->free(uniform_sets[i][j][k]); + uniform_sets[i][j][k] = RID(); + } } } } @@ -641,7 +643,7 @@ void TextureStorage::canvas_texture_set_texture_repeat(RID p_canvas_texture, RS: ct->clear_sets(); } -bool TextureStorage::canvas_texture_get_uniform_set(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID p_base_shader, int p_base_set, RID &r_uniform_set, Size2i &r_size, Color &r_specular_shininess, bool &r_use_normal, bool &r_use_specular) { +bool TextureStorage::canvas_texture_get_uniform_set(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID p_base_shader, int p_base_set, bool p_use_srgb, RID &r_uniform_set, Size2i &r_size, Color &r_specular_shininess, bool &r_use_normal, bool &r_use_specular, bool p_texture_is_data) { MaterialStorage *material_storage = MaterialStorage::get_singleton(); CanvasTexture *ct = nullptr; @@ -674,7 +676,7 @@ bool TextureStorage::canvas_texture_get_uniform_set(RID p_texture, RS::CanvasIte RS::CanvasItemTextureRepeat repeat = ct->texture_repeat != RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT ? ct->texture_repeat : p_base_repeat; ERR_FAIL_COND_V(repeat == RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT, false); - RID uniform_set = ct->uniform_sets[filter][repeat]; + RID uniform_set = ct->uniform_sets[filter][repeat][int(p_use_srgb)]; if (!RD::get_singleton()->uniform_set_is_valid(uniform_set)) { //create and update Vector<RD::Uniform> uniforms; @@ -688,7 +690,7 @@ bool TextureStorage::canvas_texture_get_uniform_set(RID p_texture, RS::CanvasIte u.append_id(texture_rd_get_default(DEFAULT_RD_TEXTURE_WHITE)); ct->size_cache = Size2i(1, 1); } else { - u.append_id(t->rd_texture); + u.append_id(t->rd_texture_srgb.is_valid() && p_use_srgb && !p_texture_is_data ? t->rd_texture_srgb : t->rd_texture); ct->size_cache = Size2i(t->width_2d, t->height_2d); if (t->render_target) { t->render_target->was_used = true; @@ -741,7 +743,7 @@ bool TextureStorage::canvas_texture_get_uniform_set(RID p_texture, RS::CanvasIte } uniform_set = RD::get_singleton()->uniform_set_create(uniforms, p_base_shader, p_base_set); - ct->uniform_sets[filter][repeat] = uniform_set; + ct->uniform_sets[filter][repeat][int(p_use_srgb)] = uniform_set; ct->cleared_cache = false; } @@ -1268,7 +1270,35 @@ Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const { #endif Vector<uint8_t> data = RD::get_singleton()->texture_get_data(tex->rd_texture, 0); ERR_FAIL_COND_V(data.size() == 0, Ref<Image>()); - Ref<Image> image = Image::create_from_data(tex->width, tex->height, tex->mipmaps > 1, tex->validated_format, data); + Ref<Image> image; + + // Expand RGB10_A2 into RGBAH. This is needed for capturing viewport data + // when using the mobile renderer with HDR mode on. + if (tex->rd_format == RD::DATA_FORMAT_A2B10G10R10_UNORM_PACK32) { + Vector<uint8_t> new_data; + new_data.resize(data.size() * 2); + uint16_t *ndp = (uint16_t *)new_data.ptr(); + + uint32_t *ptr = (uint32_t *)data.ptr(); + uint32_t num_pixels = data.size() / 4; + + for (uint32_t ofs = 0; ofs < num_pixels; ofs++) { + uint32_t px = ptr[ofs]; + uint32_t r = (px & 0x3FF); + uint32_t g = ((px >> 10) & 0x3FF); + uint32_t b = ((px >> 20) & 0x3FF); + uint32_t a = ((px >> 30) & 0x3); + + ndp[ofs * 4 + 0] = Math::make_half_float(float(r) / 1023.0); + ndp[ofs * 4 + 1] = Math::make_half_float(float(g) / 1023.0); + ndp[ofs * 4 + 2] = Math::make_half_float(float(b) / 1023.0); + ndp[ofs * 4 + 3] = Math::make_half_float(float(a) / 3.0); + } + image = Image::create_from_data(tex->width, tex->height, tex->mipmaps > 1, tex->validated_format, new_data); + } else { + image = Image::create_from_data(tex->width, tex->height, tex->mipmaps > 1, tex->validated_format, data); + } + ERR_FAIL_COND_V(image->is_empty(), Ref<Image>()); if (tex->format != tex->validated_format) { image->convert(tex->format); @@ -3020,10 +3050,15 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { if (rt->size.width == 0 || rt->size.height == 0) { return; } - //until we implement support for HDR monitors (and render target is attached to screen), this is enough. - rt->color_format = RD::DATA_FORMAT_R8G8B8A8_UNORM; - rt->color_format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; - rt->image_format = rt->is_transparent ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8; + if (rt->use_hdr) { + rt->color_format = RendererSceneRenderRD::get_singleton()->_render_buffers_get_color_format(); + rt->color_format_srgb = rt->color_format; + rt->image_format = rt->is_transparent ? Image::FORMAT_RGBAH : Image::FORMAT_RGBH; + } else { + rt->color_format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + rt->color_format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; + rt->image_format = rt->is_transparent ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8; + } RD::TextureFormat rd_color_attachment_format; RD::TextureView rd_view; @@ -3106,6 +3141,7 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { tex->rd_format = rt->color_format; tex->rd_format_srgb = rt->color_format_srgb; tex->format = rt->image_format; + tex->validated_format = rt->use_hdr ? Image::FORMAT_RGBAH : Image::FORMAT_RGBA8; Vector<RID> proxies = tex->proxies; //make a copy, since update may change it for (int i = 0; i < proxies.size(); i++) { @@ -3328,6 +3364,25 @@ RS::ViewportMSAA TextureStorage::render_target_get_msaa(RID p_render_target) con return rt->msaa; } +void TextureStorage::render_target_set_use_hdr(RID p_render_target, bool p_use_hdr) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND(!rt); + + if (p_use_hdr == rt->use_hdr) { + return; + } + + rt->use_hdr = p_use_hdr; + _update_render_target(rt); +} + +bool TextureStorage::render_target_is_using_hdr(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND_V(!rt, false); + + return rt->use_hdr; +} + RID TextureStorage::render_target_get_rd_framebuffer(RID p_render_target) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND_V(!rt, RID()); @@ -3404,7 +3459,7 @@ bool TextureStorage::render_target_is_clear_requested(RID p_render_target) { Color TextureStorage::render_target_get_clear_request_color(RID p_render_target) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND_V(!rt, Color()); - return rt->clear_color; + return rt->use_hdr ? rt->clear_color.srgb_to_linear() : rt->clear_color; } void TextureStorage::render_target_disable_clear_request(RID p_render_target) { @@ -3420,7 +3475,7 @@ void TextureStorage::render_target_do_clear_request(RID p_render_target) { return; } Vector<Color> clear_colors; - clear_colors.push_back(rt->clear_color); + clear_colors.push_back(rt->use_hdr ? rt->clear_color.srgb_to_linear() : rt->clear_color); RD::get_singleton()->draw_list_begin(rt->get_framebuffer(), RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD, clear_colors); RD::get_singleton()->draw_list_end(); rt->clear_requested = false; @@ -3735,7 +3790,7 @@ void TextureStorage::render_target_copy_to_back_buffer(RID p_render_target, cons // TODO figure out stereo support here if (RendererSceneRenderRD::get_singleton()->_render_buffers_can_be_storage()) { - copy_effects->copy_to_rect(rt->color, rt->backbuffer_mipmap0, region, false, false, false, true, true); + copy_effects->copy_to_rect(rt->color, rt->backbuffer_mipmap0, region, false, false, false, !rt->use_hdr, true); } else { copy_effects->copy_to_fb_rect(rt->color, rt->backbuffer_fb, region, false, false, false, false, RID(), false, true); } @@ -3759,7 +3814,7 @@ void TextureStorage::render_target_copy_to_back_buffer(RID p_render_target, cons RID mipmap = rt->backbuffer_mipmaps[i]; if (RendererSceneRenderRD::get_singleton()->_render_buffers_can_be_storage()) { - copy_effects->gaussian_blur(prev_texture, mipmap, region, texture_size, true); + copy_effects->gaussian_blur(prev_texture, mipmap, region, texture_size, !rt->use_hdr); } else { copy_effects->gaussian_blur_raster(prev_texture, mipmap, region, texture_size); } @@ -3789,9 +3844,9 @@ void TextureStorage::render_target_clear_back_buffer(RID p_render_target, const } } - //single texture copy for backbuffer + // Single texture copy for backbuffer. if (RendererSceneRenderRD::get_singleton()->_render_buffers_can_be_storage()) { - copy_effects->set_color(rt->backbuffer_mipmap0, p_color, region, true); + copy_effects->set_color(rt->backbuffer_mipmap0, p_color, region, !rt->use_hdr); } else { copy_effects->set_color_raster(rt->backbuffer_mipmap0, p_color, region); } @@ -3833,7 +3888,7 @@ void TextureStorage::render_target_gen_back_buffer_mipmaps(RID p_render_target, RID mipmap = rt->backbuffer_mipmaps[i]; if (RendererSceneRenderRD::get_singleton()->_render_buffers_can_be_storage()) { - copy_effects->gaussian_blur(prev_texture, mipmap, region, texture_size, true); + copy_effects->gaussian_blur(prev_texture, mipmap, region, texture_size, !rt->use_hdr); } else { copy_effects->gaussian_blur_raster(prev_texture, mipmap, region, texture_size); } diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.h b/servers/rendering/renderer_rd/storage_rd/texture_storage.h index 6df6faa40a..276c8c4ce2 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.h @@ -96,7 +96,7 @@ private: RS::CanvasItemTextureFilter texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT; RS::CanvasItemTextureRepeat texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT; - RID uniform_sets[RS::CANVAS_ITEM_TEXTURE_FILTER_MAX][RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX]; + RID uniform_sets[RS::CANVAS_ITEM_TEXTURE_FILTER_MAX][RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX][2]; Size2i size_cache = Size2i(1, 1); bool use_normal_cache = false; @@ -341,6 +341,7 @@ private: Image::Format image_format = Image::FORMAT_L8; bool is_transparent = false; + bool use_hdr = false; bool sdf_enabled = false; @@ -474,7 +475,7 @@ public: virtual void canvas_texture_set_texture_filter(RID p_item, RS::CanvasItemTextureFilter p_filter) override; virtual void canvas_texture_set_texture_repeat(RID p_item, RS::CanvasItemTextureRepeat p_repeat) override; - bool canvas_texture_get_uniform_set(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID p_base_shader, int p_base_set, RID &r_uniform_set, Size2i &r_size, Color &r_specular_shininess, bool &r_use_normal, bool &r_use_specular); + bool canvas_texture_get_uniform_set(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID p_base_shader, int p_base_set, bool p_use_srgb, RID &r_uniform_set, Size2i &r_size, Color &r_specular_shininess, bool &r_use_normal, bool &r_use_specular, bool p_texture_is_data); /* Texture API */ @@ -717,6 +718,8 @@ public: virtual void render_target_set_as_unused(RID p_render_target) override; virtual void render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) override; virtual RS::ViewportMSAA render_target_get_msaa(RID p_render_target) const override; + virtual void render_target_set_use_hdr(RID p_render_target, bool p_use_hdr) override; + virtual bool render_target_is_using_hdr(RID p_render_target) const override; void render_target_copy_to_back_buffer(RID p_render_target, const Rect2i &p_region, bool p_gen_mipmaps); void render_target_clear_back_buffer(RID p_render_target, const Rect2i &p_region, const Color &p_color); diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp index 44b15ee91d..45bbcf51c4 100644 --- a/servers/rendering/renderer_scene_cull.cpp +++ b/servers/rendering/renderer_scene_cull.cpp @@ -1970,7 +1970,6 @@ void RendererSceneCull::_update_instance_aabb(Instance *p_instance) { } } - // <Zylann> This is why I didn't re-use Instance::aabb to implement custom AABBs if (p_instance->extra_margin) { new_aabb.grow_by(p_instance->extra_margin); } diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index 7a829826d2..5880bf3951 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -1163,6 +1163,17 @@ void RendererViewport::viewport_set_msaa_3d(RID p_viewport, RS::ViewportMSAA p_m _configure_3d_render_buffers(viewport); } +void RendererViewport::viewport_set_use_hdr_2d(RID p_viewport, bool p_use_hdr_2d) { + Viewport *viewport = viewport_owner.get_or_null(p_viewport); + ERR_FAIL_COND(!viewport); + + if (viewport->use_hdr_2d == p_use_hdr_2d) { + return; + } + viewport->use_hdr_2d = p_use_hdr_2d; + RSG::texture_storage->render_target_set_use_hdr(viewport->render_target, p_use_hdr_2d); +} + void RendererViewport::viewport_set_screen_space_aa(RID p_viewport, RS::ViewportScreenSpaceAA p_mode) { Viewport *viewport = viewport_owner.get_or_null(p_viewport); ERR_FAIL_COND(!viewport); @@ -1183,6 +1194,7 @@ void RendererViewport::viewport_set_use_taa(RID p_viewport, bool p_use_taa) { return; } viewport->use_taa = p_use_taa; + num_viewports_with_motion_vectors += p_use_taa ? 1 : -1; _configure_3d_render_buffers(viewport); } @@ -1367,6 +1379,10 @@ bool RendererViewport::free(RID p_rid) { RendererSceneOcclusionCull::get_singleton()->remove_buffer(p_rid); } + if (viewport->use_taa) { + num_viewports_with_motion_vectors--; + } + viewport_owner.free(p_rid); return true; @@ -1422,6 +1438,10 @@ int RendererViewport::get_total_draw_calls_used() const { return total_draw_calls_used; } +int RendererViewport::get_num_viewports_with_motion_vectors() const { + return num_viewports_with_motion_vectors; +} + RendererViewport::RendererViewport() { occlusion_rays_per_thread = GLOBAL_GET("rendering/occlusion_culling/occlusion_rays_per_thread"); } diff --git a/servers/rendering/renderer_viewport.h b/servers/rendering/renderer_viewport.h index c004d05b23..3bfb1afd51 100644 --- a/servers/rendering/renderer_viewport.h +++ b/servers/rendering/renderer_viewport.h @@ -116,6 +116,7 @@ public: RS::CanvasItemTextureRepeat texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED; bool transparent_bg = false; + bool use_hdr_2d = false; uint32_t canvas_cull_mask = 0xffffffff; @@ -157,6 +158,7 @@ public: update_mode = RS::VIEWPORT_UPDATE_WHEN_VISIBLE; clear_mode = RS::VIEWPORT_CLEAR_ALWAYS; transparent_bg = false; + use_hdr_2d = false; viewport_to_screen = DisplayServer::INVALID_WINDOW_ID; shadow_atlas_size = 0; @@ -196,6 +198,8 @@ public: int total_vertices_drawn = 0; int total_draw_calls_used = 0; + int num_viewports_with_motion_vectors = 0; + private: Vector<Viewport *> _sort_active_viewports(); void _viewport_set_size(Viewport *p_viewport, int p_width, int p_height, uint32_t p_view_count); @@ -250,6 +254,7 @@ public: void viewport_remove_canvas(RID p_viewport, RID p_canvas); void viewport_set_canvas_transform(RID p_viewport, RID p_canvas, const Transform2D &p_offset); void viewport_set_transparent_background(RID p_viewport, bool p_enabled); + void viewport_set_use_hdr_2d(RID p_viewport, bool p_use_hdr_2d); void viewport_set_global_canvas_transform(RID p_viewport, const Transform2D &p_transform); void viewport_set_canvas_stacking(RID p_viewport, RID p_canvas, int p_layer, int p_sublayer); @@ -299,6 +304,7 @@ public: int get_total_objects_drawn() const; int get_total_primitives_drawn() const; int get_total_draw_calls_used() const; + int get_num_viewports_with_motion_vectors() const; // Workaround for setting this on thread. void call_set_vsync_mode(DisplayServer::VSyncMode p_mode, DisplayServer::WindowID p_window); diff --git a/servers/rendering/rendering_device.compat.inc b/servers/rendering/rendering_device.compat.inc new file mode 100644 index 0000000000..dc7817e66b --- /dev/null +++ b/servers/rendering/rendering_device.compat.inc @@ -0,0 +1,41 @@ +/**************************************************************************/ +/* rendering_device.compat.inc */ +/**************************************************************************/ +/* 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 DISABLE_DEPRECATED + +RID RenderingDevice::_shader_create_from_bytecode_bind_compat_79606(const Vector<uint8_t> &p_shader_binary) { + return shader_create_from_bytecode(p_shader_binary, RID()); +} + +void RenderingDevice::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("shader_create_from_bytecode", "binary_data"), &RenderingDevice::_shader_create_from_bytecode_bind_compat_79606); +} + +#endif diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index 1b0a3e9d0f..3da69b9c3c 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -29,6 +29,7 @@ /**************************************************************************/ #include "rendering_device.h" +#include "rendering_device.compat.inc" #include "rendering_device_binds.h" @@ -537,15 +538,15 @@ Error RenderingDevice::_reflect_spirv(const Vector<ShaderStageSPIRVData> &p_spir if (r_reflection_data.uniforms[set][k].binding == (uint32_t)info.binding) { // Already exists, verify that it's the same type. ERR_FAIL_COND_V_MSG(r_reflection_data.uniforms[set][k].type != info.type, FAILED, - "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different uniform type."); + "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to reuse location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different uniform type."); // Also, verify that it's the same size. ERR_FAIL_COND_V_MSG(r_reflection_data.uniforms[set][k].length != info.length, FAILED, - "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different uniform size."); + "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to reuse location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different uniform size."); // Also, verify that it has the same writability. ERR_FAIL_COND_V_MSG(r_reflection_data.uniforms[set][k].writable != info.writable, FAILED, - "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different writability."); + "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to reuse location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different writability."); // Just append stage mask and return. r_reflection_data.uniforms.write[set].write[k].stages_mask.set_flag(stage_flag); diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index 58da3df577..1ade1b25c4 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -141,6 +141,11 @@ private: protected: static void _bind_methods(); +#ifndef DISABLE_DEPRECATED + RID _shader_create_from_bytecode_bind_compat_79606(const Vector<uint8_t> &p_shader_binary); + static void _bind_compatibility_methods(); +#endif + Capabilities device_capabilities; public: @@ -835,6 +840,7 @@ public: virtual bool uniform_set_is_valid(RID p_uniform_set) = 0; virtual void uniform_set_set_invalidation_callback(RID p_uniform_set, InvalidationCallback p_callback, void *p_userdata) = 0; + virtual Error buffer_copy(RID p_src_buffer, RID p_dst_buffer, uint32_t p_src_offset, uint32_t p_dst_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS) = 0; virtual Error buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS) = 0; virtual Error buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS) = 0; virtual Vector<uint8_t> buffer_get_data(RID p_buffer, uint32_t p_offset = 0, uint32_t p_size = 0) = 0; // This causes stall, only use to retrieve large buffers for saving. diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index e9b40eb082..9ad2175332 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -629,6 +629,7 @@ public: FUNC2(viewport_remove_canvas, RID, RID) FUNC3(viewport_set_canvas_transform, RID, RID, const Transform2D &) FUNC2(viewport_set_transparent_background, RID, bool) + FUNC2(viewport_set_use_hdr_2d, RID, bool) FUNC2(viewport_set_snap_2d_transforms_to_pixel, RID, bool) FUNC2(viewport_set_snap_2d_vertices_to_pixel, RID, bool) diff --git a/servers/rendering/shader_preprocessor.cpp b/servers/rendering/shader_preprocessor.cpp index 7af326f779..8d6ddf9121 100644 --- a/servers/rendering/shader_preprocessor.cpp +++ b/servers/rendering/shader_preprocessor.cpp @@ -420,11 +420,11 @@ void ShaderPreprocessor::process_define(Tokenizer *p_tokenizer) { return; } + Vector<String> args; if (p_tokenizer->peek() == '(') { // Macro has arguments. p_tokenizer->get_token(); - Vector<String> args; while (true) { String name = p_tokenizer->get_identifier(); if (name.is_empty()) { @@ -442,17 +442,24 @@ void ShaderPreprocessor::process_define(Tokenizer *p_tokenizer) { return; } } + } - Define *define = memnew(Define); + String body = tokens_to_string(p_tokenizer->advance('\n')).strip_edges(); + if (body.begins_with("##")) { + set_error(RTR("'##' must not appear at beginning of macro expansion."), line); + return; + } + if (body.ends_with("##")) { + set_error(RTR("'##' must not appear at end of macro expansion."), line); + return; + } + + Define *define = memnew(Define); + if (!args.is_empty()) { define->arguments = args; - define->body = tokens_to_string(p_tokenizer->advance('\n')).strip_edges(); - state->defines[label] = define; - } else { - // Simple substitution macro. - Define *define = memnew(Define); - define->body = tokens_to_string(p_tokenizer->advance('\n')).strip_edges(); - state->defines[label] = define; } + define->body = body; + state->defines[label] = define; } void ShaderPreprocessor::process_elif(Tokenizer *p_tokenizer) { @@ -1074,9 +1081,13 @@ bool ShaderPreprocessor::expand_macros_once(const String &p_line, int p_line_num } } + concatenate_macro_body(body); + result = result.substr(0, index) + " " + body + " " + result.substr(args_end + 1, result.length()); } else { - result = result.substr(0, index) + body + result.substr(index + key.length(), result.length() - (index + key.length())); + concatenate_macro_body(body); + + result = result.substr(0, index) + " " + body + " " + result.substr(index + key.length(), result.length() - (index + key.length())); } r_expanded = result; @@ -1115,6 +1126,40 @@ bool ShaderPreprocessor::find_match(const String &p_string, const String &p_valu return false; } +void ShaderPreprocessor::concatenate_macro_body(String &r_body) { + int index_start = r_body.find("##"); + while (index_start > -1) { + int index_end = index_start + 2; // First character after ##. + // The macro was checked during creation so this should never happen. + ERR_FAIL_INDEX(index_end, r_body.size()); + + // If there more than two # in a row, then it's not a concatenation. + bool is_concat = true; + while (index_end <= r_body.length() && r_body[index_end] == '#') { + index_end++; + is_concat = false; + } + if (!is_concat) { + index_start = r_body.find("##", index_end); + continue; + } + + // Skip whitespace after ##. + while (index_end < r_body.length() && is_char_space(r_body[index_end])) { + index_end++; + } + + // Skip whitespace before ##. + while (index_start >= 1 && is_char_space(r_body[index_start - 1])) { + index_start--; + } + + r_body = r_body.substr(0, index_start) + r_body.substr(index_end, r_body.length() - index_end); + + index_start = r_body.find("##", index_start); + } +} + String ShaderPreprocessor::next_directive(Tokenizer *p_tokenizer, const Vector<String> &p_directives) { const int line = p_tokenizer->get_line(); int nesting = 0; diff --git a/servers/rendering/shader_preprocessor.h b/servers/rendering/shader_preprocessor.h index 406b663228..9448a97d68 100644 --- a/servers/rendering/shader_preprocessor.h +++ b/servers/rendering/shader_preprocessor.h @@ -205,6 +205,7 @@ private: Error expand_macros(const String &p_string, int p_line, String &r_result); bool expand_macros_once(const String &p_line, int p_line_number, const RBMap<String, Define *>::Element *p_define_pair, String &r_expanded); bool find_match(const String &p_string, const String &p_value, int &r_index, int &r_index_start); + void concatenate_macro_body(String &r_body); String next_directive(Tokenizer *p_tokenizer, const Vector<String> &p_directives); void add_to_output(const String &p_str); diff --git a/servers/rendering/storage/texture_storage.h b/servers/rendering/storage/texture_storage.h index c3a257595c..6332dd578e 100644 --- a/servers/rendering/storage/texture_storage.h +++ b/servers/rendering/storage/texture_storage.h @@ -151,6 +151,8 @@ public: virtual void render_target_set_as_unused(RID p_render_target) = 0; virtual void render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) = 0; virtual RS::ViewportMSAA render_target_get_msaa(RID p_render_target) const = 0; + virtual void render_target_set_use_hdr(RID p_render_target, bool p_use_hdr) = 0; + virtual bool render_target_is_using_hdr(RID p_render_target) const = 0; virtual void render_target_request_clear(RID p_render_target, const Color &p_clear_color) = 0; virtual bool render_target_is_clear_requested(RID p_render_target) = 0; diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 48b38cf2b7..45ba0b3c08 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2231,6 +2231,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("viewport_set_positional_shadow_atlas_quadrant_subdivision", "viewport", "quadrant", "subdivision"), &RenderingServer::viewport_set_positional_shadow_atlas_quadrant_subdivision); ClassDB::bind_method(D_METHOD("viewport_set_msaa_3d", "viewport", "msaa"), &RenderingServer::viewport_set_msaa_3d); ClassDB::bind_method(D_METHOD("viewport_set_msaa_2d", "viewport", "msaa"), &RenderingServer::viewport_set_msaa_2d); + ClassDB::bind_method(D_METHOD("viewport_set_use_hdr_2d", "viewport", "enabled"), &RenderingServer::viewport_set_use_hdr_2d); ClassDB::bind_method(D_METHOD("viewport_set_screen_space_aa", "viewport", "mode"), &RenderingServer::viewport_set_screen_space_aa); ClassDB::bind_method(D_METHOD("viewport_set_use_taa", "viewport", "enable"), &RenderingServer::viewport_set_use_taa); ClassDB::bind_method(D_METHOD("viewport_set_use_debanding", "viewport", "enable"), &RenderingServer::viewport_set_use_debanding); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 618ceb3091..1528a957ce 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -863,6 +863,7 @@ public: virtual void viewport_remove_canvas(RID p_viewport, RID p_canvas) = 0; virtual void viewport_set_canvas_transform(RID p_viewport, RID p_canvas, const Transform2D &p_offset) = 0; virtual void viewport_set_transparent_background(RID p_viewport, bool p_enabled) = 0; + virtual void viewport_set_use_hdr_2d(RID p_viewport, bool p_use_hdr) = 0; virtual void viewport_set_snap_2d_transforms_to_pixel(RID p_viewport, bool p_enabled) = 0; virtual void viewport_set_snap_2d_vertices_to_pixel(RID p_viewport, bool p_enabled) = 0; diff --git a/servers/xr/xr_positional_tracker.cpp b/servers/xr/xr_positional_tracker.cpp index ffb89fc385..2dacabe588 100644 --- a/servers/xr/xr_positional_tracker.cpp +++ b/servers/xr/xr_positional_tracker.cpp @@ -153,7 +153,13 @@ void XRPositionalTracker::invalidate_pose(const StringName &p_action_name) { void XRPositionalTracker::set_pose(const StringName &p_action_name, const Transform3D &p_transform, const Vector3 &p_linear_velocity, const Vector3 &p_angular_velocity, const XRPose::TrackingConfidence p_tracking_confidence) { Ref<XRPose> new_pose; - new_pose.instantiate(); + if (poses.has(p_action_name)) { + new_pose = poses[p_action_name]; + } else { + new_pose.instantiate(); + poses[p_action_name] = new_pose; + } + new_pose->set_name(p_action_name); new_pose->set_has_tracking_data(true); new_pose->set_transform(p_transform); @@ -161,7 +167,6 @@ void XRPositionalTracker::set_pose(const StringName &p_action_name, const Transf new_pose->set_angular_velocity(p_angular_velocity); new_pose->set_tracking_confidence(p_tracking_confidence); - poses[p_action_name] = new_pose; emit_signal(SNAME("pose_changed"), new_pose); // TODO discuss whether we also want to create and emit an InputEventXRPose event diff --git a/tests/SCsub b/tests/SCsub index c59ce69b92..d96a1142e4 100644 --- a/tests/SCsub +++ b/tests/SCsub @@ -13,10 +13,8 @@ env_tests = env.Clone() if env_tests["platform"] == "windows": env_tests.Append(CPPDEFINES=[("DOCTEST_THREAD_LOCAL", "")]) -# Increase number of addressable sections in object files -# due to doctest's heavy use of templates and macros. -if env_tests.msvc: - env_tests.Append(CCFLAGS=["/bigobj"]) +if env["disable_exceptions"]: + env_tests.Append(CPPDEFINES=["DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS"]) env_tests.add_source_files(env.tests_sources, "*.cpp") diff --git a/tests/core/config/test_project_settings.h b/tests/core/config/test_project_settings.h index 1deafccde1..9bd072f511 100644 --- a/tests/core/config/test_project_settings.h +++ b/tests/core/config/test_project_settings.h @@ -32,9 +32,17 @@ #define TEST_PROJECT_SETTINGS_H #include "core/config/project_settings.h" +#include "core/io/dir_access.h" #include "core/variant/variant.h" #include "tests/test_macros.h" +class TestProjectSettingsInternalsAccessor { +public: + static String &resource_path() { + return ProjectSettings::get_singleton()->resource_path; + }; +}; + namespace TestProjectSettings { TEST_CASE("[ProjectSettings] Get existing setting") { @@ -97,6 +105,58 @@ TEST_CASE("[ProjectSettings] Set value should be returned when retrieved") { CHECK(ProjectSettings::get_singleton()->has_setting("my_custom_setting")); } +TEST_CASE("[ProjectSettings] localize_path") { + String old_resource_path = TestProjectSettingsInternalsAccessor::resource_path(); + TestProjectSettingsInternalsAccessor::resource_path() = DirAccess::create(DirAccess::ACCESS_FILESYSTEM)->get_current_dir(); + String root_path = ProjectSettings::get_singleton()->get_resource_path(); +#ifdef WINDOWS_ENABLED + String root_path_win = ProjectSettings::get_singleton()->get_resource_path().replace("/", "\\"); +#endif + + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("filename"), "res://filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("path/filename"), "res://path/filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("path/something/../filename"), "res://path/filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("path/./filename"), "res://path/filename"); +#ifdef WINDOWS_ENABLED + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("path\\filename"), "res://path/filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("path\\something\\..\\filename"), "res://path/filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("path\\.\\filename"), "res://path/filename"); +#endif + + // FIXME?: These checks pass, but that doesn't seems correct + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("../filename"), "res://filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("../path/filename"), "res://path/filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("..\\path\\filename"), "res://path/filename"); + + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("/testroot/filename"), "/testroot/filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("/testroot/path/filename"), "/testroot/path/filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("/testroot/path/something/../filename"), "/testroot/path/filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("/testroot/path/./filename"), "/testroot/path/filename"); +#ifdef WINDOWS_ENABLED + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("C:/testroot/filename"), "C:/testroot/filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("C:/testroot/path/filename"), "C:/testroot/path/filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("C:/testroot/path/something/../filename"), "C:/testroot/path/filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("C:/testroot/path/./filename"), "C:/testroot/path/filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("C:\\testroot\\filename"), "C:/testroot/filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("C:\\testroot\\path\\filename"), "C:/testroot/path/filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("C:\\testroot\\path\\something\\..\\filename"), "C:/testroot/path/filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("C:\\testroot\\path\\.\\filename"), "C:/testroot/path/filename"); +#endif + + CHECK_EQ(ProjectSettings::get_singleton()->localize_path(root_path + "/filename"), "res://filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path(root_path + "/path/filename"), "res://path/filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path(root_path + "/path/something/../filename"), "res://path/filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path(root_path + "/path/./filename"), "res://path/filename"); +#ifdef WINDOWS_ENABLED + CHECK_EQ(ProjectSettings::get_singleton()->localize_path(root_path_win + "\\filename"), "res://filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path(root_path_win + "\\path\\filename"), "res://path/filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path(root_path_win + "\\path\\something\\..\\filename"), "res://path/filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path(root_path_win + "\\path\\.\\filename"), "res://path/filename"); +#endif + + TestProjectSettingsInternalsAccessor::resource_path() = old_resource_path; +} + } // namespace TestProjectSettings #endif // TEST_PROJECT_SETTINGS_H diff --git a/tests/core/io/test_image.h b/tests/core/io/test_image.h index 92ab166ae8..1897971113 100644 --- a/tests/core/io/test_image.h +++ b/tests/core/io/test_image.h @@ -262,9 +262,7 @@ TEST_CASE("[Image] Modifying pixels of an image") { for (const Rect2i &rect : rects) { Ref<Image> img = memnew(Image(img_width, img_height, false, Image::FORMAT_RGBA8)); - CHECK_NOTHROW_MESSAGE( - img->fill_rect(rect, Color(1, 1, 1, 1)), - "fill_rect() shouldn't throw for any rect."); + img->fill_rect(rect, Color(1, 1, 1, 1)); for (int y = 0; y < img->get_height(); y++) { for (int x = 0; x < img->get_width(); x++) { if (rect.abs().has_point(Point2(x, y))) { @@ -317,7 +315,7 @@ TEST_CASE("[Image] Modifying pixels of an image") { // Pre-multiply Alpha then Convert from RGBA to L8, checking alpha { Ref<Image> gray_image = memnew(Image(3, 3, false, Image::FORMAT_RGBA8)); - CHECK_NOTHROW_MESSAGE(gray_image->fill_rect(Rect2i(0, 0, 3, 3), Color(1, 1, 1, 0)), "fill_rect() shouldn't throw for any rect."); + gray_image->fill_rect(Rect2i(0, 0, 3, 3), Color(1, 1, 1, 0)); gray_image->set_pixel(1, 1, Color(1, 1, 1, 1)); gray_image->set_pixel(1, 2, Color(0.5, 0.5, 0.5, 0.5)); gray_image->set_pixel(2, 1, Color(0.25, 0.05, 0.5, 1.0)); diff --git a/tests/core/io/test_json.h b/tests/core/io/test_json.h index 73c7ac7a2e..bf2ed42740 100644 --- a/tests/core/io/test_json.h +++ b/tests/core/io/test_json.h @@ -186,20 +186,21 @@ TEST_CASE("[JSON] Parsing escape sequences") { } SUBCASE("Valid unicode escape sequences") { - String json_string = "\"\\u0000\""; + String json_string = "\"\\u0020\""; json.parse(json_string); CHECK_MESSAGE( json.get_error_line() == 0, - vformat("Parsing valid unicode escape sequence with value `0000` as JSON should parse successfully.")); + vformat("Parsing valid unicode escape sequence with value `0020` as JSON should parse successfully.")); String json_value = json.get_data(); CHECK_MESSAGE( - json_value == "\0", - vformat("Parsing valid unicode escape sequence with value `0000` as JSON should return the expected value.")); + json_value == " ", + vformat("Parsing valid unicode escape sequence with value `0020` as JSON should return the expected value.")); } SUBCASE("Invalid escape sequences") { + ERR_PRINT_OFF for (char32_t i = 0; i < 128; i++) { bool skip = false; for (int j = 0; j < valid_escapes.size(); j++) { @@ -228,6 +229,7 @@ TEST_CASE("[JSON] Parsing escape sequences") { err == ERR_PARSE_ERROR, vformat("Parsing invalid escape sequence with ASCII value `%d` as JSON should fail to parse with ERR_PARSE_ERROR.", i)); } + ERR_PRINT_ON } } } // namespace TestJSON diff --git a/tests/core/object/test_class_db.h b/tests/core/object/test_class_db.h index 3f091fd2fe..5f7de11c71 100644 --- a/tests/core/object/test_class_db.h +++ b/tests/core/object/test_class_db.h @@ -409,9 +409,6 @@ void validate_method(const Context &p_context, const ExposedClass &p_class, cons if (p_method.return_type.name != StringName()) { const ExposedClass *return_class = p_context.find_exposed_class(p_method.return_type); if (return_class) { - TEST_COND(return_class->is_singleton, - "Method return type is a singleton: '", p_class.name, ".", p_method.name, "'."); - if (p_class.api_type == ClassDB::API_CORE) { TEST_COND(return_class->api_type == ClassDB::API_EDITOR, "Method '", p_class.name, ".", p_method.name, "' has return type '", return_class->name, diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h index 93b237788d..659fb003d3 100644 --- a/tests/core/string/test_string.h +++ b/tests/core/string/test_string.h @@ -1372,8 +1372,8 @@ TEST_CASE("[String] Ensuring empty string into parse_utf8 passes empty string") } TEST_CASE("[String] Cyrillic to_lower()") { - String upper = String::utf8("АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ"); - String lower = String::utf8("абвгдеёжзийклмнопрстуфхцчшщъыьэюя"); + String upper = U"АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ"; + String lower = U"абвгдеёжзийклмнопрстуфхцчшщъыьэюя"; String test = upper.to_lower(); @@ -1609,6 +1609,11 @@ TEST_CASE("[String] Repeat") { CHECK(t == s); } +TEST_CASE("[String] Reverse") { + String s = "Abcd"; + CHECK(s.reverse() == "dcbA"); +} + TEST_CASE("[String] SHA1/SHA256/MD5") { String s = "Godot"; String sha1 = "a1e91f39b9fce6a9998b14bdbe2aa2b39dc2d201"; @@ -1700,7 +1705,7 @@ TEST_CASE("[String] validate_identifier") { String name_with_spaces = "Name with spaces"; CHECK(name_with_spaces.validate_identifier() == "Name_with_spaces"); - String name_with_invalid_chars = String::utf8("Invalid characters:@*#&世界"); + String name_with_invalid_chars = U"Invalid characters:@*#&世界"; CHECK(name_with_invalid_chars.validate_identifier() == "Invalid_characters_______"); } diff --git a/tests/core/string/test_translation_server.h b/tests/core/string/test_translation_server.h new file mode 100644 index 0000000000..2c20574309 --- /dev/null +++ b/tests/core/string/test_translation_server.h @@ -0,0 +1,136 @@ +/**************************************************************************/ +/* test_translation_server.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_TRANSLATION_SERVER_H +#define TEST_TRANSLATION_SERVER_H + +#include "core/string/translation.h" + +#include "tests/test_macros.h" + +namespace TestTranslationServer { +TEST_CASE("[TranslationServer] Translation operations") { + Ref<Translation> t = memnew(Translation); + t->set_locale("uk"); + t->add_message("Good Morning", String::utf8("Добрий ранок")); + + TranslationServer *ts = TranslationServer::get_singleton(); + + int l_count_before = ts->get_loaded_locales().size(); + ts->add_translation(t); + int l_count_after = ts->get_loaded_locales().size(); + // Newly created Translation object should be added to the list, so the counter should increase, too. + CHECK(l_count_after > l_count_before); + + Ref<Translation> trans = ts->get_translation_object("uk"); + CHECK(trans.is_valid()); + + ts->set_locale("uk"); + CHECK(ts->translate("Good Morning") == String::utf8("Добрий ранок")); + + ts->remove_translation(t); + trans = ts->get_translation_object("uk"); + CHECK(trans.is_null()); + // If no suitable Translation object has been found - the original message should be returned. + CHECK(ts->translate("Good Morning") == "Good Morning"); +} + +TEST_CASE("[TranslationServer] Locale operations") { + TranslationServer *ts = TranslationServer::get_singleton(); + + // Language variant test; we supplied the variant of Español and the result should be the same string. + String loc = "es_Hani_ES_tradnl"; + String res = ts->standardize_locale(loc); + + CHECK(res == loc); + + // No such variant in variant_map; should return everything except the variant. + loc = "es_Hani_ES_missing"; + res = ts->standardize_locale(loc); + + CHECK(res == "es_Hani_ES"); + + // Non-ISO language name check (Windows issue). + loc = "iw_Hani_IL"; + res = ts->standardize_locale(loc); + + CHECK(res == "he_Hani_IL"); + + // Country rename check. + loc = "uk_Hani_UK"; + res = ts->standardize_locale(loc); + + CHECK(res == "uk_Hani_GB"); + + // Supplying a script name that is not in the list. + loc = "de_Wrong_DE"; + res = ts->standardize_locale(loc); + + CHECK(res == "de_DE"); +} + +TEST_CASE("[TranslationServer] Comparing locales") { + TranslationServer *ts = TranslationServer::get_singleton(); + + String locale_a = "es"; + String locale_b = "es"; + + // Exact match check. + int res = ts->compare_locales(locale_a, locale_b); + + CHECK(res == 10); + + locale_a = "sr-Latn-CS"; + locale_b = "sr-Latn-RS"; + + // Two elements from locales match. + res = ts->compare_locales(locale_a, locale_b); + + CHECK(res == 2); + + locale_a = "uz-Cyrl-UZ"; + locale_b = "uz-Latn-UZ"; + + // Two elements match, but they are not sequentual. + res = ts->compare_locales(locale_a, locale_b); + + CHECK(res == 2); + + locale_a = "es-EC"; + locale_b = "fr-LU"; + + // No match. + res = ts->compare_locales(locale_a, locale_b); + + CHECK(res == 0); +} +} // namespace TestTranslationServer + +#endif // TEST_TRANSLATION_SERVER_H diff --git a/tests/scene/test_packed_scene.h b/tests/scene/test_packed_scene.h new file mode 100644 index 0000000000..3517aba31f --- /dev/null +++ b/tests/scene/test_packed_scene.h @@ -0,0 +1,155 @@ +/**************************************************************************/ +/* test_packed_scene.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_PACKED_SCENE_H +#define TEST_PACKED_SCENE_H + +#include "scene/resources/packed_scene.h" + +#include "tests/test_macros.h" + +namespace TestPackedScene { + +TEST_CASE("[PackedScene] Pack Scene and Retrieve State") { + // Create a scene to pack. + Node *scene = memnew(Node); + scene->set_name("TestScene"); + + // Pack the scene. + PackedScene packed_scene; + const Error err = packed_scene.pack(scene); + CHECK(err == OK); + + // Retrieve the packed state. + Ref<SceneState> state = packed_scene.get_state(); + CHECK(state.is_valid()); + CHECK(state->get_node_count() == 1); + CHECK(state->get_node_name(0) == "TestScene"); + + memdelete(scene); +} + +TEST_CASE("[PackedScene] Clear Packed Scene") { + // Create a scene to pack. + Node *scene = memnew(Node); + scene->set_name("TestScene"); + + // Pack the scene. + PackedScene packed_scene; + packed_scene.pack(scene); + + // Clear the packed scene. + packed_scene.clear(); + + // Check if it has been cleared. + Ref<SceneState> state = packed_scene.get_state(); + CHECK_FALSE(state->get_node_count() == 1); + + memdelete(scene); +} + +TEST_CASE("[PackedScene] Can Instantiate Packed Scene") { + // Create a scene to pack. + Node *scene = memnew(Node); + scene->set_name("TestScene"); + + // Pack the scene. + PackedScene packed_scene; + packed_scene.pack(scene); + + // Check if the packed scene can be instantiated. + const bool can_instantiate = packed_scene.can_instantiate(); + CHECK(can_instantiate == true); + + memdelete(scene); +} + +TEST_CASE("[PackedScene] Instantiate Packed Scene") { + // Create a scene to pack. + Node *scene = memnew(Node); + scene->set_name("TestScene"); + + // Pack the scene. + PackedScene packed_scene; + packed_scene.pack(scene); + + // Instantiate the packed scene. + Node *instance = packed_scene.instantiate(); + CHECK(instance != nullptr); + CHECK(instance->get_name() == "TestScene"); + + memdelete(scene); + memdelete(instance); +} + +TEST_CASE("[PackedScene] Instantiate Packed Scene With Children") { + // Create a scene to pack. + Node *scene = memnew(Node); + scene->set_name("TestScene"); + + // Add persisting child nodes to the scene. + Node *child1 = memnew(Node); + child1->set_name("Child1"); + scene->add_child(child1); + child1->set_owner(scene); + + Node *child2 = memnew(Node); + child2->set_name("Child2"); + scene->add_child(child2); + child2->set_owner(scene); + + // Add non persisting child node to the scene. + Node *child3 = memnew(Node); + child3->set_name("Child3"); + scene->add_child(child3); + + // Pack the scene. + PackedScene packed_scene; + packed_scene.pack(scene); + + // Instantiate the packed scene. + Node *instance = packed_scene.instantiate(); + CHECK(instance != nullptr); + CHECK(instance->get_name() == "TestScene"); + + // Validate the child nodes of the instantiated scene. + CHECK(instance->get_child_count() == 2); + CHECK(instance->get_child(0)->get_name() == "Child1"); + CHECK(instance->get_child(1)->get_name() == "Child2"); + CHECK(instance->get_child(0)->get_owner() == instance); + CHECK(instance->get_child(1)->get_owner() == instance); + + memdelete(scene); + memdelete(instance); +} + +} // namespace TestPackedScene + +#endif // TEST_PACKED_SCENE_H diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h index 345e617285..8cfb189370 100644 --- a/tests/scene/test_text_edit.h +++ b/tests/scene/test_text_edit.h @@ -407,7 +407,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { CHECK(text_edit->get_selection_to_line() == 1); SIGNAL_CHECK("caret_changed", empty_signal_args); - // insert before should move caret and selecion, and works when not editable. + // Insert before should move caret and selection, and works when not editable. text_edit->set_editable(false); lines_edited_args.remove_at(0); text_edit->insert_line_at(0, "new"); @@ -424,7 +424,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_CHECK_FALSE("text_set"); text_edit->set_editable(true); - // can undo/redo as single action + // Can undo/redo as single action. ((Array)lines_edited_args[0])[0] = 1; ((Array)lines_edited_args[0])[1] = 0; text_edit->undo(); diff --git a/tests/scene/test_viewport.h b/tests/scene/test_viewport.h index a6b3b2c40c..dd4786977e 100644 --- a/tests/scene/test_viewport.h +++ b/tests/scene/test_viewport.h @@ -31,10 +31,13 @@ #ifndef TEST_VIEWPORT_H #define TEST_VIEWPORT_H -#include "scene/2d/node_2d.h" +#include "scene/2d/area_2d.h" +#include "scene/2d/collision_shape_2d.h" #include "scene/gui/control.h" #include "scene/gui/subviewport_container.h" +#include "scene/main/canvas_layer.h" #include "scene/main/window.h" +#include "scene/resources/rectangle_shape_2d.h" #include "tests/test_macros.h" @@ -223,7 +226,7 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") { SUBCASE("[Viewport][GuiInputEvent] nullptr as argument doesn't lead to a crash.") { ERR_PRINT_OFF; - CHECK_NOTHROW(root->push_input(nullptr)); + root->push_input(nullptr); ERR_PRINT_ON; } @@ -756,6 +759,423 @@ TEST_CASE("[SceneTree][Viewport] Control mouse cursor shape") { } } +class TestArea2D : public Area2D { + GDCLASS(TestArea2D, Area2D); + + void _on_mouse_entered() { + enter_id = ++TestArea2D::counter; // > 0, if activated. + } + + void _on_mouse_exited() { + exit_id = ++TestArea2D::counter; // > 0, if activated. + } + + void _on_input_event(Node *p_vp, Ref<InputEvent> p_ev, int p_shape) { + last_input_event = p_ev; + } + +public: + static int counter; + int enter_id = 0; + int exit_id = 0; + Ref<InputEvent> last_input_event; + + void init_signals() { + connect(SNAME("mouse_entered"), callable_mp(this, &TestArea2D::_on_mouse_entered)); + connect(SNAME("mouse_exited"), callable_mp(this, &TestArea2D::_on_mouse_exited)); + connect(SNAME("input_event"), callable_mp(this, &TestArea2D::_on_input_event)); + } + + void test_reset() { + enter_id = 0; + exit_id = 0; + last_input_event.unref(); + } +}; + +int TestArea2D::counter = 0; + +TEST_CASE("[SceneTree][Viewport] Physics Picking 2D") { + // FIXME: MOUSE_MODE_CAPTURED if-conditions are not testable, because DisplayServerMock doesn't support it. + + struct PickingCollider { + TestArea2D *a; + CollisionShape2D *c; + Ref<RectangleShape2D> r; + }; + + SceneTree *tree = SceneTree::get_singleton(); + Window *root = tree->get_root(); + root->set_physics_object_picking(true); + + Point2i on_background = Point2i(800, 800); + Point2i on_outside = Point2i(-1, -1); + SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE); + tree->physics_process(1); + + Vector<PickingCollider> v; + for (int i = 0; i < 4; i++) { + PickingCollider pc; + pc.a = memnew(TestArea2D); + pc.c = memnew(CollisionShape2D); + pc.r = Ref<RectangleShape2D>(memnew(RectangleShape2D)); + pc.r->set_size(Size2(150, 150)); + pc.c->set_shape(pc.r); + pc.a->add_child(pc.c); + pc.a->set_name("A" + itos(i)); + pc.c->set_name("C" + itos(i)); + v.push_back(pc); + SIGNAL_WATCH(pc.a, SNAME("mouse_entered")); + SIGNAL_WATCH(pc.a, SNAME("mouse_exited")); + } + + Node2D *node_a = memnew(Node2D); + node_a->set_position(Point2i(0, 0)); + v[0].a->set_position(Point2i(0, 0)); + v[1].a->set_position(Point2i(0, 100)); + node_a->add_child(v[0].a); + node_a->add_child(v[1].a); + Node2D *node_b = memnew(Node2D); + node_b->set_position(Point2i(100, 0)); + v[2].a->set_position(Point2i(0, 0)); + v[3].a->set_position(Point2i(0, 100)); + node_b->add_child(v[2].a); + node_b->add_child(v[3].a); + root->add_child(node_a); + root->add_child(node_b); + Point2i on_all = Point2i(50, 50); + Point2i on_0 = Point2i(10, 10); + Point2i on_01 = Point2i(10, 50); + Point2i on_02 = Point2i(50, 10); + + Array empty_signal_args_2; + empty_signal_args_2.push_back(Array()); + empty_signal_args_2.push_back(Array()); + + Array empty_signal_args_4; + empty_signal_args_4.push_back(Array()); + empty_signal_args_4.push_back(Array()); + empty_signal_args_4.push_back(Array()); + empty_signal_args_4.push_back(Array()); + + for (PickingCollider E : v) { + E.a->init_signals(); + } + + SUBCASE("[Viewport][Picking2D] Mouse Motion") { + SEND_GUI_MOUSE_MOTION_EVENT(on_all, MouseButtonMask::NONE, Key::NONE); + tree->physics_process(1); + SIGNAL_CHECK(SNAME("mouse_entered"), empty_signal_args_4); + SIGNAL_CHECK_FALSE(SNAME("mouse_exited")); + for (PickingCollider E : v) { + CHECK(E.a->enter_id); + CHECK_FALSE(E.a->exit_id); + E.a->test_reset(); + } + + SEND_GUI_MOUSE_MOTION_EVENT(on_01, MouseButtonMask::NONE, Key::NONE); + tree->physics_process(1); + SIGNAL_CHECK_FALSE(SNAME("mouse_entered")); + SIGNAL_CHECK(SNAME("mouse_exited"), empty_signal_args_2); + + for (int i = 0; i < v.size(); i++) { + CHECK_FALSE(v[i].a->enter_id); + if (i < 2) { + CHECK_FALSE(v[i].a->exit_id); + } else { + CHECK(v[i].a->exit_id); + } + v[i].a->test_reset(); + } + + SEND_GUI_MOUSE_MOTION_EVENT(on_outside, MouseButtonMask::NONE, Key::NONE); + tree->physics_process(1); + SIGNAL_CHECK_FALSE(SNAME("mouse_entered")); + SIGNAL_CHECK(SNAME("mouse_exited"), empty_signal_args_2); + for (int i = 0; i < v.size(); i++) { + CHECK_FALSE(v[i].a->enter_id); + if (i < 2) { + CHECK(v[i].a->exit_id); + } else { + CHECK_FALSE(v[i].a->exit_id); + } + v[i].a->test_reset(); + } + } + + SUBCASE("[Viewport][Picking2D] Object moved / passive hovering") { + SEND_GUI_MOUSE_MOTION_EVENT(on_all, MouseButtonMask::NONE, Key::NONE); + tree->physics_process(1); + for (int i = 0; i < v.size(); i++) { + CHECK(v[i].a->enter_id); + CHECK_FALSE(v[i].a->exit_id); + v[i].a->test_reset(); + } + + node_b->set_position(Point2i(200, 0)); + tree->physics_process(1); + for (int i = 0; i < v.size(); i++) { + CHECK_FALSE(v[i].a->enter_id); + if (i < 2) { + CHECK_FALSE(v[i].a->exit_id); + } else { + CHECK(v[i].a->exit_id); + } + v[i].a->test_reset(); + } + + node_b->set_position(Point2i(100, 0)); + tree->physics_process(1); + for (int i = 0; i < v.size(); i++) { + if (i < 2) { + CHECK_FALSE(v[i].a->enter_id); + } else { + CHECK(v[i].a->enter_id); + } + CHECK_FALSE(v[i].a->exit_id); + v[i].a->test_reset(); + } + } + + SUBCASE("[Viewport][Picking2D] No Processing") { + SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE); + tree->physics_process(1); + for (PickingCollider E : v) { + E.a->test_reset(); + } + + v[0].a->set_process_mode(Node::PROCESS_MODE_DISABLED); + v[0].c->set_process_mode(Node::PROCESS_MODE_DISABLED); + SEND_GUI_MOUSE_MOTION_EVENT(on_02, MouseButtonMask::NONE, Key::NONE); + tree->physics_process(1); + CHECK_FALSE(v[0].a->enter_id); + CHECK_FALSE(v[0].a->exit_id); + CHECK(v[2].a->enter_id); + CHECK_FALSE(v[2].a->exit_id); + for (PickingCollider E : v) { + E.a->test_reset(); + } + + SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE); + tree->physics_process(1); + CHECK_FALSE(v[0].a->enter_id); + CHECK_FALSE(v[0].a->exit_id); + CHECK_FALSE(v[2].a->enter_id); + CHECK(v[2].a->exit_id); + + for (PickingCollider E : v) { + E.a->test_reset(); + } + v[0].a->set_process_mode(Node::PROCESS_MODE_ALWAYS); + v[0].c->set_process_mode(Node::PROCESS_MODE_ALWAYS); + } + + SUBCASE("[Viewport][Picking2D] Multiple events in series") { + SEND_GUI_MOUSE_MOTION_EVENT(on_0, MouseButtonMask::NONE, Key::NONE); + SEND_GUI_MOUSE_MOTION_EVENT(on_0 + Point2i(10, 0), MouseButtonMask::NONE, Key::NONE); + tree->physics_process(1); + + for (int i = 0; i < v.size(); i++) { + if (i < 1) { + CHECK(v[i].a->enter_id); + } else { + CHECK_FALSE(v[i].a->enter_id); + } + CHECK_FALSE(v[i].a->exit_id); + v[i].a->test_reset(); + } + + SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE); + SEND_GUI_MOUSE_MOTION_EVENT(on_background + Point2i(10, 10), MouseButtonMask::NONE, Key::NONE); + tree->physics_process(1); + + for (int i = 0; i < v.size(); i++) { + CHECK_FALSE(v[i].a->enter_id); + if (i < 1) { + CHECK(v[i].a->exit_id); + } else { + CHECK_FALSE(v[i].a->exit_id); + } + v[i].a->test_reset(); + } + } + + SUBCASE("[Viewport][Picking2D] Disable Picking") { + SEND_GUI_MOUSE_MOTION_EVENT(on_02, MouseButtonMask::NONE, Key::NONE); + + root->set_physics_object_picking(false); + CHECK_FALSE(root->get_physics_object_picking()); + + tree->physics_process(1); + + for (int i = 0; i < v.size(); i++) { + CHECK_FALSE(v[i].a->enter_id); + v[i].a->test_reset(); + } + + root->set_physics_object_picking(true); + CHECK(root->get_physics_object_picking()); + } + + SUBCASE("[Viewport][Picking2D] CollisionObject in CanvasLayer") { + CanvasLayer *node_c = memnew(CanvasLayer); + node_c->set_rotation(Math_PI); + node_c->set_offset(Point2i(100, 100)); + root->add_child(node_c); + + v[2].a->reparent(node_c, false); + v[3].a->reparent(node_c, false); + + SEND_GUI_MOUSE_MOTION_EVENT(on_02, MouseButtonMask::NONE, Key::NONE); + tree->physics_process(1); + + for (int i = 0; i < v.size(); i++) { + if (i == 0 || i == 3) { + CHECK(v[i].a->enter_id); + } else { + CHECK_FALSE(v[i].a->enter_id); + } + v[i].a->test_reset(); + } + + v[2].a->reparent(node_b, false); + v[3].a->reparent(node_b, false); + root->remove_child(node_c); + memdelete(node_c); + } + + SUBCASE("[Viewport][Picking2D] Picking Sort") { + root->set_physics_object_picking_sort(true); + CHECK(root->get_physics_object_picking_sort()); + + SUBCASE("[Viewport][Picking2D] Picking Sort Z-Index") { + node_a->set_z_index(10); + v[0].a->set_z_index(0); + v[1].a->set_z_index(2); + node_b->set_z_index(5); + v[2].a->set_z_index(8); + v[3].a->set_z_index(11); + v[3].a->set_z_as_relative(false); + + TestArea2D::counter = 0; + SEND_GUI_MOUSE_MOTION_EVENT(on_all, MouseButtonMask::NONE, Key::NONE); + tree->physics_process(1); + + CHECK(v[0].a->enter_id == 4); + CHECK(v[1].a->enter_id == 2); + CHECK(v[2].a->enter_id == 1); + CHECK(v[3].a->enter_id == 3); + for (int i = 0; i < v.size(); i++) { + CHECK_FALSE(v[i].a->exit_id); + v[i].a->test_reset(); + } + + TestArea2D::counter = 0; + SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE); + tree->physics_process(1); + + CHECK(v[0].a->exit_id == 4); + CHECK(v[1].a->exit_id == 2); + CHECK(v[2].a->exit_id == 1); + CHECK(v[3].a->exit_id == 3); + for (int i = 0; i < v.size(); i++) { + CHECK_FALSE(v[i].a->enter_id); + v[i].a->set_z_as_relative(true); + v[i].a->set_z_index(0); + v[i].a->test_reset(); + } + + node_a->set_z_index(0); + node_b->set_z_index(0); + } + + SUBCASE("[Viewport][Picking2D] Picking Sort Scene Tree Location") { + TestArea2D::counter = 0; + SEND_GUI_MOUSE_MOTION_EVENT(on_all, MouseButtonMask::NONE, Key::NONE); + tree->physics_process(1); + + for (int i = 0; i < v.size(); i++) { + CHECK(v[i].a->enter_id == 4 - i); + CHECK_FALSE(v[i].a->exit_id); + v[i].a->test_reset(); + } + + TestArea2D::counter = 0; + SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE); + tree->physics_process(1); + + for (int i = 0; i < v.size(); i++) { + CHECK_FALSE(v[i].a->enter_id); + CHECK(v[i].a->exit_id == 4 - i); + v[i].a->test_reset(); + } + } + + root->set_physics_object_picking_sort(false); + CHECK_FALSE(root->get_physics_object_picking_sort()); + } + + SUBCASE("[Viewport][Picking2D] Mouse Button") { + SEND_GUI_MOUSE_BUTTON_EVENT(on_0, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE); + tree->physics_process(1); + + for (int i = 0; i < v.size(); i++) { + if (i == 0) { + CHECK(v[i].a->enter_id); + } else { + CHECK_FALSE(v[i].a->enter_id); + } + CHECK_FALSE(v[i].a->exit_id); + v[i].a->test_reset(); + } + + SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(on_0, MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE); + tree->physics_process(1); + + for (int i = 0; i < v.size(); i++) { + CHECK_FALSE(v[i].a->enter_id); + CHECK_FALSE(v[i].a->exit_id); + v[i].a->test_reset(); + } + } + + SUBCASE("[Viewport][Picking2D] Screen Touch") { + SEND_GUI_TOUCH_EVENT(on_01, true, false); + tree->physics_process(1); + for (int i = 0; i < v.size(); i++) { + if (i < 2) { + Ref<InputEventScreenTouch> st = v[i].a->last_input_event; + CHECK(st.is_valid()); + } else { + CHECK(v[i].a->last_input_event.is_null()); + } + v[i].a->test_reset(); + } + } + + for (PickingCollider E : v) { + SIGNAL_UNWATCH(E.a, SNAME("mouse_entered")); + SIGNAL_UNWATCH(E.a, SNAME("mouse_exited")); + memdelete(E.c); + memdelete(E.a); + } +} + +TEST_CASE("[SceneTree][Viewport] Embedded Windows") { + Window *root = SceneTree::get_singleton()->get_root(); + Window *w = memnew(Window); + + SUBCASE("[Viewport] Safe-rect of embedded Window") { + root->add_child(w); + root->subwindow_set_popup_safe_rect(w, Rect2i(10, 10, 10, 10)); + CHECK_EQ(root->subwindow_get_popup_safe_rect(w), Rect2i(10, 10, 10, 10)); + root->remove_child(w); + CHECK_EQ(root->subwindow_get_popup_safe_rect(w), Rect2i()); + } + + memdelete(w); +} + } // namespace TestViewport #endif // TEST_VIEWPORT_H diff --git a/tests/scene/test_window.h b/tests/scene/test_window.h new file mode 100644 index 0000000000..e0c55101de --- /dev/null +++ b/tests/scene/test_window.h @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* test_window.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_WINDOW_H +#define TEST_WINDOW_H + +#include "scene/gui/control.h" +#include "scene/main/window.h" + +#include "tests/test_macros.h" + +namespace TestWindow { + +class NotificationControl : public Control { + GDCLASS(NotificationControl, Control); + +protected: + void _notification(int p_what) { + switch (p_what) { + case NOTIFICATION_MOUSE_ENTER: { + mouse_over = true; + } break; + + case NOTIFICATION_MOUSE_EXIT: { + mouse_over = false; + } break; + } + } + +public: + bool mouse_over = false; +}; + +TEST_CASE("[SceneTree][Window]") { + Window *root = SceneTree::get_singleton()->get_root(); + + SUBCASE("Control-mouse-over within Window-black bars should not happen") { + Window *w = memnew(Window); + root->add_child(w); + w->set_size(Size2i(400, 200)); + w->set_position(Size2i(0, 0)); + w->set_content_scale_size(Size2i(200, 200)); + w->set_content_scale_mode(Window::CONTENT_SCALE_MODE_CANVAS_ITEMS); + w->set_content_scale_aspect(Window::CONTENT_SCALE_ASPECT_KEEP); + NotificationControl *c = memnew(NotificationControl); + w->add_child(c); + c->set_size(Size2i(100, 100)); + c->set_position(Size2i(-50, -50)); + + CHECK_FALSE(c->mouse_over); + SEND_GUI_MOUSE_MOTION_EVENT(Point2i(110, 10), MouseButtonMask::NONE, Key::NONE); + CHECK(c->mouse_over); + SEND_GUI_MOUSE_MOTION_EVENT(Point2i(90, 10), MouseButtonMask::NONE, Key::NONE); + CHECK_FALSE(c->mouse_over); // GH-80011 + + /* TODO: + SEND_GUI_MOUSE_BUTTON_EVENT(Point2i(90, 10), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE); + SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(Point2i(90, 10), MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE); + CHECK(Control was not pressed); + */ + + memdelete(c); + memdelete(w); + } +} + +} // namespace TestWindow + +#endif // TEST_WINDOW_H diff --git a/tests/servers/rendering/test_shader_preprocessor.h b/tests/servers/rendering/test_shader_preprocessor.h new file mode 100644 index 0000000000..d65eb522e8 --- /dev/null +++ b/tests/servers/rendering/test_shader_preprocessor.h @@ -0,0 +1,333 @@ +/**************************************************************************/ +/* test_shader_preprocessor.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_SHADER_PREPROCESSOR_H +#define TEST_SHADER_PREPROCESSOR_H + +#include "servers/rendering/shader_preprocessor.h" + +#include "tests/test_macros.h" + +#include <cctype> + +namespace TestShaderPreprocessor { + +void erase_all_empty(Vector<String> &p_vec) { + int idx = p_vec.find(" "); + while (idx >= 0) { + p_vec.remove_at(idx); + idx = p_vec.find(" "); + } +} + +bool is_variable_char(unsigned char c) { + return std::isalnum(c) || c == '_'; +} + +bool is_operator_char(unsigned char c) { + return (c == '*') || (c == '+') || (c == '-') || (c == '/') || ((c >= '<') && (c <= '>')); +} + +// Remove unnecessary spaces from a line. +String remove_spaces(String &p_str) { + String res; + // Result is guaranteed to not be longer than the input. + res.resize(p_str.size()); + int wp = 0; + char32_t last = 0; + bool has_removed = false; + + for (int n = 0; n < p_str.size(); n++) { + // These test cases only use ASCII. + auto c = static_cast<unsigned char>(p_str[n]); + if (std::isblank(c)) { + has_removed = true; + } else { + if (has_removed) { + // Insert a space to avoid joining things that could potentially form a new token. + // E.g. "float x" or "- -". + if ((is_variable_char(c) && is_variable_char(last)) || + (is_operator_char(c) && is_operator_char(last))) { + res[wp++] = ' '; + } + has_removed = false; + } + res[wp++] = c; + last = c; + } + } + res.resize(wp); + return res; +} + +// The pre-processor changes indentation and inserts spaces when inserting macros. +// Re-format the code, without changing its meaning, to make it easier to compare. +String compact_spaces(String &p_str) { + Vector<String> lines = p_str.split("\n", false); + erase_all_empty(lines); + for (auto &line : lines) { + line = remove_spaces(line); + } + return String("\n").join(lines); +} + +#define CHECK_SHADER_EQ(a, b) CHECK_EQ(compact_spaces(a), compact_spaces(b)) +#define CHECK_SHADER_NE(a, b) CHECK_NE(compact_spaces(a), compact_spaces(b)) + +TEST_CASE("[ShaderPreprocessor] Simple defines") { + String code( + "#define X 1.0 // comment\n" + "#define Y mix\n" + "#define Z X\n" + "\n" + "#define func0 \\\n" + " vec3 my_fun(vec3 arg) {\\\n" + " return pow(arg, 2.2);\\\n" + " }\n" + "\n" + "func0\n" + "\n" + "fragment() {\n" + " ALBEDO = vec3(X);\n" + " float x = Y(0., Z, X);\n" + " #undef X\n" + " float X = x;\n" + " x = -Z;\n" + "}\n"); + String expected( + "vec3 my_fun(vec3 arg) { return pow(arg, 2.2); }\n" + "\n" + "fragment() {\n" + " ALBEDO = vec3( 1.0 );\n" + " float x = mix(0., 1.0 , 1.0 );\n" + " float X = x;\n" + " x = -X;\n" + "}\n"); + String result; + + ShaderPreprocessor preprocessor; + CHECK_EQ(preprocessor.preprocess(code, String("file.gdshader"), result), Error::OK); + + CHECK_SHADER_EQ(result, expected); +} + +TEST_CASE("[ShaderPreprocessor] Avoid merging adjacent tokens") { + String code( + "#define X -10\n" + "#define Y(s) s\n" + "\n" + "fragment() {\n" + " float v = 1.0-X-Y(-2);\n" + "}\n"); + String expected( + "fragment() {\n" + " float v = 1.0 - -10 - -2;\n" + "}\n"); + String result; + + ShaderPreprocessor preprocessor; + CHECK_EQ(preprocessor.preprocess(code, String("file.gdshader"), result), Error::OK); + + CHECK_SHADER_EQ(result, expected); +} + +TEST_CASE("[ShaderPreprocessor] Complex defines") { + String code( + "const float X = 2.0;\n" + "#define A(X) X*2.\n" + "#define X 1.0\n" + "#define Y Z(X, W)\n" + "#define Z max\n" + "#define C(X, Y) Z(A(Y), B(X))\n" + "#define W -X\n" + "#define B(X) X*3.\n" + "\n" + "fragment() {\n" + " float x = Y;\n" + " float y = C(5., 7.0);\n" + "}\n"); + String expected( + "const float X = 2.0;\n" + "fragment() {\n" + " float x = max(1.0, - 1.0);\n" + " float y = max(7.0*2. , 5.*3.);\n" + "}\n"); + String result; + + ShaderPreprocessor preprocessor; + CHECK_EQ(preprocessor.preprocess(code, String("file.gdshader"), result), Error::OK); + + CHECK_SHADER_EQ(result, expected); +} + +TEST_CASE("[ShaderPreprocessor] Concatenation") { + String code( + "fragment() {\n" + " #define X 1 // this is fine ##\n" + " #define y 2\n" + " #define z 3##.## 1## 4 ## 59\n" + " #define Z(y) X ## y\n" + " #define Z2(y) y##X\n" + " #define W(y) X, y\n" + " #define A(x) fl## oat a = 1##x ##.3 ## x\n" + " #define C(x, y) x##.##y\n" + " #define J(x) x##=\n" + " float Z(y) = 1.2;\n" + " float Z(z) = 2.3;\n" + " float Z2(y) = z;\n" + " float Z2(z) = 2.3;\n" + " int b = max(W(3));\n" + " Xy J(+) b J(=) 3 ? 0.1 : 0.2;\n" + " A(9);\n" + " Xy = C(X, y);\n" + "}\n"); + String expected( + "fragment() {\n" + " float Xy = 1.2;\n" + " float Xz = 2.3;\n" + " float yX = 3.1459;\n" + " float zX = 2.3;\n" + " int b = max(1, 3);\n" + " Xy += b == 3 ? 0.1 : 0.2;\n" + " float a = 19.39;\n" + " Xy = 1.2;\n" + "}\n"); + String result; + + ShaderPreprocessor preprocessor; + CHECK_EQ(preprocessor.preprocess(code, String("file.gdshader"), result), Error::OK); + + CHECK_SHADER_EQ(result, expected); +} + +TEST_CASE("[ShaderPreprocessor] Nested concatenation") { + // Concatenation ## should not expand adjacent tokens if they are macros, + // but this is currently not implemented in Godot's shader preprocessor. + // To force expanding, an extra macro should be required (B in this case). + + String code( + "fragment() {\n" + " vec2 X = vec2(0);\n" + " #define X 1\n" + " #define y 2\n" + " #define B(x, y) C(x, y)\n" + " #define C(x, y) x##.##y\n" + " C(X, y) = B(X, y);\n" + "}\n"); + String expected( + "fragment() {\n" + " vec2 X = vec2(0);\n" + " X.y = 1.2;\n" + "}\n"); + String result; + + ShaderPreprocessor preprocessor; + CHECK_EQ(preprocessor.preprocess(code, String("file.gdshader"), result), Error::OK); + + // TODO: Reverse the check when/if this is changed. + CHECK_SHADER_NE(result, expected); +} + +TEST_CASE("[ShaderPreprocessor] Concatenation sorting network") { + String code( + "fragment() {\n" + " #define ARR(X) test##X\n" + " #define ACMP(a, b) ARR(a) > ARR(b)\n" + " #define ASWAP(a, b) tmp = ARR(b); ARR(b) = ARR(a); ARR(a) = tmp;\n" + " #define ACSWAP(a, b) if(ACMP(a, b)) { ASWAP(a, b) }\n" + " float test0 = 1.2;\n" + " float test1 = 0.34;\n" + " float test3 = 0.8;\n" + " float test4 = 2.9;\n" + " float tmp;\n" + " ACSWAP(0,2)\n" + " ACSWAP(1,3)\n" + " ACSWAP(0,1)\n" + " ACSWAP(2,3)\n" + " ACSWAP(1,2)\n" + "}\n"); + String expected( + "fragment() {\n" + " float test0 = 1.2;\n" + " float test1 = 0.34;\n" + " float test3 = 0.8;\n" + " float test4 = 2.9;\n" + " float tmp;\n" + " if(test0 > test2) { tmp = test2; test2 = test0; test0 = tmp; }\n" + " if(test1 > test3) { tmp = test3; test3 = test1; test1 = tmp; }\n" + " if(test0 > test1) { tmp = test1; test1 = test0; test0 = tmp; }\n" + " if(test2 > test3) { tmp = test3; test3 = test2; test2 = tmp; }\n" + " if(test1 > test2) { tmp = test2; test2 = test1; test1 = tmp; }\n" + "}\n"); + String result; + + ShaderPreprocessor preprocessor; + CHECK_EQ(preprocessor.preprocess(code, String("file.gdshader"), result), Error::OK); + + CHECK_SHADER_EQ(result, expected); +} + +TEST_CASE("[ShaderPreprocessor] Undefined behaviour") { + // None of these are valid concatenation, nor valid shader code. + // Don't care about results, just make sure there's no crash. + const String filename("somefile.gdshader"); + String result; + ShaderPreprocessor preprocessor; + + preprocessor.preprocess("#define X ###\nX\n", filename, result); + preprocessor.preprocess("#define X ####\nX\n", filename, result); + preprocessor.preprocess("#define X #####\nX\n", filename, result); + preprocessor.preprocess("#define X 1 ### 2\nX\n", filename, result); + preprocessor.preprocess("#define X 1 #### 2\nX\n", filename, result); + preprocessor.preprocess("#define X 1 ##### 2\nX\n", filename, result); + preprocessor.preprocess("#define X ### 2\nX\n", filename, result); + preprocessor.preprocess("#define X #### 2\nX\n", filename, result); + preprocessor.preprocess("#define X ##### 2\nX\n", filename, result); + preprocessor.preprocess("#define X 1 ###\nX\n", filename, result); + preprocessor.preprocess("#define X 1 ####\nX\n", filename, result); + preprocessor.preprocess("#define X 1 #####\nX\n", filename, result); +} + +TEST_CASE("[ShaderPreprocessor] Invalid concatenations") { + const String filename("somefile.gdshader"); + String result; + ShaderPreprocessor preprocessor; + + CHECK_NE(preprocessor.preprocess("#define X ##", filename, result), Error::OK); + CHECK_NE(preprocessor.preprocess("#define X 1 ##", filename, result), Error::OK); + CHECK_NE(preprocessor.preprocess("#define X ## 1", filename, result), Error::OK); + CHECK_NE(preprocessor.preprocess("#define X(y) ## ", filename, result), Error::OK); + CHECK_NE(preprocessor.preprocess("#define X(y) y ## ", filename, result), Error::OK); + CHECK_NE(preprocessor.preprocess("#define X(y) ## y", filename, result), Error::OK); +} + +} // namespace TestShaderPreprocessor + +#endif // TEST_SHADER_PREPROCESSOR_H diff --git a/tests/servers/test_navigation_server_3d.h b/tests/servers/test_navigation_server_3d.h index 8ea69ec67c..a116559cb2 100644 --- a/tests/servers/test_navigation_server_3d.h +++ b/tests/servers/test_navigation_server_3d.h @@ -423,6 +423,7 @@ TEST_SUITE("[Navigation]") { navigation_server->free(map); } +#ifndef DISABLE_DEPRECATED // This test case uses only public APIs on purpose - other test cases use simplified baking. // FIXME: Remove once deprecated `region_bake_navigation_mesh()` is removed. TEST_CASE("[NavigationServer3D][SceneTree][DEPRECATED] Server should be able to bake map correctly") { @@ -470,6 +471,7 @@ TEST_SUITE("[Navigation]") { memdelete(mesh_instance); memdelete(node_3d); } +#endif // DISABLE_DEPRECATED // This test case uses only public APIs on purpose - other test cases use simplified baking. TEST_CASE("[NavigationServer3D][SceneTree] Server should be able to bake map correctly") { diff --git a/tests/test_macros.h b/tests/test_macros.h index d39da7f8e8..bc85ec6ddc 100644 --- a/tests/test_macros.h +++ b/tests/test_macros.h @@ -177,6 +177,13 @@ int register_test_command(String p_command, TestFunc p_function); _UPDATE_EVENT_MODIFERS(event, m_modifers); \ event->set_pressed(true); +#define _CREATE_GUI_TOUCH_EVENT(m_screen_pos, m_pressed, m_double) \ + Ref<InputEventScreenTouch> event; \ + event.instantiate(); \ + event->set_position(m_screen_pos); \ + event->set_pressed(m_pressed); \ + event->set_double_tap(m_double); + #define SEND_GUI_MOUSE_BUTTON_EVENT(m_screen_pos, m_input, m_mask, m_modifers) \ { \ _CREATE_GUI_MOUSE_EVENT(m_screen_pos, m_input, m_mask, m_modifers); \ @@ -215,6 +222,13 @@ int register_test_command(String p_command, TestFunc p_function); CoreGlobals::print_error_enabled = errors_enabled; \ } +#define SEND_GUI_TOUCH_EVENT(m_screen_pos, m_pressed, m_double) \ + { \ + _CREATE_GUI_TOUCH_EVENT(m_screen_pos, m_pressed, m_double) \ + _SEND_DISPLAYSERVER_EVENT(event); \ + MessageQueue::get_singleton()->flush(); \ + } + // Utility class / macros for testing signals // // Use SIGNAL_WATCH(*object, "signal_name") to start watching diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 96ee146e77..f1e348345b 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -72,6 +72,7 @@ #include "tests/core/string/test_node_path.h" #include "tests/core/string/test_string.h" #include "tests/core/string/test_translation.h" +#include "tests/core/string/test_translation_server.h" #include "tests/core/templates/test_command_queue.h" #include "tests/core/templates/test_hash_map.h" #include "tests/core/templates/test_hash_set.h" @@ -105,6 +106,7 @@ #include "tests/scene/test_navigation_region_2d.h" #include "tests/scene/test_navigation_region_3d.h" #include "tests/scene/test_node.h" +#include "tests/scene/test_packed_scene.h" #include "tests/scene/test_path_2d.h" #include "tests/scene/test_path_3d.h" #include "tests/scene/test_primitives.h" @@ -113,6 +115,8 @@ #include "tests/scene/test_theme.h" #include "tests/scene/test_viewport.h" #include "tests/scene/test_visual_shader.h" +#include "tests/scene/test_window.h" +#include "tests/servers/rendering/test_shader_preprocessor.h" #include "tests/servers/test_navigation_server_2d.h" #include "tests/servers/test_navigation_server_3d.h" #include "tests/servers/test_text_server.h" diff --git a/thirdparty/README.md b/thirdparty/README.md index a918acbe77..27705de997 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -315,14 +315,14 @@ Files extracted from upstream source: ## libpng - Upstream: http://libpng.org/pub/png/libpng.html -- Version: 1.6.38 (0a158f3506502dfa23edfc42790dfaed82efba17, 2022) +- Version: 1.6.40 (f135775ad4e5d4408d2e12ffcc71bb36e6b48551, 2023) - License: libpng/zlib Files extracted from upstream source: - all .c and .h files of the main directory, except from `example.c` and `pngtest.c` -- the arm/ folder +- `arm/`, `intel/` and `powerpc/` folders - `scripts/pnglibconf.h.prebuilt` as `pnglibconf.h` - `LICENSE` @@ -356,7 +356,7 @@ Files extracted from upstream source: ## libwebp - Upstream: https://chromium.googlesource.com/webm/libwebp/ -- Version: 1.3.0 (b557776962a3dcc985d83bd4ed94e1e2e50d0fa2, 2022) +- Version: 1.3.1 (fd7bb21c0cb56e8a82e9bfa376164b842f433f3b, 2023) - License: BSD-3-Clause Files extracted from upstream source: @@ -370,7 +370,7 @@ Patch `godot-node-debug-fix.patch` workarounds shadowing of godot's Node class i ## mbedtls - Upstream: https://github.com/Mbed-TLS/mbedtls -- Version: 2.18.3 (981743de6fcdbe672e482b6fd724d31d0a0d2476, 2023) +- Version: 2.28.4 (aeb97a18913a86f051afab11b2c92c6be0c2eb83, 2023) - License: Apache 2.0 File extracted from upstream release tarball: @@ -422,7 +422,7 @@ to solve some MSVC warnings. See the patches in the `patches` directory. ## miniupnpc - Upstream: https://github.com/miniupnp/miniupnp -- Version: 2.2.4 (7d1d8bc3868b08ad003bad235eee57562b95b76d, 2022) +- Version: 2.2.5 (58837ef586278d18cbebee50be758835ed4be79a, 2023) - License: BSD-3-Clause Files extracted from upstream source: diff --git a/thirdparty/libpng/LICENSE b/thirdparty/libpng/LICENSE index c8ad24eecf..086d1c2fda 100644 --- a/thirdparty/libpng/LICENSE +++ b/thirdparty/libpng/LICENSE @@ -4,8 +4,8 @@ COPYRIGHT NOTICE, DISCLAIMER, and LICENSE PNG Reference Library License version 2 --------------------------------------- - * Copyright (c) 1995-2022 The PNG Reference Library Authors. - * Copyright (c) 2018-2022 Cosmin Truta. + * Copyright (c) 1995-2023 The PNG Reference Library Authors. + * Copyright (c) 2018-2023 Cosmin Truta. * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson. * Copyright (c) 1996-1997 Andreas Dilger. * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. diff --git a/thirdparty/libpng/intel/filter_sse2_intrinsics.c b/thirdparty/libpng/intel/filter_sse2_intrinsics.c new file mode 100644 index 0000000000..d3c0fe9e2d --- /dev/null +++ b/thirdparty/libpng/intel/filter_sse2_intrinsics.c @@ -0,0 +1,391 @@ + +/* filter_sse2_intrinsics.c - SSE2 optimized filter functions + * + * Copyright (c) 2018 Cosmin Truta + * Copyright (c) 2016-2017 Glenn Randers-Pehrson + * Written by Mike Klein and Matt Sarett + * Derived from arm/filter_neon_intrinsics.c + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#include "../pngpriv.h" + +#ifdef PNG_READ_SUPPORTED + +#if PNG_INTEL_SSE_IMPLEMENTATION > 0 + +#include <immintrin.h> + +/* Functions in this file look at most 3 pixels (a,b,c) to predict the 4th (d). + * They're positioned like this: + * prev: c b + * row: a d + * The Sub filter predicts d=a, Avg d=(a+b)/2, and Paeth predicts d to be + * whichever of a, b, or c is closest to p=a+b-c. + */ + +static __m128i load4(const void* p) { + int tmp; + memcpy(&tmp, p, sizeof(tmp)); + return _mm_cvtsi32_si128(tmp); +} + +static void store4(void* p, __m128i v) { + int tmp = _mm_cvtsi128_si32(v); + memcpy(p, &tmp, sizeof(int)); +} + +static __m128i load3(const void* p) { + png_uint_32 tmp = 0; + memcpy(&tmp, p, 3); + return _mm_cvtsi32_si128(tmp); +} + +static void store3(void* p, __m128i v) { + int tmp = _mm_cvtsi128_si32(v); + memcpy(p, &tmp, 3); +} + +void png_read_filter_row_sub3_sse2(png_row_infop row_info, png_bytep row, + png_const_bytep prev) +{ + /* The Sub filter predicts each pixel as the previous pixel, a. + * There is no pixel to the left of the first pixel. It's encoded directly. + * That works with our main loop if we just say that left pixel was zero. + */ + size_t rb; + + __m128i a, d = _mm_setzero_si128(); + + png_debug(1, "in png_read_filter_row_sub3_sse2"); + + rb = row_info->rowbytes; + while (rb >= 4) { + a = d; d = load4(row); + d = _mm_add_epi8(d, a); + store3(row, d); + + row += 3; + rb -= 3; + } + if (rb > 0) { + a = d; d = load3(row); + d = _mm_add_epi8(d, a); + store3(row, d); + + row += 3; + rb -= 3; + } + PNG_UNUSED(prev) +} + +void png_read_filter_row_sub4_sse2(png_row_infop row_info, png_bytep row, + png_const_bytep prev) +{ + /* The Sub filter predicts each pixel as the previous pixel, a. + * There is no pixel to the left of the first pixel. It's encoded directly. + * That works with our main loop if we just say that left pixel was zero. + */ + size_t rb; + + __m128i a, d = _mm_setzero_si128(); + + png_debug(1, "in png_read_filter_row_sub4_sse2"); + + rb = row_info->rowbytes+4; + while (rb > 4) { + a = d; d = load4(row); + d = _mm_add_epi8(d, a); + store4(row, d); + + row += 4; + rb -= 4; + } + PNG_UNUSED(prev) +} + +void png_read_filter_row_avg3_sse2(png_row_infop row_info, png_bytep row, + png_const_bytep prev) +{ + /* The Avg filter predicts each pixel as the (truncated) average of a and b. + * There's no pixel to the left of the first pixel. Luckily, it's + * predicted to be half of the pixel above it. So again, this works + * perfectly with our loop if we make sure a starts at zero. + */ + + size_t rb; + + const __m128i zero = _mm_setzero_si128(); + + __m128i b; + __m128i a, d = zero; + + png_debug(1, "in png_read_filter_row_avg3_sse2"); + rb = row_info->rowbytes; + while (rb >= 4) { + __m128i avg; + b = load4(prev); + a = d; d = load4(row ); + + /* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */ + avg = _mm_avg_epu8(a,b); + /* ...but we can fix it up by subtracting off 1 if it rounded up. */ + avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a,b), + _mm_set1_epi8(1))); + d = _mm_add_epi8(d, avg); + store3(row, d); + + prev += 3; + row += 3; + rb -= 3; + } + if (rb > 0) { + __m128i avg; + b = load3(prev); + a = d; d = load3(row ); + + /* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */ + avg = _mm_avg_epu8(a,b); + /* ...but we can fix it up by subtracting off 1 if it rounded up. */ + avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a,b), + _mm_set1_epi8(1))); + + d = _mm_add_epi8(d, avg); + store3(row, d); + + prev += 3; + row += 3; + rb -= 3; + } +} + +void png_read_filter_row_avg4_sse2(png_row_infop row_info, png_bytep row, + png_const_bytep prev) +{ + /* The Avg filter predicts each pixel as the (truncated) average of a and b. + * There's no pixel to the left of the first pixel. Luckily, it's + * predicted to be half of the pixel above it. So again, this works + * perfectly with our loop if we make sure a starts at zero. + */ + size_t rb; + const __m128i zero = _mm_setzero_si128(); + __m128i b; + __m128i a, d = zero; + + png_debug(1, "in png_read_filter_row_avg4_sse2"); + + rb = row_info->rowbytes+4; + while (rb > 4) { + __m128i avg; + b = load4(prev); + a = d; d = load4(row ); + + /* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */ + avg = _mm_avg_epu8(a,b); + /* ...but we can fix it up by subtracting off 1 if it rounded up. */ + avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a,b), + _mm_set1_epi8(1))); + + d = _mm_add_epi8(d, avg); + store4(row, d); + + prev += 4; + row += 4; + rb -= 4; + } +} + +/* Returns |x| for 16-bit lanes. */ +static __m128i abs_i16(__m128i x) { +#if PNG_INTEL_SSE_IMPLEMENTATION >= 2 + return _mm_abs_epi16(x); +#else + /* Read this all as, return x<0 ? -x : x. + * To negate two's complement, you flip all the bits then add 1. + */ + __m128i is_negative = _mm_cmplt_epi16(x, _mm_setzero_si128()); + + /* Flip negative lanes. */ + x = _mm_xor_si128(x, is_negative); + + /* +1 to negative lanes, else +0. */ + x = _mm_sub_epi16(x, is_negative); + return x; +#endif +} + +/* Bytewise c ? t : e. */ +static __m128i if_then_else(__m128i c, __m128i t, __m128i e) { +#if PNG_INTEL_SSE_IMPLEMENTATION >= 3 + return _mm_blendv_epi8(e,t,c); +#else + return _mm_or_si128(_mm_and_si128(c, t), _mm_andnot_si128(c, e)); +#endif +} + +void png_read_filter_row_paeth3_sse2(png_row_infop row_info, png_bytep row, + png_const_bytep prev) +{ + /* Paeth tries to predict pixel d using the pixel to the left of it, a, + * and two pixels from the previous row, b and c: + * prev: c b + * row: a d + * The Paeth function predicts d to be whichever of a, b, or c is nearest to + * p=a+b-c. + * + * The first pixel has no left context, and so uses an Up filter, p = b. + * This works naturally with our main loop's p = a+b-c if we force a and c + * to zero. + * Here we zero b and d, which become c and a respectively at the start of + * the loop. + */ + size_t rb; + const __m128i zero = _mm_setzero_si128(); + __m128i c, b = zero, + a, d = zero; + + png_debug(1, "in png_read_filter_row_paeth3_sse2"); + + rb = row_info->rowbytes; + while (rb >= 4) { + /* It's easiest to do this math (particularly, deal with pc) with 16-bit + * intermediates. + */ + __m128i pa,pb,pc,smallest,nearest; + c = b; b = _mm_unpacklo_epi8(load4(prev), zero); + a = d; d = _mm_unpacklo_epi8(load4(row ), zero); + + /* (p-a) == (a+b-c - a) == (b-c) */ + + pa = _mm_sub_epi16(b,c); + + /* (p-b) == (a+b-c - b) == (a-c) */ + pb = _mm_sub_epi16(a,c); + + /* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */ + pc = _mm_add_epi16(pa,pb); + + pa = abs_i16(pa); /* |p-a| */ + pb = abs_i16(pb); /* |p-b| */ + pc = abs_i16(pc); /* |p-c| */ + + smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb)); + + /* Paeth breaks ties favoring a over b over c. */ + nearest = if_then_else(_mm_cmpeq_epi16(smallest, pa), a, + if_then_else(_mm_cmpeq_epi16(smallest, pb), b, + c)); + + /* Note `_epi8`: we need addition to wrap modulo 255. */ + d = _mm_add_epi8(d, nearest); + store3(row, _mm_packus_epi16(d,d)); + + prev += 3; + row += 3; + rb -= 3; + } + if (rb > 0) { + /* It's easiest to do this math (particularly, deal with pc) with 16-bit + * intermediates. + */ + __m128i pa,pb,pc,smallest,nearest; + c = b; b = _mm_unpacklo_epi8(load3(prev), zero); + a = d; d = _mm_unpacklo_epi8(load3(row ), zero); + + /* (p-a) == (a+b-c - a) == (b-c) */ + pa = _mm_sub_epi16(b,c); + + /* (p-b) == (a+b-c - b) == (a-c) */ + pb = _mm_sub_epi16(a,c); + + /* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */ + pc = _mm_add_epi16(pa,pb); + + pa = abs_i16(pa); /* |p-a| */ + pb = abs_i16(pb); /* |p-b| */ + pc = abs_i16(pc); /* |p-c| */ + + smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb)); + + /* Paeth breaks ties favoring a over b over c. */ + nearest = if_then_else(_mm_cmpeq_epi16(smallest, pa), a, + if_then_else(_mm_cmpeq_epi16(smallest, pb), b, + c)); + + /* Note `_epi8`: we need addition to wrap modulo 255. */ + d = _mm_add_epi8(d, nearest); + store3(row, _mm_packus_epi16(d,d)); + + prev += 3; + row += 3; + rb -= 3; + } +} + +void png_read_filter_row_paeth4_sse2(png_row_infop row_info, png_bytep row, + png_const_bytep prev) +{ + /* Paeth tries to predict pixel d using the pixel to the left of it, a, + * and two pixels from the previous row, b and c: + * prev: c b + * row: a d + * The Paeth function predicts d to be whichever of a, b, or c is nearest to + * p=a+b-c. + * + * The first pixel has no left context, and so uses an Up filter, p = b. + * This works naturally with our main loop's p = a+b-c if we force a and c + * to zero. + * Here we zero b and d, which become c and a respectively at the start of + * the loop. + */ + size_t rb; + const __m128i zero = _mm_setzero_si128(); + __m128i pa,pb,pc,smallest,nearest; + __m128i c, b = zero, + a, d = zero; + + png_debug(1, "in png_read_filter_row_paeth4_sse2"); + + rb = row_info->rowbytes+4; + while (rb > 4) { + /* It's easiest to do this math (particularly, deal with pc) with 16-bit + * intermediates. + */ + c = b; b = _mm_unpacklo_epi8(load4(prev), zero); + a = d; d = _mm_unpacklo_epi8(load4(row ), zero); + + /* (p-a) == (a+b-c - a) == (b-c) */ + pa = _mm_sub_epi16(b,c); + + /* (p-b) == (a+b-c - b) == (a-c) */ + pb = _mm_sub_epi16(a,c); + + /* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */ + pc = _mm_add_epi16(pa,pb); + + pa = abs_i16(pa); /* |p-a| */ + pb = abs_i16(pb); /* |p-b| */ + pc = abs_i16(pc); /* |p-c| */ + + smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb)); + + /* Paeth breaks ties favoring a over b over c. */ + nearest = if_then_else(_mm_cmpeq_epi16(smallest, pa), a, + if_then_else(_mm_cmpeq_epi16(smallest, pb), b, + c)); + + /* Note `_epi8`: we need addition to wrap modulo 255. */ + d = _mm_add_epi8(d, nearest); + store4(row, _mm_packus_epi16(d,d)); + + prev += 4; + row += 4; + rb -= 4; + } +} + +#endif /* PNG_INTEL_SSE_IMPLEMENTATION > 0 */ +#endif /* READ */ diff --git a/thirdparty/libpng/intel/intel_init.c b/thirdparty/libpng/intel/intel_init.c new file mode 100644 index 0000000000..2f8168b7c4 --- /dev/null +++ b/thirdparty/libpng/intel/intel_init.c @@ -0,0 +1,52 @@ + +/* intel_init.c - SSE2 optimized filter functions + * + * Copyright (c) 2018 Cosmin Truta + * Copyright (c) 2016-2017 Glenn Randers-Pehrson + * Written by Mike Klein and Matt Sarett, Google, Inc. + * Derived from arm/arm_init.c + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#include "../pngpriv.h" + +#ifdef PNG_READ_SUPPORTED +#if PNG_INTEL_SSE_IMPLEMENTATION > 0 + +void +png_init_filter_functions_sse2(png_structp pp, unsigned int bpp) +{ + /* The techniques used to implement each of these filters in SSE operate on + * one pixel at a time. + * So they generally speed up 3bpp images about 3x, 4bpp images about 4x. + * They can scale up to 6 and 8 bpp images and down to 2 bpp images, + * but they'd not likely have any benefit for 1bpp images. + * Most of these can be implemented using only MMX and 64-bit registers, + * but they end up a bit slower than using the equally-ubiquitous SSE2. + */ + png_debug(1, "in png_init_filter_functions_sse2"); + if (bpp == 3) + { + pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub3_sse2; + pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg3_sse2; + pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = + png_read_filter_row_paeth3_sse2; + } + else if (bpp == 4) + { + pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub4_sse2; + pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg4_sse2; + pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = + png_read_filter_row_paeth4_sse2; + } + + /* No need optimize PNG_FILTER_VALUE_UP. The compiler should + * autovectorize. + */ +} + +#endif /* PNG_INTEL_SSE_IMPLEMENTATION > 0 */ +#endif /* PNG_READ_SUPPORTED */ diff --git a/thirdparty/libpng/png.c b/thirdparty/libpng/png.c index 4f3e8bbd31..d6471b06cc 100644 --- a/thirdparty/libpng/png.c +++ b/thirdparty/libpng/png.c @@ -1,7 +1,7 @@ /* png.c - location for general purpose libpng functions * - * Copyright (c) 2018-2022 Cosmin Truta + * Copyright (c) 2018-2023 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -14,7 +14,7 @@ #include "pngpriv.h" /* Generate a compiler error if there is an old png.h in the search path. */ -typedef png_libpng_version_1_6_39 Your_png_h_is_not_version_1_6_39; +typedef png_libpng_version_1_6_40 Your_png_h_is_not_version_1_6_40; #ifdef __GNUC__ /* The version tests may need to be added to, but the problem warning has @@ -815,8 +815,8 @@ png_get_copyright(png_const_structrp png_ptr) return PNG_STRING_COPYRIGHT #else return PNG_STRING_NEWLINE \ - "libpng version 1.6.39" PNG_STRING_NEWLINE \ - "Copyright (c) 2018-2022 Cosmin Truta" PNG_STRING_NEWLINE \ + "libpng version 1.6.40" PNG_STRING_NEWLINE \ + "Copyright (c) 2018-2023 Cosmin Truta" PNG_STRING_NEWLINE \ "Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson" \ PNG_STRING_NEWLINE \ "Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \ diff --git a/thirdparty/libpng/png.h b/thirdparty/libpng/png.h index f109cdf336..cfc4841099 100644 --- a/thirdparty/libpng/png.h +++ b/thirdparty/libpng/png.h @@ -1,9 +1,9 @@ /* png.h - header file for PNG reference library * - * libpng version 1.6.39 - November 20, 2022 + * libpng version 1.6.40 * - * Copyright (c) 2018-2022 Cosmin Truta + * Copyright (c) 2018-2023 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -15,7 +15,7 @@ * libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger * libpng versions 0.97, January 1998, through 1.6.35, July 2018: * Glenn Randers-Pehrson - * libpng versions 1.6.36, December 2018, through 1.6.39, November 2022: + * libpng versions 1.6.36, December 2018, through 1.6.40, June 2023: * Cosmin Truta * See also "Contributing Authors", below. */ @@ -27,8 +27,8 @@ * PNG Reference Library License version 2 * --------------------------------------- * - * * Copyright (c) 1995-2022 The PNG Reference Library Authors. - * * Copyright (c) 2018-2022 Cosmin Truta. + * * Copyright (c) 1995-2023 The PNG Reference Library Authors. + * * Copyright (c) 2018-2023 Cosmin Truta. * * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson. * * Copyright (c) 1996-1997 Andreas Dilger. * * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -239,7 +239,7 @@ * ... * 1.5.30 15 10530 15.so.15.30[.0] * ... - * 1.6.39 16 10639 16.so.16.39[.0] + * 1.6.40 16 10640 16.so.16.40[.0] * * Henceforth the source version will match the shared-library major and * minor numbers; the shared-library major version number will be used for @@ -278,8 +278,8 @@ */ /* Version information for png.h - this should match the version in png.c */ -#define PNG_LIBPNG_VER_STRING "1.6.39" -#define PNG_HEADER_VERSION_STRING " libpng version 1.6.39 - November 20, 2022\n" +#define PNG_LIBPNG_VER_STRING "1.6.40" +#define PNG_HEADER_VERSION_STRING " libpng version 1.6.40 - June 21, 2023\n" #define PNG_LIBPNG_VER_SONUM 16 #define PNG_LIBPNG_VER_DLLNUM 16 @@ -287,7 +287,7 @@ /* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */ #define PNG_LIBPNG_VER_MAJOR 1 #define PNG_LIBPNG_VER_MINOR 6 -#define PNG_LIBPNG_VER_RELEASE 39 +#define PNG_LIBPNG_VER_RELEASE 40 /* This should be zero for a public release, or non-zero for a * development version. [Deprecated] @@ -318,7 +318,7 @@ * From version 1.0.1 it is: * XXYYZZ, where XX=major, YY=minor, ZZ=release */ -#define PNG_LIBPNG_VER 10639 /* 1.6.39 */ +#define PNG_LIBPNG_VER 10640 /* 1.6.40 */ /* Library configuration: these options cannot be changed after * the library has been built. @@ -428,7 +428,7 @@ extern "C" { /* This triggers a compiler error in png.c, if png.c and png.h * do not agree upon the version number. */ -typedef char* png_libpng_version_1_6_39; +typedef char* png_libpng_version_1_6_40; /* Basic control structions. Read libpng-manual.txt or libpng.3 for more info. * diff --git a/thirdparty/libpng/pngconf.h b/thirdparty/libpng/pngconf.h index fcb4b43069..6671e3c335 100644 --- a/thirdparty/libpng/pngconf.h +++ b/thirdparty/libpng/pngconf.h @@ -1,7 +1,7 @@ /* pngconf.h - machine-configurable file for libpng * - * libpng version 1.6.39 + * libpng version 1.6.40 * * Copyright (c) 2018-2022 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson diff --git a/thirdparty/libpng/pngget.c b/thirdparty/libpng/pngget.c index e44933c0d2..1490a032e1 100644 --- a/thirdparty/libpng/pngget.c +++ b/thirdparty/libpng/pngget.c @@ -1,7 +1,7 @@ /* pngget.c - retrieval of values from info struct * - * Copyright (c) 2018 Cosmin Truta + * Copyright (c) 2018-2023 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -21,7 +21,18 @@ png_get_valid(png_const_structrp png_ptr, png_const_inforp info_ptr, png_uint_32 flag) { if (png_ptr != NULL && info_ptr != NULL) + { +#ifdef PNG_READ_tRNS_SUPPORTED + /* png_handle_PLTE() may have canceled a valid tRNS chunk but left the + * 'valid' flag for the detection of duplicate chunks. Do not report a + * valid tRNS chunk in this case. + */ + if (flag == PNG_INFO_tRNS && png_ptr->num_trans == 0) + return(0); +#endif + return(info_ptr->valid & flag); + } return(0); } diff --git a/thirdparty/libpng/pnglibconf.h b/thirdparty/libpng/pnglibconf.h index e5948c8ce1..c7033ae176 100644 --- a/thirdparty/libpng/pnglibconf.h +++ b/thirdparty/libpng/pnglibconf.h @@ -1,8 +1,8 @@ /* pnglibconf.h - library build configuration */ -/* libpng version 1.6.39 */ +/* libpng version 1.6.40 */ -/* Copyright (c) 2018-2022 Cosmin Truta */ +/* Copyright (c) 2018-2023 Cosmin Truta */ /* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */ /* This code is released under the libpng license. */ diff --git a/thirdparty/libpng/pngpriv.h b/thirdparty/libpng/pngpriv.h index b8a73b685d..7c19373f0b 100644 --- a/thirdparty/libpng/pngpriv.h +++ b/thirdparty/libpng/pngpriv.h @@ -1,7 +1,7 @@ /* pngpriv.h - private declarations for use inside libpng * - * Copyright (c) 2018-2022 Cosmin Truta + * Copyright (c) 2018-2023 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -626,7 +626,7 @@ #define PNG_BACKGROUND_IS_GRAY 0x800U #define PNG_HAVE_PNG_SIGNATURE 0x1000U #define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000U /* Have another chunk after IDAT */ - /* 0x4000U (unused) */ +#define PNG_WROTE_eXIf 0x4000U #define PNG_IS_READ_STRUCT 0x8000U /* Else is a write struct */ /* Flags for the transformations the PNG library does on the image data */ @@ -1910,7 +1910,7 @@ PNG_INTERNAL_FUNCTION(void,png_ascii_from_fixed,(png_const_structrp png_ptr, */ #define PNG_FP_INVALID 512 /* Available for callers as a distinct value */ -/* Result codes for the parser (boolean - true meants ok, false means +/* Result codes for the parser (boolean - true means ok, false means * not ok yet.) */ #define PNG_FP_MAYBE 0 /* The number may be valid in the future */ diff --git a/thirdparty/libpng/pngset.c b/thirdparty/libpng/pngset.c index 8c372cf415..3fc31feb0c 100644 --- a/thirdparty/libpng/pngset.c +++ b/thirdparty/libpng/pngset.c @@ -1,7 +1,7 @@ /* pngset.c - storage of image information into info struct * - * Copyright (c) 2018-2022 Cosmin Truta + * Copyright (c) 2018-2023 Cosmin Truta * Copyright (c) 1998-2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -137,46 +137,40 @@ png_set_cHRM_XYZ(png_const_structrp png_ptr, png_inforp info_ptr, double red_X, #ifdef PNG_eXIf_SUPPORTED void PNGAPI png_set_eXIf(png_const_structrp png_ptr, png_inforp info_ptr, - png_bytep eXIf_buf) + png_bytep exif) { png_warning(png_ptr, "png_set_eXIf does not work; use png_set_eXIf_1"); PNG_UNUSED(info_ptr) - PNG_UNUSED(eXIf_buf) + PNG_UNUSED(exif) } void PNGAPI png_set_eXIf_1(png_const_structrp png_ptr, png_inforp info_ptr, - png_uint_32 num_exif, png_bytep eXIf_buf) + png_uint_32 num_exif, png_bytep exif) { - int i; + png_bytep new_exif; png_debug1(1, "in %s storage function", "eXIf"); - if (png_ptr == NULL || info_ptr == NULL) + if (png_ptr == NULL || info_ptr == NULL || + (png_ptr->mode & PNG_WROTE_eXIf) != 0) return; - if (info_ptr->exif) - { - png_free(png_ptr, info_ptr->exif); - info_ptr->exif = NULL; - } + new_exif = png_voidcast(png_bytep, png_malloc_warn(png_ptr, num_exif)); - info_ptr->num_exif = num_exif; - - info_ptr->exif = png_voidcast(png_bytep, png_malloc_warn(png_ptr, - info_ptr->num_exif)); - - if (info_ptr->exif == NULL) + if (new_exif == NULL) { png_warning(png_ptr, "Insufficient memory for eXIf chunk data"); return; } - info_ptr->free_me |= PNG_FREE_EXIF; + memcpy(new_exif, exif, (size_t)num_exif); - for (i = 0; i < (int) info_ptr->num_exif; i++) - info_ptr->exif[i] = eXIf_buf[i]; + png_free_data(png_ptr, info_ptr, PNG_FREE_EXIF, 0); + info_ptr->num_exif = num_exif; + info_ptr->exif = new_exif; + info_ptr->free_me |= PNG_FREE_EXIF; info_ptr->valid |= PNG_INFO_eXIf; } #endif /* eXIf */ @@ -237,15 +231,13 @@ png_set_hIST(png_const_structrp png_ptr, png_inforp info_ptr, if (info_ptr->hist == NULL) { png_warning(png_ptr, "Insufficient memory for hIST chunk data"); - return; } - info_ptr->free_me |= PNG_FREE_HIST; - for (i = 0; i < info_ptr->num_palette; i++) info_ptr->hist[i] = hist[i]; + info_ptr->free_me |= PNG_FREE_HIST; info_ptr->valid |= PNG_INFO_hIST; } #endif @@ -367,6 +359,8 @@ png_set_pCAL(png_const_structrp png_ptr, png_inforp info_ptr, memcpy(info_ptr->pcal_purpose, purpose, length); + info_ptr->free_me |= PNG_FREE_PCAL; + png_debug(3, "storing X0, X1, type, and nparams in info"); info_ptr->pcal_X0 = X0; info_ptr->pcal_X1 = X1; @@ -383,7 +377,6 @@ png_set_pCAL(png_const_structrp png_ptr, png_inforp info_ptr, if (info_ptr->pcal_units == NULL) { png_warning(png_ptr, "Insufficient memory for pCAL units"); - return; } @@ -395,7 +388,6 @@ png_set_pCAL(png_const_structrp png_ptr, png_inforp info_ptr, if (info_ptr->pcal_params == NULL) { png_warning(png_ptr, "Insufficient memory for pCAL params"); - return; } @@ -413,7 +405,6 @@ png_set_pCAL(png_const_structrp png_ptr, png_inforp info_ptr, if (info_ptr->pcal_params[i] == NULL) { png_warning(png_ptr, "Insufficient memory for pCAL parameter"); - return; } @@ -421,7 +412,6 @@ png_set_pCAL(png_const_structrp png_ptr, png_inforp info_ptr, } info_ptr->valid |= PNG_INFO_pCAL; - info_ptr->free_me |= PNG_FREE_PCAL; } #endif @@ -478,18 +468,17 @@ png_set_sCAL_s(png_const_structrp png_ptr, png_inforp info_ptr, if (info_ptr->scal_s_height == NULL) { - png_free (png_ptr, info_ptr->scal_s_width); + png_free(png_ptr, info_ptr->scal_s_width); info_ptr->scal_s_width = NULL; png_warning(png_ptr, "Memory allocation failed while processing sCAL"); - return; } memcpy(info_ptr->scal_s_height, sheight, lengthh); - info_ptr->valid |= PNG_INFO_sCAL; info_ptr->free_me |= PNG_FREE_SCAL; + info_ptr->valid |= PNG_INFO_sCAL; } # ifdef PNG_FLOATING_POINT_SUPPORTED @@ -625,11 +614,10 @@ png_set_PLTE(png_structrp png_ptr, png_inforp info_ptr, if (num_palette > 0) memcpy(png_ptr->palette, palette, (unsigned int)num_palette * (sizeof (png_color))); + info_ptr->palette = png_ptr->palette; info_ptr->num_palette = png_ptr->num_palette = (png_uint_16)num_palette; - info_ptr->free_me |= PNG_FREE_PLTE; - info_ptr->valid |= PNG_INFO_PLTE; } @@ -1020,8 +1008,8 @@ png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH)); memcpy(info_ptr->trans_alpha, trans_alpha, (size_t)num_trans); - info_ptr->valid |= PNG_INFO_tRNS; info_ptr->free_me |= PNG_FREE_TRNS; + info_ptr->valid |= PNG_INFO_tRNS; } png_ptr->trans_alpha = info_ptr->trans_alpha; } @@ -1054,8 +1042,8 @@ png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr, if (num_trans != 0) { - info_ptr->valid |= PNG_INFO_tRNS; info_ptr->free_me |= PNG_FREE_TRNS; + info_ptr->valid |= PNG_INFO_tRNS; } } #endif @@ -1089,11 +1077,11 @@ png_set_sPLT(png_const_structrp png_ptr, { /* Out of memory or too many chunks */ png_chunk_report(png_ptr, "too many sPLT chunks", PNG_CHUNK_WRITE_ERROR); - return; } png_free(png_ptr, info_ptr->splt_palettes); + info_ptr->splt_palettes = np; info_ptr->free_me |= PNG_FREE_SPLT; @@ -1247,11 +1235,11 @@ png_set_unknown_chunks(png_const_structrp png_ptr, { png_chunk_report(png_ptr, "too many unknown chunks", PNG_CHUNK_WRITE_ERROR); - return; } png_free(png_ptr, info_ptr->unknown_chunks); + info_ptr->unknown_chunks = np; /* safe because it is initialized */ info_ptr->free_me |= PNG_FREE_UNKN; diff --git a/thirdparty/libpng/pngwrite.c b/thirdparty/libpng/pngwrite.c index 4e58d776a9..32f4bfbe7d 100644 --- a/thirdparty/libpng/pngwrite.c +++ b/thirdparty/libpng/pngwrite.c @@ -1,7 +1,7 @@ /* pngwrite.c - general routines to write a PNG file * - * Copyright (c) 2018-2022 Cosmin Truta + * Copyright (c) 2018-2023 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * Copyright (c) 1996-1997 Andreas Dilger * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. @@ -239,7 +239,10 @@ png_write_info(png_structrp png_ptr, png_const_inforp info_ptr) #ifdef PNG_WRITE_eXIf_SUPPORTED if ((info_ptr->valid & PNG_INFO_eXIf) != 0) + { png_write_eXIf(png_ptr, info_ptr->exif, info_ptr->num_exif); + png_ptr->mode |= PNG_WROTE_eXIf; + } #endif #ifdef PNG_WRITE_hIST_SUPPORTED @@ -439,8 +442,9 @@ png_write_end(png_structrp png_ptr, png_inforp info_ptr) #endif #ifdef PNG_WRITE_eXIf_SUPPORTED - if ((info_ptr->valid & PNG_INFO_eXIf) != 0) - png_write_eXIf(png_ptr, info_ptr->exif, info_ptr->num_exif); + if ((info_ptr->valid & PNG_INFO_eXIf) != 0 && + (png_ptr->mode & PNG_WROTE_eXIf) == 0) + png_write_eXIf(png_ptr, info_ptr->exif, info_ptr->num_exif); #endif #ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED diff --git a/thirdparty/libpng/powerpc/filter_vsx_intrinsics.c b/thirdparty/libpng/powerpc/filter_vsx_intrinsics.c new file mode 100644 index 0000000000..01cf8800dc --- /dev/null +++ b/thirdparty/libpng/powerpc/filter_vsx_intrinsics.c @@ -0,0 +1,768 @@ +/* filter_vsx_intrinsics.c - PowerPC optimised filter functions + * + * Copyright (c) 2018 Cosmin Truta + * Copyright (c) 2017 Glenn Randers-Pehrson + * Written by Vadim Barkov, 2017. + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#include <stdio.h> +#include <stdint.h> +#include "../pngpriv.h" + +#ifdef PNG_READ_SUPPORTED + +/* This code requires -maltivec and -mvsx on the command line: */ +#if PNG_POWERPC_VSX_IMPLEMENTATION == 1 /* intrinsics code from pngpriv.h */ + +#include <altivec.h> + +#if PNG_POWERPC_VSX_OPT > 0 + +#ifndef __VSX__ +# error "This code requires VSX support (POWER7 and later). Please provide -mvsx compiler flag." +#endif + +#define vec_ld_unaligned(vec,data) vec = vec_vsx_ld(0,data) +#define vec_st_unaligned(vec,data) vec_vsx_st(vec,0,data) + + +/* Functions in this file look at most 3 pixels (a,b,c) to predict the 4th (d). + * They're positioned like this: + * prev: c b + * row: a d + * The Sub filter predicts d=a, Avg d=(a+b)/2, and Paeth predicts d to be + * whichever of a, b, or c is closest to p=a+b-c. + * ( this is taken from ../intel/filter_sse2_intrinsics.c ) + */ + +#define vsx_declare_common_vars(row_info,row,prev_row,offset) \ + png_byte i;\ + png_bytep rp = row + offset;\ + png_const_bytep pp = prev_row;\ + size_t unaligned_top = 16 - (((size_t)rp % 16));\ + size_t istop;\ + if(unaligned_top == 16)\ + unaligned_top = 0;\ + istop = row_info->rowbytes;\ + if((unaligned_top < istop))\ + istop -= unaligned_top;\ + else{\ + unaligned_top = istop;\ + istop = 0;\ + } + +void png_read_filter_row_up_vsx(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + vector unsigned char rp_vec; + vector unsigned char pp_vec; + vsx_declare_common_vars(row_info,row,prev_row,0) + + /* Altivec operations require 16-byte aligned data + * but input can be unaligned. So we calculate + * unaligned part as usual. + */ + for (i = 0; i < unaligned_top; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); + rp++; + } + + /* Using SIMD while we can */ + while( istop >= 16 ) + { + rp_vec = vec_ld(0,rp); + vec_ld_unaligned(pp_vec,pp); + + rp_vec = vec_add(rp_vec,pp_vec); + + vec_st(rp_vec,0,rp); + + pp += 16; + rp += 16; + istop -= 16; + } + + if(istop > 0) + { + /* If byte count of row is not divisible by 16 + * we will process remaining part as usual + */ + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); + rp++; + } +} + +} + +static const vector unsigned char VSX_LEFTSHIFTED1_4 = {16,16,16,16, 0, 1, 2, 3,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_LEFTSHIFTED2_4 = {16,16,16,16,16,16,16,16, 4, 5, 6, 7,16,16,16,16}; +static const vector unsigned char VSX_LEFTSHIFTED3_4 = {16,16,16,16,16,16,16,16,16,16,16,16, 8, 9,10,11}; + +static const vector unsigned char VSX_LEFTSHIFTED1_3 = {16,16,16, 0, 1, 2,16,16,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_LEFTSHIFTED2_3 = {16,16,16,16,16,16, 3, 4, 5,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_LEFTSHIFTED3_3 = {16,16,16,16,16,16,16,16,16, 6, 7, 8,16,16,16,16}; +static const vector unsigned char VSX_LEFTSHIFTED4_3 = {16,16,16,16,16,16,16,16,16,16,16,16, 9,10,11,16}; + +static const vector unsigned char VSX_NOT_SHIFTED1_4 = {16,16,16,16, 4, 5, 6, 7,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_NOT_SHIFTED2_4 = {16,16,16,16,16,16,16,16, 8, 9,10,11,16,16,16,16}; +static const vector unsigned char VSX_NOT_SHIFTED3_4 = {16,16,16,16,16,16,16,16,16,16,16,16,12,13,14,15}; + +static const vector unsigned char VSX_NOT_SHIFTED1_3 = {16,16,16, 3, 4, 5,16,16,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_NOT_SHIFTED2_3 = {16,16,16,16,16,16, 6, 7, 8,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_NOT_SHIFTED3_3 = {16,16,16,16,16,16,16,16,16, 9,10,11,16,16,16,16}; +static const vector unsigned char VSX_NOT_SHIFTED4_3 = {16,16,16,16,16,16,16,16,16,16,16,16,12,13,14,16}; + +static const vector unsigned char VSX_CHAR_ZERO = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +#ifdef __LITTLE_ENDIAN__ + +static const vector unsigned char VSX_CHAR_TO_SHORT1_4 = { 4,16, 5,16, 6,16, 7,16,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_CHAR_TO_SHORT2_4 = { 8,16, 9,16,10,16,11,16,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_CHAR_TO_SHORT3_4 = {12,16,13,16,14,16,15,16,16,16,16,16,16,16,16,16}; + +static const vector unsigned char VSX_SHORT_TO_CHAR1_4 = {16,16,16,16, 0, 2, 4, 6,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_SHORT_TO_CHAR2_4 = {16,16,16,16,16,16,16,16, 0, 2, 4, 6,16,16,16,16}; +static const vector unsigned char VSX_SHORT_TO_CHAR3_4 = {16,16,16,16,16,16,16,16,16,16,16,16, 0, 2, 4, 6}; + +static const vector unsigned char VSX_CHAR_TO_SHORT1_3 = { 3,16, 4,16, 5,16,16,16,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_CHAR_TO_SHORT2_3 = { 6,16, 7,16, 8,16,16,16,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_CHAR_TO_SHORT3_3 = { 9,16,10,16,11,16,16,16,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_CHAR_TO_SHORT4_3 = {12,16,13,16,14,16,16,16,16,16,16,16,16,16,16,16}; + +static const vector unsigned char VSX_SHORT_TO_CHAR1_3 = {16,16,16, 0, 2, 4,16,16,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_SHORT_TO_CHAR2_3 = {16,16,16,16,16,16, 0, 2, 4,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_SHORT_TO_CHAR3_3 = {16,16,16,16,16,16,16,16,16, 0, 2, 4,16,16,16,16}; +static const vector unsigned char VSX_SHORT_TO_CHAR4_3 = {16,16,16,16,16,16,16,16,16,16,16,16, 0, 2, 4,16}; + +#elif defined(__BIG_ENDIAN__) + +static const vector unsigned char VSX_CHAR_TO_SHORT1_4 = {16, 4,16, 5,16, 6,16, 7,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_CHAR_TO_SHORT2_4 = {16, 8,16, 9,16,10,16,11,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_CHAR_TO_SHORT3_4 = {16,12,16,13,16,14,16,15,16,16,16,16,16,16,16,16}; + +static const vector unsigned char VSX_SHORT_TO_CHAR1_4 = {16,16,16,16, 1, 3, 5, 7,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_SHORT_TO_CHAR2_4 = {16,16,16,16,16,16,16,16, 1, 3, 5, 7,16,16,16,16}; +static const vector unsigned char VSX_SHORT_TO_CHAR3_4 = {16,16,16,16,16,16,16,16,16,16,16,16, 1, 3, 5, 7}; + +static const vector unsigned char VSX_CHAR_TO_SHORT1_3 = {16, 3,16, 4,16, 5,16,16,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_CHAR_TO_SHORT2_3 = {16, 6,16, 7,16, 8,16,16,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_CHAR_TO_SHORT3_3 = {16, 9,16,10,16,11,16,16,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_CHAR_TO_SHORT4_3 = {16,12,16,13,16,14,16,16,16,16,16,16,16,16,16,16}; + +static const vector unsigned char VSX_SHORT_TO_CHAR1_3 = {16,16,16, 1, 3, 5,16,16,16,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_SHORT_TO_CHAR2_3 = {16,16,16,16,16,16, 1, 3, 5,16,16,16,16,16,16,16}; +static const vector unsigned char VSX_SHORT_TO_CHAR3_3 = {16,16,16,16,16,16,16,16,16, 1, 3, 5,16,16,16,16}; +static const vector unsigned char VSX_SHORT_TO_CHAR4_3 = {16,16,16,16,16,16,16,16,16,16,16,16, 1, 3, 5,16}; + +#endif + +#define vsx_char_to_short(vec,offset,bpp) (vector unsigned short)vec_perm((vec),VSX_CHAR_ZERO,VSX_CHAR_TO_SHORT##offset##_##bpp) +#define vsx_short_to_char(vec,offset,bpp) vec_perm(((vector unsigned char)(vec)),VSX_CHAR_ZERO,VSX_SHORT_TO_CHAR##offset##_##bpp) + +#ifdef PNG_USE_ABS +# define vsx_abs(number) abs(number) +#else +# define vsx_abs(number) (number > 0) ? (number) : -(number) +#endif + +void png_read_filter_row_sub4_vsx(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_byte bpp = 4; + + vector unsigned char rp_vec; + vector unsigned char part_vec; + + vsx_declare_common_vars(row_info,row,prev_row,bpp) + + PNG_UNUSED(pp) + + /* Altivec operations require 16-byte aligned data + * but input can be unaligned. So we calculate + * unaligned part as usual. + */ + for (i = 0; i < unaligned_top; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*(rp-bpp))) & 0xff); + rp++; + } + + /* Using SIMD while we can */ + while( istop >= 16 ) + { + for(i=0;i < bpp ; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*(rp-bpp))) & 0xff); + rp++; + } + rp -= bpp; + + rp_vec = vec_ld(0,rp); + part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED1_4); + rp_vec = vec_add(rp_vec,part_vec); + + part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED2_4); + rp_vec = vec_add(rp_vec,part_vec); + + part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED3_4); + rp_vec = vec_add(rp_vec,part_vec); + + vec_st(rp_vec,0,rp); + + rp += 16; + istop -= 16; + } + + if(istop > 0) + for (i = 0; i < istop % 16; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*(rp - bpp))) & 0xff); + rp++; + } + +} + +void png_read_filter_row_sub3_vsx(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_byte bpp = 3; + + vector unsigned char rp_vec; + vector unsigned char part_vec; + + vsx_declare_common_vars(row_info,row,prev_row,bpp) + + PNG_UNUSED(pp) + + /* Altivec operations require 16-byte aligned data + * but input can be unaligned. So we calculate + * unaligned part as usual. + */ + for (i = 0; i < unaligned_top; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*(rp-bpp))) & 0xff); + rp++; + } + + /* Using SIMD while we can */ + while( istop >= 16 ) + { + for(i=0;i < bpp ; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*(rp-bpp))) & 0xff); + rp++; + } + rp -= bpp; + + rp_vec = vec_ld(0,rp); + part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED1_3); + rp_vec = vec_add(rp_vec,part_vec); + + part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED2_3); + rp_vec = vec_add(rp_vec,part_vec); + + part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED3_3); + rp_vec = vec_add(rp_vec,part_vec); + + part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED4_3); + rp_vec = vec_add(rp_vec,part_vec); + + vec_st(rp_vec,0,rp); + rp += 15; + istop -= 16; + + /* Since 16 % bpp = 16 % 3 = 1, last element of array must + * be proceeded manually + */ + *rp = (png_byte)(((int)(*rp) + (int)(*(rp-bpp))) & 0xff); + rp++; + } + + if(istop > 0) + for (i = 0; i < istop % 16; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*(rp-bpp))) & 0xff); + rp++; + } +} + +void png_read_filter_row_avg4_vsx(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_byte bpp = 4; + + vector unsigned char rp_vec; + vector unsigned char pp_vec; + vector unsigned char pp_part_vec; + vector unsigned char rp_part_vec; + vector unsigned char avg_vec; + + vsx_declare_common_vars(row_info,row,prev_row,bpp) + rp -= bpp; + if(istop >= bpp) + istop -= bpp; + + for (i = 0; i < bpp; i++) + { + *rp = (png_byte)(((int)(*rp) + + ((int)(*pp++) / 2 )) & 0xff); + + rp++; + } + + /* Altivec operations require 16-byte aligned data + * but input can be unaligned. So we calculate + * unaligned part as usual. + */ + for (i = 0; i < unaligned_top; i++) + { + *rp = (png_byte)(((int)(*rp) + + (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff); + + rp++; + } + + /* Using SIMD while we can */ + while( istop >= 16 ) + { + for(i=0;i < bpp ; i++) + { + *rp = (png_byte)(((int)(*rp) + + (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff); + + rp++; + } + rp -= bpp; + pp -= bpp; + + vec_ld_unaligned(pp_vec,pp); + rp_vec = vec_ld(0,rp); + + rp_part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED1_4); + pp_part_vec = vec_perm(pp_vec,VSX_CHAR_ZERO,VSX_NOT_SHIFTED1_4); + avg_vec = vec_avg(rp_part_vec,pp_part_vec); + avg_vec = vec_sub(avg_vec, vec_and(vec_xor(rp_part_vec,pp_part_vec),vec_splat_u8(1))); + rp_vec = vec_add(rp_vec,avg_vec); + + rp_part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED2_4); + pp_part_vec = vec_perm(pp_vec,VSX_CHAR_ZERO,VSX_NOT_SHIFTED2_4); + avg_vec = vec_avg(rp_part_vec,pp_part_vec); + avg_vec = vec_sub(avg_vec, vec_and(vec_xor(rp_part_vec,pp_part_vec),vec_splat_u8(1))); + rp_vec = vec_add(rp_vec,avg_vec); + + rp_part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED3_4); + pp_part_vec = vec_perm(pp_vec,VSX_CHAR_ZERO,VSX_NOT_SHIFTED3_4); + avg_vec = vec_avg(rp_part_vec,pp_part_vec); + avg_vec = vec_sub(avg_vec, vec_and(vec_xor(rp_part_vec,pp_part_vec),vec_splat_u8(1))); + rp_vec = vec_add(rp_vec,avg_vec); + + vec_st(rp_vec,0,rp); + + rp += 16; + pp += 16; + istop -= 16; + } + + if(istop > 0) + for (i = 0; i < istop % 16; i++) + { + *rp = (png_byte)(((int)(*rp) + + (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff); + + rp++; + } +} + +void png_read_filter_row_avg3_vsx(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_byte bpp = 3; + + vector unsigned char rp_vec; + vector unsigned char pp_vec; + vector unsigned char pp_part_vec; + vector unsigned char rp_part_vec; + vector unsigned char avg_vec; + + vsx_declare_common_vars(row_info,row,prev_row,bpp) + rp -= bpp; + if(istop >= bpp) + istop -= bpp; + + for (i = 0; i < bpp; i++) + { + *rp = (png_byte)(((int)(*rp) + + ((int)(*pp++) / 2 )) & 0xff); + + rp++; + } + + /* Altivec operations require 16-byte aligned data + * but input can be unaligned. So we calculate + * unaligned part as usual. + */ + for (i = 0; i < unaligned_top; i++) + { + *rp = (png_byte)(((int)(*rp) + + (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff); + + rp++; + } + + /* Using SIMD while we can */ + while( istop >= 16 ) + { + for(i=0;i < bpp ; i++) + { + *rp = (png_byte)(((int)(*rp) + + (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff); + + rp++; + } + rp -= bpp; + pp -= bpp; + + vec_ld_unaligned(pp_vec,pp); + rp_vec = vec_ld(0,rp); + + rp_part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED1_3); + pp_part_vec = vec_perm(pp_vec,VSX_CHAR_ZERO,VSX_NOT_SHIFTED1_3); + avg_vec = vec_avg(rp_part_vec,pp_part_vec); + avg_vec = vec_sub(avg_vec, vec_and(vec_xor(rp_part_vec,pp_part_vec),vec_splat_u8(1))); + rp_vec = vec_add(rp_vec,avg_vec); + + rp_part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED2_3); + pp_part_vec = vec_perm(pp_vec,VSX_CHAR_ZERO,VSX_NOT_SHIFTED2_3); + avg_vec = vec_avg(rp_part_vec,pp_part_vec); + avg_vec = vec_sub(avg_vec, vec_and(vec_xor(rp_part_vec,pp_part_vec),vec_splat_u8(1))); + rp_vec = vec_add(rp_vec,avg_vec); + + rp_part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED3_3); + pp_part_vec = vec_perm(pp_vec,VSX_CHAR_ZERO,VSX_NOT_SHIFTED3_3); + avg_vec = vec_avg(rp_part_vec,pp_part_vec); + avg_vec = vec_sub(avg_vec, vec_and(vec_xor(rp_part_vec,pp_part_vec),vec_splat_u8(1))); + rp_vec = vec_add(rp_vec,avg_vec); + + rp_part_vec = vec_perm(rp_vec,VSX_CHAR_ZERO,VSX_LEFTSHIFTED4_3); + pp_part_vec = vec_perm(pp_vec,VSX_CHAR_ZERO,VSX_NOT_SHIFTED4_3); + avg_vec = vec_avg(rp_part_vec,pp_part_vec); + avg_vec = vec_sub(avg_vec, vec_and(vec_xor(rp_part_vec,pp_part_vec),vec_splat_u8(1))); + rp_vec = vec_add(rp_vec,avg_vec); + + vec_st(rp_vec,0,rp); + + rp += 15; + pp += 15; + istop -= 16; + + /* Since 16 % bpp = 16 % 3 = 1, last element of array must + * be proceeded manually + */ + *rp = (png_byte)(((int)(*rp) + + (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff); + rp++; + } + + if(istop > 0) + for (i = 0; i < istop % 16; i++) + { + *rp = (png_byte)(((int)(*rp) + + (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff); + + rp++; + } +} + +/* Bytewise c ? t : e. */ +#define if_then_else(c,t,e) vec_sel(e,t,c) + +#define vsx_paeth_process(rp,pp,a,b,c,pa,pb,pc,bpp) {\ + c = *(pp - bpp);\ + a = *(rp - bpp);\ + b = *pp++;\ + p = b - c;\ + pc = a - c;\ + pa = vsx_abs(p);\ + pb = vsx_abs(pc);\ + pc = vsx_abs(p + pc);\ + if (pb < pa) pa = pb, a = b;\ + if (pc < pa) a = c;\ + a += *rp;\ + *rp++ = (png_byte)a;\ + } + +void png_read_filter_row_paeth4_vsx(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_byte bpp = 4; + + int a, b, c, pa, pb, pc, p; + vector unsigned char rp_vec; + vector unsigned char pp_vec; + vector unsigned short a_vec,b_vec,c_vec,nearest_vec; + vector signed short pa_vec,pb_vec,pc_vec,smallest_vec; + + vsx_declare_common_vars(row_info,row,prev_row,bpp) + rp -= bpp; + if(istop >= bpp) + istop -= bpp; + + /* Process the first pixel in the row completely (this is the same as 'up' + * because there is only one candidate predictor for the first row). + */ + for(i = 0; i < bpp ; i++) + { + *rp = (png_byte)( *rp + *pp); + rp++; + pp++; + } + + for(i = 0; i < unaligned_top ; i++) + { + vsx_paeth_process(rp,pp,a,b,c,pa,pb,pc,bpp) + } + + while( istop >= 16) + { + for(i = 0; i < bpp ; i++) + { + vsx_paeth_process(rp,pp,a,b,c,pa,pb,pc,bpp) + } + + rp -= bpp; + pp -= bpp; + rp_vec = vec_ld(0,rp); + vec_ld_unaligned(pp_vec,pp); + + a_vec = vsx_char_to_short(vec_perm(rp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED1_4),1,4); + b_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_NOT_SHIFTED1_4),1,4); + c_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED1_4),1,4); + pa_vec = (vector signed short) vec_sub(b_vec,c_vec); + pb_vec = (vector signed short) vec_sub(a_vec , c_vec); + pc_vec = vec_add(pa_vec,pb_vec); + pa_vec = vec_abs(pa_vec); + pb_vec = vec_abs(pb_vec); + pc_vec = vec_abs(pc_vec); + smallest_vec = vec_min(pc_vec, vec_min(pa_vec,pb_vec)); + nearest_vec = if_then_else( + vec_cmpeq(pa_vec,smallest_vec), + a_vec, + if_then_else( + vec_cmpeq(pb_vec,smallest_vec), + b_vec, + c_vec + ) + ); + rp_vec = vec_add(rp_vec,(vsx_short_to_char(nearest_vec,1,4))); + + a_vec = vsx_char_to_short(vec_perm(rp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED2_4),2,4); + b_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_NOT_SHIFTED2_4),2,4); + c_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED2_4),2,4); + pa_vec = (vector signed short) vec_sub(b_vec,c_vec); + pb_vec = (vector signed short) vec_sub(a_vec , c_vec); + pc_vec = vec_add(pa_vec,pb_vec); + pa_vec = vec_abs(pa_vec); + pb_vec = vec_abs(pb_vec); + pc_vec = vec_abs(pc_vec); + smallest_vec = vec_min(pc_vec, vec_min(pa_vec,pb_vec)); + nearest_vec = if_then_else( + vec_cmpeq(pa_vec,smallest_vec), + a_vec, + if_then_else( + vec_cmpeq(pb_vec,smallest_vec), + b_vec, + c_vec + ) + ); + rp_vec = vec_add(rp_vec,(vsx_short_to_char(nearest_vec,2,4))); + + a_vec = vsx_char_to_short(vec_perm(rp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED3_4),3,4); + b_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_NOT_SHIFTED3_4),3,4); + c_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED3_4),3,4); + pa_vec = (vector signed short) vec_sub(b_vec,c_vec); + pb_vec = (vector signed short) vec_sub(a_vec , c_vec); + pc_vec = vec_add(pa_vec,pb_vec); + pa_vec = vec_abs(pa_vec); + pb_vec = vec_abs(pb_vec); + pc_vec = vec_abs(pc_vec); + smallest_vec = vec_min(pc_vec, vec_min(pa_vec,pb_vec)); + nearest_vec = if_then_else( + vec_cmpeq(pa_vec,smallest_vec), + a_vec, + if_then_else( + vec_cmpeq(pb_vec,smallest_vec), + b_vec, + c_vec + ) + ); + rp_vec = vec_add(rp_vec,(vsx_short_to_char(nearest_vec,3,4))); + + vec_st(rp_vec,0,rp); + + rp += 16; + pp += 16; + istop -= 16; + } + + if(istop > 0) + for (i = 0; i < istop % 16; i++) + { + vsx_paeth_process(rp,pp,a,b,c,pa,pb,pc,bpp) + } +} + +void png_read_filter_row_paeth3_vsx(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_byte bpp = 3; + + int a, b, c, pa, pb, pc, p; + vector unsigned char rp_vec; + vector unsigned char pp_vec; + vector unsigned short a_vec,b_vec,c_vec,nearest_vec; + vector signed short pa_vec,pb_vec,pc_vec,smallest_vec; + + vsx_declare_common_vars(row_info,row,prev_row,bpp) + rp -= bpp; + if(istop >= bpp) + istop -= bpp; + + /* Process the first pixel in the row completely (this is the same as 'up' + * because there is only one candidate predictor for the first row). + */ + for(i = 0; i < bpp ; i++) + { + *rp = (png_byte)( *rp + *pp); + rp++; + pp++; + } + + for(i = 0; i < unaligned_top ; i++) + { + vsx_paeth_process(rp,pp,a,b,c,pa,pb,pc,bpp) + } + + while( istop >= 16) + { + for(i = 0; i < bpp ; i++) + { + vsx_paeth_process(rp,pp,a,b,c,pa,pb,pc,bpp) + } + + rp -= bpp; + pp -= bpp; + rp_vec = vec_ld(0,rp); + vec_ld_unaligned(pp_vec,pp); + + a_vec = vsx_char_to_short(vec_perm(rp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED1_3),1,3); + b_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_NOT_SHIFTED1_3),1,3); + c_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED1_3),1,3); + pa_vec = (vector signed short) vec_sub(b_vec,c_vec); + pb_vec = (vector signed short) vec_sub(a_vec , c_vec); + pc_vec = vec_add(pa_vec,pb_vec); + pa_vec = vec_abs(pa_vec); + pb_vec = vec_abs(pb_vec); + pc_vec = vec_abs(pc_vec); + smallest_vec = vec_min(pc_vec, vec_min(pa_vec,pb_vec)); + nearest_vec = if_then_else( + vec_cmpeq(pa_vec,smallest_vec), + a_vec, + if_then_else( + vec_cmpeq(pb_vec,smallest_vec), + b_vec, + c_vec + ) + ); + rp_vec = vec_add(rp_vec,(vsx_short_to_char(nearest_vec,1,3))); + + a_vec = vsx_char_to_short(vec_perm(rp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED2_3),2,3); + b_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_NOT_SHIFTED2_3),2,3); + c_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED2_3),2,3); + pa_vec = (vector signed short) vec_sub(b_vec,c_vec); + pb_vec = (vector signed short) vec_sub(a_vec , c_vec); + pc_vec = vec_add(pa_vec,pb_vec); + pa_vec = vec_abs(pa_vec); + pb_vec = vec_abs(pb_vec); + pc_vec = vec_abs(pc_vec); + smallest_vec = vec_min(pc_vec, vec_min(pa_vec,pb_vec)); + nearest_vec = if_then_else( + vec_cmpeq(pa_vec,smallest_vec), + a_vec, + if_then_else( + vec_cmpeq(pb_vec,smallest_vec), + b_vec, + c_vec + ) + ); + rp_vec = vec_add(rp_vec,(vsx_short_to_char(nearest_vec,2,3))); + + a_vec = vsx_char_to_short(vec_perm(rp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED3_3),3,3); + b_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_NOT_SHIFTED3_3),3,3); + c_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED3_3),3,3); + pa_vec = (vector signed short) vec_sub(b_vec,c_vec); + pb_vec = (vector signed short) vec_sub(a_vec , c_vec); + pc_vec = vec_add(pa_vec,pb_vec); + pa_vec = vec_abs(pa_vec); + pb_vec = vec_abs(pb_vec); + pc_vec = vec_abs(pc_vec); + smallest_vec = vec_min(pc_vec, vec_min(pa_vec,pb_vec)); + nearest_vec = if_then_else( + vec_cmpeq(pa_vec,smallest_vec), + a_vec, + if_then_else( + vec_cmpeq(pb_vec,smallest_vec), + b_vec, + c_vec + ) + ); + rp_vec = vec_add(rp_vec,(vsx_short_to_char(nearest_vec,3,3))); + + a_vec = vsx_char_to_short(vec_perm(rp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED4_3),4,3); + b_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_NOT_SHIFTED4_3),4,3); + c_vec = vsx_char_to_short(vec_perm(pp_vec , VSX_CHAR_ZERO , VSX_LEFTSHIFTED4_3),4,3); + pa_vec = (vector signed short) vec_sub(b_vec,c_vec); + pb_vec = (vector signed short) vec_sub(a_vec , c_vec); + pc_vec = vec_add(pa_vec,pb_vec); + pa_vec = vec_abs(pa_vec); + pb_vec = vec_abs(pb_vec); + pc_vec = vec_abs(pc_vec); + smallest_vec = vec_min(pc_vec, vec_min(pa_vec,pb_vec)); + nearest_vec = if_then_else( + vec_cmpeq(pa_vec,smallest_vec), + a_vec, + if_then_else( + vec_cmpeq(pb_vec,smallest_vec), + b_vec, + c_vec + ) + ); + rp_vec = vec_add(rp_vec,(vsx_short_to_char(nearest_vec,4,3))); + + vec_st(rp_vec,0,rp); + + rp += 15; + pp += 15; + istop -= 16; + + /* Since 16 % bpp = 16 % 3 = 1, last element of array must + * be proceeded manually + */ + vsx_paeth_process(rp,pp,a,b,c,pa,pb,pc,bpp) + } + + if(istop > 0) + for (i = 0; i < istop % 16; i++) + { + vsx_paeth_process(rp,pp,a,b,c,pa,pb,pc,bpp) + } +} + +#endif /* PNG_POWERPC_VSX_OPT > 0 */ +#endif /* PNG_POWERPC_VSX_IMPLEMENTATION == 1 (intrinsics) */ +#endif /* READ */ diff --git a/thirdparty/libpng/powerpc/powerpc_init.c b/thirdparty/libpng/powerpc/powerpc_init.c new file mode 100644 index 0000000000..54426c558e --- /dev/null +++ b/thirdparty/libpng/powerpc/powerpc_init.c @@ -0,0 +1,126 @@ + +/* powerpc_init.c - POWERPC optimised filter functions + * + * Copyright (c) 2018 Cosmin Truta + * Copyright (c) 2017 Glenn Randers-Pehrson + * Written by Vadim Barkov, 2017. + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +/* Below, after checking __linux__, various non-C90 POSIX 1003.1 functions are + * called. + */ +#define _POSIX_SOURCE 1 + +#include <stdio.h> +#include "../pngpriv.h" + +#ifdef PNG_READ_SUPPORTED + +#if PNG_POWERPC_VSX_OPT > 0 +#ifdef PNG_POWERPC_VSX_CHECK_SUPPORTED /* Do run-time checks */ +/* WARNING: it is strongly recommended that you do not build libpng with + * run-time checks for CPU features if at all possible. In the case of the PowerPC + * VSX instructions there is no processor-specific way of detecting the + * presence of the required support, therefore run-time detection is extremely + * OS specific. + * + * You may set the macro PNG_POWERPC_VSX_FILE to the file name of file containing + * a fragment of C source code which defines the png_have_vsx function. There + * are a number of implementations in contrib/powerpc-vsx, but the only one that + * has partial support is contrib/powerpc-vsx/linux.c - a generic Linux + * implementation which reads /proc/cpufino. + */ +#ifndef PNG_POWERPC_VSX_FILE +# ifdef __linux__ +# define PNG_POWERPC_VSX_FILE "contrib/powerpc-vsx/linux_aux.c" +# endif +#endif + +#ifdef PNG_POWERPC_VSX_FILE + +#include <signal.h> /* for sig_atomic_t */ +static int png_have_vsx(png_structp png_ptr); +#include PNG_POWERPC_VSX_FILE + +#else /* PNG_POWERPC_VSX_FILE */ +# error "PNG_POWERPC_VSX_FILE undefined: no support for run-time POWERPC VSX checks" +#endif /* PNG_POWERPC_VSX_FILE */ +#endif /* PNG_POWERPC_VSX_CHECK_SUPPORTED */ + +void +png_init_filter_functions_vsx(png_structp pp, unsigned int bpp) +{ + /* The switch statement is compiled in for POWERPC_VSX_API, the call to + * png_have_vsx is compiled in for POWERPC_VSX_CHECK. If both are defined + * the check is only performed if the API has not set the PowerPC option on + * or off explicitly. In this case the check controls what happens. + */ + +#ifdef PNG_POWERPC_VSX_API_SUPPORTED + switch ((pp->options >> PNG_POWERPC_VSX) & 3) + { + case PNG_OPTION_UNSET: + /* Allow the run-time check to execute if it has been enabled - + * thus both API and CHECK can be turned on. If it isn't supported + * this case will fall through to the 'default' below, which just + * returns. + */ +#endif /* PNG_POWERPC_VSX_API_SUPPORTED */ +#ifdef PNG_POWERPC_VSX_CHECK_SUPPORTED + { + static volatile sig_atomic_t no_vsx = -1; /* not checked */ + + if (no_vsx < 0) + no_vsx = !png_have_vsx(pp); + + if (no_vsx) + return; + } +#ifdef PNG_POWERPC_VSX_API_SUPPORTED + break; +#endif +#endif /* PNG_POWERPC_VSX_CHECK_SUPPORTED */ + +#ifdef PNG_POWERPC_VSX_API_SUPPORTED + default: /* OFF or INVALID */ + return; + + case PNG_OPTION_ON: + /* Option turned on */ + break; + } +#endif + + /* IMPORTANT: any new internal functions used here must be declared using + * PNG_INTERNAL_FUNCTION in ../pngpriv.h. This is required so that the + * 'prefix' option to configure works: + * + * ./configure --with-libpng-prefix=foobar_ + * + * Verify you have got this right by running the above command, doing a build + * and examining pngprefix.h; it must contain a #define for every external + * function you add. (Notice that this happens automatically for the + * initialization function.) + */ + pp->read_filter[PNG_FILTER_VALUE_UP-1] = png_read_filter_row_up_vsx; + + if (bpp == 3) + { + pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub3_vsx; + pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg3_vsx; + pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = png_read_filter_row_paeth3_vsx; + } + + else if (bpp == 4) + { + pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub4_vsx; + pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg4_vsx; + pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = png_read_filter_row_paeth4_vsx; + } +} +#endif /* PNG_POWERPC_VSX_OPT > 0 */ +#endif /* READ */ diff --git a/thirdparty/libwebp/AUTHORS b/thirdparty/libwebp/AUTHORS index 2f0c537d1c..8359b20da9 100644 --- a/thirdparty/libwebp/AUTHORS +++ b/thirdparty/libwebp/AUTHORS @@ -32,6 +32,7 @@ Contributors: - Mislav Bradac (mislavm at google dot com) - Nico Weber (thakis at chromium dot org) - Noel Chromium (noel at chromium dot org) +- Nozomi Isozaki (nontan at pixiv dot co dot jp) - Oliver Wolff (oliver dot wolff at qt dot io) - Owen Rodley (orodley at google dot com) - Parag Salasakar (img dot mips1 at gmail dot com) @@ -47,6 +48,7 @@ Contributors: - Somnath Banerjee (somnath dot banerjee at gmail dot com) - Sriraman Tallam (tmsriram at google dot com) - Tamar Levy (tamar dot levy at intel dot com) +- Thiago Perrotta (tperrotta at google dot com) - Timothy Gu (timothygu99 at gmail dot com) - Urvang Joshi (urvang at google dot com) - Vikas Arora (vikasa at google dot com) diff --git a/thirdparty/libwebp/sharpyuv/sharpyuv.c b/thirdparty/libwebp/sharpyuv/sharpyuv.c index 7de34fb0b2..a074564888 100644 --- a/thirdparty/libwebp/sharpyuv/sharpyuv.c +++ b/thirdparty/libwebp/sharpyuv/sharpyuv.c @@ -440,6 +440,7 @@ static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr, // By default SharpYuvConvert calls it with SharpYuvGetCPUInfo. If needed, // users can declare it as extern and call it with an alternate VP8CPUInfo // function. +extern VP8CPUInfo SharpYuvGetCPUInfo; SHARPYUV_EXTERN void SharpYuvInit(VP8CPUInfo cpu_info_func); void SharpYuvInit(VP8CPUInfo cpu_info_func) { static volatile VP8CPUInfo sharpyuv_last_cpuinfo_used = diff --git a/thirdparty/libwebp/sharpyuv/sharpyuv.h b/thirdparty/libwebp/sharpyuv/sharpyuv.h index 181b20a0bc..7b9904d6f9 100644 --- a/thirdparty/libwebp/sharpyuv/sharpyuv.h +++ b/thirdparty/libwebp/sharpyuv/sharpyuv.h @@ -37,7 +37,7 @@ extern "C" { // SharpYUV API version following the convention from semver.org #define SHARPYUV_VERSION_MAJOR 0 #define SHARPYUV_VERSION_MINOR 2 -#define SHARPYUV_VERSION_PATCH 0 +#define SHARPYUV_VERSION_PATCH 1 // Version as a uint32_t. The major number is the high 8 bits. // The minor number is the middle 8 bits. The patch number is the low 16 bits. #define SHARPYUV_MAKE_VERSION(MAJOR, MINOR, PATCH) \ diff --git a/thirdparty/libwebp/sharpyuv/sharpyuv_dsp.c b/thirdparty/libwebp/sharpyuv/sharpyuv_dsp.c index 31c272c408..0da3efc0b8 100644 --- a/thirdparty/libwebp/sharpyuv/sharpyuv_dsp.c +++ b/thirdparty/libwebp/sharpyuv/sharpyuv_dsp.c @@ -72,6 +72,7 @@ void (*SharpYuvFilterRow)(const int16_t* A, const int16_t* B, int len, const uint16_t* best_y, uint16_t* out, int bit_depth); +extern VP8CPUInfo SharpYuvGetCPUInfo; extern void InitSharpYuvSSE2(void); extern void InitSharpYuvNEON(void); diff --git a/thirdparty/libwebp/src/dec/tree_dec.c b/thirdparty/libwebp/src/dec/tree_dec.c index 1c6fdea27c..2434605953 100644 --- a/thirdparty/libwebp/src/dec/tree_dec.c +++ b/thirdparty/libwebp/src/dec/tree_dec.c @@ -12,10 +12,11 @@ // Author: Skal (pascal.massimino@gmail.com) #include "src/dec/vp8i_dec.h" +#include "src/dsp/cpu.h" #include "src/utils/bit_reader_inl_utils.h" #if !defined(USE_GENERIC_TREE) -#if !defined(__arm__) && !defined(_M_ARM) && !defined(__aarch64__) +#if !defined(__arm__) && !defined(_M_ARM) && !WEBP_AARCH64 // using a table is ~1-2% slower on ARM. Prefer the coded-tree approach then. #define USE_GENERIC_TREE 1 // ALTERNATE_CODE #else diff --git a/thirdparty/libwebp/src/dec/vp8_dec.c b/thirdparty/libwebp/src/dec/vp8_dec.c index 2003935ec4..20b92e84c4 100644 --- a/thirdparty/libwebp/src/dec/vp8_dec.c +++ b/thirdparty/libwebp/src/dec/vp8_dec.c @@ -494,6 +494,8 @@ static int GetCoeffsAlt(VP8BitReader* const br, return 16; } +extern VP8CPUInfo VP8GetCPUInfo; + WEBP_DSP_INIT_FUNC(InitGetCoeffs) { if (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kSlowSSSE3)) { GetCoeffs = GetCoeffsAlt; diff --git a/thirdparty/libwebp/src/dec/vp8i_dec.h b/thirdparty/libwebp/src/dec/vp8i_dec.h index 83791ecd25..1ae4ff62f2 100644 --- a/thirdparty/libwebp/src/dec/vp8i_dec.h +++ b/thirdparty/libwebp/src/dec/vp8i_dec.h @@ -32,7 +32,7 @@ extern "C" { // version numbers #define DEC_MAJ_VERSION 1 #define DEC_MIN_VERSION 3 -#define DEC_REV_VERSION 0 +#define DEC_REV_VERSION 1 // YUV-cache parameters. Cache is 32-bytes wide (= one cacheline). // Constraints are: We need to store one 16x16 block of luma samples (y), diff --git a/thirdparty/libwebp/src/dec/webp_dec.c b/thirdparty/libwebp/src/dec/webp_dec.c index 3f4f7bb659..f557868b99 100644 --- a/thirdparty/libwebp/src/dec/webp_dec.c +++ b/thirdparty/libwebp/src/dec/webp_dec.c @@ -658,19 +658,26 @@ uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size, uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size, int* width, int* height, uint8_t** u, uint8_t** v, int* stride, int* uv_stride) { - WebPDecBuffer output; // only to preserve the side-infos - uint8_t* const out = Decode(MODE_YUV, data, data_size, - width, height, &output); - - if (out != NULL) { - const WebPYUVABuffer* const buf = &output.u.YUVA; - *u = buf->u; - *v = buf->v; - *stride = buf->y_stride; - *uv_stride = buf->u_stride; - assert(buf->u_stride == buf->v_stride); - } - return out; + // data, width and height are checked by Decode(). + if (u == NULL || v == NULL || stride == NULL || uv_stride == NULL) { + return NULL; + } + + { + WebPDecBuffer output; // only to preserve the side-infos + uint8_t* const out = Decode(MODE_YUV, data, data_size, + width, height, &output); + + if (out != NULL) { + const WebPYUVABuffer* const buf = &output.u.YUVA; + *u = buf->u; + *v = buf->v; + *stride = buf->y_stride; + *uv_stride = buf->u_stride; + assert(buf->u_stride == buf->v_stride); + } + return out; + } } static void DefaultFeatures(WebPBitstreamFeatures* const features) { diff --git a/thirdparty/libwebp/src/demux/demux.c b/thirdparty/libwebp/src/demux/demux.c index 324e5eb993..fd45a2500e 100644 --- a/thirdparty/libwebp/src/demux/demux.c +++ b/thirdparty/libwebp/src/demux/demux.c @@ -25,7 +25,7 @@ #define DMUX_MAJ_VERSION 1 #define DMUX_MIN_VERSION 3 -#define DMUX_REV_VERSION 0 +#define DMUX_REV_VERSION 1 typedef struct { size_t start_; // start location of the data diff --git a/thirdparty/libwebp/src/dsp/alpha_processing.c b/thirdparty/libwebp/src/dsp/alpha_processing.c index 1892929a43..1d152f24da 100644 --- a/thirdparty/libwebp/src/dsp/alpha_processing.c +++ b/thirdparty/libwebp/src/dsp/alpha_processing.c @@ -425,6 +425,7 @@ void (*WebPAlphaReplace)(uint32_t* src, int length, uint32_t color); //------------------------------------------------------------------------------ // Init function +extern VP8CPUInfo VP8GetCPUInfo; extern void WebPInitAlphaProcessingMIPSdspR2(void); extern void WebPInitAlphaProcessingSSE2(void); extern void WebPInitAlphaProcessingSSE41(void); diff --git a/thirdparty/libwebp/src/dsp/cost.c b/thirdparty/libwebp/src/dsp/cost.c index 460ec4f2a7..73d2140177 100644 --- a/thirdparty/libwebp/src/dsp/cost.c +++ b/thirdparty/libwebp/src/dsp/cost.c @@ -374,6 +374,7 @@ static void SetResidualCoeffs_C(const int16_t* const coeffs, VP8GetResidualCostFunc VP8GetResidualCost; VP8SetResidualCoeffsFunc VP8SetResidualCoeffs; +extern VP8CPUInfo VP8GetCPUInfo; extern void VP8EncDspCostInitMIPS32(void); extern void VP8EncDspCostInitMIPSdspR2(void); extern void VP8EncDspCostInitSSE2(void); diff --git a/thirdparty/libwebp/src/dsp/cost_neon.c b/thirdparty/libwebp/src/dsp/cost_neon.c index 8cc8ce58aa..6582669cb3 100644 --- a/thirdparty/libwebp/src/dsp/cost_neon.c +++ b/thirdparty/libwebp/src/dsp/cost_neon.c @@ -29,7 +29,7 @@ static void SetResidualCoeffs_NEON(const int16_t* const coeffs, const uint8x16_t eob = vcombine_u8(vqmovn_u16(eob_0), vqmovn_u16(eob_1)); const uint8x16_t masked = vandq_u8(eob, vld1q_u8(position)); -#ifdef __aarch64__ +#if WEBP_AARCH64 res->last = vmaxvq_u8(masked) - 1; #else const uint8x8_t eob_8x8 = vmax_u8(vget_low_u8(masked), vget_high_u8(masked)); @@ -43,7 +43,7 @@ static void SetResidualCoeffs_NEON(const int16_t* const coeffs, vst1_lane_s32(&res->last, vreinterpret_s32_u32(eob_32x2), 0); --res->last; -#endif // __aarch64__ +#endif // WEBP_AARCH64 res->coeffs = coeffs; } diff --git a/thirdparty/libwebp/src/dsp/cpu.c b/thirdparty/libwebp/src/dsp/cpu.c index 62de73f750..2234c77b35 100644 --- a/thirdparty/libwebp/src/dsp/cpu.c +++ b/thirdparty/libwebp/src/dsp/cpu.c @@ -173,6 +173,7 @@ static int x86CPUInfo(CPUFeature feature) { } return 0; } +WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo; VP8CPUInfo VP8GetCPUInfo = x86CPUInfo; #elif defined(WEBP_ANDROID_NEON) // NB: needs to be before generic NEON test. static int AndroidCPUInfo(CPUFeature feature) { @@ -184,6 +185,7 @@ static int AndroidCPUInfo(CPUFeature feature) { } return 0; } +WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo; VP8CPUInfo VP8GetCPUInfo = AndroidCPUInfo; #elif defined(EMSCRIPTEN) // also needs to be before generic NEON test // Use compile flags as an indicator of SIMD support instead of a runtime check. @@ -208,6 +210,7 @@ static int wasmCPUInfo(CPUFeature feature) { } return 0; } +WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo; VP8CPUInfo VP8GetCPUInfo = wasmCPUInfo; #elif defined(WEBP_HAVE_NEON) // In most cases this function doesn't check for NEON support (it's assumed by @@ -236,6 +239,7 @@ static int armCPUInfo(CPUFeature feature) { return 1; #endif } +WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo; VP8CPUInfo VP8GetCPUInfo = armCPUInfo; #elif defined(WEBP_USE_MIPS32) || defined(WEBP_USE_MIPS_DSP_R2) || \ defined(WEBP_USE_MSA) @@ -247,7 +251,9 @@ static int mipsCPUInfo(CPUFeature feature) { } } +WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo; VP8CPUInfo VP8GetCPUInfo = mipsCPUInfo; #else +WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo; VP8CPUInfo VP8GetCPUInfo = NULL; #endif diff --git a/thirdparty/libwebp/src/dsp/cpu.h b/thirdparty/libwebp/src/dsp/cpu.h index be80727c0d..c86540f280 100644 --- a/thirdparty/libwebp/src/dsp/cpu.h +++ b/thirdparty/libwebp/src/dsp/cpu.h @@ -43,6 +43,9 @@ #define __has_builtin(x) 0 #endif +//------------------------------------------------------------------------------ +// x86 defines. + #if !defined(HAVE_CONFIG_H) #if defined(_MSC_VER) && _MSC_VER > 1310 && \ (defined(_M_X64) || defined(_M_IX86)) @@ -80,6 +83,9 @@ #undef WEBP_MSC_SSE41 #undef WEBP_MSC_SSE2 +//------------------------------------------------------------------------------ +// Arm defines. + // The intrinsics currently cause compiler errors with arm-nacl-gcc and the // inline assembly would need to be modified for use with Native Client. #if ((defined(__ARM_NEON__) || defined(__aarch64__)) && \ @@ -98,16 +104,26 @@ // inclusion of arm64_neon.h; Visual Studio 2019 includes this file in // arm_neon.h. Compile errors were seen with Visual Studio 2019 16.4 with // vtbl4_u8(); a fix was made in 16.6. -#if defined(_MSC_VER) && ((_MSC_VER >= 1700 && defined(_M_ARM)) || \ - (_MSC_VER >= 1926 && defined(_M_ARM64))) +#if defined(_MSC_VER) && \ + ((_MSC_VER >= 1700 && defined(_M_ARM)) || \ + (_MSC_VER >= 1926 && (defined(_M_ARM64) || defined(_M_ARM64EC)))) #define WEBP_USE_NEON #define WEBP_USE_INTRINSICS #endif +#if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) +#define WEBP_AARCH64 1 +#else +#define WEBP_AARCH64 0 +#endif + #if defined(WEBP_USE_NEON) && !defined(WEBP_HAVE_NEON) #define WEBP_HAVE_NEON #endif +//------------------------------------------------------------------------------ +// MIPS defines. + #if defined(__mips__) && !defined(__mips64) && defined(__mips_isa_rev) && \ (__mips_isa_rev >= 1) && (__mips_isa_rev < 6) #define WEBP_USE_MIPS32 @@ -123,6 +139,8 @@ #define WEBP_USE_MSA #endif +//------------------------------------------------------------------------------ + #ifndef WEBP_DSP_OMIT_C_CODE #define WEBP_DSP_OMIT_C_CODE 1 #endif @@ -133,13 +151,14 @@ #define WEBP_NEON_OMIT_C_CODE 0 #endif -#if !(LOCAL_CLANG_PREREQ(3, 8) || LOCAL_GCC_PREREQ(4, 8) || \ - defined(__aarch64__)) +#if !(LOCAL_CLANG_PREREQ(3, 8) || LOCAL_GCC_PREREQ(4, 8) || WEBP_AARCH64) #define WEBP_NEON_WORK_AROUND_GCC 1 #else #define WEBP_NEON_WORK_AROUND_GCC 0 #endif +//------------------------------------------------------------------------------ + // This macro prevents thread_sanitizer from reporting known concurrent writes. #define WEBP_TSAN_IGNORE_FUNCTION #if defined(__has_feature) @@ -241,16 +260,7 @@ typedef enum { kMSA } CPUFeature; -#ifdef __cplusplus -extern "C" { -#endif - // returns true if the CPU supports the feature. typedef int (*VP8CPUInfo)(CPUFeature feature); -WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo; - -#ifdef __cplusplus -} // extern "C" -#endif #endif // WEBP_DSP_CPU_H_ diff --git a/thirdparty/libwebp/src/dsp/dec.c b/thirdparty/libwebp/src/dsp/dec.c index 537c701282..33d8df8a62 100644 --- a/thirdparty/libwebp/src/dsp/dec.c +++ b/thirdparty/libwebp/src/dsp/dec.c @@ -734,6 +734,7 @@ VP8SimpleFilterFunc VP8SimpleHFilter16i; void (*VP8DitherCombine8x8)(const uint8_t* dither, uint8_t* dst, int dst_stride); +extern VP8CPUInfo VP8GetCPUInfo; extern void VP8DspInitSSE2(void); extern void VP8DspInitSSE41(void); extern void VP8DspInitNEON(void); diff --git a/thirdparty/libwebp/src/dsp/dec_neon.c b/thirdparty/libwebp/src/dsp/dec_neon.c index fa851707e2..22784cf15a 100644 --- a/thirdparty/libwebp/src/dsp/dec_neon.c +++ b/thirdparty/libwebp/src/dsp/dec_neon.c @@ -1428,7 +1428,7 @@ static WEBP_INLINE void DC8_NEON(uint8_t* dst, int do_top, int do_left) { if (do_top) { const uint8x8_t A = vld1_u8(dst - BPS); // top row -#if defined(__aarch64__) +#if WEBP_AARCH64 const uint16_t p2 = vaddlv_u8(A); sum_top = vdupq_n_u16(p2); #else @@ -1511,7 +1511,7 @@ static WEBP_INLINE void DC16_NEON(uint8_t* dst, int do_top, int do_left) { if (do_top) { const uint8x16_t A = vld1q_u8(dst - BPS); // top row -#if defined(__aarch64__) +#if WEBP_AARCH64 const uint16_t p3 = vaddlvq_u8(A); sum_top = vdupq_n_u16(p3); #else diff --git a/thirdparty/libwebp/src/dsp/enc.c b/thirdparty/libwebp/src/dsp/enc.c index ea47a3fd95..2ba97ba8d6 100644 --- a/thirdparty/libwebp/src/dsp/enc.c +++ b/thirdparty/libwebp/src/dsp/enc.c @@ -732,6 +732,7 @@ VP8QuantizeBlockWHT VP8EncQuantizeBlockWHT; VP8BlockCopy VP8Copy4x4; VP8BlockCopy VP8Copy16x8; +extern VP8CPUInfo VP8GetCPUInfo; extern void VP8EncDspInitSSE2(void); extern void VP8EncDspInitSSE41(void); extern void VP8EncDspInitNEON(void); diff --git a/thirdparty/libwebp/src/dsp/enc_neon.c b/thirdparty/libwebp/src/dsp/enc_neon.c index 3a04111c55..714800367b 100644 --- a/thirdparty/libwebp/src/dsp/enc_neon.c +++ b/thirdparty/libwebp/src/dsp/enc_neon.c @@ -764,7 +764,7 @@ static WEBP_INLINE void AccumulateSSE16_NEON(const uint8_t* const a, // Horizontal sum of all four uint32_t values in 'sum'. static int SumToInt_NEON(uint32x4_t sum) { -#if defined(__aarch64__) +#if WEBP_AARCH64 return (int)vaddvq_u32(sum); #else const uint64x2_t sum2 = vpaddlq_u32(sum); @@ -865,7 +865,7 @@ static int QuantizeBlock_NEON(int16_t in[16], int16_t out[16], uint8x8x4_t shuffles; // vtbl?_u8 are marked unavailable for iOS arm64 with Xcode < 6.3, use // non-standard versions there. -#if defined(__APPLE__) && defined(__aarch64__) && \ +#if defined(__APPLE__) && WEBP_AARCH64 && \ defined(__apple_build_version__) && (__apple_build_version__< 6020037) uint8x16x2_t all_out; INIT_VECTOR2(all_out, vreinterpretq_u8_s16(out0), vreinterpretq_u8_s16(out1)); diff --git a/thirdparty/libwebp/src/dsp/enc_sse2.c b/thirdparty/libwebp/src/dsp/enc_sse2.c index 1d1055668f..010624a2f7 100644 --- a/thirdparty/libwebp/src/dsp/enc_sse2.c +++ b/thirdparty/libwebp/src/dsp/enc_sse2.c @@ -25,9 +25,160 @@ //------------------------------------------------------------------------------ // Transforms (Paragraph 14.4) -// Does one or two inverse transforms. -static void ITransform_SSE2(const uint8_t* ref, const int16_t* in, uint8_t* dst, - int do_two) { +// Does one inverse transform. +static void ITransform_One_SSE2(const uint8_t* ref, const int16_t* in, + uint8_t* dst) { + // This implementation makes use of 16-bit fixed point versions of two + // multiply constants: + // K1 = sqrt(2) * cos (pi/8) ~= 85627 / 2^16 + // K2 = sqrt(2) * sin (pi/8) ~= 35468 / 2^16 + // + // To be able to use signed 16-bit integers, we use the following trick to + // have constants within range: + // - Associated constants are obtained by subtracting the 16-bit fixed point + // version of one: + // k = K - (1 << 16) => K = k + (1 << 16) + // K1 = 85267 => k1 = 20091 + // K2 = 35468 => k2 = -30068 + // - The multiplication of a variable by a constant become the sum of the + // variable and the multiplication of that variable by the associated + // constant: + // (x * K) >> 16 = (x * (k + (1 << 16))) >> 16 = ((x * k ) >> 16) + x + const __m128i k1k2 = _mm_set_epi16(-30068, -30068, -30068, -30068, + 20091, 20091, 20091, 20091); + const __m128i k2k1 = _mm_set_epi16(20091, 20091, 20091, 20091, + -30068, -30068, -30068, -30068); + const __m128i zero = _mm_setzero_si128(); + const __m128i zero_four = _mm_set_epi16(0, 0, 0, 0, 4, 4, 4, 4); + __m128i T01, T23; + + // Load and concatenate the transform coefficients. + const __m128i in01 = _mm_loadu_si128((const __m128i*)&in[0]); + const __m128i in23 = _mm_loadu_si128((const __m128i*)&in[8]); + // a00 a10 a20 a30 a01 a11 a21 a31 + // a02 a12 a22 a32 a03 a13 a23 a33 + + // Vertical pass and subsequent transpose. + { + const __m128i in1 = _mm_unpackhi_epi64(in01, in01); + const __m128i in3 = _mm_unpackhi_epi64(in23, in23); + + // First pass, c and d calculations are longer because of the "trick" + // multiplications. + // c = MUL(in1, K2) - MUL(in3, K1) = MUL(in1, k2) - MUL(in3, k1) + in1 - in3 + // d = MUL(in1, K1) + MUL(in3, K2) = MUL(in1, k1) + MUL(in3, k2) + in1 + in3 + const __m128i a_d3 = _mm_add_epi16(in01, in23); + const __m128i b_c3 = _mm_sub_epi16(in01, in23); + const __m128i c1d1 = _mm_mulhi_epi16(in1, k2k1); + const __m128i c2d2 = _mm_mulhi_epi16(in3, k1k2); + const __m128i c3 = _mm_unpackhi_epi64(b_c3, b_c3); + const __m128i c4 = _mm_sub_epi16(c1d1, c2d2); + const __m128i c = _mm_add_epi16(c3, c4); + const __m128i d4u = _mm_add_epi16(c1d1, c2d2); + const __m128i du = _mm_add_epi16(a_d3, d4u); + const __m128i d = _mm_unpackhi_epi64(du, du); + + // Second pass. + const __m128i comb_ab = _mm_unpacklo_epi64(a_d3, b_c3); + const __m128i comb_dc = _mm_unpacklo_epi64(d, c); + + const __m128i tmp01 = _mm_add_epi16(comb_ab, comb_dc); + const __m128i tmp32 = _mm_sub_epi16(comb_ab, comb_dc); + const __m128i tmp23 = _mm_shuffle_epi32(tmp32, _MM_SHUFFLE(1, 0, 3, 2)); + + const __m128i transpose_0 = _mm_unpacklo_epi16(tmp01, tmp23); + const __m128i transpose_1 = _mm_unpackhi_epi16(tmp01, tmp23); + // a00 a20 a01 a21 a02 a22 a03 a23 + // a10 a30 a11 a31 a12 a32 a13 a33 + + T01 = _mm_unpacklo_epi16(transpose_0, transpose_1); + T23 = _mm_unpackhi_epi16(transpose_0, transpose_1); + // a00 a10 a20 a30 a01 a11 a21 a31 + // a02 a12 a22 a32 a03 a13 a23 a33 + } + + // Horizontal pass and subsequent transpose. + { + const __m128i T1 = _mm_unpackhi_epi64(T01, T01); + const __m128i T3 = _mm_unpackhi_epi64(T23, T23); + + // First pass, c and d calculations are longer because of the "trick" + // multiplications. + const __m128i dc = _mm_add_epi16(T01, zero_four); + + // c = MUL(T1, K2) - MUL(T3, K1) = MUL(T1, k2) - MUL(T3, k1) + T1 - T3 + // d = MUL(T1, K1) + MUL(T3, K2) = MUL(T1, k1) + MUL(T3, k2) + T1 + T3 + const __m128i a_d3 = _mm_add_epi16(dc, T23); + const __m128i b_c3 = _mm_sub_epi16(dc, T23); + const __m128i c1d1 = _mm_mulhi_epi16(T1, k2k1); + const __m128i c2d2 = _mm_mulhi_epi16(T3, k1k2); + const __m128i c3 = _mm_unpackhi_epi64(b_c3, b_c3); + const __m128i c4 = _mm_sub_epi16(c1d1, c2d2); + const __m128i c = _mm_add_epi16(c3, c4); + const __m128i d4u = _mm_add_epi16(c1d1, c2d2); + const __m128i du = _mm_add_epi16(a_d3, d4u); + const __m128i d = _mm_unpackhi_epi64(du, du); + + // Second pass. + const __m128i comb_ab = _mm_unpacklo_epi64(a_d3, b_c3); + const __m128i comb_dc = _mm_unpacklo_epi64(d, c); + + const __m128i tmp01 = _mm_add_epi16(comb_ab, comb_dc); + const __m128i tmp32 = _mm_sub_epi16(comb_ab, comb_dc); + const __m128i tmp23 = _mm_shuffle_epi32(tmp32, _MM_SHUFFLE(1, 0, 3, 2)); + + const __m128i shifted01 = _mm_srai_epi16(tmp01, 3); + const __m128i shifted23 = _mm_srai_epi16(tmp23, 3); + // a00 a01 a02 a03 a10 a11 a12 a13 + // a20 a21 a22 a23 a30 a31 a32 a33 + + const __m128i transpose_0 = _mm_unpacklo_epi16(shifted01, shifted23); + const __m128i transpose_1 = _mm_unpackhi_epi16(shifted01, shifted23); + // a00 a20 a01 a21 a02 a22 a03 a23 + // a10 a30 a11 a31 a12 a32 a13 a33 + + T01 = _mm_unpacklo_epi16(transpose_0, transpose_1); + T23 = _mm_unpackhi_epi16(transpose_0, transpose_1); + // a00 a10 a20 a30 a01 a11 a21 a31 + // a02 a12 a22 a32 a03 a13 a23 a33 + } + + // Add inverse transform to 'ref' and store. + { + // Load the reference(s). + __m128i ref01, ref23, ref0123; + int32_t buf[4]; + + // Load four bytes/pixels per line. + const __m128i ref0 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[0 * BPS])); + const __m128i ref1 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[1 * BPS])); + const __m128i ref2 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[2 * BPS])); + const __m128i ref3 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[3 * BPS])); + ref01 = _mm_unpacklo_epi32(ref0, ref1); + ref23 = _mm_unpacklo_epi32(ref2, ref3); + + // Convert to 16b. + ref01 = _mm_unpacklo_epi8(ref01, zero); + ref23 = _mm_unpacklo_epi8(ref23, zero); + // Add the inverse transform(s). + ref01 = _mm_add_epi16(ref01, T01); + ref23 = _mm_add_epi16(ref23, T23); + // Unsigned saturate to 8b. + ref0123 = _mm_packus_epi16(ref01, ref23); + + _mm_storeu_si128((__m128i *)buf, ref0123); + + // Store four bytes/pixels per line. + WebPInt32ToMem(&dst[0 * BPS], buf[0]); + WebPInt32ToMem(&dst[1 * BPS], buf[1]); + WebPInt32ToMem(&dst[2 * BPS], buf[2]); + WebPInt32ToMem(&dst[3 * BPS], buf[3]); + } +} + +// Does two inverse transforms. +static void ITransform_Two_SSE2(const uint8_t* ref, const int16_t* in, + uint8_t* dst) { // This implementation makes use of 16-bit fixed point versions of two // multiply constants: // K1 = sqrt(2) * cos (pi/8) ~= 85627 / 2^16 @@ -49,33 +200,21 @@ static void ITransform_SSE2(const uint8_t* ref, const int16_t* in, uint8_t* dst, __m128i T0, T1, T2, T3; // Load and concatenate the transform coefficients (we'll do two inverse - // transforms in parallel). In the case of only one inverse transform, the - // second half of the vectors will just contain random value we'll never - // use nor store. + // transforms in parallel). __m128i in0, in1, in2, in3; { - in0 = _mm_loadl_epi64((const __m128i*)&in[0]); - in1 = _mm_loadl_epi64((const __m128i*)&in[4]); - in2 = _mm_loadl_epi64((const __m128i*)&in[8]); - in3 = _mm_loadl_epi64((const __m128i*)&in[12]); - // a00 a10 a20 a30 x x x x - // a01 a11 a21 a31 x x x x - // a02 a12 a22 a32 x x x x - // a03 a13 a23 a33 x x x x - if (do_two) { - const __m128i inB0 = _mm_loadl_epi64((const __m128i*)&in[16]); - const __m128i inB1 = _mm_loadl_epi64((const __m128i*)&in[20]); - const __m128i inB2 = _mm_loadl_epi64((const __m128i*)&in[24]); - const __m128i inB3 = _mm_loadl_epi64((const __m128i*)&in[28]); - in0 = _mm_unpacklo_epi64(in0, inB0); - in1 = _mm_unpacklo_epi64(in1, inB1); - in2 = _mm_unpacklo_epi64(in2, inB2); - in3 = _mm_unpacklo_epi64(in3, inB3); - // a00 a10 a20 a30 b00 b10 b20 b30 - // a01 a11 a21 a31 b01 b11 b21 b31 - // a02 a12 a22 a32 b02 b12 b22 b32 - // a03 a13 a23 a33 b03 b13 b23 b33 - } + const __m128i tmp0 = _mm_loadu_si128((const __m128i*)&in[0]); + const __m128i tmp1 = _mm_loadu_si128((const __m128i*)&in[8]); + const __m128i tmp2 = _mm_loadu_si128((const __m128i*)&in[16]); + const __m128i tmp3 = _mm_loadu_si128((const __m128i*)&in[24]); + in0 = _mm_unpacklo_epi64(tmp0, tmp2); + in1 = _mm_unpackhi_epi64(tmp0, tmp2); + in2 = _mm_unpacklo_epi64(tmp1, tmp3); + in3 = _mm_unpackhi_epi64(tmp1, tmp3); + // a00 a10 a20 a30 b00 b10 b20 b30 + // a01 a11 a21 a31 b01 b11 b21 b31 + // a02 a12 a22 a32 b02 b12 b22 b32 + // a03 a13 a23 a33 b03 b13 b23 b33 } // Vertical pass and subsequent transpose. @@ -148,19 +287,11 @@ static void ITransform_SSE2(const uint8_t* ref, const int16_t* in, uint8_t* dst, const __m128i zero = _mm_setzero_si128(); // Load the reference(s). __m128i ref0, ref1, ref2, ref3; - if (do_two) { - // Load eight bytes/pixels per line. - ref0 = _mm_loadl_epi64((const __m128i*)&ref[0 * BPS]); - ref1 = _mm_loadl_epi64((const __m128i*)&ref[1 * BPS]); - ref2 = _mm_loadl_epi64((const __m128i*)&ref[2 * BPS]); - ref3 = _mm_loadl_epi64((const __m128i*)&ref[3 * BPS]); - } else { - // Load four bytes/pixels per line. - ref0 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[0 * BPS])); - ref1 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[1 * BPS])); - ref2 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[2 * BPS])); - ref3 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[3 * BPS])); - } + // Load eight bytes/pixels per line. + ref0 = _mm_loadl_epi64((const __m128i*)&ref[0 * BPS]); + ref1 = _mm_loadl_epi64((const __m128i*)&ref[1 * BPS]); + ref2 = _mm_loadl_epi64((const __m128i*)&ref[2 * BPS]); + ref3 = _mm_loadl_epi64((const __m128i*)&ref[3 * BPS]); // Convert to 16b. ref0 = _mm_unpacklo_epi8(ref0, zero); ref1 = _mm_unpacklo_epi8(ref1, zero); @@ -176,20 +307,21 @@ static void ITransform_SSE2(const uint8_t* ref, const int16_t* in, uint8_t* dst, ref1 = _mm_packus_epi16(ref1, ref1); ref2 = _mm_packus_epi16(ref2, ref2); ref3 = _mm_packus_epi16(ref3, ref3); - // Store the results. - if (do_two) { - // Store eight bytes/pixels per line. - _mm_storel_epi64((__m128i*)&dst[0 * BPS], ref0); - _mm_storel_epi64((__m128i*)&dst[1 * BPS], ref1); - _mm_storel_epi64((__m128i*)&dst[2 * BPS], ref2); - _mm_storel_epi64((__m128i*)&dst[3 * BPS], ref3); - } else { - // Store four bytes/pixels per line. - WebPInt32ToMem(&dst[0 * BPS], _mm_cvtsi128_si32(ref0)); - WebPInt32ToMem(&dst[1 * BPS], _mm_cvtsi128_si32(ref1)); - WebPInt32ToMem(&dst[2 * BPS], _mm_cvtsi128_si32(ref2)); - WebPInt32ToMem(&dst[3 * BPS], _mm_cvtsi128_si32(ref3)); - } + // Store eight bytes/pixels per line. + _mm_storel_epi64((__m128i*)&dst[0 * BPS], ref0); + _mm_storel_epi64((__m128i*)&dst[1 * BPS], ref1); + _mm_storel_epi64((__m128i*)&dst[2 * BPS], ref2); + _mm_storel_epi64((__m128i*)&dst[3 * BPS], ref3); + } +} + +// Does one or two inverse transforms. +static void ITransform_SSE2(const uint8_t* ref, const int16_t* in, uint8_t* dst, + int do_two) { + if (do_two) { + ITransform_Two_SSE2(ref, in, dst); + } else { + ITransform_One_SSE2(ref, in, dst); } } diff --git a/thirdparty/libwebp/src/dsp/filters.c b/thirdparty/libwebp/src/dsp/filters.c index 4506567ba3..85eee5098f 100644 --- a/thirdparty/libwebp/src/dsp/filters.c +++ b/thirdparty/libwebp/src/dsp/filters.c @@ -233,6 +233,7 @@ static void GradientUnfilter_C(const uint8_t* prev, const uint8_t* in, WebPFilterFunc WebPFilters[WEBP_FILTER_LAST]; WebPUnfilterFunc WebPUnfilters[WEBP_FILTER_LAST]; +extern VP8CPUInfo VP8GetCPUInfo; extern void VP8FiltersInitMIPSdspR2(void); extern void VP8FiltersInitMSA(void); extern void VP8FiltersInitNEON(void); diff --git a/thirdparty/libwebp/src/dsp/lossless.c b/thirdparty/libwebp/src/dsp/lossless.c index fb86e58d4a..9f81209453 100644 --- a/thirdparty/libwebp/src/dsp/lossless.c +++ b/thirdparty/libwebp/src/dsp/lossless.c @@ -588,6 +588,7 @@ VP8LConvertFunc VP8LConvertBGRAToBGR; VP8LMapARGBFunc VP8LMapColor32b; VP8LMapAlphaFunc VP8LMapColor8b; +extern VP8CPUInfo VP8GetCPUInfo; extern void VP8LDspInitSSE2(void); extern void VP8LDspInitSSE41(void); extern void VP8LDspInitNEON(void); diff --git a/thirdparty/libwebp/src/dsp/lossless_enc.c b/thirdparty/libwebp/src/dsp/lossless_enc.c index b1f9f26d72..cde1280617 100644 --- a/thirdparty/libwebp/src/dsp/lossless_enc.c +++ b/thirdparty/libwebp/src/dsp/lossless_enc.c @@ -791,6 +791,7 @@ VP8LBundleColorMapFunc VP8LBundleColorMap; VP8LPredictorAddSubFunc VP8LPredictorsSub[16]; VP8LPredictorAddSubFunc VP8LPredictorsSub_C[16]; +extern VP8CPUInfo VP8GetCPUInfo; extern void VP8LEncDspInitSSE2(void); extern void VP8LEncDspInitSSE41(void); extern void VP8LEncDspInitNEON(void); diff --git a/thirdparty/libwebp/src/dsp/lossless_enc_neon.c b/thirdparty/libwebp/src/dsp/lossless_enc_neon.c index 7c7b73f8b6..e32c7961a2 100644 --- a/thirdparty/libwebp/src/dsp/lossless_enc_neon.c +++ b/thirdparty/libwebp/src/dsp/lossless_enc_neon.c @@ -25,7 +25,7 @@ // vtbl?_u8 are marked unavailable for iOS arm64 with Xcode < 6.3, use // non-standard versions there. -#if defined(__APPLE__) && defined(__aarch64__) && \ +#if defined(__APPLE__) && WEBP_AARCH64 && \ defined(__apple_build_version__) && (__apple_build_version__< 6020037) #define USE_VTBLQ #endif diff --git a/thirdparty/libwebp/src/dsp/lossless_neon.c b/thirdparty/libwebp/src/dsp/lossless_neon.c index 89e3e013a0..ddc9b61711 100644 --- a/thirdparty/libwebp/src/dsp/lossless_neon.c +++ b/thirdparty/libwebp/src/dsp/lossless_neon.c @@ -498,7 +498,7 @@ static void PredictorAdd13_NEON(const uint32_t* in, const uint32_t* upper, // vtbl?_u8 are marked unavailable for iOS arm64 with Xcode < 6.3, use // non-standard versions there. -#if defined(__APPLE__) && defined(__aarch64__) && \ +#if defined(__APPLE__) && WEBP_AARCH64 && \ defined(__apple_build_version__) && (__apple_build_version__< 6020037) #define USE_VTBLQ #endif diff --git a/thirdparty/libwebp/src/dsp/neon.h b/thirdparty/libwebp/src/dsp/neon.h index c591f9b9a7..14acb4044b 100644 --- a/thirdparty/libwebp/src/dsp/neon.h +++ b/thirdparty/libwebp/src/dsp/neon.h @@ -21,7 +21,7 @@ // Right now, some intrinsics functions seem slower, so we disable them // everywhere except newer clang/gcc or aarch64 where the inline assembly is // incompatible. -#if LOCAL_CLANG_PREREQ(3,8) || LOCAL_GCC_PREREQ(4,9) || defined(__aarch64__) +#if LOCAL_CLANG_PREREQ(3, 8) || LOCAL_GCC_PREREQ(4, 9) || WEBP_AARCH64 #define WEBP_USE_INTRINSICS // use intrinsics when possible #endif @@ -46,7 +46,7 @@ // if using intrinsics, this flag avoids some functions that make gcc-4.6.3 // crash ("internal compiler error: in immed_double_const, at emit-rtl."). // (probably similar to gcc.gnu.org/bugzilla/show_bug.cgi?id=48183) -#if !(LOCAL_CLANG_PREREQ(3,8) || LOCAL_GCC_PREREQ(4,8) || defined(__aarch64__)) +#if !(LOCAL_CLANG_PREREQ(3, 8) || LOCAL_GCC_PREREQ(4, 8) || WEBP_AARCH64) #define WORK_AROUND_GCC #endif diff --git a/thirdparty/libwebp/src/dsp/quant.h b/thirdparty/libwebp/src/dsp/quant.h index fc099bf9d6..bf7734cb11 100644 --- a/thirdparty/libwebp/src/dsp/quant.h +++ b/thirdparty/libwebp/src/dsp/quant.h @@ -22,7 +22,7 @@ #define IsFlat IsFlat_NEON static uint32_t horizontal_add_uint32x4(const uint32x4_t a) { -#if defined(__aarch64__) +#if WEBP_AARCH64 return vaddvq_u32(a); #else const uint64x2_t b = vpaddlq_u32(a); diff --git a/thirdparty/libwebp/src/dsp/rescaler.c b/thirdparty/libwebp/src/dsp/rescaler.c index 14620ce4f1..325d8be180 100644 --- a/thirdparty/libwebp/src/dsp/rescaler.c +++ b/thirdparty/libwebp/src/dsp/rescaler.c @@ -197,6 +197,7 @@ WebPRescalerImportRowFunc WebPRescalerImportRowShrink; WebPRescalerExportRowFunc WebPRescalerExportRowExpand; WebPRescalerExportRowFunc WebPRescalerExportRowShrink; +extern VP8CPUInfo VP8GetCPUInfo; extern void WebPRescalerDspInitSSE2(void); extern void WebPRescalerDspInitMIPS32(void); extern void WebPRescalerDspInitMIPSdspR2(void); diff --git a/thirdparty/libwebp/src/dsp/ssim.c b/thirdparty/libwebp/src/dsp/ssim.c index f85c2e6e5b..9a1341ed95 100644 --- a/thirdparty/libwebp/src/dsp/ssim.c +++ b/thirdparty/libwebp/src/dsp/ssim.c @@ -137,6 +137,7 @@ VP8SSIMGetClippedFunc VP8SSIMGetClipped; VP8AccumulateSSEFunc VP8AccumulateSSE; #endif +extern VP8CPUInfo VP8GetCPUInfo; extern void VP8SSIMDspInitSSE2(void); WEBP_DSP_INIT_FUNC(VP8SSIMDspInit) { diff --git a/thirdparty/libwebp/src/dsp/upsampling.c b/thirdparty/libwebp/src/dsp/upsampling.c index 87f771f3eb..983b9c42d3 100644 --- a/thirdparty/libwebp/src/dsp/upsampling.c +++ b/thirdparty/libwebp/src/dsp/upsampling.c @@ -215,6 +215,7 @@ static void EmptyYuv444Func(const uint8_t* y, WebPYUV444Converter WebPYUV444Converters[MODE_LAST]; +extern VP8CPUInfo VP8GetCPUInfo; extern void WebPInitYUV444ConvertersMIPSdspR2(void); extern void WebPInitYUV444ConvertersSSE2(void); extern void WebPInitYUV444ConvertersSSE41(void); diff --git a/thirdparty/libwebp/src/dsp/upsampling_neon.c b/thirdparty/libwebp/src/dsp/upsampling_neon.c index 6ba71a7de5..bbc000ca2d 100644 --- a/thirdparty/libwebp/src/dsp/upsampling_neon.c +++ b/thirdparty/libwebp/src/dsp/upsampling_neon.c @@ -111,7 +111,7 @@ static const int16_t kCoeffs1[4] = { 19077, 26149, 6419, 13320 }; vst4_u8(out, v255_r_g_b); \ } while (0) -#if !defined(WEBP_SWAP_16BIT_CSP) +#if (WEBP_SWAP_16BIT_CSP == 0) #define ZIP_U8(lo, hi) vzip_u8((lo), (hi)) #else #define ZIP_U8(lo, hi) vzip_u8((hi), (lo)) diff --git a/thirdparty/libwebp/src/dsp/yuv.c b/thirdparty/libwebp/src/dsp/yuv.c index d16c13d3ca..8a04b85d82 100644 --- a/thirdparty/libwebp/src/dsp/yuv.c +++ b/thirdparty/libwebp/src/dsp/yuv.c @@ -70,6 +70,7 @@ void WebPSamplerProcessPlane(const uint8_t* y, int y_stride, WebPSamplerRowFunc WebPSamplers[MODE_LAST]; +extern VP8CPUInfo VP8GetCPUInfo; extern void WebPInitSamplersSSE2(void); extern void WebPInitSamplersSSE41(void); extern void WebPInitSamplersMIPS32(void); diff --git a/thirdparty/libwebp/src/enc/alpha_enc.c b/thirdparty/libwebp/src/enc/alpha_enc.c index f7c02690e3..26f003485a 100644 --- a/thirdparty/libwebp/src/enc/alpha_enc.c +++ b/thirdparty/libwebp/src/enc/alpha_enc.c @@ -13,6 +13,7 @@ #include <assert.h> #include <stdlib.h> +#include <string.h> #include "src/enc/vp8i_enc.h" #include "src/dsp/dsp.h" @@ -140,6 +141,11 @@ static int EncodeAlphaInternal(const uint8_t* const data, int width, int height, !reduce_levels, &tmp_bw, &result->stats); if (ok) { output = VP8LBitWriterFinish(&tmp_bw); + if (tmp_bw.error_) { + VP8LBitWriterWipeOut(&tmp_bw); + memset(&result->bw, 0, sizeof(result->bw)); + return 0; + } output_size = VP8LBitWriterNumBytes(&tmp_bw); if (output_size > data_size) { // compressed size is larger than source! Revert to uncompressed mode. @@ -148,6 +154,7 @@ static int EncodeAlphaInternal(const uint8_t* const data, int width, int height, } } else { VP8LBitWriterWipeOut(&tmp_bw); + memset(&result->bw, 0, sizeof(result->bw)); return 0; } } @@ -162,7 +169,7 @@ static int EncodeAlphaInternal(const uint8_t* const data, int width, int height, header = method | (filter << 2); if (reduce_levels) header |= ALPHA_PREPROCESSED_LEVELS << 4; - VP8BitWriterInit(&result->bw, ALPHA_HEADER_LEN + output_size); + if (!VP8BitWriterInit(&result->bw, ALPHA_HEADER_LEN + output_size)) ok = 0; ok = ok && VP8BitWriterAppend(&result->bw, &header, ALPHA_HEADER_LEN); ok = ok && VP8BitWriterAppend(&result->bw, output, output_size); @@ -312,11 +319,11 @@ static int EncodeAlpha(VP8Encoder* const enc, assert(filter >= WEBP_FILTER_NONE && filter <= WEBP_FILTER_FAST); if (quality < 0 || quality > 100) { - return 0; + return WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION); } if (method < ALPHA_NO_COMPRESSION || method > ALPHA_LOSSLESS_COMPRESSION) { - return 0; + return WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION); } if (method == ALPHA_NO_COMPRESSION) { @@ -326,7 +333,7 @@ static int EncodeAlpha(VP8Encoder* const enc, quant_alpha = (uint8_t*)WebPSafeMalloc(1ULL, data_size); if (quant_alpha == NULL) { - return 0; + return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); } // Extract alpha data (width x height) from raw_data (stride x height). @@ -346,6 +353,9 @@ static int EncodeAlpha(VP8Encoder* const enc, ok = ApplyFiltersAndEncode(quant_alpha, width, height, data_size, method, filter, reduce_levels, effort_level, output, output_size, pic->stats); + if (!ok) { + WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); // imprecise + } #if !defined(WEBP_DISABLE_STATS) if (pic->stats != NULL) { // need stats? pic->stats->coded_size += (int)(*output_size); @@ -405,7 +415,7 @@ int VP8EncStartAlpha(VP8Encoder* const enc) { WebPWorker* const worker = &enc->alpha_worker_; // Makes sure worker is good to go. if (!WebPGetWorkerInterface()->Reset(worker)) { - return 0; + return WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY); } WebPGetWorkerInterface()->Launch(worker); return 1; diff --git a/thirdparty/libwebp/src/enc/analysis_enc.c b/thirdparty/libwebp/src/enc/analysis_enc.c index a0001ac034..962eaa998f 100644 --- a/thirdparty/libwebp/src/enc/analysis_enc.c +++ b/thirdparty/libwebp/src/enc/analysis_enc.c @@ -474,6 +474,10 @@ int VP8EncAnalyze(VP8Encoder* const enc) { } else { // Use only one default segment. ResetAllMBInfo(enc); } + if (!ok) { + return WebPEncodingSetError(enc->pic_, + VP8_ENC_ERROR_OUT_OF_MEMORY); // imprecise + } return ok; } diff --git a/thirdparty/libwebp/src/enc/backward_references_enc.c b/thirdparty/libwebp/src/enc/backward_references_enc.c index 49a0fac034..dc98bf1719 100644 --- a/thirdparty/libwebp/src/enc/backward_references_enc.c +++ b/thirdparty/libwebp/src/enc/backward_references_enc.c @@ -283,8 +283,7 @@ int VP8LHashChainFill(VP8LHashChain* const p, int quality, hash_to_first_index = (int32_t*)WebPSafeMalloc(HASH_SIZE, sizeof(*hash_to_first_index)); if (hash_to_first_index == NULL) { - WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); - return 0; + return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); } percent_range = remaining_percent / 2; @@ -1050,8 +1049,7 @@ int VP8LGetBackwardReferences( refs_best = GetBackwardReferencesLowEffort( width, height, argb, cache_bits_best, hash_chain, refs); if (refs_best == NULL) { - WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); - return 0; + return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); } // Set it in first position. BackwardRefsSwap(refs_best, &refs[0]); @@ -1059,8 +1057,7 @@ int VP8LGetBackwardReferences( if (!GetBackwardReferences(width, height, argb, quality, lz77_types_to_try, cache_bits_max, do_no_cache, hash_chain, refs, cache_bits_best)) { - WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); - return 0; + return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); } } diff --git a/thirdparty/libwebp/src/enc/frame_enc.c b/thirdparty/libwebp/src/enc/frame_enc.c index b93d9e5b99..9a98dc1f3e 100644 --- a/thirdparty/libwebp/src/enc/frame_enc.c +++ b/thirdparty/libwebp/src/enc/frame_enc.c @@ -689,7 +689,7 @@ static int PreLoopInitialize(VP8Encoder* const enc) { } if (!ok) { VP8EncFreeBitWriters(enc); // malloc error occurred - WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY); + return WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY); } return ok; } @@ -719,6 +719,7 @@ static int PostLoopFinalize(VP8EncIterator* const it, int ok) { } else { // Something bad happened -> need to do some memory cleanup. VP8EncFreeBitWriters(enc); + return WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY); } return ok; } @@ -754,6 +755,11 @@ int VP8EncLoop(VP8Encoder* const enc) { // *then* decide how to code the skip decision if there's one. if (!VP8Decimate(&it, &info, rd_opt) || dont_use_skip) { CodeResiduals(it.bw_, &it, &info); + if (it.bw_->error_) { + // enc->pic_->error_code is set in PostLoopFinalize(). + ok = 0; + break; + } } else { // reset predictors after a skip ResetAfterSkip(&it); } diff --git a/thirdparty/libwebp/src/enc/picture_csp_enc.c b/thirdparty/libwebp/src/enc/picture_csp_enc.c index 78c8ca479b..a9280e6c30 100644 --- a/thirdparty/libwebp/src/enc/picture_csp_enc.c +++ b/thirdparty/libwebp/src/enc/picture_csp_enc.c @@ -98,6 +98,7 @@ static int kLinearToGammaTab[GAMMA_TAB_SIZE + 1]; static uint16_t kGammaToLinearTab[256]; static volatile int kGammaTablesOk = 0; static void InitGammaTables(void); +extern VP8CPUInfo VP8GetCPUInfo; WEBP_DSP_INIT_FUNC(InitGammaTables) { if (!kGammaTablesOk) { @@ -534,7 +535,9 @@ static int ImportYUVAFromRGBA(const uint8_t* r_ptr, WebPInitConvertARGBToYUV(); InitGammaTables(); - if (tmp_rgb == NULL) return 0; // malloc error + if (tmp_rgb == NULL) { + return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); + } // Downsample Y/U/V planes, two rows at a time for (y = 0; y < (height >> 1); ++y) { diff --git a/thirdparty/libwebp/src/enc/picture_rescale_enc.c b/thirdparty/libwebp/src/enc/picture_rescale_enc.c index 839f91cacc..ea90d82548 100644 --- a/thirdparty/libwebp/src/enc/picture_rescale_enc.c +++ b/thirdparty/libwebp/src/enc/picture_rescale_enc.c @@ -137,7 +137,9 @@ int WebPPictureCrop(WebPPicture* pic, PictureGrabSpecs(pic, &tmp); tmp.width = width; tmp.height = height; - if (!WebPPictureAlloc(&tmp)) return 0; + if (!WebPPictureAlloc(&tmp)) { + return WebPEncodingSetError(pic, tmp.error_code); + } if (!pic->use_argb) { const int y_offset = top * pic->y_stride + left; @@ -212,26 +214,28 @@ int WebPPictureRescale(WebPPicture* picture, int width, int height) { prev_height = picture->height; if (!WebPRescalerGetScaledDimensions( prev_width, prev_height, &width, &height)) { - return 0; + return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION); } PictureGrabSpecs(picture, &tmp); tmp.width = width; tmp.height = height; - if (!WebPPictureAlloc(&tmp)) return 0; + if (!WebPPictureAlloc(&tmp)) { + return WebPEncodingSetError(picture, tmp.error_code); + } if (!picture->use_argb) { work = (rescaler_t*)WebPSafeMalloc(2ULL * width, sizeof(*work)); if (work == NULL) { WebPPictureFree(&tmp); - return 0; + return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); } // If present, we need to rescale alpha first (for AlphaMultiplyY). if (picture->a != NULL) { WebPInitAlphaProcessing(); if (!RescalePlane(picture->a, prev_width, prev_height, picture->a_stride, tmp.a, width, height, tmp.a_stride, work, 1)) { - return 0; + return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION); } } @@ -246,14 +250,14 @@ int WebPPictureRescale(WebPPicture* picture, int width, int height) { !RescalePlane(picture->v, HALVE(prev_width), HALVE(prev_height), picture->uv_stride, tmp.v, HALVE(width), HALVE(height), tmp.uv_stride, work, 1)) { - return 0; + return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION); } AlphaMultiplyY(&tmp, 1); } else { work = (rescaler_t*)WebPSafeMalloc(2ULL * width * 4, sizeof(*work)); if (work == NULL) { WebPPictureFree(&tmp); - return 0; + return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); } // In order to correctly interpolate colors, we need to apply the alpha // weighting first (black-matting), scale the RGB values, and remove @@ -263,7 +267,7 @@ int WebPPictureRescale(WebPPicture* picture, int width, int height) { if (!RescalePlane((const uint8_t*)picture->argb, prev_width, prev_height, picture->argb_stride * 4, (uint8_t*)tmp.argb, width, height, tmp.argb_stride * 4, work, 4)) { - return 0; + return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION); } AlphaMultiplyARGB(&tmp, 1); } diff --git a/thirdparty/libwebp/src/enc/syntax_enc.c b/thirdparty/libwebp/src/enc/syntax_enc.c index e18cf650ca..9b8f524d69 100644 --- a/thirdparty/libwebp/src/enc/syntax_enc.c +++ b/thirdparty/libwebp/src/enc/syntax_enc.c @@ -258,7 +258,10 @@ static int EmitPartitionsSize(const VP8Encoder* const enc, buf[3 * p + 1] = (part_size >> 8) & 0xff; buf[3 * p + 2] = (part_size >> 16) & 0xff; } - return p ? pic->writer(buf, 3 * p, pic) : 1; + if (p && !pic->writer(buf, 3 * p, pic)) { + return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE); + } + return 1; } //------------------------------------------------------------------------------ @@ -381,6 +384,7 @@ int VP8EncWrite(VP8Encoder* const enc) { enc->coded_size_ = (int)(CHUNK_HEADER_SIZE + riff_size); ok = ok && WebPReportProgress(pic, final_percent, &enc->percent_); + if (!ok) WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE); return ok; } diff --git a/thirdparty/libwebp/src/enc/vp8i_enc.h b/thirdparty/libwebp/src/enc/vp8i_enc.h index c9927c47d8..19d9a6edb7 100644 --- a/thirdparty/libwebp/src/enc/vp8i_enc.h +++ b/thirdparty/libwebp/src/enc/vp8i_enc.h @@ -32,7 +32,7 @@ extern "C" { // version numbers #define ENC_MAJ_VERSION 1 #define ENC_MIN_VERSION 3 -#define ENC_REV_VERSION 0 +#define ENC_REV_VERSION 1 enum { MAX_LF_LEVELS = 64, // Maximum loop filter level MAX_VARIABLE_LEVEL = 67, // last (inclusive) level with variable cost diff --git a/thirdparty/libwebp/src/enc/vp8l_enc.c b/thirdparty/libwebp/src/enc/vp8l_enc.c index 0b07e529a9..3a8ec3dd1e 100644 --- a/thirdparty/libwebp/src/enc/vp8l_enc.c +++ b/thirdparty/libwebp/src/enc/vp8l_enc.c @@ -196,8 +196,7 @@ static int CoOccurrenceBuild(const WebPPicture* const pic, uint32_t palette_sorted[MAX_PALETTE_SIZE]; lines = (uint32_t*)WebPSafeMalloc(2 * pic->width, sizeof(*lines)); if (lines == NULL) { - WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); - return 0; + return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); } line_top = &lines[0]; line_current = &lines[pic->width]; @@ -255,10 +254,10 @@ static int PaletteSortModifiedZeng( cooccurrence = (uint32_t*)WebPSafeCalloc(num_colors * num_colors, sizeof(*cooccurrence)); if (cooccurrence == NULL) { - WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); - return 0; + return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); } if (!CoOccurrenceBuild(pic, palette_sorted, num_colors, cooccurrence)) { + WebPSafeFree(cooccurrence); return 0; } @@ -1012,8 +1011,7 @@ static int StoreImageToBitMask( VP8LRefsCursorNext(&c); } if (bw->error_) { - WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); - return 0; + return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); } return 1; } @@ -1297,7 +1295,10 @@ static int EncodeImageInternal( } } tokens = (HuffmanTreeToken*)WebPSafeMalloc(max_tokens, sizeof(*tokens)); - if (tokens == NULL) goto Error; + if (tokens == NULL) { + WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); + goto Error; + } for (i = 0; i < 5 * histogram_image_size; ++i) { HuffmanTreeCode* const codes = &huffman_codes[i]; StoreHuffmanCode(bw, huff_tree, tokens, codes); @@ -1448,18 +1449,21 @@ static int WriteImage(const WebPPicture* const pic, VP8LBitWriter* const bw, const size_t vp8l_size = VP8L_SIGNATURE_SIZE + webpll_size; const size_t pad = vp8l_size & 1; const size_t riff_size = TAG_SIZE + CHUNK_HEADER_SIZE + vp8l_size + pad; + *coded_size = 0; + + if (bw->error_) { + return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); + } if (!WriteRiffHeader(pic, riff_size, vp8l_size) || !pic->writer(webpll_data, webpll_size, pic)) { - WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE); - return 0; + return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE); } if (pad) { const uint8_t pad_byte[1] = { 0 }; if (!pic->writer(pad_byte, 1, pic)) { - WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE); - return 0; + return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE); } } *coded_size = CHUNK_HEADER_SIZE + riff_size; @@ -1504,8 +1508,7 @@ static int AllocateTransformBuffer(VP8LEncoder* const enc, int width, ClearTransformBuffer(enc); mem = (uint32_t*)WebPSafeMalloc(mem_size, sizeof(*mem)); if (mem == NULL) { - WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY); - return 0; + return WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY); } enc->transform_mem_ = mem; enc->transform_mem_size_ = (size_t)mem_size; @@ -1613,8 +1616,7 @@ static int ApplyPalette(const uint32_t* src, uint32_t src_stride, uint32_t* dst, int x, y; if (tmp_row == NULL) { - WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); - return 0; + return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); } if (palette_size < APPLY_PALETTE_GREEDY_MAX) { @@ -1968,9 +1970,8 @@ int VP8LEncodeStream(const WebPConfig* const config, int ok_main; if (enc_main == NULL || !VP8LBitWriterInit(&bw_side, 0)) { - WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); VP8LEncoderDelete(enc_main); - return 0; + return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); } // Avoid "garbage value" error from Clang's static analysis tool. @@ -2117,8 +2118,7 @@ int VP8LEncodeImage(const WebPConfig* const config, if (picture == NULL) return 0; if (config == NULL || picture->argb == NULL) { - WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER); - return 0; + return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER); } width = picture->width; diff --git a/thirdparty/libwebp/src/enc/webp_enc.c b/thirdparty/libwebp/src/enc/webp_enc.c index 9620e05070..583fe6a8bb 100644 --- a/thirdparty/libwebp/src/enc/webp_enc.c +++ b/thirdparty/libwebp/src/enc/webp_enc.c @@ -307,7 +307,10 @@ int WebPEncodingSetError(const WebPPicture* const pic, WebPEncodingError error) { assert((int)error < VP8_ENC_ERROR_LAST); assert((int)error >= VP8_ENC_OK); - ((WebPPicture*)pic)->error_code = error; + // The oldest error reported takes precedence over the new one. + if (pic->error_code == VP8_ENC_OK) { + ((WebPPicture*)pic)->error_code = error; + } return 0; } @@ -317,8 +320,7 @@ int WebPReportProgress(const WebPPicture* const pic, *percent_store = percent; if (pic->progress_hook && !pic->progress_hook(percent, pic)) { // user abort requested - WebPEncodingSetError(pic, VP8_ENC_ERROR_USER_ABORT); - return 0; + return WebPEncodingSetError(pic, VP8_ENC_ERROR_USER_ABORT); } } return 1; // ok @@ -329,7 +331,7 @@ int WebPEncode(const WebPConfig* config, WebPPicture* pic) { int ok = 0; if (pic == NULL) return 0; - WebPEncodingSetError(pic, VP8_ENC_OK); // all ok so far + pic->error_code = VP8_ENC_OK; // all ok so far if (config == NULL) { // bad params return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER); } diff --git a/thirdparty/libwebp/src/mux/muxi.h b/thirdparty/libwebp/src/mux/muxi.h index 7929138c44..fc44d6f2fe 100644 --- a/thirdparty/libwebp/src/mux/muxi.h +++ b/thirdparty/libwebp/src/mux/muxi.h @@ -29,7 +29,7 @@ extern "C" { #define MUX_MAJ_VERSION 1 #define MUX_MIN_VERSION 3 -#define MUX_REV_VERSION 0 +#define MUX_REV_VERSION 1 // Chunk object. typedef struct WebPChunk WebPChunk; diff --git a/thirdparty/libwebp/src/mux/muxread.c b/thirdparty/libwebp/src/mux/muxread.c index 80050396e1..9862ec68ee 100644 --- a/thirdparty/libwebp/src/mux/muxread.c +++ b/thirdparty/libwebp/src/mux/muxread.c @@ -116,9 +116,12 @@ static int MuxImageParse(const WebPChunk* const chunk, int copy_data, // Each of ANMF chunk contain a header at the beginning. So, its size should // be at least 'hdr_size'. if (size < hdr_size) goto Fail; - ChunkAssignData(&subchunk, &temp, copy_data, chunk->tag_); + if (ChunkAssignData(&subchunk, &temp, copy_data, + chunk->tag_) != WEBP_MUX_OK) { + goto Fail; + } } - ChunkSetHead(&subchunk, &wpi->header_); + if (ChunkSetHead(&subchunk, &wpi->header_) != WEBP_MUX_OK) goto Fail; wpi->is_partial_ = 1; // Waiting for ALPH and/or VP8/VP8L chunks. // Rest of the chunks. diff --git a/thirdparty/libwebp/src/utils/bit_reader_utils.c b/thirdparty/libwebp/src/utils/bit_reader_utils.c index 857cd60988..a26557aa49 100644 --- a/thirdparty/libwebp/src/utils/bit_reader_utils.c +++ b/thirdparty/libwebp/src/utils/bit_reader_utils.c @@ -15,6 +15,7 @@ #include "src/webp/config.h" #endif +#include "src/dsp/cpu.h" #include "src/utils/bit_reader_inl_utils.h" #include "src/utils/utils.h" @@ -121,7 +122,7 @@ int32_t VP8GetSignedValue(VP8BitReader* const br, int bits, #define VP8L_LOG8_WBITS 4 // Number of bytes needed to store VP8L_WBITS bits. -#if defined(__arm__) || defined(_M_ARM) || defined(__aarch64__) || \ +#if defined(__arm__) || defined(_M_ARM) || WEBP_AARCH64 || \ defined(__i386__) || defined(_M_IX86) || \ defined(__x86_64__) || defined(_M_X64) #define VP8L_USE_FAST_LOAD diff --git a/thirdparty/libwebp/src/utils/bit_reader_utils.h b/thirdparty/libwebp/src/utils/bit_reader_utils.h index e64156e318..25ff31e5d9 100644 --- a/thirdparty/libwebp/src/utils/bit_reader_utils.h +++ b/thirdparty/libwebp/src/utils/bit_reader_utils.h @@ -19,6 +19,7 @@ #ifdef _MSC_VER #include <stdlib.h> // _byteswap_ulong #endif +#include "src/dsp/cpu.h" #include "src/webp/types.h" // Warning! This macro triggers quite some MACRO wizardry around func signature! @@ -64,7 +65,7 @@ extern "C" { #define BITS 56 #elif defined(__arm__) || defined(_M_ARM) // ARM #define BITS 24 -#elif defined(__aarch64__) // ARM 64bit +#elif WEBP_AARCH64 // ARM 64bit #define BITS 56 #elif defined(__mips__) // MIPS #define BITS 24 diff --git a/thirdparty/libwebp/src/webp/decode.h b/thirdparty/libwebp/src/webp/decode.h index d98247509a..0177b12089 100644 --- a/thirdparty/libwebp/src/webp/decode.h +++ b/thirdparty/libwebp/src/webp/decode.h @@ -81,10 +81,11 @@ WEBP_EXTERN uint8_t* WebPDecodeBGR(const uint8_t* data, size_t data_size, // returned is the Y samples buffer. Upon return, *u and *v will point to // the U and V chroma data. These U and V buffers need NOT be passed to // WebPFree(), unlike the returned Y luma one. The dimension of the U and V -// planes are both (*width + 1) / 2 and (*height + 1)/ 2. +// planes are both (*width + 1) / 2 and (*height + 1) / 2. // Upon return, the Y buffer has a stride returned as '*stride', while U and V // have a common stride returned as '*uv_stride'. -// Return NULL in case of error. +// 'width' and 'height' may be NULL, the other pointers must not be. +// Returns NULL in case of error. // (*) Also named Y'CbCr. See: https://en.wikipedia.org/wiki/YCbCr WEBP_EXTERN uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size, int* width, int* height, diff --git a/thirdparty/mbedtls/include/mbedtls/aria.h b/thirdparty/mbedtls/include/mbedtls/aria.h index 9856a1cae7..d307ff9e47 100644 --- a/thirdparty/mbedtls/include/mbedtls/aria.h +++ b/thirdparty/mbedtls/include/mbedtls/aria.h @@ -274,10 +274,6 @@ int mbedtls_aria_crypt_cfb128(mbedtls_aria_context *ctx, * \brief This function performs an ARIA-CTR encryption or decryption * operation. * - * This function performs the operation defined in the \p mode - * parameter (encrypt/decrypt), on the input data buffer - * defined in the \p input parameter. - * * Due to the nature of CTR, you must use the same key schedule * for both encryption and decryption operations. Therefore, you * must use the context initialized with mbedtls_aria_setkey_enc() diff --git a/thirdparty/mbedtls/include/mbedtls/asn1.h b/thirdparty/mbedtls/include/mbedtls/asn1.h index 540cdcc469..82aaee8f30 100644 --- a/thirdparty/mbedtls/include/mbedtls/asn1.h +++ b/thirdparty/mbedtls/include/mbedtls/asn1.h @@ -453,7 +453,7 @@ void mbedtls_asn1_sequence_free(mbedtls_asn1_sequence *seq); * on a successful invocation. * \param end The end of the ASN.1 SEQUENCE container. * \param tag_must_mask A mask to be applied to the ASN.1 tags found within - * the SEQUENCE before comparing to \p tag_must_value. + * the SEQUENCE before comparing to \p tag_must_val. * \param tag_must_val The required value of each ASN.1 tag found in the * SEQUENCE, after masking with \p tag_must_mask. * Mismatching tags lead to an error. @@ -462,7 +462,7 @@ void mbedtls_asn1_sequence_free(mbedtls_asn1_sequence *seq); * while a value of \c 0xFF for \p tag_must_mask means * that \p tag_must_val is the only allowed tag. * \param tag_may_mask A mask to be applied to the ASN.1 tags found within - * the SEQUENCE before comparing to \p tag_may_value. + * the SEQUENCE before comparing to \p tag_may_val. * \param tag_may_val The desired value of each ASN.1 tag found in the * SEQUENCE, after masking with \p tag_may_mask. * Mismatching tags will be silently ignored. diff --git a/thirdparty/mbedtls/include/mbedtls/bignum.h b/thirdparty/mbedtls/include/mbedtls/bignum.h index 788ea21a8d..cbed25984e 100644 --- a/thirdparty/mbedtls/include/mbedtls/bignum.h +++ b/thirdparty/mbedtls/include/mbedtls/bignum.h @@ -533,7 +533,7 @@ int mbedtls_mpi_write_file(const char *p, const mbedtls_mpi *X, * \param X The destination MPI. This must point to an initialized MPI. * \param buf The input buffer. This must be a readable buffer of length * \p buflen Bytes. - * \param buflen The length of the input buffer \p p in Bytes. + * \param buflen The length of the input buffer \p buf in Bytes. * * \return \c 0 if successful. * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed. @@ -548,7 +548,7 @@ int mbedtls_mpi_read_binary(mbedtls_mpi *X, const unsigned char *buf, * \param X The destination MPI. This must point to an initialized MPI. * \param buf The input buffer. This must be a readable buffer of length * \p buflen Bytes. - * \param buflen The length of the input buffer \p p in Bytes. + * \param buflen The length of the input buffer \p buf in Bytes. * * \return \c 0 if successful. * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed. @@ -986,8 +986,8 @@ int mbedtls_mpi_gcd(mbedtls_mpi *G, const mbedtls_mpi *A, * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. * \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if \p N is less than * or equal to one. - * \return #MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if \p has no modular inverse - * with respect to \p N. + * \return #MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if \p A has no modular + * inverse with respect to \p N. */ int mbedtls_mpi_inv_mod(mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *N); @@ -1039,7 +1039,7 @@ MBEDTLS_DEPRECATED int mbedtls_mpi_is_prime(const mbedtls_mpi *X, * This must point to an initialized MPI. * \param rounds The number of bases to perform the Miller-Rabin primality * test for. The probability of returning 0 on a composite is - * at most 2<sup>-2*\p rounds</sup>. + * at most 2<sup>-2*\p rounds </sup>. * \param f_rng The RNG function to use. This must not be \c NULL. * \param p_rng The RNG parameter to be passed to \p f_rng. * This may be \c NULL if \p f_rng doesn't use diff --git a/thirdparty/mbedtls/include/mbedtls/bn_mul.h b/thirdparty/mbedtls/include/mbedtls/bn_mul.h index a0bc4d061d..6414e54291 100644 --- a/thirdparty/mbedtls/include/mbedtls/bn_mul.h +++ b/thirdparty/mbedtls/include/mbedtls/bn_mul.h @@ -677,6 +677,15 @@ #if defined(__arm__) && !defined(MULADDC_CANNOT_USE_R7) #if defined(__thumb__) && !defined(__thumb2__) +#if !defined(__ARMCC_VERSION) && !defined(__clang__) \ + && !defined(__llvm__) && !defined(__INTEL_COMPILER) +/* + * Thumb 1 ISA. This code path has only been tested successfully on gcc; + * it does not compile on clang or armclang. + * + * Other compilers which define __GNUC__ may not work. The above macro + * attempts to exclude these untested compilers. + */ #define MULADDC_INIT \ asm( \ @@ -731,6 +740,8 @@ "r6", "r7", "r8", "r9", "cc" \ ); +#endif /* Compiler is gcc */ + #elif (__ARM_ARCH >= 6) && \ defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1) diff --git a/thirdparty/mbedtls/include/mbedtls/camellia.h b/thirdparty/mbedtls/include/mbedtls/camellia.h index 05397d2316..e840947d4b 100644 --- a/thirdparty/mbedtls/include/mbedtls/camellia.h +++ b/thirdparty/mbedtls/include/mbedtls/camellia.h @@ -231,7 +231,7 @@ int mbedtls_camellia_crypt_cfb128(mbedtls_camellia_context *ctx, * *note Due to the nature of CTR mode, you should use the same * key for both encryption and decryption. In particular, calls * to this function should be preceded by a key-schedule via - * mbedtls_camellia_setkey_enc() regardless of whether \p mode + * mbedtls_camellia_setkey_enc() regardless of whether the mode * is #MBEDTLS_CAMELLIA_ENCRYPT or #MBEDTLS_CAMELLIA_DECRYPT. * * \warning You must never reuse a nonce value with the same key. Doing so diff --git a/thirdparty/mbedtls/include/mbedtls/cipher.h b/thirdparty/mbedtls/include/mbedtls/cipher.h index aa155d7bf8..56fc2d828a 100644 --- a/thirdparty/mbedtls/include/mbedtls/cipher.h +++ b/thirdparty/mbedtls/include/mbedtls/cipher.h @@ -427,7 +427,7 @@ const mbedtls_cipher_info_t *mbedtls_cipher_info_from_values(const mbedtls_ciphe const mbedtls_cipher_mode_t mode); /** - * \brief This function initializes a \p cipher_context as NONE. + * \brief This function initializes a \p ctx as NONE. * * \param ctx The context to be initialized. This must not be \c NULL. */ @@ -602,7 +602,7 @@ static inline const char *mbedtls_cipher_get_name( * \param ctx The context of the cipher. This must be initialized. * * \return The key length of the cipher in bits. - * \return #MBEDTLS_KEY_LENGTH_NONE if ctx \p has not been + * \return #MBEDTLS_KEY_LENGTH_NONE if \p ctx has not been * initialized. */ static inline int mbedtls_cipher_get_key_bitlen( @@ -779,7 +779,7 @@ int mbedtls_cipher_update(mbedtls_cipher_context_t *ctx, * \param ctx The generic cipher context. This must be initialized and * bound to a key. * \param output The buffer to write data to. This needs to be a writable - * buffer of at least \p block_size Bytes. + * buffer of at least block_size Bytes. * \param olen The length of the data written to the \p output buffer. * This may not be \c NULL. * diff --git a/thirdparty/mbedtls/include/mbedtls/config.h b/thirdparty/mbedtls/include/mbedtls/config.h index 1381c1fd16..04acc1c343 100644 --- a/thirdparty/mbedtls/include/mbedtls/config.h +++ b/thirdparty/mbedtls/include/mbedtls/config.h @@ -1458,8 +1458,8 @@ * ); * ``` * The \c context value is initialized to 0 before the first call. - * The function must fill the \c output buffer with \p output_size bytes - * of random data and set \c *output_length to \p output_size. + * The function must fill the \c output buffer with \c output_size bytes + * of random data and set \c *output_length to \c output_size. * * Requires: MBEDTLS_PSA_CRYPTO_C * diff --git a/thirdparty/mbedtls/include/mbedtls/ecdsa.h b/thirdparty/mbedtls/include/mbedtls/ecdsa.h index e42d114c42..b7029d7d56 100644 --- a/thirdparty/mbedtls/include/mbedtls/ecdsa.h +++ b/thirdparty/mbedtls/include/mbedtls/ecdsa.h @@ -266,8 +266,9 @@ int mbedtls_ecdsa_sign_det(mbedtls_ecp_group *grp, mbedtls_mpi *r, * \param md_alg The hash algorithm used to hash the original data. * \param f_rng_blind The RNG function used for blinding. This must not be * \c NULL. - * \param p_rng_blind The RNG context to be passed to \p f_rng. This may be - * \c NULL if \p f_rng doesn't need a context parameter. + * \param p_rng_blind The RNG context to be passed to \p f_rng_blind. This + * may be \c NULL if \p f_rng_blind doesn't need + * a context parameter. * * \return \c 0 on success. * \return An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX @@ -344,7 +345,7 @@ int mbedtls_ecdsa_verify(mbedtls_ecp_group *grp, * via mbedtls_ecdsa_genkey() or mbedtls_ecdsa_from_keypair(). * \param md_alg The message digest that was used to hash the message. * \param hash The message hash to be signed. This must be a readable - * buffer of length \p blen Bytes. + * buffer of length \p hlen Bytes. * \param hlen The length of the hash \p hash in Bytes. * \param sig The buffer to which to write the signature. This must be a * writable buffer of length at least twice as large as the @@ -386,7 +387,7 @@ int mbedtls_ecdsa_write_signature(mbedtls_ecdsa_context *ctx, * via mbedtls_ecdsa_genkey() or mbedtls_ecdsa_from_keypair(). * \param md_alg The message digest that was used to hash the message. * \param hash The message hash to be signed. This must be a readable - * buffer of length \p blen Bytes. + * buffer of length \p hlen Bytes. * \param hlen The length of the hash \p hash in Bytes. * \param sig The buffer to which to write the signature. This must be a * writable buffer of length at least twice as large as the @@ -453,7 +454,7 @@ int mbedtls_ecdsa_write_signature_restartable(mbedtls_ecdsa_context *ctx, * and have a group and private key bound to it, for example * via mbedtls_ecdsa_genkey() or mbedtls_ecdsa_from_keypair(). * \param hash The message hash to be signed. This must be a readable - * buffer of length \p blen Bytes. + * buffer of length \p hlen Bytes. * \param hlen The length of the hash \p hash in Bytes. * \param sig The buffer to which to write the signature. This must be a * writable buffer of length at least twice as large as the @@ -490,7 +491,7 @@ int mbedtls_ecdsa_write_signature_det(mbedtls_ecdsa_context *ctx, * \param ctx The ECDSA context to use. This must be initialized * and have a group and public key bound to it. * \param hash The message hash that was signed. This must be a readable - * buffer of length \p size Bytes. + * buffer of length \p hlen Bytes. * \param hlen The size of the hash \p hash. * \param sig The signature to read and verify. This must be a readable * buffer of length \p slen Bytes. @@ -520,7 +521,7 @@ int mbedtls_ecdsa_read_signature(mbedtls_ecdsa_context *ctx, * \param ctx The ECDSA context to use. This must be initialized * and have a group and public key bound to it. * \param hash The message hash that was signed. This must be a readable - * buffer of length \p size Bytes. + * buffer of length \p hlen Bytes. * \param hlen The size of the hash \p hash. * \param sig The signature to read and verify. This must be a readable * buffer of length \p slen Bytes. diff --git a/thirdparty/mbedtls/include/mbedtls/ecp.h b/thirdparty/mbedtls/include/mbedtls/ecp.h index 5402e74b00..d56069ec4e 100644 --- a/thirdparty/mbedtls/include/mbedtls/ecp.h +++ b/thirdparty/mbedtls/include/mbedtls/ecp.h @@ -1081,7 +1081,7 @@ int mbedtls_ecp_muladd_restartable( * * It only checks that the point is non-zero, has * valid coordinates and lies on the curve. It does not verify - * that it is indeed a multiple of \p G. This additional + * that it is indeed a multiple of \c G. This additional * check is computationally more expensive, is not required * by standards, and should not be necessary if the group * used has a small cofactor. In particular, it is useless for @@ -1106,7 +1106,7 @@ int mbedtls_ecp_check_pubkey(const mbedtls_ecp_group *grp, const mbedtls_ecp_point *pt); /** - * \brief This function checks that an \p mbedtls_mpi is a + * \brief This function checks that an \c mbedtls_mpi is a * valid private key for this curve. * * \note This function uses bare components rather than an diff --git a/thirdparty/mbedtls/include/mbedtls/hmac_drbg.h b/thirdparty/mbedtls/include/mbedtls/hmac_drbg.h index 3ccf61c7fd..6b2248531b 100644 --- a/thirdparty/mbedtls/include/mbedtls/hmac_drbg.h +++ b/thirdparty/mbedtls/include/mbedtls/hmac_drbg.h @@ -186,8 +186,8 @@ void mbedtls_hmac_drbg_init(mbedtls_hmac_drbg_context *ctx); * \param len The length of the personalization string. * This must be at most #MBEDTLS_HMAC_DRBG_MAX_INPUT * and also at most - * #MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT - \p entropy_len * 3 / 2 - * where \p entropy_len is the entropy length + * #MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT - \c entropy_len * 3 / 2 + * where \c entropy_len is the entropy length * described above. * * \return \c 0 if successful. @@ -316,8 +316,8 @@ int mbedtls_hmac_drbg_update_ret(mbedtls_hmac_drbg_context *ctx, * \param len The length of the additional data. * This must be at most #MBEDTLS_HMAC_DRBG_MAX_INPUT * and also at most - * #MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT - \p entropy_len - * where \p entropy_len is the entropy length + * #MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT - \c entropy_len + * where \c entropy_len is the entropy length * (see mbedtls_hmac_drbg_set_entropy_len()). * * \return \c 0 if successful. diff --git a/thirdparty/mbedtls/include/mbedtls/pk.h b/thirdparty/mbedtls/include/mbedtls/pk.h index ec83551368..0e9d58aec6 100644 --- a/thirdparty/mbedtls/include/mbedtls/pk.h +++ b/thirdparty/mbedtls/include/mbedtls/pk.h @@ -395,7 +395,7 @@ int mbedtls_pk_can_do(const mbedtls_pk_context *ctx, mbedtls_pk_type_t type); * * \return 0 on success (signature is valid), * #MBEDTLS_ERR_PK_SIG_LEN_MISMATCH if there is a valid - * signature in sig but its length is less than \p siglen, + * signature in \p sig but its length is less than \p sig_len, * or a specific error code. * * \note For RSA keys, the default padding type is PKCS#1 v1.5. @@ -459,7 +459,7 @@ int mbedtls_pk_verify_restartable(mbedtls_pk_context *ctx, * #MBEDTLS_ERR_PK_TYPE_MISMATCH if the PK context can't be * used for this type of signatures, * #MBEDTLS_ERR_PK_SIG_LEN_MISMATCH if there is a valid - * signature in sig but its length is less than \p siglen, + * signature in \p sig but its length is less than \p sig_len, * or a specific error code. * * \note If hash_len is 0, then the length associated with md_alg diff --git a/thirdparty/mbedtls/include/mbedtls/platform.h b/thirdparty/mbedtls/include/mbedtls/platform.h index 9033852be1..d6faa7eda6 100644 --- a/thirdparty/mbedtls/include/mbedtls/platform.h +++ b/thirdparty/mbedtls/include/mbedtls/platform.h @@ -144,6 +144,8 @@ extern "C" { #if defined(MBEDTLS_PLATFORM_MEMORY) #if defined(MBEDTLS_PLATFORM_FREE_MACRO) && \ defined(MBEDTLS_PLATFORM_CALLOC_MACRO) +#undef mbedtls_free +#undef mbedtls_calloc #define mbedtls_free MBEDTLS_PLATFORM_FREE_MACRO #define mbedtls_calloc MBEDTLS_PLATFORM_CALLOC_MACRO #else @@ -165,6 +167,8 @@ int mbedtls_platform_set_calloc_free(void *(*calloc_func)(size_t, size_t), void (*free_func)(void *)); #endif /* MBEDTLS_PLATFORM_FREE_MACRO && MBEDTLS_PLATFORM_CALLOC_MACRO */ #else /* !MBEDTLS_PLATFORM_MEMORY */ +#undef mbedtls_free +#undef mbedtls_calloc #define mbedtls_free free #define mbedtls_calloc calloc #endif /* MBEDTLS_PLATFORM_MEMORY && !MBEDTLS_PLATFORM_{FREE,CALLOC}_MACRO */ @@ -189,6 +193,7 @@ extern int (*mbedtls_fprintf)(FILE *stream, const char *format, ...); int mbedtls_platform_set_fprintf(int (*fprintf_func)(FILE *stream, const char *, ...)); #else +#undef mbedtls_fprintf #if defined(MBEDTLS_PLATFORM_FPRINTF_MACRO) #define mbedtls_fprintf MBEDTLS_PLATFORM_FPRINTF_MACRO #else @@ -213,6 +218,7 @@ extern int (*mbedtls_printf)(const char *format, ...); */ int mbedtls_platform_set_printf(int (*printf_func)(const char *, ...)); #else /* !MBEDTLS_PLATFORM_PRINTF_ALT */ +#undef mbedtls_printf #if defined(MBEDTLS_PLATFORM_PRINTF_MACRO) #define mbedtls_printf MBEDTLS_PLATFORM_PRINTF_MACRO #else @@ -248,6 +254,7 @@ extern int (*mbedtls_snprintf)(char *s, size_t n, const char *format, ...); int mbedtls_platform_set_snprintf(int (*snprintf_func)(char *s, size_t n, const char *format, ...)); #else /* MBEDTLS_PLATFORM_SNPRINTF_ALT */ +#undef mbedtls_snprintf #if defined(MBEDTLS_PLATFORM_SNPRINTF_MACRO) #define mbedtls_snprintf MBEDTLS_PLATFORM_SNPRINTF_MACRO #else @@ -284,6 +291,7 @@ extern int (*mbedtls_vsnprintf)(char *s, size_t n, const char *format, va_list a int mbedtls_platform_set_vsnprintf(int (*vsnprintf_func)(char *s, size_t n, const char *format, va_list arg)); #else /* MBEDTLS_PLATFORM_VSNPRINTF_ALT */ +#undef mbedtls_vsnprintf #if defined(MBEDTLS_PLATFORM_VSNPRINTF_MACRO) #define mbedtls_vsnprintf MBEDTLS_PLATFORM_VSNPRINTF_MACRO #else @@ -308,6 +316,7 @@ extern void (*mbedtls_exit)(int status); */ int mbedtls_platform_set_exit(void (*exit_func)(int status)); #else +#undef mbedtls_exit #if defined(MBEDTLS_PLATFORM_EXIT_MACRO) #define mbedtls_exit MBEDTLS_PLATFORM_EXIT_MACRO #else @@ -360,6 +369,8 @@ int mbedtls_platform_set_nv_seed( int (*nv_seed_write_func)(unsigned char *buf, size_t buf_len) ); #else +#undef mbedtls_nv_seed_read +#undef mbedtls_nv_seed_write #if defined(MBEDTLS_PLATFORM_NV_SEED_READ_MACRO) && \ defined(MBEDTLS_PLATFORM_NV_SEED_WRITE_MACRO) #define mbedtls_nv_seed_read MBEDTLS_PLATFORM_NV_SEED_READ_MACRO diff --git a/thirdparty/mbedtls/include/mbedtls/rsa.h b/thirdparty/mbedtls/include/mbedtls/rsa.h index f8725ffb1e..37f07c0766 100644 --- a/thirdparty/mbedtls/include/mbedtls/rsa.h +++ b/thirdparty/mbedtls/include/mbedtls/rsa.h @@ -260,15 +260,15 @@ int mbedtls_rsa_import_raw(mbedtls_rsa_context *ctx, * \brief This function completes an RSA context from * a set of imported core parameters. * - * To setup an RSA public key, precisely \p N and \p E + * To setup an RSA public key, precisely \c N and \c E * must have been imported. * * To setup an RSA private key, sufficient information must * be present for the other parameters to be derivable. * * The default implementation supports the following: - * <ul><li>Derive \p P, \p Q from \p N, \p D, \p E.</li> - * <li>Derive \p N, \p D from \p P, \p Q, \p E.</li></ul> + * <ul><li>Derive \c P, \c Q from \c N, \c D, \c E.</li> + * <li>Derive \c N, \c D from \c P, \c Q, \c E.</li></ul> * Alternative implementations need not support these. * * If this function runs successfully, it guarantees that @@ -537,7 +537,7 @@ int mbedtls_rsa_check_pub_priv(const mbedtls_rsa_context *pub, * \note This function does not handle message padding. * * \note Make sure to set \p input[0] = 0 or ensure that - * input is smaller than \p N. + * input is smaller than \c N. * * \return \c 0 on success. * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. @@ -1109,8 +1109,8 @@ int mbedtls_rsa_rsassa_pss_sign(mbedtls_rsa_context *ctx, * verification using the mode from the context. * * \note For PKCS#1 v2.1 encoding, see comments on - * mbedtls_rsa_rsassa_pss_verify() about \p md_alg and - * \p hash_id. + * mbedtls_rsa_rsassa_pss_verify() about \c md_alg and + * \c hash_id. * * \deprecated It is deprecated and discouraged to call this function * in #MBEDTLS_RSA_PRIVATE mode. Future versions of the library diff --git a/thirdparty/mbedtls/include/mbedtls/ssl.h b/thirdparty/mbedtls/include/mbedtls/ssl.h index 26e4ec400c..cc9a27082b 100644 --- a/thirdparty/mbedtls/include/mbedtls/ssl.h +++ b/thirdparty/mbedtls/include/mbedtls/ssl.h @@ -494,6 +494,7 @@ /* Dummy type used only for its size */ union mbedtls_ssl_premaster_secret { + unsigned char dummy; /* Make the union non-empty even with SSL disabled */ #if defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED) unsigned char _pms_rsa[48]; /* RFC 5246 8.1.1 */ #endif @@ -1746,10 +1747,10 @@ void mbedtls_ssl_set_bio(mbedtls_ssl_context *ssl, * \param own_cid The address of the readable buffer holding the CID we want * the peer to use when sending encrypted messages to us. * This may be \c NULL if \p own_cid_len is \c 0. - * This parameter is unused if \p enabled is set to + * This parameter is unused if \p enable is set to * MBEDTLS_SSL_CID_DISABLED. * \param own_cid_len The length of \p own_cid. - * This parameter is unused if \p enabled is set to + * This parameter is unused if \p enable is set to * MBEDTLS_SSL_CID_DISABLED. * * \note The value of \p own_cid_len must match the value of the @@ -2573,8 +2574,8 @@ int mbedtls_ssl_session_load(mbedtls_ssl_session *session, * * \param session The session structure to be saved. * \param buf The buffer to write the serialized data to. It must be a - * writeable buffer of at least \p len bytes, or may be \c - * NULL if \p len is \c 0. + * writeable buffer of at least \p buf_len bytes, or may be \c + * NULL if \p buf_len is \c 0. * \param buf_len The number of bytes available for writing in \p buf. * \param olen The size in bytes of the data that has been or would have * been written. It must point to a valid \c size_t. @@ -2659,7 +2660,7 @@ void mbedtls_ssl_conf_ciphersuites(mbedtls_ssl_config *conf, * record headers. * * \return \c 0 on success. - * \return #MBEDTLS_ERR_SSL_BAD_INPUT_DATA if \p own_cid_len + * \return #MBEDTLS_ERR_SSL_BAD_INPUT_DATA if \p len * is too large. */ int mbedtls_ssl_conf_cid(mbedtls_ssl_config *conf, size_t len, diff --git a/thirdparty/mbedtls/include/mbedtls/version.h b/thirdparty/mbedtls/include/mbedtls/version.h index 0ef52138fd..1ae06e6868 100644 --- a/thirdparty/mbedtls/include/mbedtls/version.h +++ b/thirdparty/mbedtls/include/mbedtls/version.h @@ -38,16 +38,16 @@ */ #define MBEDTLS_VERSION_MAJOR 2 #define MBEDTLS_VERSION_MINOR 28 -#define MBEDTLS_VERSION_PATCH 3 +#define MBEDTLS_VERSION_PATCH 4 /** * The single version number has the following structure: * MMNNPP00 * Major version | Minor version | Patch version */ -#define MBEDTLS_VERSION_NUMBER 0x021C0300 -#define MBEDTLS_VERSION_STRING "2.28.3" -#define MBEDTLS_VERSION_STRING_FULL "mbed TLS 2.28.3" +#define MBEDTLS_VERSION_NUMBER 0x021C0400 +#define MBEDTLS_VERSION_STRING "2.28.4" +#define MBEDTLS_VERSION_STRING_FULL "mbed TLS 2.28.4" #if defined(MBEDTLS_VERSION_C) diff --git a/thirdparty/mbedtls/include/mbedtls/x509_crt.h b/thirdparty/mbedtls/include/mbedtls/x509_crt.h index 466611f797..d436635a5e 100644 --- a/thirdparty/mbedtls/include/mbedtls/x509_crt.h +++ b/thirdparty/mbedtls/include/mbedtls/x509_crt.h @@ -509,7 +509,7 @@ int mbedtls_x509_crt_parse_path(mbedtls_x509_crt *chain, const char *path); * \param san_buf The buffer holding the raw data item of the subject * alternative name. * \param san The target structure to populate with the parsed presentation - * of the subject alternative name encoded in \p san_raw. + * of the subject alternative name encoded in \p san_buf. * * \note Only "dnsName" and "otherName" of type hardware_module_name * as defined in RFC 4180 is supported. @@ -517,7 +517,7 @@ int mbedtls_x509_crt_parse_path(mbedtls_x509_crt *chain, const char *path); * \note This function should be called on a single raw data of * subject alternative name. For example, after successful * certificate parsing, one must iterate on every item in the - * \p crt->subject_alt_names sequence, and pass it to + * \c crt->subject_alt_names sequence, and pass it to * this function. * * \warning The target structure contains pointers to the raw data of the diff --git a/thirdparty/mbedtls/library/aes.c b/thirdparty/mbedtls/library/aes.c index f08a21f595..c506709500 100644 --- a/thirdparty/mbedtls/library/aes.c +++ b/thirdparty/mbedtls/library/aes.c @@ -58,6 +58,8 @@ static int aes_padlock_ace = -1; /* * Forward S-box */ +#if !defined(MBEDTLS_AES_ENCRYPT_ALT) || !defined(MBEDTLS_AES_SETKEY_ENC_ALT) || \ + !defined(MBEDTLS_AES_SETKEY_DEC_ALT) static const unsigned char FSb[256] = { 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, @@ -93,6 +95,8 @@ static const unsigned char FSb[256] = 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 }; +#endif /* !defined(MBEDTLS_AES_ENCRYPT_ALT) || !defined(MBEDTLS_AES_SETKEY_ENC_ALT) || \ + !defined(MBEDTLS_AES_SETKEY_DEC_ALT) */ /* * Forward tables @@ -164,6 +168,7 @@ static const unsigned char FSb[256] = V(C3, 41, 41, 82), V(B0, 99, 99, 29), V(77, 2D, 2D, 5A), V(11, 0F, 0F, 1E), \ V(CB, B0, B0, 7B), V(FC, 54, 54, A8), V(D6, BB, BB, 6D), V(3A, 16, 16, 2C) +#if !defined(MBEDTLS_AES_ENCRYPT_ALT) #define V(a, b, c, d) 0x##a##b##c##d static const uint32_t FT0[256] = { FT }; #undef V @@ -184,8 +189,11 @@ static const uint32_t FT3[256] = { FT }; #endif /* !MBEDTLS_AES_FEWER_TABLES */ +#endif /* !defined(MBEDTLS_AES_ENCRYPT_ALT) */ + #undef FT +#if !defined(MBEDTLS_AES_DECRYPT_ALT) /* * Reverse S-box */ @@ -224,6 +232,7 @@ static const unsigned char RSb[256] = 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D }; +#endif /* defined(MBEDTLS_AES_DECRYPT_ALT)) */ /* * Reverse tables @@ -295,6 +304,8 @@ static const unsigned char RSb[256] = V(71, 01, A8, 39), V(DE, B3, 0C, 08), V(9C, E4, B4, D8), V(90, C1, 56, 64), \ V(61, 84, CB, 7B), V(70, B6, 32, D5), V(74, 5C, 6C, 48), V(42, 57, B8, D0) +#if !defined(MBEDTLS_AES_DECRYPT_ALT) || !defined(MBEDTLS_AES_SETKEY_DEC_ALT) + #define V(a, b, c, d) 0x##a##b##c##d static const uint32_t RT0[256] = { RT }; #undef V @@ -315,8 +326,11 @@ static const uint32_t RT3[256] = { RT }; #endif /* !MBEDTLS_AES_FEWER_TABLES */ +#endif /* !defined(MBEDTLS_AES_DECRYPT_ALT) || !defined(MBEDTLS_AES_SETKEY_DEC_ALT) */ + #undef RT +#if !defined(MBEDTLS_AES_SETKEY_ENC_ALT) /* * Round constants */ @@ -326,31 +340,44 @@ static const uint32_t RCON[10] = 0x00000010, 0x00000020, 0x00000040, 0x00000080, 0x0000001B, 0x00000036 }; +#endif /* !defined(MBEDTLS_AES_SETKEY_ENC_ALT) */ #else /* MBEDTLS_AES_ROM_TABLES */ /* * Forward S-box & tables */ +#if !defined(MBEDTLS_AES_ENCRYPT_ALT) || !defined(MBEDTLS_AES_SETKEY_ENC_ALT) || \ + !defined(MBEDTLS_AES_SETKEY_DEC_ALT) static unsigned char FSb[256]; +#endif /* !defined(MBEDTLS_AES_ENCRYPT_ALT) || !defined(MBEDTLS_AES_SETKEY_ENC_ALT) || \ + !defined(MBEDTLS_AES_SETKEY_DEC_ALT) */ +#if !defined(MBEDTLS_AES_ENCRYPT_ALT) || !defined(MBEDTLS_AES_SETKEY_ENC_ALT) static uint32_t FT0[256]; #if !defined(MBEDTLS_AES_FEWER_TABLES) static uint32_t FT1[256]; static uint32_t FT2[256]; static uint32_t FT3[256]; #endif /* !MBEDTLS_AES_FEWER_TABLES */ +#endif /* !defined(MBEDTLS_AES_ENCRYPT_ALT) || !defined(MBEDTLS_AES_SETKEY_ENC_ALT) */ /* * Reverse S-box & tables */ +#if !(defined(MBEDTLS_AES_SETKEY_ENC_ALT) && defined(MBEDTLS_AES_DECRYPT_ALT)) static unsigned char RSb[256]; +#endif /* !(defined(MBEDTLS_AES_SETKEY_ENC_ALT) && defined(MBEDTLS_AES_DECRYPT_ALT)) */ + +#if !defined(MBEDTLS_AES_DECRYPT_ALT) || !defined(MBEDTLS_AES_SETKEY_DEC_ALT) static uint32_t RT0[256]; #if !defined(MBEDTLS_AES_FEWER_TABLES) static uint32_t RT1[256]; static uint32_t RT2[256]; static uint32_t RT3[256]; #endif /* !MBEDTLS_AES_FEWER_TABLES */ +#endif /* !defined(MBEDTLS_AES_DECRYPT_ALT) || !defined(MBEDTLS_AES_SETKEY_DEC_ALT) */ +#if !defined(MBEDTLS_AES_SETKEY_ENC_ALT) /* * Round constants */ @@ -428,6 +455,7 @@ static void aes_gen_tables(void) x = RSb[i]; +#if !defined(MBEDTLS_AES_DECRYPT_ALT) || !defined(MBEDTLS_AES_SETKEY_DEC_ALT) RT0[i] = ((uint32_t) MUL(0x0E, x)) ^ ((uint32_t) MUL(0x09, x) << 8) ^ ((uint32_t) MUL(0x0D, x) << 16) ^ @@ -438,9 +466,12 @@ static void aes_gen_tables(void) RT2[i] = ROTL8(RT1[i]); RT3[i] = ROTL8(RT2[i]); #endif /* !MBEDTLS_AES_FEWER_TABLES */ +#endif /* !defined(MBEDTLS_AES_DECRYPT_ALT) || !defined(MBEDTLS_AES_SETKEY_DEC_ALT) */ } } +#endif /* !defined(MBEDTLS_AES_SETKEY_ENC_ALT) */ + #undef ROTL8 #endif /* MBEDTLS_AES_ROM_TABLES */ @@ -521,6 +552,9 @@ void mbedtls_aes_xts_free(mbedtls_aes_xts_context *ctx) (defined(MBEDTLS_AESNI_C) && MBEDTLS_AESNI_HAVE_CODE == 2) #define MAY_NEED_TO_ALIGN #endif + +#if defined(MAY_NEED_TO_ALIGN) || !defined(MBEDTLS_AES_SETKEY_DEC_ALT) || \ + !defined(MBEDTLS_AES_SETKEY_ENC_ALT) static unsigned mbedtls_aes_rk_offset(uint32_t *buf) { #if defined(MAY_NEED_TO_ALIGN) @@ -557,6 +591,8 @@ static unsigned mbedtls_aes_rk_offset(uint32_t *buf) return 0; } +#endif /* defined(MAY_NEED_TO_ALIGN) || !defined(MBEDTLS_AES_SETKEY_DEC_ALT) || \ + !defined(MBEDTLS_AES_SETKEY_ENC_ALT) */ /* * AES key schedule (encryption) diff --git a/thirdparty/mbedtls/library/aesni.c b/thirdparty/mbedtls/library/aesni.c index c909f654c6..866b6cbfbf 100644 --- a/thirdparty/mbedtls/library/aesni.c +++ b/thirdparty/mbedtls/library/aesni.c @@ -41,6 +41,8 @@ #if MBEDTLS_AESNI_HAVE_CODE == 2 #if !defined(_WIN32) #include <cpuid.h> +#else +#include <intrin.h> #endif #include <immintrin.h> #endif diff --git a/thirdparty/mbedtls/library/certs.c b/thirdparty/mbedtls/library/certs.c index af1f98cb02..5b2948d652 100644 --- a/thirdparty/mbedtls/library/certs.c +++ b/thirdparty/mbedtls/library/certs.c @@ -38,69 +38,69 @@ /* This is taken from tests/data_files/test-ca2.crt */ /* BEGIN FILE string macro TEST_CA_CRT_EC_PEM tests/data_files/test-ca2.crt */ -#define TEST_CA_CRT_EC_PEM \ - "-----BEGIN CERTIFICATE-----\r\n" \ - "MIICBDCCAYigAwIBAgIJAMFD4n5iQ8zoMAwGCCqGSM49BAMCBQAwPjELMAkGA1UE\r\n" \ - "BhMCTkwxETAPBgNVBAoMCFBvbGFyU1NMMRwwGgYDVQQDDBNQb2xhcnNzbCBUZXN0\r\n" \ - "IEVDIENBMB4XDTE5MDIxMDE0NDQwMFoXDTI5MDIxMDE0NDQwMFowPjELMAkGA1UE\r\n" \ - "BhMCTkwxETAPBgNVBAoMCFBvbGFyU1NMMRwwGgYDVQQDDBNQb2xhcnNzbCBUZXN0\r\n" \ - "IEVDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEw9orNEE3WC+HVv78ibopQ0tO\r\n" \ - "4G7DDldTMzlY1FK0kZU5CyPfXxckYkj8GpUpziwth8KIUoCv1mqrId240xxuWLjK\r\n" \ - "6LJpjvNBrSnDtF91p0dv1RkpVWmaUzsgtGYWYDMeo1AwTjAMBgNVHRMEBTADAQH/\r\n" \ - "MB0GA1UdDgQWBBSdbSAkSQE/K8t4tRm8fiTJ2/s2fDAfBgNVHSMEGDAWgBSdbSAk\r\n" \ - "SQE/K8t4tRm8fiTJ2/s2fDAMBggqhkjOPQQDAgUAA2gAMGUCMFHKrjAPpHB0BN1a\r\n" \ - "LH8TwcJ3vh0AxeKZj30mRdOKBmg/jLS3rU3g8VQBHpn8sOTTBwIxANxPO5AerimZ\r\n" \ - "hCjMe0d4CTHf1gFZMF70+IqEP+o5VHsIp2Cqvflb0VGWFC5l9a4cQg==\r\n" \ +#define TEST_CA_CRT_EC_PEM \ + "-----BEGIN CERTIFICATE-----\r\n" \ + "MIICBDCCAYigAwIBAgIJAMFD4n5iQ8zoMAwGCCqGSM49BAMCBQAwPjELMAkGA1UE\r\n" \ + "BhMCTkwxETAPBgNVBAoMCFBvbGFyU1NMMRwwGgYDVQQDDBNQb2xhcnNzbCBUZXN0\r\n" \ + "IEVDIENBMB4XDTE5MDIxMDE0NDQwMFoXDTI5MDIxMDE0NDQwMFowPjELMAkGA1UE\r\n" \ + "BhMCTkwxETAPBgNVBAoMCFBvbGFyU1NMMRwwGgYDVQQDDBNQb2xhcnNzbCBUZXN0\r\n" \ + "IEVDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEw9orNEE3WC+HVv78ibopQ0tO\r\n" \ + "4G7DDldTMzlY1FK0kZU5CyPfXxckYkj8GpUpziwth8KIUoCv1mqrId240xxuWLjK\r\n" \ + "6LJpjvNBrSnDtF91p0dv1RkpVWmaUzsgtGYWYDMeo1AwTjAMBgNVHRMEBTADAQH/\r\n" \ + "MB0GA1UdDgQWBBSdbSAkSQE/K8t4tRm8fiTJ2/s2fDAfBgNVHSMEGDAWgBSdbSAk\r\n" \ + "SQE/K8t4tRm8fiTJ2/s2fDAMBggqhkjOPQQDAgUAA2gAMGUCMFHKrjAPpHB0BN1a\r\n" \ + "LH8TwcJ3vh0AxeKZj30mRdOKBmg/jLS3rU3g8VQBHpn8sOTTBwIxANxPO5AerimZ\r\n" \ + "hCjMe0d4CTHf1gFZMF70+IqEP+o5VHsIp2Cqvflb0VGWFC5l9a4cQg==\r\n" \ "-----END CERTIFICATE-----\r\n" /* END FILE */ /* This is generated from tests/data_files/test-ca2.crt.der using `xxd -i`. */ /* BEGIN FILE binary macro TEST_CA_CRT_EC_DER tests/data_files/test-ca2.crt.der */ -#define TEST_CA_CRT_EC_DER { \ - 0x30, 0x82, 0x02, 0x04, 0x30, 0x82, 0x01, 0x88, 0xa0, 0x03, 0x02, 0x01, \ - 0x02, 0x02, 0x09, 0x00, 0xc1, 0x43, 0xe2, 0x7e, 0x62, 0x43, 0xcc, 0xe8, \ - 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, \ - 0x05, 0x00, 0x30, 0x3e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, \ - 0x06, 0x13, 0x02, 0x4e, 0x4c, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, \ - 0x04, 0x0a, 0x0c, 0x08, 0x50, 0x6f, 0x6c, 0x61, 0x72, 0x53, 0x53, 0x4c, \ - 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x13, 0x50, \ - 0x6f, 0x6c, 0x61, 0x72, 0x73, 0x73, 0x6c, 0x20, 0x54, 0x65, 0x73, 0x74, \ - 0x20, 0x45, 0x43, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x39, \ - 0x30, 0x32, 0x31, 0x30, 0x31, 0x34, 0x34, 0x34, 0x30, 0x30, 0x5a, 0x17, \ - 0x0d, 0x32, 0x39, 0x30, 0x32, 0x31, 0x30, 0x31, 0x34, 0x34, 0x34, 0x30, \ - 0x30, 0x5a, 0x30, 0x3e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, \ - 0x06, 0x13, 0x02, 0x4e, 0x4c, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, \ - 0x04, 0x0a, 0x0c, 0x08, 0x50, 0x6f, 0x6c, 0x61, 0x72, 0x53, 0x53, 0x4c, \ - 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x13, 0x50, \ - 0x6f, 0x6c, 0x61, 0x72, 0x73, 0x73, 0x6c, 0x20, 0x54, 0x65, 0x73, 0x74, \ - 0x20, 0x45, 0x43, 0x20, 0x43, 0x41, 0x30, 0x76, 0x30, 0x10, 0x06, 0x07, \ - 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, \ - 0x00, 0x22, 0x03, 0x62, 0x00, 0x04, 0xc3, 0xda, 0x2b, 0x34, 0x41, 0x37, \ - 0x58, 0x2f, 0x87, 0x56, 0xfe, 0xfc, 0x89, 0xba, 0x29, 0x43, 0x4b, 0x4e, \ - 0xe0, 0x6e, 0xc3, 0x0e, 0x57, 0x53, 0x33, 0x39, 0x58, 0xd4, 0x52, 0xb4, \ - 0x91, 0x95, 0x39, 0x0b, 0x23, 0xdf, 0x5f, 0x17, 0x24, 0x62, 0x48, 0xfc, \ - 0x1a, 0x95, 0x29, 0xce, 0x2c, 0x2d, 0x87, 0xc2, 0x88, 0x52, 0x80, 0xaf, \ - 0xd6, 0x6a, 0xab, 0x21, 0xdd, 0xb8, 0xd3, 0x1c, 0x6e, 0x58, 0xb8, 0xca, \ - 0xe8, 0xb2, 0x69, 0x8e, 0xf3, 0x41, 0xad, 0x29, 0xc3, 0xb4, 0x5f, 0x75, \ - 0xa7, 0x47, 0x6f, 0xd5, 0x19, 0x29, 0x55, 0x69, 0x9a, 0x53, 0x3b, 0x20, \ - 0xb4, 0x66, 0x16, 0x60, 0x33, 0x1e, 0xa3, 0x50, 0x30, 0x4e, 0x30, 0x0c, \ - 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, \ - 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x9d, \ - 0x6d, 0x20, 0x24, 0x49, 0x01, 0x3f, 0x2b, 0xcb, 0x78, 0xb5, 0x19, 0xbc, \ - 0x7e, 0x24, 0xc9, 0xdb, 0xfb, 0x36, 0x7c, 0x30, 0x1f, 0x06, 0x03, 0x55, \ - 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x9d, 0x6d, 0x20, 0x24, \ - 0x49, 0x01, 0x3f, 0x2b, 0xcb, 0x78, 0xb5, 0x19, 0xbc, 0x7e, 0x24, 0xc9, \ - 0xdb, 0xfb, 0x36, 0x7c, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, \ - 0x3d, 0x04, 0x03, 0x02, 0x05, 0x00, 0x03, 0x68, 0x00, 0x30, 0x65, 0x02, \ - 0x30, 0x51, 0xca, 0xae, 0x30, 0x0f, 0xa4, 0x70, 0x74, 0x04, 0xdd, 0x5a, \ - 0x2c, 0x7f, 0x13, 0xc1, 0xc2, 0x77, 0xbe, 0x1d, 0x00, 0xc5, 0xe2, 0x99, \ - 0x8f, 0x7d, 0x26, 0x45, 0xd3, 0x8a, 0x06, 0x68, 0x3f, 0x8c, 0xb4, 0xb7, \ - 0xad, 0x4d, 0xe0, 0xf1, 0x54, 0x01, 0x1e, 0x99, 0xfc, 0xb0, 0xe4, 0xd3, \ - 0x07, 0x02, 0x31, 0x00, 0xdc, 0x4f, 0x3b, 0x90, 0x1e, 0xae, 0x29, 0x99, \ - 0x84, 0x28, 0xcc, 0x7b, 0x47, 0x78, 0x09, 0x31, 0xdf, 0xd6, 0x01, 0x59, \ - 0x30, 0x5e, 0xf4, 0xf8, 0x8a, 0x84, 0x3f, 0xea, 0x39, 0x54, 0x7b, 0x08, \ - 0xa7, 0x60, 0xaa, 0xbd, 0xf9, 0x5b, 0xd1, 0x51, 0x96, 0x14, 0x2e, 0x65, \ - 0xf5, 0xae, 0x1c, 0x42 \ +#define TEST_CA_CRT_EC_DER { \ + 0x30, 0x82, 0x02, 0x04, 0x30, 0x82, 0x01, 0x88, 0xa0, 0x03, 0x02, 0x01, \ + 0x02, 0x02, 0x09, 0x00, 0xc1, 0x43, 0xe2, 0x7e, 0x62, 0x43, 0xcc, 0xe8, \ + 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, \ + 0x05, 0x00, 0x30, 0x3e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, \ + 0x06, 0x13, 0x02, 0x4e, 0x4c, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, \ + 0x04, 0x0a, 0x0c, 0x08, 0x50, 0x6f, 0x6c, 0x61, 0x72, 0x53, 0x53, 0x4c, \ + 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x13, 0x50, \ + 0x6f, 0x6c, 0x61, 0x72, 0x73, 0x73, 0x6c, 0x20, 0x54, 0x65, 0x73, 0x74, \ + 0x20, 0x45, 0x43, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x39, \ + 0x30, 0x32, 0x31, 0x30, 0x31, 0x34, 0x34, 0x34, 0x30, 0x30, 0x5a, 0x17, \ + 0x0d, 0x32, 0x39, 0x30, 0x32, 0x31, 0x30, 0x31, 0x34, 0x34, 0x34, 0x30, \ + 0x30, 0x5a, 0x30, 0x3e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, \ + 0x06, 0x13, 0x02, 0x4e, 0x4c, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, \ + 0x04, 0x0a, 0x0c, 0x08, 0x50, 0x6f, 0x6c, 0x61, 0x72, 0x53, 0x53, 0x4c, \ + 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x13, 0x50, \ + 0x6f, 0x6c, 0x61, 0x72, 0x73, 0x73, 0x6c, 0x20, 0x54, 0x65, 0x73, 0x74, \ + 0x20, 0x45, 0x43, 0x20, 0x43, 0x41, 0x30, 0x76, 0x30, 0x10, 0x06, 0x07, \ + 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, \ + 0x00, 0x22, 0x03, 0x62, 0x00, 0x04, 0xc3, 0xda, 0x2b, 0x34, 0x41, 0x37, \ + 0x58, 0x2f, 0x87, 0x56, 0xfe, 0xfc, 0x89, 0xba, 0x29, 0x43, 0x4b, 0x4e, \ + 0xe0, 0x6e, 0xc3, 0x0e, 0x57, 0x53, 0x33, 0x39, 0x58, 0xd4, 0x52, 0xb4, \ + 0x91, 0x95, 0x39, 0x0b, 0x23, 0xdf, 0x5f, 0x17, 0x24, 0x62, 0x48, 0xfc, \ + 0x1a, 0x95, 0x29, 0xce, 0x2c, 0x2d, 0x87, 0xc2, 0x88, 0x52, 0x80, 0xaf, \ + 0xd6, 0x6a, 0xab, 0x21, 0xdd, 0xb8, 0xd3, 0x1c, 0x6e, 0x58, 0xb8, 0xca, \ + 0xe8, 0xb2, 0x69, 0x8e, 0xf3, 0x41, 0xad, 0x29, 0xc3, 0xb4, 0x5f, 0x75, \ + 0xa7, 0x47, 0x6f, 0xd5, 0x19, 0x29, 0x55, 0x69, 0x9a, 0x53, 0x3b, 0x20, \ + 0xb4, 0x66, 0x16, 0x60, 0x33, 0x1e, 0xa3, 0x50, 0x30, 0x4e, 0x30, 0x0c, \ + 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, \ + 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x9d, \ + 0x6d, 0x20, 0x24, 0x49, 0x01, 0x3f, 0x2b, 0xcb, 0x78, 0xb5, 0x19, 0xbc, \ + 0x7e, 0x24, 0xc9, 0xdb, 0xfb, 0x36, 0x7c, 0x30, 0x1f, 0x06, 0x03, 0x55, \ + 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x9d, 0x6d, 0x20, 0x24, \ + 0x49, 0x01, 0x3f, 0x2b, 0xcb, 0x78, 0xb5, 0x19, 0xbc, 0x7e, 0x24, 0xc9, \ + 0xdb, 0xfb, 0x36, 0x7c, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, \ + 0x3d, 0x04, 0x03, 0x02, 0x05, 0x00, 0x03, 0x68, 0x00, 0x30, 0x65, 0x02, \ + 0x30, 0x51, 0xca, 0xae, 0x30, 0x0f, 0xa4, 0x70, 0x74, 0x04, 0xdd, 0x5a, \ + 0x2c, 0x7f, 0x13, 0xc1, 0xc2, 0x77, 0xbe, 0x1d, 0x00, 0xc5, 0xe2, 0x99, \ + 0x8f, 0x7d, 0x26, 0x45, 0xd3, 0x8a, 0x06, 0x68, 0x3f, 0x8c, 0xb4, 0xb7, \ + 0xad, 0x4d, 0xe0, 0xf1, 0x54, 0x01, 0x1e, 0x99, 0xfc, 0xb0, 0xe4, 0xd3, \ + 0x07, 0x02, 0x31, 0x00, 0xdc, 0x4f, 0x3b, 0x90, 0x1e, 0xae, 0x29, 0x99, \ + 0x84, 0x28, 0xcc, 0x7b, 0x47, 0x78, 0x09, 0x31, 0xdf, 0xd6, 0x01, 0x59, \ + 0x30, 0x5e, 0xf4, 0xf8, 0x8a, 0x84, 0x3f, 0xea, 0x39, 0x54, 0x7b, 0x08, \ + 0xa7, 0x60, 0xaa, 0xbd, 0xf9, 0x5b, 0xd1, 0x51, 0x96, 0x14, 0x2e, 0x65, \ + 0xf5, 0xae, 0x1c, 0x42 \ } /* END FILE */ @@ -348,33 +348,33 @@ #define TEST_CA_KEY_RSA_PEM \ "-----BEGIN RSA PRIVATE KEY-----\r\n" \ "Proc-Type: 4,ENCRYPTED\r\n" \ - "DEK-Info: DES-EDE3-CBC,A8A95B05D5B7206B\r\n" \ + "AES-128-CBC,781840E6B804AE83D2AF71127C4CE314\r\n" \ "\r\n" \ - "9Qd9GeArejl1GDVh2lLV1bHt0cPtfbh5h/5zVpAVaFpqtSPMrElp50Rntn9et+JA\r\n" \ - "7VOyboR+Iy2t/HU4WvA687k3Bppe9GwKHjHhtl//8xFKwZr3Xb5yO5JUP8AUctQq\r\n" \ - "Nb8CLlZyuUC+52REAAthdWgsX+7dJO4yabzUcQ22Tp9JSD0hiL43BlkWYUNK3dAo\r\n" \ - "PZlmiptjnzVTjg1MxsBSydZinWOLBV8/JQgxSPo2yD4uEfig28qbvQ2wNIn0pnAb\r\n" \ - "GxnSAOazkongEGfvcjIIs+LZN9gXFhxcOh6kc4Q/c99B7QWETwLLkYgZ+z1a9VY9\r\n" \ - "gEU7CwCxYCD+h9hY6FPmsK0/lC4O7aeRKpYq00rPPxs6i7phiexg6ax6yTMmArQq\r\n" \ - "QmK3TAsJm8V/J5AWpLEV6jAFgRGymGGHnof0DXzVWZidrcZJWTNuGEX90nB3ee2w\r\n" \ - "PXJEFWKoD3K3aFcSLdHYr3mLGxP7H9ThQai9VsycxZKS5kwvBKQ//YMrmFfwPk8x\r\n" \ - "vTeY4KZMaUrveEel5tWZC94RSMKgxR6cyE1nBXyTQnDOGbfpNNgBKxyKbINWoOJU\r\n" \ - "WJZAwlsQn+QzCDwpri7+sV1mS3gBE6UY7aQmnmiiaC2V3Hbphxct/en5QsfDOt1X\r\n" \ - "JczSfpRWLlbPznZg8OQh/VgCMA58N5DjOzTIK7sJJ5r+94ZBTCpgAMbF588f0NTR\r\n" \ - "KCe4yrxGJR7X02M4nvD4IwOlpsQ8xQxZtOSgXv4LkxvdU9XJJKWZ/XNKJeWztxSe\r\n" \ - "Z1vdTc2YfsDBA2SEv33vxHx2g1vqtw8SjDRT2RaQSS0QuSaMJimdOX6mTOCBKk1J\r\n" \ - "9Q5mXTrER+/LnK0jEmXsBXWA5bqqVZIyahXSx4VYZ7l7w/PHiUDtDgyRhMMKi4n2\r\n" \ - "iQvQcWSQTjrpnlJbca1/DkpRt3YwrvJwdqb8asZU2VrNETh5x0QVefDRLFiVpif/\r\n" \ - "tUaeAe/P1F8OkS7OIZDs1SUbv/sD2vMbhNkUoCms3/PvNtdnvgL4F0zhaDpKCmlT\r\n" \ - "P8vx49E7v5CyRNmED9zZg4o3wmMqrQO93PtTug3Eu9oVx1zPQM1NVMyBa2+f29DL\r\n" \ - "1nuTCeXdo9+ni45xx+jAI4DCwrRdhJ9uzZyC6962H37H6D+5naNvClFR1s6li1Gb\r\n" \ - "nqPoiy/OBsEx9CaDGcqQBp5Wme/3XW+6z1ISOx+igwNTVCT14mHdBMbya0eIKft5\r\n" \ - "X+GnwtgEMyCYyyWuUct8g4RzErcY9+yW9Om5Hzpx4zOuW4NPZgPDTgK+t2RSL/Yq\r\n" \ - "rE1njrgeGYcVeG3f+OftH4s6fPbq7t1A5ZgUscbLMBqr9tK+OqygR4EgKBPsH6Cz\r\n" \ - "L6zlv/2RV0qAHvVuDJcIDIgwY5rJtINEm32rhOeFNJwZS5MNIC1czXZx5//ugX7l\r\n" \ - "I4sy5nbVhwSjtAk8Xg5dZbdTZ6mIrb7xqH+fdakZor1khG7bC2uIwibD3cSl2XkR\r\n" \ - "wN48lslbHnqqagr6Xm1nNOSVl8C/6kbJEsMpLhAezfRtGwvOucoaE+WbeUNolGde\r\n" \ - "P/eQiddSf0brnpiLJRh7qZrl9XuqYdpUqnoEdMAfotDOID8OtV7gt8a48ad8VPW2\r\n" \ + "etQ3xgGLbuYF9vR1km03TH5fwfly1hOlix0PtfQ+t9HG065vTtSEHYc/OyHwdy79\r\n" \ + "NCLX5RUrPh06E/XlKzMNVHAXqkwFnIwNzRLsOozeP1L7iZEZb9QMeiN5Org+btCO\r\n" \ + "bylXPB4YirfuE7GSJalWY/pq3FQtD33zTIKmNhXfVj3sbwGI/8D9XjaKUb8PODOB\r\n" \ + "skOalmx6RvYRvg0lmRxB3+T3wejIsrrDPweYqte9B6dVHIVG1ZmvoA6/wnKZZZeV\r\n" \ + "sjj8OpL3OwUBrjuGSknE9Rs6kCuSCbHOYVK8VzcZmCYpie0TFnb3Sk8M6vjfW+45\r\n" \ + "U7WUMlSAPxKH6lJDzWdwHqLvsVJwuNnaAaBXg9/8U/rzQEWuq8Ar3s8fw2Jg3F1G\r\n" \ + "L6N5ZAEfCz3Sa0N9WKafR/RSQj+rq8Z3w4POAafhbzk249uo5K8B1Z3cQwLxeXIl\r\n" \ + "UbRQz1TZy4oNTfQzCahYruPNyvwgTkfwAFFvbLAdaiJd2ZtLBoqYE64TYakYnvcC\r\n" \ + "itim1bmySIKoxlMfBGFmMuF03epT0pSx701jlGzGi0l0m16NEjoVxDwo5j93SmiM\r\n" \ + "sQdjC1lOGk2iCLkphIQqHFjFJYWjvh1UUIqWZf+ZWOOxlf4x9a1pUVj6FvtECxNB\r\n" \ + "/mA/m4Iq4LAuVXHE1MpHeq067lJ6wWlrsb2WVmiNGfQ2AC7fMtpcPuunBVT9NV1m\r\n" \ + "1rbDzIgLIWAzqz/cy3N8Q8vfxnrFtmNUyM191Zyq+YF14hIKWX9J1qR4LXwWAzVV\r\n" \ + "UrC8IL4pA2mtRkW4qFsB0EmHAxO/cedDTPjVFty5WSzhNuvYZxX45HAkGIfK6d21\r\n" \ + "7WHPhHG+zaaUTWMUVixB0IcKp6RecjYPFzBHS0YeX88Ue2cyT/90jMiQ9ssOgRrG\r\n" \ + "ZJRJvZAc3TSCnY9sNPYoGrJPiZuCnlUj3ENNurYVy12ai0WFxwnNUZjRUhDS6hjm\r\n" \ + "cDHD5TlI9MZ6M+Mb/Bw4Ig8HuTHOtQBYD9vhtXsG+B7H/j6cS+1umaKjrnG/kK4W\r\n" \ + "R6YXwM2faAi+DwgjjoMXSzRqSTF8PdTIWbAXo3bc2qsXPTMBA8PEp4nb5scHZ4Ts\r\n" \ + "EcBNp2jv0j4gBkRmGIab17cWMrlagjFy89DhqZUFwKdeZs+yJ92A5xstWxOUfpEP\r\n" \ + "90T/bsp1G5d7WW5fl2TRJvYJNDM+djkKIh0zCkduiZ36oVM6nDdbjmXqjQXopeSD\r\n" \ + "gtOourBRF8g99W0fW8QT+yPhP0Pkyz6EG8eQO6Zwh439xdoVwu9jUzQAPmZ0uNeR\r\n" \ + "xTXXihYyv72z27rInjLiIPXL25K9eDVLlcSR3RyG7YYgjdQAL2VJDLcBz5jox1uQ\r\n" \ + "0guoD5wmfu2FWLqYE7HeTYntdY53lCflwq0GHRMjrrsVpx+5VDQ6Yi47Ny9SWLcp\r\n" \ + "fPI3iBkXuGRWupzs6N4pQdSO0dU28KfpMM5QvFoLIn67brCHEQij4dgFrCTYEyBX\r\n" \ + "9+jiNImUFYUhAFuxvUbfZt4O/ABLIElvHLfJs1oYCmI/nWpvLFqXB5rnzPNfEi0H\r\n" \ + "PGGe1Hj/t+CJIp/6ios3yNy2QtXO754TZH2UVu51Ykyig5PFjZVoUkbRvHQYcWfU\r\n" \ "-----END RSA PRIVATE KEY-----\r\n" /* END FILE */ @@ -501,72 +501,72 @@ /* This is taken from tests/data_files/server5.crt. */ /* BEGIN FILE string macro TEST_SRV_CRT_EC_PEM tests/data_files/server5.crt */ -#define TEST_SRV_CRT_EC_PEM \ - "-----BEGIN CERTIFICATE-----\r\n" \ - "MIICHzCCAaWgAwIBAgIBCTAKBggqhkjOPQQDAjA+MQswCQYDVQQGEwJOTDERMA8G\r\n" \ - "A1UEChMIUG9sYXJTU0wxHDAaBgNVBAMTE1BvbGFyc3NsIFRlc3QgRUMgQ0EwHhcN\r\n" \ - "MTMwOTI0MTU1MjA0WhcNMjMwOTIyMTU1MjA0WjA0MQswCQYDVQQGEwJOTDERMA8G\r\n" \ - "A1UEChMIUG9sYXJTU0wxEjAQBgNVBAMTCWxvY2FsaG9zdDBZMBMGByqGSM49AgEG\r\n" \ - "CCqGSM49AwEHA0IABDfMVtl2CR5acj7HWS3/IG7ufPkGkXTQrRS192giWWKSTuUA\r\n" \ - "2CMR/+ov0jRdXRa9iojCa3cNVc2KKg76Aci07f+jgZ0wgZowCQYDVR0TBAIwADAd\r\n" \ - "BgNVHQ4EFgQUUGGlj9QH2deCAQzlZX+MY0anE74wbgYDVR0jBGcwZYAUnW0gJEkB\r\n" \ - "PyvLeLUZvH4kydv7NnyhQqRAMD4xCzAJBgNVBAYTAk5MMREwDwYDVQQKEwhQb2xh\r\n" \ - "clNTTDEcMBoGA1UEAxMTUG9sYXJzc2wgVGVzdCBFQyBDQYIJAMFD4n5iQ8zoMAoG\r\n" \ - "CCqGSM49BAMCA2gAMGUCMQCaLFzXptui5WQN8LlO3ddh1hMxx6tzgLvT03MTVK2S\r\n" \ - "C12r0Lz3ri/moSEpNZWqPjkCMCE2f53GXcYLqyfyJR078c/xNSUU5+Xxl7VZ414V\r\n" \ - "fGa5kHvHARBPc8YAIVIqDvHH1Q==\r\n" \ +#define TEST_SRV_CRT_EC_PEM \ + "-----BEGIN CERTIFICATE-----\r\n" \ + "MIICHzCCAaWgAwIBAgIBCTAKBggqhkjOPQQDAjA+MQswCQYDVQQGEwJOTDERMA8G\r\n" \ + "A1UECgwIUG9sYXJTU0wxHDAaBgNVBAMME1BvbGFyc3NsIFRlc3QgRUMgQ0EwHhcN\r\n" \ + "MjMwNjE1MDMzNDE4WhcNMzMwNjEyMDMzNDE4WjA0MQswCQYDVQQGEwJOTDERMA8G\r\n" \ + "A1UECgwIUG9sYXJTU0wxEjAQBgNVBAMMCWxvY2FsaG9zdDBZMBMGByqGSM49AgEG\r\n" \ + "CCqGSM49AwEHA0IABDfMVtl2CR5acj7HWS3/IG7ufPkGkXTQrRS192giWWKSTuUA\r\n" \ + "2CMR/+ov0jRdXRa9iojCa3cNVc2KKg76Aci07f+jgZ0wgZowCQYDVR0TBAIwADAd\r\n" \ + "BgNVHQ4EFgQUUGGlj9QH2deCAQzlZX+MY0anE74wbgYDVR0jBGcwZYAUnW0gJEkB\r\n" \ + "PyvLeLUZvH4kydv7NnyhQqRAMD4xCzAJBgNVBAYTAk5MMREwDwYDVQQKDAhQb2xh\r\n" \ + "clNTTDEcMBoGA1UEAwwTUG9sYXJzc2wgVGVzdCBFQyBDQYIJAMFD4n5iQ8zoMAoG\r\n" \ + "CCqGSM49BAMCA2gAMGUCMAHFbGEzx8dZaUlIltT5s1QO9FvKmvFer4uRY3ntEy9S\r\n" \ + "k7DCCozM86WWLjfzbJ78bwIxAJYRPF1CzNEiXPHb9O46ZPHKo2S5x//g/54RowAK\r\n" \ + "uZz+hKPuMi6YY6cIm81jfeaSZQ==\r\n" \ "-----END CERTIFICATE-----\r\n" /* END FILE */ /* This is generated from tests/data_files/server5.crt.der using `xxd -i`. */ /* BEGIN FILE binary macro TEST_SRV_CRT_EC_DER tests/data_files/server5.crt.der */ -#define TEST_SRV_CRT_EC_DER { \ - 0x30, 0x82, 0x02, 0x1f, 0x30, 0x82, 0x01, 0xa5, 0xa0, 0x03, 0x02, 0x01, \ - 0x02, 0x02, 0x01, 0x09, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, \ - 0x3d, 0x04, 0x03, 0x02, 0x30, 0x3e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, \ - 0x55, 0x04, 0x06, 0x13, 0x02, 0x4e, 0x4c, 0x31, 0x11, 0x30, 0x0f, 0x06, \ - 0x03, 0x55, 0x04, 0x0a, 0x13, 0x08, 0x50, 0x6f, 0x6c, 0x61, 0x72, 0x53, \ - 0x53, 0x4c, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, \ - 0x13, 0x50, 0x6f, 0x6c, 0x61, 0x72, 0x73, 0x73, 0x6c, 0x20, 0x54, 0x65, \ - 0x73, 0x74, 0x20, 0x45, 0x43, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, \ - 0x31, 0x33, 0x30, 0x39, 0x32, 0x34, 0x31, 0x35, 0x35, 0x32, 0x30, 0x34, \ - 0x5a, 0x17, 0x0d, 0x32, 0x33, 0x30, 0x39, 0x32, 0x32, 0x31, 0x35, 0x35, \ - 0x32, 0x30, 0x34, 0x5a, 0x30, 0x34, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, \ - 0x55, 0x04, 0x06, 0x13, 0x02, 0x4e, 0x4c, 0x31, 0x11, 0x30, 0x0f, 0x06, \ - 0x03, 0x55, 0x04, 0x0a, 0x13, 0x08, 0x50, 0x6f, 0x6c, 0x61, 0x72, 0x53, \ - 0x53, 0x4c, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, \ - 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x30, 0x59, \ - 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, \ - 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, \ - 0x04, 0x37, 0xcc, 0x56, 0xd9, 0x76, 0x09, 0x1e, 0x5a, 0x72, 0x3e, 0xc7, \ - 0x59, 0x2d, 0xff, 0x20, 0x6e, 0xee, 0x7c, 0xf9, 0x06, 0x91, 0x74, 0xd0, \ - 0xad, 0x14, 0xb5, 0xf7, 0x68, 0x22, 0x59, 0x62, 0x92, 0x4e, 0xe5, 0x00, \ - 0xd8, 0x23, 0x11, 0xff, 0xea, 0x2f, 0xd2, 0x34, 0x5d, 0x5d, 0x16, 0xbd, \ - 0x8a, 0x88, 0xc2, 0x6b, 0x77, 0x0d, 0x55, 0xcd, 0x8a, 0x2a, 0x0e, 0xfa, \ - 0x01, 0xc8, 0xb4, 0xed, 0xff, 0xa3, 0x81, 0x9d, 0x30, 0x81, 0x9a, 0x30, \ - 0x09, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x1d, \ - 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x50, 0x61, 0xa5, \ - 0x8f, 0xd4, 0x07, 0xd9, 0xd7, 0x82, 0x01, 0x0c, 0xe5, 0x65, 0x7f, 0x8c, \ - 0x63, 0x46, 0xa7, 0x13, 0xbe, 0x30, 0x6e, 0x06, 0x03, 0x55, 0x1d, 0x23, \ - 0x04, 0x67, 0x30, 0x65, 0x80, 0x14, 0x9d, 0x6d, 0x20, 0x24, 0x49, 0x01, \ - 0x3f, 0x2b, 0xcb, 0x78, 0xb5, 0x19, 0xbc, 0x7e, 0x24, 0xc9, 0xdb, 0xfb, \ - 0x36, 0x7c, 0xa1, 0x42, 0xa4, 0x40, 0x30, 0x3e, 0x31, 0x0b, 0x30, 0x09, \ - 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x4e, 0x4c, 0x31, 0x11, 0x30, \ - 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x08, 0x50, 0x6f, 0x6c, 0x61, \ - 0x72, 0x53, 0x53, 0x4c, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, \ - 0x03, 0x13, 0x13, 0x50, 0x6f, 0x6c, 0x61, 0x72, 0x73, 0x73, 0x6c, 0x20, \ - 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x43, 0x20, 0x43, 0x41, 0x82, 0x09, \ - 0x00, 0xc1, 0x43, 0xe2, 0x7e, 0x62, 0x43, 0xcc, 0xe8, 0x30, 0x0a, 0x06, \ - 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x68, 0x00, \ - 0x30, 0x65, 0x02, 0x31, 0x00, 0x9a, 0x2c, 0x5c, 0xd7, 0xa6, 0xdb, 0xa2, \ - 0xe5, 0x64, 0x0d, 0xf0, 0xb9, 0x4e, 0xdd, 0xd7, 0x61, 0xd6, 0x13, 0x31, \ - 0xc7, 0xab, 0x73, 0x80, 0xbb, 0xd3, 0xd3, 0x73, 0x13, 0x54, 0xad, 0x92, \ - 0x0b, 0x5d, 0xab, 0xd0, 0xbc, 0xf7, 0xae, 0x2f, 0xe6, 0xa1, 0x21, 0x29, \ - 0x35, 0x95, 0xaa, 0x3e, 0x39, 0x02, 0x30, 0x21, 0x36, 0x7f, 0x9d, 0xc6, \ - 0x5d, 0xc6, 0x0b, 0xab, 0x27, 0xf2, 0x25, 0x1d, 0x3b, 0xf1, 0xcf, 0xf1, \ - 0x35, 0x25, 0x14, 0xe7, 0xe5, 0xf1, 0x97, 0xb5, 0x59, 0xe3, 0x5e, 0x15, \ - 0x7c, 0x66, 0xb9, 0x90, 0x7b, 0xc7, 0x01, 0x10, 0x4f, 0x73, 0xc6, 0x00, \ - 0x21, 0x52, 0x2a, 0x0e, 0xf1, 0xc7, 0xd5 \ +#define TEST_SRV_CRT_EC_DER { \ + 0x30, 0x82, 0x02, 0x1f, 0x30, 0x82, 0x01, 0xa5, 0xa0, 0x03, 0x02, 0x01, \ + 0x02, 0x02, 0x01, 0x09, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, \ + 0x3d, 0x04, 0x03, 0x02, 0x30, 0x3e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, \ + 0x55, 0x04, 0x06, 0x13, 0x02, 0x4e, 0x4c, 0x31, 0x11, 0x30, 0x0f, 0x06, \ + 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x08, 0x50, 0x6f, 0x6c, 0x61, 0x72, 0x53, \ + 0x53, 0x4c, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, \ + 0x13, 0x50, 0x6f, 0x6c, 0x61, 0x72, 0x73, 0x73, 0x6c, 0x20, 0x54, 0x65, \ + 0x73, 0x74, 0x20, 0x45, 0x43, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, \ + 0x32, 0x33, 0x30, 0x36, 0x31, 0x35, 0x30, 0x33, 0x33, 0x34, 0x31, 0x38, \ + 0x5a, 0x17, 0x0d, 0x33, 0x33, 0x30, 0x36, 0x31, 0x32, 0x30, 0x33, 0x33, \ + 0x34, 0x31, 0x38, 0x5a, 0x30, 0x34, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, \ + 0x55, 0x04, 0x06, 0x13, 0x02, 0x4e, 0x4c, 0x31, 0x11, 0x30, 0x0f, 0x06, \ + 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x08, 0x50, 0x6f, 0x6c, 0x61, 0x72, 0x53, \ + 0x53, 0x4c, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, \ + 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x30, 0x59, \ + 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, \ + 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, \ + 0x04, 0x37, 0xcc, 0x56, 0xd9, 0x76, 0x09, 0x1e, 0x5a, 0x72, 0x3e, 0xc7, \ + 0x59, 0x2d, 0xff, 0x20, 0x6e, 0xee, 0x7c, 0xf9, 0x06, 0x91, 0x74, 0xd0, \ + 0xad, 0x14, 0xb5, 0xf7, 0x68, 0x22, 0x59, 0x62, 0x92, 0x4e, 0xe5, 0x00, \ + 0xd8, 0x23, 0x11, 0xff, 0xea, 0x2f, 0xd2, 0x34, 0x5d, 0x5d, 0x16, 0xbd, \ + 0x8a, 0x88, 0xc2, 0x6b, 0x77, 0x0d, 0x55, 0xcd, 0x8a, 0x2a, 0x0e, 0xfa, \ + 0x01, 0xc8, 0xb4, 0xed, 0xff, 0xa3, 0x81, 0x9d, 0x30, 0x81, 0x9a, 0x30, \ + 0x09, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x1d, \ + 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x50, 0x61, 0xa5, \ + 0x8f, 0xd4, 0x07, 0xd9, 0xd7, 0x82, 0x01, 0x0c, 0xe5, 0x65, 0x7f, 0x8c, \ + 0x63, 0x46, 0xa7, 0x13, 0xbe, 0x30, 0x6e, 0x06, 0x03, 0x55, 0x1d, 0x23, \ + 0x04, 0x67, 0x30, 0x65, 0x80, 0x14, 0x9d, 0x6d, 0x20, 0x24, 0x49, 0x01, \ + 0x3f, 0x2b, 0xcb, 0x78, 0xb5, 0x19, 0xbc, 0x7e, 0x24, 0xc9, 0xdb, 0xfb, \ + 0x36, 0x7c, 0xa1, 0x42, 0xa4, 0x40, 0x30, 0x3e, 0x31, 0x0b, 0x30, 0x09, \ + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x4e, 0x4c, 0x31, 0x11, 0x30, \ + 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x08, 0x50, 0x6f, 0x6c, 0x61, \ + 0x72, 0x53, 0x53, 0x4c, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, \ + 0x03, 0x0c, 0x13, 0x50, 0x6f, 0x6c, 0x61, 0x72, 0x73, 0x73, 0x6c, 0x20, \ + 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x43, 0x20, 0x43, 0x41, 0x82, 0x09, \ + 0x00, 0xc1, 0x43, 0xe2, 0x7e, 0x62, 0x43, 0xcc, 0xe8, 0x30, 0x0a, 0x06, \ + 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x68, 0x00, \ + 0x30, 0x65, 0x02, 0x30, 0x01, 0xc5, 0x6c, 0x61, 0x33, 0xc7, 0xc7, 0x59, \ + 0x69, 0x49, 0x48, 0x96, 0xd4, 0xf9, 0xb3, 0x54, 0x0e, 0xf4, 0x5b, 0xca, \ + 0x9a, 0xf1, 0x5e, 0xaf, 0x8b, 0x91, 0x63, 0x79, 0xed, 0x13, 0x2f, 0x52, \ + 0x93, 0xb0, 0xc2, 0x0a, 0x8c, 0xcc, 0xf3, 0xa5, 0x96, 0x2e, 0x37, 0xf3, \ + 0x6c, 0x9e, 0xfc, 0x6f, 0x02, 0x31, 0x00, 0x96, 0x11, 0x3c, 0x5d, 0x42, \ + 0xcc, 0xd1, 0x22, 0x5c, 0xf1, 0xdb, 0xf4, 0xee, 0x3a, 0x64, 0xf1, 0xca, \ + 0xa3, 0x64, 0xb9, 0xc7, 0xff, 0xe0, 0xff, 0x9e, 0x11, 0xa3, 0x00, 0x0a, \ + 0xb9, 0x9c, 0xfe, 0x84, 0xa3, 0xee, 0x32, 0x2e, 0x98, 0x63, 0xa7, 0x08, \ + 0x9b, 0xcd, 0x63, 0x7d, 0xe6, 0x92, 0x65 \ } /* END FILE */ diff --git a/thirdparty/mbedtls/library/constant_time.c b/thirdparty/mbedtls/library/constant_time.c index 527930129b..c0f53bbe77 100644 --- a/thirdparty/mbedtls/library/constant_time.c +++ b/thirdparty/mbedtls/library/constant_time.c @@ -263,40 +263,6 @@ unsigned mbedtls_ct_uint_if(unsigned condition, #if defined(MBEDTLS_BIGNUM_C) -/** Select between two sign values without branches. - * - * This is functionally equivalent to `condition ? if1 : if0` but uses only bit - * operations in order to avoid branches. - * - * \note if1 and if0 must be either 1 or -1, otherwise the result - * is undefined. - * - * \param condition Condition to test; must be either 0 or 1. - * \param if1 The first sign; must be either +1 or -1. - * \param if0 The second sign; must be either +1 or -1. - * - * \return \c if1 if \p condition is nonzero, otherwise \c if0. - * */ -static int mbedtls_ct_cond_select_sign(unsigned char condition, - int if1, - int if0) -{ - /* In order to avoid questions about what we can reasonably assume about - * the representations of signed integers, move everything to unsigned - * by taking advantage of the fact that if1 and if0 are either +1 or -1. */ - unsigned uif1 = if1 + 1; - unsigned uif0 = if0 + 1; - - /* condition was 0 or 1, mask is 0 or 2 as are uif1 and uif0 */ - const unsigned mask = condition << 1; - - /* select uif1 or uif0 */ - unsigned ur = (uif0 & ~mask) | (uif1 & mask); - - /* ur is now 0 or 2, convert back to -1 or +1 */ - return (int) ur - 1; -} - void mbedtls_ct_mpi_uint_cond_assign(size_t n, mbedtls_mpi_uint *dest, const mbedtls_mpi_uint *src, @@ -559,7 +525,7 @@ int mbedtls_mpi_safe_cond_assign(mbedtls_mpi *X, MBEDTLS_MPI_CHK(mbedtls_mpi_grow(X, Y->n)); - X->s = mbedtls_ct_cond_select_sign(assign, Y->s, X->s); + X->s = (int) mbedtls_ct_uint_if(assign, Y->s, X->s); mbedtls_ct_mpi_uint_cond_assign(Y->n, X->p, Y->p, assign); @@ -599,8 +565,8 @@ int mbedtls_mpi_safe_cond_swap(mbedtls_mpi *X, MBEDTLS_MPI_CHK(mbedtls_mpi_grow(Y, X->n)); s = X->s; - X->s = mbedtls_ct_cond_select_sign(swap, Y->s, X->s); - Y->s = mbedtls_ct_cond_select_sign(swap, s, Y->s); + X->s = (int) mbedtls_ct_uint_if(swap, Y->s, X->s); + Y->s = (int) mbedtls_ct_uint_if(swap, s, Y->s); for (i = 0; i < X->n; i++) { diff --git a/thirdparty/mbedtls/library/ctr_drbg.c b/thirdparty/mbedtls/library/ctr_drbg.c index 652c5cbc28..45d925437e 100644 --- a/thirdparty/mbedtls/library/ctr_drbg.c +++ b/thirdparty/mbedtls/library/ctr_drbg.c @@ -30,6 +30,7 @@ #include "mbedtls/platform_util.h" #include "mbedtls/error.h" +#include <limits.h> #include <string.h> #if defined(MBEDTLS_FS_IO) diff --git a/thirdparty/mbedtls/library/debug.c b/thirdparty/mbedtls/library/debug.c index ab8b3524d4..3e794b5565 100644 --- a/thirdparty/mbedtls/library/debug.c +++ b/thirdparty/mbedtls/library/debug.c @@ -30,6 +30,7 @@ #include <stdio.h> #include <string.h> +/* DEBUG_BUF_SIZE must be at least 2 */ #define DEBUG_BUF_SIZE 512 static int debug_threshold = 0; @@ -69,6 +70,8 @@ void mbedtls_debug_print_msg(const mbedtls_ssl_context *ssl, int level, char str[DEBUG_BUF_SIZE]; int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + MBEDTLS_STATIC_ASSERT(DEBUG_BUF_SIZE >= 2, "DEBUG_BUF_SIZE too small"); + if (NULL == ssl || NULL == ssl->conf || NULL == ssl->conf->f_dbg || @@ -80,10 +83,15 @@ void mbedtls_debug_print_msg(const mbedtls_ssl_context *ssl, int level, ret = mbedtls_vsnprintf(str, DEBUG_BUF_SIZE, format, argp); va_end(argp); - if (ret >= 0 && ret < DEBUG_BUF_SIZE - 1) { - str[ret] = '\n'; - str[ret + 1] = '\0'; + if (ret < 0) { + ret = 0; + } else { + if (ret >= DEBUG_BUF_SIZE - 1) { + ret = DEBUG_BUF_SIZE - 2; + } } + str[ret] = '\n'; + str[ret + 1] = '\0'; debug_send_line(ssl, level, file, line, str); } diff --git a/thirdparty/mbedtls/library/ecdh.c b/thirdparty/mbedtls/library/ecdh.c index 2007e1670c..9f002d9682 100644 --- a/thirdparty/mbedtls/library/ecdh.c +++ b/thirdparty/mbedtls/library/ecdh.c @@ -20,7 +20,7 @@ /* * References: * - * SEC1 http://www.secg.org/index.php?action=secg,docs_secg + * SEC1 https://www.secg.org/sec1-v2.pdf * RFC 4492 */ diff --git a/thirdparty/mbedtls/library/ecdsa.c b/thirdparty/mbedtls/library/ecdsa.c index 42a65dcb59..1f0b37dfd4 100644 --- a/thirdparty/mbedtls/library/ecdsa.c +++ b/thirdparty/mbedtls/library/ecdsa.c @@ -20,7 +20,7 @@ /* * References: * - * SEC1 http://www.secg.org/index.php?action=secg,docs_secg + * SEC1 https://www.secg.org/sec1-v2.pdf */ #include "common.h" diff --git a/thirdparty/mbedtls/library/ecp.c b/thirdparty/mbedtls/library/ecp.c index 9051490b12..5b60291cc8 100644 --- a/thirdparty/mbedtls/library/ecp.c +++ b/thirdparty/mbedtls/library/ecp.c @@ -20,13 +20,15 @@ /* * References: * - * SEC1 http://www.secg.org/index.php?action=secg,docs_secg + * SEC1 https://www.secg.org/sec1-v2.pdf * GECC = Guide to Elliptic Curve Cryptography - Hankerson, Menezes, Vanstone * FIPS 186-3 http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf * RFC 4492 for the related TLS structures and constants + * - https://www.rfc-editor.org/rfc/rfc4492 * RFC 7748 for the Curve448 and Curve25519 curve definitions + * - https://www.rfc-editor.org/rfc/rfc7748 * - * [Curve25519] http://cr.yp.to/ecdh/curve25519-20060209.pdf + * [Curve25519] https://cr.yp.to/ecdh/curve25519-20060209.pdf * * [2] CORON, Jean-S'ebastien. Resistance against differential power analysis * for elliptic curve cryptosystems. In : Cryptographic Hardware and @@ -2591,6 +2593,7 @@ static int ecp_mul_mxz(mbedtls_ecp_group *grp, mbedtls_ecp_point *R, void *p_rng) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + int have_rng = 1; size_t i; unsigned char b; mbedtls_ecp_point RP; @@ -2623,9 +2626,8 @@ static int ecp_mul_mxz(mbedtls_ecp_group *grp, mbedtls_ecp_point *R, /* RP.X might be slightly larger than P, so reduce it */ MOD_ADD(RP.X); - /* Randomize coordinates of the starting point */ - int have_rng = 1; #if defined(MBEDTLS_ECP_NO_INTERNAL_RNG) + /* Derandomize coordinates of the starting point */ if (f_rng == NULL) { have_rng = 0; } diff --git a/thirdparty/mbedtls/library/ecp_invasive.h b/thirdparty/mbedtls/library/ecp_invasive.h index 18815be089..d6f6f9565e 100644 --- a/thirdparty/mbedtls/library/ecp_invasive.h +++ b/thirdparty/mbedtls/library/ecp_invasive.h @@ -61,7 +61,7 @@ void mbedtls_ecp_fix_negative(mbedtls_mpi *N, signed char c, size_t bits); * This is the bit-size of the key minus 1: * 254 for Curve25519 or 447 for Curve448. * \param d The randomly generated key. This is a number of size - * exactly \p n_bits + 1 bits, with the least significant bits + * exactly \p high_bit + 1 bits, with the least significant bits * masked as specified in [Curve25519] and in [RFC7748] §5. * \param f_rng The RNG function. * \param p_rng The RNG context to be passed to \p f_rng. @@ -69,7 +69,7 @@ void mbedtls_ecp_fix_negative(mbedtls_mpi *N, signed char c, size_t bits); * \return \c 0 on success. * \return \c MBEDTLS_ERR_ECP_xxx or MBEDTLS_ERR_MPI_xxx on failure. */ -int mbedtls_ecp_gen_privkey_mx(size_t n_bits, +int mbedtls_ecp_gen_privkey_mx(size_t high_bit, mbedtls_mpi *d, int (*f_rng)(void *, unsigned char *, size_t), void *p_rng); diff --git a/thirdparty/mbedtls/library/entropy.c b/thirdparty/mbedtls/library/entropy.c index af78acc1bf..e9a7ae63d3 100644 --- a/thirdparty/mbedtls/library/entropy.c +++ b/thirdparty/mbedtls/library/entropy.c @@ -31,6 +31,8 @@ #include "mbedtls/entropy_poll.h" #include "mbedtls/platform_util.h" #include "mbedtls/error.h" +#include "mbedtls/sha256.h" +#include "mbedtls/sha512.h" #include <string.h> diff --git a/thirdparty/mbedtls/library/net_sockets.c b/thirdparty/mbedtls/library/net_sockets.c index bdd82ac6fe..2c2a876b02 100644 --- a/thirdparty/mbedtls/library/net_sockets.c +++ b/thirdparty/mbedtls/library/net_sockets.c @@ -90,6 +90,7 @@ static int wsa_init_done = 0; #include <errno.h> #define IS_EINTR(ret) ((ret) == EINTR) +#define SOCKET int #endif /* ( _WIN32 || _WIN32_WCE ) && !EFIX64 && !EFI32 */ @@ -494,13 +495,13 @@ int mbedtls_net_poll(mbedtls_net_context *ctx, uint32_t rw, uint32_t timeout) FD_ZERO(&read_fds); if (rw & MBEDTLS_NET_POLL_READ) { rw &= ~MBEDTLS_NET_POLL_READ; - FD_SET(fd, &read_fds); + FD_SET((SOCKET) fd, &read_fds); } FD_ZERO(&write_fds); if (rw & MBEDTLS_NET_POLL_WRITE) { rw &= ~MBEDTLS_NET_POLL_WRITE; - FD_SET(fd, &write_fds); + FD_SET((SOCKET) fd, &write_fds); } if (rw != 0) { @@ -608,7 +609,7 @@ int mbedtls_net_recv_timeout(void *ctx, unsigned char *buf, } FD_ZERO(&read_fds); - FD_SET(fd, &read_fds); + FD_SET((SOCKET) fd, &read_fds); tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; diff --git a/thirdparty/mbedtls/library/pk.c b/thirdparty/mbedtls/library/pk.c index d46a93461a..12f4120225 100644 --- a/thirdparty/mbedtls/library/pk.c +++ b/thirdparty/mbedtls/library/pk.c @@ -646,6 +646,7 @@ int mbedtls_pk_wrap_as_opaque(mbedtls_pk_context *pk, psa_key_type_t key_type; size_t bits; int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + psa_status_t status; /* export the private key material in the format PSA wants */ if (mbedtls_pk_get_type(pk) != MBEDTLS_PK_ECKEY) { @@ -668,7 +669,9 @@ int mbedtls_pk_wrap_as_opaque(mbedtls_pk_context *pk, psa_set_key_algorithm(&attributes, PSA_ALG_ECDSA(hash_alg)); /* import private key into PSA */ - if (PSA_SUCCESS != psa_import_key(&attributes, d, d_len, key)) { + status = psa_import_key(&attributes, d, d_len, key); + mbedtls_platform_zeroize(d, sizeof(d)); + if (status != PSA_SUCCESS) { return MBEDTLS_ERR_PK_HW_ACCEL_FAILED; } diff --git a/thirdparty/mbedtls/library/pkparse.c b/thirdparty/mbedtls/library/pkparse.c index deaff0b310..76fe0c81e4 100644 --- a/thirdparty/mbedtls/library/pkparse.c +++ b/thirdparty/mbedtls/library/pkparse.c @@ -1235,6 +1235,8 @@ int mbedtls_pk_parse_key(mbedtls_pk_context *pk, mbedtls_pem_context pem; #endif + (void) pk_info; + PK_VALIDATE_RET(pk != NULL); if (keylen == 0) { return MBEDTLS_ERR_PK_KEY_INVALID_FORMAT; diff --git a/thirdparty/mbedtls/library/pkwrite.c b/thirdparty/mbedtls/library/pkwrite.c index 0107f20b1d..88e685503b 100644 --- a/thirdparty/mbedtls/library/pkwrite.c +++ b/thirdparty/mbedtls/library/pkwrite.c @@ -178,6 +178,11 @@ int mbedtls_pk_write_pubkey(unsigned char **p, unsigned char *start, int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; size_t len = 0; + (void) p; + (void) start; + (void) key; + (void) ret; + PK_VALIDATE_RET(p != NULL); PK_VALIDATE_RET(*p != NULL); PK_VALIDATE_RET(start != NULL); @@ -313,6 +318,10 @@ int mbedtls_pk_write_key_der(mbedtls_pk_context *key, unsigned char *buf, size_t unsigned char *c; size_t len = 0; + (void) ret; + (void) c; + (void) key; + PK_VALIDATE_RET(key != NULL); if (size == 0) { return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL; diff --git a/thirdparty/mbedtls/library/timing.c b/thirdparty/mbedtls/library/timing.c index eeb1d503a8..58c2c2730d 100644 --- a/thirdparty/mbedtls/library/timing.c +++ b/thirdparty/mbedtls/library/timing.c @@ -17,6 +17,8 @@ * limitations under the License. */ +#include <string.h> + #include "common.h" #include "mbedtls/platform.h" @@ -233,17 +235,20 @@ volatile int mbedtls_timing_alarmed = 0; unsigned long mbedtls_timing_get_timer(struct mbedtls_timing_hr_time *val, int reset) { - struct _hr_time *t = (struct _hr_time *) val; + struct _hr_time t; if (reset) { - QueryPerformanceCounter(&t->start); + QueryPerformanceCounter(&t.start); + memcpy(val, &t, sizeof(struct _hr_time)); return 0; } else { unsigned long delta; LARGE_INTEGER now, hfreq; + /* We can't safely cast val because it may not be aligned, so use memcpy */ + memcpy(&t, val, sizeof(struct _hr_time)); QueryPerformanceCounter(&now); QueryPerformanceFrequency(&hfreq); - delta = (unsigned long) ((now.QuadPart - t->start.QuadPart) * 1000ul + delta = (unsigned long) ((now.QuadPart - t.start.QuadPart) * 1000ul / hfreq.QuadPart); return delta; } @@ -279,17 +284,20 @@ void mbedtls_set_alarm(int seconds) unsigned long mbedtls_timing_get_timer(struct mbedtls_timing_hr_time *val, int reset) { - struct _hr_time *t = (struct _hr_time *) val; + struct _hr_time t; if (reset) { - gettimeofday(&t->start, NULL); + gettimeofday(&t.start, NULL); + memcpy(val, &t, sizeof(struct _hr_time)); return 0; } else { unsigned long delta; struct timeval now; + /* We can't safely cast val because it may not be aligned, so use memcpy */ + memcpy(&t, val, sizeof(struct _hr_time)); gettimeofday(&now, NULL); - delta = (now.tv_sec - t->start.tv_sec) * 1000ul - + (now.tv_usec - t->start.tv_usec) / 1000; + delta = (now.tv_sec - t.start.tv_sec) * 1000ul + + (now.tv_usec - t.start.tv_usec) / 1000; return delta; } } diff --git a/thirdparty/mbedtls/library/x509.c b/thirdparty/mbedtls/library/x509.c index 38eb2e6607..d61ef4a279 100644 --- a/thirdparty/mbedtls/library/x509.c +++ b/thirdparty/mbedtls/library/x509.c @@ -53,13 +53,17 @@ #include <time.h> #endif -#define CHECK(code) if ((ret = (code)) != 0) { return ret; } +#define CHECK(code) \ + do { \ + if ((ret = (code)) != 0) { \ + return ret; \ + } \ + } while (0) + #define CHECK_RANGE(min, max, val) \ - do \ - { \ - if ((val) < (min) || (val) > (max)) \ - { \ - return ret; \ + do { \ + if ((val) < (min) || (val) > (max)) { \ + return ret; \ } \ } while (0) diff --git a/thirdparty/mbedtls/library/x509_create.c b/thirdparty/mbedtls/library/x509_create.c index 50db95688f..cdfc82aa5d 100644 --- a/thirdparty/mbedtls/library/x509_create.c +++ b/thirdparty/mbedtls/library/x509_create.c @@ -125,7 +125,7 @@ static const x509_attr_descriptor_t *x509_attr_descr_from_name(const char *name, int mbedtls_x509_string_to_names(mbedtls_asn1_named_data **head, const char *name) { - int ret = 0; + int ret = MBEDTLS_ERR_X509_INVALID_NAME; const char *s = name, *c = s; const char *end = s + strlen(s); const char *oid = NULL; @@ -177,6 +177,9 @@ int mbedtls_x509_string_to_names(mbedtls_asn1_named_data **head, const char *nam s = c + 1; in_tag = 1; + + /* Successfully parsed one name, update ret to success */ + ret = 0; } if (!in_tag && s != c + 1) { diff --git a/thirdparty/mbedtls/library/x509_crt.c b/thirdparty/mbedtls/library/x509_crt.c index e7fcaf4627..5b51694740 100644 --- a/thirdparty/mbedtls/library/x509_crt.c +++ b/thirdparty/mbedtls/library/x509_crt.c @@ -1944,16 +1944,19 @@ int mbedtls_x509_parse_subject_alt_name(const mbedtls_x509_buf *san_buf, return 0; } -#define PRINT_ITEM(i) \ - { \ - ret = mbedtls_snprintf(p, n, "%s" i, sep); \ - MBEDTLS_X509_SAFE_SNPRINTF; \ - sep = ", "; \ - } - -#define CERT_TYPE(type, name) \ - if (ns_cert_type & (type)) \ - PRINT_ITEM(name); +#define PRINT_ITEM(i) \ + do { \ + ret = mbedtls_snprintf(p, n, "%s" i, sep); \ + MBEDTLS_X509_SAFE_SNPRINTF; \ + sep = ", "; \ + } while (0) + +#define CERT_TYPE(type, name) \ + do { \ + if (ns_cert_type & (type)) { \ + PRINT_ITEM(name); \ + } \ + } while (0) static int x509_info_cert_type(char **buf, size_t *size, unsigned char ns_cert_type) @@ -1978,9 +1981,12 @@ static int x509_info_cert_type(char **buf, size_t *size, return 0; } -#define KEY_USAGE(code, name) \ - if (key_usage & (code)) \ - PRINT_ITEM(name); +#define KEY_USAGE(code, name) \ + do { \ + if (key_usage & (code)) { \ + PRINT_ITEM(name); \ + } \ + } while (0) static int x509_info_key_usage(char **buf, size_t *size, unsigned int key_usage) diff --git a/thirdparty/mbedtls/patches/windows-arm64-hardclock.diff b/thirdparty/mbedtls/patches/windows-arm64-hardclock.diff index 9a0c430216..721509453a 100644 --- a/thirdparty/mbedtls/patches/windows-arm64-hardclock.diff +++ b/thirdparty/mbedtls/patches/windows-arm64-hardclock.diff @@ -1,8 +1,8 @@ diff --git a/thirdparty/mbedtls/library/timing.c b/thirdparty/mbedtls/library/timing.c -index 47e34f9227..eeb1d503a8 100644 +index 94b55b3715..58c2c2730d 100644 --- a/thirdparty/mbedtls/library/timing.c +++ b/thirdparty/mbedtls/library/timing.c -@@ -188,8 +188,10 @@ unsigned long mbedtls_timing_hardclock(void) +@@ -190,8 +190,10 @@ unsigned long mbedtls_timing_hardclock(void) #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && __GNUC__ && __ia64__ */ diff --git a/thirdparty/miniupnpc/LICENSE b/thirdparty/miniupnpc/LICENSE index 67ff3bb627..6eff8d2683 100644 --- a/thirdparty/miniupnpc/LICENSE +++ b/thirdparty/miniupnpc/LICENSE @@ -1,6 +1,6 @@ BSD 3-Clause License -Copyright (c) 2005-2022, Thomas BERNARD +Copyright (c) 2005-2023, Thomas BERNARD All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/thirdparty/miniupnpc/include/miniupnpc.h b/thirdparty/miniupnpc/include/miniupnpc.h index 75fb8b702d..721819583f 100644 --- a/thirdparty/miniupnpc/include/miniupnpc.h +++ b/thirdparty/miniupnpc/include/miniupnpc.h @@ -1,4 +1,4 @@ -/* $Id: miniupnpc.h,v 1.61 2022/10/21 21:15:02 nanard Exp $ */ +/* $Id: miniupnpc.h,v 1.62 2023/06/11 23:25:46 nanard Exp $ */ /* vim: tabstop=4 shiftwidth=4 noexpandtab * Project: miniupnp * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ @@ -20,7 +20,7 @@ #define UPNPDISCOVER_MEMORY_ERROR (-102) /* versions : */ -#define MINIUPNPC_VERSION "2.2.4" +#define MINIUPNPC_VERSION "2.2.5" #define MINIUPNPC_API_VERSION 17 /* Source port: diff --git a/thirdparty/miniupnpc/src/miniupnpcstrings.h b/thirdparty/miniupnpc/src/miniupnpcstrings.h index 25abc1016e..d89d9e443b 100644 --- a/thirdparty/miniupnpc/src/miniupnpcstrings.h +++ b/thirdparty/miniupnpc/src/miniupnpcstrings.h @@ -4,7 +4,7 @@ #include "core/version.h" #define OS_STRING VERSION_NAME "/1.0" -#define MINIUPNPC_VERSION_STRING "2.2.4" +#define MINIUPNPC_VERSION_STRING "2.2.5" #if 0 /* according to "UPnP Device Architecture 1.0" */ diff --git a/thirdparty/tinyexr/tinyexr.h b/thirdparty/tinyexr/tinyexr.h index d08e97f6cd..e5ac54a506 100644 --- a/thirdparty/tinyexr/tinyexr.h +++ b/thirdparty/tinyexr/tinyexr.h @@ -114,6 +114,11 @@ extern "C" { #define TINYEXR_USE_STB_ZLIB (0) #endif +// Use nanozlib. +#ifndef TINYEXR_USE_NANOZLIB +#define TINYEXR_USE_NANOZLIB (0) +#endif + // Disable PIZ compression when applying cpplint. #ifndef TINYEXR_USE_PIZ #define TINYEXR_USE_PIZ (1) @@ -608,7 +613,10 @@ extern int LoadEXRFromMemory(float **out_rgba, int *width, int *height, #define NOMINMAX #endif #include <windows.h> // for UTF-8 and memory-mapping + +#if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP) #define TINYEXR_USE_WIN32_MMAP (1) +#endif #elif defined(__linux__) || defined(__unix__) #include <fcntl.h> // for open() @@ -650,7 +658,7 @@ extern int LoadEXRFromMemory(float **out_rgba, int *width, int *height, #include <omp.h> #endif -#if TINYEXR_USE_MINIZ +#if defined(TINYEXR_USE_MINIZ) && (TINYEXR_USE_MINIZ==1) #include <miniz.h> #else // Issue #46. Please include your own zlib-compatible API header before @@ -658,6 +666,11 @@ extern int LoadEXRFromMemory(float **out_rgba, int *width, int *height, //#include "zlib.h" #endif +#if defined(TINYEXR_USE_NANOZLIB) && (TINYEXR_USE_NANOZLIB==1) +#define NANOZLIB_IMPLEMENTATION +#include "nanozlib.h" +#endif + #if TINYEXR_USE_STB_ZLIB // Since we don't know where a project has stb_image.h and stb_image_write.h // and whether they are in the include path, we don't include them here, and @@ -668,6 +681,7 @@ extern "C" int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuf extern "C" unsigned char *stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality); #endif + #if TINYEXR_USE_ZFP #ifdef __clang__ @@ -1343,7 +1357,7 @@ static bool CompressZip(unsigned char *dst, } } -#if TINYEXR_USE_MINIZ +#if defined(TINYEXR_USE_MINIZ) && (TINYEXR_USE_MINIZ==1) // // Compress the data using miniz // @@ -1357,7 +1371,7 @@ static bool CompressZip(unsigned char *dst, } compressedSize = outSize; -#elif TINYEXR_USE_STB_ZLIB +#elif defined(TINYEXR_USE_STB_ZLIB) && (TINYEXR_USE_STB_ZLIB==1) int outSize; unsigned char* ret = stbi_zlib_compress(const_cast<unsigned char*>(&tmpBuf.at(0)), src_size, &outSize, 8); if (!ret) { @@ -1367,6 +1381,18 @@ static bool CompressZip(unsigned char *dst, free(ret); compressedSize = outSize; +#elif defined(TINYEXR_USE_NANOZLIB) && (TINYEXR_USE_NANOZLIB==1) + uint64_t dstSize = nanoz_compressBound(static_cast<uint64_t>(src_size)); + int outSize{0}; + unsigned char *ret = nanoz_compress(&tmpBuf.at(0), src_size, &outSize, /* quality */8); + if (!ret) { + return false; + } + + memcpy(dst, ret, outSize); + free(ret); + + compressedSize = outSize; #else uLong outSize = compressBound(static_cast<uLong>(src_size)); int ret = compress(dst, &outSize, static_cast<const Bytef *>(&tmpBuf.at(0)), @@ -1398,7 +1424,7 @@ static bool DecompressZip(unsigned char *dst, } std::vector<unsigned char> tmpBuf(*uncompressed_size); -#if TINYEXR_USE_MINIZ +#if defined(TINYEXR_USE_MINIZ) && (TINYEXR_USE_MINIZ==1) int ret = mz_uncompress(&tmpBuf.at(0), uncompressed_size, src, src_size); if (MZ_OK != ret) { @@ -1410,6 +1436,17 @@ static bool DecompressZip(unsigned char *dst, if (ret < 0) { return false; } +#elif defined(TINYEXR_USE_NANOZLIB) && (TINYEXR_USE_NANOZLIB==1) + uint64_t dest_size = (*uncompressed_size); + uint64_t uncomp_size{0}; + nanoz_status_t ret = + nanoz_uncompress(src, src_size, dest_size, &tmpBuf.at(0), &uncomp_size); + if (NANOZ_SUCCESS != ret) { + return false; + } + if ((*uncompressed_size) != uncomp_size) { + return false; + } #else int ret = uncompress(&tmpBuf.at(0), uncompressed_size, src, src_size); if (Z_OK != ret) { @@ -5715,7 +5752,7 @@ static bool isValidTile(const EXRHeader* exr_header, static bool ReconstructTileOffsets(OffsetData& offset_data, const EXRHeader* exr_header, - const unsigned char* head, const unsigned char* marker, const size_t /*size*/, + const unsigned char* head, const unsigned char* marker, const size_t size, bool isMultiPartFile, bool isDeep) { int numXLevels = offset_data.num_x_levels; @@ -5724,11 +5761,20 @@ static bool ReconstructTileOffsets(OffsetData& offset_data, for (unsigned int dx = 0; dx < offset_data.offsets[l][dy].size(); ++dx) { tinyexr::tinyexr_uint64 tileOffset = tinyexr::tinyexr_uint64(marker - head); + if (isMultiPartFile) { + if ((marker + sizeof(int)) >= (head + size)) { + return false; + } + //int partNumber; marker += sizeof(int); } + if ((marker + 4 * sizeof(int)) >= (head + size)) { + return false; + } + int tileX; memcpy(&tileX, marker, sizeof(int)); tinyexr::swap4(&tileX); @@ -5750,6 +5796,9 @@ static bool ReconstructTileOffsets(OffsetData& offset_data, marker += sizeof(int); if (isDeep) { + if ((marker + 2 * sizeof(tinyexr::tinyexr_int64)) >= (head + size)) { + return false; + } tinyexr::tinyexr_int64 packed_offset_table_size; memcpy(&packed_offset_table_size, marker, sizeof(tinyexr::tinyexr_int64)); tinyexr::swap8(reinterpret_cast<tinyexr::tinyexr_uint64*>(&packed_offset_table_size)); @@ -5763,13 +5812,26 @@ static bool ReconstructTileOffsets(OffsetData& offset_data, // next Int64 is unpacked sample size - skip that too marker += packed_offset_table_size + packed_sample_size + 8; + if (marker >= (head + size)) { + return false; + } + } else { - int dataSize; - memcpy(&dataSize, marker, sizeof(int)); + if ((marker + sizeof(uint32_t)) >= (head + size)) { + return false; + } + + uint32_t dataSize; + memcpy(&dataSize, marker, sizeof(uint32_t)); tinyexr::swap4(&dataSize); - marker += sizeof(int); + marker += sizeof(uint32_t); + marker += dataSize; + + if (marker >= (head + size)) { + return false; + } } if (!isValidTile(exr_header, offset_data, @@ -5781,6 +5843,19 @@ static bool ReconstructTileOffsets(OffsetData& offset_data, if (level_idx < 0) { return false; } + + if (size_t(level_idx) >= offset_data.offsets.size()) { + return false; + } + + if (size_t(tileY) >= offset_data.offsets[size_t(level_idx)].size()) { + return false; + } + + if (size_t(tileX) >= offset_data.offsets[size_t(level_idx)][size_t(tileY)].size()) { + return false; + } + offset_data.offsets[size_t(level_idx)][size_t(tileY)][size_t(tileX)] = tileOffset; } } @@ -7043,13 +7118,16 @@ static bool EncodePixelData(/* out */ std::vector<unsigned char>& out_data, } else if ((compression_type == TINYEXR_COMPRESSIONTYPE_ZIPS) || (compression_type == TINYEXR_COMPRESSIONTYPE_ZIP)) { -#if TINYEXR_USE_MINIZ +#if defined(TINYEXR_USE_MINIZ) && (TINYEXR_USE_MINIZ==1) std::vector<unsigned char> block(mz_compressBound( static_cast<unsigned long>(buf.size()))); #elif TINYEXR_USE_STB_ZLIB // there is no compressBound() function, so we use a value that // is grossly overestimated, but should always work std::vector<unsigned char> block(256 + 2 * buf.size()); +#elif defined(TINYEXR_USE_NANOZLIB) && (TINYEXR_USE_NANOZLIB == 1) + std::vector<unsigned char> block(nanoz_compressBound( + static_cast<unsigned long>(buf.size()))); #else std::vector<unsigned char> block( compressBound(static_cast<uLong>(buf.size()))); |