diff --git a/.github/actions/download-artifact/action.yml b/.github/actions/download-artifact/action.yml new file mode 100644 index 000000000000..58c2aa906071 --- /dev/null +++ b/.github/actions/download-artifact/action.yml @@ -0,0 +1,18 @@ +name: Download Godot artifact +description: Download the Godot artifact. +inputs: + name: + description: The artifact name. + default: "${{ github.job }}" + path: + description: The path to download and extract to. + required: true + default: "./" +runs: + using: "composite" + steps: + - name: Download Godot Artifact + uses: actions/download-artifact@v3 + with: + name: ${{ inputs.name }} + path: ${{ inputs.path }} diff --git a/.github/actions/godot-api-dump/action.yml b/.github/actions/godot-api-dump/action.yml new file mode 100644 index 000000000000..47b675ae99fc --- /dev/null +++ b/.github/actions/godot-api-dump/action.yml @@ -0,0 +1,24 @@ +name: Dump Godot API +description: Dump Godot API for GDExtension +inputs: + bin: + description: The path to the Godot executable + required: true +runs: + using: "composite" + steps: + # Dump GDExtension interface and API + - name: Dump GDExtension interface and API for godot-cpp build + shell: sh + run: | + ${{ inputs.bin }} --headless --dump-gdextension-interface --dump-extension-api + mkdir godot-api + cp -f gdextension_interface.h godot-api/ + cp -f extension_api.json godot-api/ + + - name: Upload API dump + uses: ./.github/actions/upload-artifact + with: + name: 'godot-api-dump' + path: './godot-api/*' + diff --git a/.github/actions/godot-cache/action.yml b/.github/actions/godot-cache/action.yml index 2d7afc8514b8..09ad2099cc3c 100644 --- a/.github/actions/godot-cache/action.yml +++ b/.github/actions/godot-cache/action.yml @@ -16,7 +16,20 @@ runs: with: path: ${{inputs.scons-cache}} key: ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}} + + # We try to match an existing cache to restore from it. Each potential key is checked against + # all existing caches as a prefix. E.g. 'linux-template-minimal' would match any cache that + # starts with "linux-template-minimal", such as "linux-template-minimal-master-refs/heads/master-6588a4a29af1621086feac0117d5d4d37af957fd". + # + # We check these prefixes in this order: + # + # 1. The exact match, including the base branch, the commit reference, and the SHA hash of the commit. + # 2. A partial match for the same base branch and the same commit reference. + # 3. A partial match for the same base branch and the base branch commit reference. + # 4. A partial match for the same base branch only (not ideal, matches any PR with the same base branch). + restore-keys: | ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}} ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}} + ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-refs/heads/${{env.GODOT_BASE_BRANCH}} ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}} diff --git a/.github/actions/godot-converter-test/action.yml b/.github/actions/godot-converter-test/action.yml new file mode 100644 index 000000000000..919a76e69375 --- /dev/null +++ b/.github/actions/godot-converter-test/action.yml @@ -0,0 +1,18 @@ +name: Test Godot project converter +description: Test the Godot project converter. +inputs: + bin: + description: The path to the Godot executable + required: true +runs: + using: "composite" + steps: + - name: Test 3-to-4 conversion + shell: sh + run: | + mkdir converter_test + cd converter_test + touch project.godot + ../${{ inputs.bin }} --headless --validate-conversion-3to4 + cd .. + rm converter_test -rf diff --git a/.github/actions/godot-project-test/action.yml b/.github/actions/godot-project-test/action.yml new file mode 100644 index 000000000000..fd8c024a37df --- /dev/null +++ b/.github/actions/godot-project-test/action.yml @@ -0,0 +1,37 @@ +name: Test Godot project +description: Run the test Godot project. +inputs: + bin: + description: The path to the Godot executable + required: true +runs: + using: "composite" + steps: + # Download and extract zip archive with project, folder is renamed to be able to easy change used project + - name: Download test project + shell: sh + run: | + wget https://github.com/godotengine/regression-test-project/archive/4.0.zip + unzip 4.0.zip + mv "regression-test-project-4.0" "test_project" + + # Editor is quite complicated piece of software, so it is easy to introduce bug here. + + - name: Open and close editor (Vulkan) + shell: sh + run: | + xvfb-run ${{ inputs.bin }} --audio-driver Dummy --editor --quit --path test_project 2>&1 | tee sanitizers_log.txt || true + misc/scripts/check_ci_log.py sanitizers_log.txt + + - name: Open and close editor (GLES3) + shell: sh + run: | + DRI_PRIME=0 xvfb-run ${{ inputs.bin }} --audio-driver Dummy --rendering-driver opengl3 --editor --quit --path test_project 2>&1 | tee sanitizers_log.txt || true + misc/scripts/check_ci_log.py sanitizers_log.txt + + # Run test project + - name: Run project + shell: sh + run: | + xvfb-run ${{ inputs.bin }} 40 --audio-driver Dummy --path test_project 2>&1 | tee sanitizers_log.txt || true + misc/scripts/check_ci_log.py sanitizers_log.txt diff --git a/.github/workflows/godot_cpp_test.yml b/.github/workflows/godot_cpp_test.yml new file mode 100644 index 000000000000..2051920f3071 --- /dev/null +++ b/.github/workflows/godot_cpp_test.yml @@ -0,0 +1,54 @@ +name: ๐Ÿชฒ Godot CPP +on: + workflow_call: + +# Global Settings +env: + # Used for the cache key, and godot-cpp checkout. Add version suffix to force clean build. + GODOT_BASE_BRANCH: master + +concurrency: + group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-cpp-tests + cancel-in-progress: true + +jobs: + godot-cpp-tests: + runs-on: "ubuntu-20.04" + name: "Build and test Godot CPP" + steps: + - uses: actions/checkout@v3 + + - name: Setup python and scons + uses: ./.github/actions/godot-deps + + # Checkout godot-cpp + - name: Checkout godot-cpp + uses: actions/checkout@v3 + with: + repository: godotengine/godot-cpp + ref: ${{ env.GODOT_BASE_BRANCH }} + submodules: 'recursive' + path: 'godot-cpp' + + # Download generated API dump + - name: Download GDExtension interface and API dump + uses: ./.github/actions/download-artifact + with: + name: 'godot-api-dump' + path: './godot-api' + + # Extract and override existing files with generated files + - name: Extract GDExtension interface and API dump + run: | + cp -f godot-api/gdextension_interface.h godot-cpp/gdextension/ + cp -f godot-api/extension_api.json godot-cpp/gdextension/ + + # TODO: Add caching to the scons build and store it for CI via the godot-cache + # action. + + # Build godot-cpp test extension + - name: Build godot-cpp test extension + run: | + cd godot-cpp/test + scons target=template_debug dev_build=yes + cd ../.. diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml index aef8f83a53e1..c812996fd472 100644 --- a/.github/workflows/linux_builds.yml +++ b/.github/workflows/linux_builds.yml @@ -4,11 +4,12 @@ on: # Global Settings env: - # Used for the cache key, and godot-cpp checkout. Add version suffix to force clean build. + # Used for the cache key. Add version suffix to force clean build. GODOT_BASE_BRANCH: master 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 @@ -25,53 +26,62 @@ jobs: - name: Editor w/ Mono (target=editor) cache-name: linux-editor-mono target: editor - tests: false # Disabled due freeze caused by mix Mono build and CI sconsflags: module_mono_enabled=yes - doc-test: true bin: "./bin/godot.linuxbsd.editor.x86_64.mono" build-mono: true + tests: false # Disabled due freeze caused by mix Mono build and CI + doc-test: true proj-conv: true + api-compat: true artifact: true - compat: true - name: Editor with doubles and GCC sanitizers (target=editor, tests=yes, dev_build=yes, scu_build=yes, precision=double, use_asan=yes, use_ubsan=yes, linker=gold) cache-name: linux-editor-double-sanitizers target: editor - tests: true # Debug symbols disabled as they're huge on this build and we hit the 14 GB limit for runners. sconsflags: dev_build=yes scu_build=yes debug_symbols=no precision=double use_asan=yes use_ubsan=yes linker=gold - proj-test: true - # 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: true bin: "./bin/godot.linuxbsd.editor.dev.double.x86_64.san" build-mono: false + tests: true + proj-test: true + # Generate an API dump for godot-cpp tests. + api-dump: true # Skip 2GiB artifact speeding up action. artifact: false - name: Editor with clang sanitizers (target=editor, tests=yes, dev_build=yes, use_asan=yes, use_ubsan=yes, use_llvm=yes, linker=lld) cache-name: linux-editor-llvm-sanitizers target: editor - tests: true sconsflags: dev_build=yes use_asan=yes use_ubsan=yes use_llvm=yes linker=lld bin: "./bin/godot.linuxbsd.editor.dev.x86_64.llvm.san" build-mono: false + tests: true + # 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 - tests: false sconsflags: module_mono_enabled=yes build-mono: false + tests: false artifact: true - name: Minimal template (target=template_release, everything disabled) cache-name: linux-template-minimal target: template_release - tests: false sconsflags: modules_enabled_by_default=no disable_3d=yes disable_advanced_gui=yes deprecated=no minizip=no + tests: false artifact: true steps: @@ -85,6 +95,12 @@ jobs: sudo add-apt-repository ppa:kisak/kisak-mesa sudo apt-get install -qq mesa-vulkan-drivers + - name: Free disk space on runner + run: | + echo "Disk usage before:" && df -h + sudo rm -rf /usr/local/lib/android + echo "Disk usage after:" && df -h + - name: Setup Godot build cache uses: ./.github/actions/godot-cache with: @@ -121,6 +137,24 @@ jobs: run: | ./modules/mono/build_scripts/build_assemblies.py --godot-output-dir=./bin --godot-platform=linuxbsd + - name: Prepare artifact + if: ${{ matrix.artifact }} + run: | + strip bin/godot.* + chmod +x bin/godot.* + + - name: Upload artifact + uses: ./.github/actions/upload-artifact + if: ${{ matrix.artifact }} + with: + name: ${{ matrix.cache-name }} + + - name: Dump Godot API + uses: ./.github/actions/godot-api-dump + if: ${{ matrix.api-dump }} + with: + bin: ${{ matrix.bin }} + # Execute unit tests for the editor - name: Unit tests if: ${{ matrix.tests }} @@ -138,84 +172,22 @@ jobs: ${{ matrix.bin }} --doctool --headless 2>&1 > /dev/null || true git diff --color --exit-code && ! git ls-files --others --exclude-standard | sed -e 's/^/New doc file missing in PR: /' | grep 'xml$' - # Test 3.x -> 4.x project converter - - name: Test project converter - if: ${{ matrix.proj-conv }} - run: | - mkdir converter_test - cd converter_test - touch project.godot - ../${{ matrix.bin }} --headless --validate-conversion-3to4 - cd .. - rm converter_test -rf - - # Download and extract zip archive with project, folder is renamed to be able to easy change used project - - name: Download test project - if: ${{ matrix.proj-test }} - run: | - wget https://github.com/godotengine/regression-test-project/archive/4.0.zip - unzip 4.0.zip - mv "regression-test-project-4.0" "test_project" - - # Editor is quite complicated piece of software, so it is easy to introduce bug here - - name: Open and close editor (Vulkan) - if: ${{ matrix.proj-test }} - run: | - xvfb-run ${{ matrix.bin }} --audio-driver Dummy --editor --quit --path test_project 2>&1 | tee sanitizers_log.txt || true - misc/scripts/check_ci_log.py sanitizers_log.txt - - - name: Open and close editor (GLES3) - if: ${{ matrix.proj-test }} + # Check API backwards compatibility + - name: Check for GDExtension compatibility + if: ${{ matrix.api-compat }} run: | - DRI_PRIME=0 xvfb-run ${{ matrix.bin }} --audio-driver Dummy --rendering-driver opengl3 --editor --quit --path test_project 2>&1 | tee sanitizers_log.txt || true - misc/scripts/check_ci_log.py sanitizers_log.txt + ./misc/scripts/validate_extension_api.sh "${{ matrix.bin }}" - # Run test project - - name: Run project + # Download and run the test project + - name: Test Godot project + uses: ./.github/actions/godot-project-test if: ${{ matrix.proj-test }} - run: | - xvfb-run ${{ matrix.bin }} 40 --audio-driver Dummy --path test_project 2>&1 | tee sanitizers_log.txt || true - misc/scripts/check_ci_log.py sanitizers_log.txt - - # Checkout godot-cpp - - name: Checkout godot-cpp - if: ${{ matrix.godot-cpp-test }} - uses: actions/checkout@v3 with: - repository: godotengine/godot-cpp - ref: ${{ env.GODOT_BASE_BRANCH }} - submodules: 'recursive' - path: 'godot-cpp' - - # Dump GDExtension interface and API - - name: Dump GDExtension interface and API for godot-cpp build - if: ${{ matrix.godot-cpp-test }} - run: | - ${{ matrix.bin }} --headless --dump-gdextension-interface --dump-extension-api - cp -f gdextension_interface.h godot-cpp/gdextension/ - cp -f extension_api.json godot-cpp/gdextension/ + bin: ${{ matrix.bin }} - # Build godot-cpp test extension - - name: Build godot-cpp test extension - if: ${{ matrix.godot-cpp-test }} - run: | - cd godot-cpp/test - scons target=template_debug dev_build=yes - cd ../.. - - - name: Check for GDExtension compatibility - if: ${{ matrix.compat }} - run: | - ./misc/scripts/validate_extension_api.sh "${{ matrix.bin }}" || true # don't fail the CI for now - - - name: Prepare artifact - if: ${{ matrix.artifact }} - run: | - strip bin/godot.* - chmod +x bin/godot.* - - - name: Upload artifact - uses: ./.github/actions/upload-artifact - if: ${{ matrix.artifact }} + # Test the project converter + - name: Test project converter + uses: ./.github/actions/godot-converter-test + if: ${{ matrix.proj-conv }} with: - name: ${{ matrix.cache-name }} + bin: ${{ matrix.bin }} diff --git a/.github/workflows/macos_builds.yml b/.github/workflows/macos_builds.yml index 6e0fbbf461b0..ae6f452bc2a3 100644 --- a/.github/workflows/macos_builds.yml +++ b/.github/workflows/macos_builds.yml @@ -56,14 +56,6 @@ jobs: target: ${{ matrix.target }} tests: ${{ matrix.tests }} - # Execute unit tests for the editor - - name: Unit tests - if: ${{ matrix.tests }} - run: | - ${{ matrix.bin }} --version - ${{ matrix.bin }} --help - ${{ matrix.bin }} --test - - name: Prepare artifact run: | strip bin/godot.* @@ -73,3 +65,11 @@ jobs: uses: ./.github/actions/upload-artifact with: name: ${{ matrix.cache-name }} + + # Execute unit tests for the editor + - name: Unit tests + if: ${{ matrix.tests }} + run: | + ${{ matrix.bin }} --version + ${{ matrix.bin }} --help + ${{ matrix.bin }} --test diff --git a/.github/workflows/runner.yml b/.github/workflows/runner.yml index be255b54688a..34b6af43074d 100644 --- a/.github/workflows/runner.yml +++ b/.github/workflows/runner.yml @@ -6,36 +6,60 @@ concurrency: cancel-in-progress: true 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 + + # Third stage: Run auxiliary tests using build artifacts from previous 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 + # appropriately. + needs: linux-build + uses: ./.github/workflows/godot_cpp_test.yml diff --git a/.github/workflows/windows_builds.yml b/.github/workflows/windows_builds.yml index 182ae2fc8c41..ab62dca5cbca 100644 --- a/.github/workflows/windows_builds.yml +++ b/.github/workflows/windows_builds.yml @@ -60,14 +60,6 @@ jobs: target: ${{ matrix.target }} tests: ${{ matrix.tests }} - # Execute unit tests for the editor - - name: Unit tests - if: ${{ matrix.tests }} - run: | - ${{ matrix.bin }} --version - ${{ matrix.bin }} --help - ${{ matrix.bin }} --test - - name: Prepare artifact run: | Remove-Item bin/* -Include *.exp,*.lib,*.pdb -Force @@ -76,3 +68,11 @@ jobs: uses: ./.github/actions/upload-artifact with: name: ${{ matrix.cache-name }} + + # Execute unit tests for the editor + - name: Unit tests + if: ${{ matrix.tests }} + run: | + ${{ matrix.bin }} --version + ${{ matrix.bin }} --help + ${{ matrix.bin }} --test diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index 582784d78ea4..f884c690fa4e 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 f82c9c656e4b..6968967380f5 100644 --- a/SConstruct +++ b/SConstruct @@ -220,6 +220,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 +552,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. diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 715ed6177033..e8c885162b2e 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 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://"); @@ -1260,6 +1260,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); diff --git a/core/config/project_settings.h b/core/config/project_settings.h index b89e6694b0c6..a1f52fa1e0eb 100644 --- a/core/config/project_settings.h +++ b/core/config/project_settings.h @@ -43,6 +43,7 @@ class TypedArray; class ProjectSettings : public Object { GDCLASS(ProjectSettings, Object); _THREAD_SAFE_CLASS_ + friend class TestProjectSettingsInternalsAccessor; public: typedef HashMap CustomMap; diff --git a/core/core_bind.cpp b/core/core_bind.cpp index a73b198be2c1..4e220d08394d 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -961,6 +961,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)) { @@ -1034,6 +1039,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 dc0b2a1cf5b2..1cbbcdd25183 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -326,6 +326,7 @@ class Geometry3D : public Object { Vector 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/doc_data.h b/core/doc_data.h index 0fe7414b9890..b8c92a4b6776 100644 --- a/core/doc_data.h +++ b/core/doc_data.h @@ -532,6 +532,42 @@ class DocData { } }; + struct EnumDoc { + String description; + bool is_deprecated = false; + bool is_experimental = false; + static EnumDoc from_dict(const Dictionary &p_dict) { + EnumDoc doc; + + if (p_dict.has("description")) { + doc.description = p_dict["description"]; + } + + if (p_dict.has("is_deprecated")) { + doc.is_deprecated = p_dict["is_deprecated"]; + } + + if (p_dict.has("is_experimental")) { + doc.is_experimental = p_dict["is_experimental"]; + } + + return doc; + } + static Dictionary to_dict(const EnumDoc &p_doc) { + Dictionary dict; + + if (!p_doc.description.is_empty()) { + dict["description"] = p_doc.description; + } + + dict["is_deprecated"] = p_doc.is_deprecated; + + dict["is_experimental"] = p_doc.is_experimental; + + return dict; + } + }; + struct ClassDoc { String name; String inherits; @@ -543,7 +579,7 @@ class DocData { Vector operators; Vector signals; Vector constants; - HashMap enums; + HashMap enums; Vector properties; Vector annotations; Vector theme_properties; @@ -626,7 +662,7 @@ class DocData { enums = p_dict["enums"]; } for (int i = 0; i < enums.size(); i++) { - doc.enums[enums.get_key_at_index(i)] = enums.get_value_at_index(i); + doc.enums[enums.get_key_at_index(i)] = EnumDoc::from_dict(enums.get_value_at_index(i)); } Array properties; @@ -740,8 +776,8 @@ class DocData { if (!p_doc.enums.is_empty()) { Dictionary enums; - for (const KeyValue &E : p_doc.enums) { - enums[E.key] = E.value; + for (const KeyValue &E : p_doc.enums) { + enums[E.key] = EnumDoc::to_dict(E.value); } dict["enums"] = enums; } diff --git a/core/error/error_macros.h b/core/error/error_macros.h index 65804b779647..c8182975d57b 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_interface.cpp b/core/extension/gdextension_interface.cpp index 7ef956a470fe..b4541de8fee8 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(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(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(p_classname); const StringName methodname = *reinterpret_cast(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 6c05f3988b01..cfc21473d6aa 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 d481acf0055e..39f1acf6239a 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 ec16871b7286..c63a4e52e31e 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -149,6 +149,7 @@ class Input : public Object { HatMask last_hat = HatMask::CENTER; int mapping = -1; int hat_current = 0; + Dictionary info; }; VelocityTrack mouse_velocity_track; @@ -276,7 +277,7 @@ class Input : public Object { 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 @@ class Input : public Object { 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 b669afdc991a..6026dbf8962f 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::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 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 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 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 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 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 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 ad1ac665f313..7b9e66bb8359 100644 --- a/core/io/file_access.h +++ b/core/io/file_access.h @@ -60,6 +60,21 @@ class FileAccess : public RefCounted { 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 @@ class FileAccess : public RefCounted { 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 _get_unix_permissions(const String &p_file) = 0; + virtual Error _set_unix_permissions(const String &p_file, BitField 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 @@ class FileAccess : public RefCounted { 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 get_unix_permissions(const String &p_file); + static Error set_unix_permissions(const String &p_file, BitField 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 @@ class FileAccess : public RefCounted { 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 3e5a1217dd6f..0f00bd292cdd 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 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 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 601b74a9c147..bf57eaa07c0f 100644 --- a/core/io/file_access_compressed.h +++ b/core/io/file_access_compressed.h @@ -94,8 +94,13 @@ class FileAccessCompressed : public FileAccess { 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 _get_unix_permissions(const String &p_file) override; + virtual Error _set_unix_permissions(const String &p_file, BitField 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 c39d19d52b07..b689f5b6284e 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 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 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 9702b5a51718..489d213b8fd5 100644 --- a/core/io/file_access_encrypted.h +++ b/core/io/file_access_encrypted.h @@ -85,8 +85,13 @@ class FileAccessEncrypted : public FileAccess { 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 _get_unix_permissions(const String &p_file) override; + virtual Error _set_unix_permissions(const String &p_file, BitField 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 43fe6ab65889..ac08e5406fa0 100644 --- a/core/io/file_access_memory.h +++ b/core/io/file_access_memory.h @@ -68,8 +68,13 @@ class FileAccessMemory : public FileAccess { 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 _get_unix_permissions(const String &p_file) override { return 0; } + virtual Error _set_unix_permissions(const String &p_file, BitField 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 1538b302c290..97391a561108 100644 --- a/core/io/file_access_pack.h +++ b/core/io/file_access_pack.h @@ -150,8 +150,13 @@ class FileAccessPack : public FileAccess { Ref 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 _get_unix_permissions(const String &p_file) override { return 0; } + virtual Error _set_unix_permissions(const String &p_file, BitField 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 f8b640946c21..1062a0623802 100644 --- a/core/io/file_access_zip.h +++ b/core/io/file_access_zip.h @@ -106,8 +106,13 @@ class FileAccessZip : public FileAccess { 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 _get_unix_permissions(const String &p_file) override { return 0; } + virtual Error _set_unix_permissions(const String &p_file, BitField 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/image.cpp b/core/io/image.cpp index 7326563f18ed..a5fea09113f1 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -3017,6 +3017,7 @@ ImageMemLoadFunc Image::_webp_mem_loader_func = nullptr; ImageMemLoadFunc Image::_tga_mem_loader_func = nullptr; ImageMemLoadFunc Image::_bmp_mem_loader_func = nullptr; ScalableImageMemLoadFunc Image::_svg_scalable_mem_loader_func = nullptr; +ImageMemLoadFunc Image::_dds_mem_loader_func = nullptr; void (*Image::_image_compress_bc_func)(Image *, Image::UsedChannels) = nullptr; void (*Image::_image_compress_bptc_func)(Image *, Image::UsedChannels) = nullptr; @@ -3488,6 +3489,7 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("load_webp_from_buffer", "buffer"), &Image::load_webp_from_buffer); ClassDB::bind_method(D_METHOD("load_tga_from_buffer", "buffer"), &Image::load_tga_from_buffer); ClassDB::bind_method(D_METHOD("load_bmp_from_buffer", "buffer"), &Image::load_bmp_from_buffer); + ClassDB::bind_method(D_METHOD("load_dds_from_buffer", "buffer"), &Image::load_dds_from_buffer); ClassDB::bind_method(D_METHOD("load_svg_from_buffer", "buffer", "scale"), &Image::load_svg_from_buffer, DEFVAL(1.0)); ClassDB::bind_method(D_METHOD("load_svg_from_string", "svg_str", "scale"), &Image::load_svg_from_string, DEFVAL(1.0)); @@ -3863,6 +3865,14 @@ Error Image::load_svg_from_string(const String &p_svg_str, float scale) { return load_svg_from_buffer(p_svg_str.to_utf8_buffer(), scale); } +Error Image::load_dds_from_buffer(const Vector &p_array) { + ERR_FAIL_NULL_V_MSG( + _dds_mem_loader_func, + ERR_UNAVAILABLE, + "The DDS module isn't enabled. Recompile the Godot editor or export template binary with the `module_dds_enabled=yes` SCons option."); + return _load_from_buffer(p_array, _dds_mem_loader_func); +} + void Image::convert_rg_to_ra_rgba8() { ERR_FAIL_COND(format != FORMAT_RGBA8); ERR_FAIL_COND(!data.size()); diff --git a/core/io/image.h b/core/io/image.h index f877b00ee600..f68543ba2469 100644 --- a/core/io/image.h +++ b/core/io/image.h @@ -150,6 +150,7 @@ class Image : public Resource { static ImageMemLoadFunc _tga_mem_loader_func; static ImageMemLoadFunc _bmp_mem_loader_func; static ScalableImageMemLoadFunc _svg_scalable_mem_loader_func; + static ImageMemLoadFunc _dds_mem_loader_func; static void (*_image_compress_bc_func)(Image *, UsedChannels p_channels); static void (*_image_compress_bptc_func)(Image *, UsedChannels p_channels); @@ -402,6 +403,7 @@ class Image : public Resource { Error load_webp_from_buffer(const Vector &p_array); Error load_tga_from_buffer(const Vector &p_array); Error load_bmp_from_buffer(const Vector &p_array); + Error load_dds_from_buffer(const Vector &p_array); Error load_svg_from_buffer(const Vector &p_array, float scale = 1.0); Error load_svg_from_string(const String &p_svg_str, float scale = 1.0); diff --git a/core/io/remote_filesystem_client.cpp b/core/io/remote_filesystem_client.cpp index f22e442a3440..119881044184 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/resource_loader.cpp b/core/io/resource_loader.cpp index 1fe662b1fad2..df0253349c16 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -275,10 +275,10 @@ Ref ResourceLoader::_load(const String &p_path, const String &p_origin #ifdef TOOLS_ENABLED Ref file_check = FileAccess::create(FileAccess::ACCESS_RESOURCES); - ERR_FAIL_COND_V_MSG(!file_check->file_exists(p_path), Ref(), "Resource file not found: " + p_path + "."); + ERR_FAIL_COND_V_MSG(!file_check->file_exists(p_path), Ref(), vformat("Resource file not found: %s (expected type: %s)", p_path, p_type_hint)); #endif - ERR_FAIL_V_MSG(Ref(), "No loader found for resource: " + p_path + "."); + ERR_FAIL_V_MSG(Ref(), vformat("No loader found for resource: %s (expected type: %s)", p_path, p_type_hint)); } void ResourceLoader::_thread_load_function(void *p_userdata) { diff --git a/core/math/a_star_grid_2d.cpp b/core/math/a_star_grid_2d.cpp index 63f7c80bdd16..9ba4c2ff9ab0 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 50df58e0e95c..dd5f9d0575b9 100644 --- a/core/math/a_star_grid_2d.h +++ b/core/math/a_star_grid_2d.h @@ -177,6 +177,9 @@ class AStarGrid2D : public RefCounted { 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 55923e013326..7df8c37e3ca1 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/object/message_queue.cpp b/core/object/message_queue.cpp index dd7aba13844c..506f8291eb2c 100644 --- a/core/object/message_queue.cpp +++ b/core/object/message_queue.cpp @@ -35,10 +35,6 @@ #include "core/object/class_db.h" #include "core/object/script_language.h" -#ifdef DEBUG_ENABLED -#include "core/config/engine.h" -#endif - #ifdef DEV_ENABLED // Includes sanity checks to ensure that a queue set as a thread singleton override // is only ever called from the thread it was set for. @@ -320,34 +316,25 @@ Error CallQueue::flush() { Object *target = message->callable.get_object(); UNLOCK_MUTEX; -#ifdef DEBUG_ENABLED - if (!message->callable.is_valid()) { - // The editor would cause many of these. - if (!Engine::get_singleton()->is_editor_hint()) { - ERR_PRINT("Trying to execute a deferred call/notification/set on a previously freed instance. Consider using queue_free() instead of free()."); - } - } else -#endif - { - switch (message->type & FLAG_MASK) { - case TYPE_CALL: { - if (target || (message->type & FLAG_NULL_IS_OK)) { - Variant *args = (Variant *)(message + 1); - _call_function(message->callable, args, message->args, message->type & FLAG_SHOW_ERROR); - } - } break; - case TYPE_NOTIFICATION: { - if (target) { - target->notification(message->notification); - } - } break; - case TYPE_SET: { - if (target) { - Variant *arg = (Variant *)(message + 1); - target->set(message->callable.get_method(), *arg); - } - } break; - } + + switch (message->type & FLAG_MASK) { + case TYPE_CALL: { + if (target || (message->type & FLAG_NULL_IS_OK)) { + Variant *args = (Variant *)(message + 1); + _call_function(message->callable, args, message->args, message->type & FLAG_SHOW_ERROR); + } + } break; + case TYPE_NOTIFICATION: { + if (target) { + target->notification(message->notification); + } + } break; + case TYPE_SET: { + if (target) { + Variant *arg = (Variant *)(message + 1); + target->set(message->callable.get_method(), *arg); + } + } break; } if ((message->type & FLAG_MASK) != TYPE_NOTIFICATION) { diff --git a/core/string/locales.h b/core/string/locales.h index 8a7efb4fd1c4..840fca65a714 100644 --- a/core/string/locales.h +++ b/core/string/locales.h @@ -1057,8 +1057,8 @@ static const char *script_list[][2] = { { "Hangul", "Hang" }, { "Han", "Hani" }, { "Hanunoo", "Hano" }, - { "Simplified", "Hans" }, - { "Traditional", "Hant" }, + { "Simplified Han", "Hans" }, + { "Traditional Han", "Hant" }, { "Hatran", "Hatr" }, { "Hebrew", "Hebr" }, { "Hiragana", "Hira" }, @@ -1110,7 +1110,7 @@ static const char *script_list[][2] = { { "Mro", "Mroo" }, { "Meitei Mayek", "Mtei" }, { "Multani", "Mult" }, - { "Myanmar (Burmese)", "Mymr" }, + { "Myanmar / Burmese", "Mymr" }, { "โ€‹Nag Mundari", "Nagm" }, { "Nandinagari", "Nand" }, { "Old North Arabian", "Narb" }, diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 12e642372488..376d0832d4e6 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -1750,7 +1750,7 @@ Vector 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)); } diff --git a/core/variant/variant_op.h b/core/variant/variant_op.h index c11f726402fd..fc1f7828a29e 100644 --- a/core/variant/variant_op.h +++ b/core/variant/variant_op.h @@ -284,7 +284,7 @@ class OperatorEvaluatorModNZ { const B &b = *VariantGetInternalPtr::get_ptr(&p_right); if (b == 0) { r_valid = false; - *r_ret = "Module by zero error"; + *r_ret = "Modulo by zero error"; return; } *r_ret = a % b; @@ -307,7 +307,7 @@ class OperatorEvaluatorModNZ { const Vector2i &b = *VariantGetInternalPtr::get_ptr(&p_right); if (unlikely(b.x == 0 || b.y == 0)) { r_valid = false; - *r_ret = "Module by zero error"; + *r_ret = "Modulo by zero error"; return; } *r_ret = a % b; @@ -331,7 +331,7 @@ class OperatorEvaluatorModNZ { const Vector3i &b = *VariantGetInternalPtr::get_ptr(&p_right); if (unlikely(b.x == 0 || b.y == 0 || b.z == 0)) { r_valid = false; - *r_ret = "Module by zero error"; + *r_ret = "Modulo by zero error"; return; } *r_ret = a % b; @@ -355,7 +355,7 @@ class OperatorEvaluatorModNZ { const Vector4i &b = *VariantGetInternalPtr::get_ptr(&p_right); if (unlikely(b.x == 0 || b.y == 0 || b.z == 0 || b.w == 0)) { r_valid = false; - *r_ret = "Module by zero error"; + *r_ret = "Modulo by zero error"; return; } *r_ret = a % b; diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp index 545825011a95..4f6bcb58b320 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::get(&x); - } break; - case Variant::FLOAT: { - return Math::floor(VariantInternalAccessor::get(&x)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor::get(&x).floor(); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor::get(&x).floor(); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor::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::get(&x); + } break; + case Variant::FLOAT: { + return Math::floor(VariantInternalAccessor::get(&x)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor::get(&x).floor(); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor::get(&x).floor(); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor::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::get(&x); - } break; - case Variant::FLOAT: { - return Math::ceil(VariantInternalAccessor::get(&x)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor::get(&x).ceil(); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor::get(&x).ceil(); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor::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::get(&x); + } break; + case Variant::FLOAT: { + return Math::ceil(VariantInternalAccessor::get(&x)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor::get(&x).ceil(); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor::get(&x).ceil(); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor::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::get(&x); - } break; - case Variant::FLOAT: { - return Math::round(VariantInternalAccessor::get(&x)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor::get(&x).round(); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor::get(&x).round(); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor::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::get(&x); + } break; + case Variant::FLOAT: { + return Math::round(VariantInternalAccessor::get(&x)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor::get(&x).round(); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor::get(&x).round(); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor::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::get(&x)); - } break; - case Variant::FLOAT: { - return Math::absd(VariantInternalAccessor::get(&x)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor::get(&x).abs(); - } break; - case Variant::VECTOR2I: { - return VariantInternalAccessor::get(&x).abs(); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor::get(&x).abs(); - } break; - case Variant::VECTOR3I: { - return VariantInternalAccessor::get(&x).abs(); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor::get(&x).abs(); - } break; - case Variant::VECTOR4I: { - return VariantInternalAccessor::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::get(&x)); + } break; + case Variant::FLOAT: { + return Math::absd(VariantInternalAccessor::get(&x)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor::get(&x).abs(); + } break; + case Variant::VECTOR2I: { + return VariantInternalAccessor::get(&x).abs(); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor::get(&x).abs(); + } break; + case Variant::VECTOR3I: { + return VariantInternalAccessor::get(&x).abs(); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor::get(&x).abs(); + } break; + case Variant::VECTOR4I: { + return VariantInternalAccessor::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::get(&x)); - } break; - case Variant::FLOAT: { - return SIGN(VariantInternalAccessor::get(&x)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor::get(&x).sign(); - } break; - case Variant::VECTOR2I: { - return VariantInternalAccessor::get(&x).sign(); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor::get(&x).sign(); - } break; - case Variant::VECTOR3I: { - return VariantInternalAccessor::get(&x).sign(); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor::get(&x).sign(); - } break; - case Variant::VECTOR4I: { - return VariantInternalAccessor::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::get(&x)); + } break; + case Variant::FLOAT: { + return SIGN(VariantInternalAccessor::get(&x)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor::get(&x).sign(); + } break; + case Variant::VECTOR2I: { + return VariantInternalAccessor::get(&x).sign(); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor::get(&x).sign(); + } break; + case Variant::VECTOR3I: { + return VariantInternalAccessor::get(&x).sign(); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor::get(&x).sign(); + } break; + case Variant::VECTOR4I: { + return VariantInternalAccessor::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::get(&step)); + } break; + case Variant::FLOAT: { + return snappedf(x, VariantInternalAccessor::get(&step)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor::get(&x).snapped(VariantInternalAccessor::get(&step)); + } break; + case Variant::VECTOR2I: { + return VariantInternalAccessor::get(&x).snapped(VariantInternalAccessor::get(&step)); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor::get(&x).snapped(VariantInternalAccessor::get(&step)); + } break; + case Variant::VECTOR3I: { + return VariantInternalAccessor::get(&x).snapped(VariantInternalAccessor::get(&step)); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor::get(&x).snapped(VariantInternalAccessor::get(&step)); + } break; + case Variant::VECTOR4I: { + return VariantInternalAccessor::get(&x).snapped(VariantInternalAccessor::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::get(&step)); - } break; - case Variant::FLOAT: { - return snappedf(x, VariantInternalAccessor::get(&step)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor::get(&x).snapped(VariantInternalAccessor::get(&step)); - } break; - case Variant::VECTOR2I: { - return VariantInternalAccessor::get(&x).snapped(VariantInternalAccessor::get(&step)); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor::get(&x).snapped(VariantInternalAccessor::get(&step)); - } break; - case Variant::VECTOR3I: { - return VariantInternalAccessor::get(&x).snapped(VariantInternalAccessor::get(&step)); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor::get(&x).snapped(VariantInternalAccessor::get(&step)); - } break; - case Variant::VECTOR4I: { - return VariantInternalAccessor::get(&x).snapped(VariantInternalAccessor::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::get(&from), to, weight); + } break; + case Variant::FLOAT: { + return lerpf(VariantInternalAccessor::get(&from), to, weight); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor::get(&from).lerp(VariantInternalAccessor::get(&to), weight); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor::get(&from).lerp(VariantInternalAccessor::get(&to), weight); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor::get(&from).lerp(VariantInternalAccessor::get(&to), weight); + } break; + case Variant::QUATERNION: { + return VariantInternalAccessor::get(&from).slerp(VariantInternalAccessor::get(&to), weight); + } break; + case Variant::BASIS: { + return VariantInternalAccessor::get(&from).slerp(VariantInternalAccessor::get(&to), weight); + } break; + case Variant::COLOR: { + return VariantInternalAccessor::get(&from).lerp(VariantInternalAccessor::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::get(&from), to, weight); - } break; - case Variant::FLOAT: { - return lerpf(VariantInternalAccessor::get(&from), to, weight); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor::get(&from).lerp(VariantInternalAccessor::get(&to), weight); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor::get(&from).lerp(VariantInternalAccessor::get(&to), weight); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor::get(&from).lerp(VariantInternalAccessor::get(&to), weight); - } break; - case Variant::QUATERNION: { - return VariantInternalAccessor::get(&from).slerp(VariantInternalAccessor::get(&to), weight); - } break; - case Variant::BASIS: { - return VariantInternalAccessor::get(&from).slerp(VariantInternalAccessor::get(&to), weight); - } break; - case Variant::COLOR: { - return VariantInternalAccessor::get(&from).lerp(VariantInternalAccessor::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 wref = memnew(WeakRef); - Ref r = obj; - if (r.is_valid()) { - wref->set_ref(r); - } - return wref; - } else { - Ref 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 wref = memnew(WeakRef); + Ref 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 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 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

