diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index e40af3e8df..0000000000 --- a/.gitattributes +++ /dev/null @@ -1,15 +0,0 @@ -*.blend filter=lfs diff=lfs merge=lfs -text -*.png filter=lfs diff=lfs merge=lfs -text -*.jpg filter=lfs diff=lfs merge=lfs -text -*.jpeg filter=lfs diff=lfs merge=lfs -text -*.scn filter=lfs diff=lfs merge=lfs -text -*.res filter=lfs diff=lfs merge=lfs -text -*.psd filter=lfs diff=lfs merge=lfs -text -*.mp4 filter=lfs diff=lfs merge=lfs -text -*.mp3 filter=lfs diff=lfs merge=lfs -text -*.wav filter=lfs diff=lfs merge=lfs -text -*.ogg filter=lfs diff=lfs merge=lfs -text -*.fbx filter=lfs diff=lfs merge=lfs -text -*.glb filter=lfs diff=lfs merge=lfs -text -*.exe filter=lfs diff=lfs merge=lfs -text -*.x86_64 filter=lfs diff=lfs merge=lfs -text diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..e69de29bb2 diff --git a/engine/.clang-format b/engine/.clang-format index 22966e2db9..de12b8f423 100644 --- a/engine/.clang-format +++ b/engine/.clang-format @@ -1,3 +1,6 @@ +# If you change this file, please format all files of the codebase as part of your PR: +# pre-commit run clang-format --all + # Commented out parameters are those with the same value as base LLVM style. # We can uncomment them if we want to change their value, or enforce the # chosen value in case the base style changes (last sync: Clang 18.1.8). @@ -38,7 +41,7 @@ AlignAfterOpenBracket: DontAlign # AcrossEmptyLines: false # AcrossComments: false # AlignCaseColons: false -# AlignEscapedNewlines: Right +AlignEscapedNewlines: DontAlign # Aligning leads to long diffs AlignOperands: DontAlign AlignTrailingComments: Kind: Never diff --git a/engine/.clang-tidy b/engine/.clang-tidy index 05b2f61a7a..47ef444cc6 100644 --- a/engine/.clang-tidy +++ b/engine/.clang-tidy @@ -1,21 +1,25 @@ +# If you change this file, please format all files of the codebase as part of your PR: +# pre-commit run --hook-stage manual clang-tidy --all + Checks: - -* - - cppcoreguidelines-pro-type-member-init + - bugprone-use-after-move - modernize-deprecated-headers - modernize-redundant-void-arg - modernize-use-bool-literals - - modernize-use-default-member-init +# - modernize-use-default-member-init # TODO Re-activate - modernize-use-nullptr - readability-braces-around-statements - readability-redundant-member-init + - readability-operators-representation HeaderFileExtensions: ["", h, hh, hpp, hxx, inc, glsl] ImplementationFileExtensions: [c, cc, cpp, cxx, m, mm, java] HeaderFilterRegex: (core|doc|drivers|editor|main|modules|platform|scene|servers|tests)/ FormatStyle: file CheckOptions: - cppcoreguidelines-pro-type-member-init.IgnoreArrays: true - cppcoreguidelines-pro-type-member-init.UseAssignment: true modernize-deprecated-headers.CheckHeaderFile: true modernize-use-bool-literals.IgnoreMacros: false modernize-use-default-member-init.IgnoreMacros: false modernize-use-default-member-init.UseAssignment: true + readability-operators-representation.BinaryOperators: "&&;&=;&;|;~;!;!=;||;|=;^;^=" + readability-operators-representation.OverloadedOperators: "&&;&=;&;|;~;!;!=;||;|=;^;^=" diff --git a/engine/.git-blame-ignore-revs b/engine/.git-blame-ignore-revs index 3203caf8f7..35c68147d9 100644 --- a/engine/.git-blame-ignore-revs +++ b/engine/.git-blame-ignore-revs @@ -72,3 +72,18 @@ e06d83860d798b6766b23d6eae48557387a7db85 # Style: Replace header guards with `#pragma once` 324512e11c1b7663c3cf47bec6ddbe65c6b8db2b + +# Style: Don't right-align escaped newlines +c5df0cb82bc539eff7dcfb2add99d60771fc50c5 + +# Style: Convert `*.gen.inc` to `*.gen.h` +7dae5da1982f4a55ba91557814905faef9ce461b + +# Move RenderingServer enums to a dedicated RenderingServerEnums (`RSE`) namespace +f5a290ac462765afca34e64dd39f883511510147 + +# Style: Add `class_db.h` includes explicitly +e380a417526c11f15a9ddb3997292409b10da2af + +# Move DisplayServer enums and typedefs to DisplayServerEnums +a447ac95ec170ee117c2eae55f1bfff0d0cf0dce diff --git a/engine/.github/PULL_REQUEST_TEMPLATE.md b/engine/.github/PULL_REQUEST_TEMPLATE.md index fbc45b89f2..cccaf8acc9 100644 --- a/engine/.github/PULL_REQUEST_TEMPLATE.md +++ b/engine/.github/PULL_REQUEST_TEMPLATE.md @@ -1,8 +1,6 @@ diff --git a/engine/.github/actions/godot-cache-restore/action.yml b/engine/.github/actions/godot-cache-restore/action.yml index 5d5b9b81ef..2cfc62ab07 100644 --- a/engine/.github/actions/godot-cache-restore/action.yml +++ b/engine/.github/actions/godot-cache-restore/action.yml @@ -12,7 +12,7 @@ runs: using: composite steps: - name: Restore default cache - uses: actions/cache/restore@v5 + uses: actions/cache/restore@v4 id: cache-ping with: path: ${{ inputs.scons-cache }} @@ -27,7 +27,7 @@ runs: # This is done after pulling the default cache so that PRs can integrate any potential changes # from the default branch without conflicting with whatever local changes were already made. - name: Restore local cache - uses: actions/cache/restore@v5 + uses: actions/cache/restore@v4 if: github.ref_name != github.event.repository.default_branch with: path: ${{ inputs.scons-cache }} diff --git a/engine/.github/actions/godot-cache-save/action.yml b/engine/.github/actions/godot-cache-save/action.yml index 4cb96fec69..cc7dc42965 100644 --- a/engine/.github/actions/godot-cache-save/action.yml +++ b/engine/.github/actions/godot-cache-save/action.yml @@ -16,7 +16,7 @@ runs: run: misc/scripts/purge_cache.py ${{ env.CACHE_TIMESTAMP }} "${{ inputs.scons-cache }}" - name: Save SCons cache directory - uses: actions/cache/save@v5 + uses: actions/cache/save@v4 with: path: ${{ inputs.scons-cache }} key: ${{ inputs.cache-name }}|${{ github.ref_name }}|${{ github.sha }} diff --git a/engine/.github/actions/godot-compat-test/action.yml b/engine/.github/actions/godot-compat-test/action.yml new file mode 100644 index 0000000000..09e10519fd --- /dev/null +++ b/engine/.github/actions/godot-compat-test/action.yml @@ -0,0 +1,33 @@ +name: Godot hash compatibility test +description: Check if methods with given hashes used by the older GDExtensions still can be loaded with given Godot version. + +inputs: + bin: + description: Path to the Godot binary. + required: true + type: string + reftags: + description: Reference tags of Godot versions to check (comma separated). + required: true + type: string + +runs: + using: composite + steps: + - name: Extract GDExtension interface + shell: sh + run: | + ${{ inputs.bin }} --headless --dump-gdextension-interface + mkdir tests/compatibility_test/src/deps/ + mv gdextension_interface.h tests/compatibility_test/src/deps/ + + - name: Build minimal GDExtension + shell: sh + run: scons --directory=./tests/compatibility_test + + - name: Download reference GDExtension API JSON and try to load it + shell: sh + env: + GODOT4_BIN: ${{ inputs.bin }} + REFTAGS: ${{ inputs.reftags }} + run: ./tests/compatibility_test/run_compatibility_test.py diff --git a/engine/.github/actions/godot-deps/action.yml b/engine/.github/actions/godot-deps/action.yml index a980849d38..a00fee7492 100644 --- a/engine/.github/actions/godot-deps/action.yml +++ b/engine/.github/actions/godot-deps/action.yml @@ -16,7 +16,7 @@ runs: using: composite steps: - name: Set up Python 3.x - uses: actions/setup-python@v6 + uses: actions/setup-python@v5 with: # Semantic version range syntax or exact version of a Python version. python-version: ${{ inputs.python-version }} diff --git a/engine/.github/actions/godot-project-export/action.yml b/engine/.github/actions/godot-project-export/action.yml new file mode 100644 index 0000000000..762122343b --- /dev/null +++ b/engine/.github/actions/godot-project-export/action.yml @@ -0,0 +1,55 @@ +name: Export Godot project +description: Export a test Godot project. + +inputs: + bin: + description: The path to the Godot executable + required: true + +runs: + using: composite + steps: + - name: Import resources and export project + shell: sh + run: | + git clone --depth=1 https://github.com/godotengine/godot-tests.git /tmp/godot-tests + + echo "Exporting project for Linux (PCK)" + ${{ inputs.bin }} --headless --path /tmp/godot-tests/tests/test_project/ --export-pack "Linux" /tmp/test_project.pck 2>&1 | tee log.txt || true + GODOT_CHECK_CI_LOG_ALL_ERRORS=1 misc/scripts/check_ci_log.py log.txt + + echo "Exporting project for Linux (ZIP)" + ${{ inputs.bin }} --headless --path /tmp/godot-tests/tests/test_project/ --export-pack "Linux" /tmp/test_project.zip 2>&1 | tee log.txt || true + GODOT_CHECK_CI_LOG_ALL_ERRORS=1 misc/scripts/check_ci_log.py log.txt + + echo "Exporting project for Linux as dedicated server (PCK)" + ${{ inputs.bin }} --headless --path /tmp/godot-tests/tests/test_project/ --export-pack "Linux Server" /tmp/test_project_server.pck 2>&1 | tee log.txt || true + GODOT_CHECK_CI_LOG_ALL_ERRORS=1 misc/scripts/check_ci_log.py log.txt + + - name: Run project files from folder + shell: sh + run: | + xvfb-run ${{ inputs.bin }} --path /tmp/godot-tests/tests/test_project/ --language fr --resolution 64x64 --write-movie /tmp/test_project_folder.png --quit 2>&1 | tee log.txt || true + GODOT_CHECK_CI_LOG_ALL_ERRORS=1 misc/scripts/check_ci_log.py log.txt + + ${{ inputs.bin }} --headless --path /tmp/godot-tests/tests/test_project/ --quit 2>&1 | tee log.txt || true + GODOT_CHECK_CI_LOG_ALL_ERRORS=1 misc/scripts/check_ci_log.py log.txt + + - name: Run exported project PCK/ZIP + shell: sh + run: | + xvfb-run ${{ inputs.bin }} --main-pack /tmp/test_project.pck --language fr --resolution 64x64 --write-movie /tmp/test_project_pck.png --quit 2>&1 | tee log.txt || true + GODOT_CHECK_CI_LOG_ALL_ERRORS=1 misc/scripts/check_ci_log.py log.txt + + xvfb-run ${{ inputs.bin }} --main-pack /tmp/test_project.zip --language fr --resolution 64x64 --write-movie /tmp/test_project_zip.png --quit 2>&1 | tee log.txt || true + GODOT_CHECK_CI_LOG_ALL_ERRORS=1 misc/scripts/check_ci_log.py log.txt + + # Headless mode is implied for dedicated server PCKs. + ${{ inputs.bin }} --main-pack /tmp/test_project_server.pck --quit 2>&1 | tee log.txt || true + GODOT_CHECK_CI_LOG_ALL_ERRORS=1 misc/scripts/check_ci_log.py log.txt + + echo "Checking whether video output from project folder and exported project match..." + md5sum /tmp/test_project*.png | md5sum --check + + echo "Checking whether audio output from project folder and exported project match..." + md5sum /tmp/test_project*.wav | md5sum --check diff --git a/engine/.github/actions/upload-artifact/action.yml b/engine/.github/actions/upload-artifact/action.yml index 0e4ee13839..8524afdf59 100644 --- a/engine/.github/actions/upload-artifact/action.yml +++ b/engine/.github/actions/upload-artifact/action.yml @@ -14,7 +14,7 @@ runs: using: composite steps: - name: Upload Godot Artifact - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@v4 with: name: ${{ inputs.name }} path: ${{ inputs.path }} diff --git a/engine/.github/changed_files.yml b/engine/.github/changed_files.yml new file mode 100644 index 0000000000..cc279f48b4 --- /dev/null +++ b/engine/.github/changed_files.yml @@ -0,0 +1,16 @@ +# We lack a convenient means of gathering *all* the changes when specializations are passed, so +# a catch-all variable is the easiest workaround. +everything: + - "**" + +# Determines if build actions should occur after static checks are ran. Broadly speaking, these +# files changing would result in SCons rebuilding the engine, or are otherwise pertinent to the +# buildsystem itself. +sources: + - .github/{actions,workflows}/*.yml + - "**/{SConstruct,SCsub,*.py}" + - "**/*.{h,hpp,hh,hxx,c,cpp,cc,cxx,m,mm,inc,glsl}" + - modules/mono/**/*.{cs,csproj,sln,props,targets} + - platform/android/java/{gradle*,**/*.{jar,java,kt,gradle}} + - platform/web/{package{,-lock}.json,js/**/*.js} + - tests/** diff --git a/engine/.github/workflows/android_builds.yml b/engine/.github/workflows/android_builds.yml index 346aaf71ed..ea6fea4e11 100644 --- a/engine/.github/workflows/android_builds.yml +++ b/engine/.github/workflows/android_builds.yml @@ -8,7 +8,6 @@ env: SCONS_FLAGS: >- dev_mode=yes module_text_server_fb_enabled=yes - tests=no swappy=yes jobs: @@ -39,12 +38,12 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: recursive - name: Set up Java 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: temurin java-version: 17 diff --git a/engine/.github/workflows/ios_builds.yml b/engine/.github/workflows/ios_builds.yml index 831ed64066..5243601d5e 100644 --- a/engine/.github/workflows/ios_builds.yml +++ b/engine/.github/workflows/ios_builds.yml @@ -8,7 +8,6 @@ env: SCONS_FLAGS: >- dev_mode=yes module_text_server_fb_enabled=yes - tests=no debug_symbols=no jobs: @@ -20,7 +19,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: recursive diff --git a/engine/.github/workflows/linux_builds.yml b/engine/.github/workflows/linux_builds.yml index 388ce5287e..b86beaabb4 100644 --- a/engine/.github/workflows/linux_builds.yml +++ b/engine/.github/workflows/linux_builds.yml @@ -1,6 +1,11 @@ name: 🐧 Linux Builds on: workflow_call: + inputs: + changed-files: + description: A list of changed files. + required: true + type: string workflow_dispatch: # Global Settings @@ -8,7 +13,6 @@ env: SCONS_FLAGS: >- dev_mode=yes module_text_server_fb_enabled=yes - "accesskit_sdk_path=${{ github.workspace }}/accesskit-c-0.21.2/" GODOT_CPP_BRANCH: 4.5 DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: true @@ -30,15 +34,17 @@ jobs: - name: Editor w/ Mono (target=editor) cache-name: linux-editor-mono target: editor - scons-flags: module_mono_enabled=yes + scons-flags: module_mono_enabled=yes compiledb=yes bin: ./bin/godot.linuxbsd.editor.x86_64.mono build-mono: true doc-test: true proj-conv: true + proj-export: true api-compat: true artifact: true # Validate godot-cpp compatibility on one arbitrary editor build. godot-cpp: true + clang-tidy: true - name: Editor with doubles and GCC sanitizers (target=editor, dev_build=yes, scu_build=yes, precision=double, use_asan=yes, use_ubsan=yes, linker=mold) cache-name: linux-editor-double-sanitizers @@ -110,7 +116,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: recursive @@ -118,7 +124,7 @@ jobs: run: | sudo apt-get update sudo apt-get install libwayland-bin # TODO: Figure out somehow how to embed this one. - if [ "${{ matrix.proj-test }}" == "true" ]; then + if [ "${{ matrix.proj-test }}" == "true" -o "${{ matrix.proj-export }}" == "true" ]; then sudo apt-get install mesa-vulkan-drivers fi @@ -143,7 +149,7 @@ jobs: uses: ./.github/actions/godot-deps with: # Sync with Ensure*Version in SConstruct. - python-version: 3.8 + python-version: 3.9 scons-version: 4.0 - name: Force remove preinstalled .NET SDKs @@ -159,15 +165,15 @@ jobs: dotnet-version: 8.0.100 - name: Download pre-built AccessKit - uses: dsaltares/fetch-gh-release-asset@1.1.2 - with: - repo: godotengine/godot-accesskit-c-static - version: tags/0.21.2 - file: accesskit-c-0.21.2.zip - target: accesskit-c-0.21.2/accesskit_c.zip - - - name: Extract pre-built AccessKit - run: unzip -o accesskit-c-0.21.2/accesskit_c.zip + shell: sh + id: accesskit-sdk + run: | + if python ./misc/scripts/install_accesskit.py; then + echo "ACCESSKIT_ENABLED=yes" >> "$GITHUB_OUTPUT" + else + echo "::warning::AccessKit SDK installation failed, building without AccessKit support." + echo "ACCESSKIT_ENABLED=no" >> "$GITHUB_OUTPUT" + fi - name: Install mold linker if: matrix.proj-test @@ -176,10 +182,16 @@ jobs: - name: Compilation uses: ./.github/actions/godot-build with: - scons-flags: ${{ env.SCONS_FLAGS }} ${{ matrix.scons-flags }} + scons-flags: ${{ env.SCONS_FLAGS }} ${{ matrix.scons-flags }} accesskit=${{ steps.accesskit-sdk.outputs.ACCESSKIT_ENABLED }} platform: linuxbsd target: ${{ matrix.target }} + - name: Style checks via pre-commit + if: matrix.clang-tidy + uses: pre-commit/action@v3.0.1 + with: + extra_args: --files ${{ inputs.changed-files }} --hook-stage manual clang-tidy + - name: Compilation (godot-cpp) uses: ./.github/actions/godot-cpp-build if: matrix.godot-cpp @@ -238,11 +250,18 @@ jobs: git diff --color --exit-code && ! git ls-files --others --exclude-standard | sed -e 's/^/New doc file missing in PR: /' | grep 'xml$' # Check API backwards compatibility - - name: Check for GDExtension compatibility + - name: Check for GDExtension compatibility – JSON check if: matrix.api-compat run: | ./misc/scripts/validate_extension_api.sh "${{ matrix.bin }}" + - name: Test GDExtension compatibility – load methods + uses: ./.github/actions/godot-compat-test + if: matrix.api-compat + with: + bin: ${{ matrix.bin }} + reftags: "4.5-stable,4.4-stable" + # Download and run the test project - name: Test Godot project uses: ./.github/actions/godot-project-test @@ -250,6 +269,13 @@ jobs: with: bin: ${{ matrix.bin }} + # Test project export + - name: Test project export + uses: ./.github/actions/godot-project-export + if: matrix.proj-export + with: + bin: ${{ matrix.bin }} + # Test the project converter - name: Test project converter uses: ./.github/actions/godot-converter-test diff --git a/engine/.github/workflows/macos_builds.yml b/engine/.github/workflows/macos_builds.yml index 82478fb670..166401aed0 100644 --- a/engine/.github/workflows/macos_builds.yml +++ b/engine/.github/workflows/macos_builds.yml @@ -8,7 +8,6 @@ env: SCONS_FLAGS: >- dev_mode=yes module_text_server_fb_enabled=yes - "accesskit_sdk_path=${{ github.workspace }}/accesskit-c-0.21.2/" jobs: build-macos: @@ -33,7 +32,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: recursive @@ -51,15 +50,15 @@ jobs: uses: ./.github/actions/godot-deps - name: Download pre-built AccessKit - uses: dsaltares/fetch-gh-release-asset@1.1.2 - with: - repo: godotengine/godot-accesskit-c-static - version: tags/0.21.2 - file: accesskit-c-0.21.2.zip - target: accesskit-c-0.21.2/accesskit_c.zip - - - name: Extract pre-built AccessKit - run: unzip -o accesskit-c-0.21.2/accesskit_c.zip + shell: sh + id: accesskit-sdk + run: | + if python3 ./misc/scripts/install_accesskit.py; then + echo "ACCESSKIT_ENABLED=yes" >> "$GITHUB_OUTPUT" + else + echo "::warning::AccessKit SDK installation failed, building without AccessKit support." + echo "ACCESSKIT_ENABLED=no" >> "$GITHUB_OUTPUT" + fi - name: Setup Vulkan SDK id: vulkan-sdk @@ -75,14 +74,14 @@ jobs: - name: Compilation (x86_64) uses: ./.github/actions/godot-build with: - scons-flags: ${{ env.SCONS_FLAGS }} ${{ matrix.scons-flags }} arch=x86_64 vulkan=${{ steps.vulkan-sdk.outputs.VULKAN_ENABLED }} + scons-flags: ${{ env.SCONS_FLAGS }} ${{ matrix.scons-flags }} arch=x86_64 vulkan=${{ steps.vulkan-sdk.outputs.VULKAN_ENABLED }} accesskit=${{ steps.accesskit-sdk.outputs.ACCESSKIT_ENABLED }} platform: macos target: ${{ matrix.target }} - name: Compilation (arm64) uses: ./.github/actions/godot-build with: - scons-flags: ${{ env.SCONS_FLAGS }} ${{ matrix.scons-flags }} arch=arm64 vulkan=${{ steps.vulkan-sdk.outputs.VULKAN_ENABLED }} + scons-flags: ${{ env.SCONS_FLAGS }} ${{ matrix.scons-flags }} arch=arm64 vulkan=${{ steps.vulkan-sdk.outputs.VULKAN_ENABLED }} accesskit=${{ steps.accesskit-sdk.outputs.ACCESSKIT_ENABLED }} platform: macos target: ${{ matrix.target }} diff --git a/engine/.github/workflows/runner.yml b/engine/.github/workflows/runner.yml index cbe3d57688..e1d3ee6a7d 100644 --- a/engine/.github/workflows/runner.yml +++ b/engine/.github/workflows/runner.yml @@ -18,29 +18,37 @@ jobs: android-build: name: 🤖 Android needs: static-checks + if: needs.static-checks.outputs.sources-changed == 'true' || github.event_name != 'pull_request' uses: ./.github/workflows/android_builds.yml ios-build: name: 🍏 iOS needs: static-checks + if: needs.static-checks.outputs.sources-changed == 'true' || github.event_name != 'pull_request' uses: ./.github/workflows/ios_builds.yml linux-build: name: 🐧 Linux needs: static-checks + if: needs.static-checks.outputs.sources-changed == 'true' || github.event_name != 'pull_request' uses: ./.github/workflows/linux_builds.yml + with: + changed-files: ${{ needs.static-checks.outputs.changed-files }} macos-build: name: 🍎 macOS needs: static-checks + if: needs.static-checks.outputs.sources-changed == 'true' || github.event_name != 'pull_request' uses: ./.github/workflows/macos_builds.yml windows-build: name: 🏁 Windows needs: static-checks + if: needs.static-checks.outputs.sources-changed == 'true' || github.event_name != 'pull_request' uses: ./.github/workflows/windows_builds.yml web-build: name: 🌐 Web needs: static-checks + if: needs.static-checks.outputs.sources-changed == 'true' || github.event_name != 'pull_request' uses: ./.github/workflows/web_builds.yml diff --git a/engine/.github/workflows/static_checks.yml b/engine/.github/workflows/static_checks.yml index 7ab79b86f2..0900f4a398 100644 --- a/engine/.github/workflows/static_checks.yml +++ b/engine/.github/workflows/static_checks.yml @@ -1,6 +1,13 @@ name: 📊 Static Checks on: workflow_call: + outputs: + changed-files: + description: A list of changed files. + value: ${{ jobs.static-checks.outputs.changed-files }} + sources-changed: + description: Determines if any source files were changed. + value: ${{ jobs.static-checks.outputs.sources-changed }} workflow_dispatch: jobs: @@ -8,11 +15,15 @@ jobs: name: Code style, file formatting, and docs runs-on: ubuntu-24.04 timeout-minutes: 30 + outputs: + changed-files: '"${{ steps.changed-files.outputs.everything_all_changed_files }}"' # Wrap with quotes to bookend internal quote separators. + sources-changed: ${{ steps.changed-files.outputs.sources_any_changed }} steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: - fetch-depth: 2 + fetch-depth: 0 # Treeless clone. Slightly less performant than a shallow clone, but makes finding diffs instantaneous. + filter: tree:0 # See: https://github.blog/open-source/git/get-up-to-speed-with-partial-clone-and-shallow-clone/ # This needs to happen before Python and npm execution; it must happen before any extra files are written. - name: .gitignore checks (gitignore_check.sh) @@ -20,18 +31,17 @@ jobs: bash ./misc/scripts/gitignore_check.sh - name: Get changed files - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - if [ "${{ github.event_name }}" == "pull_request" ]; then - files=$(git diff-tree --no-commit-id --name-only -r HEAD^1..HEAD 2> /dev/null || true) - elif [ "${{ github.event_name }}" == "push" -a "${{ github.event.forced }}" == "false" -a "${{ github.event.created }}" == "false" ]; then - files=$(git diff-tree --no-commit-id --name-only -r ${{ github.event.before }}..${{ github.event.after }} 2> /dev/null || true) - fi - files=$(echo "$files" | xargs -I {} sh -c 'echo "\"./{}\""' | tr '\n' ' ') - echo "CHANGED_FILES=$files" >> $GITHUB_ENV + id: changed-files + uses: tj-actions/changed-files@v47 + with: + safe_output: false # Output passed to environment variable to avoid command injection. + separator: '" "' # To account for paths with spaces, ensure our items are split by quotes internally. + skip_initial_fetch: true + files_yaml_from_source_file: .github/changed_files.yml - name: Style checks via pre-commit uses: pre-commit/action@v3.0.1 + env: + CHANGED_FILES: '"${{ steps.changed-files.outputs.everything_all_changed_files }}"' # Wrap with quotes to bookend internal quote separators. with: extra_args: --files ${{ env.CHANGED_FILES }} diff --git a/engine/.github/workflows/web_builds.yml b/engine/.github/workflows/web_builds.yml index 04d60ab5cb..dba7896b4c 100644 --- a/engine/.github/workflows/web_builds.yml +++ b/engine/.github/workflows/web_builds.yml @@ -7,7 +7,6 @@ on: env: SCONS_FLAGS: >- dev_mode=yes - tests=no debug_symbols=no use_closure_compiler=yes EM_VERSION: 4.0.11 @@ -21,21 +20,21 @@ jobs: fail-fast: false matrix: include: - - name: Template w/ threads (target=template_release, threads=yes) + - name: Template w/ threads, 64-bit (target=template_release, threads=yes, arch=wasm64) cache-name: web-template target: template_release - scons-flags: threads=yes + scons-flags: threads=yes arch=wasm64 artifact: true - - name: Template w/o threads (target=template_release, threads=no) + - name: Template w/o threads, 32-bit (target=template_release, threads=no, arch=wasm32) cache-name: web-nothreads-template target: template_release - scons-flags: threads=no + scons-flags: threads=no arch=wasm32 artifact: true steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: recursive diff --git a/engine/.github/workflows/windows_builds.yml b/engine/.github/workflows/windows_builds.yml index b57637c099..2a207bceb9 100644 --- a/engine/.github/workflows/windows_builds.yml +++ b/engine/.github/workflows/windows_builds.yml @@ -10,7 +10,6 @@ env: module_text_server_fb_enabled=yes debug_symbols=no "angle_libs=${{ github.workspace }}/" - "accesskit_sdk_path=${{ github.workspace }}/accesskit-c-0.21.2/" SCONS_CACHE_MSVC_CONFIG: true PYTHONIOENCODING: utf8 @@ -59,7 +58,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: recursive @@ -96,20 +95,20 @@ jobs: run: Expand-Archive -Force angle/angle.zip ${{ github.workspace }}/ - name: Download pre-built AccessKit - uses: dsaltares/fetch-gh-release-asset@1.1.2 - with: - repo: godotengine/godot-accesskit-c-static - version: tags/0.21.2 - file: accesskit-c-0.21.2.zip - target: accesskit-c-0.21.2/accesskit_c.zip - - - name: Extract pre-built AccessKit - run: unzip -o accesskit-c-0.21.2/accesskit_c.zip + shell: sh + id: accesskit-sdk + run: | + if python ./misc/scripts/install_accesskit.py; then + echo "ACCESSKIT_ENABLED=yes" >> "$GITHUB_OUTPUT" + else + echo "::warning::AccessKit SDK installation failed, building without AccessKit support." + echo "ACCESSKIT_ENABLED=no" >> "$GITHUB_OUTPUT" + fi - name: Compilation uses: ./.github/actions/godot-build with: - scons-flags: ${{ env.SCONS_FLAGS }} ${{ matrix.scons-flags }} d3d12=${{ steps.d3d12-sdk.outputs.D3D12_ENABLED }} + scons-flags: ${{ env.SCONS_FLAGS }} ${{ matrix.scons-flags }} d3d12=${{ steps.d3d12-sdk.outputs.D3D12_ENABLED }} accesskit=${{ steps.accesskit-sdk.outputs.ACCESSKIT_ENABLED }} platform: windows target: ${{ matrix.target }} diff --git a/engine/.pre-commit-config.yaml b/engine/.pre-commit-config.yaml index 15119dc385..5a4ee612fe 100644 --- a/engine/.pre-commit-config.yaml +++ b/engine/.pre-commit-config.yaml @@ -29,30 +29,36 @@ repos: types_or: [text] args: [-style=file:misc/utility/clang_format_glsl.yml] + # Not automatically triggered (because it requires compile_commands.json to be up-to-date). + # Invoke it manually via `pre-commit run --hook-stage manual clang-tidy` - repo: https://github.com/pocc/pre-commit-hooks rev: v1.3.5 hooks: - id: clang-tidy - files: \.(c|h|cpp|hpp|cc|hh|cxx|hxx|m|mm|inc|java|glsl)$ - args: [--fix, --quiet, --use-color] + # TODO .inc ignored for now because they don't include their parent header. + # TODO Platform-specific subfolders currently fail, we should try to include them + files: ^(core|main|scene)/.*\.(c|h|cpp|hpp|cc|hh|cxx|hxx|m|mm|java)$ + # No unknown warning suppression used for easier compatibility with gcc + args: [--fix, --quiet, --use-color, -p=compile_commands.json, -extra-arg=-Wno-unknown-warning-option] types_or: [text] additional_dependencies: [clang-tidy==21.1.6] - require_serial: true - stages: [manual] # Not automatically triggered, invoked via `pre-commit run --hook-stage manual clang-tidy` + require_serial: false + stages: [manual] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.7 + rev: v0.15.0 hooks: - id: ruff-check - args: [--fix] + args: [--color=always] files: (\.py|SConstruct|SCsub)$ types_or: [text] - id: ruff-format + args: [--color=always] files: (\.py|SConstruct|SCsub)$ types_or: [text] - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.14.1 # Latest version that supports Python 3.8 + rev: v1.19.1 hooks: - id: mypy files: \.py$ @@ -103,6 +109,12 @@ repos: files: ^(doc/classes|.*/doc_classes)/.*\.xml$ additional_dependencies: [xmlschema] + - id: validate-codeowners + name: validate codeowners + language: python + entry: python misc/scripts/validate_codeowners.py + args: [--unowned] + - id: eslint name: eslint language: node diff --git a/engine/CHANGELOG.md b/engine/CHANGELOG.md index 08e753e017..72394127d8 100644 --- a/engine/CHANGELOG.md +++ b/engine/CHANGELOG.md @@ -7,289 +7,6 @@ previous feature release. It is equivalent to the listings on our Changelogs for earlier feature releases are available in their respective Git branches, and linked at the [end of this file](#Past-releases). -## 4.6.2 - 2026-04-01 - -- [Release announcement](https://godotengine.org/article/maintenance-release-godot-4-6-2) -- [Interactive changelog](https://godotengine.github.io/godot-interactive-changelog/#4.6.2) - -#### 3D - -- Fix `GridMap` editor pasting when orbiting using the "Alt" key ([GH-116778](https://github.com/godotengine/godot/pull/116778)). -- Fix 3D editor camera not updating correctly ([GH-106219](https://github.com/godotengine/godot/pull/106219)). -- Fix 3D focus selection for subgizmos ([GH-116972](https://github.com/godotengine/godot/pull/116972)). -- Fix collision repositioning for child nodes ([GH-115353](https://github.com/godotengine/godot/pull/115353)). -- Fix DirectionalLight3D property list ([GH-117189](https://github.com/godotengine/godot/pull/117189)). -- Fix some UI issues with the `GridMap` editor ([GH-116555](https://github.com/godotengine/godot/pull/116555)). -- Remove unneeded checks for undo-redo history from Skeleton3DEditor ([GH-115784](https://github.com/godotengine/godot/pull/115784)). - -#### Animation - -- Account for the current section when processing AnimationTracks ([GH-116046](https://github.com/godotengine/godot/pull/116046)). -- Check `playback_queue` existence after emit `animation_finished` signal ([GH-116676](https://github.com/godotengine/godot/pull/116676)). -- Clear fade-out on request firing in AnimationNodeOneShot ([GH-115125](https://github.com/godotengine/godot/pull/115125)). -- Deselect bezier keyframes when switching animations ([GH-116953](https://github.com/godotengine/godot/pull/116953)). -- Fix animation player crash when setting current animation to stop ([GH-116264](https://github.com/godotengine/godot/pull/116264)). -- Fix icon size in AnimationPlayer tracks ([GH-115576](https://github.com/godotengine/godot/pull/115576)). -- Fix visual shift of animation editor keys during selection ([GH-117290](https://github.com/godotengine/godot/pull/117290)). -- Fix timeline cursor following mouse during marker selection ([GH-117634](https://github.com/godotengine/godot/pull/117634)). - -#### Assetlib - -- Fix TLS handshake fail preventing AssetLib use with `builtin_certs=no` ([GH-115450](https://github.com/godotengine/godot/pull/115450)). - -#### Audio - -- AudioStreamWAV: Check for `eof_reached` when reading LIST INFO tags ([GH-116719](https://github.com/godotengine/godot/pull/116719)). -- Fix `AudioStreamPlaybackMicrophone` shutdown crash ([GH-116299](https://github.com/godotengine/godot/pull/116299)). - -#### Buildsystem - -- Add UTF-8 encoding to svg file open in `platform_builders.py` ([GH-117454](https://github.com/godotengine/godot/pull/117454)). -- CI: Bump JavaScript actions to Node 24 ([GH-117428](https://github.com/godotengine/godot/pull/117428)). -- CI: Restore godot-cpp caching ([GH-116586](https://github.com/godotengine/godot/pull/116586)). -- Fix Metal driver build with Xcode 26.4 ([GH-117989](https://github.com/godotengine/godot/pull/117989)). -- ScrollBar: Fix compilation with `precision=double` ([GH-117224](https://github.com/godotengine/godot/pull/117224)). -- Update CODEOWNERS ([GH-117674](https://github.com/godotengine/godot/pull/117674)). - -#### C# - -- Revert "[.NET] Remove EFS update on reloading assemblies" but with deferred call ([GH-117617](https://github.com/godotengine/godot/pull/117617)). - -#### Core - -- Fix `String::split_` crash on empty string ([GH-117353](https://github.com/godotengine/godot/pull/117353)). -- Fix editable children state when duplicating instantiated nodes ([GH-117041](https://github.com/godotengine/godot/pull/117041)). -- Make sure `ScriptLanguage` is initialized even after `init_languages` call ([GH-114131](https://github.com/godotengine/godot/pull/114131)). -- RingBuffer: Fix `T read()` method reading empty buffer ([GH-117388](https://github.com/godotengine/godot/pull/117388)). -- RingBuffer: Fix overreading on methods that take an offset as an argument ([GH-117151](https://github.com/godotengine/godot/pull/117151)). - -#### Documentation - -- Editor Settings: Prevent crash when viewing `filesystem/import/blender/blender_path` ([GH-115895](https://github.com/godotengine/godot/pull/115895)). -- Fix UPNP code sample using `OK` constant incorrectly ([GH-115549](https://github.com/godotengine/godot/pull/115549)). - -#### Editor - -- Add support for `--ignore-error-breaks` to `LocalDebugger` ([GH-116823](https://github.com/godotengine/godot/pull/116823)). -- Check if a resource from a snapshot exists before loading it ([GH-115194](https://github.com/godotengine/godot/pull/115194)). -- Create EditorHelpHighlighter in Project Manager ([GH-116014](https://github.com/godotengine/godot/pull/116014)). -- Don't tint the preview camera icon ([GH-116525](https://github.com/godotengine/godot/pull/116525)). -- Ensure debug features get cleared after use ([GH-115116](https://github.com/godotengine/godot/pull/115116)). -- Fix build profile generator always enabling 2D navigation ([GH-115412](https://github.com/godotengine/godot/pull/115412)). -- Fix build profile generator creating bogus profiles ([GH-115410](https://github.com/godotengine/godot/pull/115410)). -- Fix Change version link in web editor shell ([GH-117763](https://github.com/godotengine/godot/pull/117763)). -- Fix code completion popup colors not being updated properly ([GH-115315](https://github.com/godotengine/godot/pull/115315)). -- Fix detection of some features in Engine Compilation Configuration Editor ([GH-115230](https://github.com/godotengine/godot/pull/115230)). -- Fix editor not handling unsaved changes on restart from settings ([GH-116218](https://github.com/godotengine/godot/pull/116218)). -- Fix EditorSettings error due to `android_sdk_path` when exporting a project ([GH-116515](https://github.com/godotengine/godot/pull/116515)). -- Fix extend script button styling in scene tree dock ([GH-115045](https://github.com/godotengine/godot/pull/115045)). -- Fix hidden `Import` tab height ([GH-115172](https://github.com/godotengine/godot/pull/115172)). -- Fix mute button after pausing and stopping ([GH-116537](https://github.com/godotengine/godot/pull/116537)). -- Fix theme item inspector tooltips for Window subclasses ([GH-115245](https://github.com/godotengine/godot/pull/115245)). -- Hide property groups from the "Members" section in Debugger ([GH-116790](https://github.com/godotengine/godot/pull/116790)). -- macOS: Set apple menu node name to non empty value ([GH-115726](https://github.com/godotengine/godot/pull/115726)). -- Properly update region editor window when undoing changes to `region_rect` ([GH-108875](https://github.com/godotengine/godot/pull/108875)). -- Scene tree dock: Don't log error if there is no selection upon handling `item_icon_double_clicked` signal ([GH-115347](https://github.com/godotengine/godot/pull/115347)). -- Set accessibility name on Tree inline cell editor when editing ([GH-117135](https://github.com/godotengine/godot/pull/117135)). -- Stop autocomplete from eating words by default ([GH-117464](https://github.com/godotengine/godot/pull/117464)). - -#### Export - -- Android Editor: Copy keystore to temp file during export ([GH-116161](https://github.com/godotengine/godot/pull/116161)). -- Linux: Handle debug symbols with renamed executable ([GH-114947](https://github.com/godotengine/godot/pull/114947)). - -#### GDExtension - -- Add missing `GDVIRTUAL_BIND(_get_supported_extensions)` on `MovieWriter` ([GH-117479](https://github.com/godotengine/godot/pull/117479)). - -#### GDScript - -- Stop `RemoteDebugger` from improperly flushing messages during break ([GH-115532](https://github.com/godotengine/godot/pull/115532)). - -#### GUI - -- Fix "Custom" anchor preset being ignored if the parent isn't a `Control` ([GH-117488](https://github.com/godotengine/godot/pull/117488)). -- Fix `SplitContainerDragger` dragging with enabled screen reader, allow resizing with keyboard shortcuts ([GH-116628](https://github.com/godotengine/godot/pull/116628)). -- Fix `TabContainer` accessibility sub-element cleanup ([GH-116617](https://github.com/godotengine/godot/pull/116617)). -- Fix camera focus triggered by double-clicking Tree buttons ([GH-114519](https://github.com/godotengine/godot/pull/114519)). -- Fix crash on removing Windows PopupMenu submenu ([GH-115373](https://github.com/godotengine/godot/pull/115373)). -- Fix drag-resizing `Control` with non-zero `pivot_offset_ratio` ([GH-116057](https://github.com/godotengine/godot/pull/116057)). -- Fix error after setting native PopupMenu item ID ([GH-115378](https://github.com/godotengine/godot/pull/115378)). -- Fix LCD batching flag for StyleBoxTexture ([GH-116647](https://github.com/godotengine/godot/pull/116647)). -- Fix RichTextLabel drag selection not working after double-click ([GH-117201](https://github.com/godotengine/godot/pull/117201)). -- Fix soft hyphen not working with small (or zero) line breaking width ([GH-116196](https://github.com/godotengine/godot/pull/116196)). -- Fix SplitContainer accessibility errors ([GH-116601](https://github.com/godotengine/godot/pull/116601)). -- Fix wrong normal icon color in FileDialog ([GH-115917](https://github.com/godotengine/godot/pull/115917)). -- PopupMenu: Use parent `GraphEdit` scale for popups inside `GraphElement` instead of completely disabling it ([GH-115620](https://github.com/godotengine/godot/pull/115620)). -- RTL: Fix `%` handling in `[img=WxH]` tags ([GH-116928](https://github.com/godotengine/godot/pull/116928)). -- Stop exposing external theme item properties ([GH-115392](https://github.com/godotengine/godot/pull/115392)). -- TextEdit: Fix clipping of last character due to right margin rounding ([GH-116850](https://github.com/godotengine/godot/pull/116850)). -- TextServer: Fix numeric keycap emoji sequence rendering ([GH-115687](https://github.com/godotengine/godot/pull/115687)). -- TextServer: Ignore language of embedded object replacement spans when updating line breaks ([GH-116197](https://github.com/godotengine/godot/pull/116197)). - -#### Import - -- Blender attempts should be incremented to avoid endless loop ([GH-116589](https://github.com/godotengine/godot/pull/116589)). - -#### Input - -- Fix issues with `InputMap::load_from_project_settings()` when called in tool script ([GH-105045](https://github.com/godotengine/godot/pull/105045)). -- macOS: Hide input accessory popups when no text control selected ([GH-115619](https://github.com/godotengine/godot/pull/115619)). -- Sync controller mappings DB with SDL community repo ([GH-115752](https://github.com/godotengine/godot/pull/115752)). - -#### Physics - -- Jolt Physics: Make MoveKinematic more accurate when rotating a body by a very small angle ([GH-115327](https://github.com/godotengine/godot/pull/115327)). -- Jolt Physics: Rework how gravity is applied to dynamic bodies to prevent energy increase on elastic collisions ([GH-115305](https://github.com/godotengine/godot/pull/115305)). -- Jolt Physics: Swapping vertices of triangle if it is scaled inside out ([GH-115089](https://github.com/godotengine/godot/pull/115089)). - -#### Platforms - -- Android: Enable native file picker support on all devices ([GH-115257](https://github.com/godotengine/godot/pull/115257)). -- Android: Fix FileAccess crash when using treeUri in Gradle-built apps ([GH-117131](https://github.com/godotengine/godot/pull/117131)). -- Android: Fix JavaClassWrapper test crashes on API 26 and lower ([GH-115800](https://github.com/godotengine/godot/pull/115800)). -- Android: Remove version check for `FEATURE_NATIVE_DIALOG_FILE` support ([GH-116584](https://github.com/godotengine/godot/pull/116584)). -- Apple Embedded: Fix static .a/.xcframework library loading in `open_dynamic_library` ([GH-117469](https://github.com/godotengine/godot/pull/117469)). -- Fix macOS Steam time tracking lost when opening a project ([GH-117335](https://github.com/godotengine/godot/pull/117335)). -- Fix startup errors/warnings on X11 when the Prefer Wayland editor setting is enabled ([GH-116366](https://github.com/godotengine/godot/pull/116366)). -- iOS: Add UIScene lifecycle events ([GH-116395](https://github.com/godotengine/godot/pull/116395)). -- iOS: Propagate VC UI preferences to SwiftUI hosting controller ([GH-116633](https://github.com/godotengine/godot/pull/116633)). -- LinuxBSD: Fix UI freeze when opening file manager ([GH-114521](https://github.com/godotengine/godot/pull/114521)). -- macOS: Add `nullptr` checks of `script_debugger` in `LayerHost::gui_input` ([GH-116724](https://github.com/godotengine/godot/pull/116724)). -- macOS: Add null check to `get_framework_executable` ([GH-116354](https://github.com/godotengine/godot/pull/116354)). -- macOS: Enable wake for events if `Magnet` is running ([GH-116524](https://github.com/godotengine/godot/pull/116524)). -- macOS: Fix confined mouse movement getting out of sync ([GH-116242](https://github.com/godotengine/godot/pull/116242)). -- Wayland: Improve mapping robustness and synchronization ([GH-117385](https://github.com/godotengine/godot/pull/117385)). -- Wayland: Only handle the current output mode ([GH-116236](https://github.com/godotengine/godot/pull/116236)). -- Wayland: Skip resize request when the size is the same ([GH-116376](https://github.com/godotengine/godot/pull/116376)). -- Windows: Set current driver when ANGLE init fails ([GH-117253](https://github.com/godotengine/godot/pull/117253)). -- Windows: Use executable icon as default for the window ([GH-115294](https://github.com/godotengine/godot/pull/115294)). - -#### Plugin - -- Android: Fix java.util.HashMap handling ([GH-114941](https://github.com/godotengine/godot/pull/114941)). -- Fix EditorDock not reopening ([GH-117340](https://github.com/godotengine/godot/pull/117340)). - -#### Rendering - -- Add compatibility fallback to `textureLod` when reading from `RADIANCE` ([GH-116155](https://github.com/godotengine/godot/pull/116155)). -- Apply fixed size properly for mono/stereo rendering ([GH-115147](https://github.com/godotengine/godot/pull/115147)). -- Fix accidental write-combined memory reads in canvas renderer ([GH-115757](https://github.com/godotengine/godot/pull/115757)). -- Fix shader compilation error when writing to `FOG` when `render_mode fog_disabled` ([GH-115026](https://github.com/godotengine/godot/pull/115026)). -- Fix Tangent decoding detection when computing vertex skinning ([GH-117401](https://github.com/godotengine/godot/pull/117401)). -- Fix viewport debanding not working with spatial scalers ([GH-114890](https://github.com/godotengine/godot/pull/114890)). -- Handle nearest filtering modes in D3D12 driver when anisotropy is enabled ([GH-115504](https://github.com/godotengine/godot/pull/115504)). -- macOS: Force ANGLE (GL over Metal) when running in VM ([GH-117371](https://github.com/godotengine/godot/pull/117371)). -- Restore default sky roughness levels to 8 ([GH-116154](https://github.com/godotengine/godot/pull/116154)). - -#### Shaders - -- Fix UTF-8 handling in GLES3 shaders ([GH-116756](https://github.com/godotengine/godot/pull/116756)). -- Fixes for completion of shader preprocessor defines ([GH-116413](https://github.com/godotengine/godot/pull/116413)). -- VisualShader: Fix nodes not attaching to new Frame on duplication ([GH-115193](https://github.com/godotengine/godot/pull/115193)). - -#### Tests - -- Android: Fail instrumented tests when test function doesn't complete ([GH-115713](https://github.com/godotengine/godot/pull/115713)). -- Fix file access tests failing on older Android devices ([GH-115718](https://github.com/godotengine/godot/pull/115718)). - -#### Thirdparty - -- Accessibility: Handle adapter activation/deactivation ([GH-115565](https://github.com/godotengine/godot/pull/115565)). -- Add missing patch files to `thirdparty/jolt_physics` ([GH-115884](https://github.com/godotengine/godot/pull/115884)). -- libpng: Update to 1.6.55 ([GH-117564](https://github.com/godotengine/godot/pull/117564)). -- Update access-kit to 0.21.2 ([GH-117433](https://github.com/godotengine/godot/pull/117433)). - -## 4.6.1 - 2026-02-16 - -- [Release announcement](https://godotengine.org/article/maintenance-release-godot-4-6-1) -- [Interactive changelog](https://godotengine.github.io/godot-interactive-changelog/#4.6.1) - -#### 3D - -- Change orbit snap shortcut with navigation scheme ([GH-115298](https://github.com/godotengine/godot/pull/115298)). -- Fix `Skeleton3D` Edit Mode bone buttons have priority over transform gizmo ([GH-115608](https://github.com/godotengine/godot/pull/115608)). -- Fix viewport orbit snap defaulting to always snapping when shortcut(s) are set to none ([GH-115002](https://github.com/godotengine/godot/pull/115002)). -- Increase float precision in the editor inspector for Quaternions ([GH-106352](https://github.com/godotengine/godot/pull/106352)). -- Register zoom shortcuts to match preset `Godot` navigation scheme ([GH-115290](https://github.com/godotengine/godot/pull/115290)). - -#### Animation - -- Fix double memdelete of `dummy_player` ([GH-115968](https://github.com/godotengine/godot/pull/115968)). -- Fix LookAtModifier3D / AimModifier3D forward vector ([GH-115689](https://github.com/godotengine/godot/pull/115689)). -- Fix use-after-free in Animation Blend Tree ([GH-115919](https://github.com/godotengine/godot/pull/115919)). -- Fix use-after-free in AnimationTree (AHashMap realloc) ([GH-115931](https://github.com/godotengine/godot/pull/115931)). - -#### Buildsystem - -- Fix missing lib with `builtin_glslang=false` ([GH-93478](https://github.com/godotengine/godot/pull/93478)). - -#### C# - -- Revert "Improve performance of `CSharpLanguage::reload_assemblies`" ([GH-115759](https://github.com/godotengine/godot/pull/115759)). - -#### Core - -- Fix ClassDB class list sorting regression ([GH-115923](https://github.com/godotengine/godot/pull/115923)). -- Fix the `NodePath` hash function to not yield the same value for similar paths ([GH-115473](https://github.com/godotengine/godot/pull/115473)). - -#### Editor - -- Fix `NodePath` `EditorProperty` using the wrong scene root ([GH-115422](https://github.com/godotengine/godot/pull/115422)). -- Fix create dialog recents ([GH-115314](https://github.com/godotengine/godot/pull/115314)). -- Fix Rename option for instance roots ([GH-115575](https://github.com/godotengine/godot/pull/115575)). -- Fix Unique Resources from Inherited Scenes ([GH-115862](https://github.com/godotengine/godot/pull/115862)). -- Fix wrong base type when creating script ([GH-115778](https://github.com/godotengine/godot/pull/115778)). - -#### Export - -- Load translation files to check locale for ICU data export ([GH-115827](https://github.com/godotengine/godot/pull/115827)). - -#### GDScript - -- LSP: Add `godot` to known language ids ([GH-115671](https://github.com/godotengine/godot/pull/115671)). -- LSP: Handle clients that do not support `CompletionContext` ([GH-115672](https://github.com/godotengine/godot/pull/115672)). - -#### GUI - -- Fix current line highlight not extending into gutter ([GH-115729](https://github.com/godotengine/godot/pull/115729)). - -#### Input - -- Update editor shortcuts when changing 3D navigation scheme ([GH-115289](https://github.com/godotengine/godot/pull/115289)). - -#### Particles - -- Revert "Change curve range for particle multipliers" ([GH-116140](https://github.com/godotengine/godot/pull/116140)). - -#### Physics - -- Fix transform updates sometimes being discarded when using Jolt ([GH-115364](https://github.com/godotengine/godot/pull/115364)). - -#### Platforms - -- Android: Fix `Bad file descriptor` in SAF/MediaStore in long term access ([GH-115751](https://github.com/godotengine/godot/pull/115751)). -- Fix crash in `StorageScope.kt` on Android ([GH-115515](https://github.com/godotengine/godot/pull/115515)). -- Wayland Embedder: Fix FD leak with inert objects ([GH-115823](https://github.com/godotengine/godot/pull/115823)). -- Windows: Disable MSVC control flow check on IAT hooks ([GH-115430](https://github.com/godotengine/godot/pull/115430)). - -#### Plugin - -- Android: Fix plugin type mismatch regression ([GH-115685](https://github.com/godotengine/godot/pull/115685)). - -#### Rendering - -- Avoid reading from sky pointer when rendering background without sky ([GH-115874](https://github.com/godotengine/godot/pull/115874)). -- Ensure that uv border size is passed in to sky rendering functions ([GH-115606](https://github.com/godotengine/godot/pull/115606)). -- Pick the sample closer to the camera when resolving 2x MSAA ([GH-115124](https://github.com/godotengine/godot/pull/115124)). -- Update re-spirv with more derivative operations ([GH-115921](https://github.com/godotengine/godot/pull/115921)). -- Use sky's corrected camera projection for `combined_reprojection` ([GH-115292](https://github.com/godotengine/godot/pull/115292)). -- Use transmittance instead of opacity in the early-out branch when calculating volumetric fog ([GH-116107](https://github.com/godotengine/godot/pull/116107)). - -#### Thirdparty - -- libpng: Update to 1.6.54 ([GH-115714](https://github.com/godotengine/godot/pull/115714)). - ## 4.6 - 2026-01-26 - [Release announcement](https://godotengine.org/releases/4.6/) diff --git a/engine/CONTRIBUTING.md b/engine/CONTRIBUTING.md index 02829178ff..613aa7accf 100644 --- a/engine/CONTRIBUTING.md +++ b/engine/CONTRIBUTING.md @@ -65,7 +65,7 @@ for an introduction to developing on Godot. The [Contributing docs](https://contributing.godotengine.org/en/latest/organization/how_to_contribute.html) also have important information on the [PR workflow](https://contributing.godotengine.org/en/latest/organization/pull_requests/creating_pull_requests.html) -(with a helpful guide for Git usage), and our [Code style guidelines](https://contributing.godotengine.org/en/latest/engine/guidelines/code_style.html) +(with a helpful guide for Git usage), and our [Code style guidelines](https://contributing.godotengine.org/en/latest/engine/guidelines/cpp_usage_guidelines.html) which all contributions need to follow. ### Be mindful of your commits diff --git a/engine/COPYRIGHT.txt b/engine/COPYRIGHT.txt index ce3cdd2c85..d61c136270 100644 --- a/engine/COPYRIGHT.txt +++ b/engine/COPYRIGHT.txt @@ -310,6 +310,11 @@ Comment: The FreeType Project Copyright: 1996-2025, David Turner, Robert Wilhelm, and Werner Lemberg. License: FTL +Files: thirdparty/gamepadmotionhelpers/* +Comment: GamepadMotionHelpers +Copyright: 2020-2023, Julian "Jibb" Smart +License: Expat + Files: thirdparty/glad/* Comment: glad Copyright: 2013-2022, David Herberth @@ -427,6 +432,11 @@ Comment: meshoptimizer Copyright: 2016-2024, Arseny Kapoulkine License: Expat +Files: thirdparty/metal-cpp/* +Comment: metal-cpp +Copyright: 2024, Apple Inc. +License: Apache-2.0 + Files: thirdparty/mingw-std-threads/* Comment: mingw-std-threads Copyright: 2016, Mega Limited @@ -574,6 +584,11 @@ Comment: SPIRV-Cross Copyright: 2015-2021, Arm Limited License: Apache-2.0 or Expat +Files: thirdparty/spirv-headers/* +Comment: SPIRV-Headers +Copyright: 2015-2024, The Khronos Group Inc. +License: Expat + Files: thirdparty/spirv-reflect/* Comment: SPIRV-Reflect Copyright: 2017-2022, Google Inc. @@ -608,19 +623,19 @@ License: BSD-3-clause Files: thirdparty/volk/* Comment: volk -Copyright: 2018-2024, Arseny Kapoulkine +Copyright: 2018-2025, Arseny Kapoulkine License: Expat Files: thirdparty/vulkan/* Comment: Vulkan Headers -Copyright: 2014-2024, The Khronos Group Inc. - 2014-2024, Valve Corporation - 2014-2024, LunarG, Inc. +Copyright: 2015-2025, The Khronos Group Inc. + 2015-2025, Valve Corporation + 2015-2025, LunarG, Inc. License: Apache-2.0 Files: thirdparty/vulkan/vk_mem_alloc.h Comment: Vulkan Memory Allocator -Copyright: 2017-2024, Advanced Micro Devices, Inc. +Copyright: 2017-2025, Advanced Micro Devices, Inc. License: Expat Files: thirdparty/wayland/* diff --git a/engine/SConstruct b/engine/SConstruct index 52e37b9b1a..0b560ecf1b 100644 --- a/engine/SConstruct +++ b/engine/SConstruct @@ -2,7 +2,7 @@ from misc.utility.scons_hints import * EnsureSConsVersion(4, 0) -EnsurePythonVersion(3, 8) +EnsurePythonVersion(3, 9) # System import glob @@ -199,8 +199,7 @@ opts.Add(BoolVariable("opengl3", "Enable the OpenGL/GLES3 rendering driver", Tru opts.Add(BoolVariable("d3d12", "Enable the Direct3D 12 rendering driver on supported platforms", False)) opts.Add(BoolVariable("metal", "Enable the Metal rendering driver on supported platforms (Apple arm64 only)", False)) opts.Add(BoolVariable("use_volk", "Use the volk library to load the Vulkan loader dynamically", True)) -opts.Add(BoolVariable("accesskit", "Use AccessKit C SDK", True)) -opts.Add(("accesskit_sdk_path", "Path to the AccessKit C SDK", "")) +opts.Add(BoolVariable("accesskit", "Enable the AccessKit driver for screen reader support", True)) opts.Add(BoolVariable("sdl", "Enable the SDL3 input driver", True)) opts.Add( EnumVariable( @@ -236,6 +235,7 @@ opts.Add(BoolVariable("ninja", "Use the ninja backend for faster rebuilds", Fals opts.Add(BoolVariable("ninja_auto_run", "Run ninja automatically after generating the ninja file", True)) opts.Add("ninja_file", "Path to the generated ninja file", "build.ninja") opts.Add(BoolVariable("compiledb", "Generate compilation DB (`compile_commands.json`) for external tools", False)) +opts.Add(BoolVariable("compiledb_gen_only", "Exit after building the compilation database", False)) opts.Add( "num_jobs", "Use up to N jobs when compiling (equivalent to `-j N`). Defaults to max jobs - 1. Ignored if -j is used.", @@ -648,8 +648,11 @@ if env["build_profile"] != "": dbo = ft["disabled_build_options"] for c in dbo: env[c] = dbo[c] - except json.JSONDecodeError: - print_error(f'Failed to open feature build profile: "{env["build_profile"]}"') + except json.JSONDecodeError as err: + print_error(f'Failed to open feature build profile due to JSON decoding error: "{env["build_profile"]}"\n{err}') + Exit(255) + except FileNotFoundError: + print_error(f'Feature build profile not found at: "{env["build_profile"]}"') Exit(255) # 'dev_mode' and 'production' are aliases to set default options if they haven't been @@ -1169,7 +1172,6 @@ env.Append(BUILDERS=GLSL_BUILDERS) if env["compiledb"]: env.Tool("compilation_db") - env.Alias("compiledb", env.CompilationDatabase()) env.NoCache(env.CompilationDatabase()) if not env["verbose"]: env["COMPILATIONDB_COMSTR"] = "$GENCOMSTR" @@ -1229,6 +1231,12 @@ if env["vsproj"]: # Miscellaneous & post-build methods. if not env.GetOption("clean") and not env.GetOption("help"): + if env["compiledb"] and env["compiledb_gen_only"]: + from SCons.Tool.compilation_db import write_compilation_db + + write_compilation_db([env.File("compile_commands.json")], [], env) + env.Exit() + methods.dump(env) methods.show_progress(env) methods.prepare_purge(env) diff --git a/engine/core/config/engine.cpp b/engine/core/config/engine.cpp index 154fed8fa1..9a2c9318da 100644 --- a/engine/core/config/engine.cpp +++ b/engine/core/config/engine.cpp @@ -34,6 +34,7 @@ #include "core/config/project_settings.h" #include "core/donors.gen.h" #include "core/license.gen.h" +#include "core/object/ref_counted.h" #include "core/variant/typed_array.h" #include "core/version.h" #include "servers/rendering/rendering_device.h" diff --git a/engine/core/config/project_settings.cpp b/engine/core/config/project_settings.cpp index 8f293a3000..ff111b1fad 100644 --- a/engine/core/config/project_settings.cpp +++ b/engine/core/config/project_settings.cpp @@ -30,23 +30,27 @@ #include "project_settings.h" -#include "core/core_bind.h" // For Compression enum. #include "core/input/input_map.h" +#include "core/io/compression.h" #include "core/io/config_file.h" #include "core/io/dir_access.h" #include "core/io/file_access.h" #include "core/io/file_access_pack.h" #include "core/io/marshalls.h" #include "core/io/resource_uid.h" +#include "core/object/callable_mp.h" +#include "core/object/class_db.h" +#include "core/object/message_queue.h" #include "core/object/script_language.h" +#include "core/os/os.h" #include "core/templates/rb_set.h" #include "core/variant/typed_array.h" #include "core/variant/variant_parser.h" #include "core/version.h" -#include "servers/rendering/rendering_server.h" #ifdef TOOLS_ENABLED -#include "modules/modules_enabled.gen.h" // For mono. +#include "core/config/engine.h" +#include "modules/modules_enabled.gen.h" // IWYU pragma: keep. For mono. #endif // TOOLS_ENABLED ProjectSettings *ProjectSettings::get_singleton() { @@ -635,7 +639,8 @@ void ProjectSettings::_convert_to_last_version(int p_from_version) { } else if (p_from_version <= 6) { // Check if we still have legacy boot splash (removed in 4.6), map it to new project setting, then remove legacy setting. if (has_setting("application/boot_splash/fullsize")) { - set_setting("application/boot_splash/stretch_mode", RenderingServer::map_scaling_option_to_stretch_mode(get_setting("application/boot_splash/fullsize"))); + // See RenderingServerEnums::SplashStretchMode. + set_setting("application/boot_splash/stretch_mode", get_setting("application/boot_splash/fullsize") ? 1 : 0); set_setting("application/boot_splash/fullsize", Variant()); } } @@ -1057,6 +1062,7 @@ bool ProjectSettings::is_builtin_setting(const String &p_name) const { void ProjectSettings::clear(const String &p_name) { ERR_FAIL_COND_MSG(!props.has(p_name), vformat("Request for nonexistent project setting: '%s'.", p_name)); props.erase(p_name); + _queue_changed(p_name); } Error ProjectSettings::save() { @@ -1640,7 +1646,7 @@ void ProjectSettings::_bind_methods() { void ProjectSettings::_add_builtin_input_map() { if (InputMap::get_singleton()) { - HashMap>> builtins = InputMap::get_singleton()->get_builtins(); + HashMap>> builtins(InputMap::get_singleton()->get_builtins()); for (KeyValue>> &E : builtins) { Array events; @@ -1703,6 +1709,7 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "accessibility/general/accessibility_support", PROPERTY_HINT_ENUM, "Auto (When Screen Reader is Running),Always Active,Disabled"), 0); GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "accessibility/general/updates_per_second", PROPERTY_HINT_RANGE, "1,100,1"), 60); + GLOBAL_DEF(PropertyInfo(Variant::STRING, "accessibility/general/accessibility_driver", PROPERTY_HINT_ENUM, "accesskit,dummy"), "accesskit"); // The default window size is tuned to: // - Have a 16:9 aspect ratio, @@ -1732,6 +1739,8 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF(PropertyInfo(Variant::INT, "display/window/size/window_width_override", PROPERTY_HINT_RANGE, "0,7680,1,or_greater"), 0); // 8K resolution GLOBAL_DEF(PropertyInfo(Variant::INT, "display/window/size/window_height_override", PROPERTY_HINT_RANGE, "0,4320,1,or_greater"), 0); // 8K resolution + GLOBAL_DEF("display/window/hdr/request_hdr_output", false); + GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true); GLOBAL_DEF("animation/warnings/check_invalid_track_paths", true); GLOBAL_DEF("animation/warnings/check_angle_interpolation_type_conflicting", true); @@ -1751,10 +1760,10 @@ ProjectSettings::ProjectSettings() { _add_builtin_input_map(); - // Keep the enum values in sync with the `DisplayServer::ScreenOrientation` enum. + // Keep the enum values in sync with the `DisplayServerEnums::ScreenOrientation` enum. custom_prop_info["display/window/handheld/orientation"] = PropertyInfo(Variant::INT, "display/window/handheld/orientation", PROPERTY_HINT_ENUM, "Landscape,Portrait,Reverse Landscape,Reverse Portrait,Sensor Landscape,Sensor Portrait,Sensor"); GLOBAL_DEF("display/window/subwindows/embed_subwindows", true); - // Keep the enum values in sync with the `DisplayServer::VSyncMode` enum. + // Keep the enum values in sync with the `DisplayServerEnums::VSyncMode` enum. custom_prop_info["display/window/vsync/vsync_mode"] = PropertyInfo(Variant::INT, "display/window/vsync/vsync_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled,Adaptive,Mailbox"); GLOBAL_DEF("display/window/frame_pacing/android/enable_frame_pacing", true); diff --git a/engine/core/config/project_settings.h b/engine/core/config/project_settings.h index a861f49f46..e197a8d93b 100644 --- a/engine/core/config/project_settings.h +++ b/engine/core/config/project_settings.h @@ -31,6 +31,7 @@ #pragma once #include "core/object/object.h" +#include "core/os/thread_safe.h" #include "core/templates/rb_map.h" template @@ -204,7 +205,7 @@ public: const HashMap &get_custom_property_info() const; uint64_t get_last_saved_time() { return last_save_time; } - List get_input_presets() const { return input_presets; } + List get_input_presets() const { return List(input_presets); } Variant get_setting_with_override(const StringName &p_name) const; Variant get_setting_with_override_and_custom_features(const StringName &p_name, const Vector &p_features) const; diff --git a/engine/core/core_bind.compat.inc b/engine/core/core_bind.compat.inc index 8bfa66e4b7..260865221c 100644 --- a/engine/core/core_bind.compat.inc +++ b/engine/core/core_bind.compat.inc @@ -30,6 +30,10 @@ #ifndef DISABLE_DEPRECATED +#include "core_bind.h" + +#include "core/object/class_db.h" + namespace CoreBind { // Semaphore diff --git a/engine/core/core_bind.cpp b/engine/core/core_bind.cpp index 2fd854f48e..e78b5a5622 100644 --- a/engine/core/core_bind.cpp +++ b/engine/core/core_bind.cpp @@ -31,6 +31,7 @@ #include "core_bind.h" #include "core_bind.compat.inc" +#include "core/config/engine.h" #include "core/config/project_settings.h" #include "core/crypto/crypto_core.h" #include "core/debugger/engine_debugger.h" @@ -39,8 +40,11 @@ #include "core/io/marshalls.h" #include "core/math/geometry_2d.h" #include "core/math/geometry_3d.h" +#include "core/object/class_db.h" #include "core/os/keyboard.h" #include "core/os/main_loop.h" +#include "core/os/os.h" +#include "core/os/process_id.h" #include "core/os/thread_safe.h" #include "core/variant/typed_array.h" @@ -425,7 +429,7 @@ int OS::create_instance(const Vector &p_arguments) { for (const String &arg : p_arguments) { args.push_back(arg); } - ::OS::ProcessID pid = 0; + ProcessID pid = 0; Error err = ::OS::get_singleton()->create_instance(args, &pid); if (err != OK) { return -1; @@ -446,7 +450,7 @@ int OS::create_process(const String &p_path, const Vector &p_arguments, for (const String &arg : p_arguments) { args.push_back(arg); } - ::OS::ProcessID pid = 0; + ProcessID pid = 0; Error err = ::OS::get_singleton()->create_process(p_path, args, &pid, p_open_console); if (err != OK) { return -1; diff --git a/engine/core/core_bind.h b/engine/core/core_bind.h index 9848393e1d..547c1f0b66 100644 --- a/engine/core/core_bind.h +++ b/engine/core/core_bind.h @@ -31,8 +31,10 @@ #pragma once #include "core/debugger/engine_profiler.h" +#include "core/io/logger.h" #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" +#include "core/object/class_db.h" #include "core/object/script_backtrace.h" #include "core/os/semaphore.h" #include "core/os/thread.h" diff --git a/engine/core/core_builders.py b/engine/core/core_builders.py index d6b1a2b7af..c9d76eb068 100644 --- a/engine/core/core_builders.py +++ b/engine/core/core_builders.py @@ -41,7 +41,7 @@ def version_hash_builder(target, source, env): #include "core/version.h" const char *const GODOT_VERSION_HASH = "{git_hash}"; -const uint64_t GODOT_VERSION_TIMESTAMP = {git_timestamp}; +const unsigned long long GODOT_VERSION_TIMESTAMP = {git_timestamp}; """.format(**source[0].read()) ) @@ -63,7 +63,7 @@ def encryption_key_builder(target, source, env): with methods.generated_wrapper(str(target[0])) as file: file.write( f"""\ -#include "core/config/project_settings.h" +#include uint8_t script_encryption_key[32] = {{ {methods.format_buffer(buffer, 1)} diff --git a/engine/core/core_constants.cpp b/engine/core/core_constants.cpp index b325129538..b99da1fe2d 100644 --- a/engine/core/core_constants.cpp +++ b/engine/core/core_constants.cpp @@ -30,10 +30,13 @@ #include "core_constants.h" -#include "core/input/input_event.h" -#include "core/object/class_db.h" +#include "core/input/input_enums.h" +#include "core/object/method_bind.h" // IWYU pragma: keep. To bind `MethodFlags`. +#include "core/object/object.h" #include "core/os/keyboard.h" +#include "core/variant/type_info.h" #include "core/variant/variant.h" +#include "core/variant/variant_caster.h" struct _CoreConstant { #ifdef DEBUG_ENABLED @@ -69,183 +72,183 @@ static HashMap> _global_enums; #ifdef DEBUG_ENABLED -#define BIND_CORE_CONSTANT(m_constant) \ +#define BIND_CORE_CONSTANT(m_constant) \ _global_constants.push_back(_CoreConstant(StringName(), #m_constant, m_constant)); \ _global_constants_map[#m_constant] = _global_constants.size() - 1; -#define BIND_CORE_ENUM_CONSTANT(m_constant) \ - { \ - StringName enum_name = __constant_get_enum_name(m_constant); \ - _global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant)); \ - _global_constants_map[#m_constant] = _global_constants.size() - 1; \ +#define BIND_CORE_ENUM_CONSTANT(m_constant) \ + { \ + StringName enum_name = __constant_get_enum_name(m_constant); \ + _global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant)); \ + _global_constants_map[#m_constant] = _global_constants.size() - 1; \ _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ } -#define BIND_CORE_BITFIELD_FLAG(m_constant) \ - { \ - StringName enum_name = __constant_get_bitfield_name(m_constant); \ +#define BIND_CORE_BITFIELD_FLAG(m_constant) \ + { \ + StringName enum_name = __constant_get_bitfield_name(m_constant); \ _global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant, false, true)); \ - _global_constants_map[#m_constant] = _global_constants.size() - 1; \ + _global_constants_map[#m_constant] = _global_constants.size() - 1; \ _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ } // This just binds enum classes as if they were regular enum constants. -#define BIND_CORE_ENUM_CLASS_CONSTANT(m_enum, m_prefix, m_member) \ - { \ - StringName enum_name = __constant_get_enum_name(m_enum::m_member); \ +#define BIND_CORE_ENUM_CLASS_CONSTANT(m_enum, m_prefix, m_member) \ + { \ + StringName enum_name = __constant_get_enum_name(m_enum::m_member); \ _global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member)); \ - _global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \ - _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ + _global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \ + _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ } -#define BIND_CORE_BITFIELD_CLASS_FLAG(m_enum, m_prefix, m_member) \ - { \ - StringName enum_name = __constant_get_bitfield_name(m_enum::m_member); \ +#define BIND_CORE_BITFIELD_CLASS_FLAG(m_enum, m_prefix, m_member) \ + { \ + StringName enum_name = __constant_get_bitfield_name(m_enum::m_member); \ _global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member, false, true)); \ - _global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \ - _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ - } - -#define BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(m_enum, m_name, m_member) \ - { \ - StringName enum_name = __constant_get_enum_name(m_enum::m_member); \ - _global_constants.push_back(_CoreConstant(enum_name, #m_name, (int64_t)m_enum::m_member)); \ - _global_constants_map[#m_name] = _global_constants.size() - 1; \ + _global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \ _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ } -#define BIND_CORE_BITFIELD_CLASS_FLAG_CUSTOM(m_enum, m_name, m_member) \ - { \ - StringName enum_name = __constant_get_bitfield_name(m_enum::m_member); \ +#define BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(m_enum, m_name, m_member) \ + { \ + StringName enum_name = __constant_get_enum_name(m_enum::m_member); \ + _global_constants.push_back(_CoreConstant(enum_name, #m_name, (int64_t)m_enum::m_member)); \ + _global_constants_map[#m_name] = _global_constants.size() - 1; \ + _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ + } + +#define BIND_CORE_BITFIELD_CLASS_FLAG_CUSTOM(m_enum, m_name, m_member) \ + { \ + StringName enum_name = __constant_get_bitfield_name(m_enum::m_member); \ _global_constants.push_back(_CoreConstant(enum_name, #m_name, (int64_t)m_enum::m_member, false, true)); \ - _global_constants_map[#m_name] = _global_constants.size() - 1; \ - _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ - } - -#define BIND_CORE_ENUM_CLASS_CONSTANT_NO_VAL(m_enum, m_prefix, m_member) \ - { \ - StringName enum_name = __constant_get_enum_name(m_enum::m_member); \ - _global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member, true)); \ - _global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \ - _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ - } - -#define BIND_CORE_ENUM_CONSTANT_CUSTOM(m_custom_name, m_constant) \ - { \ - StringName enum_name = __constant_get_enum_name(m_constant); \ - _global_constants.push_back(_CoreConstant(enum_name, m_custom_name, m_constant)); \ - _global_constants_map[m_custom_name] = _global_constants.size() - 1; \ + _global_constants_map[#m_name] = _global_constants.size() - 1; \ _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ } -#define BIND_CORE_CONSTANT_NO_VAL(m_constant) \ +#define BIND_CORE_ENUM_CLASS_CONSTANT_NO_VAL(m_enum, m_prefix, m_member) \ + { \ + StringName enum_name = __constant_get_enum_name(m_enum::m_member); \ + _global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member, true)); \ + _global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \ + _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ + } + +#define BIND_CORE_ENUM_CONSTANT_CUSTOM(m_custom_name, m_constant) \ + { \ + StringName enum_name = __constant_get_enum_name(m_constant); \ + _global_constants.push_back(_CoreConstant(enum_name, m_custom_name, m_constant)); \ + _global_constants_map[m_custom_name] = _global_constants.size() - 1; \ + _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ + } + +#define BIND_CORE_CONSTANT_NO_VAL(m_constant) \ _global_constants.push_back(_CoreConstant(StringName(), #m_constant, m_constant, true)); \ _global_constants_map[#m_constant] = _global_constants.size() - 1; -#define BIND_CORE_ENUM_CONSTANT_NO_VAL(m_constant) \ - { \ - StringName enum_name = __constant_get_enum_name(m_constant); \ - _global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant, true)); \ - _global_constants_map[#m_constant] = _global_constants.size() - 1; \ +#define BIND_CORE_ENUM_CONSTANT_NO_VAL(m_constant) \ + { \ + StringName enum_name = __constant_get_enum_name(m_constant); \ + _global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant, true)); \ + _global_constants_map[#m_constant] = _global_constants.size() - 1; \ _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ } -#define BIND_CORE_ENUM_CONSTANT_CUSTOM_NO_VAL(m_custom_name, m_constant) \ - { \ - StringName enum_name = __constant_get_enum_name(m_constant); \ - _global_constants.push_back(_CoreConstant(enum_name, m_custom_name, m_constant, true)); \ - _global_constants_map[m_custom_name] = _global_constants.size() - 1; \ +#define BIND_CORE_ENUM_CONSTANT_CUSTOM_NO_VAL(m_custom_name, m_constant) \ + { \ + StringName enum_name = __constant_get_enum_name(m_constant); \ + _global_constants.push_back(_CoreConstant(enum_name, m_custom_name, m_constant, true)); \ + _global_constants_map[m_custom_name] = _global_constants.size() - 1; \ _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ } #else -#define BIND_CORE_CONSTANT(m_constant) \ +#define BIND_CORE_CONSTANT(m_constant) \ _global_constants.push_back(_CoreConstant(StringName(), #m_constant, m_constant)); \ _global_constants_map[#m_constant] = _global_constants.size() - 1; -#define BIND_CORE_ENUM_CONSTANT(m_constant) \ - { \ - StringName enum_name = __constant_get_enum_name(m_constant); \ - _global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant)); \ - _global_constants_map[#m_constant] = _global_constants.size() - 1; \ +#define BIND_CORE_ENUM_CONSTANT(m_constant) \ + { \ + StringName enum_name = __constant_get_enum_name(m_constant); \ + _global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant)); \ + _global_constants_map[#m_constant] = _global_constants.size() - 1; \ _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ } -#define BIND_CORE_BITFIELD_FLAG(m_constant) \ - { \ - StringName enum_name = __constant_get_bitfield_name(m_constant); \ - _global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant)); \ - _global_constants_map[#m_constant] = _global_constants.size() - 1; \ +#define BIND_CORE_BITFIELD_FLAG(m_constant) \ + { \ + StringName enum_name = __constant_get_bitfield_name(m_constant); \ + _global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant)); \ + _global_constants_map[#m_constant] = _global_constants.size() - 1; \ _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ } // This just binds enum classes as if they were regular enum constants. -#define BIND_CORE_ENUM_CLASS_CONSTANT(m_enum, m_prefix, m_member) \ - { \ - StringName enum_name = __constant_get_enum_name(m_enum::m_member); \ +#define BIND_CORE_ENUM_CLASS_CONSTANT(m_enum, m_prefix, m_member) \ + { \ + StringName enum_name = __constant_get_enum_name(m_enum::m_member); \ _global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member)); \ - _global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \ - _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ - } - -#define BIND_CORE_BITFIELD_CLASS_FLAG(m_enum, m_prefix, m_member) \ - { \ - StringName enum_name = __constant_get_bitfield_name(m_enum::m_member); \ - _global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member)); \ - _global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \ - _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ - } - -#define BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(m_enum, m_name, m_member) \ - { \ - StringName enum_name = __constant_get_enum_name(m_enum::m_member); \ - _global_constants.push_back(_CoreConstant(enum_name, #m_name, (int64_t)m_enum::m_member)); \ - _global_constants_map[#m_name] = _global_constants.size() - 1; \ + _global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \ _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ } -#define BIND_CORE_BITFIELD_CLASS_FLAG_CUSTOM(m_enum, m_name, m_member) \ - { \ - StringName enum_name = __constant_get_bitfield_name(m_enum::m_member); \ - _global_constants.push_back(_CoreConstant(enum_name, #m_name, (int64_t)m_enum::m_member)); \ - _global_constants_map[#m_name] = _global_constants.size() - 1; \ - _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ - } - -#define BIND_CORE_ENUM_CLASS_CONSTANT_NO_VAL(m_enum, m_prefix, m_member) \ - { \ - StringName enum_name = __constant_get_enum_name(m_enum::m_member); \ +#define BIND_CORE_BITFIELD_CLASS_FLAG(m_enum, m_prefix, m_member) \ + { \ + StringName enum_name = __constant_get_bitfield_name(m_enum::m_member); \ _global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member)); \ - _global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \ - _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ - } - -#define BIND_CORE_ENUM_CONSTANT_CUSTOM(m_custom_name, m_constant) \ - { \ - StringName enum_name = __constant_get_enum_name(m_constant); \ - _global_constants.push_back(_CoreConstant(enum_name, m_custom_name, m_constant)); \ - _global_constants_map[m_custom_name] = _global_constants.size() - 1; \ + _global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \ _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ } -#define BIND_CORE_CONSTANT_NO_VAL(m_constant) \ +#define BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(m_enum, m_name, m_member) \ + { \ + StringName enum_name = __constant_get_enum_name(m_enum::m_member); \ + _global_constants.push_back(_CoreConstant(enum_name, #m_name, (int64_t)m_enum::m_member)); \ + _global_constants_map[#m_name] = _global_constants.size() - 1; \ + _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ + } + +#define BIND_CORE_BITFIELD_CLASS_FLAG_CUSTOM(m_enum, m_name, m_member) \ + { \ + StringName enum_name = __constant_get_bitfield_name(m_enum::m_member); \ + _global_constants.push_back(_CoreConstant(enum_name, #m_name, (int64_t)m_enum::m_member)); \ + _global_constants_map[#m_name] = _global_constants.size() - 1; \ + _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ + } + +#define BIND_CORE_ENUM_CLASS_CONSTANT_NO_VAL(m_enum, m_prefix, m_member) \ + { \ + StringName enum_name = __constant_get_enum_name(m_enum::m_member); \ + _global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member)); \ + _global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \ + _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ + } + +#define BIND_CORE_ENUM_CONSTANT_CUSTOM(m_custom_name, m_constant) \ + { \ + StringName enum_name = __constant_get_enum_name(m_constant); \ + _global_constants.push_back(_CoreConstant(enum_name, m_custom_name, m_constant)); \ + _global_constants_map[m_custom_name] = _global_constants.size() - 1; \ + _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ + } + +#define BIND_CORE_CONSTANT_NO_VAL(m_constant) \ _global_constants.push_back(_CoreConstant(StringName(), #m_constant, m_constant)); \ _global_constants_map[#m_constant] = _global_constants.size() - 1; -#define BIND_CORE_ENUM_CONSTANT_NO_VAL(m_constant) \ - { \ - StringName enum_name = __constant_get_enum_name(m_constant); \ - _global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant)); \ - _global_constants_map[#m_constant] = _global_constants.size() - 1; \ +#define BIND_CORE_ENUM_CONSTANT_NO_VAL(m_constant) \ + { \ + StringName enum_name = __constant_get_enum_name(m_constant); \ + _global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant)); \ + _global_constants_map[#m_constant] = _global_constants.size() - 1; \ _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ } -#define BIND_CORE_ENUM_CONSTANT_CUSTOM_NO_VAL(m_custom_name, m_constant) \ - { \ - StringName enum_name = __constant_get_enum_name(m_constant); \ - _global_constants.push_back(_CoreConstant(enum_name, m_custom_name, m_constant)); \ - _global_constants_map[m_custom_name] = _global_constants.size() - 1; \ +#define BIND_CORE_ENUM_CONSTANT_CUSTOM_NO_VAL(m_custom_name, m_constant) \ + { \ + StringName enum_name = __constant_get_enum_name(m_constant); \ + _global_constants.push_back(_CoreConstant(enum_name, m_custom_name, m_constant)); \ + _global_constants_map[m_custom_name] = _global_constants.size() - 1; \ _global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ } @@ -549,6 +552,11 @@ void register_global_constants() { BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, PADDLE3); BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, PADDLE4); BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, TOUCHPAD); + BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, MISC2); + BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, MISC3); + BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, MISC4); + BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, MISC5); + BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, MISC6); BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, SDL_MAX); BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, MAX); @@ -729,6 +737,18 @@ void register_global_constants() { BIND_CORE_BITFIELD_FLAG(METHOD_FLAG_VIRTUAL_REQUIRED); BIND_CORE_BITFIELD_FLAG(METHOD_FLAGS_DEFAULT); + BIND_CORE_CONSTANT(UINT8_MAX); + BIND_CORE_CONSTANT(UINT16_MAX); + BIND_CORE_CONSTANT(UINT32_MAX); + BIND_CORE_CONSTANT(INT8_MIN); + BIND_CORE_CONSTANT(INT8_MAX); + BIND_CORE_CONSTANT(INT16_MIN); + BIND_CORE_CONSTANT(INT16_MAX); + BIND_CORE_CONSTANT(INT32_MIN); + BIND_CORE_CONSTANT(INT32_MAX); + BIND_CORE_CONSTANT(INT64_MIN); + BIND_CORE_CONSTANT(INT64_MAX); + BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_NIL", Variant::NIL); BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_BOOL", Variant::BOOL); BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_INT", Variant::INT); diff --git a/engine/core/crypto/aes_context.cpp b/engine/core/crypto/aes_context.cpp index 53ca65021e..a1fa439771 100644 --- a/engine/core/crypto/aes_context.cpp +++ b/engine/core/crypto/aes_context.cpp @@ -28,7 +28,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#include "core/crypto/aes_context.h" +#include "aes_context.h" + +#include "core/object/class_db.h" Error AESContext::start(Mode p_mode, const PackedByteArray &p_key, const PackedByteArray &p_iv) { ERR_FAIL_COND_V_MSG(mode != MODE_MAX, ERR_ALREADY_IN_USE, "AESContext already started. Call 'finish' before starting a new one."); diff --git a/engine/core/crypto/aes_context.h b/engine/core/crypto/aes_context.h index bc13485eff..51d5d4cd6f 100644 --- a/engine/core/crypto/aes_context.h +++ b/engine/core/crypto/aes_context.h @@ -32,6 +32,7 @@ #include "core/crypto/crypto_core.h" #include "core/object/ref_counted.h" +#include "core/variant/type_info.h" class AESContext : public RefCounted { GDCLASS(AESContext, RefCounted); diff --git a/engine/core/crypto/crypto.cpp b/engine/core/crypto/crypto.cpp index 2f0c54763b..1824580110 100644 --- a/engine/core/crypto/crypto.cpp +++ b/engine/core/crypto/crypto.cpp @@ -30,6 +30,8 @@ #include "crypto.h" +#include "core/object/class_db.h" + /// Resources CryptoKey *(*CryptoKey::_create)(bool p_notify_postinitialize) = nullptr; diff --git a/engine/core/crypto/crypto_core.cpp b/engine/core/crypto/crypto_core.cpp index 9d2c346e3a..1c7d5d0790 100644 --- a/engine/core/crypto/crypto_core.cpp +++ b/engine/core/crypto/crypto_core.cpp @@ -31,6 +31,7 @@ #include "crypto_core.h" #include "core/os/os.h" +#include "core/string/ustring.h" #include #include diff --git a/engine/core/crypto/crypto_core.h b/engine/core/crypto/crypto_core.h index 8ef50fbcf7..a061a74387 100644 --- a/engine/core/crypto/crypto_core.h +++ b/engine/core/crypto/crypto_core.h @@ -30,7 +30,12 @@ #pragma once -#include "core/object/ref_counted.h" +#include "core/error/error_list.h" + +#include +#include + +class String; class CryptoCore { public: diff --git a/engine/core/crypto/hashing_context.cpp b/engine/core/crypto/hashing_context.cpp index 01acbdbc17..d852b4744b 100644 --- a/engine/core/crypto/hashing_context.cpp +++ b/engine/core/crypto/hashing_context.cpp @@ -31,6 +31,7 @@ #include "hashing_context.h" #include "core/crypto/crypto_core.h" +#include "core/object/class_db.h" Error HashingContext::start(HashType p_type) { ERR_FAIL_COND_V(ctx != nullptr, ERR_ALREADY_IN_USE); diff --git a/engine/core/crypto/hashing_context.h b/engine/core/crypto/hashing_context.h index c441da5056..a6318c999e 100644 --- a/engine/core/crypto/hashing_context.h +++ b/engine/core/crypto/hashing_context.h @@ -31,6 +31,7 @@ #pragma once #include "core/object/ref_counted.h" +#include "core/variant/type_info.h" class HashingContext : public RefCounted { GDCLASS(HashingContext, RefCounted); diff --git a/engine/core/debugger/engine_profiler.cpp b/engine/core/debugger/engine_profiler.cpp index abe0a4df77..d93a367f15 100644 --- a/engine/core/debugger/engine_profiler.cpp +++ b/engine/core/debugger/engine_profiler.cpp @@ -31,6 +31,7 @@ #include "engine_profiler.h" #include "core/debugger/engine_debugger.h" +#include "core/object/class_db.h" // IWYU pragma: keep. `GDVIRTUAL_BIND` macro. void EngineProfiler::_bind_methods() { GDVIRTUAL_BIND(_toggle, "enable", "options"); diff --git a/engine/core/debugger/engine_profiler.h b/engine/core/debugger/engine_profiler.h index ae6ddc48d4..42d4452f1d 100644 --- a/engine/core/debugger/engine_profiler.h +++ b/engine/core/debugger/engine_profiler.h @@ -30,7 +30,7 @@ #pragma once -#include "core/object/gdvirtual.gen.inc" +#include "core/object/gdvirtual.gen.h" #include "core/object/ref_counted.h" class EngineProfiler : public RefCounted { diff --git a/engine/core/debugger/local_debugger.cpp b/engine/core/debugger/local_debugger.cpp index cd9d55380b..42e6c32324 100644 --- a/engine/core/debugger/local_debugger.cpp +++ b/engine/core/debugger/local_debugger.cpp @@ -34,6 +34,8 @@ #include "core/os/main_loop.h" #include "core/os/os.h" +#include + struct LocalDebugger::ScriptsProfiler { struct ProfileInfoSort { bool operator()(const ScriptLanguage::ProfilingInfo &A, const ScriptLanguage::ProfilingInfo &B) const { diff --git a/engine/core/debugger/local_debugger.h b/engine/core/debugger/local_debugger.h index 5c603e5f86..91c8d8be40 100644 --- a/engine/core/debugger/local_debugger.h +++ b/engine/core/debugger/local_debugger.h @@ -31,7 +31,6 @@ #pragma once #include "core/debugger/engine_debugger.h" -#include "core/object/script_language.h" #include "core/templates/list.h" class LocalDebugger : public EngineDebugger { diff --git a/engine/core/debugger/remote_debugger.cpp b/engine/core/debugger/remote_debugger.cpp index bb0271e76a..b1f2cd21c8 100644 --- a/engine/core/debugger/remote_debugger.cpp +++ b/engine/core/debugger/remote_debugger.cpp @@ -30,6 +30,7 @@ #include "remote_debugger.h" +#include "core/config/engine.h" #include "core/config/project_settings.h" #include "core/debugger/debugger_marshalls.h" #include "core/debugger/engine_debugger.h" @@ -38,6 +39,7 @@ #include "core/input/input.h" #include "core/io/resource_loader.h" #include "core/math/expression.h" +#include "core/object/class_db.h" #include "core/object/script_language.h" #include "core/os/os.h" #include "servers/display/display_server.h" @@ -426,12 +428,12 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { } send_message("debug_enter", msg); - Input::MouseMode mouse_mode = Input::MOUSE_MODE_VISIBLE; + Input::MouseMode mouse_mode = Input::MouseMode::MOUSE_MODE_VISIBLE; if (Thread::is_main_thread()) { mouse_mode = Input::get_singleton()->get_mouse_mode(); - if (mouse_mode != Input::MOUSE_MODE_VISIBLE) { - Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE); + if (mouse_mode != Input::MouseMode::MOUSE_MODE_VISIBLE) { + Input::get_singleton()->set_mouse_mode(Input::MouseMode::MOUSE_MODE_VISIBLE); } } else { MutexLock mutex_lock(mutex); diff --git a/engine/core/debugger/remote_debugger.h b/engine/core/debugger/remote_debugger.h index 8dfc3cecac..9c64342a02 100644 --- a/engine/core/debugger/remote_debugger.h +++ b/engine/core/debugger/remote_debugger.h @@ -33,8 +33,6 @@ #include "core/debugger/debugger_marshalls.h" #include "core/debugger/engine_debugger.h" #include "core/debugger/remote_debugger_peer.h" -#include "core/object/class_db.h" -#include "core/string/string_name.h" #include "core/string/ustring.h" #include "core/variant/array.h" diff --git a/engine/core/debugger/remote_debugger_peer.cpp b/engine/core/debugger/remote_debugger_peer.cpp index 759394a2a5..19dea3ec04 100644 --- a/engine/core/debugger/remote_debugger_peer.cpp +++ b/engine/core/debugger/remote_debugger_peer.cpp @@ -32,6 +32,9 @@ #include "core/config/project_settings.h" #include "core/io/marshalls.h" +#include "core/io/stream_peer_socket.h" +#include "core/io/stream_peer_tcp.h" +#include "core/io/stream_peer_uds.h" #include "core/os/os.h" bool RemoteDebuggerPeerTCP::is_peer_connected() { diff --git a/engine/core/debugger/remote_debugger_peer.h b/engine/core/debugger/remote_debugger_peer.h index 6942c09bf9..dec7d91106 100644 --- a/engine/core/debugger/remote_debugger_peer.h +++ b/engine/core/debugger/remote_debugger_peer.h @@ -30,13 +30,13 @@ #pragma once -#include "core/io/stream_peer_tcp.h" -#include "core/io/stream_peer_uds.h" #include "core/object/ref_counted.h" #include "core/os/mutex.h" #include "core/os/thread.h" #include "core/string/ustring.h" +class StreamPeerSocket; + class RemoteDebuggerPeer : public RefCounted { GDSOFTCLASS(RemoteDebuggerPeer, RefCounted); diff --git a/engine/core/doc_data.cpp b/engine/core/doc_data.cpp index 92de0a8070..9cd8e51b61 100644 --- a/engine/core/doc_data.cpp +++ b/engine/core/doc_data.cpp @@ -30,6 +30,8 @@ #include "doc_data.h" +#include "core/object/object.h" + String DocData::get_default_value_string(const Variant &p_value) { const Variant::Type type = p_value.get_type(); if (type == Variant::ARRAY) { diff --git a/engine/core/doc_data.h b/engine/core/doc_data.h index 80b7fa2382..c0aa165212 100644 --- a/engine/core/doc_data.h +++ b/engine/core/doc_data.h @@ -30,7 +30,6 @@ #pragma once -#include "core/io/xml_parser.h" #include "core/variant/variant.h" class DocData { diff --git a/engine/core/error/error_macros.cpp b/engine/core/error/error_macros.cpp index 9734c79790..82e8376bcd 100644 --- a/engine/core/error/error_macros.cpp +++ b/engine/core/error/error_macros.cpp @@ -36,6 +36,8 @@ #include "core/os/os.h" #include "core/string/ustring.h" +#include + // Optional physics interpolation warnings try to include the path to the relevant node. #if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) #include "core/config/project_settings.h" diff --git a/engine/core/error/error_macros.h b/engine/core/error/error_macros.h index 034f16f2cb..e4c34a267a 100644 --- a/engine/core/error/error_macros.h +++ b/engine/core/error/error_macros.h @@ -32,10 +32,6 @@ #include "core/typedefs.h" -#ifdef _MSC_VER -#include // `__fastfail()`. -#endif - class String; class ObjectID; @@ -95,11 +91,11 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, #define FUNCTION_STR __FUNCTION__ #endif -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(__clang__) /** * Don't use GENERATE_TRAP() directly, should only be used be the macros below. */ -#define GENERATE_TRAP() __fastfail(7 /* FAST_FAIL_FATAL_APP_EXIT */) +#define GENERATE_TRAP() __debugbreak() #else /** * Don't use GENERATE_TRAP() directly, should only be used be the macros below. @@ -135,32 +131,32 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. * If not, the current function returns. */ -#define ERR_FAIL_INDEX(m_index, m_size) \ - if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ +#define ERR_FAIL_INDEX(m_index, m_size) \ + if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \ - return; \ - } else \ + return; \ + } else \ ((void)0) /** * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. * If not, prints `m_msg` and the current function returns. */ -#define ERR_FAIL_INDEX_MSG(m_index, m_size, m_msg) \ - if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ +#define ERR_FAIL_INDEX_MSG(m_index, m_size, m_msg) \ + if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \ - return; \ - } else \ + return; \ + } else \ ((void)0) /** * Same as `ERR_FAIL_INDEX_MSG` but also notifies the editor. */ -#define ERR_FAIL_INDEX_EDMSG(m_index, m_size, m_msg) \ - if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ +#define ERR_FAIL_INDEX_EDMSG(m_index, m_size, m_msg) \ + if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \ - return; \ - } else \ + return; \ + } else \ ((void)0) /** @@ -170,32 +166,32 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. * If not, the current function returns `m_retval`. */ -#define ERR_FAIL_INDEX_V(m_index, m_size, m_retval) \ - if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ +#define ERR_FAIL_INDEX_V(m_index, m_size, m_retval) \ + if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \ - return m_retval; \ - } else \ + return m_retval; \ + } else \ ((void)0) /** * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. * If not, prints `m_msg` and the current function returns `m_retval`. */ -#define ERR_FAIL_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \ - if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ +#define ERR_FAIL_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \ + if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \ - return m_retval; \ - } else \ + return m_retval; \ + } else \ ((void)0) /** * Same as `ERR_FAIL_INDEX_V_MSG` but also notifies the editor. */ -#define ERR_FAIL_INDEX_V_EDMSG(m_index, m_size, m_retval, m_msg) \ - if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ +#define ERR_FAIL_INDEX_V_EDMSG(m_index, m_size, m_retval, m_msg) \ + if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \ - return m_retval; \ - } else \ + return m_retval; \ + } else \ ((void)0) /** @@ -206,12 +202,12 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. * If not, the application crashes. */ -#define CRASH_BAD_INDEX(m_index, m_size) \ - if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ +#define CRASH_BAD_INDEX(m_index, m_size) \ + if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), "", false, true); \ - _err_flush_stdout(); \ - GENERATE_TRAP(); \ - } else \ + _err_flush_stdout(); \ + GENERATE_TRAP(); \ + } else \ ((void)0) /** @@ -221,12 +217,12 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. * If not, prints `m_msg` and the application crashes. */ -#define CRASH_BAD_INDEX_MSG(m_index, m_size, m_msg) \ - if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ +#define CRASH_BAD_INDEX_MSG(m_index, m_size, m_msg) \ + if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, false, true); \ - _err_flush_stdout(); \ - GENERATE_TRAP(); \ - } else \ + _err_flush_stdout(); \ + GENERATE_TRAP(); \ + } else \ ((void)0) // Unsigned integer index out of bounds error macros. @@ -238,32 +234,32 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, * Ensures an unsigned integer index `m_index` is less than `m_size`. * If not, the current function returns. */ -#define ERR_FAIL_UNSIGNED_INDEX(m_index, m_size) \ - if (unlikely((m_index) >= (m_size))) { \ +#define ERR_FAIL_UNSIGNED_INDEX(m_index, m_size) \ + if (unlikely((m_index) >= (m_size))) { \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \ - return; \ - } else \ + return; \ + } else \ ((void)0) /** * Ensures an unsigned integer index `m_index` is less than `m_size`. * If not, prints `m_msg` and the current function returns. */ -#define ERR_FAIL_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg) \ - if (unlikely((m_index) >= (m_size))) { \ +#define ERR_FAIL_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg) \ + if (unlikely((m_index) >= (m_size))) { \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \ - return; \ - } else \ + return; \ + } else \ ((void)0) /** * Same as `ERR_FAIL_UNSIGNED_INDEX_MSG` but also notifies the editor. */ -#define ERR_FAIL_UNSIGNED_INDEX_EDMSG(m_index, m_size, m_msg) \ - if (unlikely((m_index) >= (m_size))) { \ +#define ERR_FAIL_UNSIGNED_INDEX_EDMSG(m_index, m_size, m_msg) \ + if (unlikely((m_index) >= (m_size))) { \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \ - return; \ - } else \ + return; \ + } else \ ((void)0) /** @@ -273,32 +269,32 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, * Ensures an unsigned integer index `m_index` is less than `m_size`. * If not, the current function returns `m_retval`. */ -#define ERR_FAIL_UNSIGNED_INDEX_V(m_index, m_size, m_retval) \ - if (unlikely((m_index) >= (m_size))) { \ +#define ERR_FAIL_UNSIGNED_INDEX_V(m_index, m_size, m_retval) \ + if (unlikely((m_index) >= (m_size))) { \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \ - return m_retval; \ - } else \ + return m_retval; \ + } else \ ((void)0) /** * Ensures an unsigned integer index `m_index` is less than `m_size`. * If not, prints `m_msg` and the current function returns `m_retval`. */ -#define ERR_FAIL_UNSIGNED_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \ - if (unlikely((m_index) >= (m_size))) { \ +#define ERR_FAIL_UNSIGNED_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \ + if (unlikely((m_index) >= (m_size))) { \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \ - return m_retval; \ - } else \ + return m_retval; \ + } else \ ((void)0) /** * Same as `ERR_FAIL_UNSIGNED_INDEX_V_MSG` but also notifies the editor. */ -#define ERR_FAIL_UNSIGNED_INDEX_V_EDMSG(m_index, m_size, m_retval, m_msg) \ - if (unlikely((m_index) >= (m_size))) { \ +#define ERR_FAIL_UNSIGNED_INDEX_V_EDMSG(m_index, m_size, m_retval, m_msg) \ + if (unlikely((m_index) >= (m_size))) { \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \ - return m_retval; \ - } else \ + return m_retval; \ + } else \ ((void)0) /** @@ -309,12 +305,12 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, * Ensures an unsigned integer index `m_index` is less than `m_size`. * If not, the application crashes. */ -#define CRASH_BAD_UNSIGNED_INDEX(m_index, m_size) \ - if (unlikely((m_index) >= (m_size))) { \ +#define CRASH_BAD_UNSIGNED_INDEX(m_index, m_size) \ + if (unlikely((m_index) >= (m_size))) { \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), "", false, true); \ - _err_flush_stdout(); \ - GENERATE_TRAP(); \ - } else \ + _err_flush_stdout(); \ + GENERATE_TRAP(); \ + } else \ ((void)0) /** @@ -324,12 +320,12 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, * Ensures an unsigned integer index `m_index` is less than `m_size`. * If not, prints `m_msg` and the application crashes. */ -#define CRASH_BAD_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg) \ - if (unlikely((m_index) >= (m_size))) { \ +#define CRASH_BAD_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg) \ + if (unlikely((m_index) >= (m_size))) { \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, false, true); \ - _err_flush_stdout(); \ - GENERATE_TRAP(); \ - } else \ + _err_flush_stdout(); \ + GENERATE_TRAP(); \ + } else \ ((void)0) // Null reference error macros. @@ -341,32 +337,32 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, * Ensures a pointer `m_param` is not null. * If it is null, the current function returns. */ -#define ERR_FAIL_NULL(m_param) \ - if (unlikely(m_param == nullptr)) { \ +#define ERR_FAIL_NULL(m_param) \ + if (unlikely(m_param == nullptr)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null."); \ - return; \ - } else \ + return; \ + } else \ ((void)0) /** * Ensures a pointer `m_param` is not null. * If it is null, prints `m_msg` and the current function returns. */ -#define ERR_FAIL_NULL_MSG(m_param, m_msg) \ - if (unlikely(m_param == nullptr)) { \ +#define ERR_FAIL_NULL_MSG(m_param, m_msg) \ + if (unlikely(m_param == nullptr)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg); \ - return; \ - } else \ + return; \ + } else \ ((void)0) /** * Same as `ERR_FAIL_NULL_MSG` but also notifies the editor. */ -#define ERR_FAIL_NULL_EDMSG(m_param, m_msg) \ - if (unlikely(m_param == nullptr)) { \ +#define ERR_FAIL_NULL_EDMSG(m_param, m_msg) \ + if (unlikely(m_param == nullptr)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg, true); \ - return; \ - } else \ + return; \ + } else \ ((void)0) /** @@ -376,32 +372,32 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, * Ensures a pointer `m_param` is not null. * If it is null, the current function returns `m_retval`. */ -#define ERR_FAIL_NULL_V(m_param, m_retval) \ - if (unlikely(m_param == nullptr)) { \ +#define ERR_FAIL_NULL_V(m_param, m_retval) \ + if (unlikely(m_param == nullptr)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null."); \ - return m_retval; \ - } else \ + return m_retval; \ + } else \ ((void)0) /** * Ensures a pointer `m_param` is not null. * If it is null, prints `m_msg` and the current function returns `m_retval`. */ -#define ERR_FAIL_NULL_V_MSG(m_param, m_retval, m_msg) \ - if (unlikely(m_param == nullptr)) { \ +#define ERR_FAIL_NULL_V_MSG(m_param, m_retval, m_msg) \ + if (unlikely(m_param == nullptr)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg); \ - return m_retval; \ - } else \ + return m_retval; \ + } else \ ((void)0) /** * Same as `ERR_FAIL_NULL_V_MSG` but also notifies the editor. */ -#define ERR_FAIL_NULL_V_EDMSG(m_param, m_retval, m_msg) \ - if (unlikely(m_param == nullptr)) { \ +#define ERR_FAIL_NULL_V_EDMSG(m_param, m_retval, m_msg) \ + if (unlikely(m_param == nullptr)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg, true); \ - return m_retval; \ - } else \ + return m_retval; \ + } else \ ((void)0) /** @@ -413,11 +409,11 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, * Ensures `m_cond` is false. * If `m_cond` is true, the current function returns. */ -#define ERR_FAIL_COND(m_cond) \ - if (unlikely(m_cond)) { \ +#define ERR_FAIL_COND(m_cond) \ + if (unlikely(m_cond)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true."); \ - return; \ - } else \ + return; \ + } else \ ((void)0) /** @@ -427,21 +423,21 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, * If checking for null use ERR_FAIL_NULL_MSG instead. * If checking index bounds use ERR_FAIL_INDEX_MSG instead. */ -#define ERR_FAIL_COND_MSG(m_cond, m_msg) \ - if (unlikely(m_cond)) { \ +#define ERR_FAIL_COND_MSG(m_cond, m_msg) \ + if (unlikely(m_cond)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true.", m_msg); \ - return; \ - } else \ + return; \ + } else \ ((void)0) /** * Same as `ERR_FAIL_COND_MSG` but also notifies the editor. */ -#define ERR_FAIL_COND_EDMSG(m_cond, m_msg) \ - if (unlikely(m_cond)) { \ +#define ERR_FAIL_COND_EDMSG(m_cond, m_msg) \ + if (unlikely(m_cond)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true.", m_msg, true); \ - return; \ - } else \ + return; \ + } else \ ((void)0) /** @@ -453,11 +449,11 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, * Ensures `m_cond` is false. * If `m_cond` is true, the current function returns `m_retval`. */ -#define ERR_FAIL_COND_V(m_cond, m_retval) \ - if (unlikely(m_cond)) { \ +#define ERR_FAIL_COND_V(m_cond, m_retval) \ + if (unlikely(m_cond)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval)); \ - return m_retval; \ - } else \ + return m_retval; \ + } else \ ((void)0) /** @@ -467,21 +463,21 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, * If checking for null use ERR_FAIL_NULL_V_MSG instead. * If checking index bounds use ERR_FAIL_INDEX_V_MSG instead. */ -#define ERR_FAIL_COND_V_MSG(m_cond, m_retval, m_msg) \ - if (unlikely(m_cond)) { \ +#define ERR_FAIL_COND_V_MSG(m_cond, m_retval, m_msg) \ + if (unlikely(m_cond)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval), m_msg); \ - return m_retval; \ - } else \ + return m_retval; \ + } else \ ((void)0) /** * Same as `ERR_FAIL_COND_V_MSG` but also notifies the editor. */ -#define ERR_FAIL_COND_V_EDMSG(m_cond, m_retval, m_msg) \ - if (unlikely(m_cond)) { \ +#define ERR_FAIL_COND_V_EDMSG(m_cond, m_retval, m_msg) \ + if (unlikely(m_cond)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval), m_msg, true); \ - return m_retval; \ - } else \ + return m_retval; \ + } else \ ((void)0) /** @@ -491,32 +487,32 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, * Ensures `m_cond` is false. * If `m_cond` is true, the current loop continues. */ -#define ERR_CONTINUE(m_cond) \ - if (unlikely(m_cond)) { \ +#define ERR_CONTINUE(m_cond) \ + if (unlikely(m_cond)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing."); \ - continue; \ - } else \ + continue; \ + } else \ ((void)0) /** * Ensures `m_cond` is false. * If `m_cond` is true, prints `m_msg` and the current loop continues. */ -#define ERR_CONTINUE_MSG(m_cond, m_msg) \ - if (unlikely(m_cond)) { \ +#define ERR_CONTINUE_MSG(m_cond, m_msg) \ + if (unlikely(m_cond)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing.", m_msg); \ - continue; \ - } else \ + continue; \ + } else \ ((void)0) /** * Same as `ERR_CONTINUE_MSG` but also notifies the editor. */ -#define ERR_CONTINUE_EDMSG(m_cond, m_msg) \ - if (unlikely(m_cond)) { \ +#define ERR_CONTINUE_EDMSG(m_cond, m_msg) \ + if (unlikely(m_cond)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing.", m_msg, true); \ - continue; \ - } else \ + continue; \ + } else \ ((void)0) /** @@ -526,32 +522,32 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, * Ensures `m_cond` is false. * If `m_cond` is true, the current loop breaks. */ -#define ERR_BREAK(m_cond) \ - if (unlikely(m_cond)) { \ +#define ERR_BREAK(m_cond) \ + if (unlikely(m_cond)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking."); \ - break; \ - } else \ + break; \ + } else \ ((void)0) /** * Ensures `m_cond` is false. * If `m_cond` is true, prints `m_msg` and the current loop breaks. */ -#define ERR_BREAK_MSG(m_cond, m_msg) \ - if (unlikely(m_cond)) { \ +#define ERR_BREAK_MSG(m_cond, m_msg) \ + if (unlikely(m_cond)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking.", m_msg); \ - break; \ - } else \ + break; \ + } else \ ((void)0) /** * Same as `ERR_BREAK_MSG` but also notifies the editor. */ -#define ERR_BREAK_EDMSG(m_cond, m_msg) \ - if (unlikely(m_cond)) { \ +#define ERR_BREAK_EDMSG(m_cond, m_msg) \ + if (unlikely(m_cond)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking.", m_msg, true); \ - break; \ - } else \ + break; \ + } else \ ((void)0) /** @@ -562,12 +558,12 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, * Ensures `m_cond` is false. * If `m_cond` is true, the application crashes. */ -#define CRASH_COND(m_cond) \ - if (unlikely(m_cond)) { \ +#define CRASH_COND(m_cond) \ + if (unlikely(m_cond)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition \"" _STR(m_cond) "\" is true."); \ - _err_flush_stdout(); \ - GENERATE_TRAP(); \ - } else \ + _err_flush_stdout(); \ + GENERATE_TRAP(); \ + } else \ ((void)0) /** @@ -577,12 +573,12 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, * Ensures `m_cond` is false. * If `m_cond` is true, prints `m_msg` and the application crashes. */ -#define CRASH_COND_MSG(m_cond, m_msg) \ - if (unlikely(m_cond)) { \ +#define CRASH_COND_MSG(m_cond, m_msg) \ + if (unlikely(m_cond)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition \"" _STR(m_cond) "\" is true.", m_msg); \ - _err_flush_stdout(); \ - GENERATE_TRAP(); \ - } else \ + _err_flush_stdout(); \ + GENERATE_TRAP(); \ + } else \ ((void)0) // Generic error macros. @@ -594,11 +590,11 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, * * The current function returns. */ -#define ERR_FAIL() \ - if (true) { \ +#define ERR_FAIL() \ + if (true) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed."); \ - return; \ - } else \ + return; \ + } else \ ((void)0) /** @@ -607,21 +603,21 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, * * Prints `m_msg`, and the current function returns. */ -#define ERR_FAIL_MSG(m_msg) \ - if (true) { \ +#define ERR_FAIL_MSG(m_msg) \ + if (true) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed.", m_msg); \ - return; \ - } else \ + return; \ + } else \ ((void)0) /** * Same as `ERR_FAIL_MSG` but also notifies the editor. */ -#define ERR_FAIL_EDMSG(m_msg) \ - if (true) { \ +#define ERR_FAIL_EDMSG(m_msg) \ + if (true) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed.", m_msg, true); \ - return; \ - } else \ + return; \ + } else \ ((void)0) /** @@ -631,11 +627,11 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, * * The current function returns `m_retval`. */ -#define ERR_FAIL_V(m_retval) \ - if (true) { \ +#define ERR_FAIL_V(m_retval) \ + if (true) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval)); \ - return m_retval; \ - } else \ + return m_retval; \ + } else \ ((void)0) /** @@ -644,21 +640,21 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, * * Prints `m_msg`, and the current function returns `m_retval`. */ -#define ERR_FAIL_V_MSG(m_retval, m_msg) \ - if (true) { \ +#define ERR_FAIL_V_MSG(m_retval, m_msg) \ + if (true) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval), m_msg); \ - return m_retval; \ - } else \ + return m_retval; \ + } else \ ((void)0) /** * Same as `ERR_FAIL_V_MSG` but also notifies the editor. */ -#define ERR_FAIL_V_EDMSG(m_retval, m_msg) \ - if (true) { \ +#define ERR_FAIL_V_EDMSG(m_retval, m_msg) \ + if (true) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval), m_msg, true); \ - return m_retval; \ - } else \ + return m_retval; \ + } else \ ((void)0) /** @@ -680,27 +676,27 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, /** * Prints `m_msg` once during the application lifetime. */ -#define ERR_PRINT_ONCE(m_msg) \ - if (true) { \ - static bool warning_shown = false; \ - if (unlikely(!warning_shown)) { \ - warning_shown = true; \ +#define ERR_PRINT_ONCE(m_msg) \ + if (true) { \ + static bool warning_shown = false; \ + if (unlikely(!warning_shown)) { \ + warning_shown = true; \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg); \ - } \ - } else \ + } \ + } else \ ((void)0) /** * Same as `ERR_PRINT_ONCE` but also notifies the editor. */ -#define ERR_PRINT_ONCE_ED(m_msg) \ - if (true) { \ - static bool warning_shown = false; \ - if (unlikely(!warning_shown)) { \ - warning_shown = true; \ +#define ERR_PRINT_ONCE_ED(m_msg) \ + if (true) { \ + static bool warning_shown = false; \ + if (unlikely(!warning_shown)) { \ + warning_shown = true; \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, true); \ - } \ - } else \ + } \ + } else \ ((void)0) // Print warning message macros. @@ -724,37 +720,37 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, * * If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead. */ -#define WARN_PRINT_ONCE(m_msg) \ - if (true) { \ - static bool warning_shown = false; \ - if (unlikely(!warning_shown)) { \ - warning_shown = true; \ +#define WARN_PRINT_ONCE(m_msg) \ + if (true) { \ + static bool warning_shown = false; \ + if (unlikely(!warning_shown)) { \ + warning_shown = true; \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, false, ERR_HANDLER_WARNING); \ - } \ - } else \ + } \ + } else \ ((void)0) /** * Same as `WARN_PRINT_ONCE` but also notifies the editor. */ -#define WARN_PRINT_ONCE_ED(m_msg) \ - if (true) { \ - static bool warning_shown = false; \ - if (unlikely(!warning_shown)) { \ - warning_shown = true; \ +#define WARN_PRINT_ONCE_ED(m_msg) \ + if (true) { \ + static bool warning_shown = false; \ + if (unlikely(!warning_shown)) { \ + warning_shown = true; \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, true, ERR_HANDLER_WARNING); \ - } \ - } else \ + } \ + } else \ ((void)0) /** * Warns about `m_msg` only when verbose mode is enabled. */ -#define WARN_VERBOSE(m_msg) \ - { \ +#define WARN_VERBOSE(m_msg) \ + { \ if (is_print_verbose_enabled()) { \ - WARN_PRINT(m_msg); \ - } \ + WARN_PRINT(m_msg); \ + } \ } // Print deprecated warning message macros. @@ -762,27 +758,27 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, /** * Warns that the current function is deprecated. */ -#define WARN_DEPRECATED \ - if (true) { \ - static bool warning_shown = false; \ - if (unlikely(!warning_shown)) { \ - warning_shown = true; \ +#define WARN_DEPRECATED \ + if (true) { \ + static bool warning_shown = false; \ + if (unlikely(!warning_shown)) { \ + warning_shown = true; \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", false, ERR_HANDLER_WARNING); \ - } \ - } else \ + } \ + } else \ ((void)0) /** * Warns that the current function is deprecated and prints `m_msg`. */ -#define WARN_DEPRECATED_MSG(m_msg) \ - if (true) { \ - static bool warning_shown = false; \ - if (unlikely(!warning_shown)) { \ - warning_shown = true; \ +#define WARN_DEPRECATED_MSG(m_msg) \ + if (true) { \ + static bool warning_shown = false; \ + if (unlikely(!warning_shown)) { \ + warning_shown = true; \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", m_msg, false, ERR_HANDLER_WARNING); \ - } \ - } else \ + } \ + } else \ ((void)0) /** @@ -791,12 +787,12 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, * * The application crashes. */ -#define CRASH_NOW() \ - if (true) { \ +#define CRASH_NOW() \ + if (true) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/function failed."); \ - _err_flush_stdout(); \ - GENERATE_TRAP(); \ - } else \ + _err_flush_stdout(); \ + GENERATE_TRAP(); \ + } else \ ((void)0) /** @@ -804,12 +800,12 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, * * Prints `m_msg`, and then the application crashes. */ -#define CRASH_NOW_MSG(m_msg) \ - if (true) { \ +#define CRASH_NOW_MSG(m_msg) \ + if (true) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/function failed.", m_msg); \ - _err_flush_stdout(); \ - GENERATE_TRAP(); \ - } else \ + _err_flush_stdout(); \ + GENERATE_TRAP(); \ + } else \ ((void)0) /** @@ -828,26 +824,26 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, * and that can't fail for other contributors once the code is finished and merged. */ #ifdef DEV_ENABLED -#define DEV_ASSERT(m_cond) \ - if (unlikely(!(m_cond))) { \ +#define DEV_ASSERT(m_cond) \ + if (unlikely(!(m_cond))) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: DEV_ASSERT failed \"" _STR(m_cond) "\" is false."); \ - _err_flush_stdout(); \ - GENERATE_TRAP(); \ - } else \ + _err_flush_stdout(); \ + GENERATE_TRAP(); \ + } else \ ((void)0) #else #define DEV_ASSERT(m_cond) #endif #ifdef DEV_ENABLED -#define DEV_CHECK_ONCE(m_cond) \ - if (true) { \ - static bool first_print = true; \ - if (first_print && unlikely(!(m_cond))) { \ +#define DEV_CHECK_ONCE(m_cond) \ + if (true) { \ + static bool first_print = true; \ + if (first_print && unlikely(!(m_cond))) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "DEV_CHECK_ONCE failed \"" _STR(m_cond) "\" is false."); \ - first_print = false; \ - } \ - } else \ + first_print = false; \ + } \ + } else \ ((void)0) #else #define DEV_CHECK_ONCE(m_cond) diff --git a/engine/core/extension/SCsub b/engine/core/extension/SCsub index e40170227a..4690234ff8 100644 --- a/engine/core/extension/SCsub +++ b/engine/core/extension/SCsub @@ -7,7 +7,7 @@ import make_interface_dumper import make_interface_header import make_wrappers -env.CommandNoCache(["ext_wrappers.gen.inc"], "make_wrappers.py", env.Run(make_wrappers.run)) +env.CommandNoCache("ext_wrappers.gen.h", "make_wrappers.py", env.Run(make_wrappers.run)) env.CommandNoCache( "gdextension_interface_dump.gen.h", ["gdextension_interface.json", "make_interface_dumper.py"], diff --git a/engine/core/extension/extension_api_dump.cpp b/engine/core/extension/extension_api_dump.cpp index 06e7a990d2..9094c26f0b 100644 --- a/engine/core/extension/extension_api_dump.cpp +++ b/engine/core/extension/extension_api_dump.cpp @@ -35,6 +35,7 @@ #include "core/extension/gdextension_special_compat_hashes.h" #include "core/io/file_access.h" #include "core/io/json.h" +#include "core/object/class_db.h" #include "core/templates/pair.h" #include "core/version.h" @@ -281,59 +282,59 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) { // Member offsets, meta types and sizes. #define REAL_MEMBER_OFFSET(type, member) \ - { \ - type, \ - member, \ - "float", \ - sizeof(float), \ - "float", \ - sizeof(float), \ - "double", \ - sizeof(double), \ - "double", \ - sizeof(double), \ + { \ + type, \ + member, \ + "float", \ + sizeof(float), \ + "float", \ + sizeof(float), \ + "double", \ + sizeof(double), \ + "double", \ + sizeof(double), \ } #define INT32_MEMBER_OFFSET(type, member) \ - { \ - type, \ - member, \ - "int32", \ - sizeof(int32_t), \ - "int32", \ - sizeof(int32_t), \ - "int32", \ - sizeof(int32_t), \ - "int32", \ - sizeof(int32_t), \ + { \ + type, \ + member, \ + "int32", \ + sizeof(int32_t), \ + "int32", \ + sizeof(int32_t), \ + "int32", \ + sizeof(int32_t), \ + "int32", \ + sizeof(int32_t), \ } #define INT32_BASED_BUILTIN_MEMBER_OFFSET(type, member, member_type, member_elems) \ - { \ - type, \ - member, \ - member_type, \ - sizeof(int32_t) * member_elems, \ - member_type, \ - sizeof(int32_t) * member_elems, \ - member_type, \ - sizeof(int32_t) * member_elems, \ - member_type, \ - sizeof(int32_t) * member_elems, \ + { \ + type, \ + member, \ + member_type, \ + sizeof(int32_t) * member_elems, \ + member_type, \ + sizeof(int32_t) * member_elems, \ + member_type, \ + sizeof(int32_t) * member_elems, \ + member_type, \ + sizeof(int32_t) * member_elems, \ } #define REAL_BASED_BUILTIN_MEMBER_OFFSET(type, member, member_type, member_elems) \ - { \ - type, \ - member, \ - member_type, \ - sizeof(float) * member_elems, \ - member_type, \ - sizeof(float) * member_elems, \ - member_type, \ - sizeof(double) * member_elems, \ - member_type, \ - sizeof(double) * member_elems, \ + { \ + type, \ + member, \ + member_type, \ + sizeof(float) * member_elems, \ + member_type, \ + sizeof(float) * member_elems, \ + member_type, \ + sizeof(double) * member_elems, \ + member_type, \ + sizeof(double) * member_elems, \ } struct { diff --git a/engine/core/extension/extension_api_dump.h b/engine/core/extension/extension_api_dump.h index 027a8d916e..1f6e1de454 100644 --- a/engine/core/extension/extension_api_dump.h +++ b/engine/core/extension/extension_api_dump.h @@ -30,14 +30,17 @@ #pragma once -#include "core/extension/gdextension.h" - #ifdef TOOLS_ENABLED +#include "core/error/error_list.h" +#include "core/string/ustring.h" +#include "core/variant/dictionary.h" + class GDExtensionAPIDump { public: static Dictionary generate_extension_api(bool p_include_docs = false); static void generate_extension_json_file(const String &p_path, bool p_include_docs = false); static Error validate_extension_json_file(const String &p_path); }; + #endif diff --git a/engine/core/extension/gdextension.compat.inc b/engine/core/extension/gdextension.compat.inc index 9dea865dbd..454f2c5d42 100644 --- a/engine/core/extension/gdextension.compat.inc +++ b/engine/core/extension/gdextension.compat.inc @@ -30,6 +30,10 @@ #ifndef DISABLE_DEPRECATED +#include "gdextension.h" + +#include "core/object/class_db.h" + Error GDExtension::_open_library_bind_compat_88418(const String &p_path, const String &p_entry_symbol) { return ERR_UNAVAILABLE; } @@ -46,4 +50,4 @@ void GDExtension::_bind_compatibility_methods() { ClassDB::bind_compatibility_method(D_METHOD("initialize_library", "level"), &GDExtension::_initialize_library_bind_compat_88418); } -#endif +#endif // DISABLE_DEPRECATED diff --git a/engine/core/extension/gdextension.cpp b/engine/core/extension/gdextension.cpp index 254095e915..bc1212cee2 100644 --- a/engine/core/extension/gdextension.cpp +++ b/engine/core/extension/gdextension.cpp @@ -32,6 +32,7 @@ #include "gdextension.compat.inc" #include "core/config/project_settings.h" +#include "core/object/callable_mp.h" #include "core/object/class_db.h" #include "core/object/method_bind.h" #include "gdextension_library_loader.h" @@ -950,7 +951,7 @@ void GDExtension::prepare_reload() { state.push_back(Pair(P.name, value)); } E.value.instance_state[obj_id] = { - state, // List> properties; + std::move(state), // List> properties; obj->is_extension_placeholder(), // bool is_placeholder; }; } diff --git a/engine/core/extension/gdextension.h b/engine/core/extension/gdextension.h index 2f0835a0d2..8bf3ebe6c3 100644 --- a/engine/core/extension/gdextension.h +++ b/engine/core/extension/gdextension.h @@ -32,7 +32,6 @@ #include "core/extension/gdextension_interface.gen.h" #include "core/extension/gdextension_loader.h" -#include "core/io/config_file.h" #include "core/io/resource_loader.h" #include "core/object/ref_counted.h" diff --git a/engine/core/extension/gdextension_function_loader.h b/engine/core/extension/gdextension_function_loader.h index febda097ca..5635a24d01 100644 --- a/engine/core/extension/gdextension_function_loader.h +++ b/engine/core/extension/gdextension_function_loader.h @@ -31,7 +31,6 @@ #pragma once #include "core/extension/gdextension_loader.h" -#include "core/os/shared_object.h" class GDExtension; diff --git a/engine/core/extension/gdextension_interface.json b/engine/core/extension/gdextension_interface.json index 04c38794e4..b9a6aa56ba 100644 --- a/engine/core/extension/gdextension_interface.json +++ b/engine/core/extension/gdextension_interface.json @@ -5859,7 +5859,7 @@ "return_value": { "type": "GDExtensionInt", "description": [ - "The resulting encoded string length in characters (not bytes), not including a null terminator." + "The resulting encoded string length in characters, not including a null terminator. Characters that cannot be converted to Latin-1 are replaced with a space." ] }, "arguments": [ @@ -5896,7 +5896,7 @@ "return_value": { "type": "GDExtensionInt", "description": [ - "The resulting encoded string length in characters (not bytes), not including a null terminator." + "The resulting encoded string length in bytes (not characters), not including a null terminator." ] }, "arguments": [ @@ -5933,7 +5933,7 @@ "return_value": { "type": "GDExtensionInt", "description": [ - "The resulting encoded string length in characters (not bytes), not including a null terminator." + "The resulting encoded string length in 16-bit code units (not bytes or characters), not including a null terminator." ] }, "arguments": [ @@ -6007,7 +6007,7 @@ "return_value": { "type": "GDExtensionInt", "description": [ - "The resulting encoded string length in characters (not bytes), not including a null terminator." + "The resulting encoded string length in characters (for UTF-32) or 16-bit code units (for UTF-16), depending on the wchar_t representation. Does not include a null terminator." ] }, "arguments": [ diff --git a/engine/core/extension/gdextension_library_loader.cpp b/engine/core/extension/gdextension_library_loader.cpp index 8db6ef390e..3a37d175f0 100644 --- a/engine/core/extension/gdextension_library_loader.cpp +++ b/engine/core/extension/gdextension_library_loader.cpp @@ -30,8 +30,10 @@ #include "gdextension_library_loader.h" +#include "core/config/engine.h" #include "core/config/project_settings.h" #include "core/io/dir_access.h" +#include "core/os/os.h" #include "core/version.h" #include "gdextension.h" diff --git a/engine/core/extension/gdextension_manager.cpp b/engine/core/extension/gdextension_manager.cpp index 166e522c00..86aacfd691 100644 --- a/engine/core/extension/gdextension_manager.cpp +++ b/engine/core/extension/gdextension_manager.cpp @@ -30,12 +30,16 @@ #include "gdextension_manager.h" +#include "core/config/engine.h" #include "core/extension/gdextension_function_loader.h" #include "core/extension/gdextension_library_loader.h" #include "core/extension/gdextension_special_compat_hashes.h" #include "core/io/dir_access.h" #include "core/io/file_access.h" +#include "core/object/callable_mp.h" +#include "core/object/class_db.h" #include "core/object/script_language.h" +#include "core/os/os.h" GDExtensionManager::LoadStatus GDExtensionManager::_load_extension_internal(const Ref &p_extension, bool p_first_load) { if (level >= 0) { // Already initialized up to some level. @@ -477,8 +481,8 @@ void GDExtensionManager::_bind_methods() { BIND_ENUM_CONSTANT(LOAD_STATUS_NEEDS_RESTART); ADD_SIGNAL(MethodInfo("extensions_reloaded")); - ADD_SIGNAL(MethodInfo("extension_loaded", PropertyInfo(Variant::OBJECT, "extension", PROPERTY_HINT_RESOURCE_TYPE, "GDExtension"))); - ADD_SIGNAL(MethodInfo("extension_unloading", PropertyInfo(Variant::OBJECT, "extension", PROPERTY_HINT_RESOURCE_TYPE, "GDExtension"))); + ADD_SIGNAL(MethodInfo("extension_loaded", PropertyInfo(Variant::OBJECT, "extension", PROPERTY_HINT_RESOURCE_TYPE, GDExtension::get_class_static()))); + ADD_SIGNAL(MethodInfo("extension_unloading", PropertyInfo(Variant::OBJECT, "extension", PROPERTY_HINT_RESOURCE_TYPE, GDExtension::get_class_static()))); } GDExtensionManager::GDExtensionManager() { diff --git a/engine/core/extension/godot_instance.cpp b/engine/core/extension/godot_instance.cpp index 343c4eb079..7825006a8c 100644 --- a/engine/core/extension/godot_instance.cpp +++ b/engine/core/extension/godot_instance.cpp @@ -31,7 +31,9 @@ #include "godot_instance.h" #include "core/extension/gdextension_manager.h" +#include "core/object/class_db.h" #include "core/os/main_loop.h" +#include "core/os/os.h" #include "main/main.h" #include "servers/display/display_server.h" diff --git a/engine/core/extension/godot_instance.h b/engine/core/extension/godot_instance.h index 8d1ecf453d..5014f68a6b 100644 --- a/engine/core/extension/godot_instance.h +++ b/engine/core/extension/godot_instance.h @@ -31,7 +31,7 @@ #pragma once #include "core/extension/gdextension_interface.gen.h" -#include "core/object/class_db.h" +#include "core/object/object.h" class GodotInstance : public Object { GDCLASS(GodotInstance, Object); diff --git a/engine/core/input/input.compat.inc b/engine/core/input/input.compat.inc index cbc8b1df0f..7f77baab93 100644 --- a/engine/core/input/input.compat.inc +++ b/engine/core/input/input.compat.inc @@ -30,6 +30,10 @@ #ifndef DISABLE_DEPRECATED +#include "input.h" + +#include "core/object/class_db.h" + void Input::_vibrate_handheld_bind_compat_91143(int p_duration_ms) { vibrate_handheld(p_duration_ms, -1.0); } diff --git a/engine/core/input/input.cpp b/engine/core/input/input.cpp index c75079206c..512f402158 100644 --- a/engine/core/input/input.cpp +++ b/engine/core/input/input.cpp @@ -31,15 +31,21 @@ #include "input.h" #include "input.compat.inc" +#include "core/config/engine.h" #include "core/config/project_settings.h" #include "core/input/default_controller_mappings.h" #include "core/input/input_map.h" +#include "core/object/class_db.h" #include "core/os/os.h" #ifdef DEV_ENABLED #include "core/os/thread.h" #endif +#include "thirdparty/gamepadmotionhelpers/GamepadMotion.hpp" + +#define STANDARD_GRAVITY 9.80665f + static const char *_joy_buttons[(size_t)JoyButton::SDL_MAX] = { "a", "b", @@ -62,6 +68,11 @@ static const char *_joy_buttons[(size_t)JoyButton::SDL_MAX] = { "paddle3", "paddle4", "touchpad", + "misc2", + "misc3", + "misc4", + "misc5", + "misc6", }; static const char *_joy_axes[(size_t)JoyAxis::SDL_MAX] = { @@ -114,6 +125,8 @@ bool Input::is_mouse_mode_override_enabled() { } void Input::_bind_methods() { + using namespace InputClassEnums; + ClassDB::bind_method(D_METHOD("is_anything_pressed"), &Input::is_anything_pressed); ClassDB::bind_method(D_METHOD("is_key_pressed", "keycode"), &Input::is_key_pressed); ClassDB::bind_method(D_METHOD("is_physical_key_pressed", "keycode"), &Input::is_physical_key_pressed); @@ -140,13 +153,32 @@ void Input::_bind_methods() { 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); ClassDB::bind_method(D_METHOD("get_joy_vibration_duration", "device"), &Input::get_joy_vibration_duration); + ClassDB::bind_method(D_METHOD("get_joy_vibration_remaining_duration", "device"), &Input::get_joy_vibration_remaining_duration); + ClassDB::bind_method(D_METHOD("is_joy_vibrating", "device"), &Input::is_joy_vibrating); + ClassDB::bind_method(D_METHOD("has_joy_vibration", "device"), &Input::has_joy_vibration); ClassDB::bind_method(D_METHOD("start_joy_vibration", "device", "weak_magnitude", "strong_magnitude", "duration"), &Input::start_joy_vibration, DEFVAL(0)); ClassDB::bind_method(D_METHOD("stop_joy_vibration", "device"), &Input::stop_joy_vibration); ClassDB::bind_method(D_METHOD("vibrate_handheld", "duration_ms", "amplitude"), &Input::vibrate_handheld, DEFVAL(500), DEFVAL(-1.0)); + ClassDB::bind_method(D_METHOD("set_ignore_joypad_on_unfocused_application", "enable"), &Input::set_ignore_joypad_on_unfocused_application); + ClassDB::bind_method(D_METHOD("is_ignoring_joypad_on_unfocused_application"), &Input::is_ignoring_joypad_on_unfocused_application); ClassDB::bind_method(D_METHOD("get_gravity"), &Input::get_gravity); ClassDB::bind_method(D_METHOD("get_accelerometer"), &Input::get_accelerometer); ClassDB::bind_method(D_METHOD("get_magnetometer"), &Input::get_magnetometer); ClassDB::bind_method(D_METHOD("get_gyroscope"), &Input::get_gyroscope); + ClassDB::bind_method(D_METHOD("get_joy_accelerometer", "device"), &Input::get_joy_accelerometer); + ClassDB::bind_method(D_METHOD("get_joy_gravity", "device"), &Input::get_joy_gravity); + ClassDB::bind_method(D_METHOD("get_joy_gyroscope", "device"), &Input::get_joy_gyroscope); + ClassDB::bind_method(D_METHOD("get_joy_motion_sensors_rate", "device"), &Input::get_joy_motion_sensors_rate); + ClassDB::bind_method(D_METHOD("is_joy_motion_sensors_enabled", "device"), &Input::is_joy_motion_sensors_enabled); + ClassDB::bind_method(D_METHOD("set_joy_motion_sensors_enabled", "device", "enable"), &Input::set_joy_motion_sensors_enabled); + ClassDB::bind_method(D_METHOD("has_joy_motion_sensors", "device"), &Input::has_joy_motion_sensors); + ClassDB::bind_method(D_METHOD("start_joy_motion_sensors_calibration", "device"), &Input::start_joy_motion_sensors_calibration); + ClassDB::bind_method(D_METHOD("stop_joy_motion_sensors_calibration", "device"), &Input::stop_joy_motion_sensors_calibration); + ClassDB::bind_method(D_METHOD("clear_joy_motion_sensors_calibration", "device"), &Input::clear_joy_motion_sensors_calibration); + ClassDB::bind_method(D_METHOD("get_joy_motion_sensors_calibration", "device"), &Input::get_joy_motion_sensors_calibration); + ClassDB::bind_method(D_METHOD("set_joy_motion_sensors_calibration", "device", "calibration_info"), &Input::set_joy_motion_sensors_calibration); + ClassDB::bind_method(D_METHOD("is_joy_motion_sensors_calibrated", "device"), &Input::is_joy_motion_sensors_calibrated); + ClassDB::bind_method(D_METHOD("is_joy_motion_sensors_calibrating", "device"), &Input::is_joy_motion_sensors_calibrating); ClassDB::bind_method(D_METHOD("set_gravity", "value"), &Input::set_gravity); ClassDB::bind_method(D_METHOD("set_accelerometer", "value"), &Input::set_accelerometer); ClassDB::bind_method(D_METHOD("set_magnetometer", "value"), &Input::set_magnetometer); @@ -177,6 +209,7 @@ void Input::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_accumulated_input"), "set_use_accumulated_input", "is_using_accumulated_input"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emulate_mouse_from_touch"), "set_emulate_mouse_from_touch", "is_emulating_mouse_from_touch"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emulate_touch_from_mouse"), "set_emulate_touch_from_mouse", "is_emulating_touch_from_mouse"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_joypad_on_unfocused_application"), "set_ignore_joypad_on_unfocused_application", "is_ignoring_joypad_on_unfocused_application"); BIND_ENUM_CONSTANT(MOUSE_MODE_VISIBLE); BIND_ENUM_CONSTANT(MOUSE_MODE_HIDDEN); @@ -296,24 +329,14 @@ bool Input::is_anything_pressed() const { return false; } -bool Input::is_anything_pressed_except_mouse() const { +bool Input::is_any_key_pressed() const { _THREAD_SAFE_METHOD_ if (disable_input) { return false; } - if (!keys_pressed.is_empty() || !joy_buttons_pressed.is_empty()) { - return true; - } - - for (const KeyValue &E : action_states) { - if (E.value.cache.pressed) { - return true; - } - } - - return false; + return !keys_pressed.is_empty(); } bool Input::is_key_pressed(Key p_keycode) const { @@ -356,6 +379,10 @@ bool Input::is_mouse_button_pressed(MouseButton p_button) const { return mouse_button_mask.has_flag(mouse_button_to_mask(p_button)); } +bool Input::_should_ignore_joypad_events() const { + return ignore_joypad_on_unfocused_application && !application_focused && !embedder_focused; +} + static JoyAxis _combine_device(JoyAxis p_value, int p_device) { return JoyAxis((int)p_value | (p_device << 20)); } @@ -615,6 +642,33 @@ float Input::get_joy_vibration_duration(int p_device) { } } +float Input::get_joy_vibration_remaining_duration(int p_device) { + _THREAD_SAFE_METHOD_ + const Joypad *joypad = joy_names.getptr(p_device); + if (joypad == nullptr || !joypad->has_vibration) { + return 0.f; + } + const VibrationInfo *vibration = joy_vibration.getptr(p_device); + if (vibration == nullptr || (vibration->weak_magnitude == 0.f && vibration->strong_magnitude == 0.f) || vibration->duration < 0.f) { + return 0.f; + } + float vibration_duration = vibration->duration; + if (vibration_duration > 0xFFFF / 1000.f || vibration_duration == 0.f) { + vibration_duration = 0xFFFF / 1000.f; // SDL_MAX_RUMBLE_DURATION_MS / 1000.f + } + return MAX(vibration_duration - (OS::get_singleton()->get_ticks_usec() - vibration->timestamp) / 1e6, 0.f); +} + +bool Input::is_joy_vibrating(int p_device) { + return get_joy_vibration_remaining_duration(p_device) > 0.0f; +} + +bool Input::has_joy_vibration(int p_device) const { + _THREAD_SAFE_METHOD_ + const Joypad *joypad = joy_names.getptr(p_device); + return joypad != nullptr && joypad->has_vibration; +} + static String _hex_str(uint8_t p_byte) { static const char *dict = "0123456789abcdef"; char ret[3]; @@ -684,6 +738,11 @@ void Input::joy_connection_changed(int p_idx, bool p_connected, const String &p_ for (int i = 0; i < (int)JoyAxis::MAX; i++) { set_joy_axis(p_idx, (JoyAxis)i, 0.0f); } + MotionInfo *motion = joy_motion.getptr(p_idx); + if (motion != nullptr && motion->gamepad_motion != nullptr) { + delete motion->gamepad_motion; + } + joy_motion.erase(p_idx); } joy_names[p_idx] = js; @@ -959,6 +1018,7 @@ void Input::_parse_input_event_impl(const Ref &p_event, bool p_is_em device_state.pressed[event_index] = is_pressed; device_state.strength[event_index] = p_event->get_action_strength(E.key); device_state.raw_strength[event_index] = p_event->get_action_raw_strength(E.key); + device_state.event_type[event_index] = p_event->get_type(); // Update the action's global state and cache. if (!is_pressed) { @@ -1005,6 +1065,12 @@ void Input::set_joy_features(int p_device, JoypadFeatures *p_features) { } void Input::set_joy_light(int p_device, const Color &p_color) { + _THREAD_SAFE_METHOD_ + + if (_should_ignore_joypad_events()) { + return; + } + Joypad *joypad = joy_names.getptr(p_device); if (!joypad || !joypad->has_light || joypad->features == nullptr) { return; @@ -1018,8 +1084,218 @@ bool Input::has_joy_light(int p_device) const { return joypad && joypad->has_light; } +Vector3 Input::get_joy_accelerometer(int p_device) const { + _THREAD_SAFE_METHOD_ + + if (_should_ignore_joypad_events()) { + return Vector3(); + } + + const MotionInfo *motion = joy_motion.getptr(p_device); + if (motion == nullptr) { + return Vector3(); + } + + float joy_acceleration_data[3]; + motion->gamepad_motion->GetProcessedAcceleration(joy_acceleration_data[0], joy_acceleration_data[1], joy_acceleration_data[2]); + Vector3 joy_acceleration(joy_acceleration_data[0], joy_acceleration_data[1], joy_acceleration_data[2]); + + float joy_gravity_data[3]; + motion->gamepad_motion->GetGravity(joy_gravity_data[0], joy_gravity_data[1], joy_gravity_data[2]); + Vector3 joy_gravity(joy_gravity_data[0], joy_gravity_data[1], joy_gravity_data[2]); + + return (-joy_acceleration + joy_gravity) * STANDARD_GRAVITY; +} + +Vector3 Input::get_joy_gravity(int p_device) const { + _THREAD_SAFE_METHOD_ + + if (_should_ignore_joypad_events()) { + return Vector3(); + } + + const MotionInfo *motion = joy_motion.getptr(p_device); + if (motion == nullptr) { + return Vector3(); + } + + float joy_gravity_data[3]; + motion->gamepad_motion->GetGravity(joy_gravity_data[0], joy_gravity_data[1], joy_gravity_data[2]); + Vector3 joy_gravity(joy_gravity_data[0], joy_gravity_data[1], joy_gravity_data[2]); + + return joy_gravity.normalized() * STANDARD_GRAVITY; +} + +Vector3 Input::get_joy_gyroscope(int p_device) const { + _THREAD_SAFE_METHOD_ + + if (_should_ignore_joypad_events()) { + return Vector3(); + } + + const MotionInfo *motion = joy_motion.getptr(p_device); + if (motion == nullptr) { + return Vector3(); + } + + float joy_gyro_data[3]; + motion->gamepad_motion->GetCalibratedGyro(joy_gyro_data[0], joy_gyro_data[1], joy_gyro_data[2]); + Vector3 joy_gyro(joy_gyro_data[0], joy_gyro_data[1], joy_gyro_data[2]); + + return joy_gyro * M_PI / 180.0; +} + +void Input::set_joy_motion_sensors_enabled(int p_device, bool p_enable) { + _THREAD_SAFE_METHOD_ + Joypad *joypad = joy_names.getptr(p_device); + if (joypad == nullptr || joypad->features == nullptr) { + return; + } + MotionInfo *motion = joy_motion.getptr(p_device); + if (motion == nullptr) { + return; + } + joypad->features->set_joy_motion_sensors_enabled(p_enable); + motion->sensors_enabled = p_enable; +} + +bool Input::is_joy_motion_sensors_enabled(int p_device) const { + _THREAD_SAFE_METHOD_ + const MotionInfo *motion = joy_motion.getptr(p_device); + return motion != nullptr && motion->sensors_enabled; +} + +bool Input::has_joy_motion_sensors(int p_device) const { + _THREAD_SAFE_METHOD_ + return joy_motion.has(p_device); +} + +float Input::get_joy_motion_sensors_rate(int p_device) const { + _THREAD_SAFE_METHOD_ + const MotionInfo *motion = joy_motion.getptr(p_device); + if (motion == nullptr) { + return 0.0f; + } + return motion->sensor_data_rate; +} + +void Input::start_joy_motion_sensors_calibration(int p_device) { + _THREAD_SAFE_METHOD_ + MotionInfo *motion = joy_motion.getptr(p_device); + if (motion == nullptr) { + return; + } + + ERR_FAIL_COND_MSG(!motion->sensors_enabled, "Motion sensors are not enabled on the joypad."); + ERR_FAIL_COND_MSG(motion->calibrating, "Calibration already in progress."); + + motion->gamepad_motion->ResetContinuousCalibration(); + motion->gamepad_motion->StartContinuousCalibration(); + + motion->calibrating = true; + motion->calibrated = false; +} + +void Input::stop_joy_motion_sensors_calibration(int p_device) { + _THREAD_SAFE_METHOD_ + MotionInfo *motion = joy_motion.getptr(p_device); + if (motion == nullptr) { + return; + } + + ERR_FAIL_COND_MSG(!motion->sensors_enabled, "Motion sensors are not enabled on the joypad."); + ERR_FAIL_COND_MSG(!motion->calibrating, "Calibration hasn't been started."); + + motion->gamepad_motion->PauseContinuousCalibration(); + + motion->calibrating = false; + motion->calibrated = true; +} + +void Input::clear_joy_motion_sensors_calibration(int p_device) { + _THREAD_SAFE_METHOD_ + MotionInfo *motion = joy_motion.getptr(p_device); + if (motion == nullptr) { + return; + } + + // Calibration might be in progress and the developer or the user might want to reset it, + // so no need to stop the calibration. + + motion->gamepad_motion->ResetContinuousCalibration(); +} + +Dictionary Input::get_joy_motion_sensors_calibration(int p_device) const { + _THREAD_SAFE_METHOD_ + const MotionInfo *motion = joy_motion.getptr(p_device); + if (motion == nullptr) { + return Dictionary(); + } + + if (!motion->calibrated) { + return Dictionary(); + } + + float joy_gyro_offset_data[3]; + motion->gamepad_motion->GetCalibrationOffset(joy_gyro_offset_data[0], joy_gyro_offset_data[1], joy_gyro_offset_data[2]); + Vector3 joy_gyro_offset(joy_gyro_offset_data[0], joy_gyro_offset_data[1], joy_gyro_offset_data[2]); + + Dictionary result; + result["gyroscope_offset"] = joy_gyro_offset * M_PI / 180.0; + return result; +} + +void Input::set_joy_motion_sensors_calibration(int p_device, const Dictionary &p_calibration_info) { + _THREAD_SAFE_METHOD_ + MotionInfo *motion = joy_motion.getptr(p_device); + if (motion == nullptr) { + return; + } + + ERR_FAIL_COND_MSG(motion->calibrating, "Calibration is currently in progress."); + + Vector3 gyro_offset = p_calibration_info.get("gyroscope_offset", Vector3()).operator Vector3() * 180.0 / M_PI; + + motion->gamepad_motion->SetCalibrationOffset(gyro_offset.x, gyro_offset.y, gyro_offset.z, 1); + motion->calibrating = false; + motion->calibrated = true; +} + +bool Input::is_joy_motion_sensors_calibrating(int p_device) const { + _THREAD_SAFE_METHOD_ + const MotionInfo *motion = joy_motion.getptr(p_device); + if (motion == nullptr) { + return false; + } + return motion->calibrating; +} + +bool Input::is_joy_motion_sensors_calibrated(int p_device) const { + _THREAD_SAFE_METHOD_ + const MotionInfo *motion = joy_motion.getptr(p_device); + if (motion == nullptr) { + return false; + } + return motion->calibrated; +} + +void Input::set_joy_motion_sensors_rate(int p_device, float p_rate) { + _THREAD_SAFE_METHOD_ + MotionInfo *motion = joy_motion.getptr(p_device); + if (motion == nullptr) { + return; + } + + motion->sensor_data_rate = p_rate; +} + void Input::start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration) { _THREAD_SAFE_METHOD_ + + if (_should_ignore_joypad_events()) { + return; + } + if (p_weak_magnitude < 0.f || p_weak_magnitude > 1.f || p_strong_magnitude < 0.f || p_strong_magnitude > 1.f) { return; } @@ -1045,6 +1321,17 @@ void Input::vibrate_handheld(int p_duration_ms, float p_amplitude) { OS::get_singleton()->vibrate_handheld(p_duration_ms, p_amplitude); } +void Input::set_ignore_joypad_on_unfocused_application(bool p_ignore) { + ignore_joypad_on_unfocused_application = p_ignore; + if (_should_ignore_joypad_events()) { + release_pressed_events(); + } +} + +bool Input::is_ignoring_joypad_on_unfocused_application() const { + return ignore_joypad_on_unfocused_application; +} + void Input::set_gravity(const Vector3 &p_gravity) { _THREAD_SAFE_METHOD_ @@ -1311,17 +1598,49 @@ bool Input::is_using_accumulated_input() { } void Input::release_pressed_events() { + // Don't release the events if the application (or the window it's embedded in) is still focused. + if (application_focused || embedder_focused) { + return; + } + flush_buffered_events(); // this is needed to release actions strengths keys_pressed.clear(); physical_keys_pressed.clear(); key_label_pressed.clear(); - joy_buttons_pressed.clear(); - _joy_axis.clear(); + if (ignore_joypad_on_unfocused_application) { + joy_buttons_pressed.clear(); + _joy_axis.clear(); - for (KeyValue &E : action_states) { - if (E.value.cache.pressed) { - action_release(E.key); + for (KeyValue &E : action_states) { + if (E.value.cache.pressed) { + action_release(E.key); + } + } + + for (int device : get_connected_joypads()) { + stop_joy_vibration(device); + } + } else { + for (KeyValue &E : action_states) { + if (E.value.cache.pressed) { + bool is_only_joypad_pressed = true; + int max_event = InputMap::get_singleton()->action_get_events(E.key)->size() + 1; // +1 comes from InputEventAction. + + for (const KeyValue &kv : E.value.device_states) { + const ActionState::DeviceState &device_state = kv.value; + for (int i = 0; i < max_event; i++) { + if (device_state.event_type[i] != InputEventType::JOY_MOTION && device_state.event_type[i] != InputEventType::JOY_BUTTON && device_state.strength[i] > 0.0f) { + is_only_joypad_pressed = false; + action_release(E.key); + break; + } + } + if (!is_only_joypad_pressed) { + break; + } + } + } } } } @@ -1332,6 +1651,11 @@ void Input::set_event_dispatch_function(EventDispatchFunc p_function) { void Input::joy_button(int p_device, JoyButton p_button, bool p_pressed) { _THREAD_SAFE_METHOD_; + + if (_should_ignore_joypad_events()) { + return; + } + Joypad &joy = joy_names[p_device]; ERR_FAIL_INDEX((int)p_button, (int)JoyButton::MAX); @@ -1360,6 +1684,10 @@ void Input::joy_button(int p_device, JoyButton p_button, bool p_pressed) { void Input::joy_axis(int p_device, JoyAxis p_axis, float p_value) { _THREAD_SAFE_METHOD_; + if (_should_ignore_joypad_events()) { + return; + } + ERR_FAIL_INDEX((int)p_axis, (int)JoyAxis::MAX); Joypad &joy = joy_names[p_device]; @@ -1429,6 +1757,11 @@ void Input::joy_axis(int p_device, JoyAxis p_axis, float p_value) { void Input::joy_hat(int p_device, BitField p_val) { _THREAD_SAFE_METHOD_; + + if (_should_ignore_joypad_events()) { + return; + } + const Joypad &joy = joy_names[p_device]; JoyEvent map[(size_t)HatDir::MAX]; @@ -1469,6 +1802,27 @@ void Input::joy_hat(int p_device, BitField p_val) { joy_names[p_device].hat_current = (int)p_val; } +void Input::joy_motion_sensors(int p_device, const Vector3 &p_accelerometer, const Vector3 &p_gyroscope) { + _THREAD_SAFE_METHOD_ + + if (_should_ignore_joypad_events()) { + return; + } + + // TODO: events + MotionInfo *motion = joy_motion.getptr(p_device); + if (motion == nullptr) { + return; + } + + Vector3 gyro_degrees = p_gyroscope * 180.0 / M_PI; + Vector3 accel_g = -p_accelerometer / STANDARD_GRAVITY; + uint64_t new_timestamp = OS::get_singleton()->get_ticks_msec(); + float delta_time = (new_timestamp - motion->last_timestamp) / 1000.0f; + motion->last_timestamp = new_timestamp; + motion->gamepad_motion->ProcessMotion(gyro_degrees.x, gyro_degrees.y, gyro_degrees.z, accel_g.x, accel_g.y, accel_g.z, delta_time); +} + void Input::_button_event(int p_device, JoyButton p_index, bool p_pressed) { Ref ievent; ievent.instantiate(); @@ -1517,9 +1871,22 @@ void Input::_update_joypad_features(int p_device) { if (!joypad || joypad->features == nullptr) { return; } + if (joypad->features->has_joy_vibration()) { + joypad->has_vibration = true; + } if (joypad->features->has_joy_light()) { joypad->has_light = true; } + if (joypad->features->has_joy_motion_sensors()) { + MotionInfo &motion = joy_motion[p_device]; + + if (!motion.gamepad_motion) { + motion.gamepad_motion = new GamepadMotion(); + } else { + motion.gamepad_motion->Reset(); + } + motion.last_timestamp = OS::get_singleton()->get_ticks_msec(); + } } Input::JoyEvent Input::_get_mapped_button_event(const JoyDeviceMapping &mapping, JoyButton p_button) { @@ -2004,6 +2371,7 @@ Input::Input() { gravity_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_gravity", false); gyroscope_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_gyroscope", false); magnetometer_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_magnetometer", false); + ignore_joypad_on_unfocused_application = GLOBAL_DEF_RST_BASIC("input_devices/joypads/ignore_joypad_on_unfocused_application", false); } Input::~Input() { diff --git a/engine/core/input/input.h b/engine/core/input/input.h index c912cb34d3..6d732877ef 100644 --- a/engine/core/input/input.h +++ b/engine/core/input/input.h @@ -38,6 +38,43 @@ #include "core/templates/rb_set.h" #include "core/variant/typed_array.h" +class GamepadMotion; + +namespace InputClassEnums { +// Keep synced with DisplayServerEnums::MouseMode enum. +enum MouseMode : int { + MOUSE_MODE_VISIBLE, + MOUSE_MODE_HIDDEN, + MOUSE_MODE_CAPTURED, + MOUSE_MODE_CONFINED, + MOUSE_MODE_CONFINED_HIDDEN, + MOUSE_MODE_MAX, +}; + +// Keep synced with DisplayServerEnums and Control enums. +#undef CursorShape +enum CursorShape : int { + CURSOR_ARROW, + CURSOR_IBEAM, + CURSOR_POINTING_HAND, + CURSOR_CROSS, + CURSOR_WAIT, + CURSOR_BUSY, + CURSOR_DRAG, + CURSOR_CAN_DROP, + CURSOR_FORBIDDEN, + CURSOR_VSIZE, + CURSOR_HSIZE, + CURSOR_BDIAGSIZE, + CURSOR_FDIAGSIZE, + CURSOR_MOVE, + CURSOR_VSPLIT, + CURSOR_HSPLIT, + CURSOR_HELP, + CURSOR_MAX +}; +} //namespace InputClassEnums + class Input : public Object { GDCLASS(Input, Object); _THREAD_SAFE_CLASS_ @@ -47,44 +84,21 @@ class Input : public Object { static constexpr uint64_t MAX_EVENT = 32; public: - // Keep synced with "DisplayServer::MouseMode" enum. - enum MouseMode { - MOUSE_MODE_VISIBLE, - MOUSE_MODE_HIDDEN, - MOUSE_MODE_CAPTURED, - MOUSE_MODE_CONFINED, - MOUSE_MODE_CONFINED_HIDDEN, - MOUSE_MODE_MAX, - }; - -#undef CursorShape - enum CursorShape { - CURSOR_ARROW, - CURSOR_IBEAM, - CURSOR_POINTING_HAND, - CURSOR_CROSS, - CURSOR_WAIT, - CURSOR_BUSY, - CURSOR_DRAG, - CURSOR_CAN_DROP, - CURSOR_FORBIDDEN, - CURSOR_VSIZE, - CURSOR_HSIZE, - CURSOR_BDIAGSIZE, - CURSOR_FDIAGSIZE, - CURSOR_MOVE, - CURSOR_VSPLIT, - CURSOR_HSPLIT, - CURSOR_HELP, - CURSOR_MAX - }; + // TODO: When we migrate to C++20, replace these with "using enum" and skip prefixing MouseMode and CursorShape in other files. + using MouseMode = InputClassEnums::MouseMode; + using CursorShape = InputClassEnums::CursorShape; class JoypadFeatures { public: virtual ~JoypadFeatures() {} + virtual bool has_joy_vibration() const { return false; } + virtual bool has_joy_light() const { return false; } virtual void set_joy_light(const Color &p_color) {} + + virtual bool has_joy_motion_sensors() const { return false; } + virtual void set_joy_motion_sensors_enabled(bool p_enable) {} }; static constexpr int32_t JOYPADS_MAX = 16; @@ -112,6 +126,9 @@ private: int64_t mouse_window = 0; bool legacy_just_pressed_behavior = false; bool disable_input = false; + bool ignore_joypad_on_unfocused_application = false; + bool application_focused = true; + bool embedder_focused = false; struct ActionState { uint64_t pressed_physics_frame = UINT64_MAX; @@ -126,6 +143,7 @@ private: bool pressed[MAX_EVENT] = { false }; float strength[MAX_EVENT] = { 0.0 }; float raw_strength[MAX_EVENT] = { 0.0 }; + InputEventType event_type[MAX_EVENT] = { InputEventType::INVALID }; }; bool api_pressed = false; float api_strength = 0.0; @@ -157,6 +175,23 @@ private: HashMap joy_vibration; + struct MotionInfo { + bool sensors_enabled : 1; + bool calibrating : 1; + bool calibrated : 1; + float sensor_data_rate = 0.0f; + uint64_t last_timestamp = 0; + GamepadMotion *gamepad_motion = nullptr; + + MotionInfo() { + sensors_enabled = false; + calibrating = false; + calibrated = false; + } + }; + + HashMap joy_motion; + struct VelocityTrack { uint64_t last_tick = 0; Vector2 velocity; @@ -184,6 +219,7 @@ private: int hat_current = 0; Dictionary info; bool has_light = false; + bool has_vibration = false; Input::JoypadFeatures *features = nullptr; }; @@ -195,7 +231,7 @@ private: int fallback_mapping = -1; // Index of the guid in map_db. - CursorShape default_shape = CURSOR_ARROW; + CursorShape default_shape = CursorShape::CURSOR_ARROW; enum JoyType { TYPE_BUTTON, @@ -275,6 +311,8 @@ private: #endif friend class DisplayServer; + friend class SceneTree; + friend class SceneDebugger; static void (*set_mouse_mode_func)(MouseMode); static MouseMode (*get_mouse_mode_func)(); @@ -289,6 +327,8 @@ private: EventDispatchFunc event_dispatch_function = nullptr; + bool _should_ignore_joypad_events() const; + #ifndef DISABLE_DEPRECATED void _vibrate_handheld_bind_compat_91143(int p_duration_ms = 500); static void _bind_compatibility_methods(); @@ -312,7 +352,7 @@ public: static Input *get_singleton(); bool is_anything_pressed() const; - bool is_anything_pressed_except_mouse() const; + bool is_any_key_pressed() const; bool is_key_pressed(Key p_keycode) const; bool is_physical_key_pressed(Key p_keycode) const; bool is_key_label_pressed(Key p_keycode) const; @@ -334,7 +374,10 @@ public: TypedArray get_connected_joypads(); Vector2 get_joy_vibration_strength(int p_device); float get_joy_vibration_duration(int p_device); + float get_joy_vibration_remaining_duration(int p_device); uint64_t get_joy_vibration_timestamp(int p_device); + bool is_joy_vibrating(int p_device); + bool has_joy_vibration(int p_device) const; void joy_connection_changed(int p_idx, bool p_connected, const String &p_name, const String &p_guid = "", const Dictionary &p_joypad_info = Dictionary()); Vector3 get_gravity() const; @@ -363,10 +406,35 @@ public: void set_joy_light(int p_device, const Color &p_color); bool has_joy_light(int p_device) const; + Vector3 get_joy_accelerometer(int p_device) const; + Vector3 get_joy_gravity(int p_device) const; + Vector3 get_joy_gyroscope(int p_device) const; + + void set_joy_motion_sensors_enabled(int p_device, bool p_enable); + bool is_joy_motion_sensors_enabled(int p_device) const; + + bool has_joy_motion_sensors(int p_device) const; + float get_joy_motion_sensors_rate(int p_device) const; + + void start_joy_motion_sensors_calibration(int p_device); + void stop_joy_motion_sensors_calibration(int p_device); + void clear_joy_motion_sensors_calibration(int p_device); + + Dictionary get_joy_motion_sensors_calibration(int p_device) const; + void set_joy_motion_sensors_calibration(int p_device, const Dictionary &p_calibration_info); + + bool is_joy_motion_sensors_calibrating(int p_device) const; + bool is_joy_motion_sensors_calibrated(int p_device) const; + + void set_joy_motion_sensors_rate(int p_device, float p_rate); + void start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration = 0); void stop_joy_vibration(int p_device); void vibrate_handheld(int p_duration_ms = 500, float p_amplitude = -1.0); + void set_ignore_joypad_on_unfocused_application(bool p_ignore); + bool is_ignoring_joypad_on_unfocused_application() const; + void set_mouse_position(const Point2 &p_posf); void action_press(const StringName &p_action, float p_strength = 1.f); @@ -382,12 +450,13 @@ public: CursorShape get_default_cursor_shape() const; void set_default_cursor_shape(CursorShape p_shape); CursorShape get_current_cursor_shape() const; - void set_custom_mouse_cursor(const Ref &p_cursor, CursorShape p_shape = Input::CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()); + void set_custom_mouse_cursor(const Ref &p_cursor, CursorShape p_shape = Input::CursorShape::CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()); void parse_mapping(const String &p_mapping); void joy_button(int p_device, JoyButton p_button, bool p_pressed); void joy_axis(int p_device, JoyAxis p_axis, float p_value); void joy_hat(int p_device, BitField p_val); + void joy_motion_sensors(int p_device, const Vector3 &p_accelerometer, const Vector3 &p_gyroscope); void add_joy_mapping(const String &p_mapping, bool p_update_existing = false); void remove_joy_mapping(const String &p_guid); diff --git a/engine/core/input/input_enums.h b/engine/core/input/input_enums.h index 27508cce46..b0bd154006 100644 --- a/engine/core/input/input_enums.h +++ b/engine/core/input/input_enums.h @@ -100,7 +100,12 @@ enum class JoyButton { PADDLE3 = 18, PADDLE4 = 19, TOUCHPAD = 20, - SDL_MAX = 21, + MISC2 = 21, + MISC3 = 22, + MISC4 = 23, + MISC5 = 24, + MISC6 = 25, + SDL_MAX = 26, MAX = 128, // Android supports up to 36 buttons. DirectInput supports up to 128 buttons. }; diff --git a/engine/core/input/input_event.cpp b/engine/core/input/input_event.cpp index a3424c3433..2d6c293c41 100644 --- a/engine/core/input/input_event.cpp +++ b/engine/core/input/input_event.cpp @@ -32,8 +32,11 @@ #include "core/input/input_map.h" #include "core/input/shortcut.h" +#include "core/math/transform_2d.h" +#include "core/object/class_db.h" #include "core/os/keyboard.h" #include "core/os/os.h" +#include "core/string/ustring.h" void InputEvent::set_device(int p_device) { device = p_device; @@ -131,6 +134,8 @@ void InputEvent::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "device"), "set_device", "get_device"); BIND_CONSTANT(DEVICE_ID_EMULATION); + BIND_CONSTANT(DEVICE_ID_KEYBOARD); + BIND_CONSTANT(DEVICE_ID_MOUSE); } /////////////////////////////////// @@ -314,8 +319,7 @@ void InputEventWithModifiers::_validate_property(PropertyInfo &p_property) const if (p_property.name == "meta_pressed") { p_property.usage ^= PROPERTY_USAGE_STORAGE; p_property.usage ^= PROPERTY_USAGE_EDITOR; - } - if (p_property.name == "ctrl_pressed") { + } else if (p_property.name == "ctrl_pressed") { p_property.usage ^= PROPERTY_USAGE_STORAGE; p_property.usage ^= PROPERTY_USAGE_EDITOR; } @@ -662,6 +666,10 @@ void InputEventKey::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "echo"), "set_echo", "is_echo"); } +InputEventKey::InputEventKey() { + set_device(DEVICE_ID_KEYBOARD); +} + /////////////////////////////////// void InputEventMouse::set_button_mask(BitField p_mask) { @@ -704,6 +712,10 @@ void InputEventMouse::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_position", PROPERTY_HINT_NONE, "suffix:px"), "set_global_position", "get_global_position"); } +InputEventMouse::InputEventMouse() { + set_device(DEVICE_ID_MOUSE); +} + /////////////////////////////////// void InputEventMouseButton::set_factor(float p_factor) { @@ -1920,7 +1932,7 @@ void InputEventShortcut::_bind_methods() { ClassDB::bind_method(D_METHOD("set_shortcut", "shortcut"), &InputEventShortcut::set_shortcut); ClassDB::bind_method(D_METHOD("get_shortcut"), &InputEventShortcut::get_shortcut); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut", PROPERTY_HINT_RESOURCE_TYPE, "Shortcut"), "set_shortcut", "get_shortcut"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut", PROPERTY_HINT_RESOURCE_TYPE, Shortcut::get_class_static()), "set_shortcut", "get_shortcut"); } String InputEventShortcut::as_text() const { diff --git a/engine/core/input/input_event.h b/engine/core/input/input_event.h index ea163230d3..ec00dfdc58 100644 --- a/engine/core/input/input_event.h +++ b/engine/core/input/input_event.h @@ -32,9 +32,7 @@ #include "core/input/input_enums.h" #include "core/io/resource.h" -#include "core/math/transform_2d.h" #include "core/os/keyboard.h" -#include "core/string/ustring.h" #include "core/typedefs.h" /** @@ -43,6 +41,8 @@ */ class Shortcut; +class String; +struct Transform2D; /** * Input Modifier Status @@ -63,6 +63,8 @@ protected: public: static constexpr int DEVICE_ID_EMULATION = -1; static constexpr int DEVICE_ID_INTERNAL = -2; + static constexpr int DEVICE_ID_KEYBOARD = 16; // IDs 0-15 are reserved for joypads. + static constexpr int DEVICE_ID_MOUSE = 32; // IDs 17-31 are reserved for multiple keyboard support in the future. void set_device(int p_device); int get_device() const; @@ -199,6 +201,8 @@ public: static Ref create_reference(Key p_keycode_with_modifier_masks, bool p_physical = false); InputEventType get_type() const final override { return InputEventType::KEY; } + + InputEventKey(); }; class InputEventMouse : public InputEventWithModifiers { @@ -221,6 +225,8 @@ public: void set_global_position(const Vector2 &p_global_pos); Vector2 get_global_position() const; + + InputEventMouse(); }; class InputEventMouseButton : public InputEventMouse { diff --git a/engine/core/input/input_event_codec.cpp b/engine/core/input/input_event_codec.cpp index 85c0416aa3..ac24eb4502 100644 --- a/engine/core/input/input_event_codec.cpp +++ b/engine/core/input/input_event_codec.cpp @@ -32,7 +32,6 @@ #include "core/input/input.h" #include "core/io/marshalls.h" -#include "core/os/os.h" enum class BoolShift : uint8_t { SHIFT = 0, diff --git a/engine/core/input/input_map.compat.inc b/engine/core/input/input_map.compat.inc index da4bd962b6..f54e9c9b6a 100644 --- a/engine/core/input/input_map.compat.inc +++ b/engine/core/input/input_map.compat.inc @@ -30,6 +30,10 @@ #ifndef DISABLE_DEPRECATED +#include "input_map.h" + +#include "core/object/class_db.h" + void InputMap::_add_action_bind_compat_97281(const StringName &p_action, float p_deadzone) { add_action(p_action, p_deadzone); } diff --git a/engine/core/input/input_map.cpp b/engine/core/input/input_map.cpp index d1f2aad9cd..e5f4bd78c5 100644 --- a/engine/core/input/input_map.cpp +++ b/engine/core/input/input_map.cpp @@ -33,6 +33,7 @@ #include "core/config/project_settings.h" #include "core/input/input.h" +#include "core/object/class_db.h" #include "core/os/keyboard.h" #include "core/os/os.h" #include "core/variant/typed_array.h" @@ -205,6 +206,22 @@ void InputMap::action_add_event(const StringName &p_action, RequiredParamget_device() == 0) { + switch (p_event->get_type()) { + case InputEventType::KEY: + p_event->set_device(InputEvent::DEVICE_ID_KEYBOARD); + break; + case InputEventType::MOUSE_BUTTON: + case InputEventType::MOUSE_MOTION: + p_event->set_device(InputEvent::DEVICE_ID_MOUSE); + break; + default: + break; + } + } + input_map[p_action].inputs.push_back(p_event); } @@ -319,6 +336,11 @@ void InputMap::load_from_project_settings() { String name = pi.name.substr(pi.name.find_char('/') + 1); Dictionary action = GLOBAL_GET(pi.name); + + if (!action.has("events")) { + continue; + } + float deadzone = action.has("deadzone") ? (float)action["deadzone"] : DEFAULT_DEADZONE; Array events = action["events"]; @@ -543,16 +565,16 @@ const HashMap>> &InputMap::get_builtins() { inputs.push_back(InputEventKey::create_reference(Key::SPACE | KeyModifierMask::CTRL)); default_builtin_cache.insert("ui_text_completion_query", inputs); - inputs = List>(); - inputs.push_back(InputEventKey::create_reference(Key::TAB)); - inputs.push_back(InputEventKey::create_reference(Key::ENTER)); - inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER)); - default_builtin_cache.insert("ui_text_completion_accept", inputs); - inputs = List>(); inputs.push_back(InputEventKey::create_reference(KeyModifierMask::SHIFT | Key::TAB)); inputs.push_back(InputEventKey::create_reference(KeyModifierMask::SHIFT | Key::ENTER)); inputs.push_back(InputEventKey::create_reference(KeyModifierMask::SHIFT | Key::KP_ENTER)); + default_builtin_cache.insert("ui_text_completion_accept", inputs); + + inputs = List>(); + inputs.push_back(InputEventKey::create_reference(Key::TAB)); + inputs.push_back(InputEventKey::create_reference(Key::ENTER)); + inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER)); default_builtin_cache.insert("ui_text_completion_replace", inputs); // Newlines @@ -904,7 +926,7 @@ const HashMap>> &InputMap::get_builtins_with_featur } void InputMap::load_default() { - HashMap>> builtins = get_builtins_with_feature_overrides_applied(); + HashMap>> builtins(get_builtins_with_feature_overrides_applied()); for (const KeyValue>> &E : builtins) { String name = E.key; diff --git a/engine/core/input/shortcut.cpp b/engine/core/input/shortcut.cpp index 7264626237..36a4e87674 100644 --- a/engine/core/input/shortcut.cpp +++ b/engine/core/input/shortcut.cpp @@ -30,6 +30,8 @@ #include "shortcut.h" +#include "core/object/class_db.h" + void Shortcut::set_events(const Array &p_events) { for (int i = 0; i < p_events.size(); i++) { Ref ies = p_events[i]; diff --git a/engine/core/io/compression.cpp b/engine/core/io/compression.cpp index aeed0d272b..72d2e1b222 100644 --- a/engine/core/io/compression.cpp +++ b/engine/core/io/compression.cpp @@ -30,7 +30,6 @@ #include "compression.h" -#include "core/config/project_settings.h" #include "core/io/zip_io.h" #include "thirdparty/misc/fastlz.h" diff --git a/engine/core/io/config_file.cpp b/engine/core/io/config_file.cpp index 793287079d..dd5c068660 100644 --- a/engine/core/io/config_file.cpp +++ b/engine/core/io/config_file.cpp @@ -31,6 +31,7 @@ #include "config_file.h" #include "core/io/file_access_encrypted.h" +#include "core/object/class_db.h" #include "core/string/string_builder.h" #include "core/variant/variant_parser.h" diff --git a/engine/core/io/delta_encoding.cpp b/engine/core/io/delta_encoding.cpp index 9b5242a60e..d4db12a467 100644 --- a/engine/core/io/delta_encoding.cpp +++ b/engine/core/io/delta_encoding.cpp @@ -30,6 +30,10 @@ #include "delta_encoding.h" +#include "core/error/error_list.h" +#include "core/templates/vector.h" +#include "core/variant/variant.h" // vformat + #include #define ERR_FAIL_ZSTD_V_MSG(m_result, m_retval, m_msg) \ diff --git a/engine/core/io/delta_encoding.h b/engine/core/io/delta_encoding.h index 835f1e8409..7d2253055e 100644 --- a/engine/core/io/delta_encoding.h +++ b/engine/core/io/delta_encoding.h @@ -30,7 +30,12 @@ #pragma once -#include "core/io/file_access.h" +#include "core/templates/span.h" + +#include + +template +class Vector; class DeltaEncoding { public: diff --git a/engine/core/io/dir_access.cpp b/engine/core/io/dir_access.cpp index fde1fbb796..13d3547890 100644 --- a/engine/core/io/dir_access.cpp +++ b/engine/core/io/dir_access.cpp @@ -32,6 +32,7 @@ #include "core/config/project_settings.h" #include "core/io/file_access.h" +#include "core/object/class_db.h" #include "core/os/os.h" #include "core/os/time.h" #include "core/templates/local_vector.h" diff --git a/engine/core/io/dtls_server.cpp b/engine/core/io/dtls_server.cpp index cdb2235b5f..cbb8bcf029 100644 --- a/engine/core/io/dtls_server.cpp +++ b/engine/core/io/dtls_server.cpp @@ -30,6 +30,8 @@ #include "dtls_server.h" +#include "core/object/class_db.h" + DTLSServer *DTLSServer::create(bool p_notify_postinitialize) { if (_create) { return _create(p_notify_postinitialize); diff --git a/engine/core/io/dtls_server.h b/engine/core/io/dtls_server.h index 10965b3b88..416a76a067 100644 --- a/engine/core/io/dtls_server.h +++ b/engine/core/io/dtls_server.h @@ -30,7 +30,6 @@ #pragma once -#include "core/io/net_socket.h" #include "core/io/packet_peer_dtls.h" class DTLSServer : public RefCounted { diff --git a/engine/core/io/file_access.compat.inc b/engine/core/io/file_access.compat.inc index d340100fd7..933c108f89 100644 --- a/engine/core/io/file_access.compat.inc +++ b/engine/core/io/file_access.compat.inc @@ -30,6 +30,10 @@ #ifndef DISABLE_DEPRECATED +#include "file_access.h" + +#include "core/object/class_db.h" + Ref FileAccess::_open_encrypted_bind_compat_98918(const String &p_path, ModeFlags p_mode_flags, const Vector &p_key) { return open_encrypted(p_path, p_mode_flags, p_key, Vector()); } @@ -123,4 +127,4 @@ void FileAccess::_bind_compatibility_methods() { ClassDB::bind_compatibility_method(D_METHOD("get_as_text", "skip_cr"), &FileAccess::get_as_text_bind_compat_110867, DEFVAL(false)); } -#endif +#endif // DISABLE_DEPRECATED diff --git a/engine/core/io/file_access.cpp b/engine/core/io/file_access.cpp index caae8ee026..853c764f81 100644 --- a/engine/core/io/file_access.cpp +++ b/engine/core/io/file_access.cpp @@ -38,6 +38,7 @@ #include "core/io/file_access_pack.h" #include "core/io/marshalls.h" #include "core/io/resource_uid.h" +#include "core/object/class_db.h" #include "core/os/os.h" #include "core/os/time.h" @@ -425,7 +426,7 @@ class CharBuffer { int64_t written = 0; bool grow() { - if (vector.resize(next_power_of_2((uint64_t)1 + (uint64_t)written)) != OK) { + if (vector.resize(Math::next_power_of_2((uint64_t)1 + (uint64_t)written)) != OK) { return false; } diff --git a/engine/core/io/file_access.h b/engine/core/io/file_access.h index 77fbc99925..2d57e0c3e0 100644 --- a/engine/core/io/file_access.h +++ b/engine/core/io/file_access.h @@ -36,6 +36,7 @@ #include "core/os/memory.h" #include "core/string/ustring.h" #include "core/typedefs.h" +#include "core/variant/type_info.h" /** * Multi-Platform abstraction for accessing to files. diff --git a/engine/core/io/file_access_compressed.cpp b/engine/core/io/file_access_compressed.cpp index 8062d97b7a..1ba926de08 100644 --- a/engine/core/io/file_access_compressed.cpp +++ b/engine/core/io/file_access_compressed.cpp @@ -30,6 +30,8 @@ #include "file_access_compressed.h" +#include "core/math/math_funcs_binary.h" + void FileAccessCompressed::configure(const String &p_magic, Compression::Mode p_mode, uint32_t p_block_size) { magic = p_magic.ascii().get_data(); magic = (magic + " ").substr(0, 4); @@ -320,7 +322,7 @@ bool FileAccessCompressed::store_buffer(const uint8_t *p_src, uint64_t p_length) write_max = write_pos + (p_length); } if (write_max > write_buffer_size) { - write_buffer_size = next_power_of_2(write_max); + write_buffer_size = Math::next_power_of_2(write_max); ERR_FAIL_COND_V(buffer.resize(write_buffer_size) != OK, false); write_ptr = buffer.ptrw(); } diff --git a/engine/core/io/file_access_pack.cpp b/engine/core/io/file_access_pack.cpp index 89967c1572..f1124fd539 100644 --- a/engine/core/io/file_access_pack.cpp +++ b/engine/core/io/file_access_pack.cpp @@ -36,9 +36,9 @@ #include "core/os/os.h" #include "core/version.h" -Error PackedData::add_pack(const String &p_path, bool p_replace_files, uint64_t p_offset) { +Error PackedData::add_pack(const String &p_path, bool p_replace_files, uint64_t p_offset, const Vector &p_decryption_key) { for (int i = 0; i < sources.size(); i++) { - if (sources[i]->try_open_pack(p_path, p_replace_files, p_offset)) { + if (sources[i]->try_open_pack(p_path, p_replace_files, p_offset, p_decryption_key)) { return OK; } } @@ -46,7 +46,7 @@ Error PackedData::add_pack(const String &p_path, bool p_replace_files, uint64_t return ERR_FILE_UNRECOGNIZED; } -void PackedData::add_path(const String &p_pkg_path, const String &p_path, uint64_t p_ofs, uint64_t p_size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files, bool p_encrypted, bool p_bundle, bool p_delta) { +void PackedData::add_path(const String &p_pkg_path, const String &p_path, uint64_t p_ofs, uint64_t p_size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files, bool p_encrypted, bool p_bundle, bool p_delta, const String &p_salt) { String simplified_path = p_path.simplify_path().trim_prefix("res://"); PathMD5 pmd5(simplified_path.md5_buffer()); @@ -57,6 +57,7 @@ void PackedData::add_path(const String &p_pkg_path, const String &p_path, uint64 pf.bundle = p_bundle; pf.delta = p_delta; pf.pack = p_pkg_path; + pf.salt = p_salt; pf.offset = p_ofs; pf.size = p_size; for (int i = 0; i < 16; i++) { @@ -214,7 +215,7 @@ PackedData::~PackedData() { ////////////////////////////////////////////////////////////////// -bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset) { +bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset, const Vector &p_decryption_key) { Ref f = FileAccess::open(p_path, FileAccess::READ); if (f.is_null()) { return false; @@ -291,22 +292,28 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files, uint32_t ver_minor = f->get_32(); uint32_t ver_patch = f->get_32(); // Not used for validation. - ERR_FAIL_COND_V_MSG(version != PACK_FORMAT_VERSION_V3 && version != PACK_FORMAT_VERSION_V2, false, vformat("Pack version unsupported: %d.", version)); + ERR_FAIL_COND_V_MSG(version != PACK_FORMAT_VERSION_V4 && version != PACK_FORMAT_VERSION_V3 && version != PACK_FORMAT_VERSION_V2, false, vformat("Pack version unsupported: %d.", version)); ERR_FAIL_COND_V_MSG(ver_major > GODOT_VERSION_MAJOR || (ver_major == GODOT_VERSION_MAJOR && ver_minor > GODOT_VERSION_MINOR), false, vformat("Pack created with a newer version of the engine: %d.%d.%d.", ver_major, ver_minor, ver_patch)); uint32_t pack_flags = f->get_32(); bool enc_directory = (pack_flags & PACK_DIR_ENCRYPTED); bool rel_filebase = (pack_flags & PACK_REL_FILEBASE); // Note: Always enabled for V3. bool sparse_bundle = (pack_flags & PACK_SPARSE_BUNDLE); + String salt; uint64_t file_base = f->get_64(); - if ((version == PACK_FORMAT_VERSION_V3) || (version == PACK_FORMAT_VERSION_V2 && rel_filebase)) { + if ((version == PACK_FORMAT_VERSION_V4) || (version == PACK_FORMAT_VERSION_V3) || (version == PACK_FORMAT_VERSION_V2 && rel_filebase)) { file_base += pck_start_pos; } - if (version == PACK_FORMAT_VERSION_V3) { - // V3: Read directory offset and skip reserved part of the header. + if (version == PACK_FORMAT_VERSION_V3 || version == PACK_FORMAT_VERSION_V4) { + // V3/V4: Read directory offset and skip reserved part of the header. uint64_t dir_offset = f->get_64() + pck_start_pos; + if (sparse_bundle && enc_directory && version == PACK_FORMAT_VERSION_V4) { + // V4: Read encrypted directory salt. + Vector salt_data = f->get_buffer(32); + salt.append_latin1(Span((const char *)salt_data.ptr(), salt_data.size())); + } f->seek(dir_offset); } else if (version == PACK_FORMAT_VERSION_V2) { // V2: Directory directly after the header. @@ -323,9 +330,18 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files, ERR_FAIL_COND_V_MSG(fae.is_null(), false, "Can't open encrypted pack directory."); Vector key; - key.resize(32); - for (int i = 0; i < key.size(); i++) { - key.write[i] = script_encryption_key[i]; +#ifdef TOOLS_ENABLED + if (!p_decryption_key.is_empty()) { + ERR_FAIL_COND_V_MSG(p_decryption_key.size() != 32, false, "Decryption key must be 256-bit."); + constexpr uint8_t empty_key[32] = {}; + if (memcmp(script_encryption_key, empty_key, sizeof(empty_key)) == 0) { + key = p_decryption_key; + } + } else +#endif + { + key.resize(32); + memcpy(key.ptrw(), script_encryption_key, 32); } Error err = fae->open_and_parse(f, key, FileAccessEncrypted::MODE_READ, false); @@ -350,15 +366,15 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files, if (flags & PACK_FILE_REMOVAL) { // The file was removed. PackedData::get_singleton()->remove_path(path); } else { - PackedData::get_singleton()->add_path(p_path, path, file_base + ofs, size, md5, this, p_replace_files, (flags & PACK_FILE_ENCRYPTED), sparse_bundle, (flags & PACK_FILE_DELTA)); + PackedData::get_singleton()->add_path(p_path, path, file_base + ofs, size, md5, this, p_replace_files, (flags & PACK_FILE_ENCRYPTED), sparse_bundle, (flags & PACK_FILE_DELTA), salt); } } return true; } -Ref PackedSourcePCK::get_file(const String &p_path, PackedData::PackedFile *p_file) { - Ref file(memnew(FileAccessPack(p_path, *p_file))); +Ref PackedSourcePCK::get_file(const String &p_path, PackedData::PackedFile *p_file, const Vector &p_decryption_key) { + Ref file(memnew(FileAccessPack(p_path, *p_file, p_decryption_key))); if (PackedData::get_singleton()->has_delta_patches(p_path)) { Ref file_patched; @@ -373,7 +389,7 @@ Ref PackedSourcePCK::get_file(const String &p_path, PackedData::Pack ////////////////////////////////////////////////////////////////// -bool PackedSourceDirectory::try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset) { +bool PackedSourceDirectory::try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset, const Vector &p_decryption_key) { // Load with offset feature only supported for PCK files. ERR_FAIL_COND_V_MSG(p_offset != 0, false, "Invalid PCK data. Note that loading files with a non-zero offset isn't supported with directories."); @@ -384,7 +400,7 @@ bool PackedSourceDirectory::try_open_pack(const String &p_path, bool p_replace_f return true; } -Ref PackedSourceDirectory::get_file(const String &p_path, PackedData::PackedFile *p_file) { +Ref PackedSourceDirectory::get_file(const String &p_path, PackedData::PackedFile *p_file, const Vector &p_decryption_key) { Ref ret = FileAccess::create_for_path(p_path); ret->reopen(p_path, FileAccess::READ); return ret; @@ -507,12 +523,24 @@ void FileAccessPack::close() { f = Ref(); } -FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFile &p_file) { +FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFile &p_file, const Vector &p_decryption_key) { path = p_path; pf = p_file; if (pf.bundle) { String simplified_path = p_path.simplify_path(); - f = FileAccess::open(simplified_path, FileAccess::READ | FileAccess::SKIP_PACK); + String path_to_load = simplified_path; +#ifdef TOOLS_ENABLED + if (!pf.salt.is_empty()) { + path_to_load = pf.pack.get_base_dir().path_join((simplified_path + pf.salt).sha256_text()); + } else { + path_to_load = pf.pack.get_base_dir().path_join(simplified_path.replace("res://", "")); + } +#else + if (!pf.salt.is_empty()) { + path_to_load = "res://" + (simplified_path + pf.salt).sha256_text(); + } +#endif + f = FileAccess::open(path_to_load, FileAccess::READ | FileAccess::SKIP_PACK); ERR_FAIL_COND_MSG(f.is_null(), vformat(R"(Can't open pack-referenced file "%s" from sparse pack "%s".)", simplified_path, pf.pack)); off = 0; // For the sparse pack offset is always zero. } else { @@ -528,9 +556,18 @@ FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFil ERR_FAIL_COND_MSG(fae.is_null(), vformat(R"(Can't open encrypted pack-referenced file "%s" from pack "%s".)", p_path, pf.pack)); Vector key; - key.resize(32); - for (int i = 0; i < key.size(); i++) { - key.write[i] = script_encryption_key[i]; +#ifdef TOOLS_ENABLED + if (!p_decryption_key.is_empty()) { + ERR_FAIL_COND_MSG(p_decryption_key.size() != 32, "Decryption key must be 256-bit."); + constexpr uint8_t empty_key[32] = {}; + if (memcmp(script_encryption_key, empty_key, sizeof(empty_key)) == 0) { + key = p_decryption_key; + } + } else +#endif + { + key.resize(32); + memcpy(key.ptrw(), script_encryption_key, 32); } Error err = fae->open_and_parse(f, key, FileAccessEncrypted::MODE_READ, false); diff --git a/engine/core/io/file_access_pack.h b/engine/core/io/file_access_pack.h index 121372b51e..490778f203 100644 --- a/engine/core/io/file_access_pack.h +++ b/engine/core/io/file_access_pack.h @@ -32,6 +32,7 @@ #include "core/io/dir_access.h" #include "core/io/file_access.h" +#include "core/io/resource_uid.h" #include "core/string/print_string.h" #include "core/templates/hash_set.h" #include "core/templates/list.h" @@ -41,9 +42,10 @@ #define PACK_FORMAT_VERSION_V2 2 #define PACK_FORMAT_VERSION_V3 3 +#define PACK_FORMAT_VERSION_V4 4 // The current packed file format version number. -#define PACK_FORMAT_VERSION PACK_FORMAT_VERSION_V3 +#define PACK_FORMAT_VERSION PACK_FORMAT_VERSION_V4 enum PackFlags { PACK_DIR_ENCRYPTED = 1 << 0, @@ -74,6 +76,7 @@ public: bool encrypted; bool bundle; bool delta; + String salt; }; private: @@ -117,9 +120,18 @@ private: void _free_packed_dirs(PackedDir *p_dir); void _get_file_paths(PackedDir *p_dir, const String &p_parent_dir, HashSet &r_paths) const; + _FORCE_INLINE_ PathMD5 _get_simplified_path(const String &p_path) { + String simplified_path = p_path; + if (simplified_path.begins_with("uid://")) { + simplified_path = ResourceUID::uid_to_path(simplified_path); + } + simplified_path = simplified_path.simplify_path().trim_prefix("res://"); + return PathMD5(simplified_path.md5_buffer()); + } + public: void add_pack_source(PackSource *p_source); - void add_path(const String &p_pkg_path, const String &p_path, uint64_t p_ofs, uint64_t p_size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files, bool p_encrypted = false, bool p_bundle = false, bool p_delta = false); // for PackSource + void add_path(const String &p_pkg_path, const String &p_path, uint64_t p_ofs, uint64_t p_size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files, bool p_encrypted = false, bool p_bundle = false, bool p_delta = false, const String &p_salt = String()); // for PackSource void remove_path(const String &p_path); uint8_t *get_file_hash(const String &p_path); Vector get_delta_patches(const String &p_path) const; @@ -130,11 +142,11 @@ public: _FORCE_INLINE_ bool is_disabled() const { return disabled; } static PackedData *get_singleton() { return singleton; } - Error add_pack(const String &p_path, bool p_replace_files, uint64_t p_offset); + Error add_pack(const String &p_path, bool p_replace_files, uint64_t p_offset, const Vector &p_decryption_key = Vector()); void clear(); - _FORCE_INLINE_ Ref try_open_path(const String &p_path); + _FORCE_INLINE_ Ref try_open_path(const String &p_path, const Vector &p_decryption_key = Vector()); _FORCE_INLINE_ bool has_path(const String &p_path); _FORCE_INLINE_ int64_t get_size(const String &p_path); @@ -148,23 +160,23 @@ public: class PackSource { public: - virtual bool try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset) = 0; - virtual Ref get_file(const String &p_path, PackedData::PackedFile *p_file) = 0; + virtual bool try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset, const Vector &p_decryption_key = Vector()) = 0; + virtual Ref get_file(const String &p_path, PackedData::PackedFile *p_file, const Vector &p_decryption_key = Vector()) = 0; virtual ~PackSource() {} }; class PackedSourcePCK : public PackSource { public: - virtual bool try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset) override; - virtual Ref get_file(const String &p_path, PackedData::PackedFile *p_file) override; + virtual bool try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset, const Vector &p_decryption_key = Vector()) override; + virtual Ref get_file(const String &p_path, PackedData::PackedFile *p_file, const Vector &p_decryption_key = Vector()) override; }; class PackedSourceDirectory : public PackSource { void add_directory(const String &p_path, bool p_replace_files); public: - virtual bool try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset) override; - virtual Ref get_file(const String &p_path, PackedData::PackedFile *p_file) override; + virtual bool try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset, const Vector &p_decryption_key = Vector()) override; + virtual Ref get_file(const String &p_path, PackedData::PackedFile *p_file, const Vector &p_decryption_key = Vector()) override; }; class FileAccessPack : public FileAccess { @@ -216,13 +228,11 @@ public: virtual void close() override; - FileAccessPack(const String &p_path, const PackedData::PackedFile &p_file); + FileAccessPack(const String &p_path, const PackedData::PackedFile &p_file, const Vector &p_decryption_key = Vector()); }; int64_t PackedData::get_size(const String &p_path) { - String simplified_path = p_path.simplify_path(); - PathMD5 pmd5(simplified_path.md5_buffer()); - HashMap::Iterator E = files.find(pmd5); + HashMap::Iterator E = files.find(_get_simplified_path(p_path)); if (!E) { return -1; // File not found. } @@ -232,19 +242,17 @@ int64_t PackedData::get_size(const String &p_path) { return E->value.size; } -Ref PackedData::try_open_path(const String &p_path) { - String simplified_path = p_path.simplify_path().trim_prefix("res://"); - PathMD5 pmd5(simplified_path.md5_buffer()); - HashMap::Iterator E = files.find(pmd5); +Ref PackedData::try_open_path(const String &p_path, const Vector &p_decryption_key) { + HashMap::Iterator E = files.find(_get_simplified_path(p_path)); if (!E) { return nullptr; // Not found. } - return E->value.src->get_file(p_path, &E->value); + return E->value.src->get_file(p_path, &E->value, p_decryption_key); } bool PackedData::has_path(const String &p_path) { - return files.has(PathMD5(p_path.simplify_path().trim_prefix("res://").md5_buffer())); + return files.has(_get_simplified_path(p_path)); } bool PackedData::has_directory(const String &p_path) { diff --git a/engine/core/io/file_access_zip.cpp b/engine/core/io/file_access_zip.cpp index 33bceee52d..2bb9db761d 100644 --- a/engine/core/io/file_access_zip.cpp +++ b/engine/core/io/file_access_zip.cpp @@ -144,7 +144,7 @@ unzFile ZipArchive::get_file_handle(const String &p_file) const { return pkg; } -bool ZipArchive::try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset = 0) { +bool ZipArchive::try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset, const Vector &p_decryption_key) { // load with offset feature only supported for PCK files ERR_FAIL_COND_V_MSG(p_offset != 0, false, "Invalid PCK data. Note that loading files with a non-zero offset isn't supported with ZIP archives."); @@ -209,7 +209,7 @@ bool ZipArchive::file_exists(const String &p_name) const { return files.has(p_name); } -Ref ZipArchive::get_file(const String &p_path, PackedData::PackedFile *p_file) { +Ref ZipArchive::get_file(const String &p_path, PackedData::PackedFile *p_file, const Vector &p_decryption_key) { return memnew(FileAccessZip(p_path, *p_file)); } diff --git a/engine/core/io/file_access_zip.h b/engine/core/io/file_access_zip.h index a352322886..3cdd1643b5 100644 --- a/engine/core/io/file_access_zip.h +++ b/engine/core/io/file_access_zip.h @@ -61,8 +61,8 @@ public: bool file_exists(const String &p_name) const; - virtual bool try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset) override; - Ref get_file(const String &p_path, PackedData::PackedFile *p_file) override; + virtual bool try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset, const Vector &p_decryption_key = Vector()) override; + Ref get_file(const String &p_path, PackedData::PackedFile *p_file, const Vector &p_decryption_key = Vector()) override; static ZipArchive *get_singleton(); diff --git a/engine/core/io/http_client.cpp b/engine/core/io/http_client.cpp index c452faf18f..d0a2bd01b5 100644 --- a/engine/core/io/http_client.cpp +++ b/engine/core/io/http_client.cpp @@ -30,6 +30,8 @@ #include "http_client.h" +#include "core/object/class_db.h" + const char *HTTPClient::_methods[METHOD_MAX] = { "GET", "HEAD", @@ -167,7 +169,7 @@ void HTTPClient::_bind_methods() { ClassDB::bind_method(D_METHOD("query_string_from_dict", "fields"), &HTTPClient::query_string_from_dict); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "blocking_mode_enabled"), "set_blocking_mode", "is_blocking_mode_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "connection", PROPERTY_HINT_RESOURCE_TYPE, "StreamPeer", PROPERTY_USAGE_NONE), "set_connection", "get_connection"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "connection", PROPERTY_HINT_RESOURCE_TYPE, StreamPeer::get_class_static(), PROPERTY_USAGE_NONE), "set_connection", "get_connection"); ADD_PROPERTY(PropertyInfo(Variant::INT, "read_chunk_size", PROPERTY_HINT_RANGE, "256,16777216"), "set_read_chunk_size", "get_read_chunk_size"); BIND_ENUM_CONSTANT(METHOD_GET); diff --git a/engine/core/io/http_client.h b/engine/core/io/http_client.h index 54a3d9c436..d2da1e6a6f 100644 --- a/engine/core/io/http_client.h +++ b/engine/core/io/http_client.h @@ -31,9 +31,7 @@ #pragma once #include "core/crypto/crypto.h" -#include "core/io/ip.h" #include "core/io/stream_peer.h" -#include "core/io/stream_peer_tcp.h" #include "core/object/ref_counted.h" class HTTPClient : public RefCounted { diff --git a/engine/core/io/http_client_tcp.cpp b/engine/core/io/http_client_tcp.cpp index f6495585a7..13f487e13c 100644 --- a/engine/core/io/http_client_tcp.cpp +++ b/engine/core/io/http_client_tcp.cpp @@ -32,7 +32,10 @@ #include "http_client_tcp.h" +#include "core/io/stream_peer_tcp.h" #include "core/io/stream_peer_tls.h" +#include "core/object/class_db.h" +#include "core/os/os.h" #include "core/version.h" HTTPClient *HTTPClientTCP::_create_func(bool p_notify_postinitialize) { diff --git a/engine/core/io/http_client_tcp.h b/engine/core/io/http_client_tcp.h index 84f9bcc16b..ae6f3b453a 100644 --- a/engine/core/io/http_client_tcp.h +++ b/engine/core/io/http_client_tcp.h @@ -33,6 +33,9 @@ #include "http_client.h" #include "core/crypto/crypto.h" +#include "core/io/ip.h" + +class StreamPeerTCP; class HTTPClientTCP : public HTTPClient { GDSOFTCLASS(HTTPClientTCP, HTTPClient); diff --git a/engine/core/io/image.cpp b/engine/core/io/image.cpp index 47e7342e48..0b4900892c 100644 --- a/engine/core/io/image.cpp +++ b/engine/core/io/image.cpp @@ -35,6 +35,7 @@ #include "core/io/image_loader.h" #include "core/io/resource_loader.h" #include "core/math/math_funcs.h" +#include "core/object/class_db.h" #include "core/templates/hash_map.h" #include "core/variant/dictionary.h" @@ -1231,14 +1232,14 @@ static void _overlay(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, } bool Image::is_size_po2() const { - return is_power_of_2(width) && is_power_of_2(height); + return Math::is_power_of_2(width) && Math::is_power_of_2(height); } void Image::resize_to_po2(bool p_square, Interpolation p_interpolation) { ERR_FAIL_COND_MSG(is_compressed(), "Cannot resize in compressed image formats."); - int w = next_power_of_2((uint32_t)width); - int h = next_power_of_2((uint32_t)height); + int w = Math::next_power_of_2((uint32_t)width); + int h = Math::next_power_of_2((uint32_t)height); if (p_square) { w = h = MAX(w, h); } @@ -1255,9 +1256,6 @@ void Image::resize_to_po2(bool p_square, Interpolation p_interpolation) { void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { ERR_FAIL_COND_MSG(data.is_empty(), "Cannot resize image before creating it, use set_data() first."); ERR_FAIL_COND_MSG(is_compressed(), "Cannot resize in compressed image formats."); - - bool mipmap_aware = p_interpolation == INTERPOLATE_TRILINEAR /* || p_interpolation == INTERPOLATE_TRICUBIC */; - ERR_FAIL_COND_MSG(p_width <= 0, "Image width must be greater than 0."); ERR_FAIL_COND_MSG(p_height <= 0, "Image height must be greater than 0."); ERR_FAIL_COND_MSG(p_width > MAX_WIDTH, vformat("Image width cannot be greater than %d pixels.", MAX_WIDTH)); @@ -1268,9 +1266,19 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { return; } + // Convert the image to 'standard' RGB(A) formats that may be resized. + Format original_format = format; + if (original_format == FORMAT_RGB565 || original_format == FORMAT_RGBA4444) { + convert(FORMAT_RGBA8); + } else if (original_format == FORMAT_RGBE9995) { + convert(FORMAT_RGBH); + } + Image dst(p_width, p_height, false, format); // Setup mipmap-aware scaling + bool mipmap_aware = p_interpolation == INTERPOLATE_TRILINEAR /* || p_interpolation == INTERPOLATE_TRICUBIC */; + Image dst2; int mip1 = 0; int mip2 = 0; @@ -1614,6 +1622,11 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { } _copy_internals_from(dst); + + // Reconvert the image to its original format. + if (original_format != format) { + convert(original_format); + } } void Image::crop_from_point(int p_x, int p_y, int p_width, int p_height) { @@ -2600,24 +2613,24 @@ void Image::initialize_data(const char **p_xpm) { #define DETECT_ALPHA_MAX_THRESHOLD 254 #define DETECT_ALPHA_MIN_THRESHOLD 2 -#define DETECT_ALPHA(m_value) \ - { \ - uint8_t value = m_value; \ - if (value < DETECT_ALPHA_MIN_THRESHOLD) \ - bit = true; \ +#define DETECT_ALPHA(m_value) \ + { \ + uint8_t value = m_value; \ + if (value < DETECT_ALPHA_MIN_THRESHOLD) \ + bit = true; \ else if (value < DETECT_ALPHA_MAX_THRESHOLD) { \ - detected = true; \ - break; \ - } \ + detected = true; \ + break; \ + } \ } #define DETECT_NON_ALPHA(m_value) \ - { \ - uint8_t value = m_value; \ - if (value > 0) { \ - detected = true; \ - break; \ - } \ + { \ + uint8_t value = m_value; \ + if (value > 0) { \ + detected = true; \ + break; \ + } \ } bool Image::is_invisible() const { diff --git a/engine/core/io/image_loader.cpp b/engine/core/io/image_loader.cpp index df36c515cf..c8e909e281 100644 --- a/engine/core/io/image_loader.cpp +++ b/engine/core/io/image_loader.cpp @@ -30,6 +30,8 @@ #include "image_loader.h" +#include "core/object/class_db.h" + void ImageFormatLoader::_bind_methods() { BIND_BITFIELD_FLAG(FLAG_NONE); BIND_BITFIELD_FLAG(FLAG_FORCE_LINEAR); diff --git a/engine/core/io/image_loader.h b/engine/core/io/image_loader.h index 3ae09c5fb3..e8fbbc5110 100644 --- a/engine/core/io/image_loader.h +++ b/engine/core/io/image_loader.h @@ -30,14 +30,13 @@ #pragma once -#include "core/core_bind.h" #include "core/io/file_access.h" #include "core/io/image.h" #include "core/io/resource_loader.h" -#include "core/object/gdvirtual.gen.inc" +#include "core/object/gdvirtual.gen.h" #include "core/string/ustring.h" #include "core/templates/list.h" -#include "core/variant/binder_common.h" +#include "core/variant/type_info.h" class ImageLoader; diff --git a/engine/core/io/ip.cpp b/engine/core/io/ip.cpp index 94256a7b57..ec7c72d274 100644 --- a/engine/core/io/ip.cpp +++ b/engine/core/io/ip.cpp @@ -30,6 +30,7 @@ #include "ip.h" +#include "core/object/class_db.h" #include "core/os/semaphore.h" #include "core/os/thread.h" #include "core/templates/hash_map.h" @@ -205,7 +206,7 @@ IPAddress IP::get_resolve_item_address(ResolverID p_id) const { return IPAddress(); } - List res = resolver->queue[p_id].response; + List res(resolver->queue[p_id].response); for (const IPAddress &E : res) { if (E.is_valid()) { @@ -224,7 +225,7 @@ Array IP::get_resolve_item_addresses(ResolverID p_id) const { return Array(); } - List res = resolver->queue[p_id].response; + List res(resolver->queue[p_id].response); Array result; for (const IPAddress &E : res) { diff --git a/engine/core/io/ip.h b/engine/core/io/ip.h index bab9625c7e..f9de6316b3 100644 --- a/engine/core/io/ip.h +++ b/engine/core/io/ip.h @@ -31,7 +31,7 @@ #pragma once #include "core/io/ip_address.h" -#include "core/os/os.h" +#include "core/variant/type_info.h" template class TypedArray; diff --git a/engine/core/io/json.cpp b/engine/core/io/json.cpp index 857695e2b9..dd91816155 100644 --- a/engine/core/io/json.cpp +++ b/engine/core/io/json.cpp @@ -32,6 +32,7 @@ #include "core/config/engine.h" #include "core/io/file_access.h" +#include "core/object/class_db.h" #include "core/object/script_language.h" #include "core/variant/container_type_validate.h" @@ -155,6 +156,11 @@ void JSON::_stringify(String &r_result, const Variant &p_var, const String &p_in ERR_FAIL_MSG("Converting circular structure to JSON."); } + if (d.is_empty()) { + r_result += "{}"; + return; + } + r_result += '{'; r_result += end_statement; p_markers.insert(d.id()); @@ -668,10 +674,10 @@ static bool _encode_container_type(Dictionary &r_dict, const String &p_key, cons } Variant JSON::_from_native(const Variant &p_variant, bool p_full_objects, int p_depth) { -#define RETURN_ARGS \ - Dictionary ret; \ +#define RETURN_ARGS \ + Dictionary ret; \ ret[TYPE] = Variant::get_type_name(p_variant.get_type()); \ - ret[ARGS] = args; \ + ret[ARGS] = args; \ return ret switch (p_variant.get_type()) { @@ -1093,18 +1099,18 @@ Variant JSON::_to_native(const Variant &p_json, bool p_allow_objects, int p_dept ERR_FAIL_COND_V(!dict.has(TYPE), Variant()); -#define LOAD_ARGS() \ +#define LOAD_ARGS() \ ERR_FAIL_COND_V(!dict.has(ARGS), Variant()); \ const Array args = dict[ARGS] -#define LOAD_ARGS_CHECK_SIZE(m_size) \ +#define LOAD_ARGS_CHECK_SIZE(m_size) \ ERR_FAIL_COND_V(!dict.has(ARGS), Variant()); \ - const Array args = dict[ARGS]; \ + const Array args = dict[ARGS]; \ ERR_FAIL_COND_V(args.size() != (m_size), Variant()) -#define LOAD_ARGS_CHECK_FACTOR(m_factor) \ +#define LOAD_ARGS_CHECK_FACTOR(m_factor) \ ERR_FAIL_COND_V(!dict.has(ARGS), Variant()); \ - const Array args = dict[ARGS]; \ + const Array args = dict[ARGS]; \ ERR_FAIL_COND_V(args.size() % (m_factor) != 0, Variant()) switch (Variant::get_type_by_name(dict[TYPE])) { diff --git a/engine/core/io/logger.cpp b/engine/core/io/logger.cpp index c9625354d2..feab968add 100644 --- a/engine/core/io/logger.cpp +++ b/engine/core/io/logger.cpp @@ -39,6 +39,8 @@ #include "modules/modules_enabled.gen.h" // For regex. +#include + #ifdef MODULE_REGEX_ENABLED #include "modules/regex/regex.h" #endif // MODULE_REGEX_ENABLED diff --git a/engine/core/io/marshalls.cpp b/engine/core/io/marshalls.cpp index 6eb7e2db04..4579fb9393 100644 --- a/engine/core/io/marshalls.cpp +++ b/engine/core/io/marshalls.cpp @@ -31,6 +31,7 @@ #include "marshalls.h" #include "core/io/resource_loader.h" +#include "core/object/class_db.h" #include "core/object/ref_counted.h" #include "core/object/script_language.h" #include "core/variant/container_type_validate.h" @@ -1519,7 +1520,9 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo encode_uint32(utf8.length(), buf); buf += 4; memcpy(buf, utf8.get_data(), utf8.length()); - buf += pad + utf8.length(); + buf += utf8.length(); + memset(buf, 0, pad); + buf += pad; } r_len += 4 + utf8.length() + pad; diff --git a/engine/core/io/missing_resource.cpp b/engine/core/io/missing_resource.cpp index 3bb7c8e90b..e7b598d37c 100644 --- a/engine/core/io/missing_resource.cpp +++ b/engine/core/io/missing_resource.cpp @@ -30,6 +30,8 @@ #include "missing_resource.h" +#include "core/object/class_db.h" + bool MissingResource::_set(const StringName &p_name, const Variant &p_value) { if (is_recording_properties()) { properties.insert(p_name, p_value); diff --git a/engine/core/io/packed_data_container.cpp b/engine/core/io/packed_data_container.cpp index 222e167919..701bb9c10e 100644 --- a/engine/core/io/packed_data_container.cpp +++ b/engine/core/io/packed_data_container.cpp @@ -31,6 +31,7 @@ #include "packed_data_container.h" #include "core/io/marshalls.h" +#include "core/object/class_db.h" Variant PackedDataContainer::getvar(const Variant &p_key, bool *r_valid) const { bool err = false; diff --git a/engine/core/io/packet_peer.cpp b/engine/core/io/packet_peer.cpp index d4b9cdb754..f5e6b8d5f9 100644 --- a/engine/core/io/packet_peer.cpp +++ b/engine/core/io/packet_peer.cpp @@ -32,13 +32,14 @@ #include "core/config/project_settings.h" #include "core/io/marshalls.h" +#include "core/object/class_db.h" /* helpers / binders */ void PacketPeer::set_encode_buffer_max_size(int p_max_size) { ERR_FAIL_COND_MSG(p_max_size < 1024, "Max encode buffer must be at least 1024 bytes"); ERR_FAIL_COND_MSG(p_max_size > 256 * 1024 * 1024, "Max encode buffer cannot exceed 256 MiB"); - encode_buffer_max_size = next_power_of_2((uint32_t)p_max_size); + encode_buffer_max_size = Math::next_power_of_2((uint32_t)p_max_size); encode_buffer.clear(); } @@ -103,7 +104,7 @@ Error PacketPeer::put_var(const Variant &p_packet, bool p_full_objects) { if (unlikely(encode_buffer.size() < len)) { encode_buffer.resize(0); // Avoid realloc - encode_buffer.resize(next_power_of_2((uint32_t)len)); + encode_buffer.resize(Math::next_power_of_2((uint32_t)len)); } uint8_t *w = encode_buffer.ptrw(); @@ -189,7 +190,7 @@ void PacketPeerStream::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "input_buffer_max_size"), "set_input_buffer_max_size", "get_input_buffer_max_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "output_buffer_max_size"), "set_output_buffer_max_size", "get_output_buffer_max_size"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream_peer", PROPERTY_HINT_RESOURCE_TYPE, "StreamPeer", PROPERTY_USAGE_NONE), "set_stream_peer", "get_stream_peer"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream_peer", PROPERTY_HINT_RESOURCE_TYPE, StreamPeer::get_class_static(), PROPERTY_USAGE_NONE), "set_stream_peer", "get_stream_peer"); } Error PacketPeerStream::_poll_buffer() const { @@ -301,8 +302,8 @@ void PacketPeerStream::set_input_buffer_max_size(int p_max_size) { ERR_FAIL_COND_MSG(p_max_size < 0, "Max size of input buffer size cannot be smaller than 0."); // WARNING: May lose packets. ERR_FAIL_COND_MSG(ring_buffer.data_left(), "Buffer in use, resizing would cause loss of data."); - ring_buffer.resize(nearest_shift(next_power_of_2((uint32_t)p_max_size + (uint32_t)4)) - 1); - input_buffer.resize(next_power_of_2((uint32_t)p_max_size + (uint32_t)4)); + ring_buffer.resize(Math::nearest_shift(Math::next_power_of_2((uint32_t)p_max_size + (uint32_t)4)) - 1); + input_buffer.resize(Math::next_power_of_2((uint32_t)p_max_size + (uint32_t)4)); } int PacketPeerStream::get_input_buffer_max_size() const { @@ -310,7 +311,7 @@ int PacketPeerStream::get_input_buffer_max_size() const { } void PacketPeerStream::set_output_buffer_max_size(int p_max_size) { - output_buffer.resize(next_power_of_2((uint32_t)p_max_size + (uint32_t)4)); + output_buffer.resize(Math::next_power_of_2((uint32_t)p_max_size + (uint32_t)4)); } int PacketPeerStream::get_output_buffer_max_size() const { diff --git a/engine/core/io/packet_peer.h b/engine/core/io/packet_peer.h index 85526daedf..ecb664b41f 100644 --- a/engine/core/io/packet_peer.h +++ b/engine/core/io/packet_peer.h @@ -31,11 +31,10 @@ #pragma once #include "core/io/stream_peer.h" -#include "core/object/class_db.h" #include "core/templates/ring_buffer.h" -#include "core/extension/ext_wrappers.gen.inc" -#include "core/object/gdvirtual.gen.inc" +#include "core/extension/ext_wrappers.gen.h" +#include "core/object/gdvirtual.gen.h" #include "core/variant/native_ptr.h" class PacketPeer : public RefCounted { diff --git a/engine/core/io/packet_peer_dtls.cpp b/engine/core/io/packet_peer_dtls.cpp index b37b79914e..c8ad201e0a 100644 --- a/engine/core/io/packet_peer_dtls.cpp +++ b/engine/core/io/packet_peer_dtls.cpp @@ -30,6 +30,8 @@ #include "packet_peer_dtls.h" +#include "core/object/class_db.h" + PacketPeerDTLS *PacketPeerDTLS::create(bool p_notify_postinitialize) { if (_create) { return _create(p_notify_postinitialize); diff --git a/engine/core/io/packet_peer_udp.cpp b/engine/core/io/packet_peer_udp.cpp index 478c2d76ba..86076044d1 100644 --- a/engine/core/io/packet_peer_udp.cpp +++ b/engine/core/io/packet_peer_udp.cpp @@ -32,6 +32,7 @@ #include "core/io/ip.h" #include "core/io/udp_server.h" +#include "core/object/class_db.h" void PacketPeerUDP::set_blocking_mode(bool p_enable) { blocking = p_enable; @@ -201,7 +202,7 @@ Error PacketPeerUDP::bind(int p_port, const IPAddress &p_bind_address, int p_rec _sock->close(); return err; } - rb.resize(nearest_shift((uint32_t)p_recv_buffer_size)); + rb.resize(Math::nearest_shift((uint32_t)p_recv_buffer_size)); return OK; } diff --git a/engine/core/io/packet_peer_udp.h b/engine/core/io/packet_peer_udp.h index 1c909860c5..ef005b0854 100644 --- a/engine/core/io/packet_peer_udp.h +++ b/engine/core/io/packet_peer_udp.h @@ -30,7 +30,7 @@ #pragma once -#include "core/io/ip.h" +#include "core/io/ip_address.h" #include "core/io/net_socket.h" #include "core/io/packet_peer.h" diff --git a/engine/core/io/pck_packer.cpp b/engine/core/io/pck_packer.cpp index ee7bb15ba5..b9670ddfd1 100644 --- a/engine/core/io/pck_packer.cpp +++ b/engine/core/io/pck_packer.cpp @@ -34,6 +34,7 @@ #include "core/io/file_access.h" #include "core/io/file_access_encrypted.h" #include "core/io/file_access_pack.h" // PACK_HEADER_MAGIC, PACK_FORMAT_VERSION +#include "core/object/class_db.h" #include "core/version.h" static int _get_pad(int p_alignment, int p_n) { @@ -49,6 +50,7 @@ static int _get_pad(int p_alignment, int p_n) { void PCKPacker::_bind_methods() { ClassDB::bind_method(D_METHOD("pck_start", "pck_path", "alignment", "key", "encrypt_directory"), &PCKPacker::pck_start, DEFVAL(32), DEFVAL("0000000000000000000000000000000000000000000000000000000000000000"), DEFVAL(false)); ClassDB::bind_method(D_METHOD("add_file", "target_path", "source_path", "encrypt"), &PCKPacker::add_file, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("add_file_from_buffer", "target_path", "data", "encrypt"), &PCKPacker::add_file_from_buffer, DEFVAL(false)); ClassDB::bind_method(D_METHOD("add_file_removal", "target_path"), &PCKPacker::add_file_removal); ClassDB::bind_method(D_METHOD("flush", "verbose"), &PCKPacker::flush, DEFVAL(false)); } @@ -152,6 +154,17 @@ Error PCKPacker::add_file(const String &p_target_path, const String &p_source_pa if (f.is_null()) { return ERR_FILE_CANT_OPEN; } + Vector data = FileAccess::get_file_as_bytes(p_source_path); + + return _add_file(p_target_path, p_source_path, data, p_encrypt); +} + +Error PCKPacker::add_file_from_buffer(const String &p_target_path, const Vector &p_data, bool p_encrypt) { + return _add_file(p_target_path, "", p_data, p_encrypt); +} + +Error PCKPacker::_add_file(const String &p_target_path, const String &p_source_path, const Vector &p_data, bool p_encrypt) { + ERR_FAIL_COND_V_MSG(file.is_null(), ERR_INVALID_PARAMETER, "File must be opened before use."); File pf; // Simplify path here and on every 'files' access so that paths that have extra '/' @@ -159,12 +172,11 @@ Error PCKPacker::add_file(const String &p_target_path, const String &p_source_pa pf.path = p_target_path.simplify_path().trim_prefix("res://"); pf.src_path = p_source_path; pf.ofs = file->get_position(); - pf.size = f->get_length(); + pf.size = p_data.size(); - Vector data = FileAccess::get_file_as_bytes(p_source_path); { unsigned char hash[16]; - CryptoCore::md5(data.ptr(), data.size(), hash); + CryptoCore::md5(p_data.ptr(), p_data.size(), hash); pf.md5.resize(16); for (int i = 0; i < 16; i++) { pf.md5.write[i] = hash[i]; @@ -184,7 +196,7 @@ Error PCKPacker::add_file(const String &p_target_path, const String &p_source_pa ftmp = fae; } - ftmp->store_buffer(data); + ftmp->store_buffer(p_data); if (fae.is_valid()) { ftmp.unref(); @@ -256,7 +268,7 @@ Error PCKPacker::flush(bool p_verbose) { fhead->store_32(flags); if (p_verbose) { - print_line(vformat("[%d/%d - %d%%] PCKPacker flush: %s -> %s", i, file_num, float(i) / file_num * 100, files[i].src_path, files[i].path)); + print_line(vformat("[%d/%d - %d%%] PCKPacker flush: %s -> %s", i + 1, file_num, float(i + 1) / file_num * 100, files[i].src_path, files[i].path)); } } diff --git a/engine/core/io/pck_packer.h b/engine/core/io/pck_packer.h index 65f88397d4..a2726bdecf 100644 --- a/engine/core/io/pck_packer.h +++ b/engine/core/io/pck_packer.h @@ -60,9 +60,12 @@ class PCKPacker : public RefCounted { }; Vector files; + Error _add_file(const String &p_target_path, const String &p_source_path, const Vector &p_data, bool p_encrypt = false); + public: Error pck_start(const String &p_pck_path, int p_alignment = 32, const String &p_key = "0000000000000000000000000000000000000000000000000000000000000000", bool p_encrypt_directory = false); Error add_file(const String &p_target_path, const String &p_source_path, bool p_encrypt = false); + Error add_file_from_buffer(const String &p_target_path, const Vector &p_data, bool p_encrypt = false); Error add_file_removal(const String &p_target_path); Error flush(bool p_verbose = false); diff --git a/engine/core/io/remote_filesystem_client.cpp b/engine/core/io/remote_filesystem_client.cpp index e542111588..f637fb6725 100644 --- a/engine/core/io/remote_filesystem_client.cpp +++ b/engine/core/io/remote_filesystem_client.cpp @@ -33,6 +33,7 @@ #include "core/io/dir_access.h" #include "core/io/file_access.h" #include "core/io/stream_peer_tcp.h" +#include "core/os/os.h" #include "core/string/string_builder.h" #define FILESYSTEM_CACHE_VERSION 1 diff --git a/engine/core/io/resource.cpp b/engine/core/io/resource.cpp index 50d399db71..d4cdee23cc 100644 --- a/engine/core/io/resource.cpp +++ b/engine/core/io/resource.cpp @@ -33,10 +33,19 @@ #include "core/io/resource_loader.h" #include "core/math/math_funcs.h" #include "core/math/random_pcg.h" +#include "core/object/class_db.h" #include "core/os/os.h" -#include "core/variant/container_type_validate.h" +#include "core/variant/container_type_validate.h" // IWYU pragma: keep. #include "scene/main/node.h" //only so casting works +void Resource::register_custom_data_to_otdb() { + ClassDB::add_resource_base_extension("res", get_class_static()); +} + +void Resource::_add_resource_base_extension_to_classdb(const String &p_extension, const String &p_class) { + ClassDB::add_resource_base_extension(p_extension, p_class); +} + void Resource::emit_changed() { if (emit_changed_state != EMIT_CHANGED_UNBLOCKED) { emit_changed_state = EMIT_CHANGED_BLOCKED_PENDING_EMIT; @@ -373,7 +382,7 @@ Ref Resource::_duplicate(const DuplicateParams &p_params) const { // These are for avoiding potential duplicates that can happen in custom code // from participating in the same duplication session (remap cache). #define BEFORE_USER_CODE thread_duplicate_remap_cache = nullptr; -#define AFTER_USER_CODE \ +#define AFTER_USER_CODE \ thread_duplicate_remap_cache = remap_cache_backup; \ thread_duplicate_remap_cache_needs_deallocation = remap_cache_needs_deallocation_backup; diff --git a/engine/core/io/resource.h b/engine/core/io/resource.h index 7feffb7458..dd432b9db7 100644 --- a/engine/core/io/resource.h +++ b/engine/core/io/resource.h @@ -30,24 +30,23 @@ #pragma once -#include "core/io/resource_uid.h" -#include "core/object/class_db.h" -#include "core/object/gdvirtual.gen.inc" +#include "core/io/resource_uid.h" // IWYU pragma: export. Make available to all resources. +#include "core/object/gdvirtual.gen.h" #include "core/object/ref_counted.h" -#include "core/templates/safe_refcount.h" #include "core/templates/self_list.h" class Node; +class RWLock; -#define RES_BASE_EXTENSION(m_ext) \ -public: \ - static void register_custom_data_to_otdb() { \ - ClassDB::add_resource_base_extension(m_ext, get_class_static()); \ - } \ - virtual String get_base_extension() const override { \ - return m_ext; \ - } \ - \ +#define RES_BASE_EXTENSION(m_ext) \ +public: \ + static void register_custom_data_to_otdb() { \ + Resource::_add_resource_base_extension_to_classdb(m_ext, get_class_static()); \ + } \ + virtual String get_base_extension() const override { \ + return m_ext; \ + } \ +\ private: class Resource : public RefCounted { @@ -56,10 +55,11 @@ class Resource : public RefCounted { public: static constexpr AncestralClass static_ancestral_class = AncestralClass::RESOURCE; - static void register_custom_data_to_otdb() { ClassDB::add_resource_base_extension("res", get_class_static()); } + static void register_custom_data_to_otdb(); virtual String get_base_extension() const { return "res"; } protected: + static void _add_resource_base_extension_to_classdb(const String &p_extension, const String &p_class); struct DuplicateParams { bool deep = false; ResourceDeepDuplicateMode subres_mode = RESOURCE_DEEP_DUPLICATE_MAX; @@ -67,7 +67,6 @@ protected: }; private: - friend class ResBase; friend class ResourceCache; String name; diff --git a/engine/core/io/resource_format_binary.cpp b/engine/core/io/resource_format_binary.cpp index 8446e10413..0889810c3c 100644 --- a/engine/core/io/resource_format_binary.cpp +++ b/engine/core/io/resource_format_binary.cpp @@ -34,6 +34,7 @@ #include "core/io/dir_access.h" #include "core/io/file_access_compressed.h" #include "core/io/missing_resource.h" +#include "core/object/class_db.h" #include "core/object/script_language.h" #include "core/version.h" #include "scene/property_utils.h" diff --git a/engine/core/io/resource_importer.cpp b/engine/core/io/resource_importer.cpp index fc36e5e131..636dc74396 100644 --- a/engine/core/io/resource_importer.cpp +++ b/engine/core/io/resource_importer.cpp @@ -33,6 +33,7 @@ #include "core/config/project_settings.h" #include "core/io/config_file.h" #include "core/io/image.h" +#include "core/object/class_db.h" #include "core/os/os.h" #include "core/variant/variant_parser.h" diff --git a/engine/core/io/resource_loader.cpp b/engine/core/io/resource_loader.cpp index 952a7e93cc..b6abee964d 100644 --- a/engine/core/io/resource_loader.cpp +++ b/engine/core/io/resource_loader.cpp @@ -30,11 +30,15 @@ #include "resource_loader.h" +#include "core/config/engine.h" #include "core/config/project_settings.h" #include "core/core_bind.h" #include "core/io/dir_access.h" #include "core/io/file_access.h" #include "core/io/resource_importer.h" +#include "core/object/callable_mp.h" +#include "core/object/class_db.h" +#include "core/object/message_queue.h" #include "core/object/script_language.h" #include "core/os/condition_variable.h" #include "core/os/os.h" @@ -218,26 +222,6 @@ void ResourceFormatLoader::_bind_methods() { GDVIRTUAL_BIND(_load, "path", "original_path", "use_sub_threads", "cache_mode"); } -/////////////////////////////////// - -// These are used before and after a wait for a WorkerThreadPool task -// because that can lead to another load started in the same thread, -// something we must treat as a different stack for the purposes -// of tracking nesting. - -#define PREPARE_FOR_WTP_WAIT \ - int load_nesting_backup = ResourceLoader::load_nesting; \ - Vector load_paths_stack_backup = ResourceLoader::load_paths_stack; \ - ResourceLoader::load_nesting = 0; \ - ResourceLoader::load_paths_stack.clear(); - -#define RESTORE_AFTER_WTP_WAIT \ - DEV_ASSERT(ResourceLoader::load_nesting == 0); \ - DEV_ASSERT(ResourceLoader::load_paths_stack.is_empty()); \ - ResourceLoader::load_nesting = load_nesting_backup; \ - ResourceLoader::load_paths_stack = load_paths_stack_backup; \ - load_paths_stack_backup.clear(); - // This should be robust enough to be called redundantly without issues. void ResourceLoader::LoadToken::clear() { WorkerThreadPool::TaskID task_to_await = 0; @@ -276,9 +260,11 @@ void ResourceLoader::LoadToken::clear() { // If task is unused, await it here, locally, now the token data is consistent. if (task_to_await) { - PREPARE_FOR_WTP_WAIT + int load_nesting_backup = load_nesting; + load_nesting = 0; WorkerThreadPool::get_singleton()->wait_for_task_completion(task_to_await); - RESTORE_AFTER_WTP_WAIT + DEV_ASSERT(load_nesting == 0); + load_nesting = load_nesting_backup; } } @@ -289,17 +275,6 @@ ResourceLoader::LoadToken::~LoadToken() { Ref ResourceLoader::_load(const String &p_path, const String &p_original_path, const String &p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, Error *r_error, bool p_use_sub_threads, float *r_progress) { const String &original_path = p_original_path.is_empty() ? p_path : p_original_path; load_nesting++; - if (load_paths_stack.size()) { - MutexLock thread_load_lock(thread_load_mutex); - const String &parent_task_path = load_paths_stack.get(load_paths_stack.size() - 1); - HashMap::Iterator E = thread_load_tasks.find(parent_task_path); - // Avoid double-tracking, for progress reporting, resources that boil down to a remapped path containing the real payload (e.g., imported resources). - bool is_remapped_load = original_path == parent_task_path; - if (E && !is_remapped_load) { - E->value.sub_tasks.insert(original_path); - } - } - load_paths_stack.push_back(original_path); print_verbose(vformat("Loading resource: %s", p_path)); @@ -317,7 +292,6 @@ Ref ResourceLoader::_load(const String &p_path, const String &p_origin } } - load_paths_stack.resize(load_paths_stack.size() - 1); res_ref_overrides.erase(load_nesting); load_nesting--; @@ -377,7 +351,6 @@ void ResourceLoader::_run_load_task(void *p_userdata) { // Thread-safe either if it's the current thread or a brand new one. CallQueue *own_mq_override = nullptr; if (load_nesting == 0) { - DEV_ASSERT(load_paths_stack.is_empty()); if (!Thread::is_main_thread()) { // Let the caller thread use its own, for added flexibility. Provide one otherwise. if (MessageQueue::get_singleton() == MessageQueue::get_main_singleton()) { @@ -496,7 +469,6 @@ void ResourceLoader::_run_load_task(void *p_userdata) { MessageQueue::set_thread_singleton_override(nullptr); memdelete(own_mq_override); } - DEV_ASSERT(load_paths_stack.is_empty()); } curr_load_task = curr_load_task_backup; @@ -626,6 +598,12 @@ Ref ResourceLoader::_load_start(const String &p_path, } } + // Task hierarchy + if (curr_load_task) { + load_task.parent_task = curr_load_task; + curr_load_task->sub_tasks.insert(load_task.local_path); + } + // If we want to ignore cache, but there's another task loading it, we can't add this one to the map. must_not_register = ignoring_cache && thread_load_tasks.has(local_path); if (must_not_register) { @@ -815,6 +793,22 @@ void ResourceLoader::set_is_import_thread(bool p_import_thread) { import_thread = p_import_thread; } +void ResourceLoader::notify_load_error(const String &p_err) { + if (err_notify) { + MessageQueue::get_main_singleton()->push_callable(callable_mp_static(err_notify).bind(p_err)); + } +} + +void ResourceLoader::notify_dependency_error(const String &p_path, const String &p_dependency, const String &p_type) { + if (dep_err_notify) { + if (Thread::get_caller_id() == Thread::get_main_id()) { + dep_err_notify(p_path, p_dependency, p_type); + } else { + MessageQueue::get_main_singleton()->push_callable(callable_mp_static(dep_err_notify).bind(p_path, p_dependency, p_type)); + } + } +} + Ref ResourceLoader::_load_complete_inner(LoadToken &p_load_token, Error *r_error, MutexLock> &p_thread_load_lock) { if (r_error) { *r_error = OK; @@ -851,9 +845,11 @@ Ref ResourceLoader::_load_complete_inner(LoadToken &p_load_token, Erro // Loading thread is in the worker pool. p_thread_load_lock.temp_unlock(); - PREPARE_FOR_WTP_WAIT + int load_nesting_backup = load_nesting; + load_nesting = 0; Error wait_err = WorkerThreadPool::get_singleton()->wait_for_task_completion(load_task.task_id); - RESTORE_AFTER_WTP_WAIT + DEV_ASSERT(load_nesting == 0); + load_nesting = load_nesting_backup; DEV_ASSERT(!wait_err || wait_err == ERR_BUSY); if (wait_err == ERR_BUSY) { @@ -904,21 +900,21 @@ Ref ResourceLoader::_load_complete_inner(LoadToken &p_load_token, Erro load_task_ptr = &load_task; } - p_thread_load_lock.temp_unlock(); - Ref resource = load_task_ptr->resource; if (r_error) { *r_error = load_task_ptr->error; } if (resource.is_valid()) { - if (curr_load_task) { + if (load_task_ptr->parent_task) { // A task awaiting another => Let the awaiter accumulate the resource changed connections. - DEV_ASSERT(curr_load_task != load_task_ptr); + DEV_ASSERT(load_task_ptr->parent_task != load_task_ptr); for (const ThreadLoadTask::ResourceChangedConnection &rcc : load_task_ptr->resource_changed_connections) { - curr_load_task->resource_changed_connections.push_back(rcc); + load_task_ptr->parent_task->resource_changed_connections.push_back(rcc); } } else { + p_thread_load_lock.temp_unlock(); + // A leaf task being awaited => Propagate the resource changed connections. if (Thread::is_main_thread()) { // On the main thread it's safe to migrate the connections to the standard signal mechanism. @@ -942,11 +938,11 @@ Ref ResourceLoader::_load_complete_inner(LoadToken &p_load_token, Erro } } } + + p_thread_load_lock.temp_relock(); } } - p_thread_load_lock.temp_relock(); - return resource; } @@ -1568,7 +1564,6 @@ bool ResourceLoader::timestamp_on_load = false; thread_local bool ResourceLoader::import_thread = false; thread_local int ResourceLoader::load_nesting = 0; -thread_local Vector ResourceLoader::load_paths_stack; thread_local HashMap>> ResourceLoader::res_ref_overrides; thread_local ResourceLoader::ThreadLoadTask *ResourceLoader::curr_load_task = nullptr; diff --git a/engine/core/io/resource_loader.h b/engine/core/io/resource_loader.h index 33b79b5a80..ef6ab41415 100644 --- a/engine/core/io/resource_loader.h +++ b/engine/core/io/resource_loader.h @@ -31,7 +31,7 @@ #pragma once #include "core/io/resource.h" -#include "core/object/gdvirtual.gen.inc" +#include "core/object/gdvirtual.gen.h" #include "core/object/worker_thread_pool.h" #include "core/os/thread.h" @@ -188,6 +188,7 @@ private: ResourceFormatLoader::CacheMode cache_mode = ResourceFormatLoader::CACHE_MODE_REUSE; Error error = OK; Ref resource; + ThreadLoadTask *parent_task = nullptr; HashSet sub_tasks; bool awaited : 1; // If it's in the pool, this helps not awaiting from more than one dependent thread. @@ -213,7 +214,6 @@ private: static thread_local bool import_thread; static thread_local int load_nesting; static thread_local HashMap>> res_ref_overrides; // Outermost key is nesting level. - static thread_local Vector load_paths_stack; static thread_local ThreadLoadTask *curr_load_task; static SafeBinaryMutex thread_load_mutex; @@ -264,25 +264,15 @@ public: static bool get_timestamp_on_load() { return timestamp_on_load; } // Loaders can safely use this regardless which thread they are running on. - static void notify_load_error(const String &p_err) { - if (err_notify) { - MessageQueue::get_main_singleton()->push_callable(callable_mp_static(err_notify).bind(p_err)); - } - } + static void notify_load_error(const String &p_err); + static void set_error_notify_func(ResourceLoadErrorNotify p_err_notify) { err_notify = p_err_notify; } // Loaders can safely use this regardless which thread they are running on. - static void notify_dependency_error(const String &p_path, const String &p_dependency, const String &p_type) { - if (dep_err_notify) { - if (Thread::get_caller_id() == Thread::get_main_id()) { - dep_err_notify(p_path, p_dependency, p_type); - } else { - MessageQueue::get_main_singleton()->push_callable(callable_mp_static(dep_err_notify).bind(p_path, p_dependency, p_type)); - } - } - } + static void notify_dependency_error(const String &p_path, const String &p_dependency, const String &p_type); + static void set_dependency_error_notify_func(DependencyErrorNotify p_err_notify) { dep_err_notify = p_err_notify; } diff --git a/engine/core/io/resource_saver.cpp b/engine/core/io/resource_saver.cpp index be58564a1f..3cc7fc2ac8 100644 --- a/engine/core/io/resource_saver.cpp +++ b/engine/core/io/resource_saver.cpp @@ -29,9 +29,11 @@ /**************************************************************************/ #include "resource_saver.h" + #include "core/config/project_settings.h" #include "core/io/file_access.h" #include "core/io/resource_loader.h" +#include "core/object/class_db.h" #include "core/object/script_language.h" Ref ResourceSaver::saver[MAX_SAVERS]; diff --git a/engine/core/io/resource_saver.h b/engine/core/io/resource_saver.h index 0fc660c1a0..9a4a35873e 100644 --- a/engine/core/io/resource_saver.h +++ b/engine/core/io/resource_saver.h @@ -31,7 +31,7 @@ #pragma once #include "core/io/resource.h" -#include "core/object/gdvirtual.gen.inc" +#include "core/object/gdvirtual.gen.h" class ResourceFormatSaver : public RefCounted { GDCLASS(ResourceFormatSaver, RefCounted); diff --git a/engine/core/io/resource_uid.cpp b/engine/core/io/resource_uid.cpp index 5a0f1f723e..09fff07a2e 100644 --- a/engine/core/io/resource_uid.cpp +++ b/engine/core/io/resource_uid.cpp @@ -36,6 +36,7 @@ #include "core/io/file_access.h" #include "core/io/resource_loader.h" #include "core/math/random_pcg.h" +#include "core/object/class_db.h" // These constants are off by 1, causing the 'z' and '9' characters never to be used. // This cannot be fixed without breaking compatibility; see GH-83843. @@ -357,18 +358,21 @@ String ResourceUID::get_path_from_cache(Ref &p_cache_file, const Str const int64_t uid_from_string = singleton->text_to_id(p_uid_string); if (uid_from_string != INVALID_ID) { const uint32_t entry_count = p_cache_file->get_32(); - CharString cs; for (uint32_t i = 0; i < entry_count; i++) { int64_t id = p_cache_file->get_64(); int32_t len = p_cache_file->get_32(); - cs.resize_uninitialized(len + 1); - ERR_FAIL_COND_V(cs.size() != len + 1, String()); - cs[len] = 0; - int32_t rl = p_cache_file->get_buffer((uint8_t *)cs.ptrw(), len); - ERR_FAIL_COND_V(rl != len, String()); if (id == uid_from_string) { + CharString cs; + cs.resize_uninitialized(len + 1); + ERR_FAIL_COND_V(cs.size() != len + 1, String()); + cs[len] = 0; + int32_t rl = p_cache_file->get_buffer((uint8_t *)cs.ptrw(), len); + ERR_FAIL_COND_V(rl != len, String()); return String::utf8(cs.get_data()); + } else { + p_cache_file->seek(p_cache_file->get_position() + len); + ERR_FAIL_COND_V(p_cache_file->eof_reached(), String()); } } } diff --git a/engine/core/io/resource_uid.h b/engine/core/io/resource_uid.h index 3832d25d91..618f548b1e 100644 --- a/engine/core/io/resource_uid.h +++ b/engine/core/io/resource_uid.h @@ -30,8 +30,8 @@ #pragma once -#include "core/object/ref_counted.h" -#include "core/string/string_name.h" +#include "core/object/object.h" +#include "core/string/ustring.h" #include "core/templates/hash_map.h" class FileAccess; diff --git a/engine/core/io/socket_server.cpp b/engine/core/io/socket_server.cpp index 6929f675c9..31abc2c868 100644 --- a/engine/core/io/socket_server.cpp +++ b/engine/core/io/socket_server.cpp @@ -30,6 +30,8 @@ #include "socket_server.h" +#include "core/object/class_db.h" + void SocketServer::_bind_methods() { ClassDB::bind_method(D_METHOD("is_connection_available"), &SocketServer::is_connection_available); ClassDB::bind_method(D_METHOD("is_listening"), &SocketServer::is_listening); diff --git a/engine/core/io/stream_peer.cpp b/engine/core/io/stream_peer.cpp index 2f2c1d3086..3ab5a28463 100644 --- a/engine/core/io/stream_peer.cpp +++ b/engine/core/io/stream_peer.cpp @@ -31,6 +31,7 @@ #include "stream_peer.h" #include "core/io/marshalls.h" +#include "core/object/class_db.h" Error StreamPeer::_put_data(const Vector &p_data) { int len = p_data.size(); diff --git a/engine/core/io/stream_peer.h b/engine/core/io/stream_peer.h index 7efebcfccf..af503b7d0a 100644 --- a/engine/core/io/stream_peer.h +++ b/engine/core/io/stream_peer.h @@ -32,8 +32,8 @@ #include "core/object/ref_counted.h" -#include "core/extension/ext_wrappers.gen.inc" -#include "core/object/gdvirtual.gen.inc" +#include "core/extension/ext_wrappers.gen.h" +#include "core/object/gdvirtual.gen.h" #include "core/variant/native_ptr.h" class StreamPeer : public RefCounted { diff --git a/engine/core/io/stream_peer_gzip.cpp b/engine/core/io/stream_peer_gzip.cpp index bfd6b7bd65..52c920dad4 100644 --- a/engine/core/io/stream_peer_gzip.cpp +++ b/engine/core/io/stream_peer_gzip.cpp @@ -31,6 +31,8 @@ #include "core/io/stream_peer_gzip.h" #include "core/io/zip_io.h" +#include "core/object/class_db.h" + #include void StreamPeerGZIP::_bind_methods() { @@ -76,7 +78,7 @@ Error StreamPeerGZIP::_start(bool p_compress, bool p_is_deflate, int buffer_size ERR_FAIL_COND_V_MSG(buffer_size <= 0, ERR_INVALID_PARAMETER, "Invalid buffer size. It should be a positive integer."); clear(); compressing = p_compress; - rb.resize(nearest_shift(uint32_t(buffer_size - 1))); + rb.resize(Math::nearest_shift(uint32_t(buffer_size - 1))); buffer.resize(1024); // Create ctx. diff --git a/engine/core/io/stream_peer_gzip.h b/engine/core/io/stream_peer_gzip.h index e712253c20..4d5a84f879 100644 --- a/engine/core/io/stream_peer_gzip.h +++ b/engine/core/io/stream_peer_gzip.h @@ -32,8 +32,6 @@ #include "core/io/stream_peer.h" -#include "core/core_bind.h" -#include "core/io/compression.h" #include "core/templates/ring_buffer.h" class StreamPeerGZIP : public StreamPeer { diff --git a/engine/core/io/stream_peer_socket.compat.inc b/engine/core/io/stream_peer_socket.compat.inc index 65bd92f107..e7f6ee1117 100644 --- a/engine/core/io/stream_peer_socket.compat.inc +++ b/engine/core/io/stream_peer_socket.compat.inc @@ -30,6 +30,10 @@ #ifndef DISABLE_DEPRECATED +#include "stream_peer_socket.h" + +#include "core/object/class_db.h" + namespace compat::StreamPeerTCP { enum class Status { STATUS_NONE = StreamPeerSocket::STATUS_NONE, @@ -49,4 +53,4 @@ void StreamPeerSocket::_bind_compatibility_methods() { ClassDB::bind_compatibility_method(D_METHOD("get_status"), &StreamPeerSocket::_get_status_compat_107954); } -#endif +#endif // DISABLE_DEPRECATED diff --git a/engine/core/io/stream_peer_socket.cpp b/engine/core/io/stream_peer_socket.cpp index a60ea118ed..1fa0b3ecfa 100644 --- a/engine/core/io/stream_peer_socket.cpp +++ b/engine/core/io/stream_peer_socket.cpp @@ -31,6 +31,9 @@ #include "stream_peer_socket.h" #include "stream_peer_socket.compat.inc" +#include "core/object/class_db.h" +#include "core/os/os.h" + Error StreamPeerSocket::poll() { if (status == STATUS_CONNECTED) { Error err; diff --git a/engine/core/io/stream_peer_tcp.cpp b/engine/core/io/stream_peer_tcp.cpp index d5b5f34cc7..e3b6dfb281 100644 --- a/engine/core/io/stream_peer_tcp.cpp +++ b/engine/core/io/stream_peer_tcp.cpp @@ -31,6 +31,8 @@ #include "stream_peer_tcp.h" #include "core/config/project_settings.h" +#include "core/object/class_db.h" +#include "core/os/os.h" void StreamPeerTCP::accept_socket(Ref p_sock, const NetSocket::Address &p_addr) { _sock = p_sock; diff --git a/engine/core/io/stream_peer_tcp.h b/engine/core/io/stream_peer_tcp.h index e472a46cf0..62f0f99e17 100644 --- a/engine/core/io/stream_peer_tcp.h +++ b/engine/core/io/stream_peer_tcp.h @@ -30,7 +30,6 @@ #pragma once -#include "core/io/ip.h" #include "core/io/ip_address.h" #include "core/io/stream_peer_socket.h" diff --git a/engine/core/io/stream_peer_tls.cpp b/engine/core/io/stream_peer_tls.cpp index f04e217a26..3eb51c9b36 100644 --- a/engine/core/io/stream_peer_tls.cpp +++ b/engine/core/io/stream_peer_tls.cpp @@ -30,7 +30,7 @@ #include "stream_peer_tls.h" -#include "core/config/engine.h" +#include "core/object/class_db.h" StreamPeerTLS *(*StreamPeerTLS::_create)(bool p_notify_postinitialize) = nullptr; diff --git a/engine/core/io/stream_peer_uds.cpp b/engine/core/io/stream_peer_uds.cpp index 70e3220ded..ec70a78dce 100644 --- a/engine/core/io/stream_peer_uds.cpp +++ b/engine/core/io/stream_peer_uds.cpp @@ -31,6 +31,8 @@ #include "stream_peer_uds.h" #include "core/config/project_settings.h" +#include "core/object/class_db.h" +#include "core/os/os.h" void StreamPeerUDS::_bind_methods() { ClassDB::bind_method(D_METHOD("bind", "path"), &StreamPeerUDS::bind); diff --git a/engine/core/io/tcp_server.cpp b/engine/core/io/tcp_server.cpp index e8728bd946..cf0943effe 100644 --- a/engine/core/io/tcp_server.cpp +++ b/engine/core/io/tcp_server.cpp @@ -30,6 +30,8 @@ #include "tcp_server.h" +#include "core/object/class_db.h" + void TCPServer::_bind_methods() { ClassDB::bind_method(D_METHOD("listen", "port", "bind_address"), &TCPServer::listen, DEFVAL("*")); ClassDB::bind_method(D_METHOD("get_local_port"), &TCPServer::get_local_port); diff --git a/engine/core/io/tcp_server.h b/engine/core/io/tcp_server.h index 4e3248a846..c1967b92e0 100644 --- a/engine/core/io/tcp_server.h +++ b/engine/core/io/tcp_server.h @@ -30,7 +30,7 @@ #pragma once -#include "core/io/ip.h" +#include "core/io/ip_address.h" #include "core/io/socket_server.h" #include "core/io/stream_peer_tcp.h" diff --git a/engine/core/io/udp_server.cpp b/engine/core/io/udp_server.cpp index ada0e09a48..a2f1fc0778 100644 --- a/engine/core/io/udp_server.cpp +++ b/engine/core/io/udp_server.cpp @@ -30,6 +30,8 @@ #include "udp_server.h" +#include "core/object/class_db.h" + void UDPServer::_bind_methods() { ClassDB::bind_method(D_METHOD("listen", "port", "bind_address"), &UDPServer::listen, DEFVAL("*")); ClassDB::bind_method(D_METHOD("poll"), &UDPServer::poll); diff --git a/engine/core/io/uds_server.cpp b/engine/core/io/uds_server.cpp index e93262a01e..8249c80f52 100644 --- a/engine/core/io/uds_server.cpp +++ b/engine/core/io/uds_server.cpp @@ -30,6 +30,8 @@ #include "uds_server.h" +#include "core/object/class_db.h" + void UDSServer::_bind_methods() { ClassDB::bind_method(D_METHOD("listen", "path"), &UDSServer::listen); ClassDB::bind_method(D_METHOD("take_connection"), &UDSServer::take_connection); diff --git a/engine/core/io/xml_parser.cpp b/engine/core/io/xml_parser.cpp index a50a17231c..40f0e48ef8 100644 --- a/engine/core/io/xml_parser.cpp +++ b/engine/core/io/xml_parser.cpp @@ -31,6 +31,7 @@ #include "xml_parser.h" #include "core/io/file_access.h" +#include "core/object/class_db.h" //#define DEBUG_XML diff --git a/engine/core/io/xml_parser.h b/engine/core/io/xml_parser.h index 911d2b11b2..01d763cda7 100644 --- a/engine/core/io/xml_parser.h +++ b/engine/core/io/xml_parser.h @@ -33,6 +33,7 @@ #include "core/object/ref_counted.h" #include "core/string/ustring.h" #include "core/templates/vector.h" +#include "core/variant/type_info.h" /* Based on irrXML (see their zlib license). Added mainly for compatibility with their Collada loader. diff --git a/engine/core/io/zip_io.h b/engine/core/io/zip_io.h index 82004d9ca3..f4c00f2c03 100644 --- a/engine/core/io/zip_io.h +++ b/engine/core/io/zip_io.h @@ -32,11 +32,9 @@ #include "core/io/file_access.h" -// Not directly used in this header, but assumed available in downstream users -// like platform/*/export/export.cpp. Could be fixed, but probably better to have -// thirdparty includes in as little headers as possible. -#include "thirdparty/minizip/unzip.h" -#include "thirdparty/minizip/zip.h" +// This file serves as the Godot interface to minizip. +#include "thirdparty/minizip/unzip.h" // IWYU pragma: export +#include "thirdparty/minizip/zip.h" // IWYU pragma: export // Get the current file info and safely convert the full filepath to a String. int godot_unzip_get_current_file_info(unzFile p_zip_file, unz_file_info64 &r_file_info, String &r_filepath); diff --git a/engine/core/math/a_star.compat.inc b/engine/core/math/a_star.compat.inc index 664d7ffd5e..b2077d020c 100644 --- a/engine/core/math/a_star.compat.inc +++ b/engine/core/math/a_star.compat.inc @@ -30,6 +30,10 @@ #ifndef DISABLE_DEPRECATED +#include "a_star.h" + +#include "core/object/class_db.h" + Vector AStar3D::_get_id_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id) { return get_id_path(p_from_id, p_to_id, false); } diff --git a/engine/core/math/a_star.cpp b/engine/core/math/a_star.cpp index df0db9a184..bbd5be8694 100644 --- a/engine/core/math/a_star.cpp +++ b/engine/core/math/a_star.cpp @@ -32,6 +32,7 @@ #include "a_star.compat.inc" #include "core/math/geometry_3d.h" +#include "core/object/class_db.h" int64_t AStar3D::get_available_point_id() const { if (points.has(last_free_id)) { diff --git a/engine/core/math/a_star.h b/engine/core/math/a_star.h index 048ead784c..8e643f4555 100644 --- a/engine/core/math/a_star.h +++ b/engine/core/math/a_star.h @@ -30,7 +30,7 @@ #pragma once -#include "core/object/gdvirtual.gen.inc" +#include "core/object/gdvirtual.gen.h" #include "core/object/ref_counted.h" #include "core/templates/a_hash_map.h" diff --git a/engine/core/math/a_star_grid_2d.compat.inc b/engine/core/math/a_star_grid_2d.compat.inc index e7124c2477..5871d0b087 100644 --- a/engine/core/math/a_star_grid_2d.compat.inc +++ b/engine/core/math/a_star_grid_2d.compat.inc @@ -30,6 +30,9 @@ #ifndef DISABLE_DEPRECATED +#include "a_star_grid_2d.h" + +#include "core/object/class_db.h" #include "core/variant/typed_array.h" TypedArray AStarGrid2D::_get_id_path_bind_compat_88047(const Vector2i &p_from_id, const Vector2i &p_to_id) { diff --git a/engine/core/math/a_star_grid_2d.cpp b/engine/core/math/a_star_grid_2d.cpp index 9a6e804a9a..4f6d605153 100644 --- a/engine/core/math/a_star_grid_2d.cpp +++ b/engine/core/math/a_star_grid_2d.cpp @@ -31,6 +31,7 @@ #include "a_star_grid_2d.h" #include "a_star_grid_2d.compat.inc" +#include "core/object/class_db.h" #include "core/variant/typed_array.h" static real_t heuristic_euclidean(const Vector2i &p_from, const Vector2i &p_to) { @@ -158,7 +159,7 @@ void AStarGrid2D::update() { solid_mask.push_back(false); } solid_mask.push_back(true); - points.push_back(line); + points.push_back(std::move(line)); } for (int32_t x = region.position.x; x < end_x + 2; x++) { diff --git a/engine/core/math/a_star_grid_2d.h b/engine/core/math/a_star_grid_2d.h index 4883c09206..14f8ccc8b9 100644 --- a/engine/core/math/a_star_grid_2d.h +++ b/engine/core/math/a_star_grid_2d.h @@ -30,7 +30,7 @@ #pragma once -#include "core/object/gdvirtual.gen.inc" +#include "core/object/gdvirtual.gen.h" #include "core/object/ref_counted.h" #include "core/templates/local_vector.h" diff --git a/engine/core/math/basis.cpp b/engine/core/math/basis.cpp index 5931ecb257..48bba25dc2 100644 --- a/engine/core/math/basis.cpp +++ b/engine/core/math/basis.cpp @@ -54,7 +54,8 @@ void Basis::invert() { } void Basis::orthonormalize() { - // Gram-Schmidt Process + // Orthonormalizable check is done in Vector3 class below. + // Gram-Schmidt Process: Vector3 x = get_column(0); Vector3 y = get_column(1); diff --git a/engine/core/math/bvh.h b/engine/core/math/bvh.h index 5f0bf23f28..03a358f118 100644 --- a/engine/core/math/bvh.h +++ b/engine/core/math/bvh.h @@ -55,6 +55,8 @@ #include "core/math/geometry_3d.h" #include "core/os/mutex.h" +#include // INT_MAX + #define BVHTREE_CLASS BVH_Tree #define BVH_LOCKED_FUNCTION BVHLockedFunction _lock_guard(&_mutex, BVH_THREAD_SAFE &&_thread_safe); diff --git a/engine/core/math/bvh_abb.h b/engine/core/math/bvh_abb.h index b96ea0859a..cfb5ebd43e 100644 --- a/engine/core/math/bvh_abb.h +++ b/engine/core/math/bvh_abb.h @@ -32,6 +32,8 @@ #include "core/math/aabb.h" +#include // FLT_MAX + // special optimized version of axis aligned bounding box template struct BVH_ABB { diff --git a/engine/core/math/bvh_tree.h b/engine/core/math/bvh_tree.h index d33b8608ec..86daaab5ce 100644 --- a/engine/core/math/bvh_tree.h +++ b/engine/core/math/bvh_tree.h @@ -44,8 +44,6 @@ #include "core/templates/local_vector.h" #include "core/templates/pooled_list.h" -#include - #define BVHABB_CLASS BVH_ABB // not sure if this is better yet so making optional diff --git a/engine/core/math/convex_hull.cpp b/engine/core/math/convex_hull.cpp index 87f0652cc1..e72515c99c 100644 --- a/engine/core/math/convex_hull.cpp +++ b/engine/core/math/convex_hull.cpp @@ -64,6 +64,8 @@ subject to the following restrictions: #include "core/templates/a_hash_map.h" #include "core/templates/paged_allocator.h" +#include // FLT_MAX + //#define DEBUG_CONVEX_HULL //#define SHOW_ITERATIONS @@ -73,17 +75,17 @@ subject to the following restrictions: // -- GODOT end -- #ifdef DEBUG_ENABLED -#define CHULL_ASSERT(m_cond) \ - if constexpr (true) { \ - if (unlikely(!(m_cond))) { \ +#define CHULL_ASSERT(m_cond) \ + if constexpr (true) { \ + if (unlikely(!(m_cond))) { \ ERR_PRINT("Assertion \"" _STR(m_cond) "\" failed."); \ - } \ - } else \ + } \ + } else \ ((void)0) #else #define CHULL_ASSERT(m_cond) \ - if constexpr (true) { \ - } else \ + if constexpr (true) { \ + } else \ ((void)0) #endif diff --git a/engine/core/math/face3.cpp b/engine/core/math/face3.cpp index 5972f429c7..6a21056093 100644 --- a/engine/core/math/face3.cpp +++ b/engine/core/math/face3.cpp @@ -144,22 +144,22 @@ bool Face3::intersects_aabb(const AABB &p_aabb) const { return false; } -#define TEST_AXIS(m_ax) \ - /** TEST FACE AXIS */ \ - { \ - real_t aabb_min = p_aabb.position.m_ax; \ +#define TEST_AXIS(m_ax) \ + /** TEST FACE AXIS */ \ + { \ + real_t aabb_min = p_aabb.position.m_ax; \ real_t aabb_max = p_aabb.position.m_ax + p_aabb.size.m_ax; \ - real_t tri_min = vertex[0].m_ax; \ - real_t tri_max = vertex[0].m_ax; \ - for (int i = 1; i < 3; i++) { \ - if (vertex[i].m_ax > tri_max) \ - tri_max = vertex[i].m_ax; \ - if (vertex[i].m_ax < tri_min) \ - tri_min = vertex[i].m_ax; \ - } \ - \ - if (tri_max < aabb_min || aabb_max < tri_min) \ - return false; \ + real_t tri_min = vertex[0].m_ax; \ + real_t tri_max = vertex[0].m_ax; \ + for (int i = 1; i < 3; i++) { \ + if (vertex[i].m_ax > tri_max) \ + tri_max = vertex[i].m_ax; \ + if (vertex[i].m_ax < tri_min) \ + tri_min = vertex[i].m_ax; \ + } \ +\ + if (tri_max < aabb_min || aabb_max < tri_min) \ + return false; \ } TEST_AXIS(x); diff --git a/engine/core/math/face3.h b/engine/core/math/face3.h index 96e4584c22..0ae5efc4a0 100644 --- a/engine/core/math/face3.h +++ b/engine/core/math/face3.h @@ -102,20 +102,20 @@ bool Face3::intersects_aabb2(const AABB &p_aabb) const { return false; //does not intersect the plane } -#define TEST_AXIS(m_ax) \ - { \ - real_t aabb_min = p_aabb.position.m_ax; \ +#define TEST_AXIS(m_ax) \ + { \ + real_t aabb_min = p_aabb.position.m_ax; \ real_t aabb_max = p_aabb.position.m_ax + p_aabb.size.m_ax; \ - real_t tri_min, tri_max; \ - for (int i = 0; i < 3; i++) { \ - if (i == 0 || vertex[i].m_ax > tri_max) \ - tri_max = vertex[i].m_ax; \ - if (i == 0 || vertex[i].m_ax < tri_min) \ - tri_min = vertex[i].m_ax; \ - } \ - \ - if (tri_max < aabb_min || aabb_max < tri_min) \ - return false; \ + real_t tri_min, tri_max; \ + for (int i = 0; i < 3; i++) { \ + if (i == 0 || vertex[i].m_ax > tri_max) \ + tri_max = vertex[i].m_ax; \ + if (i == 0 || vertex[i].m_ax < tri_min) \ + tri_min = vertex[i].m_ax; \ + } \ +\ + if (tri_max < aabb_min || aabb_max < tri_min) \ + return false; \ } TEST_AXIS(x); diff --git a/engine/core/math/geometry_2d.cpp b/engine/core/math/geometry_2d.cpp index f6d0ed74ee..7b6a3a1828 100644 --- a/engine/core/math/geometry_2d.cpp +++ b/engine/core/math/geometry_2d.cpp @@ -30,6 +30,8 @@ #include "geometry_2d.h" +#include "core/math/math_funcs_binary.h" + GODOT_GCC_WARNING_PUSH_AND_IGNORE("-Walloc-zero") #include "thirdparty/clipper2/include/clipper2/clipper.h" GODOT_GCC_WARNING_POP @@ -227,8 +229,8 @@ void Geometry2D::make_atlas(const Vector &p_rects, Vector &r_re real_t best_aspect = 1e20; for (int i = 0; i < results.size(); i++) { - real_t h = next_power_of_2((uint32_t)results[i].max_h); - real_t w = next_power_of_2((uint32_t)results[i].max_w); + real_t h = Math::next_power_of_2((uint32_t)results[i].max_h); + real_t w = Math::next_power_of_2((uint32_t)results[i].max_w); real_t aspect = h > w ? h / w : w / h; if (aspect < best_aspect) { best = i; diff --git a/engine/core/math/geometry_3d.cpp b/engine/core/math/geometry_3d.cpp index 15799cdb45..987e0c2399 100644 --- a/engine/core/math/geometry_3d.cpp +++ b/engine/core/math/geometry_3d.cpp @@ -237,15 +237,15 @@ static inline void _plot_face(uint8_t ***p_cell_status, int x, int y, int z, int int div_z = len_z > 1 ? 2 : 1; #define SPLIT_DIV(m_i, m_div, m_v, m_len_v, m_new_v, m_new_len_v) \ - if (m_div == 1) { \ - m_new_v = m_v; \ - m_new_len_v = 1; \ - } else if (m_i == 0) { \ - m_new_v = m_v; \ - m_new_len_v = m_len_v / 2; \ - } else { \ - m_new_v = m_v + m_len_v / 2; \ - m_new_len_v = m_len_v - m_len_v / 2; \ + if (m_div == 1) { \ + m_new_v = m_v; \ + m_new_len_v = 1; \ + } else if (m_i == 0) { \ + m_new_v = m_v; \ + m_new_len_v = m_len_v / 2; \ + } else { \ + m_new_v = m_v + m_len_v / 2; \ + m_new_len_v = m_len_v - m_len_v / 2; \ } int new_x; diff --git a/engine/core/math/geometry_3d.h b/engine/core/math/geometry_3d.h index 6af902b28b..ec98bd9f24 100644 --- a/engine/core/math/geometry_3d.h +++ b/engine/core/math/geometry_3d.h @@ -37,828 +37,827 @@ #include "core/templates/local_vector.h" #include "core/templates/vector.h" -class Geometry3D { -public: - static void get_closest_points_between_segments(const Vector3 &p_p0, const Vector3 &p_p1, const Vector3 &p_q0, const Vector3 &p_q1, Vector3 &r_ps, Vector3 &r_qt); - static real_t get_closest_distance_between_segments(const Vector3 &p_p0, const Vector3 &p_p1, const Vector3 &p_q0, const Vector3 &p_q1); +namespace Geometry3D { +void get_closest_points_between_segments(const Vector3 &p_p0, const Vector3 &p_p1, const Vector3 &p_q0, const Vector3 &p_q1, Vector3 &r_ps, Vector3 &r_qt); +real_t get_closest_distance_between_segments(const Vector3 &p_p0, const Vector3 &p_p1, const Vector3 &p_q0, const Vector3 &p_q1); - static inline bool ray_intersects_triangle(const Vector3 &p_from, const Vector3 &p_dir, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2, Vector3 *r_res = nullptr) { - Vector3 e1 = p_v1 - p_v0; - Vector3 e2 = p_v2 - p_v0; - Vector3 h = p_dir.cross(e2); - real_t a = e1.dot(h); - if (Math::is_zero_approx(a)) { // Parallel test. - return false; - } - - real_t f = 1.0f / a; - - Vector3 s = p_from - p_v0; - real_t u = f * s.dot(h); - - if ((u < 0.0f) || (u > 1.0f)) { - return false; - } - - Vector3 q = s.cross(e1); - - real_t v = f * p_dir.dot(q); - - if ((v < 0.0f) || (u + v > 1.0f)) { - return false; - } - - // At this stage we can compute t to find out where - // the intersection point is on the line. - real_t t = f * e2.dot(q); - - if (t > 0.00001f) { // ray intersection - if (r_res) { - *r_res = p_from + p_dir * t; - } - return true; - } else { // This means that there is a line intersection but not a ray intersection. - return false; - } +inline bool ray_intersects_triangle(const Vector3 &p_from, const Vector3 &p_dir, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2, Vector3 *r_res = nullptr) { + Vector3 e1 = p_v1 - p_v0; + Vector3 e2 = p_v2 - p_v0; + Vector3 h = p_dir.cross(e2); + real_t a = e1.dot(h); + if (Math::is_zero_approx(a)) { // Parallel test. + return false; } - static inline bool segment_intersects_triangle(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2, Vector3 *r_res = nullptr) { - Vector3 rel = p_to - p_from; - Vector3 e1 = p_v1 - p_v0; - Vector3 e2 = p_v2 - p_v0; - Vector3 h = rel.cross(e2); - real_t a = e1.dot(h); - if (Math::is_zero_approx(a)) { // Parallel test. - return false; - } + real_t f = 1.0f / a; - real_t f = 1.0f / a; + Vector3 s = p_from - p_v0; + real_t u = f * s.dot(h); - Vector3 s = p_from - p_v0; - real_t u = f * s.dot(h); - - if ((u < 0.0f) || (u > 1.0f)) { - return false; - } - - Vector3 q = s.cross(e1); - - real_t v = f * rel.dot(q); - - if ((v < 0.0f) || (u + v > 1.0f)) { - return false; - } - - // At this stage we can compute t to find out where - // the intersection point is on the line. - real_t t = f * e2.dot(q); - - if (t > (real_t)CMP_EPSILON && t <= 1.0f) { // Ray intersection. - if (r_res) { - *r_res = p_from + rel * t; - } - return true; - } else { // This means that there is a line intersection but not a ray intersection. - return false; - } + if ((u < 0.0f) || (u > 1.0f)) { + return false; } - static inline bool segment_intersects_sphere(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_sphere_pos, real_t p_sphere_radius, Vector3 *r_res = nullptr, Vector3 *r_norm = nullptr) { - Vector3 sphere_pos = p_sphere_pos - p_from; - Vector3 rel = (p_to - p_from); - real_t rel_l = rel.length(); - if (rel_l < (real_t)CMP_EPSILON) { - return false; // Both points are the same. - } - Vector3 normal = rel / rel_l; + Vector3 q = s.cross(e1); - real_t sphere_d = normal.dot(sphere_pos); + real_t v = f * p_dir.dot(q); - real_t ray_distance = sphere_pos.distance_to(normal * sphere_d); + if ((v < 0.0f) || (u + v > 1.0f)) { + return false; + } - if (ray_distance >= p_sphere_radius) { - return false; - } - - real_t inters_d2 = p_sphere_radius * p_sphere_radius - ray_distance * ray_distance; - real_t inters_d = sphere_d; - - if (inters_d2 >= (real_t)CMP_EPSILON) { - inters_d -= Math::sqrt(inters_d2); - } - - // Check in segment. - if (inters_d < 0 || inters_d > rel_l) { - return false; - } - - Vector3 result = p_from + normal * inters_d; + // At this stage we can compute t to find out where + // the intersection point is on the line. + real_t t = f * e2.dot(q); + if (t > 0.00001f) { // ray intersection if (r_res) { - *r_res = result; + *r_res = p_from + p_dir * t; } - if (r_norm) { - *r_norm = (result - p_sphere_pos).normalized(); - } - return true; + } else { // This means that there is a line intersection but not a ray intersection. + return false; + } +} + +inline bool segment_intersects_triangle(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2, Vector3 *r_res = nullptr) { + Vector3 rel = p_to - p_from; + Vector3 e1 = p_v1 - p_v0; + Vector3 e2 = p_v2 - p_v0; + Vector3 h = rel.cross(e2); + real_t a = e1.dot(h); + if (Math::is_zero_approx(a)) { // Parallel test. + return false; } - static inline bool segment_intersects_cylinder(const Vector3 &p_from, const Vector3 &p_to, real_t p_height, real_t p_radius, Vector3 *r_res = nullptr, Vector3 *r_norm = nullptr, int p_cylinder_axis = 2) { - Vector3 rel = (p_to - p_from); - real_t rel_l = rel.length(); - if (rel_l < (real_t)CMP_EPSILON) { - return false; // Both points are the same. + real_t f = 1.0f / a; + + Vector3 s = p_from - p_v0; + real_t u = f * s.dot(h); + + if ((u < 0.0f) || (u > 1.0f)) { + return false; + } + + Vector3 q = s.cross(e1); + + real_t v = f * rel.dot(q); + + if ((v < 0.0f) || (u + v > 1.0f)) { + return false; + } + + // At this stage we can compute t to find out where + // the intersection point is on the line. + real_t t = f * e2.dot(q); + + if (t > (real_t)CMP_EPSILON && t <= 1.0f) { // Ray intersection. + if (r_res) { + *r_res = p_from + rel * t; } + return true; + } else { // This means that there is a line intersection but not a ray intersection. + return false; + } +} - ERR_FAIL_COND_V(p_cylinder_axis < 0, false); - ERR_FAIL_COND_V(p_cylinder_axis > 2, false); - Vector3 cylinder_axis; - cylinder_axis[p_cylinder_axis] = 1.0f; +inline bool segment_intersects_sphere(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_sphere_pos, real_t p_sphere_radius, Vector3 *r_res = nullptr, Vector3 *r_norm = nullptr) { + Vector3 sphere_pos = p_sphere_pos - p_from; + Vector3 rel = (p_to - p_from); + real_t rel_l = rel.length(); + if (rel_l < (real_t)CMP_EPSILON) { + return false; // Both points are the same. + } + Vector3 normal = rel / rel_l; - // First check if they are parallel. - Vector3 normal = (rel / rel_l); - Vector3 crs = normal.cross(cylinder_axis); - real_t crs_l = crs.length(); + real_t sphere_d = normal.dot(sphere_pos); - Vector3 axis_dir; + real_t ray_distance = sphere_pos.distance_to(normal * sphere_d); - if (crs_l < (real_t)CMP_EPSILON) { - Vector3 side_axis; - side_axis[(p_cylinder_axis + 1) % 3] = 1.0f; // Any side axis OK. - axis_dir = side_axis; - } else { - axis_dir = crs / crs_l; - } + if (ray_distance >= p_sphere_radius) { + return false; + } - real_t dist = axis_dir.dot(p_from); + real_t inters_d2 = p_sphere_radius * p_sphere_radius - ray_distance * ray_distance; + real_t inters_d = sphere_d; - if (dist >= p_radius) { - return false; // Too far away. - } + if (inters_d2 >= (real_t)CMP_EPSILON) { + inters_d -= Math::sqrt(inters_d2); + } - // Convert to 2D. - real_t w2 = p_radius * p_radius - dist * dist; - if (w2 < (real_t)CMP_EPSILON) { - return false; // Avoid numerical error. - } - Size2 size(Math::sqrt(w2), p_height * 0.5f); + // Check in segment. + if (inters_d < 0 || inters_d > rel_l) { + return false; + } - Vector3 side_dir = axis_dir.cross(cylinder_axis).normalized(); + Vector3 result = p_from + normal * inters_d; - Vector2 from2D(side_dir.dot(p_from), p_from[p_cylinder_axis]); - Vector2 to2D(side_dir.dot(p_to), p_to[p_cylinder_axis]); + if (r_res) { + *r_res = result; + } + if (r_norm) { + *r_norm = (result - p_sphere_pos).normalized(); + } - real_t min = 0, max = 1; + return true; +} - int axis = -1; +inline bool segment_intersects_cylinder(const Vector3 &p_from, const Vector3 &p_to, real_t p_height, real_t p_radius, Vector3 *r_res = nullptr, Vector3 *r_norm = nullptr, int p_cylinder_axis = 2) { + Vector3 rel = (p_to - p_from); + real_t rel_l = rel.length(); + if (rel_l < (real_t)CMP_EPSILON) { + return false; // Both points are the same. + } - for (int i = 0; i < 2; i++) { - real_t seg_from = from2D[i]; - real_t seg_to = to2D[i]; - real_t box_begin = -size[i]; - real_t box_end = size[i]; - real_t cmin, cmax; + ERR_FAIL_COND_V(p_cylinder_axis < 0, false); + ERR_FAIL_COND_V(p_cylinder_axis > 2, false); + Vector3 cylinder_axis; + cylinder_axis[p_cylinder_axis] = 1.0f; - if (seg_from < seg_to) { - if (seg_from > box_end || seg_to < box_begin) { - return false; - } - real_t length = seg_to - seg_from; - cmin = (seg_from < box_begin) ? ((box_begin - seg_from) / length) : 0; - cmax = (seg_to > box_end) ? ((box_end - seg_from) / length) : 1; + // First check if they are parallel. + Vector3 normal = (rel / rel_l); + Vector3 crs = normal.cross(cylinder_axis); + real_t crs_l = crs.length(); - } else { - if (seg_to > box_end || seg_from < box_begin) { - return false; - } - real_t length = seg_to - seg_from; - cmin = (seg_from > box_end) ? (box_end - seg_from) / length : 0; - cmax = (seg_to < box_begin) ? (box_begin - seg_from) / length : 1; - } + Vector3 axis_dir; - if (cmin > min) { - min = cmin; - axis = i; - } - if (cmax < max) { - max = cmax; - } - if (max < min) { + if (crs_l < (real_t)CMP_EPSILON) { + Vector3 side_axis; + side_axis[(p_cylinder_axis + 1) % 3] = 1.0f; // Any side axis OK. + axis_dir = side_axis; + } else { + axis_dir = crs / crs_l; + } + + real_t dist = axis_dir.dot(p_from); + + if (dist >= p_radius) { + return false; // Too far away. + } + + // Convert to 2D. + real_t w2 = p_radius * p_radius - dist * dist; + if (w2 < (real_t)CMP_EPSILON) { + return false; // Avoid numerical error. + } + Size2 size(Math::sqrt(w2), p_height * 0.5f); + + Vector3 side_dir = axis_dir.cross(cylinder_axis).normalized(); + + Vector2 from2D(side_dir.dot(p_from), p_from[p_cylinder_axis]); + Vector2 to2D(side_dir.dot(p_to), p_to[p_cylinder_axis]); + + real_t min = 0, max = 1; + + int axis = -1; + + for (int i = 0; i < 2; i++) { + real_t seg_from = from2D[i]; + real_t seg_to = to2D[i]; + real_t box_begin = -size[i]; + real_t box_end = size[i]; + real_t cmin, cmax; + + if (seg_from < seg_to) { + if (seg_from > box_end || seg_to < box_begin) { return false; } - } + real_t length = seg_to - seg_from; + cmin = (seg_from < box_begin) ? ((box_begin - seg_from) / length) : 0; + cmax = (seg_to > box_end) ? ((box_end - seg_from) / length) : 1; - // Convert to 3D again. - Vector3 result = p_from + (rel * min); - Vector3 res_normal = result; - - if (axis == 0) { - res_normal[p_cylinder_axis] = 0; } else { - int axis_side = (p_cylinder_axis + 1) % 3; - res_normal[axis_side] = 0; - axis_side = (axis_side + 1) % 3; - res_normal[axis_side] = 0; + if (seg_to > box_end || seg_from < box_begin) { + return false; + } + real_t length = seg_to - seg_from; + cmin = (seg_from > box_end) ? (box_end - seg_from) / length : 0; + cmax = (seg_to < box_begin) ? (box_begin - seg_from) / length : 1; } - res_normal.normalize(); - - if (r_res) { - *r_res = result; + if (cmin > min) { + min = cmin; + axis = i; } - if (r_norm) { - *r_norm = res_normal; + if (cmax < max) { + max = cmax; } - - return true; - } - - static bool segment_intersects_convex(const Vector3 &p_from, const Vector3 &p_to, const Plane *p_planes, int p_plane_count, Vector3 *r_res, Vector3 *r_norm) { - real_t min = -1e20, max = 1e20; - - Vector3 rel = p_to - p_from; - real_t rel_l = rel.length(); - - if (rel_l < (real_t)CMP_EPSILON) { + if (max < min) { return false; } - - Vector3 dir = rel / rel_l; - - int min_index = -1; - - for (int i = 0; i < p_plane_count; i++) { - const Plane &p = p_planes[i]; - - real_t den = p.normal.dot(dir); - - if (Math::abs(den) <= (real_t)CMP_EPSILON) { - if (p.is_point_over(p_from)) { - // Separating plane. - return false; - } - continue; // Ignore parallel plane. - } - - real_t dist = -p.distance_to(p_from) / den; - - if (den > 0) { - // Backwards facing plane. - if (dist < max) { - max = dist; - } - } else { - // Front facing plane. - if (dist > min) { - min = dist; - min_index = i; - } - } - } - - if (max <= min || min < 0 || min > rel_l || min_index == -1) { // Exit conditions. - return false; // No intersection. - } - - if (r_res) { - *r_res = p_from + dir * min; - } - if (r_norm) { - *r_norm = p_planes[min_index].normal; - } - - return true; } -#ifndef DISABLE_DEPRECATED - static Vector3 get_closest_point_to_segment(const Vector3 &p_point, const Vector3 *p_segment) { - return get_closest_point_to_segment(p_point, p_segment[0], p_segment[1]); - } -#endif // DISABLE_DEPRECATED + // Convert to 3D again. + Vector3 result = p_from + (rel * min); + Vector3 res_normal = result; - static Vector3 get_closest_point_to_segment(const Vector3 &p_point, const Vector3 &p_segment_a, const Vector3 &p_segment_b) { - Vector3 p = p_point - p_segment_a; - Vector3 n = p_segment_b - p_segment_a; - real_t l2 = n.length_squared(); - if (l2 < 1e-20f) { - return p_segment_a; // Both points are the same, just give any. + if (axis == 0) { + res_normal[p_cylinder_axis] = 0; + } else { + int axis_side = (p_cylinder_axis + 1) % 3; + res_normal[axis_side] = 0; + axis_side = (axis_side + 1) % 3; + res_normal[axis_side] = 0; + } + + res_normal.normalize(); + + if (r_res) { + *r_res = result; + } + if (r_norm) { + *r_norm = res_normal; + } + + return true; +} + +inline bool segment_intersects_convex(const Vector3 &p_from, const Vector3 &p_to, const Plane *p_planes, int p_plane_count, Vector3 *r_res, Vector3 *r_norm) { + real_t min = -1e20, max = 1e20; + + Vector3 rel = p_to - p_from; + real_t rel_l = rel.length(); + + if (rel_l < (real_t)CMP_EPSILON) { + return false; + } + + Vector3 dir = rel / rel_l; + + int min_index = -1; + + for (int i = 0; i < p_plane_count; i++) { + const Plane &p = p_planes[i]; + + real_t den = p.normal.dot(dir); + + if (Math::abs(den) <= (real_t)CMP_EPSILON) { + if (p.is_point_over(p_from)) { + // Separating plane. + return false; + } + continue; // Ignore parallel plane. } - real_t d = n.dot(p) / l2; + real_t dist = -p.distance_to(p_from) / den; - if (d <= 0.0f) { - return p_segment_a; // Before first point. - } else if (d >= 1.0f) { - return p_segment_b; // After first point. + if (den > 0) { + // Backwards facing plane. + if (dist < max) { + max = dist; + } } else { - return p_segment_a + n * d; // Inside. + // Front facing plane. + if (dist > min) { + min = dist; + min_index = i; + } } } -#ifndef DISABLE_DEPRECATED - static Vector3 get_closest_point_to_segment_uncapped(const Vector3 &p_point, const Vector3 *p_segment) { - return get_closest_point_to_segment_uncapped(p_point, p_segment[0], p_segment[1]); + if (max <= min || min < 0 || min > rel_l || min_index == -1) { // Exit conditions. + return false; // No intersection. } -#endif // DISABLE_DEPRECATED - static Vector3 get_closest_point_to_segment_uncapped(const Vector3 &p_point, const Vector3 &p_segment_a, const Vector3 &p_segment_b) { - Vector3 p = p_point - p_segment_a; - Vector3 n = p_segment_b - p_segment_a; - real_t l2 = n.length_squared(); - if (l2 < 1e-20f) { - return p_segment_a; // Both points are the same, just give any. - } + if (r_res) { + *r_res = p_from + dir * min; + } + if (r_norm) { + *r_norm = p_planes[min_index].normal; + } - real_t d = n.dot(p) / l2; + return true; +} +inline Vector3 get_closest_point_to_segment(const Vector3 &p_point, const Vector3 &p_segment_a, const Vector3 &p_segment_b) { + Vector3 p = p_point - p_segment_a; + Vector3 n = p_segment_b - p_segment_a; + real_t l2 = n.length_squared(); + if (l2 < 1e-20f) { + return p_segment_a; // Both points are the same, just give any. + } + + real_t d = n.dot(p) / l2; + + if (d <= 0.0f) { + return p_segment_a; // Before first point. + } else if (d >= 1.0f) { + return p_segment_b; // After first point. + } else { return p_segment_a + n * d; // Inside. } +} - static inline bool point_in_projected_triangle(const Vector3 &p_point, const Vector3 &p_v1, const Vector3 &p_v2, const Vector3 &p_v3) { - Vector3 face_n = (p_v1 - p_v3).cross(p_v1 - p_v2); +#ifndef DISABLE_DEPRECATED +inline Vector3 get_closest_point_to_segment(const Vector3 &p_point, const Vector3 *p_segment) { + return get_closest_point_to_segment(p_point, p_segment[0], p_segment[1]); +} +#endif // DISABLE_DEPRECATED - Vector3 n1 = (p_point - p_v3).cross(p_point - p_v2); +inline Vector3 get_closest_point_to_segment_uncapped(const Vector3 &p_point, const Vector3 &p_segment_a, const Vector3 &p_segment_b) { + Vector3 p = p_point - p_segment_a; + Vector3 n = p_segment_b - p_segment_a; + real_t l2 = n.length_squared(); + if (l2 < 1e-20f) { + return p_segment_a; // Both points are the same, just give any. + } - if (face_n.dot(n1) < 0) { - return false; - } + real_t d = n.dot(p) / l2; - Vector3 n2 = (p_v1 - p_v3).cross(p_v1 - p_point); + return p_segment_a + n * d; // Inside. +} - if (face_n.dot(n2) < 0) { - return false; - } +#ifndef DISABLE_DEPRECATED +inline Vector3 get_closest_point_to_segment_uncapped(const Vector3 &p_point, const Vector3 *p_segment) { + return get_closest_point_to_segment_uncapped(p_point, p_segment[0], p_segment[1]); +} +#endif // DISABLE_DEPRECATED - Vector3 n3 = (p_v1 - p_point).cross(p_v1 - p_v2); +inline bool point_in_projected_triangle(const Vector3 &p_point, const Vector3 &p_v1, const Vector3 &p_v2, const Vector3 &p_v3) { + Vector3 face_n = (p_v1 - p_v3).cross(p_v1 - p_v2); - if (face_n.dot(n3) < 0) { - return false; - } + Vector3 n1 = (p_point - p_v3).cross(p_point - p_v2); + if (face_n.dot(n1) < 0) { + return false; + } + + Vector3 n2 = (p_v1 - p_v3).cross(p_v1 - p_point); + + if (face_n.dot(n2) < 0) { + return false; + } + + Vector3 n3 = (p_v1 - p_point).cross(p_v1 - p_v2); + + if (face_n.dot(n3) < 0) { + return false; + } + + return true; +} + +inline bool triangle_sphere_intersection_test(const Vector3 &p_triangle_a, const Vector3 &p_triangle_b, const Vector3 &p_triangle_c, const Vector3 &p_normal, const Vector3 &p_sphere_pos, real_t p_sphere_radius, Vector3 &r_triangle_contact, Vector3 &r_sphere_contact) { + real_t d = p_normal.dot(p_sphere_pos) - p_normal.dot(p_triangle_a); + + if (d > p_sphere_radius || d < -p_sphere_radius) { + // Not touching the plane of the face, return. + return false; + } + + Vector3 contact = p_sphere_pos - (p_normal * d); + + /** 2nd) TEST INSIDE TRIANGLE **/ + + if (Geometry3D::point_in_projected_triangle(contact, p_triangle_a, p_triangle_b, p_triangle_c)) { + r_triangle_contact = contact; + r_sphere_contact = p_sphere_pos - p_normal * p_sphere_radius; + //printf("solved inside triangle\n"); return true; } -#ifndef DISABLE_DEPRECATED - static inline bool triangle_sphere_intersection_test(const Vector3 *p_triangle, const Vector3 &p_normal, const Vector3 &p_sphere_pos, real_t p_sphere_radius, Vector3 &r_triangle_contact, Vector3 &r_sphere_contact) { - return triangle_sphere_intersection_test(p_triangle[0], p_triangle[1], p_triangle[2], p_normal, p_sphere_pos, p_sphere_radius, r_triangle_contact, r_sphere_contact); + /** 3rd TEST INSIDE EDGE CYLINDERS **/ + + const Vector3 verts[4] = { p_triangle_a, p_triangle_b, p_triangle_c, p_triangle_a }; // for() friendly + + for (int i = 0; i < 3; i++) { + // Check edge cylinder. + + Vector3 n1 = verts[i] - verts[i + 1]; + Vector3 n2 = p_sphere_pos - verts[i + 1]; + + ///@TODO Maybe discard by range here to make the algorithm quicker. + + // Check point within cylinder radius. + Vector3 axis = n1.cross(n2).cross(n1); + axis.normalize(); + + real_t ad = axis.dot(n2); + + if (Math::abs(ad) > p_sphere_radius) { + // No chance with this edge, too far away. + continue; + } + + // Check point within edge capsule cylinder. + /** 4th TEST INSIDE EDGE POINTS **/ + + real_t sphere_at = n1.dot(n2); + + if (sphere_at >= 0 && sphere_at < n1.dot(n1)) { + r_triangle_contact = p_sphere_pos - axis * (axis.dot(n2)); + r_sphere_contact = p_sphere_pos - axis * p_sphere_radius; + // Point inside here. + return true; + } + + real_t r2 = p_sphere_radius * p_sphere_radius; + + if (n2.length_squared() < r2) { + Vector3 n = (p_sphere_pos - verts[i + 1]).normalized(); + + r_triangle_contact = verts[i + 1]; + r_sphere_contact = p_sphere_pos - n * p_sphere_radius; + return true; + } + + if (n2.distance_squared_to(n1) < r2) { + Vector3 n = (p_sphere_pos - verts[i]).normalized(); + + r_triangle_contact = verts[i]; + r_sphere_contact = p_sphere_pos - n * p_sphere_radius; + return true; + } + + break; // It's pointless to continue at this point, so save some CPU cycles. } + + return false; +} + +#ifndef DISABLE_DEPRECATED +inline bool triangle_sphere_intersection_test(const Vector3 *p_triangle, const Vector3 &p_normal, const Vector3 &p_sphere_pos, real_t p_sphere_radius, Vector3 &r_triangle_contact, Vector3 &r_sphere_contact) { + return triangle_sphere_intersection_test(p_triangle[0], p_triangle[1], p_triangle[2], p_normal, p_sphere_pos, p_sphere_radius, r_triangle_contact, r_sphere_contact); +} #endif // DISABLE_DEPRECATED - static inline bool triangle_sphere_intersection_test(const Vector3 &p_triangle_a, const Vector3 &p_triangle_b, const Vector3 &p_triangle_c, const Vector3 &p_normal, const Vector3 &p_sphere_pos, real_t p_sphere_radius, Vector3 &r_triangle_contact, Vector3 &r_sphere_contact) { - real_t d = p_normal.dot(p_sphere_pos) - p_normal.dot(p_triangle_a); - - if (d > p_sphere_radius || d < -p_sphere_radius) { - // Not touching the plane of the face, return. - return false; - } - - Vector3 contact = p_sphere_pos - (p_normal * d); - - /** 2nd) TEST INSIDE TRIANGLE **/ - - if (Geometry3D::point_in_projected_triangle(contact, p_triangle_a, p_triangle_b, p_triangle_c)) { - r_triangle_contact = contact; - r_sphere_contact = p_sphere_pos - p_normal * p_sphere_radius; - //printf("solved inside triangle\n"); - return true; - } - - /** 3rd TEST INSIDE EDGE CYLINDERS **/ - - const Vector3 verts[4] = { p_triangle_a, p_triangle_b, p_triangle_c, p_triangle_a }; // for() friendly - - for (int i = 0; i < 3; i++) { - // Check edge cylinder. - - Vector3 n1 = verts[i] - verts[i + 1]; - Vector3 n2 = p_sphere_pos - verts[i + 1]; - - ///@TODO Maybe discard by range here to make the algorithm quicker. - - // Check point within cylinder radius. - Vector3 axis = n1.cross(n2).cross(n1); - axis.normalize(); - - real_t ad = axis.dot(n2); - - if (Math::abs(ad) > p_sphere_radius) { - // No chance with this edge, too far away. - continue; - } - - // Check point within edge capsule cylinder. - /** 4th TEST INSIDE EDGE POINTS **/ - - real_t sphere_at = n1.dot(n2); - - if (sphere_at >= 0 && sphere_at < n1.dot(n1)) { - r_triangle_contact = p_sphere_pos - axis * (axis.dot(n2)); - r_sphere_contact = p_sphere_pos - axis * p_sphere_radius; - // Point inside here. - return true; - } - - real_t r2 = p_sphere_radius * p_sphere_radius; - - if (n2.length_squared() < r2) { - Vector3 n = (p_sphere_pos - verts[i + 1]).normalized(); - - r_triangle_contact = verts[i + 1]; - r_sphere_contact = p_sphere_pos - n * p_sphere_radius; - return true; - } - - if (n2.distance_squared_to(n1) < r2) { - Vector3 n = (p_sphere_pos - verts[i]).normalized(); - - r_triangle_contact = verts[i]; - r_sphere_contact = p_sphere_pos - n * p_sphere_radius; - return true; - } - - break; // It's pointless to continue at this point, so save some CPU cycles. - } - - return false; - } - - static inline Vector clip_polygon(const Vector &polygon, const Plane &p_plane) { - enum LocationCache { - LOC_INSIDE = 1, - LOC_BOUNDARY = 0, - LOC_OUTSIDE = -1 - }; - - if (polygon.is_empty()) { - return polygon; - } - - int *location_cache = (int *)alloca(sizeof(int) * polygon.size()); - int inside_count = 0; - int outside_count = 0; - - for (int a = 0; a < polygon.size(); a++) { - real_t dist = p_plane.distance_to(polygon[a]); - if (dist < (real_t)-CMP_POINT_IN_PLANE_EPSILON) { - location_cache[a] = LOC_INSIDE; - inside_count++; - } else { - if (dist > (real_t)CMP_POINT_IN_PLANE_EPSILON) { - location_cache[a] = LOC_OUTSIDE; - outside_count++; - } else { - location_cache[a] = LOC_BOUNDARY; - } - } - } - - if (outside_count == 0) { - return polygon; // No changes. - } else if (inside_count == 0) { - return Vector(); // Empty. - } - - long previous = polygon.size() - 1; - Vector clipped; - - for (int index = 0; index < polygon.size(); index++) { - int loc = location_cache[index]; - if (loc == LOC_OUTSIDE) { - if (location_cache[previous] == LOC_INSIDE) { - const Vector3 &v1 = polygon[previous]; - const Vector3 &v2 = polygon[index]; - - Vector3 segment = v1 - v2; - real_t den = p_plane.normal.dot(segment); - real_t dist = p_plane.distance_to(v1) / den; - dist = -dist; - clipped.push_back(v1 + segment * dist); - } - } else { - const Vector3 &v1 = polygon[index]; - if ((loc == LOC_INSIDE) && (location_cache[previous] == LOC_OUTSIDE)) { - const Vector3 &v2 = polygon[previous]; - Vector3 segment = v1 - v2; - real_t den = p_plane.normal.dot(segment); - real_t dist = p_plane.distance_to(v1) / den; - dist = -dist; - clipped.push_back(v1 + segment * dist); - } - - clipped.push_back(v1); - } - - previous = index; - } - - return clipped; - } - - static Vector tetrahedralize_delaunay(const Vector &p_points) { - Vector tetr = Delaunay3D::tetrahedralize(p_points); - Vector tetrahedrons; - - tetrahedrons.resize(4 * tetr.size()); - int32_t *ptr = tetrahedrons.ptrw(); - for (int i = 0; i < tetr.size(); i++) { - *ptr++ = tetr[i].points[0]; - *ptr++ = tetr[i].points[1]; - *ptr++ = tetr[i].points[2]; - *ptr++ = tetr[i].points[3]; - } - return tetrahedrons; - } - - // Create a "wrap" that encloses the given geometry. - static Vector wrap_geometry(const Vector &p_array, real_t *p_error = nullptr); - - struct MeshData { - struct Face { - Plane plane; - LocalVector indices; - }; - - LocalVector faces; - - struct Edge { - int vertex_a, vertex_b; - int face_a, face_b; - }; - - LocalVector edges; - - LocalVector vertices; - - void optimize_vertices(); +inline Vector clip_polygon(const Vector &polygon, const Plane &p_plane) { + enum LocationCache { + LOC_INSIDE = 1, + LOC_BOUNDARY = 0, + LOC_OUTSIDE = -1 }; - static MeshData build_convex_mesh(const Vector &p_planes); - static Vector build_sphere_planes(real_t p_radius, int p_lats, int p_lons, Vector3::Axis p_axis = Vector3::AXIS_Z); - static Vector build_box_planes(const Vector3 &p_extents); - static Vector build_cylinder_planes(real_t p_radius, real_t p_height, int p_sides, Vector3::Axis p_axis = Vector3::AXIS_Z); - static Vector build_capsule_planes(real_t p_radius, real_t p_height, int p_sides, int p_lats, Vector3::Axis p_axis = Vector3::AXIS_Z); - - static Vector compute_convex_mesh_points(const Plane *p_planes, int p_plane_count); - -#define FINDMINMAX(x0, x1, x2, min, max) \ - min = max = x0; \ - if (x1 < min) { \ - min = x1; \ - } \ - if (x1 > max) { \ - max = x1; \ - } \ - if (x2 < min) { \ - min = x2; \ - } \ - if (x2 > max) { \ - max = x2; \ + if (polygon.is_empty()) { + return polygon; } - _FORCE_INLINE_ static bool planeBoxOverlap(Vector3 normal, real_t d, Vector3 maxbox) { - int q; - Vector3 vmin, vmax; - for (q = 0; q <= 2; q++) { - if (normal[q] > 0.0f) { - vmin[q] = -maxbox[q]; - vmax[q] = maxbox[q]; + int *location_cache = (int *)alloca(sizeof(int) * polygon.size()); + int inside_count = 0; + int outside_count = 0; + + for (int a = 0; a < polygon.size(); a++) { + real_t dist = p_plane.distance_to(polygon[a]); + if (dist < (real_t)-CMP_POINT_IN_PLANE_EPSILON) { + location_cache[a] = LOC_INSIDE; + inside_count++; + } else { + if (dist > (real_t)CMP_POINT_IN_PLANE_EPSILON) { + location_cache[a] = LOC_OUTSIDE; + outside_count++; } else { - vmin[q] = maxbox[q]; - vmax[q] = -maxbox[q]; + location_cache[a] = LOC_BOUNDARY; } } - if (normal.dot(vmin) + d > 0.0f) { - return false; - } - if (normal.dot(vmax) + d >= 0.0f) { - return true; + } + + if (outside_count == 0) { + return polygon; // No changes. + } else if (inside_count == 0) { + return Vector(); // Empty. + } + + long previous = polygon.size() - 1; + Vector clipped; + + for (int index = 0; index < polygon.size(); index++) { + int loc = location_cache[index]; + if (loc == LOC_OUTSIDE) { + if (location_cache[previous] == LOC_INSIDE) { + const Vector3 &v1 = polygon[previous]; + const Vector3 &v2 = polygon[index]; + + Vector3 segment = v1 - v2; + real_t den = p_plane.normal.dot(segment); + real_t dist = p_plane.distance_to(v1) / den; + dist = -dist; + clipped.push_back(v1 + segment * dist); + } + } else { + const Vector3 &v1 = polygon[index]; + if ((loc == LOC_INSIDE) && (location_cache[previous] == LOC_OUTSIDE)) { + const Vector3 &v2 = polygon[previous]; + Vector3 segment = v1 - v2; + real_t den = p_plane.normal.dot(segment); + real_t dist = p_plane.distance_to(v1) / den; + dist = -dist; + clipped.push_back(v1 + segment * dist); + } + + clipped.push_back(v1); } + previous = index; + } + + return clipped; +} + +inline Vector tetrahedralize_delaunay(const Vector &p_points) { + Vector tetr = Delaunay3D::tetrahedralize(p_points); + Vector tetrahedrons; + + tetrahedrons.resize(4 * tetr.size()); + int32_t *ptr = tetrahedrons.ptrw(); + for (int i = 0; i < tetr.size(); i++) { + *ptr++ = tetr[i].points[0]; + *ptr++ = tetr[i].points[1]; + *ptr++ = tetr[i].points[2]; + *ptr++ = tetr[i].points[3]; + } + return tetrahedrons; +} + +// Create a "wrap" that encloses the given geometry. +Vector wrap_geometry(const Vector &p_array, real_t *p_error = nullptr); + +struct MeshData { + struct Face { + Plane plane; + LocalVector indices; + }; + + LocalVector faces; + + struct Edge { + int vertex_a, vertex_b; + int face_a, face_b; + }; + + LocalVector edges; + + LocalVector vertices; + + void optimize_vertices(); +}; + +MeshData build_convex_mesh(const Vector &p_planes); +Vector build_sphere_planes(real_t p_radius, int p_lats, int p_lons, Vector3::Axis p_axis = Vector3::AXIS_Z); +Vector build_box_planes(const Vector3 &p_extents); +Vector build_cylinder_planes(real_t p_radius, real_t p_height, int p_sides, Vector3::Axis p_axis = Vector3::AXIS_Z); +Vector build_capsule_planes(real_t p_radius, real_t p_height, int p_sides, int p_lats, Vector3::Axis p_axis = Vector3::AXIS_Z); + +Vector compute_convex_mesh_points(const Plane *p_planes, int p_plane_count); + +#define FINDMINMAX(x0, x1, x2, min, max) \ + min = max = x0; \ + if (x1 < min) { \ + min = x1; \ + } \ + if (x1 > max) { \ + max = x1; \ + } \ + if (x2 < min) { \ + min = x2; \ + } \ + if (x2 > max) { \ + max = x2; \ + } + +_FORCE_INLINE_ bool planeBoxOverlap(Vector3 normal, real_t d, Vector3 maxbox) { + int q; + Vector3 vmin, vmax; + for (q = 0; q <= 2; q++) { + if (normal[q] > 0.0f) { + vmin[q] = -maxbox[q]; + vmax[q] = maxbox[q]; + } else { + vmin[q] = maxbox[q]; + vmax[q] = -maxbox[q]; + } + } + if (normal.dot(vmin) + d > 0.0f) { return false; } - -/*======================== X-tests ========================*/ -#define AXISTEST_X01(a, b, fa, fb) \ - p0 = a * v0.y - b * v0.z; \ - p2 = a * v2.y - b * v2.z; \ - if (p0 < p2) { \ - min = p0; \ - max = p2; \ - } else { \ - min = p2; \ - max = p0; \ - } \ - rad = fa * boxhalfsize.y + fb * boxhalfsize.z; \ - if (min > rad || max < -rad) { \ - return false; \ + if (normal.dot(vmax) + d >= 0.0f) { + return true; } -#define AXISTEST_X2(a, b, fa, fb) \ - p0 = a * v0.y - b * v0.z; \ - p1 = a * v1.y - b * v1.z; \ - if (p0 < p1) { \ - min = p0; \ - max = p1; \ - } else { \ - min = p1; \ - max = p0; \ - } \ + return false; +} + +/*======================== X-tests ========================*/ +#define AXISTEST_X01(a, b, fa, fb) \ + p0 = a * v0.y - b * v0.z; \ + p2 = a * v2.y - b * v2.z; \ + if (p0 < p2) { \ + min = p0; \ + max = p2; \ + } else { \ + min = p2; \ + max = p0; \ + } \ rad = fa * boxhalfsize.y + fb * boxhalfsize.z; \ - if (min > rad || max < -rad) { \ - return false; \ + if (min > rad || max < -rad) { \ + return false; \ + } + +#define AXISTEST_X2(a, b, fa, fb) \ + p0 = a * v0.y - b * v0.z; \ + p1 = a * v1.y - b * v1.z; \ + if (p0 < p1) { \ + min = p0; \ + max = p1; \ + } else { \ + min = p1; \ + max = p0; \ + } \ + rad = fa * boxhalfsize.y + fb * boxhalfsize.z; \ + if (min > rad || max < -rad) { \ + return false; \ } /*======================== Y-tests ========================*/ -#define AXISTEST_Y02(a, b, fa, fb) \ - p0 = -a * v0.x + b * v0.z; \ - p2 = -a * v2.x + b * v2.z; \ - if (p0 < p2) { \ - min = p0; \ - max = p2; \ - } else { \ - min = p2; \ - max = p0; \ - } \ +#define AXISTEST_Y02(a, b, fa, fb) \ + p0 = -a * v0.x + b * v0.z; \ + p2 = -a * v2.x + b * v2.z; \ + if (p0 < p2) { \ + min = p0; \ + max = p2; \ + } else { \ + min = p2; \ + max = p0; \ + } \ rad = fa * boxhalfsize.x + fb * boxhalfsize.z; \ - if (min > rad || max < -rad) { \ - return false; \ + if (min > rad || max < -rad) { \ + return false; \ } -#define AXISTEST_Y1(a, b, fa, fb) \ - p0 = -a * v0.x + b * v0.z; \ - p1 = -a * v1.x + b * v1.z; \ - if (p0 < p1) { \ - min = p0; \ - max = p1; \ - } else { \ - min = p1; \ - max = p0; \ - } \ +#define AXISTEST_Y1(a, b, fa, fb) \ + p0 = -a * v0.x + b * v0.z; \ + p1 = -a * v1.x + b * v1.z; \ + if (p0 < p1) { \ + min = p0; \ + max = p1; \ + } else { \ + min = p1; \ + max = p0; \ + } \ rad = fa * boxhalfsize.x + fb * boxhalfsize.z; \ - if (min > rad || max < -rad) { \ - return false; \ + if (min > rad || max < -rad) { \ + return false; \ } /*======================== Z-tests ========================*/ -#define AXISTEST_Z12(a, b, fa, fb) \ - p1 = a * v1.x - b * v1.y; \ - p2 = a * v2.x - b * v2.y; \ - if (p2 < p1) { \ - min = p2; \ - max = p1; \ - } else { \ - min = p1; \ - max = p2; \ - } \ +#define AXISTEST_Z12(a, b, fa, fb) \ + p1 = a * v1.x - b * v1.y; \ + p2 = a * v2.x - b * v2.y; \ + if (p2 < p1) { \ + min = p2; \ + max = p1; \ + } else { \ + min = p1; \ + max = p2; \ + } \ rad = fa * boxhalfsize.x + fb * boxhalfsize.y; \ - if (min > rad || max < -rad) { \ - return false; \ + if (min > rad || max < -rad) { \ + return false; \ } -#define AXISTEST_Z0(a, b, fa, fb) \ - p0 = a * v0.x - b * v0.y; \ - p1 = a * v1.x - b * v1.y; \ - if (p0 < p1) { \ - min = p0; \ - max = p1; \ - } else { \ - min = p1; \ - max = p0; \ - } \ +#define AXISTEST_Z0(a, b, fa, fb) \ + p0 = a * v0.x - b * v0.y; \ + p1 = a * v1.x - b * v1.y; \ + if (p0 < p1) { \ + min = p0; \ + max = p1; \ + } else { \ + min = p1; \ + max = p0; \ + } \ rad = fa * boxhalfsize.x + fb * boxhalfsize.y; \ - if (min > rad || max < -rad) { \ - return false; \ + if (min > rad || max < -rad) { \ + return false; \ } - _FORCE_INLINE_ static bool triangle_box_overlap(const Vector3 &boxcenter, const Vector3 boxhalfsize, const Vector3 *triverts) { - /* use separating axis theorem to test overlap between triangle and box */ - /* need to test for overlap in these directions: */ - /* 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle */ - /* we do not even need to test these) */ - /* 2) normal of the triangle */ - /* 3) crossproduct(edge from tri, {x,y,z}-directin) */ - /* this gives 3x3=9 more tests */ - real_t min, max, p0, p1, p2, rad, fex, fey, fez; +_FORCE_INLINE_ bool triangle_box_overlap(const Vector3 &boxcenter, const Vector3 boxhalfsize, const Vector3 *triverts) { + /* use separating axis theorem to test overlap between triangle and box */ + /* need to test for overlap in these directions: */ + /* 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle */ + /* we do not even need to test these) */ + /* 2) normal of the triangle */ + /* 3) crossproduct(edge from tri, {x,y,z}-directin) */ + /* this gives 3x3=9 more tests */ + real_t min, max, p0, p1, p2, rad, fex, fey, fez; - /* This is the fastest branch on Sun */ - /* move everything so that the boxcenter is in (0,0,0) */ + /* This is the fastest branch on Sun */ + /* move everything so that the boxcenter is in (0,0,0) */ - const Vector3 v0 = triverts[0] - boxcenter; - const Vector3 v1 = triverts[1] - boxcenter; - const Vector3 v2 = triverts[2] - boxcenter; + const Vector3 v0 = triverts[0] - boxcenter; + const Vector3 v1 = triverts[1] - boxcenter; + const Vector3 v2 = triverts[2] - boxcenter; - /* compute triangle edges */ - const Vector3 e0 = v1 - v0; /* tri edge 0 */ - const Vector3 e1 = v2 - v1; /* tri edge 1 */ - const Vector3 e2 = v0 - v2; /* tri edge 2 */ + /* compute triangle edges */ + const Vector3 e0 = v1 - v0; /* tri edge 0 */ + const Vector3 e1 = v2 - v1; /* tri edge 1 */ + const Vector3 e2 = v0 - v2; /* tri edge 2 */ - /* Bullet 3: */ - /* test the 9 tests first (this was faster) */ - fex = Math::abs(e0.x); - fey = Math::abs(e0.y); - fez = Math::abs(e0.z); - AXISTEST_X01(e0.z, e0.y, fez, fey); - AXISTEST_Y02(e0.z, e0.x, fez, fex); - AXISTEST_Z12(e0.y, e0.x, fey, fex); + /* Bullet 3: */ + /* test the 9 tests first (this was faster) */ + fex = Math::abs(e0.x); + fey = Math::abs(e0.y); + fez = Math::abs(e0.z); + AXISTEST_X01(e0.z, e0.y, fez, fey); + AXISTEST_Y02(e0.z, e0.x, fez, fex); + AXISTEST_Z12(e0.y, e0.x, fey, fex); - fex = Math::abs(e1.x); - fey = Math::abs(e1.y); - fez = Math::abs(e1.z); - AXISTEST_X01(e1.z, e1.y, fez, fey); - AXISTEST_Y02(e1.z, e1.x, fez, fex); - AXISTEST_Z0(e1.y, e1.x, fey, fex); + fex = Math::abs(e1.x); + fey = Math::abs(e1.y); + fez = Math::abs(e1.z); + AXISTEST_X01(e1.z, e1.y, fez, fey); + AXISTEST_Y02(e1.z, e1.x, fez, fex); + AXISTEST_Z0(e1.y, e1.x, fey, fex); - fex = Math::abs(e2.x); - fey = Math::abs(e2.y); - fez = Math::abs(e2.z); - AXISTEST_X2(e2.z, e2.y, fez, fey); - AXISTEST_Y1(e2.z, e2.x, fez, fex); - AXISTEST_Z12(e2.y, e2.x, fey, fex); + fex = Math::abs(e2.x); + fey = Math::abs(e2.y); + fez = Math::abs(e2.z); + AXISTEST_X2(e2.z, e2.y, fez, fey); + AXISTEST_Y1(e2.z, e2.x, fez, fex); + AXISTEST_Z12(e2.y, e2.x, fey, fex); - /* Bullet 1: */ - /* first test overlap in the {x,y,z}-directions */ - /* find min, max of the triangle each direction, and test for overlap in */ - /* that direction -- this is equivalent to testing a minimal AABB around */ - /* the triangle against the AABB */ + /* Bullet 1: */ + /* first test overlap in the {x,y,z}-directions */ + /* find min, max of the triangle each direction, and test for overlap in */ + /* that direction -- this is equivalent to testing a minimal AABB around */ + /* the triangle against the AABB */ - /* test in X-direction */ - FINDMINMAX(v0.x, v1.x, v2.x, min, max); - if (min > boxhalfsize.x || max < -boxhalfsize.x) { - return false; - } - - /* test in Y-direction */ - FINDMINMAX(v0.y, v1.y, v2.y, min, max); - if (min > boxhalfsize.y || max < -boxhalfsize.y) { - return false; - } - - /* test in Z-direction */ - FINDMINMAX(v0.z, v1.z, v2.z, min, max); - if (min > boxhalfsize.z || max < -boxhalfsize.z) { - return false; - } - - /* Bullet 2: */ - /* test if the box intersects the plane of the triangle */ - /* compute plane equation of triangle: normal*x+d=0 */ - const Vector3 normal = e0.cross(e1); - const real_t d = -normal.dot(v0); /* plane eq: normal.x+d=0 */ - return planeBoxOverlap(normal, d, boxhalfsize); /* if true, box and triangle overlaps */ + /* test in X-direction */ + FINDMINMAX(v0.x, v1.x, v2.x, min, max); + if (min > boxhalfsize.x || max < -boxhalfsize.x) { + return false; } - static Vector generate_edf(const Vector &p_voxels, const Vector3i &p_size, bool p_negative); - static Vector generate_sdf8(const Vector &p_positive, const Vector &p_negative); - - static Vector3 triangle_get_barycentric_coords(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, const Vector3 &p_pos) { - const Vector3 v0 = p_b - p_a; - const Vector3 v1 = p_c - p_a; - const Vector3 v2 = p_pos - p_a; - - const real_t d00 = v0.dot(v0); - const real_t d01 = v0.dot(v1); - const real_t d11 = v1.dot(v1); - const real_t d20 = v2.dot(v0); - const real_t d21 = v2.dot(v1); - const real_t denom = (d00 * d11 - d01 * d01); - if (denom == 0) { - return Vector3(); //invalid triangle, return empty - } - const real_t v = (d11 * d20 - d01 * d21) / denom; - const real_t w = (d00 * d21 - d01 * d20) / denom; - const real_t u = 1.0f - v - w; - return Vector3(u, v, w); + /* test in Y-direction */ + FINDMINMAX(v0.y, v1.y, v2.y, min, max); + if (min > boxhalfsize.y || max < -boxhalfsize.y) { + return false; } - static Color tetrahedron_get_barycentric_coords(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, const Vector3 &p_d, const Vector3 &p_pos) { - const Vector3 vap = p_pos - p_a; - const Vector3 vbp = p_pos - p_b; + /* test in Z-direction */ + FINDMINMAX(v0.z, v1.z, v2.z, min, max); + if (min > boxhalfsize.z || max < -boxhalfsize.z) { + return false; + } - const Vector3 vab = p_b - p_a; - const Vector3 vac = p_c - p_a; - const Vector3 vad = p_d - p_a; + /* Bullet 2: */ + /* test if the box intersects the plane of the triangle */ + /* compute plane equation of triangle: normal*x+d=0 */ + const Vector3 normal = e0.cross(e1); + const real_t d = -normal.dot(v0); /* plane eq: normal.x+d=0 */ + return planeBoxOverlap(normal, d, boxhalfsize); /* if true, box and triangle overlaps */ +} - const Vector3 vbc = p_c - p_b; - const Vector3 vbd = p_d - p_b; - // ScTP computes the scalar triple product +Vector generate_edf(const Vector &p_voxels, const Vector3i &p_size, bool p_negative); +Vector generate_sdf8(const Vector &p_positive, const Vector &p_negative); + +inline Vector3 triangle_get_barycentric_coords(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, const Vector3 &p_pos) { + const Vector3 v0 = p_b - p_a; + const Vector3 v1 = p_c - p_a; + const Vector3 v2 = p_pos - p_a; + + const real_t d00 = v0.dot(v0); + const real_t d01 = v0.dot(v1); + const real_t d11 = v1.dot(v1); + const real_t d20 = v2.dot(v0); + const real_t d21 = v2.dot(v1); + const real_t denom = (d00 * d11 - d01 * d01); + if (denom == 0) { + return Vector3(); //invalid triangle, return empty + } + const real_t v = (d11 * d20 - d01 * d21) / denom; + const real_t w = (d00 * d21 - d01 * d20) / denom; + const real_t u = 1.0f - v - w; + return Vector3(u, v, w); +} + +inline Color tetrahedron_get_barycentric_coords(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, const Vector3 &p_d, const Vector3 &p_pos) { + const Vector3 vap = p_pos - p_a; + const Vector3 vbp = p_pos - p_b; + + const Vector3 vab = p_b - p_a; + const Vector3 vac = p_c - p_a; + const Vector3 vad = p_d - p_a; + + const Vector3 vbc = p_c - p_b; + const Vector3 vbd = p_d - p_b; + // ScTP computes the scalar triple product #define STP(m_a, m_b, m_c) ((m_a).dot((m_b).cross((m_c)))) - const real_t va6 = STP(vbp, vbd, vbc); - const real_t vb6 = STP(vap, vac, vad); - const real_t vc6 = STP(vap, vad, vab); - const real_t vd6 = STP(vap, vab, vac); - const real_t v6 = 1 / STP(vab, vac, vad); - return Color(va6 * v6, vb6 * v6, vc6 * v6, vd6 * v6); + const real_t va6 = STP(vbp, vbd, vbc); + const real_t vb6 = STP(vap, vac, vad); + const real_t vc6 = STP(vap, vad, vab); + const real_t vd6 = STP(vap, vab, vac); + const real_t v6 = 1 / STP(vab, vac, vad); + return Color(va6 * v6, vb6 * v6, vc6 * v6, vd6 * v6); #undef STP - } +} - _FORCE_INLINE_ static Vector3 octahedron_map_decode(const Vector2 &p_uv) { - // https://twitter.com/Stubbesaurus/status/937994790553227264 - const Vector2 f = p_uv * 2.0f - Vector2(1.0f, 1.0f); - Vector3 n = Vector3(f.x, f.y, 1.0f - Math::abs(f.x) - Math::abs(f.y)); - const real_t t = CLAMP(-n.z, 0.0f, 1.0f); - n.x += n.x >= 0 ? -t : t; - n.y += n.y >= 0 ? -t : t; - return n.normalized(); - } -}; +_FORCE_INLINE_ Vector3 octahedron_map_decode(const Vector2 &p_uv) { + // https://twitter.com/Stubbesaurus/status/937994790553227264 + const Vector2 f = p_uv * 2.0f - Vector2(1.0f, 1.0f); + Vector3 n = Vector3(f.x, f.y, 1.0f - Math::abs(f.x) - Math::abs(f.y)); + const real_t t = CLAMP(-n.z, 0.0f, 1.0f); + n.x += n.x >= 0 ? -t : t; + n.y += n.y >= 0 ? -t : t; + return n.normalized(); +} +} //namespace Geometry3D diff --git a/engine/core/math/math_defs.h b/engine/core/math/math_defs.h index 5a9be58c29..4213b810a2 100644 --- a/engine/core/math/math_defs.h +++ b/engine/core/math/math_defs.h @@ -145,3 +145,16 @@ typedef double real_t; #else typedef float real_t; #endif + +/** + * Rarely, there will be a scenario where a function/variable expects one of the builtin integral + * types that do NOT utilize the fixed-width constants. In practice, the only discrepancies are + * with `long` or `long long` (and their unsigned equivalents) not being declared when most/all + * other integral constants are. We'll account for this with `int_alt_t` and `uint_alt_t`, + * which assign to the unused fixed-width slot. As this will only be used rarely, keep the types + * scoped to `Math` instead of the global namespace. + */ +namespace Math { +using int_alt_t = std::conditional_t, long long, long>; +using uint_alt_t = std::conditional_t, unsigned long long, unsigned long>; +} //namespace Math diff --git a/engine/core/math/math_fieldwise.cpp b/engine/core/math/math_fieldwise.cpp index 8e81e4cc34..36cccfc651 100644 --- a/engine/core/math/math_fieldwise.cpp +++ b/engine/core/math/math_fieldwise.cpp @@ -32,12 +32,12 @@ #include "math_fieldwise.h" -#define SETUP_TYPE(m_type) \ +#define SETUP_TYPE(m_type) \ m_type source = p_source; \ m_type target = p_target; #define TRY_TRANSFER_FIELD(m_name, m_member) \ - if (p_field == m_name) { \ - target.m_member = source.m_member; \ + if (p_field == m_name) { \ + target.m_member = source.m_member; \ } Variant fieldwise_assign(const Variant &p_target, const Variant &p_source, const String &p_field) { diff --git a/engine/core/math/math_funcs.h b/engine/core/math/math_funcs.h index 971ecd5aea..43e4aac4eb 100644 --- a/engine/core/math/math_funcs.h +++ b/engine/core/math/math_funcs.h @@ -34,7 +34,6 @@ #include "core/math/math_defs.h" #include "core/typedefs.h" -#include #include namespace Math { diff --git a/engine/core/math/math_funcs_binary.h b/engine/core/math/math_funcs_binary.h new file mode 100644 index 0000000000..2e9a13853a --- /dev/null +++ b/engine/core/math/math_funcs_binary.h @@ -0,0 +1,188 @@ +/**************************************************************************/ +/* math_funcs_binary.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. */ +/**************************************************************************/ + +#pragma once + +#include "core/typedefs.h" + +namespace Math { + +/* Functions to handle powers of 2 and shifting. */ + +// Returns `true` if a positive integer is a power of 2, `false` otherwise. +template +inline bool is_power_of_2(const T x) { + return x && ((x & (x - 1)) == 0); +} + +// Function to find the next power of 2 to an integer. +constexpr uint64_t next_power_of_2(uint64_t p_number) { + if (p_number == 0) { + return 0; + } + + --p_number; + p_number |= p_number >> 1; + p_number |= p_number >> 2; + p_number |= p_number >> 4; + p_number |= p_number >> 8; + p_number |= p_number >> 16; + p_number |= p_number >> 32; + + return ++p_number; +} + +constexpr uint32_t next_power_of_2(uint32_t p_number) { + if (p_number == 0) { + return 0; + } + + --p_number; + p_number |= p_number >> 1; + p_number |= p_number >> 2; + p_number |= p_number >> 4; + p_number |= p_number >> 8; + p_number |= p_number >> 16; + + return ++p_number; +} + +// Function to find the previous power of 2 to an integer. +constexpr uint64_t previous_power_of_2(uint64_t p_number) { + p_number |= p_number >> 1; + p_number |= p_number >> 2; + p_number |= p_number >> 4; + p_number |= p_number >> 8; + p_number |= p_number >> 16; + p_number |= p_number >> 32; + return p_number - (p_number >> 1); +} + +constexpr uint32_t previous_power_of_2(uint32_t p_number) { + p_number |= p_number >> 1; + p_number |= p_number >> 2; + p_number |= p_number >> 4; + p_number |= p_number >> 8; + p_number |= p_number >> 16; + return p_number - (p_number >> 1); +} + +// Function to find the closest power of 2 to an integer. +constexpr uint64_t closest_power_of_2(uint64_t p_number) { + uint64_t nx = next_power_of_2(p_number); + uint64_t px = previous_power_of_2(p_number); + return (nx - p_number) > (p_number - px) ? px : nx; +} + +constexpr uint32_t closest_power_of_2(uint32_t p_number) { + uint32_t nx = next_power_of_2(p_number); + uint32_t px = previous_power_of_2(p_number); + return (nx - p_number) > (p_number - px) ? px : nx; +} + +// Get a shift value from a power of 2. +constexpr int32_t get_shift_from_power_of_2(uint64_t p_bits) { + for (uint64_t i = 0; i < (uint64_t)64; i++) { + if (p_bits == (uint64_t)((uint64_t)1 << i)) { + return i; + } + } + + return -1; +} + +constexpr int32_t get_shift_from_power_of_2(uint32_t p_bits) { + for (uint32_t i = 0; i < (uint32_t)32; i++) { + if (p_bits == (uint32_t)((uint32_t)1 << i)) { + return i; + } + } + + return -1; +} + +template +_FORCE_INLINE_ T nearest_power_of_2_templated(T p_number) { + --p_number; + + // The number of operations on x is the base two logarithm + // of the number of bits in the type. Add three to account + // for sizeof(T) being in bytes. + constexpr size_t shift_steps = get_shift_from_power_of_2((uint64_t)sizeof(T)) + 3; + + // If the compiler is smart, it unrolls this loop. + // If it's dumb, this is a bit slow. + for (size_t i = 0; i < shift_steps; i++) { + p_number |= p_number >> (1 << i); + } + + return ++p_number; +} + +// Function to find the nearest (bigger) power of 2 to an integer. +constexpr uint64_t nearest_shift(uint64_t p_number) { + uint64_t i = 63; + do { + i--; + if (p_number & ((uint64_t)1 << i)) { + return i + (uint64_t)1; + } + } while (i != 0); + + return 0; +} + +constexpr uint32_t nearest_shift(uint32_t p_number) { + uint32_t i = 31; + do { + i--; + if (p_number & ((uint32_t)1 << i)) { + return i + (uint32_t)1; + } + } while (i != 0); + + return 0; +} + +// constexpr function to find the floored log2 of a number +template +constexpr T floor_log2(T x) { + return x < 2 ? x : 1 + floor_log2(x >> 1); +} + +// Get the number of bits needed to represent the number. +// IE, if you pass in 8, you will get 4. +// If you want to know how many bits are needed to store 8 values however, pass in (8 - 1). +template +constexpr T get_num_bits(T x) { + return floor_log2(x); +} + +} //namespace Math diff --git a/engine/core/math/plane.cpp b/engine/core/math/plane.cpp index 74783b2a69..b23514f007 100644 --- a/engine/core/math/plane.cpp +++ b/engine/core/math/plane.cpp @@ -38,9 +38,15 @@ void Plane::set_normal(const Vector3 &p_normal) { } void Plane::normalize() { +#ifdef MATH_CHECKS + if (!is_finite()) { + WARN_PRINT("Plane cannot be normalized, the distance and the normal should be finite."); + } +#endif // MATH_CHECKS + real_t l = normal.length(); if (l == 0) { - *this = Plane(0, 0, 0, 0); + zero(); return; } normal /= l; diff --git a/engine/core/math/plane.h b/engine/core/math/plane.h index 6bce4e773f..a440e8c4c0 100644 --- a/engine/core/math/plane.h +++ b/engine/core/math/plane.h @@ -45,6 +45,11 @@ struct [[nodiscard]] Plane { void set_normal(const Vector3 &p_normal); _FORCE_INLINE_ Vector3 get_normal() const { return normal; } + void zero() { + normal.zero(); + d = 0; + } + void normalize(); Plane normalized() const; diff --git a/engine/core/math/quaternion.cpp b/engine/core/math/quaternion.cpp index 536682de8d..e4ae4027da 100644 --- a/engine/core/math/quaternion.cpp +++ b/engine/core/math/quaternion.cpp @@ -63,6 +63,11 @@ real_t Quaternion::length() const { } void Quaternion::normalize() { +#ifdef MATH_CHECKS + if (!is_finite()) { + WARN_PRINT("Quaternion cannot be normalized, the elements should be finite."); + } +#endif // MATH_CHECKS *this /= length(); } diff --git a/engine/core/math/random_number_generator.cpp b/engine/core/math/random_number_generator.cpp index 226d748c52..52d06fba43 100644 --- a/engine/core/math/random_number_generator.cpp +++ b/engine/core/math/random_number_generator.cpp @@ -30,6 +30,8 @@ #include "random_number_generator.h" +#include "core/object/class_db.h" + void RandomNumberGenerator::_bind_methods() { ClassDB::bind_method(D_METHOD("set_seed", "seed"), &RandomNumberGenerator::set_seed); ClassDB::bind_method(D_METHOD("get_seed"), &RandomNumberGenerator::get_seed); diff --git a/engine/core/math/random_pcg.h b/engine/core/math/random_pcg.h index bfcd3eeecc..618f3ebe76 100644 --- a/engine/core/math/random_pcg.h +++ b/engine/core/math/random_pcg.h @@ -34,6 +34,8 @@ #include "thirdparty/misc/pcg.h" +#include // ldexp + #if defined(__GNUC__) #define CLZ32(x) __builtin_clz(x) #elif defined(_MSC_VER) @@ -122,14 +124,14 @@ public: if (temp < CMP_EPSILON) { temp += CMP_EPSILON; // To prevent generating of INF value in log function, resulting to return NaN value from this function. } - return p_mean + p_deviation * (std::cos(Math::TAU * randd()) * std::sqrt(-2.0 * std::log(temp))); // Box-Muller transform. + return p_mean + p_deviation * (Math::cos(Math::TAU * randd()) * Math::sqrt(-2.0 * Math::log(temp))); // Box-Muller transform. } _FORCE_INLINE_ float randfn(float p_mean, float p_deviation) { float temp = randf(); if (temp < CMP_EPSILON) { temp += CMP_EPSILON; // To prevent generating of INF value in log function, resulting to return NaN value from this function. } - return p_mean + p_deviation * (std::cos((float)Math::TAU * randf()) * std::sqrt(-2.0 * std::log(temp))); // Box-Muller transform. + return p_mean + p_deviation * (Math::cos((float)Math::TAU * randf()) * Math::sqrt(-2.0 * Math::log(temp))); // Box-Muller transform. } double random(double p_from, double p_to); diff --git a/engine/core/math/transform_2d.cpp b/engine/core/math/transform_2d.cpp index b5a971e7d9..611b6f0851 100644 --- a/engine/core/math/transform_2d.cpp +++ b/engine/core/math/transform_2d.cpp @@ -145,7 +145,8 @@ void Transform2D::translate_local(const Vector2 &p_translation) { } void Transform2D::orthonormalize() { - // Gram-Schmidt Process + // Orthonormalizable check is done in Vector2 class below. + // Gram-Schmidt Process: Vector2 x = columns[0]; Vector2 y = columns[1]; diff --git a/engine/core/math/transform_3d.cpp b/engine/core/math/transform_3d.cpp index b4d02408ac..a5b825be03 100644 --- a/engine/core/math/transform_3d.cpp +++ b/engine/core/math/transform_3d.cpp @@ -150,6 +150,7 @@ Transform3D Transform3D::translated_local(const Vector3 &p_translation) const { } void Transform3D::orthonormalize() { + // Orthonormalizable check is done in Basis class below. basis.orthonormalize(); } diff --git a/engine/core/math/triangle_mesh.cpp b/engine/core/math/triangle_mesh.cpp index 3b24b351ea..c991699863 100644 --- a/engine/core/math/triangle_mesh.cpp +++ b/engine/core/math/triangle_mesh.cpp @@ -30,6 +30,7 @@ #include "triangle_mesh.h" +#include "core/object/class_db.h" #include "core/templates/sort_array.h" int TriangleMesh::_create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, int p_depth, int &r_max_depth, int &r_max_alloc) { diff --git a/engine/core/math/vector2.cpp b/engine/core/math/vector2.cpp index 7a51c13945..581214e6e1 100644 --- a/engine/core/math/vector2.cpp +++ b/engine/core/math/vector2.cpp @@ -50,8 +50,18 @@ real_t Vector2::length_squared() const { } void Vector2::normalize() { - real_t l = x * x + y * y; - if (l != 0) { + if (!is_finite()) { +#ifdef MATH_CHECKS + WARN_PRINT("Vector2 cannot be normalized, the elements must be finite. Making (0, 0) as a fallback."); +#endif // MATH_CHECKS + zero(); + return; + } + + real_t l = length_squared(); + if (l == 0) { + zero(); + } else { l = Math::sqrt(l); x /= l; y /= l; diff --git a/engine/core/math/vector2.h b/engine/core/math/vector2.h index 98207474a3..5bb064523b 100644 --- a/engine/core/math/vector2.h +++ b/engine/core/math/vector2.h @@ -91,6 +91,8 @@ struct [[nodiscard]] Vector2 { real_t length_squared() const; Vector2 limit_length(real_t p_len = 1.0) const; + void zero() { x = y = 0; } + Vector2 min(const Vector2 &p_vector2) const { return Vector2(MIN(x, p_vector2.x), MIN(y, p_vector2.y)); } diff --git a/engine/core/math/vector3.h b/engine/core/math/vector3.h index b0f29e0d56..f83431b59c 100644 --- a/engine/core/math/vector3.h +++ b/engine/core/math/vector3.h @@ -546,14 +546,22 @@ real_t Vector3::length_squared() const { } void Vector3::normalize() { - real_t lengthsq = length_squared(); - if (lengthsq == 0) { - x = y = z = 0; + if (!is_finite()) { +#ifdef MATH_CHECKS + WARN_PRINT("Vector3 cannot be normalized, the elements must be finite. Making (0, 0, 0) as a fallback."); +#endif // MATH_CHECKS + zero(); + return; + } + + real_t l = length_squared(); + if (l == 0) { + zero(); } else { - real_t length = Math::sqrt(lengthsq); - x /= length; - y /= length; - z /= length; + l = Math::sqrt(l); + x /= l; + y /= l; + z /= l; } } diff --git a/engine/core/math/vector4.cpp b/engine/core/math/vector4.cpp index e4fa1769c0..adf6053735 100644 --- a/engine/core/math/vector4.cpp +++ b/engine/core/math/vector4.cpp @@ -79,15 +79,23 @@ real_t Vector4::length() const { } void Vector4::normalize() { - real_t lengthsq = length_squared(); - if (lengthsq == 0) { - x = y = z = w = 0; + if (!is_finite()) { +#ifdef MATH_CHECKS + WARN_PRINT("Vector4 cannot be normalized, the elements must be finite. Making (0, 0, 0, 0) as a fallback."); +#endif // MATH_CHECKS + zero(); + return; + } + + real_t l = length_squared(); + if (l == 0) { + zero(); } else { - real_t length = Math::sqrt(lengthsq); - x /= length; - y /= length; - z /= length; - w /= length; + l = Math::sqrt(l); + x /= l; + y /= l; + z /= l; + w /= l; } } diff --git a/engine/core/math/vector4.h b/engine/core/math/vector4.h index edfe28b077..6b2fe70656 100644 --- a/engine/core/math/vector4.h +++ b/engine/core/math/vector4.h @@ -98,6 +98,8 @@ struct [[nodiscard]] Vector4 { Vector4 normalized() const; bool is_normalized() const; + void zero() { x = y = z = w = 0; } + real_t distance_to(const Vector4 &p_to) const; real_t distance_squared_to(const Vector4 &p_to) const; Vector4 direction_to(const Vector4 &p_to) const; diff --git a/engine/core/object/SCsub b/engine/core/object/SCsub index 3d0d2c14dd..eeb59324a3 100644 --- a/engine/core/object/SCsub +++ b/engine/core/object/SCsub @@ -5,7 +5,7 @@ Import("env") import make_virtuals -env.CommandNoCache(["gdvirtual.gen.inc"], "make_virtuals.py", env.Run(make_virtuals.run)) +env.CommandNoCache("gdvirtual.gen.h", "make_virtuals.py", env.Run(make_virtuals.run)) env_object = env.Clone() diff --git a/engine/core/object/callable_method_pointer.cpp b/engine/core/object/callable_mp.cpp similarity index 97% rename from engine/core/object/callable_method_pointer.cpp rename to engine/core/object/callable_mp.cpp index ed400788b1..6506643f1f 100644 --- a/engine/core/object/callable_method_pointer.cpp +++ b/engine/core/object/callable_mp.cpp @@ -1,5 +1,5 @@ /**************************************************************************/ -/* callable_method_pointer.cpp */ +/* callable_mp.cpp */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#include "callable_method_pointer.h" +#include "callable_mp.h" bool CallableCustomMethodPointerBase::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) { const CallableCustomMethodPointerBase *a = static_cast(p_a); diff --git a/engine/core/object/callable_method_pointer.h b/engine/core/object/callable_mp.h similarity index 99% rename from engine/core/object/callable_method_pointer.h rename to engine/core/object/callable_mp.h index 218799933d..aad45a0048 100644 --- a/engine/core/object/callable_method_pointer.h +++ b/engine/core/object/callable_mp.h @@ -1,5 +1,5 @@ /**************************************************************************/ -/* callable_method_pointer.h */ +/* callable_mp.h */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ diff --git a/engine/core/object/class_db.cpp b/engine/core/object/class_db.cpp index 2a97d2b913..fc3096dbf0 100644 --- a/engine/core/object/class_db.cpp +++ b/engine/core/object/class_db.cpp @@ -1018,7 +1018,7 @@ void ClassDB::get_method_list_with_compatibility(const StringName &p_class, List #endif // DEBUG_ENABLED for (const KeyValue> &E : type->method_map_compatibility) { - LocalVector compat = E.value; + LocalVector compat(E.value); for (MethodBind *method : compat) { MethodInfo minfo = info_from_bind(method); @@ -1539,11 +1539,14 @@ void ClassDB::add_property(const StringName &p_class, const PropertyInfo &p_pinf type->property_list.push_back(p_pinfo); type->property_map[p_pinfo.name] = p_pinfo; #ifdef DEBUG_ENABLED - if (mb_get) { - type->methods_in_properties.insert(p_getter); - } - if (mb_set) { - type->methods_in_properties.insert(p_setter); + // Used to filter out setters and getters in the editor (e.g. autocomplete) to not clutter menus. We only want to filter methods from properties that are easily available to users. + if (p_index == -1 && !(p_pinfo.usage & PropertyUsageFlags::PROPERTY_USAGE_INTERNAL)) { + if (mb_get) { + type->methods_in_properties.insert(p_getter); + } + if (mb_set) { + type->methods_in_properties.insert(p_setter); + } } #endif // DEBUG_ENABLED PropertySetGet psg; diff --git a/engine/core/object/class_db.h b/engine/core/object/class_db.h index 9516f655df..24072b36de 100644 --- a/engine/core/object/class_db.h +++ b/engine/core/object/class_db.h @@ -34,10 +34,6 @@ #include "core/object/object.h" #include "core/os/rw_lock.h" #include "core/string/print_string.h" - -// Makes callable_mp readily available in all classes connecting signals. -// Needs to come after method_bind and object have been included. -#include "core/object/callable_method_pointer.h" #include "core/templates/a_hash_map.h" #include "core/templates/hash_set.h" @@ -59,7 +55,7 @@ inline constexpr bool is_class_enabled_v = is_class_enabled::value; #define GD_IS_CLASS_ENABLED(m_class) is_class_enabled_v -#include "core/disabled_classes.gen.h" +#include "core/disabled_classes.gen.h" // IWYU pragma: keep. #define DEFVAL(m_defval) (m_defval) #define DEFVAL_ARRAY DEFVAL(ClassDB::default_array_arg) @@ -555,13 +551,13 @@ public: }; #define BIND_ENUM_CONSTANT(m_constant) \ - ::ClassDB::bind_integer_constant(get_class_static(), __constant_get_enum_name(m_constant), #m_constant, m_constant); + ::ClassDB::bind_integer_constant(get_class_static(), __constant_get_enum_name(m_constant), __constant_get_enum_value_name(#m_constant), m_constant); #define BIND_BITFIELD_FLAG(m_constant) \ - ::ClassDB::bind_integer_constant(get_class_static(), __constant_get_bitfield_name(m_constant), #m_constant, m_constant, true); + ::ClassDB::bind_integer_constant(get_class_static(), __constant_get_bitfield_name(m_constant), __constant_get_enum_value_name(#m_constant), m_constant, true); #define BIND_CONSTANT(m_constant) \ - ::ClassDB::bind_integer_constant(get_class_static(), StringName(), #m_constant, m_constant); + ::ClassDB::bind_integer_constant(get_class_static(), StringName(), __constant_get_enum_value_name(#m_constant), m_constant); #ifdef DEBUG_ENABLED @@ -574,24 +570,24 @@ public: #endif // DEBUG_ENABLED -#define GDREGISTER_CLASS(m_class) \ +#define GDREGISTER_CLASS(m_class) \ if constexpr (GD_IS_CLASS_ENABLED(m_class)) { \ - ::ClassDB::register_class(); \ + ::ClassDB::register_class(); \ } -#define GDREGISTER_VIRTUAL_CLASS(m_class) \ +#define GDREGISTER_VIRTUAL_CLASS(m_class) \ if constexpr (GD_IS_CLASS_ENABLED(m_class)) { \ ::ClassDB::register_class(true); \ } -#define GDREGISTER_ABSTRACT_CLASS(m_class) \ - if constexpr (GD_IS_CLASS_ENABLED(m_class)) { \ +#define GDREGISTER_ABSTRACT_CLASS(m_class) \ + if constexpr (GD_IS_CLASS_ENABLED(m_class)) { \ ::ClassDB::register_abstract_class(); \ } -#define GDREGISTER_INTERNAL_CLASS(m_class) \ - if constexpr (GD_IS_CLASS_ENABLED(m_class)) { \ +#define GDREGISTER_INTERNAL_CLASS(m_class) \ + if constexpr (GD_IS_CLASS_ENABLED(m_class)) { \ ::ClassDB::register_internal_class(); \ } -#define GDREGISTER_RUNTIME_CLASS(m_class) \ - if constexpr (GD_IS_CLASS_ENABLED(m_class)) { \ +#define GDREGISTER_RUNTIME_CLASS(m_class) \ + if constexpr (GD_IS_CLASS_ENABLED(m_class)) { \ ::ClassDB::register_runtime_class(); \ } diff --git a/engine/core/object/make_virtuals.py b/engine/core/object/make_virtuals.py index 9ba6330c43..7ba4c31e70 100644 --- a/engine/core/object/make_virtuals.py +++ b/engine/core/object/make_virtuals.py @@ -196,7 +196,12 @@ def run(target, source, env): txt = """/* THIS FILE IS GENERATED DO NOT EDIT */ #pragma once +// IWYU pragma: begin_keep #include "core/object/script_instance.h" +#include "core/variant/method_ptrcall.h" +#include "core/variant/variant_caster.h" +#include "core/variant/variant_internal.h" +// IWYU pragma: end_keep inline constexpr uintptr_t _INVALID_GDVIRTUAL_FUNC_ADDR = static_cast(-1); diff --git a/engine/core/object/message_queue.cpp b/engine/core/object/message_queue.cpp index eb2066ecf5..5b97eb5f62 100644 --- a/engine/core/object/message_queue.cpp +++ b/engine/core/object/message_queue.cpp @@ -31,31 +31,29 @@ #include "message_queue.h" #include "core/config/project_settings.h" -#include "core/object/class_db.h" -#include "core/object/script_language.h" #include #ifdef DEV_ENABLED // Includes safety checks to ensure that a queue set as a thread singleton override // is only ever called from the thread it was set for. -#define LOCK_MUTEX \ +#define LOCK_MUTEX \ if (this != MessageQueue::thread_singleton) { \ - DEV_ASSERT(!is_current_thread_override); \ - mutex.lock(); \ - } else { \ - DEV_ASSERT(is_current_thread_override); \ + DEV_ASSERT(!is_current_thread_override); \ + mutex.lock(); \ + } else { \ + DEV_ASSERT(is_current_thread_override); \ } #else -#define LOCK_MUTEX \ +#define LOCK_MUTEX \ if (this != MessageQueue::thread_singleton) { \ - mutex.lock(); \ + mutex.lock(); \ } #endif -#define UNLOCK_MUTEX \ +#define UNLOCK_MUTEX \ if (this != MessageQueue::thread_singleton) { \ - mutex.unlock(); \ + mutex.unlock(); \ } void CallQueue::_add_page() { diff --git a/engine/core/object/message_queue.h b/engine/core/object/message_queue.h index 8a66d0c182..2722a1ed95 100644 --- a/engine/core/object/message_queue.h +++ b/engine/core/object/message_queue.h @@ -31,7 +31,7 @@ #pragma once #include "core/object/object_id.h" -#include "core/os/thread_safe.h" +#include "core/os/mutex.h" #include "core/templates/local_vector.h" #include "core/templates/paged_allocator.h" #include "core/variant/variant.h" @@ -130,15 +130,6 @@ public: } Error push_callp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error = false); - template - Error push_call(Object *p_object, const StringName &p_method, VarArgs... p_args) { - Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. - const Variant *argptrs[sizeof...(p_args) + 1]; - for (uint32_t i = 0; i < sizeof...(p_args); i++) { - argptrs[i] = &args[i]; - } - return push_callp(p_object, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); - } Error push_notification(Object *p_object, int p_notification); Error push_set(Object *p_object, const StringName &p_prop, const Variant &p_value); diff --git a/engine/core/object/object.cpp b/engine/core/object/object.cpp index c49962c7f0..19d8e916c8 100644 --- a/engine/core/object/object.cpp +++ b/engine/core/object/object.cpp @@ -30,6 +30,7 @@ #include "object.h" +#include "core/config/engine.h" #include "core/extension/gdextension_manager.h" #include "core/io/resource.h" #include "core/object/class_db.h" @@ -616,9 +617,8 @@ void Object::get_property_list(List *p_list, bool p_reversed) cons _get_property_listv(p_list, p_reversed); - if (!is_class("Script")) { // can still be set, but this is for user-friendliness - p_list->push_back(PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NEVER_DUPLICATE)); - } + const uint32_t base_script_usage = is_class(Script::get_class_static()) ? PROPERTY_USAGE_NO_EDITOR : PROPERTY_USAGE_DEFAULT; + p_list->push_back(PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, Script::get_class_static(), base_script_usage | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_NEVER_DUPLICATE)); if (script_instance && !p_reversed) { script_instance->get_property_list(p_list); @@ -630,10 +630,10 @@ void Object::get_property_list(List *p_list, bool p_reversed) cons pi.hint = PROPERTY_HINT_RESOURCE_TYPE; Object *obj = K.value; if (Object::cast_to diff --git a/engine/misc/dist/macos_tools.app/Contents/Info.plist b/engine/misc/dist/macos_tools.app/Contents/Info.plist index 1627b1cbba..0c7faac6ca 100644 --- a/engine/misc/dist/macos_tools.app/Contents/Info.plist +++ b/engine/misc/dist/macos_tools.app/Contents/Info.plist @@ -19,11 +19,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 4.6.3 + 4.7 CFBundleSignature GODO CFBundleVersion - 4.6.3 + 4.7 NSMicrophoneUsageDescription Microphone access is required to capture audio. NSCameraUsageDescription diff --git a/engine/misc/dist/windows/godot.iss b/engine/misc/dist/windows/godot.iss index 1ca9464514..3aad2bd7c5 100644 --- a/engine/misc/dist/windows/godot.iss +++ b/engine/misc/dist/windows/godot.iss @@ -1,5 +1,5 @@ #define MyAppName "Godot Engine" -#define MyAppVersion "4.6.3" +#define MyAppVersion "4.7" #define MyAppPublisher "Godot Engine contributors" #define MyAppURL "https://godotengine.org/" #define MyAppExeName "godot.exe" diff --git a/engine/misc/error_suppressions/tsan.txt b/engine/misc/error_suppressions/tsan.txt index 83be638b15..00cfb3f147 100644 --- a/engine/misc/error_suppressions/tsan.txt +++ b/engine/misc/error_suppressions/tsan.txt @@ -3,7 +3,7 @@ deadlock:modules/text_server_adv/text_server_adv.cpp deadlock:modules/text_server_fb/text_server_fb.cpp -deadlock:tests/core/templates/test_command_queue.h +deadlock:tests/core/templates/test_command_queue.cpp race:modules/navigation_2d/nav_map_2d.cpp race:modules/navigation_3d/nav_map_3d.cpp race:thirdparty/thorvg/src/loaders/external_jpg/tvgJpgLoader.cpp diff --git a/engine/misc/extension_api_validation/4.5-stable/GH-107167.txt b/engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-107167.txt similarity index 100% rename from engine/misc/extension_api_validation/4.5-stable/GH-107167.txt rename to engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-107167.txt diff --git a/engine/misc/extension_api_validation/4.5-stable/GH-107954.txt b/engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-107954.txt similarity index 100% rename from engine/misc/extension_api_validation/4.5-stable/GH-107954.txt rename to engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-107954.txt diff --git a/engine/misc/extension_api_validation/4.5-stable/GH-109302.txt b/engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-109302.txt similarity index 100% rename from engine/misc/extension_api_validation/4.5-stable/GH-109302.txt rename to engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-109302.txt diff --git a/engine/misc/extension_api_validation/4.5-stable/GH-110120.txt b/engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-110120.txt similarity index 100% rename from engine/misc/extension_api_validation/4.5-stable/GH-110120.txt rename to engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-110120.txt diff --git a/engine/misc/extension_api_validation/4.5-stable/GH-110250.txt b/engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-110250.txt similarity index 100% rename from engine/misc/extension_api_validation/4.5-stable/GH-110250.txt rename to engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-110250.txt diff --git a/engine/misc/extension_api_validation/4.5-stable/GH-110433.txt b/engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-110433.txt similarity index 100% rename from engine/misc/extension_api_validation/4.5-stable/GH-110433.txt rename to engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-110433.txt diff --git a/engine/misc/extension_api_validation/4.5-stable/GH-110767.txt b/engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-110767.txt similarity index 100% rename from engine/misc/extension_api_validation/4.5-stable/GH-110767.txt rename to engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-110767.txt diff --git a/engine/misc/extension_api_validation/4.5-stable/GH-110867.txt b/engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-110867.txt similarity index 100% rename from engine/misc/extension_api_validation/4.5-stable/GH-110867.txt rename to engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-110867.txt diff --git a/engine/misc/extension_api_validation/4.5-stable/GH-111117.txt b/engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-111117.txt similarity index 100% rename from engine/misc/extension_api_validation/4.5-stable/GH-111117.txt rename to engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-111117.txt diff --git a/engine/misc/extension_api_validation/4.5-stable/GH-111212.txt b/engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-111212.txt similarity index 100% rename from engine/misc/extension_api_validation/4.5-stable/GH-111212.txt rename to engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-111212.txt diff --git a/engine/misc/extension_api_validation/4.5-stable/GH-111439.txt b/engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-111439.txt similarity index 100% rename from engine/misc/extension_api_validation/4.5-stable/GH-111439.txt rename to engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-111439.txt diff --git a/engine/misc/extension_api_validation/4.5-stable/GH-112290.txt b/engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-112290.txt similarity index 100% rename from engine/misc/extension_api_validation/4.5-stable/GH-112290.txt rename to engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-112290.txt diff --git a/engine/misc/extension_api_validation/4.5-stable/GH-112379.txt b/engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-112379.txt similarity index 100% rename from engine/misc/extension_api_validation/4.5-stable/GH-112379.txt rename to engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-112379.txt diff --git a/engine/misc/extension_api_validation/4.5-stable/GH-112539.txt b/engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-112539.txt similarity index 100% rename from engine/misc/extension_api_validation/4.5-stable/GH-112539.txt rename to engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-112539.txt diff --git a/engine/misc/extension_api_validation/4.5-stable/GH-113172.txt b/engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-113172.txt similarity index 100% rename from engine/misc/extension_api_validation/4.5-stable/GH-113172.txt rename to engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-113172.txt diff --git a/engine/misc/extension_api_validation/4.5-stable/GH-113459.txt b/engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-113459.txt similarity index 100% rename from engine/misc/extension_api_validation/4.5-stable/GH-113459.txt rename to engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-113459.txt diff --git a/engine/misc/extension_api_validation/4.5-stable/GH-114053.txt b/engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-114053.txt similarity index 100% rename from engine/misc/extension_api_validation/4.5-stable/GH-114053.txt rename to engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-114053.txt diff --git a/engine/misc/extension_api_validation/4.5-stable/GH-90411.txt b/engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-90411.txt similarity index 100% rename from engine/misc/extension_api_validation/4.5-stable/GH-90411.txt rename to engine/misc/extension_api_validation/4.5-stable_4.6-stable/GH-90411.txt diff --git a/engine/misc/extension_api_validation/4.6-stable/GH-104736.txt b/engine/misc/extension_api_validation/4.6-stable/GH-104736.txt new file mode 100644 index 0000000000..655436d4c1 --- /dev/null +++ b/engine/misc/extension_api_validation/4.6-stable/GH-104736.txt @@ -0,0 +1,6 @@ +GH-104736 +--------- +Validate extension JSON: Error: Field 'classes/PhysicsServer2D/methods/body_set_shape_as_one_way_collision/arguments': size changed value in new API, from 4 to 5. +Validate extension JSON: Error: Field 'classes/PhysicsServer2DExtension/methods/_body_set_shape_as_one_way_collision/arguments': size changed value in new API, from 4 to 5. + +Argument added. Compatibility methods registered. diff --git a/engine/misc/extension_api_validation/4.6-stable/GH-109004.txt b/engine/misc/extension_api_validation/4.6-stable/GH-109004.txt new file mode 100644 index 0000000000..ef6853d08d --- /dev/null +++ b/engine/misc/extension_api_validation/4.6-stable/GH-109004.txt @@ -0,0 +1,6 @@ +GH-109004 +--------- +Validate extension JSON: API was removed: classes/ImageTexture/methods/get_format +Validate extension JSON: API was removed: classes/PortableCompressedTexture2D/methods/get_format + +The above are now members of the base class Texture2D. diff --git a/engine/misc/extension_api_validation/4.6-stable/GH-110369.txt b/engine/misc/extension_api_validation/4.6-stable/GH-110369.txt new file mode 100644 index 0000000000..090b0d7e08 --- /dev/null +++ b/engine/misc/extension_api_validation/4.6-stable/GH-110369.txt @@ -0,0 +1,6 @@ +GH-110369 +-------------- +Validate extension JSON: Error: Field 'classes/AnimationNodeBlendSpace2D/methods/add_blend_point/arguments': size changed value in new API, from 3 to 4. +Validate extension JSON: Error: Field 'classes/AnimationNodeBlendSpace1D/methods/add_blend_point/arguments': size changed value in new API, from 3 to 4. + +Added "name" parameter, to functionally replace index reference for points. Compatibility methods registered. diff --git a/engine/misc/extension_api_validation/4.6-stable/GH-114355.txt b/engine/misc/extension_api_validation/4.6-stable/GH-114355.txt new file mode 100644 index 0000000000..6ee0a84f90 --- /dev/null +++ b/engine/misc/extension_api_validation/4.6-stable/GH-114355.txt @@ -0,0 +1,8 @@ +GH-114355 +--------- +Validate extension JSON: API was removed: classes/AudioEffectSpectrumAnalyzer/methods/set_tap_back_pos +Validate extension JSON: API was removed: classes/AudioEffectSpectrumAnalyzer/methods/get_tap_back_pos +Validate extension JSON: API was removed: classes/AudioEffectSpectrumAnalyzer/properties/tap_back_pos + +Removed this property because it caused buggy behavior for no discernible benefit. +Compatibility methods registered. diff --git a/engine/misc/extension_api_validation/4.6-stable/GH-115799.txt b/engine/misc/extension_api_validation/4.6-stable/GH-115799.txt new file mode 100644 index 0000000000..124bfa4c1a --- /dev/null +++ b/engine/misc/extension_api_validation/4.6-stable/GH-115799.txt @@ -0,0 +1,9 @@ +GH-115799 +--------- +Validate extension JSON: Error: Field 'classes/RenderingServer/methods/viewport_set_size/arguments': size changed value in new API, from 3 to 4. + +Optional argument added. Compatibility method registered. + +Validate extension JSON: Error: Field 'classes/Viewport/methods/is_using_xr': is_const changed value in new API, from false to true. + +Changed to const. Compatibility method registered. diff --git a/engine/misc/extension_api_validation/4.6-stable/GH-115946.txt b/engine/misc/extension_api_validation/4.6-stable/GH-115946.txt new file mode 100644 index 0000000000..5285cc9560 --- /dev/null +++ b/engine/misc/extension_api_validation/4.6-stable/GH-115946.txt @@ -0,0 +1,5 @@ +GH-115946 +--------- +Validate extension JSON: Error: Field 'classes/ZIPPacker/methods/start_file/arguments': size changed value in new API, from 1 to 3. + +Optional argument added. Compatibility method registered. diff --git a/engine/misc/extension_api_validation/4.6-stable/GH-116394.txt b/engine/misc/extension_api_validation/4.6-stable/GH-116394.txt new file mode 100644 index 0000000000..1e36d5c223 --- /dev/null +++ b/engine/misc/extension_api_validation/4.6-stable/GH-116394.txt @@ -0,0 +1,6 @@ +GH-116394 +--------- +Validate extension JSON: Error: Field 'classes/Animation/methods/get_length/return_value': meta changed value in new API, from "float" to "double". +Validate extension JSON: Error: Field 'classes/Animation/methods/set_length/arguments/0': meta changed value in new API, from "float" to "double". + +Return type and parameter changed from real_t to double to match internals. No compatibility methods registered because GDExtension FFI already uses double. diff --git a/engine/misc/extension_api_validation/4.6-stable/GH-116839.txt b/engine/misc/extension_api_validation/4.6-stable/GH-116839.txt new file mode 100644 index 0000000000..8457d18b04 --- /dev/null +++ b/engine/misc/extension_api_validation/4.6-stable/GH-116839.txt @@ -0,0 +1,6 @@ +GH-116839 +--------- +Validate extension JSON: Error: Field 'classes/Control/methods/get_accessibility_live/return_value': type changed value in new API, from "enum::DisplayServer.AccessibilityLiveMode" to "enum::AccessibilityServer.AccessibilityLiveMode". +Validate extension JSON: Error: Field 'classes/Control/methods/set_accessibility_live/arguments/0': type changed value in new API, from "enum::DisplayServer.AccessibilityLiveMode" to "enum::AccessibilityServer.AccessibilityLiveMode". + +Enum moved to AccessibilityServer. Compatibility methods registered. diff --git a/engine/misc/extension_api_validation/4.6-stable/GH-XXXXXX.txt b/engine/misc/extension_api_validation/4.6-stable/GH-XXXXXX.txt new file mode 100644 index 0000000000..179085d974 --- /dev/null +++ b/engine/misc/extension_api_validation/4.6-stable/GH-XXXXXX.txt @@ -0,0 +1,11 @@ +# This file should contain expected output of --validate-extension-api when run against the extension_api.json of the 4.6-stable tag. +# Only lines that start with "Validate extension JSON:" matter, everything else is considered a comment and ignored. +# They should instead be used to justify these changes and describe how users should work around these changes. + +GH-{PR_NUMBER} +-------------- + +Validate extension .... +Validate extension .... + +Description of changes. diff --git a/engine/misc/scripts/char_range_fetch.py b/engine/misc/scripts/char_range_fetch.py index 0f135b2b27..336d31b6db 100755 --- a/engine/misc/scripts/char_range_fetch.py +++ b/engine/misc/scripts/char_range_fetch.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # Script used to dump char ranges for specific properties from -# the Unicode Character Database to the `char_range.inc` file. +# the Unicode Character Database to the `char_range.cpp` file. # NOTE: This script is deliberately not integrated into the build system; # you should run it manually whenever you want to update the data. from __future__ import annotations @@ -16,7 +16,7 @@ if __name__ == "__main__": from methods import generate_copyright_header -URL: Final[str] = "https://www.unicode.org/Public/16.0.0/ucd/DerivedCoreProperties.txt" +URL: Final[str] = "https://www.unicode.org/Public/17.0.0/ucd/DerivedCoreProperties.txt" xid_start: list[tuple[int, int]] = [] @@ -89,7 +89,8 @@ def parse_unicode_data() -> None: def make_array(array_name: str, range_list: list[tuple[int, int]]) -> str: - result: str = f"\n\nconstexpr inline CharRange {array_name}[] = {{\n" + result: str = f"\n\nconst int {array_name}_size = {len(range_list)};\n" + result += f"const CharRange {array_name}[{array_name}_size] = {{\n" for start, end in range_list: result += f"\t{{ 0x{start:x}, 0x{end:x} }},\n" @@ -102,22 +103,16 @@ def make_array(array_name: str, range_list: list[tuple[int, int]]) -> str: def generate_char_range_inc() -> None: parse_unicode_data() - source: str = generate_copyright_header("char_range.inc") + source: str = generate_copyright_header("char_range.cpp") source += f""" // This file was generated using the `misc/scripts/char_range_fetch.py` script. -#pragma once - -#include "core/typedefs.h" +#include "core/string/char_utils.h" // Unicode Derived Core Properties -// Source: {URL} - -struct CharRange {{ -\tchar32_t start; -\tchar32_t end; -}};""" +// Source: {URL}\ +""" source += make_array("xid_start", xid_start) source += make_array("xid_continue", xid_continue) @@ -127,11 +122,11 @@ struct CharRange {{ source += "\n" - char_range_path: str = os.path.join(os.path.dirname(__file__), "../../core/string/char_range.inc") + char_range_path: str = os.path.join(os.path.dirname(__file__), "../../core/string/char_range.cpp") with open(char_range_path, "w", newline="\n") as f: f.write(source) - print("`char_range.inc` generated successfully.") + print("`char_range.cpp` generated successfully.") if __name__ == "__main__": diff --git a/engine/misc/scripts/check_ci_log.py b/engine/misc/scripts/check_ci_log.py index fb8f4a582d..171783e7b3 100755 --- a/engine/misc/scripts/check_ci_log.py +++ b/engine/misc/scripts/check_ci_log.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 +import os import sys if len(sys.argv) < 2: @@ -45,7 +46,10 @@ if file_contents.find("ERROR: LeakSanitizer:") != -1: # this possibility should also be handled as a potential error, even if # LeakSanitizer doesn't report anything -if file_contents.find("ObjectDB instances leaked at exit") != -1: +if ( + file_contents.find("ObjectDB instance was leaked at exit") != -1 + or file_contents.find("ObjectDB instances were leaked at exit") != -1 +): print("ERROR: Memory leak was found") sys.exit(54) @@ -57,6 +61,12 @@ if file_contents.find("Assertion failed") != -1: print("ERROR: Assertion failed in project, check execution log for more info") sys.exit(55) +if os.environ.get("GODOT_CHECK_CI_LOG_ALL_ERRORS"): + # If any occurrence of "ERROR:" is found in the log, we consider it a failure. + if file_contents.find("ERROR:") != -1: + print("ERROR: 'ERROR:' found in log and GODOT_CHECK_CI_LOG_ALL_ERRORS is set.") + sys.exit(56) + # For now Godot leaks a lot of rendering stuff so for now we just show info # about it and this needs to be re-enabled after fixing this memory leaks. diff --git a/engine/misc/scripts/install_accesskit.py b/engine/misc/scripts/install_accesskit.py new file mode 100755 index 0000000000..2d2789b728 --- /dev/null +++ b/engine/misc/scripts/install_accesskit.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 + +if __name__ != "__main__": + raise SystemExit(f'Utility script "{__file__}" should not be used as a module!') + +import os +import shutil +import sys +import urllib.request + +sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../")) + + +# Base Godot dependencies path +# If cross-compiling (no LOCALAPPDATA), we install in `bin` +deps_folder = os.getenv("LOCALAPPDATA") +if deps_folder: + deps_folder = os.path.join(deps_folder, "Godot", "build_deps") +else: + deps_folder = os.path.join("bin", "build_deps") + +# AccessKit +ac_version = "0.21.2" + +# Create dependencies folder +if not os.path.exists(deps_folder): + os.makedirs(deps_folder) + +ac_filename = "accesskit-c-" + ac_version + ".zip" +ac_archive = os.path.join(deps_folder, "accesskit.zip") +ac_folder = os.path.join(deps_folder, "accesskit") + +if os.path.isfile(ac_archive): + os.remove(ac_archive) + +print(f"Downloading AccessKit {ac_filename} ...") +urllib.request.urlretrieve( + f"https://github.com/godotengine/godot-accesskit-c-static/releases/download/{ac_version}/{ac_filename}", + ac_archive, +) +if os.path.exists(ac_folder): + print(f"Removing existing local AccessKit installation in {ac_folder} ...") + shutil.rmtree(ac_folder) +print(f"Extracting AccessKit {ac_filename} to {ac_folder} ...") +shutil.unpack_archive(ac_archive, deps_folder) +os.remove(ac_archive) +os.rename(os.path.join(deps_folder, "accesskit-c-" + ac_version), ac_folder) + +print("AccessKit installed successfully.\n") diff --git a/engine/misc/scripts/ucaps_fetch.py b/engine/misc/scripts/ucaps_fetch.py index 84d33abd17..d173dfa17c 100755 --- a/engine/misc/scripts/ucaps_fetch.py +++ b/engine/misc/scripts/ucaps_fetch.py @@ -16,7 +16,7 @@ if __name__ == "__main__": from methods import generate_copyright_header -URL: Final[str] = "https://www.unicode.org/Public/16.0.0/ucd/UnicodeData.txt" +URL: Final[str] = "https://www.unicode.org/Public/17.0.0/ucd/UnicodeData.txt" lower_to_upper: list[tuple[str, str]] = [] diff --git a/engine/misc/scripts/unicode_ranges_fetch.py b/engine/misc/scripts/unicode_ranges_fetch.py index 40c7b874ca..e84ca7545c 100755 --- a/engine/misc/scripts/unicode_ranges_fetch.py +++ b/engine/misc/scripts/unicode_ranges_fetch.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # Script used to dump char ranges from -# the Unicode Character Database to the `char_range.inc` file. +# the Unicode Character Database to the `unicode_ranges.inc` file. # NOTE: This script is deliberately not integrated into the build system; # you should run it manually whenever you want to update the data. from __future__ import annotations @@ -16,7 +16,7 @@ if __name__ == "__main__": from methods import generate_copyright_header -URL: Final[str] = "https://www.unicode.org/Public/16.0.0/ucd/Blocks.txt" +URL: Final[str] = "https://www.unicode.org/Public/17.0.0/ucd/Blocks.txt" ranges: list[tuple[str, str, str]] = [] diff --git a/engine/misc/scripts/validate_codeowners.py b/engine/misc/scripts/validate_codeowners.py new file mode 100755 index 0000000000..8b74e1777c --- /dev/null +++ b/engine/misc/scripts/validate_codeowners.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 + +if __name__ != "__main__": + raise SystemExit(f'Utility script "{__file__}" should not be used as a module!') + +import argparse +import re +import subprocess +import sys + +sys.path.insert(0, "./") + +try: + from methods import print_error, print_info +except ImportError: + raise SystemExit(f"Utility script {__file__} must be run from repository root!") + + +def glob_to_regex(glob: str) -> re.Pattern[str]: + """Convert a CODEOWNERS glob to a RegEx pattern.""" + + # Heavily inspired by: https://github.com/hmarr/codeowners/blob/main/match.go + + # Handle specific edgecases first. + if "***" in glob: + raise SyntaxError("Pattern cannot contain three consecutive asterisks") + if glob == "/": + raise SyntaxError('Standalone "/" will not match anything') + if not glob: + raise ValueError("Empty pattern") + + segments = glob.split("/") + if not segments[0]: + # Leading slash; relative to root. + segments = segments[1:] + else: + # Check for single-segment pattern, which matches relative to any descendent path. + # This is equivalent to a leading `**/`. + if len(segments) == 1 or (len(segments) == 2 and not segments[1]): + if segments[0] != "**": + segments.insert(0, "**") + + if len(segments) > 1 and not segments[-1]: + # A trailing slash is equivalent to `/**`. + segments[-1] = "**" + + last_index = len(segments) - 1 + need_slash = False + pattern = r"\A" + + for index, segment in enumerate(segments): + if segment == "**": + if index == 0 and index == last_index: + pattern += r".+" # Pattern is just `**`; match everything. + elif index == 0: + pattern += r"(?:.+/)?" # Pattern starts with `**`; match any leading path segment. + need_slash = False + elif index == last_index: + pattern += r"/.*" # Pattern ends with `**`; match any trailing path segment. + else: + pattern += r"(?:/.+)?" # Pattern contains `**`; match zero or more path segments. + need_slash = True + + elif segment == "*": + if need_slash: + pattern += "/" + # Regular wildcard; match any non-separator characters. + pattern += r"[^/]+" + need_slash = True + + else: + if need_slash: + pattern += "/" + + escape = False + for char in segment: + if escape: + escape = False + pattern += re.escape(char) + continue + elif char == "\\": + escape = True + elif char == "*": + # Multi-character wildcard. + pattern += r"[^/]*" + elif char == "?": + # Single-character wildcard. + pattern += r"[^/]" + else: + # Regular character + pattern += re.escape(char) + + if index == last_index: + pattern += r"(?:/.*)?" # No trailing slash; match descendent paths. + need_slash = True + + pattern += r"\Z" + return re.compile(pattern) + + +RE_CODEOWNERS = re.compile(r"^(?P[^#](?:\\ |[^\s])+) +(?P(?:[^#][^\s]+ ?)+)") + + +def parse_codeowners() -> list[tuple[re.Pattern[str], list[str]]]: + codeowners = [] + with open(".github/CODEOWNERS", encoding="utf-8", newline="\n") as file: + for line in reversed(file.readlines()): # Lower items have higher precedence. + if match := RE_CODEOWNERS.match(line): + codeowners.append((glob_to_regex(match["code"]), match["owners"].split())) + return codeowners + + +def main() -> int: + parser = argparse.ArgumentParser(description="Utility script for validating CODEOWNERS assignment.") + parser.add_argument("files", nargs="*", help="A list of files to validate. If excluded, checks all owned files.") + parser.add_argument("-u", "--unowned", action="store_true", help="Only output files without an owner.") + args = parser.parse_args() + + files: list[str] = args.files + if not files: + files = subprocess.run(["git", "ls-files"], text=True, capture_output=True).stdout.splitlines() + + ret = 0 + codeowners = parse_codeowners() + + for file in files: + matched = False + for code, owners in codeowners: + if code.match(file): + matched = True + if not args.unowned: + print_info(f"{file}: {owners}") + break + if not matched: + print_error(f"{file}: ") + ret += 1 + + return ret + + +try: + raise SystemExit(main()) +except KeyboardInterrupt: + import os + import signal + + signal.signal(signal.SIGINT, signal.SIG_DFL) + os.kill(os.getpid(), signal.SIGINT) diff --git a/engine/misc/scripts/validate_extension_api.sh b/engine/misc/scripts/validate_extension_api.sh index ffcbd053c1..b17f1edc1c 100755 --- a/engine/misc/scripts/validate_extension_api.sh +++ b/engine/misc/scripts/validate_extension_api.sh @@ -69,7 +69,7 @@ while read -r dir; do get_expected_output "$dir" # Download the reference extension_api.json - wget -nv --retry-on-http-error=503 --tries=5 --timeout=60 -cO "$reference_file" "https://raw.githubusercontent.com/godotengine/godot-cpp/godot-$reference_tag/gdextension/extension_api.json" || has_problems=1 + wget -nv --retry-on-http-error=503 --tries=5 --timeout=60 -cO "$reference_file" "https://raw.githubusercontent.com/godotengine/godot-headers/godot-$reference_tag/extension_api.json" || has_problems=1 # Validate the current API against the reference "$1" --headless --validate-extension-api "$reference_file" 2>&1 | tee "$validate" | awk '!/^Validate extension JSON:/' - || true # Collect the expected and actual validation errors diff --git a/engine/modules/SCsub b/engine/modules/SCsub index e987dde0e5..69dfadfa31 100644 --- a/engine/modules/SCsub +++ b/engine/modules/SCsub @@ -52,7 +52,6 @@ for name, path in env.module_list.items(): # Generate header to be included in `tests/test_main.cpp` to run module-specific tests. if env["tests"]: - env.Append(CPPDEFINES=["TESTS_ENABLED"]) env.CommandNoCache("modules_tests.gen.h", test_headers, env.Run(modules_builders.modules_tests_builder)) # libmodules.a with only register_module_types. diff --git a/engine/modules/betsy/image_compress_betsy.cpp b/engine/modules/betsy/image_compress_betsy.cpp index e689d7dd2a..63d6c59b03 100644 --- a/engine/modules/betsy/image_compress_betsy.cpp +++ b/engine/modules/betsy/image_compress_betsy.cpp @@ -30,8 +30,6 @@ #include "image_compress_betsy.h" -#include "core/config/project_settings.h" - #include "betsy_bc1.h" #include "alpha_stitch.glsl.gen.h" @@ -39,7 +37,22 @@ #include "bc4.glsl.gen.h" #include "bc6h.glsl.gen.h" #include "rgb_to_rgba.glsl.gen.h" + +#include "core/config/project_settings.h" +#include "core/object/callable_mp.h" +#include "core/os/os.h" #include "servers/display/display_server.h" +#include "servers/rendering/rendering_context_driver.h" +#include "servers/rendering/rendering_device.h" +#include "servers/rendering/rendering_device_binds.h" +#include "servers/rendering/rendering_server.h" + +#if defined(VULKAN_ENABLED) +#include "drivers/vulkan/rendering_context_driver_vulkan.h" +#endif +#if defined(METAL_ENABLED) +#include "drivers/metal/rendering_context_driver_metal.h" +#endif static Mutex betsy_mutex; static BetsyCompressor *betsy = nullptr; diff --git a/engine/modules/betsy/image_compress_betsy.h b/engine/modules/betsy/image_compress_betsy.h index 15b5889497..1eae808643 100644 --- a/engine/modules/betsy/image_compress_betsy.h +++ b/engine/modules/betsy/image_compress_betsy.h @@ -32,18 +32,11 @@ #include "core/io/image.h" #include "core/object/worker_thread_pool.h" -#include "core/os/thread.h" #include "core/templates/command_queue_mt.h" -#include "servers/rendering/rendering_device_binds.h" -#include "servers/rendering/rendering_server_default.h" - -#if defined(VULKAN_ENABLED) -#include "drivers/vulkan/rendering_context_driver_vulkan.h" -#endif -#if defined(METAL_ENABLED) -#include "drivers/metal/rendering_context_driver_metal.h" -#endif +class RDShaderFile; +class RenderingDevice; +class RenderingContextDriver; enum BetsyFormat { BETSY_FORMAT_BC1, diff --git a/engine/modules/camera/camera_android.cpp b/engine/modules/camera/camera_android.cpp index 5fbeda0ee4..e1b907381c 100644 --- a/engine/modules/camera/camera_android.cpp +++ b/engine/modules/camera/camera_android.cpp @@ -72,7 +72,7 @@ public: #ifndef IF_EQUAL_RETURN #define MAKE_FORMAT_CONST(suffix) AIMAGE_FORMAT_##suffix -#define IF_EQUAL_RETURN(param, val) \ +#define IF_EQUAL_RETURN(param, val) \ if (MAKE_FORMAT_CONST(val) == param) \ return #val #endif diff --git a/engine/modules/csg/csg_shape.cpp b/engine/modules/csg/csg_shape.cpp index 13226a8395..914937ab37 100644 --- a/engine/modules/csg/csg_shape.cpp +++ b/engine/modules/csg/csg_shape.cpp @@ -30,18 +30,27 @@ #include "csg_shape.h" +#include "core/config/engine.h" +#include "core/math/geometry_2d.h" +#include "core/object/callable_mp.h" +#include "core/object/class_db.h" +#include "scene/main/scene_tree.h" +#include "scene/resources/3d/navigation_mesh_source_geometry_data_3d.h" +#include "scene/resources/navigation_mesh.h" +#include "servers/rendering/rendering_server.h" + #ifdef DEV_ENABLED #include "core/io/json.h" #endif // DEV_ENABLED -#include "core/math/geometry_2d.h" -#include "scene/resources/3d/navigation_mesh_source_geometry_data_3d.h" -#include "scene/resources/navigation_mesh.h" + #ifndef NAVIGATION_3D_DISABLED #include "servers/navigation_3d/navigation_server_3d.h" #endif // NAVIGATION_3D_DISABLED #include +#include // FLT_EPSILON + #ifndef NAVIGATION_3D_DISABLED Callable CSGShape3D::_navmesh_source_geometry_parsing_callback; RID CSGShape3D::_navmesh_source_geometry_parser; @@ -195,6 +204,26 @@ void CSGShape3D::set_collision_priority(real_t p_priority) { real_t CSGShape3D::get_collision_priority() const { return collision_priority; } + +void CSGShape3D::set_autosmooth(bool p_smooth) { + autosmooth = p_smooth; + _make_dirty(); + notify_property_list_changed(); +} + +bool CSGShape3D::is_autosmooth() const { + return autosmooth; +} + +void CSGShape3D::set_smoothing_angle(const float p_angle) { + smoothing_angle = p_angle; + _make_dirty(); +} + +float CSGShape3D::get_smoothing_angle() const { + return smoothing_angle; +} + #endif // PHYSICS_3D_DISABLED bool CSGShape3D::is_root_shape() const { @@ -576,10 +605,14 @@ void CSGShape3D::update_shape() { CSGBrush *n = _get_brush(); ERR_FAIL_NULL_MSG(n, "Cannot get CSGBrush."); - AHashMap vec_map; - Vector face_count; face_count.resize(n->materials.size() + 1); + + Vector smooth_faces; + LocalVector smooth_vertex; + smooth_faces.resize(n->faces.size()); + smooth_vertex.resize(n->faces.size() * 3); + for (int i = 0; i < face_count.size(); i++) { face_count.write[i] = 0; } @@ -589,21 +622,70 @@ void CSGShape3D::update_shape() { ERR_CONTINUE(mat < -1 || mat >= face_count.size()); int idx = mat == -1 ? face_count.size() - 1 : mat; - if (n->faces[i].smooth) { - Plane p(n->faces[i].vertices[0], n->faces[i].vertices[1], n->faces[i].vertices[2]); + Plane p(n->faces[i].vertices[0], n->faces[i].vertices[1], n->faces[i].vertices[2]); - for (int j = 0; j < 3; j++) { - Vector3 v = n->faces[i].vertices[j]; - Vector3 *vec = vec_map.getptr(v); - if (vec) { - *vec += p.normal; - } else { - vec_map.insert(v, p.normal); + smooth_faces.write[i] = p.normal; + // Not sure if resize populates the LocalVector. + smooth_vertex[i * 3 + 0] = Vector3(p.normal); + smooth_vertex[i * 3 + 1] = Vector3(p.normal); + smooth_vertex[i * 3 + 2] = Vector3(p.normal); + // We could use a AHashMap Vector3, int to store the number of connections of each vertex position and end the loop earlier. But I'm not sure if the performance gains outweigh the cost. + face_count.write[idx]++; + } + + if (autosmooth) { + // We could add a `use_groups` property later to only apply autosmooth on smooth faces or respect smoothing groups in some way. + if (smoothing_angle > 0.1) { + float smooth_angle_rad = Math::cos(Math::deg_to_rad(smoothing_angle)); + for (int i = 0; i < smooth_faces.size(); i++) { + for (int k = 0; k < 3; k++) { + int curr_vert = i * 3 + k; + Vector3 vert_a = n->faces[i].vertices[k]; + for (int j = i + 1; j < smooth_faces.size(); j++) { + // Compare the angles of faces instead of vertices. + if (smooth_faces[i].dot(smooth_faces[j]) > smooth_angle_rad) { + for (int h = 0; h < 3; h++) { + Vector3 vert_b = n->faces[j].vertices[h]; + if (vert_a == vert_b) { + int curr_j = j * 3 + h; + smooth_vertex[curr_vert] += smooth_faces[j]; + smooth_vertex[curr_j] += smooth_faces[i]; + break; + } + } + } + } + smooth_vertex[curr_vert].normalize(); + } + } + } + } else { + for (int i = 0; i < smooth_faces.size(); i++) { + bool face_is_smooth = n->faces[i].smooth; + if (face_is_smooth) { + for (int k = 0; k < 3; k++) { + Vector3 vert_a = n->faces[i].vertices[k]; + int curr_vert = i * 3 + k; + // Skip the other vertices of the face as they will never occupy the same position. + for (int j = i + 1; j < smooth_faces.size(); j++) { + // Preparing for when and if we replace Vector of bool for Vector of int smoothing groups. for now, face_is_smooth is always true. + if (face_is_smooth == n->faces[j].smooth) { + for (int h = 0; h < 3; h++) { + Vector3 vert_b = n->faces[j].vertices[h]; + if (vert_a == vert_b) { + int curr_j = j * 3 + h; + smooth_vertex[curr_vert] += smooth_faces[j]; + smooth_vertex[curr_j] += smooth_faces[i]; + // Skip the other 2 vertices as only one vertex of each face can connect with one vertex of other face. + break; + } + } + } + } + smooth_vertex[curr_vert].normalize(); } } } - - face_count.write[idx]++; } Vector surfaces; @@ -647,19 +729,12 @@ void CSGShape3D::update_shape() { int last = surfaces[idx].last_added; - Plane p(n->faces[i].vertices[0], n->faces[i].vertices[1], n->faces[i].vertices[2]); + int face_pos_i = i * 3; for (int j = 0; j < 3; j++) { Vector3 v = n->faces[i].vertices[j]; - Vector3 normal = p.normal; - - if (n->faces[i].smooth) { - Vector3 *ptr = vec_map.getptr(v); - if (ptr) { - normal = ptr->normalized(); - } - } + Vector3 normal = smooth_vertex[face_pos_i + j]; if (n->faces[i].invert) { normal = -normal; @@ -728,6 +803,8 @@ void CSGShape3D::update_shape() { set_base(root_mesh->get_rid()); + update_gizmos(); + #ifndef PHYSICS_3D_DISABLED _update_collision_faces(); #endif // PHYSICS_3D_DISABLED @@ -953,6 +1030,19 @@ void CSGShape3D::_validate_property(PropertyInfo &p_property) const { if (!Engine::get_singleton()->is_editor_hint()) { return; } + + if (p_property.name == "smoothing_angle") { + if (!autosmooth || (is_inside_tree() && !is_root_shape())) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } + } + + if (p_property.name == "autosmooth") { + if (is_inside_tree() && !is_root_shape()) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } + } + bool is_collision_prefixed = p_property.name.begins_with("collision_"); if ((is_collision_prefixed || p_property.name.begins_with("use_collision")) && is_inside_tree() && !is_root_shape()) { //hide collision if not root @@ -1037,6 +1127,15 @@ void CSGShape3D::_bind_methods() { ClassDB::bind_method(D_METHOD("bake_static_mesh"), &CSGShape3D::bake_static_mesh); + ClassDB::bind_method(D_METHOD("set_autosmooth", "autosmooth"), &CSGShape3D::set_autosmooth); + ClassDB::bind_method(D_METHOD("is_autosmooth"), &CSGShape3D::is_autosmooth); + + ClassDB::bind_method(D_METHOD("set_smoothing_angle", "smoothing_angle"), &CSGShape3D::set_smoothing_angle); + ClassDB::bind_method(D_METHOD("get_smoothing_angle"), &CSGShape3D::get_smoothing_angle); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autosmooth"), "set_autosmooth", "is_autosmooth"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "smoothing_angle", PROPERTY_HINT_RANGE, "0,180,0.1,degrees"), "set_smoothing_angle", "get_smoothing_angle"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "operation", PROPERTY_HINT_ENUM, "Union,Intersection,Subtraction"), "set_operation", "get_operation"); #ifndef DISABLE_DEPRECATED ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_RANGE, "0.000001,1,0.000001,suffix:m", PROPERTY_USAGE_NONE), "set_snap", "get_snap"); diff --git a/engine/modules/csg/csg_shape.h b/engine/modules/csg/csg_shape.h index da3ee688fc..f285aa3ceb 100644 --- a/engine/modules/csg/csg_shape.h +++ b/engine/modules/csg/csg_shape.h @@ -41,6 +41,7 @@ #include "thirdparty/misc/mikktspace.h" +class Mesh; class NavigationMesh; class NavigationMeshSourceGeometryData3D; @@ -67,6 +68,9 @@ private: bool last_visible = false; float snap = 0.001; + bool autosmooth = false; + float smoothing_angle = 50.0; + #ifndef PHYSICS_3D_DISABLED bool use_collision = false; uint32_t collision_layer = 1; @@ -158,6 +162,12 @@ public: void set_collision_priority(real_t p_priority); real_t get_collision_priority() const; + void set_autosmooth(bool p_smooth); + bool is_autosmooth() const; + + void set_smoothing_angle(const float p_angle); + float get_smoothing_angle() const; + #ifndef DISABLE_DEPRECATED void set_snap(float p_snap); float get_snap() const; diff --git a/engine/modules/csg/doc_classes/CSGShape3D.xml b/engine/modules/csg/doc_classes/CSGShape3D.xml index 848c7b7235..e52c3ebee9 100644 --- a/engine/modules/csg/doc_classes/CSGShape3D.xml +++ b/engine/modules/csg/doc_classes/CSGShape3D.xml @@ -74,6 +74,10 @@ + + Enables automatic smoothing. This overrides any smoothing on the CSG node and instead uses [member smoothing_angle] to calculate normals based on the angle between faces. + Children of a [CSGCombiner3D] node will be treated as a single mesh. + Calculate tangents for the CSG shape which allows the use of normal and height maps. This is only applied on the root shape, this setting is ignored on any child. Setting this to [code]false[/code] can speed up shape generation slightly. @@ -91,6 +95,10 @@ The operation that is performed on this shape. This is ignored for the first CSG child node as the operation is between this node and the previous child of this nodes parent. + + When autosmooth is enabled, faces with an angle between them greater than this will be smoothed, while faces with a smaller angle will remain sharp. + Note: An angle lower than 0.1 will cause all smoothing to be disabled, this can be used to increase performance. + This property does nothing. diff --git a/engine/modules/csg/editor/csg_gizmos.cpp b/engine/modules/csg/editor/csg_gizmos.cpp index d26e0ad0da..0ff2417313 100644 --- a/engine/modules/csg/editor/csg_gizmos.cpp +++ b/engine/modules/csg/editor/csg_gizmos.cpp @@ -30,6 +30,9 @@ #include "csg_gizmos.h" +#include "core/math/geometry_3d.h" +#include "core/object/callable_mp.h" +#include "core/object/class_db.h" #include "editor/editor_node.h" #include "editor/editor_undo_redo_manager.h" #include "editor/scene/3d/gizmos/gizmo_3d_helper.h" @@ -40,6 +43,7 @@ #include "scene/3d/physics/collision_shape_3d.h" #include "scene/gui/dialogs.h" #include "scene/gui/menu_button.h" +#include "scene/main/scene_tree.h" void CSGShapeEditor::_node_removed(Node *p_node) { if (p_node == node) { @@ -416,17 +420,14 @@ void CSGShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { Ref handles_material = get_material("handles"); p_gizmo->add_lines(lines, material); - p_gizmo->add_collision_segments(lines); - if (cs->is_root_shape()) { - Array csg_meshes = cs->get_meshes(); - if (csg_meshes.size() == 2) { - Ref csg_mesh = csg_meshes[1]; - if (csg_mesh.is_valid()) { - p_gizmo->add_collision_triangles(csg_mesh->generate_triangle_mesh()); - } - } - } + Ref collision_mesh; + collision_mesh.instantiate(); + Array collision_array; + collision_array.resize(Mesh::ARRAY_MAX); + collision_array[Mesh::ARRAY_VERTEX] = faces; + collision_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, collision_array); + p_gizmo->add_collision_triangles(collision_mesh->generate_triangle_mesh()); if (p_gizmo->is_selected()) { // Draw a translucent representation of the CSG node diff --git a/engine/modules/csg/register_types.cpp b/engine/modules/csg/register_types.cpp index b157e32952..57347ef946 100644 --- a/engine/modules/csg/register_types.cpp +++ b/engine/modules/csg/register_types.cpp @@ -32,6 +32,8 @@ #include "csg_shape.h" +#include "core/object/class_db.h" + #ifdef TOOLS_ENABLED #include "editor/csg_gizmos.h" #endif diff --git a/engine/modules/csg/tests/test_csg.h b/engine/modules/csg/tests/test_csg.h index 73860051c4..1512645adf 100644 --- a/engine/modules/csg/tests/test_csg.h +++ b/engine/modules/csg/tests/test_csg.h @@ -32,6 +32,7 @@ #include "../csg.h" #include "../csg_shape.h" +#include "scene/main/scene_tree.h" #include "tests/test_macros.h" diff --git a/engine/modules/dds/dds_enums.h b/engine/modules/dds/dds_enums.h index aa5fcf9d35..176c6c5c96 100644 --- a/engine/modules/dds/dds_enums.h +++ b/engine/modules/dds/dds_enums.h @@ -164,6 +164,8 @@ enum DDSFormat { DDS_LUMINANCE, DDS_LUMINANCE_ALPHA, DDS_LUMINANCE_ALPHA_4, + DDS_RG8, + DDS_R8, DDS_MAX }; @@ -223,4 +225,6 @@ static const DDSFormatInfo dds_format_info[DDS_MAX] = { { "GRAYSCALE", false, 1, 1, Image::FORMAT_L8 }, { "GRAYSCALE_ALPHA", false, 1, 2, Image::FORMAT_LA8 }, { "GRAYSCALE_ALPHA_4", false, 1, 1, Image::FORMAT_LA8 }, + { "RG8", false, 1, 2, Image::FORMAT_RG8 }, + { "R8", false, 1, 1, Image::FORMAT_R8 }, }; diff --git a/engine/modules/dds/register_types.cpp b/engine/modules/dds/register_types.cpp index fc831b9f59..e0681c4c40 100644 --- a/engine/modules/dds/register_types.cpp +++ b/engine/modules/dds/register_types.cpp @@ -33,6 +33,7 @@ #include "image_saver_dds.h" #include "texture_loader_dds.h" +#include "core/object/class_db.h" #include "scene/resources/texture.h" static Ref resource_loader_dds; diff --git a/engine/modules/dds/tests/test_dds.h b/engine/modules/dds/tests/test_dds.h index 3029bc9c97..90507590e0 100644 --- a/engine/modules/dds/tests/test_dds.h +++ b/engine/modules/dds/tests/test_dds.h @@ -34,8 +34,8 @@ #include "core/config/project_settings.h" #include "core/io/dir_access.h" +#include "core/io/file_access.h" #include "core/io/image.h" -#include "tests/core/config/test_project_settings.h" #include "tests/test_macros.h" #include "tests/test_utils.h" diff --git a/engine/modules/dds/texture_loader_dds.cpp b/engine/modules/dds/texture_loader_dds.cpp index 8cc3f54ae6..8db4f6a9fe 100644 --- a/engine/modules/dds/texture_loader_dds.cpp +++ b/engine/modules/dds/texture_loader_dds.cpp @@ -34,6 +34,7 @@ #include "core/io/file_access.h" #include "core/io/file_access_memory.h" +#include "core/object/class_db.h" #include "scene/resources/image_texture.h" DDSFormat _dxgi_to_dds_format(uint32_t p_dxgi_format) { @@ -75,7 +76,9 @@ DDSFormat _dxgi_to_dds_format(uint32_t p_dxgi_format) { case DXGI_R32_FLOAT: { return DDS_R32F; } - case DXGI_R8_UNORM: + case DXGI_R8_UNORM: { + return DDS_R8; + } case DXGI_A8_UNORM: { return DDS_LUMINANCE; } @@ -89,7 +92,7 @@ DDSFormat _dxgi_to_dds_format(uint32_t p_dxgi_format) { return DDS_R16I; } case DXGI_R8G8_UNORM: { - return DDS_LUMINANCE_ALPHA; + return DDS_RG8; } case DXGI_R9G9B9E5: { return DDS_RGB9E5; @@ -648,6 +651,10 @@ static Vector> _dds_load_images_from_buffer(Ref p_f, DDSF r_dds_format = DDS_BGRX8; } else if (format_rgb_bits == 32 && format_red_mask == 0xff && format_green_mask == 0xff00 && format_blue_mask == 0xff0000) { r_dds_format = DDS_RGBX8; + } else if (format_rgb_bits == 8 && format_red_mask == 0xff && format_green_mask == 0 && format_blue_mask == 0) { + r_dds_format = DDS_R8; + } else if (format_rgb_bits == 16 && format_red_mask == 0xff && format_green_mask == 0xff00 && format_blue_mask == 0) { + r_dds_format = DDS_RG8; } } diff --git a/engine/modules/enet/enet_connection.cpp b/engine/modules/enet/enet_connection.cpp index 9b764b1ad9..a88ed5c43a 100644 --- a/engine/modules/enet/enet_connection.cpp +++ b/engine/modules/enet/enet_connection.cpp @@ -34,6 +34,7 @@ #include "core/io/compression.h" #include "core/io/ip.h" +#include "core/object/class_db.h" #include "core/variant/typed_array.h" void ENetConnection::broadcast(enet_uint8 p_channel, ENetPacket *p_packet) { diff --git a/engine/modules/enet/enet_multiplayer_peer.cpp b/engine/modules/enet/enet_multiplayer_peer.cpp index da41378bc9..837a2cdb4e 100644 --- a/engine/modules/enet/enet_multiplayer_peer.cpp +++ b/engine/modules/enet/enet_multiplayer_peer.cpp @@ -30,6 +30,8 @@ #include "enet_multiplayer_peer.h" +#include "core/object/class_db.h" + void ENetMultiplayerPeer::set_target_peer(int p_peer) { target_peer = p_peer; } @@ -477,7 +479,7 @@ void ENetMultiplayerPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_host"), &ENetMultiplayerPeer::get_host); ClassDB::bind_method(D_METHOD("get_peer", "id"), &ENetMultiplayerPeer::get_peer); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "host", PROPERTY_HINT_RESOURCE_TYPE, "ENetConnection", PROPERTY_USAGE_NONE), "", "get_host"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "host", PROPERTY_HINT_RESOURCE_TYPE, ENetConnection::get_class_static(), PROPERTY_USAGE_NONE), "", "get_host"); } ENetMultiplayerPeer::ENetMultiplayerPeer() { diff --git a/engine/modules/enet/enet_packet_peer.cpp b/engine/modules/enet/enet_packet_peer.cpp index 9ec68465a5..36a00d512e 100644 --- a/engine/modules/enet/enet_packet_peer.cpp +++ b/engine/modules/enet/enet_packet_peer.cpp @@ -30,6 +30,8 @@ #include "enet_packet_peer.h" +#include "core/object/class_db.h" + void ENetPacketPeer::peer_disconnect(int p_data) { ERR_FAIL_NULL(peer); enet_peer_disconnect(peer, p_data); @@ -182,6 +184,9 @@ int ENetPacketPeer::get_packet_flags() const { void ENetPacketPeer::_on_disconnect() { if (peer) { +#ifdef GODOT_ENET + enet_peer_socket_destroy(peer); +#endif peer->data = nullptr; } peer = nullptr; @@ -256,6 +261,9 @@ void ENetPacketPeer::_bind_methods() { ENetPacketPeer::ENetPacketPeer(ENetPeer *p_peer) { peer = p_peer; peer->data = this; +#ifdef GODOT_ENET + enet_peer_socket_bind(peer); +#endif } ENetPacketPeer::~ENetPacketPeer() { diff --git a/engine/modules/enet/register_types.cpp b/engine/modules/enet/register_types.cpp index 5787f9c2da..6c87de7956 100644 --- a/engine/modules/enet/register_types.cpp +++ b/engine/modules/enet/register_types.cpp @@ -35,6 +35,7 @@ #include "enet_packet_peer.h" #include "core/error/error_macros.h" +#include "core/object/class_db.h" static bool enet_ok = false; diff --git a/engine/modules/fbx/editor/editor_scene_importer_fbx2gltf.cpp b/engine/modules/fbx/editor/editor_scene_importer_fbx2gltf.cpp index db2b4f97b6..8aabc216cf 100644 --- a/engine/modules/fbx/editor/editor_scene_importer_fbx2gltf.cpp +++ b/engine/modules/fbx/editor/editor_scene_importer_fbx2gltf.cpp @@ -31,6 +31,7 @@ #include "editor_scene_importer_fbx2gltf.h" #include "core/config/project_settings.h" +#include "core/os/os.h" #include "editor/settings/editor_settings.h" #include "editor_scene_importer_ufbx.h" diff --git a/engine/modules/fbx/fbx_document.cpp b/engine/modules/fbx/fbx_document.cpp index 7ad89d1cbc..ddca283c4e 100644 --- a/engine/modules/fbx/fbx_document.cpp +++ b/engine/modules/fbx/fbx_document.cpp @@ -30,6 +30,7 @@ #include "fbx_document.h" +#include "core/config/engine.h" #include "core/config/project_settings.h" #include "core/crypto/crypto_core.h" #include "core/io/config_file.h" diff --git a/engine/modules/fbx/fbx_state.cpp b/engine/modules/fbx/fbx_state.cpp index 0b42a86533..37e2b23e10 100644 --- a/engine/modules/fbx/fbx_state.cpp +++ b/engine/modules/fbx/fbx_state.cpp @@ -30,6 +30,8 @@ #include "fbx_state.h" +#include "core/object/class_db.h" + void FBXState::_bind_methods() { ClassDB::bind_method(D_METHOD("get_allow_geometry_helper_nodes"), &FBXState::get_allow_geometry_helper_nodes); ClassDB::bind_method(D_METHOD("set_allow_geometry_helper_nodes", "allow"), &FBXState::set_allow_geometry_helper_nodes); diff --git a/engine/modules/fbx/register_types.cpp b/engine/modules/fbx/register_types.cpp index c1a2bd5bfc..e246551913 100644 --- a/engine/modules/fbx/register_types.cpp +++ b/engine/modules/fbx/register_types.cpp @@ -33,10 +33,14 @@ #include "../gltf/extensions/gltf_document_extension_convert_importer_mesh.h" #include "fbx_document.h" +#include "core/config/engine.h" +#include "core/object/class_db.h" + #ifdef TOOLS_ENABLED #include "editor/editor_scene_importer_fbx2gltf.h" #include "editor/editor_scene_importer_ufbx.h" +#include "core/config/engine.h" #include "core/config/project_settings.h" #include "editor/editor_node.h" @@ -55,8 +59,8 @@ static void _editor_init() { #endif // TOOLS_ENABLED #define FBX_REGISTER_DOCUMENT_EXTENSION(m_doc_ext_class) \ - Ref extension_##m_doc_ext_class; \ - extension_##m_doc_ext_class.instantiate(); \ + Ref extension_##m_doc_ext_class; \ + extension_##m_doc_ext_class.instantiate(); \ FBXDocument::register_gltf_document_extension(extension_##m_doc_ext_class); void initialize_fbx_module(ModuleInitializationLevel p_level) { diff --git a/engine/modules/gdscript/SCsub b/engine/modules/gdscript/SCsub index 8f50bf9588..f39b1a0fe9 100644 --- a/engine/modules/gdscript/SCsub +++ b/engine/modules/gdscript/SCsub @@ -25,5 +25,6 @@ if env.editor_build: if env["tests"]: + # TODO: Handle test creation magic without needing to pass macro. env_gdscript.Append(CPPDEFINES=["TESTS_ENABLED"]) env_gdscript.add_source_files(env.modules_sources, "./tests/*.cpp") diff --git a/engine/modules/gdscript/config.py b/engine/modules/gdscript/config.py index ecd33a5dac..25278db61b 100644 --- a/engine/modules/gdscript/config.py +++ b/engine/modules/gdscript/config.py @@ -11,7 +11,10 @@ def get_doc_classes(): return [ "@GDScript", "GDScript", + "GDScriptLanguageProtocol", "GDScriptSyntaxHighlighter", + "GDScriptTextDocument", + "GDScriptWorkspace", ] diff --git a/engine/modules/gdscript/doc_classes/GDScriptLanguageProtocol.xml b/engine/modules/gdscript/doc_classes/GDScriptLanguageProtocol.xml new file mode 100644 index 0000000000..2553f65790 --- /dev/null +++ b/engine/modules/gdscript/doc_classes/GDScriptLanguageProtocol.xml @@ -0,0 +1,69 @@ + + + + GDScript language server. + + + Provides access to certain features that are implemented in the language server. + [b]Note:[/b] This class is not a language server client that can be used to access LSP functionality. It only provides access to a limited set of features that is implemented using the same technical foundation as the language server. + + + + + + + + Returns the language server's [GDScriptTextDocument] instance. + + + + + + Returns the language server's [GDScriptWorkspace] instance. + + + + + + + + + + + + + + + + + + Returns [code]true[/code] if the language server was initialized by a language server client, [code]false[/code] otherwise. + + + + + + Returns [code]true[/code] if the language server is providing the smart resolve feature, [code]false[/code] otherwise. The feature can be configured through the editor settings. + + + + + + + + + + + + + + + + + + + + + + + diff --git a/engine/modules/gdscript/doc_classes/GDScriptTextDocument.xml b/engine/modules/gdscript/doc_classes/GDScriptTextDocument.xml new file mode 100644 index 0000000000..de40161650 --- /dev/null +++ b/engine/modules/gdscript/doc_classes/GDScriptTextDocument.xml @@ -0,0 +1,139 @@ + + + + Document related language server functionality. + + + Provides language server functionality related to documents. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/engine/modules/gdscript/doc_classes/GDScriptWorkspace.xml b/engine/modules/gdscript/doc_classes/GDScriptWorkspace.xml new file mode 100644 index 0000000000..883ddb17f4 --- /dev/null +++ b/engine/modules/gdscript/doc_classes/GDScriptWorkspace.xml @@ -0,0 +1,67 @@ + + + + Workspace related language server functionality. + + + Provides language server functionality related to the workspace. + + + + + + + + + + + + + + + + + + + + + + + Returns the interface of the script in a machine-readable format. + + + + + + + Converts a URI to a file path. + + + + + + + Converts a file path to a URI. + + + + + + + + + + + + + + + + + + + + + + + diff --git a/engine/modules/gdscript/editor/gdscript_highlighter.cpp b/engine/modules/gdscript/editor/gdscript_highlighter.cpp index a6f03409d5..9d38d6cef0 100644 --- a/engine/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/engine/modules/gdscript/editor/gdscript_highlighter.cpp @@ -35,6 +35,7 @@ #include "core/config/project_settings.h" #include "core/core_constants.h" +#include "core/object/class_db.h" #include "editor/settings/editor_settings.h" #include "editor/themes/editor_theme_manager.h" #include "scene/gui/text_edit.h" diff --git a/engine/modules/gdscript/editor/gdscript_highlighter.h b/engine/modules/gdscript/editor/gdscript_highlighter.h index e6116d74fb..0cec1a52ed 100644 --- a/engine/modules/gdscript/editor/gdscript_highlighter.h +++ b/engine/modules/gdscript/editor/gdscript_highlighter.h @@ -30,7 +30,7 @@ #pragma once -#include "editor/script/script_editor_plugin.h" +#include "editor/script/syntax_highlighters.h" class GDScriptSyntaxHighlighter : public EditorSyntaxHighlighter { GDCLASS(GDScriptSyntaxHighlighter, EditorSyntaxHighlighter) diff --git a/engine/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp b/engine/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp index 7026797c5f..9e7a54cc21 100644 --- a/engine/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp +++ b/engine/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp @@ -450,6 +450,7 @@ GDScriptEditorTranslationParserPlugin::GDScriptEditorTranslationParserPlugin() { first_arg_patterns.insert("add_radio_check_item"); first_arg_patterns.insert("add_separator"); first_arg_patterns.insert("add_submenu_item"); + first_arg_patterns.insert("add_submenu_node_item"); second_arg_patterns.insert("set_tab_title"); second_arg_patterns.insert("add_icon_check_item"); diff --git a/engine/modules/gdscript/gdscript.cpp b/engine/modules/gdscript/gdscript.cpp index 8b133961ae..ab4d08f9e4 100644 --- a/engine/modules/gdscript/gdscript.cpp +++ b/engine/modules/gdscript/gdscript.cpp @@ -30,6 +30,8 @@ #include "gdscript.h" +#include "core/object/callable_mp.h" +#include "core/object/class_db.h" #include "gdscript_analyzer.h" #include "gdscript_cache.h" #include "gdscript_compiler.h" @@ -644,7 +646,7 @@ void GDScript::_update_exports_down(bool p_base_exports_changed) { return; } - HashSet copy = inheriters_cache; //might get modified + HashSet copy(inheriters_cache); //might get modified for (const ObjectID &E : copy) { Object *id = ObjectDB::get_instance(E); @@ -820,10 +822,10 @@ Error GDScript::reload(bool p_keep_state) { } if (err) { if (EngineDebugger::is_active()) { - GDScriptLanguage::get_singleton()->debug_break_parse(_get_debug_path(), parser.get_errors().front()->get().line, "Parser Error: " + parser.get_errors().front()->get().message); + GDScriptLanguage::get_singleton()->debug_break_parse(_get_debug_path(), parser.get_errors().front()->get().start_line, "Parser Error: " + parser.get_errors().front()->get().message); } // TODO: Show all error messages. - _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_errors().front()->get().line, ("Parse Error: " + parser.get_errors().front()->get().message).utf8().get_data(), false, ERR_HANDLER_SCRIPT); + _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_errors().front()->get().start_line, ("Parse Error: " + parser.get_errors().front()->get().message).utf8().get_data(), false, ERR_HANDLER_SCRIPT); reloading = false; return ERR_PARSE_ERROR; } @@ -833,12 +835,12 @@ Error GDScript::reload(bool p_keep_state) { if (err) { if (EngineDebugger::is_active()) { - GDScriptLanguage::get_singleton()->debug_break_parse(_get_debug_path(), parser.get_errors().front()->get().line, "Parser Error: " + parser.get_errors().front()->get().message); + GDScriptLanguage::get_singleton()->debug_break_parse(_get_debug_path(), parser.get_errors().front()->get().start_line, "Parser Error: " + parser.get_errors().front()->get().message); } const List::Element *e = parser.get_errors().front(); while (e != nullptr) { - _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), e->get().line, ("Parse Error: " + e->get().message).utf8().get_data(), false, ERR_HANDLER_SCRIPT); + _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), e->get().start_line, ("Parse Error: " + e->get().message).utf8().get_data(), false, ERR_HANDLER_SCRIPT); e = e->next(); } reloading = false; @@ -927,10 +929,6 @@ const Variant GDScript::get_rpc_config() const { return rpc_config; } -void GDScript::unload_static() const { - GDScriptCache::remove_script(fully_qualified_name); -} - Variant GDScript::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { GDScript *top = this; while (top) { @@ -945,9 +943,19 @@ Variant GDScript::callp(const StringName &p_method, const Variant **p_args, int top = top->base.ptr(); } - //none found, regular + { + Variant ret = Script::callp(p_method, p_args, p_argcount, r_error); + if (r_error.error != Callable::CallError::CALL_ERROR_INVALID_METHOD) { + return ret; + } + } - return Script::callp(p_method, p_args, p_argcount, r_error); + if (native.is_valid()) { + return native->callp(p_method, p_args, p_argcount, r_error); + } + + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); } bool GDScript::_get(const StringName &p_name, Variant &r_ret) const { @@ -1095,7 +1103,9 @@ void GDScript::set_path(const String &p_path, bool p_take_over) { String old_path = path; path = p_path; path_valid = true; - GDScriptCache::move_script(old_path, p_path); + if (is_root_script()) { + GDScriptCache::move_script(old_path, p_path); + } for (KeyValue> &kv : subclasses) { kv.value->set_path(p_path, p_take_over); diff --git a/engine/modules/gdscript/gdscript.h b/engine/modules/gdscript/gdscript.h index a0be255205..4522d2d3d7 100644 --- a/engine/modules/gdscript/gdscript.h +++ b/engine/modules/gdscript/gdscript.h @@ -339,8 +339,6 @@ public: virtual const Variant get_rpc_config() const override; - void unload_static() const; - #ifdef TOOLS_ENABLED virtual bool is_placeholder_fallback_enabled() const override { return placeholder_fallback_enabled; } #endif @@ -600,7 +598,6 @@ public: virtual Ref