::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 000000000000..78f66987cba6 --- /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 9d597d8e2385..008452a92ece 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -362,8 +362,8 @@ - The natural exponential function. It raises the mathematical constant [b]e[/b] to the power of [param x] and returns it. - [b]e[/b] has an approximate value of 2.71828, and can be obtained with [code]exp(1)[/code]. + The natural exponential function. It raises the mathematical constant [i]e[/i] to the power of [param x] and returns it. + [i]e[/i] has an approximate value of 2.71828, and can be obtained with [code]exp(1)[/code]. For exponents to other bases use the method [method pow]. [codeblock] var a = exp(2) # Approximately 7.39 @@ -739,8 +739,7 @@ - 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. diff --git a/doc/classes/AStarGrid2D.xml b/doc/classes/AStarGrid2D.xml index 5d54af60ec5c..d64941cb9b60 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]. + + + + + + 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. + + + + + + + + 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. + + diff --git a/doc/classes/Area3D.xml b/doc/classes/Area3D.xml index 89b3f843af3f..4f89e9b015a1 100644 --- a/doc/classes/Area3D.xml +++ b/doc/classes/Area3D.xml @@ -6,7 +6,7 @@ [Area3D] is a region of 3D space defined by one or multiple [CollisionShape3D] or [CollisionPolygon3D] child nodes. It detects when other [CollisionObject3D]s enter or exit it, and it also keeps track of which collision objects haven't exited it yet (i.e. which one are overlapping it). This node can also locally alter or override physics parameters (gravity, damping) and route audio to custom audio buses. - [b]Warning:[/b] Using a [ConcavePolygonShape3D] inside a [CollisionShape3D] child of this node (created e.g. by using the [i]Create Trimesh Collision Sibling[/i] option in the [i]Mesh[/i] menu that appears when selecting a [MeshInstance3D] node) may give unexpected results, since this collision shape is hollow. If this is not desired, it has to be split into multiple [ConvexPolygonShape3D]s or primitive shapes like [BoxShape3D], or in some cases it may be replaceable by a [CollisionPolygon3D]. + [b]Warning:[/b] Using a [ConcavePolygonShape3D] inside a [CollisionShape3D] child of this node (created e.g. by using the [b]Create Trimesh Collision Sibling[/b] option in the [b]Mesh[/b] menu that appears when selecting a [MeshInstance3D] node) may give unexpected results, since this collision shape is hollow. If this is not desired, it has to be split into multiple [ConvexPolygonShape3D]s or primitive shapes like [BoxShape3D], or in some cases it may be replaceable by a [CollisionPolygon3D]. $DOCS_URL/tutorials/physics/using_area_2d.html diff --git a/doc/classes/AudioEffectDelay.xml b/doc/classes/AudioEffectDelay.xml index fa255e320181..eae49f6a9845 100644 --- a/doc/classes/AudioEffectDelay.xml +++ b/doc/classes/AudioEffectDelay.xml @@ -21,34 +21,34 @@ Feedback delay time in milliseconds. - Sound level for [code]tap1[/code]. + Sound level for feedback. Low-pass filter for feedback, in Hz. Frequencies below this value are filtered out of the source signal. - If [code]true[/code], [code]tap1[/code] will be enabled. + If [code]true[/code], the first tap will be enabled. - [code]tap1[/code] delay time in milliseconds. + First tap delay time in milliseconds. - Sound level for [code]tap1[/code]. + Sound level for the first tap. - Pan position for [code]tap1[/code]. Value can range from -1 (fully left) to 1 (fully right). + Pan position for the first tap. Value can range from -1 (fully left) to 1 (fully right). - If [code]true[/code], [code]tap2[/code] will be enabled. + If [code]true[/code], the second tap will be enabled. - [b]Tap2[/b] delay time in milliseconds. + Second tap delay time in milliseconds. - Sound level for [code]tap2[/code]. + Sound level for the second tap. - Pan position for [code]tap2[/code]. Value can range from -1 (fully left) to 1 (fully right). + Pan position for the second tap. Value can range from -1 (fully left) to 1 (fully right). diff --git a/doc/classes/AudioServer.xml b/doc/classes/AudioServer.xml index 20a87aea7b4d..cb4fb8d5ca9b 100644 --- a/doc/classes/AudioServer.xml +++ b/doc/classes/AudioServer.xml @@ -132,7 +132,7 @@ - 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. diff --git a/doc/classes/BackBufferCopy.xml b/doc/classes/BackBufferCopy.xml index a671b85c833b..e4e6f1d76dbc 100644 --- a/doc/classes/BackBufferCopy.xml +++ b/doc/classes/BackBufferCopy.xml @@ -1,7 +1,7 @@ - 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 ``hint_screen_texture``). + 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]). 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. diff --git a/doc/classes/BaseButton.xml b/doc/classes/BaseButton.xml index b18ee95a4304..c5998ad8a189 100644 --- a/doc/classes/BaseButton.xml +++ b/doc/classes/BaseButton.xml @@ -17,7 +17,7 @@ - + Called when the button is toggled (only if [member toggle_mode] is active). @@ -98,9 +98,9 @@ - + - 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. diff --git a/doc/classes/Bone2D.xml b/doc/classes/Bone2D.xml index 605f217eff85..fa8112de29ee 100644 --- a/doc/classes/Bone2D.xml +++ b/doc/classes/Bone2D.xml @@ -61,7 +61,7 @@ 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]. diff --git a/doc/classes/Button.xml b/doc/classes/Button.xml index 6faefcbe0dd7..25c71deb1fb4 100644 --- a/doc/classes/Button.xml +++ b/doc/classes/Button.xml @@ -115,7 +115,7 @@ Icon modulate [Color] used when the [Button] is being pressed. - + The horizontal space between [Button]'s icon and text. Negative values will be treated as [code]0[/code] when used. diff --git a/doc/classes/ColorPickerButton.xml b/doc/classes/ColorPickerButton.xml index 32ec27868a24..f6af188998ca 100644 --- a/doc/classes/ColorPickerButton.xml +++ b/doc/classes/ColorPickerButton.xml @@ -74,7 +74,7 @@ Text [Color] used when the [ColorPickerButton] is being pressed. - + The horizontal space between [ColorPickerButton]'s icon and text. diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index 54c38f2db9f9..ee790b696860 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -1094,15 +1094,15 @@ - Emitted when the mouse enters the control's [code]Rect[/code] area, provided its [member mouse_filter] lets the event reach it. - [b]Note:[/b] [signal mouse_entered] will not be emitted if the mouse enters a child [Control] node before entering the parent's [code]Rect[/code] area, at least until the mouse is moved to reach the parent's [code]Rect[/code] area. + Emitted when the mouse cursor enters the control's visible area, that is not occluded behind other Controls or Windows, provided its [member mouse_filter] lets the event reach it and regardless if it's currently focused or not. + [b]Note:[/b] [member CanvasItem.z_index] doesn't affect, which Control receives the signal. - Emitted when the mouse leaves the control's [code]Rect[/code] area, provided its [member mouse_filter] lets the event reach it. - [b]Note:[/b] [signal mouse_exited] will be emitted if the mouse enters a child [Control] node, even if the mouse cursor is still inside the parent's [code]Rect[/code] area. - If you want to check whether the mouse truly left the area, ignoring any top nodes, you can use code like this: + Emitted when the mouse cursor leaves the control's visible area, that is not occluded behind other Controls or Windows, provided its [member mouse_filter] lets the event reach it and regardless if it's currently focused or not. + [b]Note:[/b] [member CanvasItem.z_index] doesn't affect, which Control receives the signal. + [b]Note:[/b] If you want to check whether the mouse truly left the area, ignoring any top nodes, you can use code like this: [codeblock] func _on_mouse_exited(): if not Rect2(Vector2(), size).has_point(get_local_mouse_position()): @@ -1140,10 +1140,12 @@ Sent when the node changes size. Use [member size] to get the new size. - Sent when the mouse pointer enters the node. + Sent when the mouse cursor enters the control's visible area, that is not occluded behind other Controls or Windows, provided its [member mouse_filter] lets the event reach it and regardless if it's currently focused or not. + [b]Note:[/b] [member CanvasItem.z_index] doesn't affect, which Control receives the notification. - Sent when the mouse pointer exits the node. + Sent when the mouse cursor leaves the control's visible area, that is not occluded behind other Controls or Windows, provided its [member mouse_filter] lets the event reach it and regardless if it's currently focused or not. + [b]Note:[/b] [member CanvasItem.z_index] doesn't affect, which Control receives the notification. Sent when the node grabs focus. diff --git a/doc/classes/EditorExportPlugin.xml b/doc/classes/EditorExportPlugin.xml index 75641a448692..fde6307a1300 100644 --- a/doc/classes/EditorExportPlugin.xml +++ b/doc/classes/EditorExportPlugin.xml @@ -44,7 +44,7 @@ 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]. @@ -112,7 +112,7 @@ Virtual method to be overridden by the user. This is called to retrieve the local paths of the Android libraries archive (AAR) files provided by this plugin. - [b]Note:[/b] Relative paths **must** be relative to Godot's [code]res://addons/[/code] directory. For example, an AAR file located under [code]res://addons/hello_world_plugin/HelloWorld.release.aar[/code] can be returned as an absolute path using [code]res://addons/hello_world_plugin/HelloWorld.release.aar[/code] or a relative path using [code]hello_world_plugin/HelloWorld.release.aar[/code]. + [b]Note:[/b] Relative paths [b]must[/b] be relative to Godot's [code]res://addons/[/code] directory. For example, an AAR file located under [code]res://addons/hello_world_plugin/HelloWorld.release.aar[/code] can be returned as an absolute path using [code]res://addons/hello_world_plugin/HelloWorld.release.aar[/code] or a relative path using [code]hello_world_plugin/HelloWorld.release.aar[/code]. [b]Note:[/b] Only supported on Android and requires [member EditorExportPlatformAndroid.gradle_build/use_gradle_build] to be enabled. diff --git a/doc/classes/FileAccess.xml b/doc/classes/FileAccess.xml index 5c1f6775a1d3..85fbff793265 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. + + + + + 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. + + @@ -228,6 +236,14 @@ Returns the file cursor's position. + + + + + 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. + + @@ -241,6 +257,14 @@ Returns a SHA-256 [String] representing the file at the given path or an empty [String] on failure. + + + + + Returns file UNIX permissions. + [b]Note:[/b] This method is implemented on iOS, Linux/BSD, and macOS. + + @@ -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. + + + + + + Sets file [code]hidden[/code] attribute. + [b]Note:[/b] This method is implemented on iOS, BSD, macOS, and Windows. + + + + + + + + Sets file [code]read only[/code] attribute. + [b]Note:[/b] This method is implemented on iOS, BSD, macOS, and Windows. + + + + + + + + Sets file UNIX permissions. + [b]Note:[/b] This method is implemented on iOS, Linux/BSD, and macOS. + + @@ -485,5 +536,41 @@ Uses the [url=https://github.com/google/brotli]brotli[/url] compression method (only decompression is supported). + + Read for owner bit. + + + Write for owner bit. + + + Execute for owner bit. + + + Read for group bit. + + + Write for group bit. + + + Execute for group bit. + + + Read for other bit. + + + Write for other bit. + + + Execute for other bit. + + + Set user id on execution bit. + + + Set group id on execution bit. + + + Restricted deletion (sticky) bit. + diff --git a/doc/classes/Font.xml b/doc/classes/Font.xml index 461e96def478..e87712825d25 100644 --- a/doc/classes/Font.xml +++ b/doc/classes/Font.xml @@ -149,12 +149,6 @@ Returns number of faces in the TrueType / OpenType collection. - - - - Returns array of fallback [Font]s. - - @@ -335,12 +329,11 @@ Sets LRU cache capacity for [code]draw_*[/code] methods. - - - - - Sets array of fallback [Font]s. - - + + + 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. + + diff --git a/doc/classes/FontFile.xml b/doc/classes/FontFile.xml index ba4761e900f2..86d1cb552a74 100644 --- a/doc/classes/FontFile.xml +++ b/doc/classes/FontFile.xml @@ -568,9 +568,6 @@ Contents of the dynamic font source file. - - Array of fallback [Font]s. - Font size, used only for the bitmap fonts. diff --git a/doc/classes/FontVariation.xml b/doc/classes/FontVariation.xml index eb72b4020c30..f02fda31b96f 100644 --- a/doc/classes/FontVariation.xml +++ b/doc/classes/FontVariation.xml @@ -46,9 +46,6 @@ Base font used to create a variation. If not set, default [Theme] font is used. - - 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. - A set of OpenType feature tags. More info: [url=https://docs.microsoft.com/en-us/typography/opentype/spec/featuretags]OpenType feature tags[/url]. diff --git a/doc/classes/Geometry3D.xml b/doc/classes/Geometry3D.xml index 014ebd224802..85b8965faf1e 100644 --- a/doc/classes/Geometry3D.xml +++ b/doc/classes/Geometry3D.xml @@ -73,6 +73,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]). + + + + + + + + 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] + + diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml index 81954dd7de50..14869909959b 100644 --- a/doc/classes/Image.xml +++ b/doc/classes/Image.xml @@ -312,6 +312,13 @@ [b]Note:[/b] Godot's BMP module doesn't support 16-bit per pixel images. Only 1-bit, 4-bit, 8-bit, 24-bit, and 32-bit per pixel images are supported. + + + + + Loads an image from the binary contents of a DDS file. + + diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index c2a20827d2c2..6d907bbb28e9 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. + + + + + 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. + + diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml index cea0adbe0f73..f5cb2e32a28e 100644 --- a/doc/classes/LineEdit.xml +++ b/doc/classes/LineEdit.xml @@ -210,6 +210,9 @@ If [code]true[/code], the selected text will be deselected when focus is lost. + + If [code]true[/code], allow drag and drop of selected text. + If [code]true[/code], control characters are displayed. diff --git a/doc/classes/MenuButton.xml b/doc/classes/MenuButton.xml index 8af233427c7c..e3a707ba729c 100644 --- a/doc/classes/MenuButton.xml +++ b/doc/classes/MenuButton.xml @@ -69,7 +69,7 @@ Text [Color] used when the [MenuButton] is being pressed. - + The horizontal space between [MenuButton]'s icon and text. Negative values will be treated as [code]0[/code] when used. diff --git a/doc/classes/Mesh.xml b/doc/classes/Mesh.xml index 6c4242b92d2b..6448ace9baa3 100644 --- a/doc/classes/Mesh.xml +++ b/doc/classes/Mesh.xml @@ -253,7 +253,7 @@ [PackedFloat32Array] or [PackedFloat64Array] of bone weights in the range [code]0.0[/code] to [code]1.0[/code] (inclusive). Contains either 4 or 8 numbers per vertex depending on the presence of the [constant ARRAY_FLAG_USE_8_BONE_WEIGHTS] flag. - [PackedInt32Array] of integers used as indices referencing vertices, colors, normals, tangents, and textures. All of those arrays must have the same number of elements as the vertex array. No index can be beyond the vertex array size. When this index array is present, it puts the function into "index mode," where the index selects the *i*'th vertex, normal, tangent, color, UV, etc. This means if you want to have different normals or colors along an edge, you have to duplicate the vertices. + [PackedInt32Array] of integers used as indices referencing vertices, colors, normals, tangents, and textures. All of those arrays must have the same number of elements as the vertex array. No index can be beyond the vertex array size. When this index array is present, it puts the function into "index mode," where the index selects the [i]i[/i]'th vertex, normal, tangent, color, UV, etc. This means if you want to have different normals or colors along an edge, you have to duplicate the vertices. For triangles, the index array is interpreted as triples, referring to the vertices of each triangle. For lines, the index array is in pairs indicating the start and end of each line. diff --git a/doc/classes/NavigationMeshGenerator.xml b/doc/classes/NavigationMeshGenerator.xml index dc9b7dc3b2bf..0997354aff05 100644 --- a/doc/classes/NavigationMeshGenerator.xml +++ b/doc/classes/NavigationMeshGenerator.xml @@ -1,5 +1,5 @@ - + Helper class for creating and clearing navigation meshes. diff --git a/doc/classes/NavigationMeshSourceGeometryData3D.xml b/doc/classes/NavigationMeshSourceGeometryData3D.xml index bf57baeb449c..dc446dcaaab0 100644 --- a/doc/classes/NavigationMeshSourceGeometryData3D.xml +++ b/doc/classes/NavigationMeshSourceGeometryData3D.xml @@ -54,7 +54,7 @@ - Returns [b]true[/b] when parsed source geometry data exists. + Returns [code]true[/code] when parsed source geometry data exists. diff --git a/doc/classes/NavigationServer2D.xml b/doc/classes/NavigationServer2D.xml index 32215316d13f..a52c9abefdb6 100644 --- a/doc/classes/NavigationServer2D.xml +++ b/doc/classes/NavigationServer2D.xml @@ -66,7 +66,7 @@ - If [param enabled] is [code]true[/code] the specified [param agent] uses avoidance. + If [param enabled] is [code]true[/code], the specified [param agent] uses avoidance. @@ -283,7 +283,7 @@ - If [param enabled] is [code]true[/code] the specified [param link] will contribute to its current navigation map. + If [param enabled] is [code]true[/code], the specified [param link] will contribute to its current navigation map. @@ -485,7 +485,7 @@ - Set the navigation [param map] edge connection use. If [param enabled] the navigation map allows navigation regions to use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin. + Set the navigation [param map] edge connection use. If [param enabled] is [code]true[/code], the navigation map allows navigation regions to use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin. @@ -520,7 +520,7 @@ - If [param enabled] the provided [param obstacle] affects avoidance using agents. + If [param enabled] is [code]true[/code], the provided [param obstacle] affects avoidance using agents. @@ -744,7 +744,7 @@ - If [param enabled] the navigation [param region] will use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin. + If [param enabled] is [code]true[/code], the navigation [param region] will use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin. diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml index ebde6fdf7b45..86c605b8a302 100644 --- a/doc/classes/NavigationServer3D.xml +++ b/doc/classes/NavigationServer3D.xml @@ -73,7 +73,7 @@ - If [param enabled] the provided [param agent] calculates avoidance. + If [param enabled] is [code]true[/code], the provided [param agent] calculates avoidance. @@ -324,7 +324,7 @@ - If [param enabled] is [code]true[/code] the specified [param link] will contribute to its current navigation map. + If [param enabled] is [code]true[/code], the specified [param link] will contribute to its current navigation map. @@ -574,7 +574,7 @@ - Set the navigation [param map] edge connection use. If [param enabled] the navigation map allows navigation regions to use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin. + Set the navigation [param map] edge connection use. If [param enabled] is [code]true[/code], the navigation map allows navigation regions to use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin. @@ -616,7 +616,7 @@ - If [param enabled] the provided [param obstacle] affects avoidance using agents. + If [param enabled] is [code]true[/code], the provided [param obstacle] affects avoidance using agents. @@ -813,7 +813,7 @@ - If [param enabled] is [code]true[/code] the specified [param region] will contribute to its current navigation map. + If [param enabled] is [code]true[/code], the specified [param region] will contribute to its current navigation map. @@ -877,7 +877,7 @@ - If [param enabled] the navigation [param region] will use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin. + If [param enabled] is [code]true[/code], the navigation [param region] will use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin. diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index ce02c3e51aae..49ab3918bb09 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -1052,10 +1052,10 @@ Notification received from the OS when the screen's DPI has been changed. Only implemented on macOS. - Notification received when the mouse enters the viewport. + Notification received when the mouse cursor enters the [Viewport]'s visible area, that is not occluded behind other [Control]s or [Window]s, provided its [member Viewport.gui_disable_input] is [code]false[/code] and regardless if it's currently focused or not. - Notification received when the mouse leaves the viewport. + Notification received when the mouse cursor leaves the [Viewport]'s visible area, that is not occluded behind other [Control]s or [Window]s, provided its [member Viewport.gui_disable_input] is [code]false[/code] and regardless if it's currently focused or not. Notification received from the OS when the application is exceeding its allocated memory. diff --git a/doc/classes/Node3D.xml b/doc/classes/Node3D.xml index b5ead07a104d..2606a2b320cf 100644 --- a/doc/classes/Node3D.xml +++ b/doc/classes/Node3D.xml @@ -269,7 +269,7 @@ - Direct access to the 3x3 basis of the [Transform3D] property. + Direct access to the 3x3 basis of the [member transform] property. 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 77a5cfa6c68c..a56e80ec1f33 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] diff --git a/doc/classes/OptionButton.xml b/doc/classes/OptionButton.xml index 659f08d49adf..7737754fc680 100644 --- a/doc/classes/OptionButton.xml +++ b/doc/classes/OptionButton.xml @@ -262,7 +262,7 @@ The horizontal space between the arrow icon and the right edge of the button. - + The horizontal space between [OptionButton]'s icon and text. Negative values will be treated as [code]0[/code] when used. diff --git a/doc/classes/Performance.xml b/doc/classes/Performance.xml index 377dbba9fc9a..4d01fe17606a 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] - 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. 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/PhysicsDirectSpaceState3D.xml b/doc/classes/PhysicsDirectSpaceState3D.xml index 17324e49c61d..b00dc4236d42 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/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 02f001edc9ce..40a475851dfd 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. + + The project's human-readable version identifier. This should always be set to a non-empty string, as some exporters rely on this value being defined. + 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]. @@ -430,6 +433,12 @@ When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an identifier contains characters that can be confused with something else, like when mixing different alphabets. + + When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an identifier declared in the nested block has the same name as an identifier declared below in the parent block. + + + When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an identifier that will be shadowed below in the block is used. + When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a constant is used as a function. @@ -800,8 +809,8 @@ 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. @@ -2614,7 +2623,7 @@ 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. @@ -2644,6 +2653,9 @@ The default compression factor for lossless WebP. Decompression speed is mostly unaffected by the compression factor. Supported values are 0 to 100. + + 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). + 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]. diff --git a/doc/classes/RDPipelineMultisampleState.xml b/doc/classes/RDPipelineMultisampleState.xml index 8f43b3a2f700..94b8e3d1379b 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. - 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. diff --git a/doc/classes/RDPipelineRasterizationState.xml b/doc/classes/RDPipelineRasterizationState.xml index 9da66f3d0dd3..e97113cce451 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. - 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. 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 83476a6d48f4..4c4db36aca90 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]). + + + + 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]. + + diff --git a/doc/classes/Rect2.xml b/doc/classes/Rect2.xml index 1b6e2bbeb256..4f17e3cf2de6 100644 --- a/doc/classes/Rect2.xml +++ b/doc/classes/Rect2.xml @@ -4,10 +4,10 @@ A 2D axis-aligned bounding box using floating-point coordinates. - [Rect2] consists of a position, a size, and several utility functions. It is typically used for fast overlap tests. - It uses floating-point coordinates. If you need integer coordinates, use [Rect2i] instead. - The 3D counterpart to [Rect2] is [AABB]. - Negative values for [member size] are not supported and will not work for most methods. Use [method abs] to get a Rect2 with a positive size. + The [Rect2] built-in [Variant] type represents an axis-aligned rectangle in a 2D space. It is defined by its [member position] and [member size], which are [Vector2]. It is frequently used for fast overlap tests (see [method intersects]). Although [Rect2] itself is axis-aligned, it can be combined with [Transform2D] to represent a rotated or skewed rectangle. + For integer coordinates, use [Rect2i]. The 3D equivalent to [Rect2] is [AABB]. + [b]Note:[/b] Negative values for [member size] are not supported. With negative size, most [Rect2] methods do not work correctly. Use [method abs] to get an equivalent [Rect2] with a non-negative size. + [b]Note:[/b] In a boolean context, a [Rect2] evaluates to [code]false[/code] if both [member position] and [member size] are zero (equal to [constant Vector2.ZERO]). Otherwise, it always evaluates to [code]true[/code]. $DOCS_URL/tutorials/math/index.html @@ -18,7 +18,7 @@ - Constructs a default-initialized [Rect2] with default (zero) values of [member position] and [member size]. + Constructs a [Rect2] with its [member position] and [member size] set to [constant Vector2.ZERO]. @@ -40,7 +40,7 @@ - Constructs a [Rect2] by position and size. + Constructs a [Rect2] by [param position] and [param size]. @@ -50,7 +50,7 @@ - Constructs a [Rect2] by x, y, width, and height. + Constructs a [Rect2] by setting its [member position] to ([param x], [param y]), and its [member size] to ([param width], [param height]). @@ -58,34 +58,44 @@ - Returns a [Rect2] with equivalent position and area, modified so that the top-left corner is the origin and [code]width[/code] and [code]height[/code] are positive. + Returns a [Rect2] equivalent to this rectangle, with its width and height modified to be non-negative values, and with its [member position] being the top-left corner of the rectangle. + [codeblocks] + [gdscript] + var rect = Rect2(25, 25, -100, -50) + var absolute = rect.abs() # absolute is Rect2(-75, -25, 100, 50) + [/gdscript] + [csharp] + var rect = new Rect2(25, 25, -100, -50); + var absolute = rect.Abs(); // absolute is Rect2(-75, -25, 100, 50) + [/csharp] + [/codeblocks] + [b]Note:[/b] It's recommended to use this method when [member size] is negative, as most other methods in Godot assume that the [member position] is the top-left corner, and the [member end] is the bottom-right corner. - Returns [code]true[/code] if this [Rect2] completely encloses another one. + Returns [code]true[/code] if this rectangle [i]completely[/i] encloses the [param b] rectangle. - Returns a copy of this [Rect2] expanded to include a given point. - [b]Example:[/b] + Returns a copy of this rectangle expanded to align the edges with the given [param to] point, if necessary. [codeblocks] [gdscript] - # position (-3, 2), size (1, 1) - var rect = Rect2(Vector2(-3, 2), Vector2(1, 1)) - # position (-3, -1), size (3, 4), so we fit both rect and Vector2(0, -1) - var rect2 = rect.expand(Vector2(0, -1)) + var rect = Rect2(0, 0, 5, 2) + + rect = rect.expand(Vector2(10, 0)) # rect is Rect2(0, 0, 10, 2) + rect = rect.expand(Vector2(-5, 5)) # rect is Rect2(-5, 0, 10, 5) [/gdscript] [csharp] - // position (-3, 2), size (1, 1) - var rect = new Rect2(new Vector2(-3, 2), new Vector2(1, 1)); - // position (-3, -1), size (3, 4), so we fit both rect and Vector2(0, -1) - var rect2 = rect.Expand(new Vector2(0, -1)); + var rect = new Rect2(0, 0, 5, 2); + + rect = rect.Expand(new Vector2(10, 0)); // rect is Rect2(0, 0, 10, 2) + rect = rect.Expand(new Vector2(-5, 5)); // rect is Rect2(-5, 0, 10, 5) [/csharp] [/codeblocks] @@ -93,20 +103,30 @@ - Returns the area of the [Rect2]. See also [method has_area]. + Returns the rectangle's area. This is equivalent to [code]size.x * size.y[/code]. See also [method has_area]. - Returns the center of the [Rect2], which is equal to [member position] + ([member size] / 2). + Returns the center point of the rectangle. This is the same as [code]position + (size / 2.0)[/code]. - Returns a copy of the [Rect2] grown by the specified [param amount] on all sides. + Returns a copy of this rectangle extended on all sides by the given [param amount]. A negative [param amount] shrinks the rectangle instead. See also [method grow_individual] and [method grow_side]. + [codeblocks] + [gdscript] + var a = Rect2(4, 4, 8, 8).grow(4) # a is Rect2(0, 0, 16, 16) + var b = Rect2(0, 0, 8, 4).grow(2) # b is Rect2(-2, -2, 12, 8) + [/gdscript] + [csharp] + var a = new Rect2(4, 4, 8, 8).Grow(4); // a is Rect2(0, 0, 16, 16) + var b = new Rect2(0, 0, 8, 4).Grow(2); // b is Rect2(-2, -2, 12, 8) + [/csharp] + [/codeblocks] @@ -116,7 +136,7 @@ - Returns a copy of the [Rect2] grown by the specified amount on each side individually. + Returns a copy of this rectangle with its [param left], [param top], [param right], and [param bottom] sides extended by the given amounts. Negative values shrink the sides, instead. See also [method grow] and [method grow_side]. @@ -124,29 +144,43 @@ - Returns a copy of the [Rect2] grown by the specified [param amount] on the specified [enum Side]. + Returns a copy of this rectangle with its [param side] extended by the given [param amount] (see [enum Side] constants). A negative [param amount] shrinks the rectangle, instead. See also [method grow] and [method grow_individual]. - Returns [code]true[/code] if the [Rect2] has area, and [code]false[/code] if the [Rect2] is linear, empty, or has a negative [member size]. See also [method get_area]. + Returns [code]true[/code] if this rectangle has positive width and height. See also [method get_area]. - Returns [code]true[/code] if the [Rect2] contains a point. By convention, the right and bottom edges of the [Rect2] are considered exclusive, so points on these edges are [b]not[/b] included. - [b]Note:[/b] This method is not reliable for [Rect2] with a [i]negative size[/i]. Use [method abs] to get a positive sized equivalent rectangle to check for contained points. + Returns [code]true[/code] if the rectangle contains the given [param point]. By convention, points on the right and bottom edges are [b]not[/b] included. + [b]Note:[/b] This method is not reliable for [Rect2] with a [i]negative[/i] [member size]. Use [method abs] first to get a valid rectangle. - Returns the intersection of this [Rect2] and [param b]. - If the rectangles do not intersect, an empty [Rect2] is returned. + Returns the intersection between this rectangle and [param b]. If the rectangles do not intersect, returns an empty [Rect2]. + [codeblocks] + [gdscript] + var rect1 = Rect2(0, 0, 5, 10) + var rect2 = Rect2(2, 0, 8, 4) + + var a = rect1.intersection(rect2) # a is Rect2(2, 0, 3, 4) + [/gdscript] + [csharp] + var rect1 = new Rect2(0, 0, 5, 10); + var rect2 = new Rect2(2, 0, 8, 4); + + var a = rect1.Intersection(rect2); // a is Rect2(2, 0, 3, 4) + [/csharp] + [/codeblocks] + [b]Note:[/b] If you only need to know whether two rectangles are overlapping, use [method intersects], instead. @@ -154,41 +188,40 @@ - Returns [code]true[/code] if the [Rect2] overlaps with [param b] (i.e. they have at least one point in common). - If [param include_borders] is [code]true[/code], they will also be considered overlapping if their borders touch, even without intersection. + Returns [code]true[/code] if this rectangle overlaps with the [param b] rectangle. The edges of both rectangles are excluded, unless [param include_borders] is [code]true[/code]. - Returns [code]true[/code] if this [Rect2] and [param rect] are approximately equal, by calling [code]is_equal_approx[/code] on each component. + Returns [code]true[/code] if this rectangle and [param rect] are approximately equal, by calling [method Vector2.is_equal_approx] on the [member position] and the [member size]. - Returns [code]true[/code] if this [Rect2] is finite, by calling [method @GlobalScope.is_finite] on each component. + Returns [code]true[/code] if this rectangle's values are finite, by calling [method Vector2.is_finite] on the [member position] and the [member size]. - Returns a larger [Rect2] that contains this [Rect2] and [param b]. + Returns a [Rect2] that encloses both this rectangle and [param b] around the edges. See also [method encloses]. - Ending corner. This is calculated as [code]position + size[/code]. Setting this value will change the size. + The ending point. This is usually the bottom-right corner of the rectangle, and is equivalent to [code]position + size[/code]. Setting this point affects the [member size]. - Beginning corner. Typically has values lower than [member end]. + The origin point. This is usually the top-left corner of the rectangle. - Size from [member position] to [member end]. Typically, all components are positive. - If the size is negative, you can use [method abs] to fix it. + The rectangle's width and height, starting from [member position]. Setting this value also affects the [member end] point. + [b]Note:[/b] It's recommended setting the width and height to non-negative values, as most methods in Godot assume that the [member position] is the top-left corner, and the [member end] is the bottom-right corner. To get an equivalent rectangle with non-negative size, use [method abs]. @@ -196,7 +229,7 @@ - Returns [code]true[/code] if the rectangles are not equal. + Returns [code]true[/code] if the [member position] or [member size] of both rectangles are not equal. [b]Note:[/b] Due to floating-point precision errors, consider using [method is_equal_approx] instead, which is more reliable. @@ -211,7 +244,7 @@ - Returns [code]true[/code] if the rectangles are exactly equal. + Returns [code]true[/code] if both [member position] and [member size] of the rectangles are exactly equal, respectively. [b]Note:[/b] Due to floating-point precision errors, consider using [method is_equal_approx] instead, which is more reliable. diff --git a/doc/classes/Rect2i.xml b/doc/classes/Rect2i.xml index 5bb1a83cdc85..c021a1be26f4 100644 --- a/doc/classes/Rect2i.xml +++ b/doc/classes/Rect2i.xml @@ -4,9 +4,10 @@ A 2D axis-aligned bounding box using integer coordinates. - [Rect2i] consists of a position, a size, and several utility functions. It is typically used for fast overlap tests. - It uses integer coordinates. If you need floating-point coordinates, use [Rect2] instead. - Negative values for [member size] are not supported and will not work for most methods. Use [method abs] to get a Rect2i with a positive size. + The [Rect2i] built-in [Variant] type represents an axis-aligned rectangle in a 2D space, using integer coordinates. It is defined by its [member position] and [member size], which are [Vector2i]. Because it does not rotate, it is frequently used for fast overlap tests (see [method intersects]). + For floating-point coordinates, see [Rect2]. + [b]Note:[/b] Negative values for [member size] are not supported. With negative size, most [Rect2i] methods do not work correctly. Use [method abs] to get an equivalent [Rect2i] with a non-negative size. + [b]Note:[/b] In a boolean context, a [Rect2i] evaluates to [code]false[/code] if both [member position] and [member size] are zero (equal to [constant Vector2i.ZERO]). Otherwise, it always evaluates to [code]true[/code]. $DOCS_URL/tutorials/math/index.html @@ -16,7 +17,7 @@ - Constructs a default-initialized [Rect2i] with default (zero) values of [member position] and [member size]. + Constructs a [Rect2i] with its [member position] and [member size] set to [constant Vector2i.ZERO]. @@ -30,7 +31,7 @@ - Constructs a new [Rect2i] from [Rect2]. The floating point coordinates will be truncated. + Constructs a [Rect2i] from a [Rect2]. The floating-point coordinates are truncated. @@ -38,7 +39,7 @@ - Constructs a [Rect2i] by position and size. + Constructs a [Rect2i] by [param position] and [param size]. @@ -48,7 +49,7 @@ - Constructs a [Rect2i] by x, y, width, and height. + Constructs a [Rect2i] by setting its [member position] to ([param x], [param y]), and its [member size] to ([param width], [param height]). @@ -56,7 +57,18 @@ - Returns a [Rect2i] with equivalent position and area, modified so that the top-left corner is the origin and [code]width[/code] and [code]height[/code] are positive. + Returns a [Rect2i] equivalent to this rectangle, with its width and height modified to be non-negative values, and with its [member position] being the top-left corner of the rectangle. + [codeblocks] + [gdscript] + var rect = Rect2i(25, 25, -100, -50) + var absolute = rect.abs() # absolute is Rect2i(-75, -25, 100, 50) + [/gdscript] + [csharp] + var rect = new Rect2I(25, 25, -100, -50); + var absolute = rect.Abs(); // absolute is Rect2I(-75, -25, 100, 50) + [/csharp] + [/codeblocks] + [b]Note:[/b] It's recommended to use this method when [member size] is negative, as most other methods in Godot assume that the [member position] is the top-left corner, and the [member end] is the bottom-right corner. @@ -70,19 +82,19 @@ - Returns a copy of this [Rect2i] expanded so that the borders align with the given point. + Returns a copy of this rectangle expanded to align the edges with the given [param to] point, if necessary. [codeblocks] [gdscript] - # position (-3, 2), size (1, 1) - var rect = Rect2i(Vector2i(-3, 2), Vector2i(1, 1)) - # position (-3, -1), size (3, 4), so we fit both rect and Vector2i(0, -1) - var rect2 = rect.expand(Vector2i(0, -1)) + var rect = Rect2i(0, 0, 5, 2) + + rect = rect.expand(Vector2i(10, 0)) # rect is Rect2i(0, 0, 10, 2) + rect = rect.expand(Vector2i(-5, 5)) # rect is Rect2i(-5, 0, 10, 5) [/gdscript] [csharp] - // position (-3, 2), size (1, 1) - var rect = new Rect2I(new Vector2I(-3, 2), new Vector2I(1, 1)); - // position (-3, -1), size (3, 4), so we fit both rect and Vector2I(0, -1) - var rect2 = rect.Expand(new Vector2I(0, -1)); + var rect = new Rect2I(0, 0, 5, 2); + + rect = rect.Expand(new Vector2I(10, 0)); // rect is Rect2I(0, 0, 10, 2) + rect = rect.Expand(new Vector2I(-5, 5)); // rect is Rect2I(-5, 0, 10, 5) [/csharp] [/codeblocks] @@ -90,21 +102,31 @@ - Returns the area of the [Rect2i]. See also [method has_area]. + Returns the rectangle's area. This is equivalent to [code]size.x * size.y[/code]. See also [method has_area]. - Returns the center of the [Rect2i], which is equal to [member position] + ([member size] / 2). - If [member size] is an odd number, the returned center value will be rounded towards [member position]. + Returns the center point of the rectangle. This is the same as [code]position + (size / 2)[/code]. + [b]Note:[/b] If the [member size] is odd, the result will be rounded towards [member position]. - Returns a copy of the [Rect2i] grown by the specified [param amount] on all sides. + Returns a copy of this rectangle extended on all sides by the given [param amount]. A negative [param amount] shrinks the rectangle instead. See also [method grow_individual] and [method grow_side]. + [codeblocks] + [gdscript] + var a = Rect2i(4, 4, 8, 8).grow(4) # a is Rect2i(0, 0, 16, 16) + var b = Rect2i(0, 0, 8, 4).grow(2) # b is Rect2i(-2, -2, 12, 8) + [/gdscript] + [csharp] + var a = new Rect2I(4, 4, 8, 8).Grow(4); // a is Rect2I(0, 0, 16, 16) + var b = new Rect2I(0, 0, 8, 4).Grow(2); // b is Rect2I(-2, -2, 12, 8) + [/csharp] + [/codeblocks] @@ -114,7 +136,7 @@ - Returns a copy of the [Rect2i] grown by the specified amount on each side individually. + Returns a copy of this rectangle with its [param left], [param top], [param right], and [param bottom] sides extended by the given amounts. Negative values shrink the sides, instead. See also [method grow] and [method grow_side]. @@ -122,56 +144,70 @@ - Returns a copy of the [Rect2i] grown by the specified [param amount] on the specified [enum Side]. + Returns a copy of this rectangle with its [param side] extended by the given [param amount] (see [enum Side] constants). A negative [param amount] shrinks the rectangle, instead. See also [method grow] and [method grow_individual]. - Returns [code]true[/code] if the [Rect2i] has area, and [code]false[/code] if the [Rect2i] is linear, empty, or has a negative [member size]. See also [method get_area]. + Returns [code]true[/code] if this rectangle has positive width and height. See also [method get_area]. - Returns [code]true[/code] if the [Rect2i] contains a point. By convention, the right and bottom edges of the [Rect2i] are considered exclusive, so points on these edges are [b]not[/b] included. - [b]Note:[/b] This method is not reliable for [Rect2i] with a [i]negative size[/i]. Use [method abs] to get a positive sized equivalent rectangle to check for contained points. + Returns [code]true[/code] if the rectangle contains the given [param point]. By convention, points on the right and bottom edges are [b]not[/b] included. + [b]Note:[/b] This method is not reliable for [Rect2i] with a [i]negative[/i] [member size]. Use [method abs] first to get a valid rectangle. - Returns the intersection of this [Rect2i] and [param b]. - If the rectangles do not intersect, an empty [Rect2i] is returned. + Returns the intersection between this rectangle and [param b]. If the rectangles do not intersect, returns an empty [Rect2i]. + [codeblocks] + [gdscript] + var a = Rect2i(0, 0, 5, 10) + var b = Rect2i(2, 0, 8, 4) + + var c = a.intersection(b) # c is Rect2i(2, 0, 3, 4) + [/gdscript] + [csharp] + var a = new Rect2I(0, 0, 5, 10); + var b = new Rect2I(2, 0, 8, 4); + + var c = rect1.Intersection(rect2); // c is Rect2I(2, 0, 3, 4) + [/csharp] + [/codeblocks] + [b]Note:[/b] If you only need to know whether two rectangles are overlapping, use [method intersects], instead. - Returns [code]true[/code] if the [Rect2i] overlaps with [param b] (i.e. they have at least one point in common). + Returns [code]true[/code] if this rectangle overlaps with the [param b] rectangle. The edges of both rectangles are excluded. - Returns a larger [Rect2i] that contains this [Rect2i] and [param b]. + Returns a [Rect2i] that encloses both this rectangle and [param b] around the edges. See also [method encloses]. - Ending corner. This is calculated as [code]position + size[/code]. Setting this value will change the size. + The ending point. This is usually the bottom-right corner of the rectangle, and is equivalent to [code]position + size[/code]. Setting this point affects the [member size]. - Beginning corner. Typically has values lower than [member end]. + The origin point. This is usually the top-left corner of the rectangle. - Size from [member position] to [member end]. Typically, all components are positive. - If the size is negative, you can use [method abs] to fix it. + The rectangle's width and height, starting from [member position]. Setting this value also affects the [member end] point. + [b]Note:[/b] It's recommended setting the width and height to non-negative values, as most methods in Godot assume that the [member position] is the top-left corner, and the [member end] is the bottom-right corner. To get an equivalent rectangle with non-negative size, use [method abs]. @@ -179,14 +215,14 @@ - Returns [code]true[/code] if the rectangles are not equal. + Returns [code]true[/code] if the [member position] or [member size] of both rectangles are not equal. - Returns [code]true[/code] if the rectangles are equal. + Returns [code]true[/code] if both [member position] and [member size] of the rectangles are equal, respectively. diff --git a/doc/classes/RenderSceneBuffersRD.xml b/doc/classes/RenderSceneBuffersRD.xml index 80a644ad2658..d40d835d269c 100644 --- a/doc/classes/RenderSceneBuffersRD.xml +++ b/doc/classes/RenderSceneBuffersRD.xml @@ -5,7 +5,7 @@ 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. @@ -133,7 +133,7 @@ - Returns [b]true[/b] if TAA is enabled. + Returns [code]true[/code] if TAA is enabled. @@ -160,7 +160,7 @@ - Returns [b]true[/b] if a cached texture exists for this name. + Returns [code]true[/code] if a cached texture exists for this name. diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml index 3205a331683c..14f6235d08a3 100644 --- a/doc/classes/RenderingDevice.xml +++ b/doc/classes/RenderingDevice.xml @@ -16,8 +16,8 @@ - - + + Puts a memory barrier in place. This is used for synchronization to avoid data races. See also [method full_barrier], which may be useful for debugging. @@ -27,7 +27,7 @@ - + @@ -46,7 +46,7 @@ - + @@ -114,7 +114,7 @@ - + Finishes a list of compute commands created with the [code]compute_*[/code] methods. @@ -296,7 +296,7 @@ - + Finishes a list of raster drawing commands created with the [code]draw_*[/code] methods. @@ -619,6 +619,7 @@ + Creates a new shader instance from a binary compiled shader. It can be accessed with the RID that is returned. Once finished with your RID, you will want to free the RID using the RenderingDevice's [method free_rid] method. See also [method shader_compile_binary_from_spirv] and [method shader_create_from_spirv]. @@ -633,6 +634,12 @@ Once finished with your RID, you will want to free the RID using the RenderingDevice's [method free_rid] method. See also [method shader_compile_spirv_from_source] and [method shader_create_from_bytecode]. + + + + Create a placeholder RID by allocating an RID without initializing it for use in [method shader_create_from_bytecode]. This allows you to create an RID for a shader and pass it around, but defer compiling the shader to a later time. + + @@ -682,7 +689,7 @@ - + Clears the specified [param texture] by replacing all of its pixels with the specified [param color]. [param base_mipmap] and [param mipmap_count] determine which mipmaps of the texture are affected by this clear operation, while [param base_layer] and [param layer_count] determine which layers of a 3D texture (or texture array) are affected by this clear operation. For 2D textures (which only have one layer by design), [param base_layer] and [param layer_count] must both be [code]0[/code]. [b]Note:[/b] [param texture] can't be cleared while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to [constant FINAL_ACTION_CONTINUE]) to clear this texture. @@ -699,7 +706,7 @@ - + Copies the [param from_texture] to [param to_texture] with the specified [param from_pos], [param to_pos] and [param size] coordinates. The Z axis of the [param from_pos], [param to_pos] and [param size] must be [code]0[/code] for 2-dimensional textures. Source and destination mipmaps/layers must also be specified, with these parameters being [code]0[/code] for textures without mipmaps or single-layer textures. Returns [constant @GlobalScope.OK] if the texture copy was successful or [constant @GlobalScope.ERR_INVALID_PARAMETER] otherwise. [b]Note:[/b] [param from_texture] texture can't be copied while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to [constant FINAL_ACTION_CONTINUE]) to copy this texture. @@ -793,7 +800,7 @@ - + Resolves the [param from_texture] texture onto [param to_texture] with multisample antialiasing enabled. This must be used when rendering a framebuffer for MSAA to work. Returns [constant @GlobalScope.OK] if successful, [constant @GlobalScope.ERR_INVALID_PARAMETER] otherwise. [b]Note:[/b] [param from_texture] and [param to_texture] textures must have the same dimension, format and type (color or depth). @@ -810,7 +817,7 @@ - + Updates texture data with new data, replacing the previous data in place. The updated texture data must have the same dimensions and format. For 2D textures (which only have one layer), [param layer] must be [code]0[/code]. Returns [constant @GlobalScope.OK] if the update was successful, [constant @GlobalScope.ERR_INVALID_PARAMETER] otherwise. [b]Note:[/b] Updating textures is forbidden during creation of a draw or compute list. @@ -1590,22 +1597,22 @@ Vertex shader barrier mask. - + Fragment shader barrier mask. - + Compute barrier mask. - + Transfer barrier mask. - + Raster barrier mask (vertex and fragment). Equivalent to [code]BARRIER_MASK_VERTEX | BARRIER_MASK_FRAGMENT[/code]. - - Barrier mask for all types (raster, compute, transfer). Equivalent to [code]BARRIER_MASK_RASTER | BARRIER_MASK_COMPUTE | BARRIER_MASK_TRANSFER[/code]. + + Barrier mask for all types (vertex, fragment, compute, transfer). - + No barrier for any type. @@ -1779,7 +1786,7 @@ - Sampler uniform. TODO: Difference between sampler and texture uniform + Sampler uniform. Sampler uniform with a texture. @@ -1788,16 +1795,16 @@ Texture uniform. - Image uniform. TODO: Difference between texture and image uniform + Image uniform. - Texture buffer uniform. TODO: Difference between texture and texture buffe uniformr + Texture buffer uniform. - Sampler uniform with a texture buffer. TODO: Difference between texture and texture buffer uniform + Sampler uniform with a texture buffer. - Image buffer uniform. TODO: Difference between texture and image uniforms + Image buffer uniform. Uniform buffer uniform. diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 64d54b3ba0b9..bb4a0f8a1622 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -27,6 +27,13 @@ Bakes the material data of the Mesh passed in the [param base] parameter with optional [param material_overrides] to a set of [Image]s of size [param image_size]. Returns an array of [Image]s containing material properties as specified in [enum BakeChannels]. + + + + + As the RenderingServer actual logic may run on an separate thread, accessing its internals from the main (or any other) thread will result in errors. To make it easier to run code that can safely access the rendering internals (such as [RenderingDevice] and similar RD classes), push a callable via this function so it will be executed on the render thread. + + @@ -1368,7 +1375,7 @@ - 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. @@ -3749,6 +3756,15 @@ If [code]true[/code], enables debanding on the specified viewport. Equivalent to [member ProjectSettings.rendering/anti_aliasing/quality/use_debanding]. + + + + + + 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. + + diff --git a/doc/classes/ResourceImporterScene.xml b/doc/classes/ResourceImporterScene.xml index b7d42b5a09f4..982f6e56addc 100644 --- a/doc/classes/ResourceImporterScene.xml +++ b/doc/classes/ResourceImporterScene.xml @@ -48,16 +48,16 @@ [b]Note:[/b] Only effective if [member meshes/light_baking] is set to [b]Static Lightmaps[/b]. - If [code]true[/code], [member nodes/root_scale] will be applied on the meshes and animations directly, while keeping the root node's scale to the default [code](1, 1, 1)[/code]. This means that if you add a child node later on within the imported scene, it won't be scaled. If disabled, [member nodes/root_scale] will multiply the scale of the root node instead. + If [code]true[/code], [member nodes/root_scale] will be applied to the descendant nodes, meshes, animations, bones, etc. This means that if you add a child node later on within the imported scene, it won't be scaled. If [code]false[/code], [member nodes/root_scale] will multiply the scale of the root node instead. - - The name of the root node in the imported scene. This is generally not noticeable when instancing the scene in the editor (or drag-and-dropping from the FileSystem dock), as the root node is renamed to match the filename in this case. + + Override for the root node name. If empty, the root node will use what the scene specifies, or the file name if the scene does not specify a root name. - The scale of meshes and animations (if [member nodes/apply_root_scale] is [code]true[/code]), or the scale of the root node in the imported scene (if [member nodes/apply_root_scale] is [code]false[/code]). + The uniform scale to use for the scene root. The default value of [code]1.0[/code] will not perform any rescaling. See [member nodes/apply_root_scale] for details of how this scale is applied. - - The node type to use as a root node. Using node types that inherit from [Node3D] is recommended. Otherwise, you'll lose the ability to position the node directly in the 3D editor. + + Override for the root node type. If empty, the root node will use what the scene specifies, or [Node3D] if the scene does not specify a root type. Using a node type that inherits from [Node3D] is recommended. Otherwise, you'll lose the ability to position the node directly in the 3D editor. If checked, use named [Skin]s for animation. The [MeshInstance3D] node contains 3 properties of relevance here: a skeleton [NodePath] pointing to the [Skeleton3D] node (usually [code]..[/code]), a mesh, and a skin: diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml index e2030f5927ed..b951566707e9 100644 --- a/doc/classes/RichTextLabel.xml +++ b/doc/classes/RichTextLabel.xml @@ -551,6 +551,9 @@ If [code]true[/code], the selected text will be deselected when focus is lost. + + If [code]true[/code], allow drag and drop of selected text. + If [code]true[/code], the label's minimum size will be automatically updated to fit its content, matching the behavior of [Label]. diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml index c0d98ef921d7..7aed63a2fc61 100644 --- a/doc/classes/SceneTree.xml +++ b/doc/classes/SceneTree.xml @@ -225,7 +225,8 @@ For mobile platforms, see [member quit_on_go_back]. - 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. If [code]true[/code], collision shapes will be visible when running the game from the editor for debugging purposes. diff --git a/doc/classes/SoftBody3D.xml b/doc/classes/SoftBody3D.xml index 1d227241adad..b5202bd4e307 100644 --- a/doc/classes/SoftBody3D.xml +++ b/doc/classes/SoftBody3D.xml @@ -107,6 +107,7 @@ + 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). [NodePath] to a [CollisionObject3D] this SoftBody3D should avoid clipping. diff --git a/doc/classes/StyleBox.xml b/doc/classes/StyleBox.xml index 75fbb03dd674..8ba57766eb46 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. - 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. diff --git a/doc/classes/SystemFont.xml b/doc/classes/SystemFont.xml index ea3dd0acd221..239bcc257cc4 100644 --- a/doc/classes/SystemFont.xml +++ b/doc/classes/SystemFont.xml @@ -19,9 +19,6 @@ Font anti-aliasing mode. - - Array of fallback [Font]s. - If set to [code]true[/code], italic or oblique font is preferred. diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml index 4ed831c21319..3eac11792f90 100644 --- a/doc/classes/TileMap.xml +++ b/doc/classes/TileMap.xml @@ -242,7 +242,7 @@ 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. @@ -316,7 +316,7 @@ - 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 b1b772081070..1733ecf592de 100644 --- a/doc/classes/TileMapPattern.xml +++ b/doc/classes/TileMapPattern.xml @@ -71,7 +71,7 @@ - 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]. diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml index 1193855d7c9b..3cba3d7d1162 100644 --- a/doc/classes/TileSet.xml +++ b/doc/classes/TileSet.xml @@ -332,7 +332,7 @@ - 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. diff --git a/doc/classes/Tween.xml b/doc/classes/Tween.xml index f104c5f1075e..7645b249e39e 100644 --- a/doc/classes/Tween.xml +++ b/doc/classes/Tween.xml @@ -366,7 +366,7 @@ [/gdscript] [csharp] Tween tween = CreateTween(); - tween.TweenMethod(Callable.From(() => LookAt(Vector3.Up)), new Vector3(-1.0f, 0.0f, -1.0f), new Vector3(1.0f, 0.0f, -1.0f), 1.0f); // The LookAt() method takes up vector as second argument. + tween.TweenMethod(Callable.From((Vector3 target) => LookAt(target, Vector3.Up)), new Vector3(-1.0f, 0.0f, -1.0f), new Vector3(1.0f, 0.0f, -1.0f), 1.0f); // Use lambdas to bind additional arguments for the call. [/csharp] [/codeblocks] [b]Example:[/b] Setting the text of a [Label], using an intermediate method and after a delay: diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml index 9a5e7ed6f643..98836db157b2 100644 --- a/doc/classes/Viewport.xml +++ b/doc/classes/Viewport.xml @@ -337,7 +337,7 @@ 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. @@ -348,6 +348,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. + + 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. + 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/Window.xml b/doc/classes/Window.xml index 4114a83584bf..82498b9ba474 100644 --- a/doc/classes/Window.xml +++ b/doc/classes/Window.xml @@ -10,6 +10,12 @@ + + + + Virtual method to be implemented by the user. Overrides the value returned by [method get_contents_minimum_size]. + + @@ -92,6 +98,7 @@ 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]. @@ -626,7 +633,6 @@ If [code]true[/code], the [Window] will be considered a popup. Popups are sub-windows that don't show as separate windows in system's window manager's window list and will send close request when anything is clicked outside of them (unless [member exclusive] is enabled). - [b]Note:[/b] This property only works with native windows. The window's position in pixels. @@ -719,12 +725,12 @@ - Emitted when the mouse cursor enters the [Window]'s area, regardless if it's currently focused or not. + Emitted when the mouse cursor enters the [Window]'s visible area, that is not occluded behind other [Control]s or windows, provided its [member Viewport.gui_disable_input] is [code]false[/code] and regardless if it's currently focused or not. - Emitted when the mouse cursor exits the [Window]'s area (including when it's hovered over another window on top of this one). + Emitted when the mouse cursor leaves the [Window]'s visible area, that is not occluded behind other [Control]s or windows, provided its [member Viewport.gui_disable_input] is [code]false[/code] and regardless if it's currently focused or not. diff --git a/doc/classes/XRInterfaceExtension.xml b/doc/classes/XRInterfaceExtension.xml index 9059a5a46553..b933abd18219 100644 --- a/doc/classes/XRInterfaceExtension.xml +++ b/doc/classes/XRInterfaceExtension.xml @@ -101,7 +101,7 @@ - Returns a [Dictionary] with system informationr elated to this interface. + Returns a [Dictionary] with system information related to this interface. diff --git a/doc/classes/bool.xml b/doc/classes/bool.xml index 9e7d1fc965dc..647bd85d3d87 100644 --- a/doc/classes/bool.xml +++ b/doc/classes/bool.xml @@ -4,42 +4,46 @@ A built-in boolean type. - 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.). @@ -47,7 +51,7 @@ - Constructs a default-initialized [bool] set to [code]false[/code]. + Constructs a [bool] set to [code]false[/code]. @@ -61,14 +65,14 @@ - 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]). - 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. @@ -77,7 +81,7 @@ - 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. @@ -91,7 +95,7 @@ - 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. diff --git a/doc/classes/float.xml b/doc/classes/float.xml index 14e30b69a5d3..7d74e3d83252 100644 --- a/doc/classes/float.xml +++ b/doc/classes/float.xml @@ -70,7 +70,7 @@ 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] diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index d8ddfe5c326d..1fe33b7914de 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -1738,7 +1738,7 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ RenderDataGLES3 render_data; { render_data.render_buffers = rb; - render_data.transparent_bg = rb.is_valid() ? rb->is_transparent : false; + render_data.transparent_bg = rb.is_valid() ? rt->is_transparent : false; // Our first camera is used by default render_data.cam_transform = p_camera_data->main_transform; render_data.inv_cam_transform = render_data.cam_transform.affine_inverse(); @@ -1984,6 +1984,7 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ } if (!keep_color) { + clear_color.a = render_data.transparent_bg ? 0.0f : 1.0f; glClearBufferfv(GL_COLOR, 0, clear_color.components); } RENDER_TIMESTAMP("Render Opaque Pass"); @@ -2170,7 +2171,9 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, if (scene_state.current_depth_draw != shader->depth_draw) { switch (shader->depth_draw) { case GLES3::SceneShaderData::DEPTH_DRAW_OPAQUE: { - glDepthMask(p_pass_mode == PASS_MODE_COLOR); + glDepthMask((p_pass_mode == PASS_MODE_COLOR && !GLES3::Config::get_singleton()->use_depth_prepass) || + p_pass_mode == PASS_MODE_DEPTH || + p_pass_mode == PASS_MODE_SHADOW); } break; case GLES3::SceneShaderData::DEPTH_DRAW_ALWAYS: { glDepthMask(GL_TRUE); diff --git a/drivers/gles3/shaders/canvas_uniforms_inc.glsl b/drivers/gles3/shaders/canvas_uniforms_inc.glsl index d53c0fcb2659..21fd4d3d9dd1 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/render_scene_buffers_gles3.cpp b/drivers/gles3/storage/render_scene_buffers_gles3.cpp index 76c0097bab0a..829574cae04a 100644 --- a/drivers/gles3/storage/render_scene_buffers_gles3.cpp +++ b/drivers/gles3/storage/render_scene_buffers_gles3.cpp @@ -38,8 +38,6 @@ RenderSceneBuffersGLES3::~RenderSceneBuffersGLES3() { } void RenderSceneBuffersGLES3::configure(const RenderSceneBuffersConfiguration *p_config) { - GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); - //internal_size.x = p_config->get_internal_size().x; // ignore for now //internal_size.y = p_config->get_internal_size().y; width = p_config->get_target_size().x; @@ -54,10 +52,6 @@ void RenderSceneBuffersGLES3::configure(const RenderSceneBuffersConfiguration *p view_count = p_config->get_view_count(); free_render_buffer_data(); - - GLES3::RenderTarget *rt = texture_storage->get_render_target(render_target); - - is_transparent = rt->is_transparent; } void RenderSceneBuffersGLES3::free_render_buffer_data() { diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.h b/drivers/gles3/storage/render_scene_buffers_gles3.h index 64b95c417c26..aa98797ed1b9 100644 --- a/drivers/gles3/storage/render_scene_buffers_gles3.h +++ b/drivers/gles3/storage/render_scene_buffers_gles3.h @@ -58,8 +58,6 @@ class RenderSceneBuffersGLES3 : public RenderSceneBuffers { //bool use_debanding = false; uint32_t view_count = 1; - bool is_transparent = false; - RID render_target; //built-in textures used for ping pong image processing and blurring diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h index bad2b31a3151..c25dbef288d1 100644 --- a/drivers/gles3/storage/texture_storage.h +++ b/drivers/gles3/storage/texture_storage.h @@ -629,6 +629,8 @@ class TextureStorage : public RendererTextureStorage { 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 fe8c8fa8cc8b..dd4777a19b4f 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 ab94d8911ffc..8ca396af2018 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 a80b7d449e6a..3b15cf60f768 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 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 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 79c4e7363693..2bfac27c4f08 100644 --- a/drivers/unix/file_access_unix.h +++ b/drivers/unix/file_access_unix.h @@ -79,8 +79,13 @@ class FileAccessUnix : public FileAccess { 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 _get_unix_permissions(const String &p_file) override; + virtual Error _set_unix_permissions(const String &p_file, BitField 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 cd18d8483794..1ed6839dd7e7 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -4858,7 +4858,7 @@ Vector RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve return ret; } -RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector &p_shader_binary) { +RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector &p_shader_binary, RID p_placeholder) { const uint8_t *binptr = p_shader_binary.ptr(); uint32_t binsize = p_shader_binary.size(); @@ -5043,17 +5043,24 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector &p_ _THREAD_SAFE_METHOD_ - Shader shader; + RID id; + if (p_placeholder.is_null()) { + id = shader_owner.make_rid(); + } else { + id = p_placeholder; + } - 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 *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; String error_text; @@ -5085,7 +5092,7 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector &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 &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 &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 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 &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,24 +5181,30 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector &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 = shader_owner.make_rid(shader); #ifdef DEV_ENABLED set_resource_name(id, "RID:" + itos(id.get_id())); #endif return id; } +RID RenderingDeviceVulkan::shader_create_placeholder() { + Shader shader; + return shader_owner.make_rid(shader); +} + uint32_t RenderingDeviceVulkan::shader_get_vertex_input_attribute_mask(RID p_shader) { _THREAD_SAFE_METHOD_ diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h index 010f7c9337f9..fd832312acb5 100644 --- a/drivers/vulkan/rendering_device_vulkan.h +++ b/drivers/vulkan/rendering_device_vulkan.h @@ -1135,7 +1135,8 @@ class RenderingDeviceVulkan : public RenderingDevice { virtual String shader_get_binary_cache_key() const; virtual Vector shader_compile_binary_from_spirv(const Vector &p_spirv, const String &p_shader_name = ""); - virtual RID shader_create_from_bytecode(const Vector &p_shader_binary); + virtual RID shader_create_from_bytecode(const Vector &p_shader_binary, RID p_placeholder = RID()); + virtual RID shader_create_placeholder(); virtual uint32_t shader_get_vertex_input_attribute_mask(RID p_shader); diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp index 3a1330b33104..c167caeb7cff 100644 --- a/drivers/vulkan/vulkan_context.cpp +++ b/drivers/vulkan/vulkan_context.cpp @@ -1195,12 +1195,15 @@ Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) { VkQueueFamilyProperties *device_queue_props = (VkQueueFamilyProperties *)malloc(device_queue_family_count * sizeof(VkQueueFamilyProperties)); vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &device_queue_family_count, device_queue_props); for (uint32_t j = 0; j < device_queue_family_count; j++) { - VkBool32 supports; - vkGetPhysicalDeviceSurfaceSupportKHR(physical_devices[i], j, p_surface, &supports); - if (supports && ((device_queue_props[j].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0)) { - present_supported = true; - } else { - continue; + if ((device_queue_props[j].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) { + VkBool32 supports; + err = vkGetPhysicalDeviceSurfaceSupportKHR( + physical_devices[i], j, p_surface, &supports); + if (err == VK_SUCCESS && supports) { + present_supported = true; + } else { + continue; + } } } String name = props.deviceName; @@ -1804,6 +1807,16 @@ Error VulkanContext::_update_swap_chain(Window *window) { err = fpGetPhysicalDeviceSurfaceCapabilitiesKHR(gpu, window->surface, &surfCapabilities); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); + { + VkBool32 supports = VK_FALSE; + err = vkGetPhysicalDeviceSurfaceSupportKHR( + gpu, present_queue_family_index, window->surface, &supports); + ERR_FAIL_COND_V_MSG(err != VK_SUCCESS || supports == false, ERR_CANT_CREATE, + "Window's surface is not supported by device. Did the GPU go offline? Was the window " + "created on another monitor? Check previous errors & try launching with " + "--gpu-validation."); + } + uint32_t presentModeCount; err = fpGetPhysicalDeviceSurfacePresentModesKHR(gpu, window->surface, &presentModeCount, nullptr); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp index eeeb6ee689ef..6c84aee2b724 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(p_udata); uint32_t avail_frames = 0; diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp index 6e69743d4ef1..c1e0c3fb60b2 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 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 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 13c881e562a0..73143009fcf9 100644 --- a/drivers/windows/file_access_windows.h +++ b/drivers/windows/file_access_windows.h @@ -80,8 +80,13 @@ class FileAccessWindows : public FileAccess { 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 _get_unix_permissions(const String &p_file) override; + virtual Error _set_unix_permissions(const String &p_file, BitField 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/code_editor.cpp b/editor/code_editor.cpp index cde74e08548f..6c5c99698db6 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -891,7 +891,7 @@ void CodeTextEditor::_line_col_changed() { sb.append(itos(positional_column + 1).lpad(3)); sb.append(" | "); - sb.append(text_editor->is_indent_using_spaces() ? "Spaces" : "Tabs"); + sb.append(text_editor->is_indent_using_spaces() ? TTR("Spaces", "Indentation") : TTR("Tabs", "Indentation")); line_and_col_txt->set_text(sb.as_string()); diff --git a/editor/debugger/editor_debugger_inspector.cpp b/editor/debugger/editor_debugger_inspector.cpp index 1e9b7c2c609b..b2fd84d2eddf 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(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 2c40f0e12067..afa6aaf395ea 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 df860bab2c00..fed7cb82c914 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 e2601181da81..82e2e98ae535 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/doc_tools.cpp b/editor/doc_tools.cpp index 7929c4b0ca49..e0b344fc1935 100644 --- a/editor/doc_tools.cpp +++ b/editor/doc_tools.cpp @@ -481,6 +481,8 @@ void DocTools::generate(bool p_basic_types) { default_value_valid = true; } } + } else if (name == "EditorSettings") { + // Special case for editor settings, to prevent hardware or OS specific settings to affect the result. } else if (import_option) { default_value = import_options_default[E.name]; default_value_valid = true; diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index c9a95df34e75..7573fcd21e9c 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -1059,6 +1059,7 @@ void EditorHelp::_update_doc() { if (cd.properties[i].is_deprecated) { DEPRECATED_DOC_TAG; } + if (cd.properties[i].is_experimental) { EXPERIMENTAL_DOC_TAG; } @@ -1303,6 +1304,7 @@ void EditorHelp::_update_doc() { if (cd.signals[i].is_deprecated) { DEPRECATED_DOC_TAG; } + if (cd.signals[i].is_experimental) { EXPERIMENTAL_DOC_TAG; } @@ -1363,6 +1365,7 @@ void EditorHelp::_update_doc() { enum_line[E.key] = class_desc->get_paragraph_count() - 2; _push_code_font(); + class_desc->push_color(theme_cache.title_color); if (E.value.size() && E.value[0].is_bitfield) { class_desc->add_text("flags "); @@ -1379,21 +1382,32 @@ void EditorHelp::_update_doc() { class_desc->push_color(theme_cache.headline_color); class_desc->add_text(e); class_desc->pop(); - _pop_code_font(); class_desc->push_color(theme_cache.symbol_color); class_desc->add_text(":"); class_desc->pop(); + if (cd.enums.has(e)) { + if (cd.enums[e].is_deprecated) { + DEPRECATED_DOC_TAG; + } + + if (cd.enums[e].is_experimental) { + EXPERIMENTAL_DOC_TAG; + } + } + + _pop_code_font(); + class_desc->add_newline(); class_desc->add_newline(); // Enum description. - if (e != "@unnamed_enums" && cd.enums.has(e) && !cd.enums[e].strip_edges().is_empty()) { + if (e != "@unnamed_enums" && cd.enums.has(e) && !cd.enums[e].description.strip_edges().is_empty()) { class_desc->push_color(theme_cache.text_color); _push_normal_font(); class_desc->push_indent(1); - _add_text(cd.enums[e]); + _add_text(cd.enums[e].description); class_desc->pop(); _pop_normal_font(); class_desc->pop(); @@ -1417,6 +1431,7 @@ void EditorHelp::_update_doc() { constant_line[enum_list[i].name] = class_desc->get_paragraph_count() - 2; _push_code_font(); + _add_bulletpoint(); class_desc->push_color(theme_cache.headline_color); _add_text(enum_list[i].name); @@ -1427,7 +1442,6 @@ void EditorHelp::_update_doc() { class_desc->push_color(theme_cache.value_color); _add_text(_fix_constant(enum_list[i].value)); class_desc->pop(); - _pop_code_font(); if (enum_list[i].is_deprecated) { DEPRECATED_DOC_TAG; @@ -1437,6 +1451,8 @@ void EditorHelp::_update_doc() { EXPERIMENTAL_DOC_TAG; } + _pop_code_font(); + class_desc->add_newline(); if (!enum_list[i].description.strip_edges().is_empty()) { @@ -1503,8 +1519,6 @@ void EditorHelp::_update_doc() { _add_text(_fix_constant(constants[i].value)); class_desc->pop(); - _pop_code_font(); - if (constants[i].is_deprecated) { DEPRECATED_DOC_TAG; } @@ -1513,6 +1527,8 @@ void EditorHelp::_update_doc() { EXPERIMENTAL_DOC_TAG; } + _pop_code_font(); + class_desc->add_newline(); if (!constants[i].description.strip_edges().is_empty()) { @@ -1692,6 +1708,7 @@ void EditorHelp::_update_doc() { if (cd.properties[i].is_deprecated) { DEPRECATED_DOC_TAG; } + if (cd.properties[i].is_experimental) { EXPERIMENTAL_DOC_TAG; } diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index d95b1de36580..7ac812101aa6 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(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 9a4c4f7f9924..ed0d0ec373be 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_interface.cpp b/editor/editor_interface.cpp index 8d63082f3173..94040d4ca095 100644 --- a/editor/editor_interface.cpp +++ b/editor/editor_interface.cpp @@ -289,7 +289,7 @@ void EditorInterface::edit_node(Node *p_node) { } void EditorInterface::edit_script(const Ref