diff --git a/.github/actions/godot-cpp-build/action.yml b/.github/actions/godot-cpp-build/action.yml new file mode 100644 index 000000000000..9596c5a8fd78 --- /dev/null +++ b/.github/actions/godot-cpp-build/action.yml @@ -0,0 +1,39 @@ +name: Build godot-cpp +description: Build godot-cpp with the provided options. + +env: + GODOT_CPP_BRANCH: 4.3 + +inputs: + bin: + description: Path to the Godot binary. + required: true + type: string + scons-flags: + description: Additional SCons flags. + type: string + scons-cache: + description: The SCons cache path. + default: ${{ github.workspace }}/.scons_cache/ + type: string + +runs: + using: composite + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + repository: godotengine/godot-cpp + ref: ${{ env.GODOT_CPP_BRANCH }} + path: godot-cpp + + - name: Extract API + shell: sh + run: ${{ inputs.bin }} --headless --dump-gdextension-interface --dump-extension-api + + - name: SCons Build + shell: sh + env: + SCONS_CACHE: ${{ inputs.scons-cache }} + run: scons --directory=./godot-cpp/test "gdextension_dir=${{ github.workspace }}" ${{ inputs.scons-flags }} diff --git a/.github/workflows/android_builds.yml b/.github/workflows/android_builds.yml index 20c65e128c2e..bb4ee25f7c22 100644 --- a/.github/workflows/android_builds.yml +++ b/.github/workflows/android_builds.yml @@ -12,6 +12,7 @@ jobs: build-android: runs-on: ubuntu-24.04 name: ${{ matrix.name }} + timeout-minutes: 60 strategy: fail-fast: false matrix: diff --git a/.github/workflows/godot_cpp_test.yml b/.github/workflows/godot_cpp_test.yml deleted file mode 100644 index e4013f04938e..000000000000 --- a/.github/workflows/godot_cpp_test.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: 🪲 Godot CPP -on: - workflow_call: - -# Global Settings -env: - # Used for the cache key. Add version suffix to force clean build. - GODOT_BASE_BRANCH: master - # Used for the godot-cpp checkout. - GODOT_CPP_BRANCH: 4.3 - -jobs: - godot-cpp-tests: - runs-on: ubuntu-24.04 - name: Build and test Godot CPP - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - sparse-checkout: | - .github - misc/utility/problem-matchers.json - - - name: Checkout godot-cpp - uses: actions/checkout@v4 - with: - submodules: recursive - repository: godotengine/godot-cpp - ref: ${{ env.GODOT_CPP_BRANCH }} - path: godot-cpp - - - name: Setup Python and SCons - uses: ./.github/actions/godot-deps - - - name: Download GDExtension interface and API dump - uses: ./.github/actions/download-artifact - with: - name: godot-api-dump - path: ./godot-cpp/gdextension - - # TODO: Enable caching when godot-cpp has proper cache limiting. - - # - name: Restore Godot build cache - # uses: ./.github/actions/godot-cache-restore - # with: - # cache-name: godot-cpp - # continue-on-error: true - - - name: Build godot-cpp test extension - run: scons --directory=./godot-cpp/test target=template_debug dev_build=yes verbose=yes - - # - name: Save Godot build cache - # uses: ./.github/actions/godot-cache-save - # with: - # cache-name: godot-cpp - # continue-on-error: true diff --git a/.github/workflows/ios_builds.yml b/.github/workflows/ios_builds.yml index b1b96e56eb7a..f5a4f755dedd 100644 --- a/.github/workflows/ios_builds.yml +++ b/.github/workflows/ios_builds.yml @@ -12,6 +12,7 @@ jobs: ios-template: runs-on: macos-latest name: Template (target=template_release) + timeout-minutes: 60 steps: - name: Checkout diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml index e2e6e57de127..e978f9c8203d 100644 --- a/.github/workflows/linux_builds.yml +++ b/.github/workflows/linux_builds.yml @@ -17,6 +17,7 @@ jobs: # Stay one LTS before latest to increase portability of Linux artifacts. runs-on: ubuntu-22.04 name: ${{ matrix.name }} + timeout-minutes: 120 strategy: fail-fast: false matrix: @@ -32,7 +33,9 @@ jobs: proj-conv: true api-compat: true artifact: true - cache-limit: 1 + # Validate godot-cpp compatibility on one arbitrary editor build. + godot-cpp: true + cache-limit: 2 - name: Editor with doubles and GCC sanitizers (target=editor, tests=yes, dev_build=yes, scu_build=yes, precision=double, use_asan=yes, use_ubsan=yes, linker=gold) cache-name: linux-editor-double-sanitizers @@ -43,8 +46,6 @@ jobs: build-mono: false tests: true proj-test: true - # Generate an API dump for godot-cpp tests. - api-dump: true # Skip 2GiB artifact speeding up action. artifact: false cache-limit: 7 @@ -157,6 +158,13 @@ jobs: tests: ${{ matrix.tests }} scons-cache-limit: ${{ matrix.cache-limit }} + - name: Compilation (godot-cpp) + uses: ./.github/actions/godot-cpp-build + if: matrix.godot-cpp + with: + bin: ${{ matrix.bin }} + scons-flags: target=template_debug dev_build=yes verbose=yes + - name: Save Godot build cache uses: ./.github/actions/godot-cache-save with: @@ -186,12 +194,6 @@ jobs: with: name: ${{ matrix.cache-name }} - - name: Dump Godot API - uses: ./.github/actions/godot-api-dump - if: matrix.api-dump - with: - bin: ${{ matrix.bin }} - - name: Unit tests if: matrix.tests run: | diff --git a/.github/workflows/macos_builds.yml b/.github/workflows/macos_builds.yml index 7e302d98759f..3923b918f49e 100644 --- a/.github/workflows/macos_builds.yml +++ b/.github/workflows/macos_builds.yml @@ -12,6 +12,7 @@ jobs: build-macos: runs-on: macos-latest name: ${{ matrix.name }} + timeout-minutes: 120 strategy: fail-fast: false matrix: diff --git a/.github/workflows/runner.yml b/.github/workflows/runner.yml index 937f0e895d79..c88d44a098d6 100644 --- a/.github/workflows/runner.yml +++ b/.github/workflows/runner.yml @@ -44,15 +44,3 @@ jobs: name: 🌐 Web needs: static-checks uses: ./.github/workflows/web_builds.yml - - # Third stage: Run auxiliary tests using build artifacts from previous jobs. - - # Can be turned off for PRs that intentionally break compat with godot-cpp, - # until both the upstream PR and the matching godot-cpp changes are merged. - godot-cpp-test: - name: 🪲 Godot CPP - # This can be changed to depend on another platform, if we decide to use it for - # godot-cpp instead. Make sure to move the .github/actions/godot-api-dump step - # appropriately. - needs: linux-build - uses: ./.github/workflows/godot_cpp_test.yml diff --git a/.github/workflows/static_checks.yml b/.github/workflows/static_checks.yml index 65344a32c4ae..ad7f7aa6ceb5 100644 --- a/.github/workflows/static_checks.yml +++ b/.github/workflows/static_checks.yml @@ -6,6 +6,7 @@ jobs: static-checks: name: Code style, file formatting, and docs runs-on: ubuntu-24.04 + timeout-minutes: 30 steps: - name: Checkout uses: actions/checkout@v4 diff --git a/.github/workflows/web_builds.yml b/.github/workflows/web_builds.yml index 2f253fb82ffb..2f08d8c2f956 100644 --- a/.github/workflows/web_builds.yml +++ b/.github/workflows/web_builds.yml @@ -13,6 +13,7 @@ jobs: web-template: runs-on: ubuntu-24.04 name: ${{ matrix.name }} + timeout-minutes: 60 strategy: fail-fast: false matrix: diff --git a/.github/workflows/windows_builds.yml b/.github/workflows/windows_builds.yml index 843d39dcb23c..8d5ff5ec38ec 100644 --- a/.github/workflows/windows_builds.yml +++ b/.github/workflows/windows_builds.yml @@ -15,6 +15,7 @@ jobs: # Windows 10 with latest image runs-on: windows-latest name: ${{ matrix.name }} + timeout-minutes: 120 strategy: fail-fast: false matrix: diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index 406e23b58d18..34e95d2f4131 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -1,42 +1,40 @@ -# Exhaustive licensing information for files in the Godot Engine repository -# ========================================================================= -# -# This file aims at documenting the copyright and license for every source -# file in the Godot Engine repository, and especially outline the files -# whose license differs from the MIT/Expat license used by Godot Engine. -# -# It is written as a machine-readable format following the debian/copyright -# specification. Globbing patterns (e.g. "Files: *") mean that they affect -# all corresponding files (also recursively in subfolders), apart from those -# with a more explicit copyright statement. -# -# Licenses are given with their debian/copyright short name (or SPDX identifier -# if no standard short name exists) and are all included in plain text at the -# end of this file (in alphabetical order). -# -# Disclaimer for thirdparty libraries: -# ------------------------------------ -# -# Licensing details for thirdparty libraries in the 'thirdparty/' directory -# are given in summarized form, i.e. with only the "main" license described -# in the library's license statement. Different licenses of single files or -# code snippets in thirdparty libraries are not documented here. -# For example: -# Files: ./thirdparty/zlib/ -# Copyright: 1995-2017, Jean-loup Gailly and Mark Adler -# License: Zlib -# The exact copyright for each file in that library *may* differ, and some -# files or code snippets might be distributed under other compatible licenses -# (e.g. a public domain dedication), but as far as Godot Engine is concerned -# the library is considered as a whole under the Zlib license. -# -# Note: When linking dynamically against thirdparty libraries instead of -# building them into the Godot binary, you may remove the corresponding -# license details from this file. - ------------------------------------------------------------------------ - Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Comment: + Exhaustive licensing information for files in the Godot Engine repository + ========================================================================= + . + This file aims at documenting the copyright and license for every source + file in the Godot Engine repository, and especially outline the files + whose license differs from the MIT/Expat license used by Godot Engine. + . + It is written as a machine-readable format following the debian/copyright + specification. Globbing patterns (e.g. "Files: *") mean that they affect + all corresponding files (also recursively in subfolders), apart from those + with a more explicit copyright statement. + . + Licenses are given with their debian/copyright short name (or SPDX identifier + if no standard short name exists) and are all included in plain text at the + end of this file (in alphabetical order). + . + Disclaimer for thirdparty libraries: + ------------------------------------ + . + Licensing details for thirdparty libraries in the 'thirdparty/' directory + are given in summarized form, i.e. with only the "main" license described + in the library's license statement. Different licenses of single files or + code snippets in thirdparty libraries are not documented here. + For example: + Files: thirdparty/zlib/* + Copyright: 1995-2017, Jean-loup Gailly and Mark Adler + License: Zlib + The exact copyright for each file in that library *may* differ, and some + files or code snippets might be distributed under other compatible licenses + (e.g. a public domain dedication), but as far as Godot Engine is concerned + the library is considered as a whole under the Zlib license. + . + Note: When linking dynamically against thirdparty libraries instead of + building them into the Godot binary, you may remove the corresponding + license details from this file. Upstream-Name: Godot Engine Upstream-Contact: Rémi Verschelde Source: https://github.com/godotengine/godot @@ -47,240 +45,240 @@ Copyright: 2014-present, Godot Engine contributors 2007-2014, Juan Linietsky, Ariel Manzur License: Expat -Files: ./icon.png - ./icon.svg - ./logo.png - ./logo.svg +Files: icon.png + icon.svg + logo.png + logo.svg Comment: Godot Engine logo Copyright: 2017, Andrea Calabró License: CC-BY-4.0 -Files: ./core/math/convex_hull.cpp - ./core/math/convex_hull.h +Files: core/math/convex_hull.cpp + core/math/convex_hull.h Comment: Bullet Continuous Collision Detection and Physics Library Copyright: 2011, Ole Kniemeyer, MAXON, www.maxon.net 2014-present, Godot Engine contributors 2007-2014, Juan Linietsky, Ariel Manzur License: Expat and Zlib -Files: ./modules/godot_physics_2d/godot_joints_2d.cpp +Files: modules/godot_physics_2d/godot_joints_2d.cpp Comment: Chipmunk2D Joint Constraints Copyright: 2007, Scott Lembcke License: Expat -Files: ./modules/godot_physics_3d/gjk_epa.cpp - ./modules/godot_physics_3d/joints/godot_generic_6dof_joint_3d.cpp - ./modules/godot_physics_3d/joints/godot_generic_6dof_joint_3d.h - ./modules/godot_physics_3d/joints/godot_hinge_joint_3d.cpp - ./modules/godot_physics_3d/joints/godot_hinge_joint_3d_sw.h - ./modules/godot_physics_3d/joints/godot_jacobian_entry_3d_sw.h - ./modules/godot_physics_3d/joints/godot_pin_joint_3d.cpp - ./modules/godot_physics_3d/joints/godot_pin_joint_3d.h - ./modules/godot_physics_3d/joints/godot_slider_joint_3d.cpp - ./modules/godot_physics_3d/joints/godot_slider_joint_3d.h - ./modules/godot_physics_3d/godot_soft_body_3d.cpp - ./modules/godot_physics_3d/godot_soft_body_3d.h - ./modules/godot_physics_3d/godot_shape_3d.cpp - ./modules/godot_physics_3d/godot_shape_3d.h +Files: modules/godot_physics_3d/gjk_epa.cpp + modules/godot_physics_3d/joints/godot_generic_6dof_joint_3d.cpp + modules/godot_physics_3d/joints/godot_generic_6dof_joint_3d.h + modules/godot_physics_3d/joints/godot_hinge_joint_3d.cpp + modules/godot_physics_3d/joints/godot_hinge_joint_3d_sw.h + modules/godot_physics_3d/joints/godot_jacobian_entry_3d_sw.h + modules/godot_physics_3d/joints/godot_pin_joint_3d.cpp + modules/godot_physics_3d/joints/godot_pin_joint_3d.h + modules/godot_physics_3d/joints/godot_slider_joint_3d.cpp + modules/godot_physics_3d/joints/godot_slider_joint_3d.h + modules/godot_physics_3d/godot_soft_body_3d.cpp + modules/godot_physics_3d/godot_soft_body_3d.h + modules/godot_physics_3d/godot_shape_3d.cpp + modules/godot_physics_3d/godot_shape_3d.h Comment: Bullet Continuous Collision Detection and Physics Library Copyright: 2003-2008, Erwin Coumans 2014-present, Godot Engine contributors 2007-2014, Juan Linietsky, Ariel Manzur License: Expat and Zlib -Files: ./modules/godot_physics_3d/godot_collision_solver_3d_sat.cpp +Files: modules/godot_physics_3d/godot_collision_solver_3d_sat.cpp Comment: Open Dynamics Engine Copyright: 2001-2003, Russell L. Smith, Alen Ladavac, Nguyen Binh License: BSD-3-clause -Files: ./modules/godot_physics_3d/joints/godot_cone_twist_joint_3d.cpp - ./modules/godot_physics_3d/joints/godot_cone_twist_joint_3d.h +Files: modules/godot_physics_3d/joints/godot_cone_twist_joint_3d.cpp + modules/godot_physics_3d/joints/godot_cone_twist_joint_3d.h Comment: Bullet Continuous Collision Detection and Physics Library Copyright: 2007, Starbreeze Studios 2014-present, Godot Engine contributors 2007-2014, Juan Linietsky, Ariel Manzur License: Expat and Zlib -Files: ./modules/jolt_physics/spaces/jolt_temp_allocator.cpp +Files: modules/jolt_physics/spaces/jolt_temp_allocator.cpp Comment: Jolt Physics Copyright: 2021, Jorrit Rouwe 2014-present, Godot Engine contributors 2007-2014, Juan Linietsky, Ariel Manzur License: Expat -Files: ./modules/lightmapper_rd/lm_compute.glsl +Files: modules/lightmapper_rd/lm_compute.glsl Comment: Joint Non-Local Means (JNLM) denoiser Copyright: 2020, Manuel Prandini 2014-present, Godot Engine contributors 2007-2014, Juan Linietsky, Ariel Manzur License: Expat -Files: ./platform/android/java/editor/src/main/java/com/android/* - ./platform/android/java/lib/aidl/com/android/* - ./platform/android/java/lib/res/layout/status_bar_ongoing_event_progress_bar.xml - ./platform/android/java/lib/src/com/google/android/* - ./platform/android/java/lib/src/org/godotengine/godot/input/InputManagerCompat.java - ./platform/android/java/lib/src/org/godotengine/godot/input/InputManagerV16.java +Files: platform/android/java/editor/src/main/java/com/android/* + platform/android/java/lib/aidl/com/android/* + platform/android/java/lib/res/layout/status_bar_ongoing_event_progress_bar.xml + platform/android/java/lib/src/com/google/android/* + platform/android/java/lib/src/org/godotengine/godot/input/InputManagerCompat.java + platform/android/java/lib/src/org/godotengine/godot/input/InputManagerV16.java Comment: The Android Open Source Project Copyright: 2008-2016, The Android Open Source Project 2002, Google, Inc. License: Apache-2.0 -Files: ./platform/android/java/lib/src/org/godotengine/godot/utils/ProcessPhoenix.java +Files: platform/android/java/lib/src/org/godotengine/godot/utils/ProcessPhoenix.java Comment: ProcessPhoenix Copyright: 2015, Jake Wharton License: Apache-2.0 -Files: ./scene/animation/easing_equations.h +Files: scene/animation/easing_equations.h Comment: Robert Penner's Easing Functions Copyright: 2001, Robert Penner 2014-present, Godot Engine contributors 2007-2014, Juan Linietsky, Ariel Manzur License: Expat -Files: ./servers/rendering/renderer_rd/shaders/ss_effects_downsample.glsl - ./servers/rendering/renderer_rd/shaders/ssao_blur.glsl - ./servers/rendering/renderer_rd/shaders/ssao_importance_map.glsl - ./servers/rendering/renderer_rd/shaders/ssao_interleave.glsl - ./servers/rendering/renderer_rd/shaders/ssao.glsl - ./servers/rendering/renderer_rd/shaders/ssil_blur.glsl - ./servers/rendering/renderer_rd/shaders/ssil_importance_map.glsl - ./servers/rendering/renderer_rd/shaders/ssil_interleave.glsl - ./servers/rendering/renderer_rd/shaders/ssil.glsl +Files: servers/rendering/renderer_rd/shaders/ss_effects_downsample.glsl + servers/rendering/renderer_rd/shaders/ssao_blur.glsl + servers/rendering/renderer_rd/shaders/ssao_importance_map.glsl + servers/rendering/renderer_rd/shaders/ssao_interleave.glsl + servers/rendering/renderer_rd/shaders/ssao.glsl + servers/rendering/renderer_rd/shaders/ssil_blur.glsl + servers/rendering/renderer_rd/shaders/ssil_importance_map.glsl + servers/rendering/renderer_rd/shaders/ssil_interleave.glsl + servers/rendering/renderer_rd/shaders/ssil.glsl Comment: Intel ASSAO and related files Copyright: 2016, Intel Corporation License: Expat -Files: ./servers/rendering/renderer_rd/shaders/effects/taa_resolve.glsl +Files: servers/rendering/renderer_rd/shaders/effects/taa_resolve.glsl Comment: Temporal Anti-Aliasing resolve implementation Copyright: 2016, Panos Karabelas License: Expat -Files: ./thirdparty/amd-fsr/ +Files: thirdparty/amd-fsr/* Comment: AMD FidelityFX Super Resolution Copyright: 2021, Advanced Micro Devices, Inc. License: Expat -Files: ./thirdparty/amd-fsr2/ +Files: thirdparty/amd-fsr2/* Comment: AMD FidelityFX Super Resolution 2 Copyright: 2022-2023, Advanced Micro Devices, Inc. License: Expat -Files: ./thirdparty/angle/ +Files: thirdparty/angle/* Comment: ANGLE Copyright: 2018, The ANGLE Project Authors. License: BSD-3-clause -Files: ./thirdparty/astcenc/ +Files: thirdparty/astcenc/* Comment: Arm ASTC Encoder Copyright: 2011-2024, Arm Limited License: Apache-2.0 -Files: ./thirdparty/basis_universal/ +Files: thirdparty/basis_universal/* Comment: Basis Universal Copyright: 2019-2024, Binomial LLC. License: Apache-2.0 -Files: ./thirdparty/betsy/ +Files: thirdparty/betsy/* Comment: Betsy Copyright: 2020-2022, Matias N. Goldberg License: Expat -Files: ./thirdparty/brotli/ +Files: thirdparty/brotli/* Comment: Brotli Copyright: 2009, 2010, 2013-2016 by the Brotli Authors. License: Expat -Files: ./thirdparty/certs/ca-certificates.crt +Files: thirdparty/certs/ca-certificates.crt Comment: CA certificates Copyright: Mozilla Contributors License: MPL-2.0 -Files: ./thirdparty/clipper2/ +Files: thirdparty/clipper2/* Comment: Clipper2 Copyright: 2010-2024, Angus Johnson License: BSL-1.0 -Files: ./thirdparty/cvtt/ +Files: thirdparty/cvtt/* Comment: Convection Texture Tools Stand-Alone Kernels Copyright: 2018, Eric Lasota 2018, Microsoft Corp. License: Expat -Files: ./thirdparty/d3d12ma/ +Files: thirdparty/d3d12ma/* Comment: D3D12 Memory Allocator Copyright: 2019-2022 Advanced Micro Devices, Inc. License: Expat -Files: ./thirdparty/directx_headers/ +Files: thirdparty/directx_headers/* Comment: DirectX Headers Copyright: Microsoft Corporation License: Expat -Files: ./thirdparty/doctest/ +Files: thirdparty/doctest/* Comment: doctest Copyright: 2016-2023, Viktor Kirilov License: Expat -Files: ./thirdparty/embree/ +Files: thirdparty/embree/* Comment: Embree Copyright: 2009-2021 Intel Corporation License: Apache-2.0 -Files: ./thirdparty/enet/ +Files: thirdparty/enet/* Comment: ENet Copyright: 2002-2024, Lee Salzman License: Expat -Files: ./thirdparty/etcpak/ +Files: thirdparty/etcpak/* Comment: etcpak Copyright: 2013-2022, Bartosz Taudul License: BSD-3-clause -Files: ./thirdparty/fonts/DroidSans*.woff2 +Files: thirdparty/fonts/DroidSans*.woff2 Comment: DroidSans font Copyright: 2008, The Android Open Source Project License: Apache-2.0 -Files: ./thirdparty/fonts/JetBrainsMono_Regular.woff2 +Files: thirdparty/fonts/JetBrainsMono_Regular.woff2 Comment: JetBrains Mono font Copyright: 2020, JetBrains s.r.o. License: OFL-1.1 -Files: ./thirdparty/fonts/NotoSans*.woff2 +Files: thirdparty/fonts/NotoSans*.woff2 Comment: Noto Sans font Copyright: 2012, Google Inc. License: OFL-1.1 -Files: ./thirdparty/fonts/Vazirmatn*.woff2 +Files: thirdparty/fonts/Vazirmatn*.woff2 Comment: Vazirmatn font Copyright: 2015, The Vazirmatn Project Authors. License: OFL-1.1 -Files: ./thirdparty/freetype/ +Files: thirdparty/freetype/* Comment: The FreeType Project Copyright: 1996-2023, David Turner, Robert Wilhelm, and Werner Lemberg. License: FTL -Files: ./thirdparty/glad/ +Files: thirdparty/glad/* Comment: glad Copyright: 2013-2022, David Herberth 2013-2020, The Khronos Group Inc. License: CC0-1.0 and Apache-2.0 -Files: ./thirdparty/glslang/ +Files: thirdparty/glslang/* Comment: glslang Copyright: 2015-2020, Google, Inc. 2014-2020, The Khronos Group Inc 2002, NVIDIA Corporation. License: glslang -Files: ./thirdparty/graphite/ +Files: thirdparty/graphite/* Comment: Graphite engine Copyright: 2010, SIL International License: Expat -Files: ./thirdparty/harfbuzz/ +Files: thirdparty/harfbuzz/* Comment: HarfBuzz text shaping library Copyright: 2010-2022, Google, Inc. 2015-2020, Ebrahim Byagowi @@ -301,38 +299,38 @@ Copyright: 2010-2022, Google, Inc. 2013-2015, Alexei Podtelezhnikov License: HarfBuzz -Files: ./thirdparty/icu4c/ +Files: thirdparty/icu4c/* Comment: International Components for Unicode Copyright: 2016-2024, Unicode, Inc. License: Unicode -Files: ./thirdparty/jolt_physics/ +Files: thirdparty/jolt_physics/* Comment: Jolt Physics Copyright: 2021, Jorrit Rouwe License: Expat -Files: ./thirdparty/jpeg-compressor/ +Files: thirdparty/jpeg-compressor/* Comment: jpeg-compressor Copyright: 2012, Rich Geldreich License: public-domain or Apache-2.0 -Files: ./thirdparty/libbacktrace/ +Files: thirdparty/libbacktrace/* Comment: libbacktrace Copyright: 2012-2021, Free Software Foundation, Inc. License: BSD-3-clause -Files: ./thirdparty/libktx/ +Files: thirdparty/libktx/* Comment: KTX Copyright: 2013-2020, Mark Callow 2010-2020 The Khronos Group, Inc. License: Apache-2.0 -Files: ./thirdparty/libogg/ +Files: thirdparty/libogg/* Comment: OggVorbis Copyright: 2002, Xiph.org Foundation License: BSD-3-clause -Files: ./thirdparty/libpng/ +Files: thirdparty/libpng/* Comment: libpng Copyright: 1995-2025, The PNG Reference Library Authors. 2018-2025, Cosmin Truta. @@ -341,223 +339,223 @@ Copyright: 1995-2025, The PNG Reference Library Authors. 1995-1996, Guy Eric Schalnat, Group 42, Inc. License: Zlib -Files: ./thirdparty/libtheora/ +Files: thirdparty/libtheora/* Comment: OggTheora Copyright: 2002-2009, Xiph.org Foundation License: BSD-3-clause -Files: ./thirdparty/libvorbis/ +Files: thirdparty/libvorbis/* Comment: OggVorbis Copyright: 2002-2015, Xiph.org Foundation License: BSD-3-clause -Files: ./thirdparty/libwebp/ +Files: thirdparty/libwebp/* Comment: WebP codec Copyright: 2010, Google Inc. License: BSD-3-clause -Files: ./thirdparty/manifold/ +Files: thirdparty/manifold/* Comment: Manifold Copyright: 2020-2024, The Manifold Authors License: Apache-2.0 -Files: ./thirdparty/mbedtls/ +Files: thirdparty/mbedtls/* Comment: Mbed TLS Copyright: The Mbed TLS Contributors License: Apache-2.0 -Files: ./thirdparty/meshoptimizer/ +Files: thirdparty/meshoptimizer/* Comment: meshoptimizer Copyright: 2016-2024, Arseny Kapoulkine License: Expat -Files: ./thirdparty/mingw-std-threads/ +Files: thirdparty/mingw-std-threads/* Comment: mingw-std-threads Copyright: 2016, Mega Limited License: BSD-2-clause -Files: ./thirdparty/minimp3/ +Files: thirdparty/minimp3/* Comment: MiniMP3 Copyright: lieff License: CC0-1.0 -Files: ./thirdparty/miniupnpc/ +Files: thirdparty/miniupnpc/* Comment: MiniUPnP Project Copyright: 2005-2024, Thomas Bernard License: BSD-3-clause -Files: ./thirdparty/minizip/ +Files: thirdparty/minizip/* Comment: MiniZip Copyright: 1998-2010, Gilles Vollant 2007-2008, Even Rouault 2009-2010, Mathias Svensson License: Zlib -Files: ./thirdparty/misc/bcdec.h +Files: thirdparty/misc/bcdec.h Comment: bcdec Copyright: 2022, Sergii Kudlai License: Expat -Files: ./thirdparty/misc/cubemap_coeffs.h +Files: thirdparty/misc/cubemap_coeffs.h Comment: Fast Filtering of Reflection Probes Copyright: 2016, Activision Publishing, Inc. License: Expat -Files: ./thirdparty/misc/fastlz.c - ./thirdparty/misc/fastlz.h +Files: thirdparty/misc/fastlz.c + thirdparty/misc/fastlz.h Comment: FastLZ Copyright: 2005-2020, Ariya Hidayat License: Expat -Files: ./thirdparty/misc/ifaddrs-android.cc - ./thirdparty/misc/ifaddrs-android.h +Files: thirdparty/misc/ifaddrs-android.cc + thirdparty/misc/ifaddrs-android.h Comment: libjingle Copyright: 2012-2013, Google Inc. License: BSD-3-clause -Files: ./thirdparty/misc/mikktspace.c - ./thirdparty/misc/mikktspace.h +Files: thirdparty/misc/mikktspace.c + thirdparty/misc/mikktspace.h Comment: Tangent Space Normal Maps implementation Copyright: 2011, Morten S. Mikkelsen License: Zlib -Files: ./thirdparty/misc/ok_color.h - ./thirdparty/misc/ok_color_shader.h +Files: thirdparty/misc/ok_color.h + thirdparty/misc/ok_color_shader.h Comment: OK Lab color space Copyright: 2021, Björn Ottosson License: Expat -Files: ./thirdparty/noise/FastNoiseLite.h +Files: thirdparty/noise/FastNoiseLite.h Comment: FastNoise Lite Copyright: 2023, Jordan Peck and contributors License: Expat -Files: ./thirdparty/misc/pcg.cpp - ./thirdparty/misc/pcg.h +Files: thirdparty/misc/pcg.cpp + thirdparty/misc/pcg.h Comment: Minimal PCG32 implementation Copyright: 2014, M.E. O'Neill License: Apache-2.0 -Files: ./thirdparty/misc/polypartition.cpp - ./thirdparty/misc/polypartition.h +Files: thirdparty/misc/polypartition.cpp + thirdparty/misc/polypartition.h Comment: PolyPartition / Triangulator Copyright: 2011-2021, Ivan Fratric and contributors License: Expat -Files: ./thirdparty/misc/qoa.h +Files: thirdparty/misc/qoa.h Comment: Quite OK Audio Format Copyright: 2023, Dominic Szablewski License: Expat -Files: ./thirdparty/misc/r128.c - ./thirdparty/misc/r128.h +Files: thirdparty/misc/r128.c + thirdparty/misc/r128.h Comment: r128 library Copyright: Alan Hickman License: public-domain or Unlicense -Files: ./thirdparty/misc/smaz.c - ./thirdparty/misc/smaz.h +Files: thirdparty/misc/smaz.c + thirdparty/misc/smaz.h Comment: SMAZ Copyright: 2006-2009, Salvatore Sanfilippo License: BSD-3-clause -Files: ./thirdparty/misc/smolv.cpp - ./thirdparty/misc/smolv.h +Files: thirdparty/misc/smolv.cpp + thirdparty/misc/smolv.h Comment: SMOL-V Copyright: 2016-2024, Aras Pranckevicius License: public-domain or Unlicense or Expat -Files: ./thirdparty/misc/stb_rect_pack.h +Files: thirdparty/misc/stb_rect_pack.h Comment: stb libraries Copyright: Sean Barrett License: public-domain or Unlicense or Expat -Files: ./thirdparty/misc/yuv2rgb.h +Files: thirdparty/misc/yuv2rgb.h Comment: YUV2RGB Copyright: 2008-2011, Robin Watts License: BSD-2-clause -Files: ./thirdparty/msdfgen/ +Files: thirdparty/msdfgen/* Comment: Multi-channel signed distance field generator Copyright: 2014-2024, Viktor Chlumsky License: Expat -Files: ./thirdparty/nvapi/nvapi_minimal.h +Files: thirdparty/nvapi/nvapi_minimal.h Comment: Stripped down version of "nvapi.h" from the NVIDIA NVAPI SDK Copyright: 2019-2022, NVIDIA Corporation License: Expat -Files: ./thirdparty/openxr/ +Files: thirdparty/openxr/* Comment: OpenXR Loader Copyright: 2020-2023, The Khronos Group Inc. License: Apache-2.0 -Files: ./thirdparty/pcre2/ +Files: thirdparty/pcre2/* Comment: PCRE2 Copyright: 1997-2024, University of Cambridge 2009-2024, Zoltan Herczeg License: BSD-3-clause -Files: ./thirdparty/recastnavigation/ +Files: thirdparty/recastnavigation/* Comment: Recast Copyright: 2009, Mikko Mononen License: Zlib -Files: ./thirdparty/rvo2/ +Files: thirdparty/rvo2/* Comment: RVO2 Copyright: 2016, University of North Carolina at Chapel Hill License: Apache-2.0 -Files: ./thirdparty/spirv-cross/ +Files: thirdparty/spirv-cross/* Comment: SPIRV-Cross Copyright: 2015-2021, Arm Limited License: Apache-2.0 or Expat -Files: ./thirdparty/spirv-reflect/ +Files: thirdparty/spirv-reflect/* Comment: SPIRV-Reflect Copyright: 2017-2022, Google Inc. License: Apache-2.0 -Files: ./thirdparty/thorvg/ +Files: thirdparty/thorvg/* Comment: ThorVG Copyright: 2020-2024, The ThorVG Project License: Expat -Files: ./thirdparty/tinyexr/ +Files: thirdparty/tinyexr/* Comment: TinyEXR Copyright: 2014-2021, Syoyo Fujita 2002, Industrial Light & Magic, a division of Lucas Digital Ltd. LLC License: BSD-3-clause -Files: ./thirdparty/ufbx/ +Files: thirdparty/ufbx/* Comment: ufbx Copyright: 2020, Samuli Raivio License: Expat -Files: ./thirdparty/vhacd/ +Files: thirdparty/vhacd/* Comment: V-HACD Copyright: 2011, Khaled Mamou 2003-2009, Erwin Coumans License: BSD-3-clause -Files: ./thirdparty/volk/ +Files: thirdparty/volk/* Comment: volk Copyright: 2018-2024, Arseny Kapoulkine License: Expat -Files: ./thirdparty/vulkan/ +Files: thirdparty/vulkan/* Comment: Vulkan Headers Copyright: 2014-2024, The Khronos Group Inc. 2014-2024, Valve Corporation 2014-2024, LunarG, Inc. License: Apache-2.0 -Files: ./thirdparty/vulkan/vk_mem_alloc.h +Files: thirdparty/vulkan/vk_mem_alloc.h Comment: Vulkan Memory Allocator Copyright: 2017-2024, Advanced Micro Devices, Inc. License: Expat -Files: ./thirdparty/wayland/ +Files: thirdparty/wayland/* Comment: Wayland core protocol Copyright: 2008-2012, Kristian Høgsberg 2010-2012, Intel Corporation @@ -565,7 +563,7 @@ Copyright: 2008-2012, Kristian Høgsberg 2012, Collabora, Ltd. License: Expat -Files: ./thirdparty/wayland-protocols/ +Files: thirdparty/wayland-protocols/* Comment: Wayland protocols that add functionality not available in the core protocol Copyright: 2008-2013, Kristian Høgsberg 2010-2013, Intel Corporation @@ -577,24 +575,24 @@ Copyright: 2008-2013, Kristian Høgsberg 2015, Red Hat Inc. License: Expat -Files: ./thirdparty/wslay/ +Files: thirdparty/wslay/* Comment: Wslay Copyright: 2011, 2012, 2015, Tatsuhiro Tsujikawa License: Expat -Files: ./thirdparty/xatlas/ +Files: thirdparty/xatlas/* Comment: xatlas Copyright: 2018-2020, Jonathan Young 2013, Thekla, Inc 2006, NVIDIA Corporation, Ignacio Castano License: Expat -Files: ./thirdparty/zlib/ +Files: thirdparty/zlib/* Comment: zlib Copyright: 1995-2024, Jean-loup Gailly and Mark Adler License: Zlib -Files: ./thirdparty/zstd/ +Files: thirdparty/zstd/* Comment: Zstandard Copyright: Meta Platforms, Inc. and affiliates. License: BSD-3-clause diff --git a/SConstruct b/SConstruct index 248b00f8b3ec..8f8f329f233d 100644 --- a/SConstruct +++ b/SConstruct @@ -107,22 +107,11 @@ for x in sorted(glob.glob("platform/*")): sys.path.remove(tmppath) sys.modules.pop("detect") -custom_tools = ["default"] - -platform_arg = ARGUMENTS.get("platform", ARGUMENTS.get("p", False)) - -if platform_arg == "android": - custom_tools = ["clang", "clang++", "as", "ar", "link"] -elif platform_arg == "web": - # Use generic POSIX build toolchain for Emscripten. - custom_tools = ["cc", "c++", "ar", "link", "textfile", "zip"] -elif os.name == "nt" and methods.get_cmdline_bool("use_mingw", False): - custom_tools = ["mingw"] - # We let SCons build its default ENV as it includes OS-specific things which we don't -# want to have to pull in manually. +# want to have to pull in manually. However we enforce no "tools", which we register +# further down after parsing our platform-specific configuration. # Then we prepend PATH to make it take precedence, while preserving SCons' own entries. -env = Environment(tools=custom_tools) +env = Environment(tools=[]) env.PrependENVPath("PATH", os.getenv("PATH")) env.PrependENVPath("PKG_CONFIG_PATH", os.getenv("PKG_CONFIG_PATH")) if "TERM" in os.environ: # Used for colored output. @@ -168,11 +157,7 @@ if profile: opts = Variables(customs, ARGUMENTS) # Target build options -if env.scons_version >= (4, 3): - opts.Add(["platform", "p"], "Target platform (%s)" % "|".join(platform_list), "") -else: - opts.Add("platform", "Target platform (%s)" % "|".join(platform_list), "") - opts.Add("p", "Alias for 'platform'", "") +opts.Add((["platform", "p"], "Target platform (%s)" % "|".join(platform_list), "")) opts.Add(EnumVariable("target", "Compilation target", "editor", ("editor", "template_release", "template_debug"))) opts.Add(EnumVariable("arch", "CPU architecture", "auto", ["auto"] + architectures, architecture_aliases)) opts.Add(BoolVariable("dev_build", "Developer build with dev-only debugging code (DEV_ENABLED)", False)) @@ -312,10 +297,7 @@ if env["import_env_vars"]: # Platform selection: validate input, and add options. -if env.scons_version < (4, 3) and not env["platform"]: - env["platform"] = env["p"] - -if env["platform"] == "": +if not env["platform"]: # Missing `platform` argument, try to detect platform automatically if ( sys.platform.startswith("linux") @@ -330,7 +312,7 @@ if env["platform"] == "": elif sys.platform == "win32": env["platform"] = "windows" - if env["platform"] != "": + if env["platform"]: print(f'Automatically detected platform: {env["platform"]}') # Deprecated aliases kept for compatibility. @@ -352,7 +334,7 @@ if env["platform"] not in platform_list: if env["platform"] == "list": print(text) - elif env["platform"] == "": + elif not env["platform"]: print_error("Could not detect platform automatically.\n" + text) else: print_error(f'Invalid target platform "{env["platform"]}".\n' + text) @@ -434,6 +416,23 @@ env.modules_detected = modules_detected opts.Update(env, {**ARGUMENTS, **env.Dictionary()}) Help(opts.GenerateHelpText(env)) + +# FIXME: Tool assignment happening at this stage is a direct consequence of getting the platform logic AFTER the SCons +# environment was already been constructed. Fixing this would require a broader refactor where all options are setup +# ahead of time with native validator/converter functions. +tmppath = "./platform/" + env["platform"] +sys.path.insert(0, tmppath) +import detect + +custom_tools = ["default"] +try: # Platform custom tools are optional + custom_tools = detect.get_tools(env) +except AttributeError: + pass +for tool in custom_tools: + env.Tool(tool) + + # add default include paths env.Prepend(CPPPATH=["#"]) @@ -515,10 +514,6 @@ if not env["deprecated"]: if env["precision"] == "double": env.Append(CPPDEFINES=["REAL_T_IS_DOUBLE"]) -tmppath = "./platform/" + env["platform"] -sys.path.insert(0, tmppath) -import detect - # Default num_jobs to local cpu count if not user specified. # SCons has a peculiarity where user-specified options won't be overridden # by SetOption, so we can rely on this to know if we should use our default. @@ -587,7 +582,7 @@ if env["dev_mode"]: if env["production"]: env["use_static_cpp"] = methods.get_cmdline_bool("use_static_cpp", True) env["debug_symbols"] = methods.get_cmdline_bool("debug_symbols", False) - if platform_arg == "android": + if env["platform"] == "android": env["swappy"] = methods.get_cmdline_bool("swappy", True) # LTO "auto" means we handle the preferred option in each platform detect.py. env["lto"] = ARGUMENTS.get("lto", "auto") @@ -1039,6 +1034,9 @@ 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" if env["ninja"]: if env.scons_version < (4, 2, 0): diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index eb8e80a31d89..fdcd4f9b6064 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -472,13 +472,30 @@ void ProjectSettings::_emit_changed() { emit_signal("settings_changed"); } -bool ProjectSettings::_load_resource_pack(const String &p_pack, bool p_replace_files, int p_offset) { +bool ProjectSettings::load_resource_pack(const String &p_pack, bool p_replace_files, int p_offset) { + return ProjectSettings::_load_resource_pack(p_pack, p_replace_files, p_offset, false); +} + +bool ProjectSettings::_load_resource_pack(const String &p_pack, bool p_replace_files, int p_offset, bool p_main_pack) { if (PackedData::get_singleton()->is_disabled()) { return false; } - bool ok = PackedData::get_singleton()->add_pack(p_pack, p_replace_files, p_offset) == OK; + if (p_pack == "res://") { + // Loading the resource directory as a pack source is reserved for internal use only. + return false; + } + if (!p_main_pack && !using_datapack && !OS::get_singleton()->get_resource_dir().is_empty()) { + // Add the project's resource file system to PackedData so directory access keeps working when + // the game is running without a main pack, like in the editor or on Android. + PackedData::get_singleton()->add_pack_source(memnew(PackedSourceDirectory)); + PackedData::get_singleton()->add_pack("res://", false, 0); + DirAccess::make_default(DirAccess::ACCESS_RESOURCES); + using_datapack = true; + } + + bool ok = PackedData::get_singleton()->add_pack(p_pack, p_replace_files, p_offset) == OK; if (!ok) { return false; } @@ -491,9 +508,11 @@ bool ProjectSettings::_load_resource_pack(const String &p_pack, bool p_replace_f ResourceUID::get_singleton()->load_from_cache(false); } - //if data.pck is found, all directory access will be from here - DirAccess::make_default(DirAccess::ACCESS_RESOURCES); - using_datapack = true; + // If the data pack was found, all directory access will be from here. + if (!using_datapack) { + DirAccess::make_default(DirAccess::ACCESS_RESOURCES); + using_datapack = true; + } return true; } @@ -572,7 +591,7 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b // Attempt with a user-defined main pack first if (!p_main_pack.is_empty()) { - bool ok = _load_resource_pack(p_main_pack); + bool ok = _load_resource_pack(p_main_pack, false, 0, true); ERR_FAIL_COND_V_MSG(!ok, ERR_CANT_OPEN, vformat("Cannot open resource pack '%s'.", p_main_pack)); Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary"); @@ -591,7 +610,7 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b // and if so, we attempt loading it at the end. // Attempt with PCK bundled into executable. - bool found = _load_resource_pack(exec_path); + bool found = _load_resource_pack(exec_path, false, 0, true); // Attempt with exec_name.pck. // (This is the usual case when distributing a Godot game.) @@ -607,20 +626,20 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b #ifdef MACOS_ENABLED if (!found) { // Attempt to load PCK from macOS .app bundle resources. - found = _load_resource_pack(OS::get_singleton()->get_bundle_resource_dir().path_join(exec_basename + ".pck")) || _load_resource_pack(OS::get_singleton()->get_bundle_resource_dir().path_join(exec_filename + ".pck")); + found = _load_resource_pack(OS::get_singleton()->get_bundle_resource_dir().path_join(exec_basename + ".pck"), false, 0, true) || _load_resource_pack(OS::get_singleton()->get_bundle_resource_dir().path_join(exec_filename + ".pck"), false, 0, true); } #endif if (!found) { // Try to load data pack at the location of the executable. // As mentioned above, we have two potential names to attempt. - found = _load_resource_pack(exec_dir.path_join(exec_basename + ".pck")) || _load_resource_pack(exec_dir.path_join(exec_filename + ".pck")); + found = _load_resource_pack(exec_dir.path_join(exec_basename + ".pck"), false, 0, true) || _load_resource_pack(exec_dir.path_join(exec_filename + ".pck"), false, 0, true); } if (!found) { // If we couldn't find them next to the executable, we attempt // the current working directory. Same story, two tests. - found = _load_resource_pack(exec_basename + ".pck") || _load_resource_pack(exec_filename + ".pck"); + found = _load_resource_pack(exec_basename + ".pck", false, 0, true) || _load_resource_pack(exec_filename + ".pck", false, 0, true); } // If we opened our package, try and load our project. @@ -1418,7 +1437,7 @@ void ProjectSettings::_bind_methods() { ClassDB::bind_method(D_METHOD("localize_path", "path"), &ProjectSettings::localize_path); ClassDB::bind_method(D_METHOD("globalize_path", "path"), &ProjectSettings::globalize_path); ClassDB::bind_method(D_METHOD("save"), &ProjectSettings::save); - ClassDB::bind_method(D_METHOD("load_resource_pack", "pack", "replace_files", "offset"), &ProjectSettings::_load_resource_pack, DEFVAL(true), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("load_resource_pack", "pack", "replace_files", "offset"), &ProjectSettings::load_resource_pack, DEFVAL(true), DEFVAL(0)); ClassDB::bind_method(D_METHOD("save_custom", "file"), &ProjectSettings::_save_custom_bnd); diff --git a/core/config/project_settings.h b/core/config/project_settings.h index 72877526812c..02049290f6b2 100644 --- a/core/config/project_settings.h +++ b/core/config/project_settings.h @@ -135,7 +135,8 @@ class ProjectSettings : public Object { void _convert_to_last_version(int p_from_version); - bool _load_resource_pack(const String &p_pack, bool p_replace_files = true, int p_offset = 0); + bool load_resource_pack(const String &p_pack, bool p_replace_files, int p_offset); + bool _load_resource_pack(const String &p_pack, bool p_replace_files = true, int p_offset = 0, bool p_main_pack = false); void _add_property_info_bind(const Dictionary &p_info); diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 6974d98234cc..acd589783ece 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -113,12 +113,12 @@ PackedStringArray ResourceLoader::get_dependencies(const String &p_path) { } bool ResourceLoader::has_cached(const String &p_path) { - String local_path = ProjectSettings::get_singleton()->localize_path(p_path); + String local_path = ::ResourceLoader::_validate_local_path(p_path); return ResourceCache::has(local_path); } Ref ResourceLoader::get_cached_ref(const String &p_path) { - String local_path = ProjectSettings::get_singleton()->localize_path(p_path); + String local_path = ::ResourceLoader::_validate_local_path(p_path); return ResourceCache::get_ref(local_path); } diff --git a/core/core_builders.py b/core/core_builders.py index a3dc935b7910..d7c0603c8d77 100644 --- a/core/core_builders.py +++ b/core/core_builders.py @@ -200,8 +200,8 @@ def next_tag(self): tag, content = reader.next_tag() if tag in ("Files", "Copyright", "License"): part[tag] = content[:] - elif tag == "Comment": - # attach part to named project + elif tag == "Comment" and part: + # attach non-empty part to named project projects[content[0]] = projects.get(content[0], []) + [part] if not tag or not reader.current: diff --git a/core/debugger/remote_debugger_peer.cpp b/core/debugger/remote_debugger_peer.cpp index 499b221ce6a7..1afb7fe09b4e 100644 --- a/core/debugger/remote_debugger_peer.cpp +++ b/core/debugger/remote_debugger_peer.cpp @@ -173,12 +173,12 @@ Error RemoteDebuggerPeerTCP::connect_to_host(const String &p_host, uint16_t p_po } else { const int ms = waits[i]; OS::get_singleton()->delay_usec(ms * 1000); - print_verbose("Remote Debugger: Connection failed with status: '" + String::num(tcp_client->get_status()) + "', retrying in " + String::num(ms) + " msec."); + print_verbose("Remote Debugger: Connection failed with status: '" + String::num_int64(tcp_client->get_status()) + "', retrying in " + String::num_int64(ms) + " msec."); } } if (tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED) { - ERR_PRINT(vformat("Remote Debugger: Unable to connect. Status: %s.", String::num(tcp_client->get_status()))); + ERR_PRINT(vformat("Remote Debugger: Unable to connect. Status: %s.", String::num_int64(tcp_client->get_status()))); return FAILED; } connected = true; diff --git a/core/input/input.cpp b/core/input/input.cpp index c50e8b0163cc..b705968b87ed 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -77,6 +77,10 @@ Input *Input::singleton = nullptr; void (*Input::set_mouse_mode_func)(Input::MouseMode) = nullptr; Input::MouseMode (*Input::get_mouse_mode_func)() = nullptr; +void (*Input::set_mouse_mode_override_func)(Input::MouseMode) = nullptr; +Input::MouseMode (*Input::get_mouse_mode_override_func)() = nullptr; +void (*Input::set_mouse_mode_override_enabled_func)(bool) = nullptr; +bool (*Input::is_mouse_mode_override_enabled_func)() = nullptr; void (*Input::warp_mouse_func)(const Vector2 &p_position) = nullptr; Input::CursorShape (*Input::get_current_cursor_shape_func)() = nullptr; void (*Input::set_custom_mouse_cursor_func)(const Ref &, Input::CursorShape, const Vector2 &) = nullptr; @@ -86,51 +90,29 @@ Input *Input::get_singleton() { } void Input::set_mouse_mode(MouseMode p_mode) { - ERR_FAIL_INDEX((int)p_mode, 5); - - if (p_mode == mouse_mode) { - return; - } - - // Allow to be set even if overridden, to see if the platform allows the mode. + ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX); set_mouse_mode_func(p_mode); - mouse_mode = get_mouse_mode_func(); - - if (mouse_mode_override_enabled) { - set_mouse_mode_func(mouse_mode_override); - } } Input::MouseMode Input::get_mouse_mode() const { - return mouse_mode; + return get_mouse_mode_func(); } -void Input::set_mouse_mode_override_enabled(bool p_enabled) { - if (p_enabled == mouse_mode_override_enabled) { - return; - } - - mouse_mode_override_enabled = p_enabled; - - if (p_enabled) { - set_mouse_mode_func(mouse_mode_override); - mouse_mode_override = get_mouse_mode_func(); - } else { - set_mouse_mode_func(mouse_mode); - } +void Input::set_mouse_mode_override(MouseMode p_mode) { + ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX); + set_mouse_mode_override_func(p_mode); } -void Input::set_mouse_mode_override(MouseMode p_mode) { - ERR_FAIL_INDEX((int)p_mode, 5); +Input::MouseMode Input::get_mouse_mode_override() const { + return get_mouse_mode_override_func(); +} - if (p_mode == mouse_mode_override) { - return; - } +void Input::set_mouse_mode_override_enabled(bool p_override_enabled) { + set_mouse_mode_override_enabled_func(p_override_enabled); +} - if (mouse_mode_override_enabled) { - set_mouse_mode_func(p_mode); - mouse_mode_override = get_mouse_mode_func(); - } +bool Input::is_mouse_mode_override_enabled() { + return is_mouse_mode_override_enabled_func(); } void Input::_bind_methods() { @@ -199,6 +181,7 @@ void Input::_bind_methods() { BIND_ENUM_CONSTANT(MOUSE_MODE_CAPTURED); BIND_ENUM_CONSTANT(MOUSE_MODE_CONFINED); BIND_ENUM_CONSTANT(MOUSE_MODE_CONFINED_HIDDEN); + BIND_ENUM_CONSTANT(MOUSE_MODE_MAX); BIND_ENUM_CONSTANT(CURSOR_ARROW); BIND_ENUM_CONSTANT(CURSOR_IBEAM); @@ -1602,9 +1585,6 @@ void Input::parse_mapping(const String &p_mapping) { return; } - CharString uid; - uid.resize(17); - mapping.uid = entry[0]; mapping.name = entry[1]; @@ -1712,15 +1692,72 @@ void Input::add_joy_mapping(const String &p_mapping, bool p_update_existing) { } void Input::remove_joy_mapping(const String &p_guid) { + // One GUID can exist multiple times in `map_db`, and + // `add_joy_mapping` can choose not to update the existing mapping, + // so the indices can be all over the place. Therefore we need to remember them. + Vector removed_idx; + int min_removed_idx = -1; + int max_removed_idx = -1; + int fallback_mapping_offset = 0; + for (int i = map_db.size() - 1; i >= 0; i--) { if (p_guid == map_db[i].uid) { map_db.remove_at(i); + + if (max_removed_idx == -1) { + max_removed_idx = i; + } + min_removed_idx = i; + removed_idx.push_back(i); + + if (i < fallback_mapping) { + fallback_mapping_offset++; + } else if (i == fallback_mapping) { + fallback_mapping = -1; + WARN_PRINT_ONCE(vformat("Removed fallback joypad input mapping \"%s\". This could lead to joypads not working as intended.", p_guid)); + } } } + + if (min_removed_idx == -1) { + return; // Nothing removed. + } + + if (fallback_mapping > 0) { + // Fix the shifted index. + fallback_mapping -= fallback_mapping_offset; + } + + int removed_idx_size = removed_idx.size(); + + // Update joypad mapping references: some + // * should use the fallback_mapping (if set; if not, they get unmapped), or + // * need their mapping reference fixed, because the deletion(s) offset them. for (KeyValue &E : joy_names) { Joypad &joy = E.value; - if (joy.uid == p_guid) { - _set_joypad_mapping(joy, -1); + if (joy.mapping < min_removed_idx) { + continue; // Not affected. + } + + if (joy.mapping > max_removed_idx) { + _set_joypad_mapping(joy, joy.mapping - removed_idx_size); + continue; // Simple offset fix. + } + + // removed_idx is in reverse order (ie. high to low), because the first loop is in reverse order. + for (int i = 0; i < removed_idx.size(); i++) { + if (removed_idx[i] == joy.mapping) { + // Set to fallback_mapping, if defined, else unmap the joypad. + // Currently, the fallback_mapping is only set internally, and only for Android. + _set_joypad_mapping(joy, fallback_mapping); + break; + } + if (removed_idx[i] < joy.mapping) { + // Complex offset fix: + // This mapping was shifted by `(removed_idx_size - i)` deletions. + _set_joypad_mapping(joy, joy.mapping - (removed_idx_size - i)); + break; + } } } } diff --git a/core/input/input.h b/core/input/input.h index 81722d013e6d..bbe185de3cbc 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -47,12 +47,14 @@ 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 @@ -105,10 +107,6 @@ class Input : public Object { bool legacy_just_pressed_behavior = false; bool disable_input = false; - MouseMode mouse_mode = MOUSE_MODE_VISIBLE; - bool mouse_mode_override_enabled = false; - MouseMode mouse_mode_override = MOUSE_MODE_VISIBLE; - struct ActionState { uint64_t pressed_physics_frame = UINT64_MAX; uint64_t pressed_process_frame = UINT64_MAX; @@ -184,7 +182,7 @@ class Input : public Object { HashSet ignored_device_ids; - int fallback_mapping = -1; + int fallback_mapping = -1; // Index of the guid in map_db. CursorShape default_shape = CURSOR_ARROW; @@ -268,6 +266,10 @@ class Input : public Object { static void (*set_mouse_mode_func)(MouseMode); static MouseMode (*get_mouse_mode_func)(); + static void (*set_mouse_mode_override_func)(MouseMode); + static MouseMode (*get_mouse_mode_override_func)(); + static void (*set_mouse_mode_override_enabled_func)(bool); + static bool (*is_mouse_mode_override_enabled_func)(); static void (*warp_mouse_func)(const Vector2 &p_position); static CursorShape (*get_current_cursor_shape_func)(); @@ -286,8 +288,10 @@ class Input : public Object { public: void set_mouse_mode(MouseMode p_mode); MouseMode get_mouse_mode() const; - void set_mouse_mode_override_enabled(bool p_enabled); void set_mouse_mode_override(MouseMode p_mode); + MouseMode get_mouse_mode_override() const; + void set_mouse_mode_override_enabled(bool p_override_enabled); + bool is_mouse_mode_override_enabled(); #ifdef TOOLS_ENABLED void get_argument_options(const StringName &p_function, int p_idx, List *r_options) const override; diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp index 1a0e0b06cc91..3fba2c70679a 100644 --- a/core/io/file_access_pack.cpp +++ b/core/io/file_access_pack.cpp @@ -331,6 +331,44 @@ 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) { + // 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."); + + if (p_path != "res://") { + return false; + } + add_directory(p_path, p_replace_files); + return true; +} + +Ref PackedSourceDirectory::get_file(const String &p_path, PackedData::PackedFile *p_file) { + Ref ret = FileAccess::create_for_path(p_path); + ret->reopen(p_path, FileAccess::READ); + return ret; +} + +void PackedSourceDirectory::add_directory(const String &p_path, bool p_replace_files) { + Ref da = DirAccess::open(p_path); + if (da.is_null()) { + return; + } + da->set_include_hidden(true); + + for (const String &file_name : da->get_files()) { + String file_path = p_path.path_join(file_name); + uint8_t md5[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + PackedData::get_singleton()->add_path(p_path, file_path, 0, 0, md5, this, p_replace_files, false); + } + + for (const String &sub_dir_name : da->get_directories()) { + String sub_dir_path = p_path.path_join(sub_dir_name); + add_directory(sub_dir_path, p_replace_files); + } +} + +////////////////////////////////////////////////////////////////// + Error FileAccessPack::open_internal(const String &p_path, int p_mode_flags) { ERR_PRINT("Can't open pack-referenced file."); return ERR_UNAVAILABLE; diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h index 80d69d7b541d..0e4e7cd2893a 100644 --- a/core/io/file_access_pack.h +++ b/core/io/file_access_pack.h @@ -147,6 +147,14 @@ class PackedSourcePCK : public PackSource { virtual Ref get_file(const String &p_path, PackedData::PackedFile *p_file) 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; +}; + class FileAccessPack : public FileAccess { PackedData::PackedFile pf; diff --git a/core/io/image.cpp b/core/io/image.cpp index 05586b3d58f8..45f9599cbe38 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -3993,11 +3993,11 @@ uint32_t Image::get_format_component_mask(Format p_format) { case FORMAT_RGBAH: return rgba; case FORMAT_RGBE9995: - return rgba; + return rgb; case FORMAT_DXT1: return rgb; case FORMAT_DXT3: - return rgb; + return rgba; case FORMAT_DXT5: return rgba; case FORMAT_RGTC_R: @@ -4027,9 +4027,9 @@ uint32_t Image::get_format_component_mask(Format p_format) { case FORMAT_ETC2_RGB8A1: return rgba; case FORMAT_ETC2_RA_AS_RG: - return rgba; + return rg; case FORMAT_DXT5_RA_AS_RG: - return rgba; + return rg; case FORMAT_ASTC_4x4: return rgba; case FORMAT_ASTC_4x4_HDR: diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index e0ee310a1a33..7cc91ef4318b 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -320,14 +320,34 @@ Ref ResourceLoader::_load(const String &p_path, const String &p_origin print_verbose(vformat("Failed loading resource: %s", p_path)); } +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + if (ResourceFormatImporter::get_singleton()->get_importer_by_extension(p_path.get_extension()).is_valid()) { + // The format is known to the editor, but the file hasn't been imported + // (otherwise, ResourceFormatImporter would have been found as a suitable loader). + found = true; + if (r_error) { + *r_error = ERR_FILE_NOT_FOUND; + } + } + } +#endif ERR_FAIL_COND_V_MSG(found, Ref(), vformat("Failed loading resource: %s. Make sure resources have been imported by opening the project in the editor at least once.", p_path)); #ifdef TOOLS_ENABLED Ref file_check = FileAccess::create(FileAccess::ACCESS_RESOURCES); - ERR_FAIL_COND_V_MSG(!file_check->file_exists(p_path), Ref(), vformat("Resource file not found: %s (expected type: %s)", p_path, p_type_hint)); + if (!file_check->file_exists(p_path)) { + if (r_error) { + *r_error = ERR_FILE_NOT_FOUND; + } + ERR_FAIL_V_MSG(Ref(), vformat("Resource file not found: %s (expected type: %s)", p_path, p_type_hint)); + } #endif + if (r_error) { + *r_error = ERR_FILE_UNRECOGNIZED; + } ERR_FAIL_V_MSG(Ref(), vformat("No loader found for resource: %s (expected type: %s)", p_path, p_type_hint)); } @@ -475,7 +495,7 @@ void ResourceLoader::_run_load_task(void *p_userdata) { curr_load_task = curr_load_task_backup; } -static String _validate_local_path(const String &p_path) { +String ResourceLoader::_validate_local_path(const String &p_path) { ResourceUID::ID uid = ResourceUID::get_singleton()->text_to_id(p_path); if (uid != ResourceUID::INVALID_ID) { return ResourceUID::get_singleton()->get_id_path(uid); diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h index f933e88b23bf..56052b6a6fc4 100644 --- a/core/io/resource_loader.h +++ b/core/io/resource_loader.h @@ -36,6 +36,10 @@ #include "core/object/worker_thread_pool.h" #include "core/os/thread.h" +namespace core_bind { +class ResourceLoader; +} + class ConditionVariable; template @@ -101,6 +105,7 @@ typedef void (*ResourceLoadedCallback)(Ref p_resource, const String &p class ResourceLoader { friend class LoadToken; + friend class core_bind::ResourceLoader; enum { MAX_LOADERS = 64 @@ -217,6 +222,8 @@ class ResourceLoader { static bool _ensure_load_progress(); + static String _validate_local_path(const String &p_path); + public: static Error load_threaded_request(const String &p_path, const String &p_type_hint = "", bool p_use_sub_threads = false, ResourceFormatLoader::CacheMode p_cache_mode = ResourceFormatLoader::CACHE_MODE_REUSE); static ThreadLoadStatus load_threaded_get_status(const String &p_path, float *r_progress = nullptr); diff --git a/core/math/quaternion.h b/core/math/quaternion.h index 605035c21438..58a4ec5b74da 100644 --- a/core/math/quaternion.h +++ b/core/math/quaternion.h @@ -141,16 +141,24 @@ struct [[nodiscard]] Quaternion { } Quaternion(const Vector3 &p_v0, const Vector3 &p_v1) { // Shortest arc. - Vector3 c = p_v0.cross(p_v1); - - if (c.is_zero_approx()) { - Vector3 axis = p_v0.get_any_perpendicular(); +#ifdef MATH_CHECKS + ERR_FAIL_COND_MSG(p_v0.is_zero_approx() || p_v1.is_zero_approx(), "The vectors must not be zero."); +#endif + constexpr real_t ALMOST_ONE = 1.0f - (real_t)CMP_EPSILON; + Vector3 n0 = p_v0.normalized(); + Vector3 n1 = p_v1.normalized(); + real_t d = n0.dot(n1); + if (abs(d) > ALMOST_ONE) { + if (d >= 0) { + return; // Vectors are same. + } + Vector3 axis = n0.get_any_perpendicular(); x = axis.x; y = axis.y; z = axis.z; w = 0; } else { - real_t d = p_v0.dot(p_v1); + Vector3 c = n0.cross(n1); real_t s = Math::sqrt((1.0f + d) * 2.0f); real_t rs = 1.0f / s; diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index ffd796de9cff..e6536fe8e98a 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -1866,9 +1866,12 @@ void ClassDB::_bind_compatibility(ClassInfo *type, MethodBind *p_method) { void ClassDB::_bind_method_custom(const StringName &p_class, MethodBind *p_method, bool p_compatibility) { OBJTYPE_WLOCK; + StringName method_name = p_method->get_name(); + ClassInfo *type = classes.getptr(p_class); if (!type) { - ERR_FAIL_MSG(vformat("Couldn't bind custom method '%s' for instance '%s'.", p_method->get_name(), p_class)); + memdelete(p_method); + ERR_FAIL_MSG(vformat("Couldn't bind custom method '%s' for instance '%s'.", method_name, p_class)); } if (p_compatibility) { @@ -1876,16 +1879,17 @@ void ClassDB::_bind_method_custom(const StringName &p_class, MethodBind *p_metho return; } - if (type->method_map.has(p_method->get_name())) { + if (type->method_map.has(method_name)) { // overloading not supported - ERR_FAIL_MSG(vformat("Method already bound '%s::%s'.", p_class, p_method->get_name())); + memdelete(p_method); + ERR_FAIL_MSG(vformat("Method already bound '%s::%s'.", p_class, method_name)); } #ifdef DEBUG_METHODS_ENABLED - type->method_order.push_back(p_method->get_name()); + type->method_order.push_back(method_name); #endif - type->method_map[p_method->get_name()] = p_method; + type->method_map[method_name] = p_method; } MethodBind *ClassDB::_bind_vararg_method(MethodBind *p_bind, const StringName &p_name, const Vector &p_default_args, bool p_compatibility) { diff --git a/core/object/make_virtuals.py b/core/object/make_virtuals.py index 325477692a6d..13775ecdde07 100644 --- a/core/object/make_virtuals.py +++ b/core/object/make_virtuals.py @@ -154,7 +154,7 @@ def generate_version(argcount, const=False, returns=False, required=False, compa callptrargsptr += ", " argtext += f"m_type{i + 1}" callargtext += f"m_type{i + 1} arg{i + 1}" - callsiargs += f"_to_variant(arg{i + 1})" + callsiargs += f"arg{i + 1}" callsiargptrs += f"&vargs[{i}]" callptrargs += ( f"PtrToArg::EncodeT argval{i + 1} = (PtrToArg::EncodeT)arg{i + 1};\\\n" @@ -240,37 +240,6 @@ def run(target, source, env): #define _GDVIRTUAL_GET_DEPRECATED(m_name, m_name_sn, m_compat) #endif -// MSVC WORKAROUND START -// FIXME The below helper functions are needed to work around an MSVC bug. -// They should be removed (by modifying core/object/make_virtuals.py) once the bug ceases to be triggered. -// The bug is triggered by the following code: -// `Variant(arg)` -// Through the introduction of the move constructor, MSVC forgets that `operator Variant()` -// is also a valid way to resolve this call. So for some argument types, it fails the call because -// it cannot convert to `Variant`. -// The function `_to_variant` helps the compiler select `.operator Variant()` for appropriate arguments using SFINAE. - -template -struct has_variant_operator : std::false_type {}; - -template -struct has_variant_operator().operator Variant())>> : std::true_type {}; - -// Function that is enabled if T has `.operator Variant()`. -template -_ALWAYS_INLINE_ typename std::enable_if::value, Variant>::type -_to_variant(T&& t) { - return std::forward(t).operator Variant(); -} - -// Function that is enabled if T does not have `.operator Variant()`. -template -_ALWAYS_INLINE_ typename std::enable_if::value, Variant>::type -_to_variant(T&& t) { - return Variant(std::forward(t)); -} -// MSVC WORKAROUND END - """ for i in range(max_versions + 1): diff --git a/core/object/method_bind.h b/core/object/method_bind.h index e06eb1f8fae0..a07824a0dca8 100644 --- a/core/object/method_bind.h +++ b/core/object/method_bind.h @@ -90,7 +90,7 @@ class MethodBind { } _FORCE_INLINE_ Variant::Type get_argument_type(int p_argument) const { - ERR_FAIL_COND_V(p_argument < -1 || p_argument > argument_count, Variant::NIL); + ERR_FAIL_COND_V(p_argument < -1 || p_argument >= argument_count, Variant::NIL); return argument_types[p_argument + 1]; } diff --git a/core/string/string_name.cpp b/core/string/string_name.cpp index 2d3c376d0cbe..a0c225d28eb3 100644 --- a/core/string/string_name.cpp +++ b/core/string/string_name.cpp @@ -269,12 +269,10 @@ StringName::StringName(const char *p_name, bool p_static) { return; //empty, ignore } - MutexLock lock(mutex); - - uint32_t hash = String::hash(p_name); - - uint32_t idx = hash & STRING_TABLE_MASK; + const uint32_t hash = String::hash(p_name); + const uint32_t idx = hash & STRING_TABLE_MASK; + MutexLock lock(mutex); _data = _table[idx]; while (_data) { @@ -328,12 +326,10 @@ StringName::StringName(const StaticCString &p_static_string, bool p_static) { ERR_FAIL_COND(!p_static_string.ptr || !p_static_string.ptr[0]); - MutexLock lock(mutex); - - uint32_t hash = String::hash(p_static_string.ptr); - - uint32_t idx = hash & STRING_TABLE_MASK; + const uint32_t hash = String::hash(p_static_string.ptr); + const uint32_t idx = hash & STRING_TABLE_MASK; + MutexLock lock(mutex); _data = _table[idx]; while (_data) { @@ -388,11 +384,10 @@ StringName::StringName(const String &p_name, bool p_static) { return; } - MutexLock lock(mutex); - - uint32_t hash = p_name.hash(); - uint32_t idx = hash & STRING_TABLE_MASK; + const uint32_t hash = p_name.hash(); + const uint32_t idx = hash & STRING_TABLE_MASK; + MutexLock lock(mutex); _data = _table[idx]; while (_data) { @@ -446,11 +441,10 @@ StringName StringName::search(const char *p_name) { return StringName(); } - MutexLock lock(mutex); - - uint32_t hash = String::hash(p_name); - uint32_t idx = hash & STRING_TABLE_MASK; + const uint32_t hash = String::hash(p_name); + const uint32_t idx = hash & STRING_TABLE_MASK; + MutexLock lock(mutex); _Data *_data = _table[idx]; while (_data) { @@ -482,12 +476,10 @@ StringName StringName::search(const char32_t *p_name) { return StringName(); } - MutexLock lock(mutex); - - uint32_t hash = String::hash(p_name); - - uint32_t idx = hash & STRING_TABLE_MASK; + const uint32_t hash = String::hash(p_name); + const uint32_t idx = hash & STRING_TABLE_MASK; + MutexLock lock(mutex); _Data *_data = _table[idx]; while (_data) { @@ -508,12 +500,10 @@ StringName StringName::search(const char32_t *p_name) { StringName StringName::search(const String &p_name) { ERR_FAIL_COND_V(p_name.is_empty(), StringName()); - MutexLock lock(mutex); - - uint32_t hash = p_name.hash(); - - uint32_t idx = hash & STRING_TABLE_MASK; + const uint32_t hash = p_name.hash(); + const uint32_t idx = hash & STRING_TABLE_MASK; + MutexLock lock(mutex); _Data *_data = _table[idx]; while (_data) { diff --git a/core/string/ucaps.h b/core/string/ucaps.h index 01b81943c106..ea58331be1da 100644 --- a/core/string/ucaps.h +++ b/core/string/ucaps.h @@ -31,10 +31,12 @@ #ifndef UCAPS_H #define UCAPS_H -//satan invented unicode? -#define CAPS_LEN 666 +// This file was generated using the `misc/scripts/ucaps_fetch.py` script. -static const int caps_table[CAPS_LEN][2] = { +#define LTU_LEN 1477 +#define UTL_LEN 1460 + +static const int caps_table[LTU_LEN][2] = { { 0x0061, 0x0041 }, { 0x0062, 0x0042 }, { 0x0063, 0x0043 }, @@ -61,6 +63,7 @@ static const int caps_table[CAPS_LEN][2] = { { 0x0078, 0x0058 }, { 0x0079, 0x0059 }, { 0x007A, 0x005A }, + { 0x00B5, 0x039C }, { 0x00E0, 0x00C0 }, { 0x00E1, 0x00C1 }, { 0x00E2, 0x00C2 }, @@ -154,12 +157,18 @@ static const int caps_table[CAPS_LEN][2] = { { 0x017A, 0x0179 }, { 0x017C, 0x017B }, { 0x017E, 0x017D }, + { 0x017F, 0x0053 }, + { 0x0180, 0x0243 }, { 0x0183, 0x0182 }, { 0x0185, 0x0184 }, { 0x0188, 0x0187 }, { 0x018C, 0x018B }, { 0x0192, 0x0191 }, + { 0x0195, 0x01F6 }, { 0x0199, 0x0198 }, + { 0x019A, 0x023D }, + { 0x019B, 0xA7DC }, + { 0x019E, 0x0220 }, { 0x01A1, 0x01A0 }, { 0x01A3, 0x01A2 }, { 0x01A5, 0x01A4 }, @@ -170,8 +179,12 @@ static const int caps_table[CAPS_LEN][2] = { { 0x01B6, 0x01B5 }, { 0x01B9, 0x01B8 }, { 0x01BD, 0x01BC }, + { 0x01BF, 0x01F7 }, + { 0x01C5, 0x01C4 }, { 0x01C6, 0x01C4 }, + { 0x01C8, 0x01C7 }, { 0x01C9, 0x01C7 }, + { 0x01CB, 0x01CA }, { 0x01CC, 0x01CA }, { 0x01CE, 0x01CD }, { 0x01D0, 0x01CF }, @@ -181,6 +194,7 @@ static const int caps_table[CAPS_LEN][2] = { { 0x01D8, 0x01D7 }, { 0x01DA, 0x01D9 }, { 0x01DC, 0x01DB }, + { 0x01DD, 0x018E }, { 0x01DF, 0x01DE }, { 0x01E1, 0x01E0 }, { 0x01E3, 0x01E2 }, @@ -190,8 +204,10 @@ static const int caps_table[CAPS_LEN][2] = { { 0x01EB, 0x01EA }, { 0x01ED, 0x01EC }, { 0x01EF, 0x01EE }, + { 0x01F2, 0x01F1 }, { 0x01F3, 0x01F1 }, { 0x01F5, 0x01F4 }, + { 0x01F9, 0x01F8 }, { 0x01FB, 0x01FA }, { 0x01FD, 0x01FC }, { 0x01FF, 0x01FE }, @@ -207,24 +223,73 @@ static const int caps_table[CAPS_LEN][2] = { { 0x0213, 0x0212 }, { 0x0215, 0x0214 }, { 0x0217, 0x0216 }, + { 0x0219, 0x0218 }, + { 0x021B, 0x021A }, + { 0x021D, 0x021C }, + { 0x021F, 0x021E }, + { 0x0223, 0x0222 }, + { 0x0225, 0x0224 }, + { 0x0227, 0x0226 }, + { 0x0229, 0x0228 }, + { 0x022B, 0x022A }, + { 0x022D, 0x022C }, + { 0x022F, 0x022E }, + { 0x0231, 0x0230 }, + { 0x0233, 0x0232 }, + { 0x023C, 0x023B }, + { 0x023F, 0x2C7E }, + { 0x0240, 0x2C7F }, + { 0x0242, 0x0241 }, + { 0x0247, 0x0246 }, + { 0x0249, 0x0248 }, + { 0x024B, 0x024A }, + { 0x024D, 0x024C }, + { 0x024F, 0x024E }, + { 0x0250, 0x2C6F }, + { 0x0251, 0x2C6D }, + { 0x0252, 0x2C70 }, { 0x0253, 0x0181 }, { 0x0254, 0x0186 }, + { 0x0256, 0x0189 }, { 0x0257, 0x018A }, - { 0x0258, 0x018E }, { 0x0259, 0x018F }, { 0x025B, 0x0190 }, + { 0x025C, 0xA7AB }, { 0x0260, 0x0193 }, + { 0x0261, 0xA7AC }, { 0x0263, 0x0194 }, + { 0x0264, 0xA7CB }, + { 0x0265, 0xA78D }, + { 0x0266, 0xA7AA }, { 0x0268, 0x0197 }, { 0x0269, 0x0196 }, + { 0x026A, 0xA7AE }, + { 0x026B, 0x2C62 }, + { 0x026C, 0xA7AD }, { 0x026F, 0x019C }, + { 0x0271, 0x2C6E }, { 0x0272, 0x019D }, { 0x0275, 0x019F }, + { 0x027D, 0x2C64 }, + { 0x0280, 0x01A6 }, + { 0x0282, 0xA7C5 }, { 0x0283, 0x01A9 }, + { 0x0287, 0xA7B1 }, { 0x0288, 0x01AE }, + { 0x0289, 0x0244 }, { 0x028A, 0x01B1 }, { 0x028B, 0x01B2 }, + { 0x028C, 0x0245 }, { 0x0292, 0x01B7 }, + { 0x029D, 0xA7B2 }, + { 0x029E, 0xA7B0 }, + { 0x0345, 0x0399 }, + { 0x0371, 0x0370 }, + { 0x0373, 0x0372 }, + { 0x0377, 0x0376 }, + { 0x037B, 0x03FD }, + { 0x037C, 0x03FE }, + { 0x037D, 0x03FF }, { 0x03AC, 0x0386 }, { 0x03AD, 0x0388 }, { 0x03AE, 0x0389 }, @@ -246,6 +311,7 @@ static const int caps_table[CAPS_LEN][2] = { { 0x03BF, 0x039F }, { 0x03C0, 0x03A0 }, { 0x03C1, 0x03A1 }, + { 0x03C2, 0x03A3 }, { 0x03C3, 0x03A3 }, { 0x03C4, 0x03A4 }, { 0x03C5, 0x03A5 }, @@ -258,6 +324,16 @@ static const int caps_table[CAPS_LEN][2] = { { 0x03CC, 0x038C }, { 0x03CD, 0x038E }, { 0x03CE, 0x038F }, + { 0x03D0, 0x0392 }, + { 0x03D1, 0x0398 }, + { 0x03D5, 0x03A6 }, + { 0x03D6, 0x03A0 }, + { 0x03D7, 0x03CF }, + { 0x03D9, 0x03D8 }, + { 0x03DB, 0x03DA }, + { 0x03DD, 0x03DC }, + { 0x03DF, 0x03DE }, + { 0x03E1, 0x03E0 }, { 0x03E3, 0x03E2 }, { 0x03E5, 0x03E4 }, { 0x03E7, 0x03E6 }, @@ -265,6 +341,13 @@ static const int caps_table[CAPS_LEN][2] = { { 0x03EB, 0x03EA }, { 0x03ED, 0x03EC }, { 0x03EF, 0x03EE }, + { 0x03F0, 0x039A }, + { 0x03F1, 0x03A1 }, + { 0x03F2, 0x03F9 }, + { 0x03F3, 0x037F }, + { 0x03F5, 0x0395 }, + { 0x03F8, 0x03F7 }, + { 0x03FB, 0x03FA }, { 0x0430, 0x0410 }, { 0x0431, 0x0411 }, { 0x0432, 0x0412 }, @@ -297,6 +380,7 @@ static const int caps_table[CAPS_LEN][2] = { { 0x044D, 0x042D }, { 0x044E, 0x042E }, { 0x044F, 0x042F }, + { 0x0450, 0x0400 }, { 0x0451, 0x0401 }, { 0x0452, 0x0402 }, { 0x0453, 0x0403 }, @@ -309,6 +393,7 @@ static const int caps_table[CAPS_LEN][2] = { { 0x045A, 0x040A }, { 0x045B, 0x040B }, { 0x045C, 0x040C }, + { 0x045D, 0x040D }, { 0x045E, 0x040E }, { 0x045F, 0x040F }, { 0x0461, 0x0460 }, @@ -328,6 +413,9 @@ static const int caps_table[CAPS_LEN][2] = { { 0x047D, 0x047C }, { 0x047F, 0x047E }, { 0x0481, 0x0480 }, + { 0x048B, 0x048A }, + { 0x048D, 0x048C }, + { 0x048F, 0x048E }, { 0x0491, 0x0490 }, { 0x0493, 0x0492 }, { 0x0495, 0x0494 }, @@ -354,8 +442,12 @@ static const int caps_table[CAPS_LEN][2] = { { 0x04BF, 0x04BE }, { 0x04C2, 0x04C1 }, { 0x04C4, 0x04C3 }, + { 0x04C6, 0x04C5 }, { 0x04C8, 0x04C7 }, + { 0x04CA, 0x04C9 }, { 0x04CC, 0x04CB }, + { 0x04CE, 0x04CD }, + { 0x04CF, 0x04C0 }, { 0x04D1, 0x04D0 }, { 0x04D3, 0x04D2 }, { 0x04D5, 0x04D4 }, @@ -370,11 +462,40 @@ static const int caps_table[CAPS_LEN][2] = { { 0x04E7, 0x04E6 }, { 0x04E9, 0x04E8 }, { 0x04EB, 0x04EA }, + { 0x04ED, 0x04EC }, { 0x04EF, 0x04EE }, { 0x04F1, 0x04F0 }, { 0x04F3, 0x04F2 }, { 0x04F5, 0x04F4 }, + { 0x04F7, 0x04F6 }, { 0x04F9, 0x04F8 }, + { 0x04FB, 0x04FA }, + { 0x04FD, 0x04FC }, + { 0x04FF, 0x04FE }, + { 0x0501, 0x0500 }, + { 0x0503, 0x0502 }, + { 0x0505, 0x0504 }, + { 0x0507, 0x0506 }, + { 0x0509, 0x0508 }, + { 0x050B, 0x050A }, + { 0x050D, 0x050C }, + { 0x050F, 0x050E }, + { 0x0511, 0x0510 }, + { 0x0513, 0x0512 }, + { 0x0515, 0x0514 }, + { 0x0517, 0x0516 }, + { 0x0519, 0x0518 }, + { 0x051B, 0x051A }, + { 0x051D, 0x051C }, + { 0x051F, 0x051E }, + { 0x0521, 0x0520 }, + { 0x0523, 0x0522 }, + { 0x0525, 0x0524 }, + { 0x0527, 0x0526 }, + { 0x0529, 0x0528 }, + { 0x052B, 0x052A }, + { 0x052D, 0x052C }, + { 0x052F, 0x052E }, { 0x0561, 0x0531 }, { 0x0562, 0x0532 }, { 0x0563, 0x0533 }, @@ -413,44 +534,71 @@ static const int caps_table[CAPS_LEN][2] = { { 0x0584, 0x0554 }, { 0x0585, 0x0555 }, { 0x0586, 0x0556 }, - { 0x10D0, 0x10A0 }, - { 0x10D1, 0x10A1 }, - { 0x10D2, 0x10A2 }, - { 0x10D3, 0x10A3 }, - { 0x10D4, 0x10A4 }, - { 0x10D5, 0x10A5 }, - { 0x10D6, 0x10A6 }, - { 0x10D7, 0x10A7 }, - { 0x10D8, 0x10A8 }, - { 0x10D9, 0x10A9 }, - { 0x10DA, 0x10AA }, - { 0x10DB, 0x10AB }, - { 0x10DC, 0x10AC }, - { 0x10DD, 0x10AD }, - { 0x10DE, 0x10AE }, - { 0x10DF, 0x10AF }, - { 0x10E0, 0x10B0 }, - { 0x10E1, 0x10B1 }, - { 0x10E2, 0x10B2 }, - { 0x10E3, 0x10B3 }, - { 0x10E4, 0x10B4 }, - { 0x10E5, 0x10B5 }, - { 0x10E6, 0x10B6 }, - { 0x10E7, 0x10B7 }, - { 0x10E8, 0x10B8 }, - { 0x10E9, 0x10B9 }, - { 0x10EA, 0x10BA }, - { 0x10EB, 0x10BB }, - { 0x10EC, 0x10BC }, - { 0x10ED, 0x10BD }, - { 0x10EE, 0x10BE }, - { 0x10EF, 0x10BF }, - { 0x10F0, 0x10C0 }, - { 0x10F1, 0x10C1 }, - { 0x10F2, 0x10C2 }, - { 0x10F3, 0x10C3 }, - { 0x10F4, 0x10C4 }, - { 0x10F5, 0x10C5 }, + { 0x10D0, 0x1C90 }, + { 0x10D1, 0x1C91 }, + { 0x10D2, 0x1C92 }, + { 0x10D3, 0x1C93 }, + { 0x10D4, 0x1C94 }, + { 0x10D5, 0x1C95 }, + { 0x10D6, 0x1C96 }, + { 0x10D7, 0x1C97 }, + { 0x10D8, 0x1C98 }, + { 0x10D9, 0x1C99 }, + { 0x10DA, 0x1C9A }, + { 0x10DB, 0x1C9B }, + { 0x10DC, 0x1C9C }, + { 0x10DD, 0x1C9D }, + { 0x10DE, 0x1C9E }, + { 0x10DF, 0x1C9F }, + { 0x10E0, 0x1CA0 }, + { 0x10E1, 0x1CA1 }, + { 0x10E2, 0x1CA2 }, + { 0x10E3, 0x1CA3 }, + { 0x10E4, 0x1CA4 }, + { 0x10E5, 0x1CA5 }, + { 0x10E6, 0x1CA6 }, + { 0x10E7, 0x1CA7 }, + { 0x10E8, 0x1CA8 }, + { 0x10E9, 0x1CA9 }, + { 0x10EA, 0x1CAA }, + { 0x10EB, 0x1CAB }, + { 0x10EC, 0x1CAC }, + { 0x10ED, 0x1CAD }, + { 0x10EE, 0x1CAE }, + { 0x10EF, 0x1CAF }, + { 0x10F0, 0x1CB0 }, + { 0x10F1, 0x1CB1 }, + { 0x10F2, 0x1CB2 }, + { 0x10F3, 0x1CB3 }, + { 0x10F4, 0x1CB4 }, + { 0x10F5, 0x1CB5 }, + { 0x10F6, 0x1CB6 }, + { 0x10F7, 0x1CB7 }, + { 0x10F8, 0x1CB8 }, + { 0x10F9, 0x1CB9 }, + { 0x10FA, 0x1CBA }, + { 0x10FD, 0x1CBD }, + { 0x10FE, 0x1CBE }, + { 0x10FF, 0x1CBF }, + { 0x13F8, 0x13F0 }, + { 0x13F9, 0x13F1 }, + { 0x13FA, 0x13F2 }, + { 0x13FB, 0x13F3 }, + { 0x13FC, 0x13F4 }, + { 0x13FD, 0x13F5 }, + { 0x1C80, 0x0412 }, + { 0x1C81, 0x0414 }, + { 0x1C82, 0x041E }, + { 0x1C83, 0x0421 }, + { 0x1C84, 0x0422 }, + { 0x1C85, 0x0422 }, + { 0x1C86, 0x042A }, + { 0x1C87, 0x0462 }, + { 0x1C88, 0xA64A }, + { 0x1C8A, 0x1C89 }, + { 0x1D79, 0xA77D }, + { 0x1D7D, 0x2C63 }, + { 0x1D8E, 0xA7C6 }, { 0x1E01, 0x1E00 }, { 0x1E03, 0x1E02 }, { 0x1E05, 0x1E04 }, @@ -526,6 +674,7 @@ static const int caps_table[CAPS_LEN][2] = { { 0x1E91, 0x1E90 }, { 0x1E93, 0x1E92 }, { 0x1E95, 0x1E94 }, + { 0x1E9B, 0x1E60 }, { 0x1EA1, 0x1EA0 }, { 0x1EA3, 0x1EA2 }, { 0x1EA5, 0x1EA4 }, @@ -571,6 +720,9 @@ static const int caps_table[CAPS_LEN][2] = { { 0x1EF5, 0x1EF4 }, { 0x1EF7, 0x1EF6 }, { 0x1EF9, 0x1EF8 }, + { 0x1EFB, 0x1EFA }, + { 0x1EFD, 0x1EFC }, + { 0x1EFF, 0x1EFE }, { 0x1F00, 0x1F08 }, { 0x1F01, 0x1F09 }, { 0x1F02, 0x1F0A }, @@ -619,6 +771,20 @@ static const int caps_table[CAPS_LEN][2] = { { 0x1F65, 0x1F6D }, { 0x1F66, 0x1F6E }, { 0x1F67, 0x1F6F }, + { 0x1F70, 0x1FBA }, + { 0x1F71, 0x1FBB }, + { 0x1F72, 0x1FC8 }, + { 0x1F73, 0x1FC9 }, + { 0x1F74, 0x1FCA }, + { 0x1F75, 0x1FCB }, + { 0x1F76, 0x1FDA }, + { 0x1F77, 0x1FDB }, + { 0x1F78, 0x1FF8 }, + { 0x1F79, 0x1FF9 }, + { 0x1F7A, 0x1FEA }, + { 0x1F7B, 0x1FEB }, + { 0x1F7C, 0x1FFA }, + { 0x1F7D, 0x1FFB }, { 0x1F80, 0x1F88 }, { 0x1F81, 0x1F89 }, { 0x1F82, 0x1F8A }, @@ -645,10 +811,33 @@ static const int caps_table[CAPS_LEN][2] = { { 0x1FA7, 0x1FAF }, { 0x1FB0, 0x1FB8 }, { 0x1FB1, 0x1FB9 }, + { 0x1FB3, 0x1FBC }, + { 0x1FBE, 0x0399 }, + { 0x1FC3, 0x1FCC }, { 0x1FD0, 0x1FD8 }, { 0x1FD1, 0x1FD9 }, { 0x1FE0, 0x1FE8 }, { 0x1FE1, 0x1FE9 }, + { 0x1FE5, 0x1FEC }, + { 0x1FF3, 0x1FFC }, + { 0x214E, 0x2132 }, + { 0x2170, 0x2160 }, + { 0x2171, 0x2161 }, + { 0x2172, 0x2162 }, + { 0x2173, 0x2163 }, + { 0x2174, 0x2164 }, + { 0x2175, 0x2165 }, + { 0x2176, 0x2166 }, + { 0x2177, 0x2167 }, + { 0x2178, 0x2168 }, + { 0x2179, 0x2169 }, + { 0x217A, 0x216A }, + { 0x217B, 0x216B }, + { 0x217C, 0x216C }, + { 0x217D, 0x216D }, + { 0x217E, 0x216E }, + { 0x217F, 0x216F }, + { 0x2184, 0x2183 }, { 0x24D0, 0x24B6 }, { 0x24D1, 0x24B7 }, { 0x24D2, 0x24B8 }, @@ -675,6 +864,348 @@ static const int caps_table[CAPS_LEN][2] = { { 0x24E7, 0x24CD }, { 0x24E8, 0x24CE }, { 0x24E9, 0x24CF }, + { 0x2C30, 0x2C00 }, + { 0x2C31, 0x2C01 }, + { 0x2C32, 0x2C02 }, + { 0x2C33, 0x2C03 }, + { 0x2C34, 0x2C04 }, + { 0x2C35, 0x2C05 }, + { 0x2C36, 0x2C06 }, + { 0x2C37, 0x2C07 }, + { 0x2C38, 0x2C08 }, + { 0x2C39, 0x2C09 }, + { 0x2C3A, 0x2C0A }, + { 0x2C3B, 0x2C0B }, + { 0x2C3C, 0x2C0C }, + { 0x2C3D, 0x2C0D }, + { 0x2C3E, 0x2C0E }, + { 0x2C3F, 0x2C0F }, + { 0x2C40, 0x2C10 }, + { 0x2C41, 0x2C11 }, + { 0x2C42, 0x2C12 }, + { 0x2C43, 0x2C13 }, + { 0x2C44, 0x2C14 }, + { 0x2C45, 0x2C15 }, + { 0x2C46, 0x2C16 }, + { 0x2C47, 0x2C17 }, + { 0x2C48, 0x2C18 }, + { 0x2C49, 0x2C19 }, + { 0x2C4A, 0x2C1A }, + { 0x2C4B, 0x2C1B }, + { 0x2C4C, 0x2C1C }, + { 0x2C4D, 0x2C1D }, + { 0x2C4E, 0x2C1E }, + { 0x2C4F, 0x2C1F }, + { 0x2C50, 0x2C20 }, + { 0x2C51, 0x2C21 }, + { 0x2C52, 0x2C22 }, + { 0x2C53, 0x2C23 }, + { 0x2C54, 0x2C24 }, + { 0x2C55, 0x2C25 }, + { 0x2C56, 0x2C26 }, + { 0x2C57, 0x2C27 }, + { 0x2C58, 0x2C28 }, + { 0x2C59, 0x2C29 }, + { 0x2C5A, 0x2C2A }, + { 0x2C5B, 0x2C2B }, + { 0x2C5C, 0x2C2C }, + { 0x2C5D, 0x2C2D }, + { 0x2C5E, 0x2C2E }, + { 0x2C5F, 0x2C2F }, + { 0x2C61, 0x2C60 }, + { 0x2C65, 0x023A }, + { 0x2C66, 0x023E }, + { 0x2C68, 0x2C67 }, + { 0x2C6A, 0x2C69 }, + { 0x2C6C, 0x2C6B }, + { 0x2C73, 0x2C72 }, + { 0x2C76, 0x2C75 }, + { 0x2C81, 0x2C80 }, + { 0x2C83, 0x2C82 }, + { 0x2C85, 0x2C84 }, + { 0x2C87, 0x2C86 }, + { 0x2C89, 0x2C88 }, + { 0x2C8B, 0x2C8A }, + { 0x2C8D, 0x2C8C }, + { 0x2C8F, 0x2C8E }, + { 0x2C91, 0x2C90 }, + { 0x2C93, 0x2C92 }, + { 0x2C95, 0x2C94 }, + { 0x2C97, 0x2C96 }, + { 0x2C99, 0x2C98 }, + { 0x2C9B, 0x2C9A }, + { 0x2C9D, 0x2C9C }, + { 0x2C9F, 0x2C9E }, + { 0x2CA1, 0x2CA0 }, + { 0x2CA3, 0x2CA2 }, + { 0x2CA5, 0x2CA4 }, + { 0x2CA7, 0x2CA6 }, + { 0x2CA9, 0x2CA8 }, + { 0x2CAB, 0x2CAA }, + { 0x2CAD, 0x2CAC }, + { 0x2CAF, 0x2CAE }, + { 0x2CB1, 0x2CB0 }, + { 0x2CB3, 0x2CB2 }, + { 0x2CB5, 0x2CB4 }, + { 0x2CB7, 0x2CB6 }, + { 0x2CB9, 0x2CB8 }, + { 0x2CBB, 0x2CBA }, + { 0x2CBD, 0x2CBC }, + { 0x2CBF, 0x2CBE }, + { 0x2CC1, 0x2CC0 }, + { 0x2CC3, 0x2CC2 }, + { 0x2CC5, 0x2CC4 }, + { 0x2CC7, 0x2CC6 }, + { 0x2CC9, 0x2CC8 }, + { 0x2CCB, 0x2CCA }, + { 0x2CCD, 0x2CCC }, + { 0x2CCF, 0x2CCE }, + { 0x2CD1, 0x2CD0 }, + { 0x2CD3, 0x2CD2 }, + { 0x2CD5, 0x2CD4 }, + { 0x2CD7, 0x2CD6 }, + { 0x2CD9, 0x2CD8 }, + { 0x2CDB, 0x2CDA }, + { 0x2CDD, 0x2CDC }, + { 0x2CDF, 0x2CDE }, + { 0x2CE1, 0x2CE0 }, + { 0x2CE3, 0x2CE2 }, + { 0x2CEC, 0x2CEB }, + { 0x2CEE, 0x2CED }, + { 0x2CF3, 0x2CF2 }, + { 0x2D00, 0x10A0 }, + { 0x2D01, 0x10A1 }, + { 0x2D02, 0x10A2 }, + { 0x2D03, 0x10A3 }, + { 0x2D04, 0x10A4 }, + { 0x2D05, 0x10A5 }, + { 0x2D06, 0x10A6 }, + { 0x2D07, 0x10A7 }, + { 0x2D08, 0x10A8 }, + { 0x2D09, 0x10A9 }, + { 0x2D0A, 0x10AA }, + { 0x2D0B, 0x10AB }, + { 0x2D0C, 0x10AC }, + { 0x2D0D, 0x10AD }, + { 0x2D0E, 0x10AE }, + { 0x2D0F, 0x10AF }, + { 0x2D10, 0x10B0 }, + { 0x2D11, 0x10B1 }, + { 0x2D12, 0x10B2 }, + { 0x2D13, 0x10B3 }, + { 0x2D14, 0x10B4 }, + { 0x2D15, 0x10B5 }, + { 0x2D16, 0x10B6 }, + { 0x2D17, 0x10B7 }, + { 0x2D18, 0x10B8 }, + { 0x2D19, 0x10B9 }, + { 0x2D1A, 0x10BA }, + { 0x2D1B, 0x10BB }, + { 0x2D1C, 0x10BC }, + { 0x2D1D, 0x10BD }, + { 0x2D1E, 0x10BE }, + { 0x2D1F, 0x10BF }, + { 0x2D20, 0x10C0 }, + { 0x2D21, 0x10C1 }, + { 0x2D22, 0x10C2 }, + { 0x2D23, 0x10C3 }, + { 0x2D24, 0x10C4 }, + { 0x2D25, 0x10C5 }, + { 0x2D27, 0x10C7 }, + { 0x2D2D, 0x10CD }, + { 0xA641, 0xA640 }, + { 0xA643, 0xA642 }, + { 0xA645, 0xA644 }, + { 0xA647, 0xA646 }, + { 0xA649, 0xA648 }, + { 0xA64B, 0xA64A }, + { 0xA64D, 0xA64C }, + { 0xA64F, 0xA64E }, + { 0xA651, 0xA650 }, + { 0xA653, 0xA652 }, + { 0xA655, 0xA654 }, + { 0xA657, 0xA656 }, + { 0xA659, 0xA658 }, + { 0xA65B, 0xA65A }, + { 0xA65D, 0xA65C }, + { 0xA65F, 0xA65E }, + { 0xA661, 0xA660 }, + { 0xA663, 0xA662 }, + { 0xA665, 0xA664 }, + { 0xA667, 0xA666 }, + { 0xA669, 0xA668 }, + { 0xA66B, 0xA66A }, + { 0xA66D, 0xA66C }, + { 0xA681, 0xA680 }, + { 0xA683, 0xA682 }, + { 0xA685, 0xA684 }, + { 0xA687, 0xA686 }, + { 0xA689, 0xA688 }, + { 0xA68B, 0xA68A }, + { 0xA68D, 0xA68C }, + { 0xA68F, 0xA68E }, + { 0xA691, 0xA690 }, + { 0xA693, 0xA692 }, + { 0xA695, 0xA694 }, + { 0xA697, 0xA696 }, + { 0xA699, 0xA698 }, + { 0xA69B, 0xA69A }, + { 0xA723, 0xA722 }, + { 0xA725, 0xA724 }, + { 0xA727, 0xA726 }, + { 0xA729, 0xA728 }, + { 0xA72B, 0xA72A }, + { 0xA72D, 0xA72C }, + { 0xA72F, 0xA72E }, + { 0xA733, 0xA732 }, + { 0xA735, 0xA734 }, + { 0xA737, 0xA736 }, + { 0xA739, 0xA738 }, + { 0xA73B, 0xA73A }, + { 0xA73D, 0xA73C }, + { 0xA73F, 0xA73E }, + { 0xA741, 0xA740 }, + { 0xA743, 0xA742 }, + { 0xA745, 0xA744 }, + { 0xA747, 0xA746 }, + { 0xA749, 0xA748 }, + { 0xA74B, 0xA74A }, + { 0xA74D, 0xA74C }, + { 0xA74F, 0xA74E }, + { 0xA751, 0xA750 }, + { 0xA753, 0xA752 }, + { 0xA755, 0xA754 }, + { 0xA757, 0xA756 }, + { 0xA759, 0xA758 }, + { 0xA75B, 0xA75A }, + { 0xA75D, 0xA75C }, + { 0xA75F, 0xA75E }, + { 0xA761, 0xA760 }, + { 0xA763, 0xA762 }, + { 0xA765, 0xA764 }, + { 0xA767, 0xA766 }, + { 0xA769, 0xA768 }, + { 0xA76B, 0xA76A }, + { 0xA76D, 0xA76C }, + { 0xA76F, 0xA76E }, + { 0xA77A, 0xA779 }, + { 0xA77C, 0xA77B }, + { 0xA77F, 0xA77E }, + { 0xA781, 0xA780 }, + { 0xA783, 0xA782 }, + { 0xA785, 0xA784 }, + { 0xA787, 0xA786 }, + { 0xA78C, 0xA78B }, + { 0xA791, 0xA790 }, + { 0xA793, 0xA792 }, + { 0xA794, 0xA7C4 }, + { 0xA797, 0xA796 }, + { 0xA799, 0xA798 }, + { 0xA79B, 0xA79A }, + { 0xA79D, 0xA79C }, + { 0xA79F, 0xA79E }, + { 0xA7A1, 0xA7A0 }, + { 0xA7A3, 0xA7A2 }, + { 0xA7A5, 0xA7A4 }, + { 0xA7A7, 0xA7A6 }, + { 0xA7A9, 0xA7A8 }, + { 0xA7B5, 0xA7B4 }, + { 0xA7B7, 0xA7B6 }, + { 0xA7B9, 0xA7B8 }, + { 0xA7BB, 0xA7BA }, + { 0xA7BD, 0xA7BC }, + { 0xA7BF, 0xA7BE }, + { 0xA7C1, 0xA7C0 }, + { 0xA7C3, 0xA7C2 }, + { 0xA7C8, 0xA7C7 }, + { 0xA7CA, 0xA7C9 }, + { 0xA7CD, 0xA7CC }, + { 0xA7D1, 0xA7D0 }, + { 0xA7D7, 0xA7D6 }, + { 0xA7D9, 0xA7D8 }, + { 0xA7DB, 0xA7DA }, + { 0xA7F6, 0xA7F5 }, + { 0xAB53, 0xA7B3 }, + { 0xAB70, 0x13A0 }, + { 0xAB71, 0x13A1 }, + { 0xAB72, 0x13A2 }, + { 0xAB73, 0x13A3 }, + { 0xAB74, 0x13A4 }, + { 0xAB75, 0x13A5 }, + { 0xAB76, 0x13A6 }, + { 0xAB77, 0x13A7 }, + { 0xAB78, 0x13A8 }, + { 0xAB79, 0x13A9 }, + { 0xAB7A, 0x13AA }, + { 0xAB7B, 0x13AB }, + { 0xAB7C, 0x13AC }, + { 0xAB7D, 0x13AD }, + { 0xAB7E, 0x13AE }, + { 0xAB7F, 0x13AF }, + { 0xAB80, 0x13B0 }, + { 0xAB81, 0x13B1 }, + { 0xAB82, 0x13B2 }, + { 0xAB83, 0x13B3 }, + { 0xAB84, 0x13B4 }, + { 0xAB85, 0x13B5 }, + { 0xAB86, 0x13B6 }, + { 0xAB87, 0x13B7 }, + { 0xAB88, 0x13B8 }, + { 0xAB89, 0x13B9 }, + { 0xAB8A, 0x13BA }, + { 0xAB8B, 0x13BB }, + { 0xAB8C, 0x13BC }, + { 0xAB8D, 0x13BD }, + { 0xAB8E, 0x13BE }, + { 0xAB8F, 0x13BF }, + { 0xAB90, 0x13C0 }, + { 0xAB91, 0x13C1 }, + { 0xAB92, 0x13C2 }, + { 0xAB93, 0x13C3 }, + { 0xAB94, 0x13C4 }, + { 0xAB95, 0x13C5 }, + { 0xAB96, 0x13C6 }, + { 0xAB97, 0x13C7 }, + { 0xAB98, 0x13C8 }, + { 0xAB99, 0x13C9 }, + { 0xAB9A, 0x13CA }, + { 0xAB9B, 0x13CB }, + { 0xAB9C, 0x13CC }, + { 0xAB9D, 0x13CD }, + { 0xAB9E, 0x13CE }, + { 0xAB9F, 0x13CF }, + { 0xABA0, 0x13D0 }, + { 0xABA1, 0x13D1 }, + { 0xABA2, 0x13D2 }, + { 0xABA3, 0x13D3 }, + { 0xABA4, 0x13D4 }, + { 0xABA5, 0x13D5 }, + { 0xABA6, 0x13D6 }, + { 0xABA7, 0x13D7 }, + { 0xABA8, 0x13D8 }, + { 0xABA9, 0x13D9 }, + { 0xABAA, 0x13DA }, + { 0xABAB, 0x13DB }, + { 0xABAC, 0x13DC }, + { 0xABAD, 0x13DD }, + { 0xABAE, 0x13DE }, + { 0xABAF, 0x13DF }, + { 0xABB0, 0x13E0 }, + { 0xABB1, 0x13E1 }, + { 0xABB2, 0x13E2 }, + { 0xABB3, 0x13E3 }, + { 0xABB4, 0x13E4 }, + { 0xABB5, 0x13E5 }, + { 0xABB6, 0x13E6 }, + { 0xABB7, 0x13E7 }, + { 0xABB8, 0x13E8 }, + { 0xABB9, 0x13E9 }, + { 0xABBA, 0x13EA }, + { 0xABBB, 0x13EB }, + { 0xABBC, 0x13EC }, + { 0xABBD, 0x13ED }, + { 0xABBE, 0x13EE }, + { 0xABBF, 0x13EF }, { 0xFF41, 0xFF21 }, { 0xFF42, 0xFF22 }, { 0xFF43, 0xFF23 }, @@ -701,9 +1232,291 @@ static const int caps_table[CAPS_LEN][2] = { { 0xFF58, 0xFF38 }, { 0xFF59, 0xFF39 }, { 0xFF5A, 0xFF3A }, + { 0x10428, 0x10400 }, + { 0x10429, 0x10401 }, + { 0x1042A, 0x10402 }, + { 0x1042B, 0x10403 }, + { 0x1042C, 0x10404 }, + { 0x1042D, 0x10405 }, + { 0x1042E, 0x10406 }, + { 0x1042F, 0x10407 }, + { 0x10430, 0x10408 }, + { 0x10431, 0x10409 }, + { 0x10432, 0x1040A }, + { 0x10433, 0x1040B }, + { 0x10434, 0x1040C }, + { 0x10435, 0x1040D }, + { 0x10436, 0x1040E }, + { 0x10437, 0x1040F }, + { 0x10438, 0x10410 }, + { 0x10439, 0x10411 }, + { 0x1043A, 0x10412 }, + { 0x1043B, 0x10413 }, + { 0x1043C, 0x10414 }, + { 0x1043D, 0x10415 }, + { 0x1043E, 0x10416 }, + { 0x1043F, 0x10417 }, + { 0x10440, 0x10418 }, + { 0x10441, 0x10419 }, + { 0x10442, 0x1041A }, + { 0x10443, 0x1041B }, + { 0x10444, 0x1041C }, + { 0x10445, 0x1041D }, + { 0x10446, 0x1041E }, + { 0x10447, 0x1041F }, + { 0x10448, 0x10420 }, + { 0x10449, 0x10421 }, + { 0x1044A, 0x10422 }, + { 0x1044B, 0x10423 }, + { 0x1044C, 0x10424 }, + { 0x1044D, 0x10425 }, + { 0x1044E, 0x10426 }, + { 0x1044F, 0x10427 }, + { 0x104D8, 0x104B0 }, + { 0x104D9, 0x104B1 }, + { 0x104DA, 0x104B2 }, + { 0x104DB, 0x104B3 }, + { 0x104DC, 0x104B4 }, + { 0x104DD, 0x104B5 }, + { 0x104DE, 0x104B6 }, + { 0x104DF, 0x104B7 }, + { 0x104E0, 0x104B8 }, + { 0x104E1, 0x104B9 }, + { 0x104E2, 0x104BA }, + { 0x104E3, 0x104BB }, + { 0x104E4, 0x104BC }, + { 0x104E5, 0x104BD }, + { 0x104E6, 0x104BE }, + { 0x104E7, 0x104BF }, + { 0x104E8, 0x104C0 }, + { 0x104E9, 0x104C1 }, + { 0x104EA, 0x104C2 }, + { 0x104EB, 0x104C3 }, + { 0x104EC, 0x104C4 }, + { 0x104ED, 0x104C5 }, + { 0x104EE, 0x104C6 }, + { 0x104EF, 0x104C7 }, + { 0x104F0, 0x104C8 }, + { 0x104F1, 0x104C9 }, + { 0x104F2, 0x104CA }, + { 0x104F3, 0x104CB }, + { 0x104F4, 0x104CC }, + { 0x104F5, 0x104CD }, + { 0x104F6, 0x104CE }, + { 0x104F7, 0x104CF }, + { 0x104F8, 0x104D0 }, + { 0x104F9, 0x104D1 }, + { 0x104FA, 0x104D2 }, + { 0x104FB, 0x104D3 }, + { 0x10597, 0x10570 }, + { 0x10598, 0x10571 }, + { 0x10599, 0x10572 }, + { 0x1059A, 0x10573 }, + { 0x1059B, 0x10574 }, + { 0x1059C, 0x10575 }, + { 0x1059D, 0x10576 }, + { 0x1059E, 0x10577 }, + { 0x1059F, 0x10578 }, + { 0x105A0, 0x10579 }, + { 0x105A1, 0x1057A }, + { 0x105A3, 0x1057C }, + { 0x105A4, 0x1057D }, + { 0x105A5, 0x1057E }, + { 0x105A6, 0x1057F }, + { 0x105A7, 0x10580 }, + { 0x105A8, 0x10581 }, + { 0x105A9, 0x10582 }, + { 0x105AA, 0x10583 }, + { 0x105AB, 0x10584 }, + { 0x105AC, 0x10585 }, + { 0x105AD, 0x10586 }, + { 0x105AE, 0x10587 }, + { 0x105AF, 0x10588 }, + { 0x105B0, 0x10589 }, + { 0x105B1, 0x1058A }, + { 0x105B3, 0x1058C }, + { 0x105B4, 0x1058D }, + { 0x105B5, 0x1058E }, + { 0x105B6, 0x1058F }, + { 0x105B7, 0x10590 }, + { 0x105B8, 0x10591 }, + { 0x105B9, 0x10592 }, + { 0x105BB, 0x10594 }, + { 0x105BC, 0x10595 }, + { 0x10CC0, 0x10C80 }, + { 0x10CC1, 0x10C81 }, + { 0x10CC2, 0x10C82 }, + { 0x10CC3, 0x10C83 }, + { 0x10CC4, 0x10C84 }, + { 0x10CC5, 0x10C85 }, + { 0x10CC6, 0x10C86 }, + { 0x10CC7, 0x10C87 }, + { 0x10CC8, 0x10C88 }, + { 0x10CC9, 0x10C89 }, + { 0x10CCA, 0x10C8A }, + { 0x10CCB, 0x10C8B }, + { 0x10CCC, 0x10C8C }, + { 0x10CCD, 0x10C8D }, + { 0x10CCE, 0x10C8E }, + { 0x10CCF, 0x10C8F }, + { 0x10CD0, 0x10C90 }, + { 0x10CD1, 0x10C91 }, + { 0x10CD2, 0x10C92 }, + { 0x10CD3, 0x10C93 }, + { 0x10CD4, 0x10C94 }, + { 0x10CD5, 0x10C95 }, + { 0x10CD6, 0x10C96 }, + { 0x10CD7, 0x10C97 }, + { 0x10CD8, 0x10C98 }, + { 0x10CD9, 0x10C99 }, + { 0x10CDA, 0x10C9A }, + { 0x10CDB, 0x10C9B }, + { 0x10CDC, 0x10C9C }, + { 0x10CDD, 0x10C9D }, + { 0x10CDE, 0x10C9E }, + { 0x10CDF, 0x10C9F }, + { 0x10CE0, 0x10CA0 }, + { 0x10CE1, 0x10CA1 }, + { 0x10CE2, 0x10CA2 }, + { 0x10CE3, 0x10CA3 }, + { 0x10CE4, 0x10CA4 }, + { 0x10CE5, 0x10CA5 }, + { 0x10CE6, 0x10CA6 }, + { 0x10CE7, 0x10CA7 }, + { 0x10CE8, 0x10CA8 }, + { 0x10CE9, 0x10CA9 }, + { 0x10CEA, 0x10CAA }, + { 0x10CEB, 0x10CAB }, + { 0x10CEC, 0x10CAC }, + { 0x10CED, 0x10CAD }, + { 0x10CEE, 0x10CAE }, + { 0x10CEF, 0x10CAF }, + { 0x10CF0, 0x10CB0 }, + { 0x10CF1, 0x10CB1 }, + { 0x10CF2, 0x10CB2 }, + { 0x10D70, 0x10D50 }, + { 0x10D71, 0x10D51 }, + { 0x10D72, 0x10D52 }, + { 0x10D73, 0x10D53 }, + { 0x10D74, 0x10D54 }, + { 0x10D75, 0x10D55 }, + { 0x10D76, 0x10D56 }, + { 0x10D77, 0x10D57 }, + { 0x10D78, 0x10D58 }, + { 0x10D79, 0x10D59 }, + { 0x10D7A, 0x10D5A }, + { 0x10D7B, 0x10D5B }, + { 0x10D7C, 0x10D5C }, + { 0x10D7D, 0x10D5D }, + { 0x10D7E, 0x10D5E }, + { 0x10D7F, 0x10D5F }, + { 0x10D80, 0x10D60 }, + { 0x10D81, 0x10D61 }, + { 0x10D82, 0x10D62 }, + { 0x10D83, 0x10D63 }, + { 0x10D84, 0x10D64 }, + { 0x10D85, 0x10D65 }, + { 0x118C0, 0x118A0 }, + { 0x118C1, 0x118A1 }, + { 0x118C2, 0x118A2 }, + { 0x118C3, 0x118A3 }, + { 0x118C4, 0x118A4 }, + { 0x118C5, 0x118A5 }, + { 0x118C6, 0x118A6 }, + { 0x118C7, 0x118A7 }, + { 0x118C8, 0x118A8 }, + { 0x118C9, 0x118A9 }, + { 0x118CA, 0x118AA }, + { 0x118CB, 0x118AB }, + { 0x118CC, 0x118AC }, + { 0x118CD, 0x118AD }, + { 0x118CE, 0x118AE }, + { 0x118CF, 0x118AF }, + { 0x118D0, 0x118B0 }, + { 0x118D1, 0x118B1 }, + { 0x118D2, 0x118B2 }, + { 0x118D3, 0x118B3 }, + { 0x118D4, 0x118B4 }, + { 0x118D5, 0x118B5 }, + { 0x118D6, 0x118B6 }, + { 0x118D7, 0x118B7 }, + { 0x118D8, 0x118B8 }, + { 0x118D9, 0x118B9 }, + { 0x118DA, 0x118BA }, + { 0x118DB, 0x118BB }, + { 0x118DC, 0x118BC }, + { 0x118DD, 0x118BD }, + { 0x118DE, 0x118BE }, + { 0x118DF, 0x118BF }, + { 0x16E60, 0x16E40 }, + { 0x16E61, 0x16E41 }, + { 0x16E62, 0x16E42 }, + { 0x16E63, 0x16E43 }, + { 0x16E64, 0x16E44 }, + { 0x16E65, 0x16E45 }, + { 0x16E66, 0x16E46 }, + { 0x16E67, 0x16E47 }, + { 0x16E68, 0x16E48 }, + { 0x16E69, 0x16E49 }, + { 0x16E6A, 0x16E4A }, + { 0x16E6B, 0x16E4B }, + { 0x16E6C, 0x16E4C }, + { 0x16E6D, 0x16E4D }, + { 0x16E6E, 0x16E4E }, + { 0x16E6F, 0x16E4F }, + { 0x16E70, 0x16E50 }, + { 0x16E71, 0x16E51 }, + { 0x16E72, 0x16E52 }, + { 0x16E73, 0x16E53 }, + { 0x16E74, 0x16E54 }, + { 0x16E75, 0x16E55 }, + { 0x16E76, 0x16E56 }, + { 0x16E77, 0x16E57 }, + { 0x16E78, 0x16E58 }, + { 0x16E79, 0x16E59 }, + { 0x16E7A, 0x16E5A }, + { 0x16E7B, 0x16E5B }, + { 0x16E7C, 0x16E5C }, + { 0x16E7D, 0x16E5D }, + { 0x16E7E, 0x16E5E }, + { 0x16E7F, 0x16E5F }, + { 0x1E922, 0x1E900 }, + { 0x1E923, 0x1E901 }, + { 0x1E924, 0x1E902 }, + { 0x1E925, 0x1E903 }, + { 0x1E926, 0x1E904 }, + { 0x1E927, 0x1E905 }, + { 0x1E928, 0x1E906 }, + { 0x1E929, 0x1E907 }, + { 0x1E92A, 0x1E908 }, + { 0x1E92B, 0x1E909 }, + { 0x1E92C, 0x1E90A }, + { 0x1E92D, 0x1E90B }, + { 0x1E92E, 0x1E90C }, + { 0x1E92F, 0x1E90D }, + { 0x1E930, 0x1E90E }, + { 0x1E931, 0x1E90F }, + { 0x1E932, 0x1E910 }, + { 0x1E933, 0x1E911 }, + { 0x1E934, 0x1E912 }, + { 0x1E935, 0x1E913 }, + { 0x1E936, 0x1E914 }, + { 0x1E937, 0x1E915 }, + { 0x1E938, 0x1E916 }, + { 0x1E939, 0x1E917 }, + { 0x1E93A, 0x1E918 }, + { 0x1E93B, 0x1E919 }, + { 0x1E93C, 0x1E91A }, + { 0x1E93D, 0x1E91B }, + { 0x1E93E, 0x1E91C }, + { 0x1E93F, 0x1E91D }, + { 0x1E940, 0x1E91E }, + { 0x1E941, 0x1E91F }, + { 0x1E942, 0x1E920 }, + { 0x1E943, 0x1E921 }, }; -static const int reverse_caps_table[CAPS_LEN - 1][2] = { +static const int reverse_caps_table[UTL_LEN][2] = { { 0x0041, 0x0061 }, { 0x0042, 0x0062 }, { 0x0043, 0x0063 }, @@ -713,7 +1526,6 @@ static const int reverse_caps_table[CAPS_LEN - 1][2] = { { 0x0047, 0x0067 }, { 0x0048, 0x0068 }, { 0x0049, 0x0069 }, - // { 0x0049, 0x0131 }, // dotless I { 0x004A, 0x006A }, { 0x004B, 0x006B }, { 0x004C, 0x006C }, @@ -785,6 +1597,7 @@ static const int reverse_caps_table[CAPS_LEN - 1][2] = { { 0x012A, 0x012B }, { 0x012C, 0x012D }, { 0x012E, 0x012F }, + { 0x0130, 0x0069 }, { 0x0132, 0x0133 }, { 0x0134, 0x0135 }, { 0x0136, 0x0137 }, @@ -828,9 +1641,10 @@ static const int reverse_caps_table[CAPS_LEN - 1][2] = { { 0x0184, 0x0185 }, { 0x0186, 0x0254 }, { 0x0187, 0x0188 }, + { 0x0189, 0x0256 }, { 0x018A, 0x0257 }, { 0x018B, 0x018C }, - { 0x018E, 0x0258 }, + { 0x018E, 0x01DD }, { 0x018F, 0x0259 }, { 0x0190, 0x025B }, { 0x0191, 0x0192 }, @@ -845,6 +1659,7 @@ static const int reverse_caps_table[CAPS_LEN - 1][2] = { { 0x01A0, 0x01A1 }, { 0x01A2, 0x01A3 }, { 0x01A4, 0x01A5 }, + { 0x01A6, 0x0280 }, { 0x01A7, 0x01A8 }, { 0x01A9, 0x0283 }, { 0x01AC, 0x01AD }, @@ -858,8 +1673,11 @@ static const int reverse_caps_table[CAPS_LEN - 1][2] = { { 0x01B8, 0x01B9 }, { 0x01BC, 0x01BD }, { 0x01C4, 0x01C6 }, + { 0x01C5, 0x01C6 }, { 0x01C7, 0x01C9 }, + { 0x01C8, 0x01C9 }, { 0x01CA, 0x01CC }, + { 0x01CB, 0x01CC }, { 0x01CD, 0x01CE }, { 0x01CF, 0x01D0 }, { 0x01D1, 0x01D2 }, @@ -878,7 +1696,11 @@ static const int reverse_caps_table[CAPS_LEN - 1][2] = { { 0x01EC, 0x01ED }, { 0x01EE, 0x01EF }, { 0x01F1, 0x01F3 }, + { 0x01F2, 0x01F3 }, { 0x01F4, 0x01F5 }, + { 0x01F6, 0x0195 }, + { 0x01F7, 0x01BF }, + { 0x01F8, 0x01F9 }, { 0x01FA, 0x01FB }, { 0x01FC, 0x01FD }, { 0x01FE, 0x01FF }, @@ -894,6 +1716,37 @@ static const int reverse_caps_table[CAPS_LEN - 1][2] = { { 0x0212, 0x0213 }, { 0x0214, 0x0215 }, { 0x0216, 0x0217 }, + { 0x0218, 0x0219 }, + { 0x021A, 0x021B }, + { 0x021C, 0x021D }, + { 0x021E, 0x021F }, + { 0x0220, 0x019E }, + { 0x0222, 0x0223 }, + { 0x0224, 0x0225 }, + { 0x0226, 0x0227 }, + { 0x0228, 0x0229 }, + { 0x022A, 0x022B }, + { 0x022C, 0x022D }, + { 0x022E, 0x022F }, + { 0x0230, 0x0231 }, + { 0x0232, 0x0233 }, + { 0x023A, 0x2C65 }, + { 0x023B, 0x023C }, + { 0x023D, 0x019A }, + { 0x023E, 0x2C66 }, + { 0x0241, 0x0242 }, + { 0x0243, 0x0180 }, + { 0x0244, 0x0289 }, + { 0x0245, 0x028C }, + { 0x0246, 0x0247 }, + { 0x0248, 0x0249 }, + { 0x024A, 0x024B }, + { 0x024C, 0x024D }, + { 0x024E, 0x024F }, + { 0x0370, 0x0371 }, + { 0x0372, 0x0373 }, + { 0x0376, 0x0377 }, + { 0x037F, 0x03F3 }, { 0x0386, 0x03AC }, { 0x0388, 0x03AD }, { 0x0389, 0x03AE }, @@ -927,6 +1780,12 @@ static const int reverse_caps_table[CAPS_LEN - 1][2] = { { 0x03A9, 0x03C9 }, { 0x03AA, 0x03CA }, { 0x03AB, 0x03CB }, + { 0x03CF, 0x03D7 }, + { 0x03D8, 0x03D9 }, + { 0x03DA, 0x03DB }, + { 0x03DC, 0x03DD }, + { 0x03DE, 0x03DF }, + { 0x03E0, 0x03E1 }, { 0x03E2, 0x03E3 }, { 0x03E4, 0x03E5 }, { 0x03E6, 0x03E7 }, @@ -934,6 +1793,14 @@ static const int reverse_caps_table[CAPS_LEN - 1][2] = { { 0x03EA, 0x03EB }, { 0x03EC, 0x03ED }, { 0x03EE, 0x03EF }, + { 0x03F4, 0x03B8 }, + { 0x03F7, 0x03F8 }, + { 0x03F9, 0x03F2 }, + { 0x03FA, 0x03FB }, + { 0x03FD, 0x037B }, + { 0x03FE, 0x037C }, + { 0x03FF, 0x037D }, + { 0x0400, 0x0450 }, { 0x0401, 0x0451 }, { 0x0402, 0x0452 }, { 0x0403, 0x0453 }, @@ -946,6 +1813,7 @@ static const int reverse_caps_table[CAPS_LEN - 1][2] = { { 0x040A, 0x045A }, { 0x040B, 0x045B }, { 0x040C, 0x045C }, + { 0x040D, 0x045D }, { 0x040E, 0x045E }, { 0x040F, 0x045F }, { 0x0410, 0x0430 }, @@ -997,6 +1865,9 @@ static const int reverse_caps_table[CAPS_LEN - 1][2] = { { 0x047C, 0x047D }, { 0x047E, 0x047F }, { 0x0480, 0x0481 }, + { 0x048A, 0x048B }, + { 0x048C, 0x048D }, + { 0x048E, 0x048F }, { 0x0490, 0x0491 }, { 0x0492, 0x0493 }, { 0x0494, 0x0495 }, @@ -1021,10 +1892,14 @@ static const int reverse_caps_table[CAPS_LEN - 1][2] = { { 0x04BA, 0x04BB }, { 0x04BC, 0x04BD }, { 0x04BE, 0x04BF }, + { 0x04C0, 0x04CF }, { 0x04C1, 0x04C2 }, { 0x04C3, 0x04C4 }, + { 0x04C5, 0x04C6 }, { 0x04C7, 0x04C8 }, + { 0x04C9, 0x04CA }, { 0x04CB, 0x04CC }, + { 0x04CD, 0x04CE }, { 0x04D0, 0x04D1 }, { 0x04D2, 0x04D3 }, { 0x04D4, 0x04D5 }, @@ -1039,11 +1914,40 @@ static const int reverse_caps_table[CAPS_LEN - 1][2] = { { 0x04E6, 0x04E7 }, { 0x04E8, 0x04E9 }, { 0x04EA, 0x04EB }, + { 0x04EC, 0x04ED }, { 0x04EE, 0x04EF }, { 0x04F0, 0x04F1 }, { 0x04F2, 0x04F3 }, { 0x04F4, 0x04F5 }, + { 0x04F6, 0x04F7 }, { 0x04F8, 0x04F9 }, + { 0x04FA, 0x04FB }, + { 0x04FC, 0x04FD }, + { 0x04FE, 0x04FF }, + { 0x0500, 0x0501 }, + { 0x0502, 0x0503 }, + { 0x0504, 0x0505 }, + { 0x0506, 0x0507 }, + { 0x0508, 0x0509 }, + { 0x050A, 0x050B }, + { 0x050C, 0x050D }, + { 0x050E, 0x050F }, + { 0x0510, 0x0511 }, + { 0x0512, 0x0513 }, + { 0x0514, 0x0515 }, + { 0x0516, 0x0517 }, + { 0x0518, 0x0519 }, + { 0x051A, 0x051B }, + { 0x051C, 0x051D }, + { 0x051E, 0x051F }, + { 0x0520, 0x0521 }, + { 0x0522, 0x0523 }, + { 0x0524, 0x0525 }, + { 0x0526, 0x0527 }, + { 0x0528, 0x0529 }, + { 0x052A, 0x052B }, + { 0x052C, 0x052D }, + { 0x052E, 0x052F }, { 0x0531, 0x0561 }, { 0x0532, 0x0562 }, { 0x0533, 0x0563 }, @@ -1082,44 +1986,179 @@ static const int reverse_caps_table[CAPS_LEN - 1][2] = { { 0x0554, 0x0584 }, { 0x0555, 0x0585 }, { 0x0556, 0x0586 }, - { 0x10A0, 0x10D0 }, - { 0x10A1, 0x10D1 }, - { 0x10A2, 0x10D2 }, - { 0x10A3, 0x10D3 }, - { 0x10A4, 0x10D4 }, - { 0x10A5, 0x10D5 }, - { 0x10A6, 0x10D6 }, - { 0x10A7, 0x10D7 }, - { 0x10A8, 0x10D8 }, - { 0x10A9, 0x10D9 }, - { 0x10AA, 0x10DA }, - { 0x10AB, 0x10DB }, - { 0x10AC, 0x10DC }, - { 0x10AD, 0x10DD }, - { 0x10AE, 0x10DE }, - { 0x10AF, 0x10DF }, - { 0x10B0, 0x10E0 }, - { 0x10B1, 0x10E1 }, - { 0x10B2, 0x10E2 }, - { 0x10B3, 0x10E3 }, - { 0x10B4, 0x10E4 }, - { 0x10B5, 0x10E5 }, - { 0x10B6, 0x10E6 }, - { 0x10B7, 0x10E7 }, - { 0x10B8, 0x10E8 }, - { 0x10B9, 0x10E9 }, - { 0x10BA, 0x10EA }, - { 0x10BB, 0x10EB }, - { 0x10BC, 0x10EC }, - { 0x10BD, 0x10ED }, - { 0x10BE, 0x10EE }, - { 0x10BF, 0x10EF }, - { 0x10C0, 0x10F0 }, - { 0x10C1, 0x10F1 }, - { 0x10C2, 0x10F2 }, - { 0x10C3, 0x10F3 }, - { 0x10C4, 0x10F4 }, - { 0x10C5, 0x10F5 }, + { 0x10A0, 0x2D00 }, + { 0x10A1, 0x2D01 }, + { 0x10A2, 0x2D02 }, + { 0x10A3, 0x2D03 }, + { 0x10A4, 0x2D04 }, + { 0x10A5, 0x2D05 }, + { 0x10A6, 0x2D06 }, + { 0x10A7, 0x2D07 }, + { 0x10A8, 0x2D08 }, + { 0x10A9, 0x2D09 }, + { 0x10AA, 0x2D0A }, + { 0x10AB, 0x2D0B }, + { 0x10AC, 0x2D0C }, + { 0x10AD, 0x2D0D }, + { 0x10AE, 0x2D0E }, + { 0x10AF, 0x2D0F }, + { 0x10B0, 0x2D10 }, + { 0x10B1, 0x2D11 }, + { 0x10B2, 0x2D12 }, + { 0x10B3, 0x2D13 }, + { 0x10B4, 0x2D14 }, + { 0x10B5, 0x2D15 }, + { 0x10B6, 0x2D16 }, + { 0x10B7, 0x2D17 }, + { 0x10B8, 0x2D18 }, + { 0x10B9, 0x2D19 }, + { 0x10BA, 0x2D1A }, + { 0x10BB, 0x2D1B }, + { 0x10BC, 0x2D1C }, + { 0x10BD, 0x2D1D }, + { 0x10BE, 0x2D1E }, + { 0x10BF, 0x2D1F }, + { 0x10C0, 0x2D20 }, + { 0x10C1, 0x2D21 }, + { 0x10C2, 0x2D22 }, + { 0x10C3, 0x2D23 }, + { 0x10C4, 0x2D24 }, + { 0x10C5, 0x2D25 }, + { 0x10C7, 0x2D27 }, + { 0x10CD, 0x2D2D }, + { 0x13A0, 0xAB70 }, + { 0x13A1, 0xAB71 }, + { 0x13A2, 0xAB72 }, + { 0x13A3, 0xAB73 }, + { 0x13A4, 0xAB74 }, + { 0x13A5, 0xAB75 }, + { 0x13A6, 0xAB76 }, + { 0x13A7, 0xAB77 }, + { 0x13A8, 0xAB78 }, + { 0x13A9, 0xAB79 }, + { 0x13AA, 0xAB7A }, + { 0x13AB, 0xAB7B }, + { 0x13AC, 0xAB7C }, + { 0x13AD, 0xAB7D }, + { 0x13AE, 0xAB7E }, + { 0x13AF, 0xAB7F }, + { 0x13B0, 0xAB80 }, + { 0x13B1, 0xAB81 }, + { 0x13B2, 0xAB82 }, + { 0x13B3, 0xAB83 }, + { 0x13B4, 0xAB84 }, + { 0x13B5, 0xAB85 }, + { 0x13B6, 0xAB86 }, + { 0x13B7, 0xAB87 }, + { 0x13B8, 0xAB88 }, + { 0x13B9, 0xAB89 }, + { 0x13BA, 0xAB8A }, + { 0x13BB, 0xAB8B }, + { 0x13BC, 0xAB8C }, + { 0x13BD, 0xAB8D }, + { 0x13BE, 0xAB8E }, + { 0x13BF, 0xAB8F }, + { 0x13C0, 0xAB90 }, + { 0x13C1, 0xAB91 }, + { 0x13C2, 0xAB92 }, + { 0x13C3, 0xAB93 }, + { 0x13C4, 0xAB94 }, + { 0x13C5, 0xAB95 }, + { 0x13C6, 0xAB96 }, + { 0x13C7, 0xAB97 }, + { 0x13C8, 0xAB98 }, + { 0x13C9, 0xAB99 }, + { 0x13CA, 0xAB9A }, + { 0x13CB, 0xAB9B }, + { 0x13CC, 0xAB9C }, + { 0x13CD, 0xAB9D }, + { 0x13CE, 0xAB9E }, + { 0x13CF, 0xAB9F }, + { 0x13D0, 0xABA0 }, + { 0x13D1, 0xABA1 }, + { 0x13D2, 0xABA2 }, + { 0x13D3, 0xABA3 }, + { 0x13D4, 0xABA4 }, + { 0x13D5, 0xABA5 }, + { 0x13D6, 0xABA6 }, + { 0x13D7, 0xABA7 }, + { 0x13D8, 0xABA8 }, + { 0x13D9, 0xABA9 }, + { 0x13DA, 0xABAA }, + { 0x13DB, 0xABAB }, + { 0x13DC, 0xABAC }, + { 0x13DD, 0xABAD }, + { 0x13DE, 0xABAE }, + { 0x13DF, 0xABAF }, + { 0x13E0, 0xABB0 }, + { 0x13E1, 0xABB1 }, + { 0x13E2, 0xABB2 }, + { 0x13E3, 0xABB3 }, + { 0x13E4, 0xABB4 }, + { 0x13E5, 0xABB5 }, + { 0x13E6, 0xABB6 }, + { 0x13E7, 0xABB7 }, + { 0x13E8, 0xABB8 }, + { 0x13E9, 0xABB9 }, + { 0x13EA, 0xABBA }, + { 0x13EB, 0xABBB }, + { 0x13EC, 0xABBC }, + { 0x13ED, 0xABBD }, + { 0x13EE, 0xABBE }, + { 0x13EF, 0xABBF }, + { 0x13F0, 0x13F8 }, + { 0x13F1, 0x13F9 }, + { 0x13F2, 0x13FA }, + { 0x13F3, 0x13FB }, + { 0x13F4, 0x13FC }, + { 0x13F5, 0x13FD }, + { 0x1C89, 0x1C8A }, + { 0x1C90, 0x10D0 }, + { 0x1C91, 0x10D1 }, + { 0x1C92, 0x10D2 }, + { 0x1C93, 0x10D3 }, + { 0x1C94, 0x10D4 }, + { 0x1C95, 0x10D5 }, + { 0x1C96, 0x10D6 }, + { 0x1C97, 0x10D7 }, + { 0x1C98, 0x10D8 }, + { 0x1C99, 0x10D9 }, + { 0x1C9A, 0x10DA }, + { 0x1C9B, 0x10DB }, + { 0x1C9C, 0x10DC }, + { 0x1C9D, 0x10DD }, + { 0x1C9E, 0x10DE }, + { 0x1C9F, 0x10DF }, + { 0x1CA0, 0x10E0 }, + { 0x1CA1, 0x10E1 }, + { 0x1CA2, 0x10E2 }, + { 0x1CA3, 0x10E3 }, + { 0x1CA4, 0x10E4 }, + { 0x1CA5, 0x10E5 }, + { 0x1CA6, 0x10E6 }, + { 0x1CA7, 0x10E7 }, + { 0x1CA8, 0x10E8 }, + { 0x1CA9, 0x10E9 }, + { 0x1CAA, 0x10EA }, + { 0x1CAB, 0x10EB }, + { 0x1CAC, 0x10EC }, + { 0x1CAD, 0x10ED }, + { 0x1CAE, 0x10EE }, + { 0x1CAF, 0x10EF }, + { 0x1CB0, 0x10F0 }, + { 0x1CB1, 0x10F1 }, + { 0x1CB2, 0x10F2 }, + { 0x1CB3, 0x10F3 }, + { 0x1CB4, 0x10F4 }, + { 0x1CB5, 0x10F5 }, + { 0x1CB6, 0x10F6 }, + { 0x1CB7, 0x10F7 }, + { 0x1CB8, 0x10F8 }, + { 0x1CB9, 0x10F9 }, + { 0x1CBA, 0x10FA }, + { 0x1CBD, 0x10FD }, + { 0x1CBE, 0x10FE }, + { 0x1CBF, 0x10FF }, { 0x1E00, 0x1E01 }, { 0x1E02, 0x1E03 }, { 0x1E04, 0x1E05 }, @@ -1195,6 +2234,7 @@ static const int reverse_caps_table[CAPS_LEN - 1][2] = { { 0x1E90, 0x1E91 }, { 0x1E92, 0x1E93 }, { 0x1E94, 0x1E95 }, + { 0x1E9E, 0x00DF }, { 0x1EA0, 0x1EA1 }, { 0x1EA2, 0x1EA3 }, { 0x1EA4, 0x1EA5 }, @@ -1240,6 +2280,9 @@ static const int reverse_caps_table[CAPS_LEN - 1][2] = { { 0x1EF4, 0x1EF5 }, { 0x1EF6, 0x1EF7 }, { 0x1EF8, 0x1EF9 }, + { 0x1EFA, 0x1EFB }, + { 0x1EFC, 0x1EFD }, + { 0x1EFE, 0x1EFF }, { 0x1F08, 0x1F00 }, { 0x1F09, 0x1F01 }, { 0x1F0A, 0x1F02 }, @@ -1314,10 +2357,49 @@ static const int reverse_caps_table[CAPS_LEN - 1][2] = { { 0x1FAF, 0x1FA7 }, { 0x1FB8, 0x1FB0 }, { 0x1FB9, 0x1FB1 }, + { 0x1FBA, 0x1F70 }, + { 0x1FBB, 0x1F71 }, + { 0x1FBC, 0x1FB3 }, + { 0x1FC8, 0x1F72 }, + { 0x1FC9, 0x1F73 }, + { 0x1FCA, 0x1F74 }, + { 0x1FCB, 0x1F75 }, + { 0x1FCC, 0x1FC3 }, { 0x1FD8, 0x1FD0 }, { 0x1FD9, 0x1FD1 }, + { 0x1FDA, 0x1F76 }, + { 0x1FDB, 0x1F77 }, { 0x1FE8, 0x1FE0 }, { 0x1FE9, 0x1FE1 }, + { 0x1FEA, 0x1F7A }, + { 0x1FEB, 0x1F7B }, + { 0x1FEC, 0x1FE5 }, + { 0x1FF8, 0x1F78 }, + { 0x1FF9, 0x1F79 }, + { 0x1FFA, 0x1F7C }, + { 0x1FFB, 0x1F7D }, + { 0x1FFC, 0x1FF3 }, + { 0x2126, 0x03C9 }, + { 0x212A, 0x006B }, + { 0x212B, 0x00E5 }, + { 0x2132, 0x214E }, + { 0x2160, 0x2170 }, + { 0x2161, 0x2171 }, + { 0x2162, 0x2172 }, + { 0x2163, 0x2173 }, + { 0x2164, 0x2174 }, + { 0x2165, 0x2175 }, + { 0x2166, 0x2176 }, + { 0x2167, 0x2177 }, + { 0x2168, 0x2178 }, + { 0x2169, 0x2179 }, + { 0x216A, 0x217A }, + { 0x216B, 0x217B }, + { 0x216C, 0x217C }, + { 0x216D, 0x217D }, + { 0x216E, 0x217E }, + { 0x216F, 0x217F }, + { 0x2183, 0x2184 }, { 0x24B6, 0x24D0 }, { 0x24B7, 0x24D1 }, { 0x24B8, 0x24D2 }, @@ -1344,6 +2426,249 @@ static const int reverse_caps_table[CAPS_LEN - 1][2] = { { 0x24CD, 0x24E7 }, { 0x24CE, 0x24E8 }, { 0x24CF, 0x24E9 }, + { 0x2C00, 0x2C30 }, + { 0x2C01, 0x2C31 }, + { 0x2C02, 0x2C32 }, + { 0x2C03, 0x2C33 }, + { 0x2C04, 0x2C34 }, + { 0x2C05, 0x2C35 }, + { 0x2C06, 0x2C36 }, + { 0x2C07, 0x2C37 }, + { 0x2C08, 0x2C38 }, + { 0x2C09, 0x2C39 }, + { 0x2C0A, 0x2C3A }, + { 0x2C0B, 0x2C3B }, + { 0x2C0C, 0x2C3C }, + { 0x2C0D, 0x2C3D }, + { 0x2C0E, 0x2C3E }, + { 0x2C0F, 0x2C3F }, + { 0x2C10, 0x2C40 }, + { 0x2C11, 0x2C41 }, + { 0x2C12, 0x2C42 }, + { 0x2C13, 0x2C43 }, + { 0x2C14, 0x2C44 }, + { 0x2C15, 0x2C45 }, + { 0x2C16, 0x2C46 }, + { 0x2C17, 0x2C47 }, + { 0x2C18, 0x2C48 }, + { 0x2C19, 0x2C49 }, + { 0x2C1A, 0x2C4A }, + { 0x2C1B, 0x2C4B }, + { 0x2C1C, 0x2C4C }, + { 0x2C1D, 0x2C4D }, + { 0x2C1E, 0x2C4E }, + { 0x2C1F, 0x2C4F }, + { 0x2C20, 0x2C50 }, + { 0x2C21, 0x2C51 }, + { 0x2C22, 0x2C52 }, + { 0x2C23, 0x2C53 }, + { 0x2C24, 0x2C54 }, + { 0x2C25, 0x2C55 }, + { 0x2C26, 0x2C56 }, + { 0x2C27, 0x2C57 }, + { 0x2C28, 0x2C58 }, + { 0x2C29, 0x2C59 }, + { 0x2C2A, 0x2C5A }, + { 0x2C2B, 0x2C5B }, + { 0x2C2C, 0x2C5C }, + { 0x2C2D, 0x2C5D }, + { 0x2C2E, 0x2C5E }, + { 0x2C2F, 0x2C5F }, + { 0x2C60, 0x2C61 }, + { 0x2C62, 0x026B }, + { 0x2C63, 0x1D7D }, + { 0x2C64, 0x027D }, + { 0x2C67, 0x2C68 }, + { 0x2C69, 0x2C6A }, + { 0x2C6B, 0x2C6C }, + { 0x2C6D, 0x0251 }, + { 0x2C6E, 0x0271 }, + { 0x2C6F, 0x0250 }, + { 0x2C70, 0x0252 }, + { 0x2C72, 0x2C73 }, + { 0x2C75, 0x2C76 }, + { 0x2C7E, 0x023F }, + { 0x2C7F, 0x0240 }, + { 0x2C80, 0x2C81 }, + { 0x2C82, 0x2C83 }, + { 0x2C84, 0x2C85 }, + { 0x2C86, 0x2C87 }, + { 0x2C88, 0x2C89 }, + { 0x2C8A, 0x2C8B }, + { 0x2C8C, 0x2C8D }, + { 0x2C8E, 0x2C8F }, + { 0x2C90, 0x2C91 }, + { 0x2C92, 0x2C93 }, + { 0x2C94, 0x2C95 }, + { 0x2C96, 0x2C97 }, + { 0x2C98, 0x2C99 }, + { 0x2C9A, 0x2C9B }, + { 0x2C9C, 0x2C9D }, + { 0x2C9E, 0x2C9F }, + { 0x2CA0, 0x2CA1 }, + { 0x2CA2, 0x2CA3 }, + { 0x2CA4, 0x2CA5 }, + { 0x2CA6, 0x2CA7 }, + { 0x2CA8, 0x2CA9 }, + { 0x2CAA, 0x2CAB }, + { 0x2CAC, 0x2CAD }, + { 0x2CAE, 0x2CAF }, + { 0x2CB0, 0x2CB1 }, + { 0x2CB2, 0x2CB3 }, + { 0x2CB4, 0x2CB5 }, + { 0x2CB6, 0x2CB7 }, + { 0x2CB8, 0x2CB9 }, + { 0x2CBA, 0x2CBB }, + { 0x2CBC, 0x2CBD }, + { 0x2CBE, 0x2CBF }, + { 0x2CC0, 0x2CC1 }, + { 0x2CC2, 0x2CC3 }, + { 0x2CC4, 0x2CC5 }, + { 0x2CC6, 0x2CC7 }, + { 0x2CC8, 0x2CC9 }, + { 0x2CCA, 0x2CCB }, + { 0x2CCC, 0x2CCD }, + { 0x2CCE, 0x2CCF }, + { 0x2CD0, 0x2CD1 }, + { 0x2CD2, 0x2CD3 }, + { 0x2CD4, 0x2CD5 }, + { 0x2CD6, 0x2CD7 }, + { 0x2CD8, 0x2CD9 }, + { 0x2CDA, 0x2CDB }, + { 0x2CDC, 0x2CDD }, + { 0x2CDE, 0x2CDF }, + { 0x2CE0, 0x2CE1 }, + { 0x2CE2, 0x2CE3 }, + { 0x2CEB, 0x2CEC }, + { 0x2CED, 0x2CEE }, + { 0x2CF2, 0x2CF3 }, + { 0xA640, 0xA641 }, + { 0xA642, 0xA643 }, + { 0xA644, 0xA645 }, + { 0xA646, 0xA647 }, + { 0xA648, 0xA649 }, + { 0xA64A, 0xA64B }, + { 0xA64C, 0xA64D }, + { 0xA64E, 0xA64F }, + { 0xA650, 0xA651 }, + { 0xA652, 0xA653 }, + { 0xA654, 0xA655 }, + { 0xA656, 0xA657 }, + { 0xA658, 0xA659 }, + { 0xA65A, 0xA65B }, + { 0xA65C, 0xA65D }, + { 0xA65E, 0xA65F }, + { 0xA660, 0xA661 }, + { 0xA662, 0xA663 }, + { 0xA664, 0xA665 }, + { 0xA666, 0xA667 }, + { 0xA668, 0xA669 }, + { 0xA66A, 0xA66B }, + { 0xA66C, 0xA66D }, + { 0xA680, 0xA681 }, + { 0xA682, 0xA683 }, + { 0xA684, 0xA685 }, + { 0xA686, 0xA687 }, + { 0xA688, 0xA689 }, + { 0xA68A, 0xA68B }, + { 0xA68C, 0xA68D }, + { 0xA68E, 0xA68F }, + { 0xA690, 0xA691 }, + { 0xA692, 0xA693 }, + { 0xA694, 0xA695 }, + { 0xA696, 0xA697 }, + { 0xA698, 0xA699 }, + { 0xA69A, 0xA69B }, + { 0xA722, 0xA723 }, + { 0xA724, 0xA725 }, + { 0xA726, 0xA727 }, + { 0xA728, 0xA729 }, + { 0xA72A, 0xA72B }, + { 0xA72C, 0xA72D }, + { 0xA72E, 0xA72F }, + { 0xA732, 0xA733 }, + { 0xA734, 0xA735 }, + { 0xA736, 0xA737 }, + { 0xA738, 0xA739 }, + { 0xA73A, 0xA73B }, + { 0xA73C, 0xA73D }, + { 0xA73E, 0xA73F }, + { 0xA740, 0xA741 }, + { 0xA742, 0xA743 }, + { 0xA744, 0xA745 }, + { 0xA746, 0xA747 }, + { 0xA748, 0xA749 }, + { 0xA74A, 0xA74B }, + { 0xA74C, 0xA74D }, + { 0xA74E, 0xA74F }, + { 0xA750, 0xA751 }, + { 0xA752, 0xA753 }, + { 0xA754, 0xA755 }, + { 0xA756, 0xA757 }, + { 0xA758, 0xA759 }, + { 0xA75A, 0xA75B }, + { 0xA75C, 0xA75D }, + { 0xA75E, 0xA75F }, + { 0xA760, 0xA761 }, + { 0xA762, 0xA763 }, + { 0xA764, 0xA765 }, + { 0xA766, 0xA767 }, + { 0xA768, 0xA769 }, + { 0xA76A, 0xA76B }, + { 0xA76C, 0xA76D }, + { 0xA76E, 0xA76F }, + { 0xA779, 0xA77A }, + { 0xA77B, 0xA77C }, + { 0xA77D, 0x1D79 }, + { 0xA77E, 0xA77F }, + { 0xA780, 0xA781 }, + { 0xA782, 0xA783 }, + { 0xA784, 0xA785 }, + { 0xA786, 0xA787 }, + { 0xA78B, 0xA78C }, + { 0xA78D, 0x0265 }, + { 0xA790, 0xA791 }, + { 0xA792, 0xA793 }, + { 0xA796, 0xA797 }, + { 0xA798, 0xA799 }, + { 0xA79A, 0xA79B }, + { 0xA79C, 0xA79D }, + { 0xA79E, 0xA79F }, + { 0xA7A0, 0xA7A1 }, + { 0xA7A2, 0xA7A3 }, + { 0xA7A4, 0xA7A5 }, + { 0xA7A6, 0xA7A7 }, + { 0xA7A8, 0xA7A9 }, + { 0xA7AA, 0x0266 }, + { 0xA7AB, 0x025C }, + { 0xA7AC, 0x0261 }, + { 0xA7AD, 0x026C }, + { 0xA7AE, 0x026A }, + { 0xA7B0, 0x029E }, + { 0xA7B1, 0x0287 }, + { 0xA7B2, 0x029D }, + { 0xA7B3, 0xAB53 }, + { 0xA7B4, 0xA7B5 }, + { 0xA7B6, 0xA7B7 }, + { 0xA7B8, 0xA7B9 }, + { 0xA7BA, 0xA7BB }, + { 0xA7BC, 0xA7BD }, + { 0xA7BE, 0xA7BF }, + { 0xA7C0, 0xA7C1 }, + { 0xA7C2, 0xA7C3 }, + { 0xA7C4, 0xA794 }, + { 0xA7C5, 0x0282 }, + { 0xA7C6, 0x1D8E }, + { 0xA7C7, 0xA7C8 }, + { 0xA7C9, 0xA7CA }, + { 0xA7CB, 0x0264 }, + { 0xA7CC, 0xA7CD }, + { 0xA7D0, 0xA7D1 }, + { 0xA7D6, 0xA7D7 }, + { 0xA7D8, 0xA7D9 }, + { 0xA7DA, 0xA7DB }, + { 0xA7DC, 0x019B }, + { 0xA7F5, 0xA7F6 }, { 0xFF21, 0xFF41 }, { 0xFF22, 0xFF42 }, { 0xFF23, 0xFF43 }, @@ -1370,20 +2695,302 @@ static const int reverse_caps_table[CAPS_LEN - 1][2] = { { 0xFF38, 0xFF58 }, { 0xFF39, 0xFF59 }, { 0xFF3A, 0xFF5A }, + { 0x10400, 0x10428 }, + { 0x10401, 0x10429 }, + { 0x10402, 0x1042A }, + { 0x10403, 0x1042B }, + { 0x10404, 0x1042C }, + { 0x10405, 0x1042D }, + { 0x10406, 0x1042E }, + { 0x10407, 0x1042F }, + { 0x10408, 0x10430 }, + { 0x10409, 0x10431 }, + { 0x1040A, 0x10432 }, + { 0x1040B, 0x10433 }, + { 0x1040C, 0x10434 }, + { 0x1040D, 0x10435 }, + { 0x1040E, 0x10436 }, + { 0x1040F, 0x10437 }, + { 0x10410, 0x10438 }, + { 0x10411, 0x10439 }, + { 0x10412, 0x1043A }, + { 0x10413, 0x1043B }, + { 0x10414, 0x1043C }, + { 0x10415, 0x1043D }, + { 0x10416, 0x1043E }, + { 0x10417, 0x1043F }, + { 0x10418, 0x10440 }, + { 0x10419, 0x10441 }, + { 0x1041A, 0x10442 }, + { 0x1041B, 0x10443 }, + { 0x1041C, 0x10444 }, + { 0x1041D, 0x10445 }, + { 0x1041E, 0x10446 }, + { 0x1041F, 0x10447 }, + { 0x10420, 0x10448 }, + { 0x10421, 0x10449 }, + { 0x10422, 0x1044A }, + { 0x10423, 0x1044B }, + { 0x10424, 0x1044C }, + { 0x10425, 0x1044D }, + { 0x10426, 0x1044E }, + { 0x10427, 0x1044F }, + { 0x104B0, 0x104D8 }, + { 0x104B1, 0x104D9 }, + { 0x104B2, 0x104DA }, + { 0x104B3, 0x104DB }, + { 0x104B4, 0x104DC }, + { 0x104B5, 0x104DD }, + { 0x104B6, 0x104DE }, + { 0x104B7, 0x104DF }, + { 0x104B8, 0x104E0 }, + { 0x104B9, 0x104E1 }, + { 0x104BA, 0x104E2 }, + { 0x104BB, 0x104E3 }, + { 0x104BC, 0x104E4 }, + { 0x104BD, 0x104E5 }, + { 0x104BE, 0x104E6 }, + { 0x104BF, 0x104E7 }, + { 0x104C0, 0x104E8 }, + { 0x104C1, 0x104E9 }, + { 0x104C2, 0x104EA }, + { 0x104C3, 0x104EB }, + { 0x104C4, 0x104EC }, + { 0x104C5, 0x104ED }, + { 0x104C6, 0x104EE }, + { 0x104C7, 0x104EF }, + { 0x104C8, 0x104F0 }, + { 0x104C9, 0x104F1 }, + { 0x104CA, 0x104F2 }, + { 0x104CB, 0x104F3 }, + { 0x104CC, 0x104F4 }, + { 0x104CD, 0x104F5 }, + { 0x104CE, 0x104F6 }, + { 0x104CF, 0x104F7 }, + { 0x104D0, 0x104F8 }, + { 0x104D1, 0x104F9 }, + { 0x104D2, 0x104FA }, + { 0x104D3, 0x104FB }, + { 0x10570, 0x10597 }, + { 0x10571, 0x10598 }, + { 0x10572, 0x10599 }, + { 0x10573, 0x1059A }, + { 0x10574, 0x1059B }, + { 0x10575, 0x1059C }, + { 0x10576, 0x1059D }, + { 0x10577, 0x1059E }, + { 0x10578, 0x1059F }, + { 0x10579, 0x105A0 }, + { 0x1057A, 0x105A1 }, + { 0x1057C, 0x105A3 }, + { 0x1057D, 0x105A4 }, + { 0x1057E, 0x105A5 }, + { 0x1057F, 0x105A6 }, + { 0x10580, 0x105A7 }, + { 0x10581, 0x105A8 }, + { 0x10582, 0x105A9 }, + { 0x10583, 0x105AA }, + { 0x10584, 0x105AB }, + { 0x10585, 0x105AC }, + { 0x10586, 0x105AD }, + { 0x10587, 0x105AE }, + { 0x10588, 0x105AF }, + { 0x10589, 0x105B0 }, + { 0x1058A, 0x105B1 }, + { 0x1058C, 0x105B3 }, + { 0x1058D, 0x105B4 }, + { 0x1058E, 0x105B5 }, + { 0x1058F, 0x105B6 }, + { 0x10590, 0x105B7 }, + { 0x10591, 0x105B8 }, + { 0x10592, 0x105B9 }, + { 0x10594, 0x105BB }, + { 0x10595, 0x105BC }, + { 0x10C80, 0x10CC0 }, + { 0x10C81, 0x10CC1 }, + { 0x10C82, 0x10CC2 }, + { 0x10C83, 0x10CC3 }, + { 0x10C84, 0x10CC4 }, + { 0x10C85, 0x10CC5 }, + { 0x10C86, 0x10CC6 }, + { 0x10C87, 0x10CC7 }, + { 0x10C88, 0x10CC8 }, + { 0x10C89, 0x10CC9 }, + { 0x10C8A, 0x10CCA }, + { 0x10C8B, 0x10CCB }, + { 0x10C8C, 0x10CCC }, + { 0x10C8D, 0x10CCD }, + { 0x10C8E, 0x10CCE }, + { 0x10C8F, 0x10CCF }, + { 0x10C90, 0x10CD0 }, + { 0x10C91, 0x10CD1 }, + { 0x10C92, 0x10CD2 }, + { 0x10C93, 0x10CD3 }, + { 0x10C94, 0x10CD4 }, + { 0x10C95, 0x10CD5 }, + { 0x10C96, 0x10CD6 }, + { 0x10C97, 0x10CD7 }, + { 0x10C98, 0x10CD8 }, + { 0x10C99, 0x10CD9 }, + { 0x10C9A, 0x10CDA }, + { 0x10C9B, 0x10CDB }, + { 0x10C9C, 0x10CDC }, + { 0x10C9D, 0x10CDD }, + { 0x10C9E, 0x10CDE }, + { 0x10C9F, 0x10CDF }, + { 0x10CA0, 0x10CE0 }, + { 0x10CA1, 0x10CE1 }, + { 0x10CA2, 0x10CE2 }, + { 0x10CA3, 0x10CE3 }, + { 0x10CA4, 0x10CE4 }, + { 0x10CA5, 0x10CE5 }, + { 0x10CA6, 0x10CE6 }, + { 0x10CA7, 0x10CE7 }, + { 0x10CA8, 0x10CE8 }, + { 0x10CA9, 0x10CE9 }, + { 0x10CAA, 0x10CEA }, + { 0x10CAB, 0x10CEB }, + { 0x10CAC, 0x10CEC }, + { 0x10CAD, 0x10CED }, + { 0x10CAE, 0x10CEE }, + { 0x10CAF, 0x10CEF }, + { 0x10CB0, 0x10CF0 }, + { 0x10CB1, 0x10CF1 }, + { 0x10CB2, 0x10CF2 }, + { 0x10D50, 0x10D70 }, + { 0x10D51, 0x10D71 }, + { 0x10D52, 0x10D72 }, + { 0x10D53, 0x10D73 }, + { 0x10D54, 0x10D74 }, + { 0x10D55, 0x10D75 }, + { 0x10D56, 0x10D76 }, + { 0x10D57, 0x10D77 }, + { 0x10D58, 0x10D78 }, + { 0x10D59, 0x10D79 }, + { 0x10D5A, 0x10D7A }, + { 0x10D5B, 0x10D7B }, + { 0x10D5C, 0x10D7C }, + { 0x10D5D, 0x10D7D }, + { 0x10D5E, 0x10D7E }, + { 0x10D5F, 0x10D7F }, + { 0x10D60, 0x10D80 }, + { 0x10D61, 0x10D81 }, + { 0x10D62, 0x10D82 }, + { 0x10D63, 0x10D83 }, + { 0x10D64, 0x10D84 }, + { 0x10D65, 0x10D85 }, + { 0x118A0, 0x118C0 }, + { 0x118A1, 0x118C1 }, + { 0x118A2, 0x118C2 }, + { 0x118A3, 0x118C3 }, + { 0x118A4, 0x118C4 }, + { 0x118A5, 0x118C5 }, + { 0x118A6, 0x118C6 }, + { 0x118A7, 0x118C7 }, + { 0x118A8, 0x118C8 }, + { 0x118A9, 0x118C9 }, + { 0x118AA, 0x118CA }, + { 0x118AB, 0x118CB }, + { 0x118AC, 0x118CC }, + { 0x118AD, 0x118CD }, + { 0x118AE, 0x118CE }, + { 0x118AF, 0x118CF }, + { 0x118B0, 0x118D0 }, + { 0x118B1, 0x118D1 }, + { 0x118B2, 0x118D2 }, + { 0x118B3, 0x118D3 }, + { 0x118B4, 0x118D4 }, + { 0x118B5, 0x118D5 }, + { 0x118B6, 0x118D6 }, + { 0x118B7, 0x118D7 }, + { 0x118B8, 0x118D8 }, + { 0x118B9, 0x118D9 }, + { 0x118BA, 0x118DA }, + { 0x118BB, 0x118DB }, + { 0x118BC, 0x118DC }, + { 0x118BD, 0x118DD }, + { 0x118BE, 0x118DE }, + { 0x118BF, 0x118DF }, + { 0x16E40, 0x16E60 }, + { 0x16E41, 0x16E61 }, + { 0x16E42, 0x16E62 }, + { 0x16E43, 0x16E63 }, + { 0x16E44, 0x16E64 }, + { 0x16E45, 0x16E65 }, + { 0x16E46, 0x16E66 }, + { 0x16E47, 0x16E67 }, + { 0x16E48, 0x16E68 }, + { 0x16E49, 0x16E69 }, + { 0x16E4A, 0x16E6A }, + { 0x16E4B, 0x16E6B }, + { 0x16E4C, 0x16E6C }, + { 0x16E4D, 0x16E6D }, + { 0x16E4E, 0x16E6E }, + { 0x16E4F, 0x16E6F }, + { 0x16E50, 0x16E70 }, + { 0x16E51, 0x16E71 }, + { 0x16E52, 0x16E72 }, + { 0x16E53, 0x16E73 }, + { 0x16E54, 0x16E74 }, + { 0x16E55, 0x16E75 }, + { 0x16E56, 0x16E76 }, + { 0x16E57, 0x16E77 }, + { 0x16E58, 0x16E78 }, + { 0x16E59, 0x16E79 }, + { 0x16E5A, 0x16E7A }, + { 0x16E5B, 0x16E7B }, + { 0x16E5C, 0x16E7C }, + { 0x16E5D, 0x16E7D }, + { 0x16E5E, 0x16E7E }, + { 0x16E5F, 0x16E7F }, + { 0x1E900, 0x1E922 }, + { 0x1E901, 0x1E923 }, + { 0x1E902, 0x1E924 }, + { 0x1E903, 0x1E925 }, + { 0x1E904, 0x1E926 }, + { 0x1E905, 0x1E927 }, + { 0x1E906, 0x1E928 }, + { 0x1E907, 0x1E929 }, + { 0x1E908, 0x1E92A }, + { 0x1E909, 0x1E92B }, + { 0x1E90A, 0x1E92C }, + { 0x1E90B, 0x1E92D }, + { 0x1E90C, 0x1E92E }, + { 0x1E90D, 0x1E92F }, + { 0x1E90E, 0x1E930 }, + { 0x1E90F, 0x1E931 }, + { 0x1E910, 0x1E932 }, + { 0x1E911, 0x1E933 }, + { 0x1E912, 0x1E934 }, + { 0x1E913, 0x1E935 }, + { 0x1E914, 0x1E936 }, + { 0x1E915, 0x1E937 }, + { 0x1E916, 0x1E938 }, + { 0x1E917, 0x1E939 }, + { 0x1E918, 0x1E93A }, + { 0x1E919, 0x1E93B }, + { 0x1E91A, 0x1E93C }, + { 0x1E91B, 0x1E93D }, + { 0x1E91C, 0x1E93E }, + { 0x1E91D, 0x1E93F }, + { 0x1E91E, 0x1E940 }, + { 0x1E91F, 0x1E941 }, + { 0x1E920, 0x1E942 }, + { 0x1E921, 0x1E943 }, }; static int _find_upper(int ch) { int low = 0; - int high = CAPS_LEN - 1; + int high = LTU_LEN - 1; int middle; while (low <= high) { middle = (low + high) / 2; if (ch < caps_table[middle][0]) { - high = middle - 1; //search low end of array + high = middle - 1; // Search low end of array. } else if (caps_table[middle][0] < ch) { - low = middle + 1; //search high end of array + low = middle + 1; // Search high end of array. } else { return caps_table[middle][1]; } @@ -1394,16 +3001,16 @@ static int _find_upper(int ch) { static int _find_lower(int ch) { int low = 0; - int high = CAPS_LEN - 2; + int high = UTL_LEN - 1; int middle; while (low <= high) { middle = (low + high) / 2; if (ch < reverse_caps_table[middle][0]) { - high = middle - 1; //search low end of array + high = middle - 1; // Search low end of array. } else if (reverse_caps_table[middle][0] < ch) { - low = middle + 1; //search high end of array + low = middle + 1; // Search high end of array. } else { return reverse_caps_table[middle][1]; } diff --git a/core/templates/a_hash_map.h b/core/templates/a_hash_map.h index de24fe4487ee..03089f093a34 100644 --- a/core/templates/a_hash_map.h +++ b/core/templates/a_hash_map.h @@ -35,12 +35,12 @@ struct HashMapData { union { + uint64_t data; struct { uint32_t hash; uint32_t hash_to_key; }; - uint64_t data; }; }; diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp index ec9492ac5d1c..a53b5660d5a5 100644 --- a/core/variant/variant_utility.cpp +++ b/core/variant/variant_utility.cpp @@ -999,9 +999,7 @@ void VariantUtilityFunctions::print_rich(const Variant **p_args, int p_arg_count r_error.error = Callable::CallError::CALL_OK; } -#undef print_verbose - -void VariantUtilityFunctions::print_verbose(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { +void VariantUtilityFunctions::_print_verbose(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { if (OS::get_singleton()->is_stdout_verbose()) { String s; for (int i = 0; i < p_arg_count; i++) { @@ -1581,16 +1579,16 @@ static _FORCE_INLINE_ Variant::Type get_ret_type_helper(void (*p_func)(P...)) { }; \ register_utility_function(#m_func, m_args) -#define FUNCBINDVARARGV(m_func, m_args, m_category) \ +#define FUNCBINDVARARGV_CNAME(m_func, m_func_cname, m_args, m_category) \ class Func_##m_func { \ public: \ static void call(Variant *r_ret, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { \ r_error.error = Callable::CallError::CALL_OK; \ - VariantUtilityFunctions::m_func(p_args, p_argcount, r_error); \ + VariantUtilityFunctions::m_func_cname(p_args, p_argcount, r_error); \ } \ static void validated_call(Variant *r_ret, const Variant **p_args, int p_argcount) { \ Callable::CallError c; \ - VariantUtilityFunctions::m_func(p_args, p_argcount, c); \ + VariantUtilityFunctions::m_func_cname(p_args, p_argcount, c); \ } \ static void ptrcall(void *ret, const void **p_args, int p_argcount) { \ Vector args; \ @@ -1625,6 +1623,8 @@ static _FORCE_INLINE_ Variant::Type get_ret_type_helper(void (*p_func)(P...)) { }; \ register_utility_function(#m_func, m_args) +#define FUNCBINDVARARGV(m_func, m_args, m_category) FUNCBINDVARARGV_CNAME(m_func, m_func, m_args, m_category) + #define FUNCBIND(m_func, m_args, m_category) \ class Func_##m_func { \ public: \ @@ -1832,7 +1832,7 @@ void Variant::_register_variant_utility_functions() { FUNCBINDVARARGV(printt, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDVARARGV(prints, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDVARARGV(printraw, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); - FUNCBINDVARARGV(print_verbose, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); + FUNCBINDVARARGV_CNAME(print_verbose, _print_verbose, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDVARARGV(push_error, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDVARARGV(push_warning, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); diff --git a/core/variant/variant_utility.h b/core/variant/variant_utility.h index 75cde4942b5f..a653369621a0 100644 --- a/core/variant/variant_utility.h +++ b/core/variant/variant_utility.h @@ -133,8 +133,7 @@ struct VariantUtilityFunctions { static String type_string(Variant::Type p_type); static void print(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); static void print_rich(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); -#undef print_verbose - static void print_verbose(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static void _print_verbose(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); static void printerr(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); static void printt(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); static void prints(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); diff --git a/doc/classes/CPUParticles2D.xml b/doc/classes/CPUParticles2D.xml index e52e7274006a..d573b9fe6e48 100644 --- a/doc/classes/CPUParticles2D.xml +++ b/doc/classes/CPUParticles2D.xml @@ -140,10 +140,10 @@ Each particle's initial color. If [member texture] is defined, it will be multiplied by this color. - Each particle's initial color will vary along this [GradientTexture1D] (multiplied with [member color]). + Each particle's initial color will vary along this [Gradient] (multiplied with [member color]). - Each particle's color will vary along this [Gradient] (multiplied with [member color]). + Each particle's color will vary along this [Gradient] over its lifetime (multiplied with [member color]). Damping will vary along this [Curve]. Should be a unit [Curve]. @@ -241,6 +241,7 @@ Align Y axis of particle with the direction of its velocity. + Particle system starts as if it had already run for this many seconds. diff --git a/doc/classes/CPUParticles3D.xml b/doc/classes/CPUParticles3D.xml index cf80fecca69c..170f684638b8 100644 --- a/doc/classes/CPUParticles3D.xml +++ b/doc/classes/CPUParticles3D.xml @@ -146,11 +146,11 @@ [b]Note:[/b] [member color] multiplies the particle mesh's vertex colors. To have a visible effect on a [BaseMaterial3D], [member BaseMaterial3D.vertex_color_use_as_albedo] [i]must[/i] be [code]true[/code]. For a [ShaderMaterial], [code]ALBEDO *= COLOR.rgb;[/code] must be inserted in the shader's [code]fragment()[/code] function. Otherwise, [member color] will have no visible effect. - Each particle's initial color will vary along this [GradientTexture1D] (multiplied with [member color]). + Each particle's initial color will vary along this [Gradient] (multiplied with [member color]). [b]Note:[/b] [member color_initial_ramp] multiplies the particle mesh's vertex colors. To have a visible effect on a [BaseMaterial3D], [member BaseMaterial3D.vertex_color_use_as_albedo] [i]must[/i] be [code]true[/code]. For a [ShaderMaterial], [code]ALBEDO *= COLOR.rgb;[/code] must be inserted in the shader's [code]fragment()[/code] function. Otherwise, [member color_initial_ramp] will have no visible effect. - Each particle's color will vary along this [GradientTexture1D] over its lifetime (multiplied with [member color]). + Each particle's color will vary along this [Gradient] over its lifetime (multiplied with [member color]). [b]Note:[/b] [member color_ramp] multiplies the particle mesh's vertex colors. To have a visible effect on a [BaseMaterial3D], [member BaseMaterial3D.vertex_color_use_as_albedo] [i]must[/i] be [code]true[/code]. For a [ShaderMaterial], [code]ALBEDO *= COLOR.rgb;[/code] must be inserted in the shader's [code]fragment()[/code] function. Otherwise, [member color_ramp] will have no visible effect. diff --git a/doc/classes/CollisionPolygon3D.xml b/doc/classes/CollisionPolygon3D.xml index 4f5866c34846..5758e1799671 100644 --- a/doc/classes/CollisionPolygon3D.xml +++ b/doc/classes/CollisionPolygon3D.xml @@ -10,6 +10,13 @@ + + The collision shape color that is displayed in the editor, or in the running project if [b]Debug > Visible Collision Shapes[/b] is checked at the top of the editor. + [b]Note:[/b] The default value is [member ProjectSettings.debug/shapes/collision/shape_color]. The [code]Color(0, 0, 0, 0)[/code] value documented here is a placeholder, and not the actual default debug color. + + + If [code]true[/code], when the shape is displayed, it will show a solid fill color in addition to its wireframe. + Length that the resulting collision extends in either direction perpendicular to its 2D polygon. diff --git a/doc/classes/Cubemap.xml b/doc/classes/Cubemap.xml index 8a850920f74b..c55adcffe33f 100644 --- a/doc/classes/Cubemap.xml +++ b/doc/classes/Cubemap.xml @@ -7,10 +7,10 @@ A cubemap is made of 6 textures organized in layers. They are typically used for faking reflections in 3D rendering (see [ReflectionProbe]). It can be used to make an object look as if it's reflecting its surroundings. This usually delivers much better performance than other reflection methods. This resource is typically used as a uniform in custom shaders. Few core Godot methods make use of [Cubemap] resources. To create such a texture file yourself, reimport your image files using the Godot Editor import presets. The expected image order is X+, X-, Y+, Y-, Z+, Z- (in Godot's coordinate system, so Y+ is "up" and Z- is "forward"). You can use one of the following templates as a base: - - [url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/cubemap_template_2x3.webp]2×3 cubemap template (default layout option)[/url] - - [url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/cubemap_template_3x2.webp]3×2 cubemap template[/url] - - [url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/cubemap_template_1x6.webp]1×6 cubemap template[/url] - - [url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/cubemap_template_6x1.webp]6×1 cubemap template[/url] + - [url=https://raw.githubusercontent.com/godotengine/godot-docs/master/tutorials/assets_pipeline/img/cubemap_template_2x3.webp]2×3 cubemap template (default layout option)[/url] + - [url=https://raw.githubusercontent.com/godotengine/godot-docs/master/tutorials/assets_pipeline/img/cubemap_template_3x2.webp]3×2 cubemap template[/url] + - [url=https://raw.githubusercontent.com/godotengine/godot-docs/master/tutorials/assets_pipeline/img/cubemap_template_1x6.webp]1×6 cubemap template[/url] + - [url=https://raw.githubusercontent.com/godotengine/godot-docs/master/tutorials/assets_pipeline/img/cubemap_template_6x1.webp]6×1 cubemap template[/url] [b]Note:[/b] Godot doesn't support using cubemaps in a [PanoramaSkyMaterial]. To use a cubemap as a skybox, convert the default [PanoramaSkyMaterial] to a [ShaderMaterial] using the [b]Convert to ShaderMaterial[/b] resource dropdown option, then replace its code with the following: [codeblock lang=text] shader_type sky; diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index a22d05f5f019..6089f85bc774 100644 --- a/doc/classes/DisplayServer.xml +++ b/doc/classes/DisplayServer.xml @@ -214,6 +214,7 @@ Returns the unobscured area of the display where interactive controls should be rendered. See also [method get_display_cutouts]. + [b]Note:[/b] Currently only implemented on Android and iOS. On other platforms, [code]screen_get_usable_rect(SCREEN_OF_MAIN_WINDOW)[/code] will be returned as a fallback. See also [method screen_get_usable_rect]. @@ -1967,6 +1968,9 @@ Confines the mouse cursor to the game window, and make it hidden. + + Max value of the [enum MouseMode]. + Represents the screen containing the mouse pointer. [b]Note:[/b] On Linux (Wayland), this constant always represents the screen at index [code]0[/code]. diff --git a/doc/classes/EditorInspector.xml b/doc/classes/EditorInspector.xml index 0bbd55f00757..644cbe5b5817 100644 --- a/doc/classes/EditorInspector.xml +++ b/doc/classes/EditorInspector.xml @@ -51,7 +51,6 @@ - diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index fcabcf14600f..3900a81502c1 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -1136,6 +1136,9 @@ - [b]Launch in PiP mode[/b] will launch the Play window directly in picture-in-picture (PiP) mode if PiP mode is supported and enabled. When maximized, the Play window will occupy the same window as the Editor. [b]Note:[/b] Only available in the Android editor. + + Overrides game embedding setting for all newly opened projects. If enabled, game embedding settings are not saved. + Specifies the picture-in-picture (PiP) mode for the Play window. - [b]Disabled:[/b] PiP is disabled for the Play window. diff --git a/doc/classes/FileDialog.xml b/doc/classes/FileDialog.xml index 921fb0b71464..aced51b906c7 100644 --- a/doc/classes/FileDialog.xml +++ b/doc/classes/FileDialog.xml @@ -151,6 +151,7 @@ If [code]true[/code], changing the [member file_mode] property will set the window title accordingly (e.g. setting [member file_mode] to [constant FILE_MODE_OPEN_FILE] will change the window title to "Open a File"). + The number of additional [OptionButton]s and [CheckBox]es in the dialog. diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index 3a8bbb246a49..e0d8be915778 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -311,7 +311,8 @@ - Removes all mappings from the internal database that match the given GUID. + Removes all mappings from the internal database that match the given GUID. All currently connected joypads that use this GUID will become unmapped. + On Android, Godot will map to an internal fallback mapping. @@ -463,6 +464,9 @@ Confines the mouse cursor to the game window, and make it hidden. + + Max value of the [enum MouseMode]. + Arrow cursor. Standard, default pointing cursor. diff --git a/doc/classes/InputEventWithModifiers.xml b/doc/classes/InputEventWithModifiers.xml index 5c018b0c56d1..1434f3411573 100644 --- a/doc/classes/InputEventWithModifiers.xml +++ b/doc/classes/InputEventWithModifiers.xml @@ -5,6 +5,7 @@ Stores information about mouse, keyboard, and touch gesture input events. This includes information about which modifier keys are pressed, such as [kbd]Shift[/kbd] or [kbd]Alt[/kbd]. See [method Node._input]. + [b]Note:[/b] Modifier keys are considered modifiers only when used in combination with another key. As a result, their corresponding member variables, such as [member ctrl_pressed], will return [code]false[/code] if the key is pressed on its own. $DOCS_URL/tutorials/inputs/inputevent.html diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml index dd0d6ed4117e..c6cdacb6ec25 100644 --- a/doc/classes/LineEdit.xml +++ b/doc/classes/LineEdit.xml @@ -277,7 +277,7 @@ If [code]false[/code], existing text cannot be modified and new text cannot be added. - If [code]false[/code], "Emoji and Symbols" menu is enabled. + If [code]true[/code], "Emoji and Symbols" menu is enabled. If [code]true[/code], the [LineEdit] width will increase to stay longer than the [member text]. It will [b]not[/b] compress if the [member text] is shortened. diff --git a/doc/classes/ParticleProcessMaterial.xml b/doc/classes/ParticleProcessMaterial.xml index 8336899e8698..7118df8ed85b 100644 --- a/doc/classes/ParticleProcessMaterial.xml +++ b/doc/classes/ParticleProcessMaterial.xml @@ -418,6 +418,7 @@ Emitted when this material's emission shape is changed in any way. This includes changes to [member emission_shape], [member emission_shape_scale], or [member emission_sphere_radius], and any other property that affects the emission shape's offset, size, scale, or orientation. + [b]Note:[/b] This signal is only emitted inside the editor for performance reasons. diff --git a/doc/classes/PhysicsDirectSpaceState2D.xml b/doc/classes/PhysicsDirectSpaceState2D.xml index 94e9096334dd..059073a936f7 100644 --- a/doc/classes/PhysicsDirectSpaceState2D.xml +++ b/doc/classes/PhysicsDirectSpaceState2D.xml @@ -37,7 +37,7 @@ [b]Note:[/b] This method does not take into account the [code]motion[/code] property of the object. The returned object is a dictionary containing the following fields: [code]collider_id[/code]: The colliding object's ID. [code]linear_velocity[/code]: The colliding object's velocity [Vector2]. If the object is an [Area2D], the result is [code](0, 0)[/code]. - [code]normal[/code]: The object's surface normal at the intersection point. + [code]normal[/code]: The collision normal of the query shape at the intersection point, pointing away from the intersecting object. [code]point[/code]: The intersection point. [code]rid[/code]: The intersecting object's [RID]. [code]shape[/code]: The shape index of the colliding shape. diff --git a/doc/classes/PhysicsDirectSpaceState3D.xml b/doc/classes/PhysicsDirectSpaceState3D.xml index b00dc4236d42..2f09b4f92810 100644 --- a/doc/classes/PhysicsDirectSpaceState3D.xml +++ b/doc/classes/PhysicsDirectSpaceState3D.xml @@ -37,7 +37,7 @@ Checks the intersections of a shape, given through a [PhysicsShapeQueryParameters3D] object, against the space. If it collides with more than one shape, the nearest one is selected. The returned object is a dictionary containing the following fields: [code]collider_id[/code]: The colliding object's ID. [code]linear_velocity[/code]: The colliding object's velocity [Vector3]. If the object is an [Area3D], the result is [code](0, 0, 0)[/code]. - [code]normal[/code]: The object's surface normal at the intersection point. + [code]normal[/code]: The collision normal of the query shape at the intersection point, pointing away from the intersecting object. [code]point[/code]: The intersection point. [code]rid[/code]: The intersecting object's [RID]. [code]shape[/code]: The shape index of the colliding shape. diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 742dcdabc1c6..131ac6ea9308 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -151,6 +151,7 @@ Loads the contents of the .pck or .zip file specified by [param pack] into the resource filesystem ([code]res://[/code]). Returns [code]true[/code] on success. [b]Note:[/b] If a file from [param pack] shares the same path as a file already in the resource filesystem, any attempts to load that file will use the file from [param pack] unless [param replace_files] is set to [code]false[/code]. [b]Note:[/b] The optional [param offset] parameter can be used to specify the offset in bytes to the start of the resource pack. This is only supported for .pck files. + [b]Note:[/b] [DirAccess] will not show changes made to the contents of [code]res://[/code] after calling this function. @@ -2237,8 +2238,10 @@ Sets which physics engine to use for 2D physics. - "DEFAULT" and "GodotPhysics2D" are the same, as there is currently no alternative 2D physics server implemented. - "Dummy" is a 2D physics server that does nothing and returns only dummy values, effectively disabling all 2D physics functionality. + [b]DEFAULT[/b] is currently equivalent to [b]GodotPhysics2D[/b], but may change in future releases. Select an explicit implementation if you want to ensure that your project stays on the same engine. + [b]GodotPhysics2D[/b] is Godot's internal 2D physics engine. + [b]Dummy[/b] is a 2D physics server that does nothing and returns only dummy values, effectively disabling all 2D physics functionality. + Third-party extensions and modules can add other physics engines to select with this setting. If [code]true[/code], the 2D physics server runs on a separate thread, making better use of multi-core CPUs. If [code]false[/code], the 2D physics server runs on the main thread. Running the physics server on a separate thread can increase performance, but restricts API access to only physics process. @@ -2316,8 +2319,11 @@ Sets which physics engine to use for 3D physics. - "DEFAULT" and "GodotPhysics3D" are the same, as there is currently no alternative 3D physics server implemented. - "Dummy" is a 3D physics server that does nothing and returns only dummy values, effectively disabling all 3D physics functionality. + [b]DEFAULT[/b] is currently equivalent to [b]GodotPhysics3D[/b], but may change in future releases. Select an explicit implementation if you want to ensure that your project stays on the same engine. + [b]GodotPhysics3D[/b] is Godot's internal 3D physics engine. + [b]Jolt Physics[/b] is an alternative physics engine that is generally faster and more reliable than [b]GodotPhysics3D[/b]. As it was recently implemented, it is currently considered experimental and its behavior may change in future releases. + [b]Dummy[/b] is a 3D physics server that does nothing and returns only dummy values, effectively disabling all 3D physics functionality. + Third-party extensions and modules can add other physics engines to select with this setting. If [code]true[/code], the 3D physics server runs on a separate thread, making better use of multi-core CPUs. If [code]false[/code], the 3D physics server runs on the main thread. Running the physics server on a separate thread can increase performance, but restricts API access to only physics process. diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index a3df48d099d8..fd16a665096c 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -4269,7 +4269,7 @@ Creates a new 3D visibility notifier object and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]visibility_notifier_*[/code] RenderingServer functions. Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] method. - To place in a scene, attach this mesh to an instance using [method instance_set_base] using the returned RID. + To place in a scene, attach this notifier to an instance using [method instance_set_base] using the returned RID. [b]Note:[/b] The equivalent node is [VisibleOnScreenNotifier3D]. diff --git a/doc/classes/ResourceImporterLayeredTexture.xml b/doc/classes/ResourceImporterLayeredTexture.xml index e1e8ee710d0e..f69c44e29bd5 100644 --- a/doc/classes/ResourceImporterLayeredTexture.xml +++ b/doc/classes/ResourceImporterLayeredTexture.xml @@ -14,7 +14,7 @@ Controls how color channels should be used in the imported texture. [b]sRGB Friendly:[/b], prevents the RG color format from being used, as it does not support sRGB color. [b]Optimized:[/b], allows the RG color format to be used if the texture does not use the blue channel. This reduces memory usage if the texture's blue channel can be discarded (all pixels must have a blue value of [code]0[/code]). - [b]Normal Map (RG Channels):[/b] This forces all layers from the texture to be imported with the RG color format to reduce memory usage, with only the red and green channels preserved. This only has an effect on textures with the VRAM Compressed or Basis Universal compression modes. This mode is only available in layered textures ([Cubemap], [CubemapArray], [Texture2DArray] and [Texture3D]). + [b]Normal Map (RG Channels):[/b] This forces all layers from the texture to be imported with the RG color format, with only the red and green channels preserved. RGTC (Red-Green Texture Compression) compression is able to preserve its detail much better, while using the same amount of memory as a standard RGBA VRAM-compressed texture. This only has an effect on textures with the VRAM Compressed or Basis Universal compression modes. This mode is only available in layered textures ([Cubemap], [CubemapArray], [Texture2DArray] and [Texture3D]). Controls how VRAM compression should be performed for HDR images. diff --git a/doc/classes/SpringBoneCollision3D.xml b/doc/classes/SpringBoneCollision3D.xml index ea4556674cf7..85738b669715 100644 --- a/doc/classes/SpringBoneCollision3D.xml +++ b/doc/classes/SpringBoneCollision3D.xml @@ -7,6 +7,7 @@ A collision can be a child of [SpringBoneSimulator3D]. If it is not a child of [SpringBoneSimulator3D], it has no effect. The colliding and sliding are done in the [SpringBoneSimulator3D]'s modification process in order of its collision list which is set by [method SpringBoneSimulator3D.set_collision_path]. If [method SpringBoneSimulator3D.are_all_child_collisions_enabled] is [code]true[/code], the order matches [SceneTree]. If [member bone] is set, it synchronizes with the bone pose of the ancestor [Skeleton3D], which is done in before the [SpringBoneSimulator3D]'s modification process as the pre-process. + [b]Warning:[/b] A scaled [SpringBoneCollision3D] will likely not behave as expected. Make sure that the parent [Skeleton3D] and its bones are not scaled. diff --git a/doc/classes/SpringBoneSimulator3D.xml b/doc/classes/SpringBoneSimulator3D.xml index 95c357f60bbf..6012121685d4 100644 --- a/doc/classes/SpringBoneSimulator3D.xml +++ b/doc/classes/SpringBoneSimulator3D.xml @@ -10,6 +10,7 @@ Several properties can be applied to each joint, such as [method set_joint_stiffness], [method set_joint_drag], and [method set_joint_gravity]. For simplicity, you can set values to all joints at the same time by using a [Curve]. If you want to specify detailed values individually, set [method set_individual_config] to [code]true[/code]. For physical simulation, [SpringBoneSimulator3D] can have children as self-standing collisions that are not related to [PhysicsServer3D], see also [SpringBoneCollision3D]. + [b]Warning:[/b] A scaled [SpringBoneSimulator3D] will likely not behave as expected. Make sure that the parent [Skeleton3D] and its bones are not scaled. @@ -446,7 +447,7 @@ - Sets the gravity amount of the bone chain. + Sets the gravity amount of the bone chain. This value is not an acceleration, but a constant velocity of movement in [method set_gravity_direction]. If [param gravity] is not [code]0[/code], the modified pose will not return to the original pose since it is always affected by gravity. The value is scaled by [method set_gravity_damping_curve] and cached in each joint setting in the joint list. @@ -464,7 +465,7 @@ - Sets the gravity direction of the bone chain. + Sets the gravity direction of the bone chain. This value is internally normalized and then multiplied by [method set_gravity]. The value is cached in each joint setting in the joint list. @@ -570,6 +571,7 @@ Sets the rotation axis of the bone chain. If sets a specific axis, it acts like a hinge joint. The value is cached in each joint setting in the joint list. + [b]Note:[/b] The rotation axis and the forward vector shouldn't be colinear to avoid unintended rotation since [SpringBoneSimulator3D] does not factor in twisting forces. diff --git a/doc/classes/SubViewport.xml b/doc/classes/SubViewport.xml index 605cf949c10a..9d41a5e36d63 100644 --- a/doc/classes/SubViewport.xml +++ b/doc/classes/SubViewport.xml @@ -6,6 +6,7 @@ [SubViewport] Isolates a rectangular region of a scene to be displayed independently. This can be used, for example, to display UI in 3D space. [b]Note:[/b] [SubViewport] is a [Viewport] that isn't a [Window], i.e. it doesn't draw anything by itself. To display anything, [SubViewport] must have a non-zero size and be either put inside a [SubViewportContainer] or assigned to a [ViewportTexture]. + [b]Note:[/b] [InputEvent]s are not passed to a standalone [SubViewport] by default. To ensure [InputEvent] propagation, a [SubViewport] can be placed inside of a [SubViewportContainer]. $DOCS_URL/tutorials/rendering/viewports.html diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml index 89e2a2017af1..3b6f81aebc62 100644 --- a/doc/classes/TextEdit.xml +++ b/doc/classes/TextEdit.xml @@ -1300,7 +1300,7 @@ If [code]false[/code], existing text cannot be modified and new text cannot be added. - If [code]false[/code], "Emoji and Symbols" menu is enabled. + If [code]true[/code], "Emoji and Symbols" menu is enabled. If [code]true[/code], copying or cutting without a selection is performed on all lines with a caret. Otherwise, copy and cut require a selection. diff --git a/doc/classes/WorldBoundaryShape3D.xml b/doc/classes/WorldBoundaryShape3D.xml index 33609916a364..0f6224c40c85 100644 --- a/doc/classes/WorldBoundaryShape3D.xml +++ b/doc/classes/WorldBoundaryShape3D.xml @@ -5,6 +5,7 @@ A 3D world boundary shape, intended for use in physics. [WorldBoundaryShape3D] works like an infinite plane that forces all physics bodies to stay above it. The [member plane]'s normal determines which direction is considered as "above" and in the editor, the line over the plane represents this direction. It can for example be used for endless flat floors. + [b]Note:[/b] When the physics engine is set to [b]Jolt Physics[/b] in the project settings ([member ProjectSettings.physics/3d/physics_engine]), [WorldBoundaryShape3D] has a finite size (centered at the world origin). It can be adjusted by changing [member ProjectSettings.physics/jolt_physics_3d/limits/world_boundary_shape_size]. diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 38ced316a136..0ca013abd1a0 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -662,8 +662,6 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou if (index == 0) { // Nothing to render, just return. - state.current_batch_index = 0; - state.canvas_instance_batches.clear(); return; } @@ -915,8 +913,6 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend state.instance_data_array[r_index].flags = base_flags; state.instance_data_array[r_index].instance_uniforms_ofs = p_item->instance_allocated_shader_uniforms_offset; - state.instance_data_array[r_index].flags = base_flags | (state.instance_data_array[r_index == 0 ? 0 : r_index - 1].flags & (BATCH_FLAGS_DEFAULT_NORMAL_MAP_USED | BATCH_FLAGS_DEFAULT_SPECULAR_MAP_USED)); // Reset on each command for safety, keep canvastexture binding config. - Color blend_color = base_color; GLES3::CanvasShaderData::BlendMode blend_mode = p_blend_mode; if (c->type == Item::Command::TYPE_RECT) { @@ -1571,7 +1567,9 @@ void RasterizerCanvasGLES3::_add_to_batch(uint32_t &r_index, bool &r_batch_broke void RasterizerCanvasGLES3::_new_batch(bool &r_batch_broken) { if (state.canvas_instance_batches.size() == 0) { - state.canvas_instance_batches.push_back(Batch()); + Batch new_batch; + new_batch.instance_buffer_index = state.current_instance_buffer_index; + state.canvas_instance_batches.push_back(new_batch); return; } diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 8e3a3d4b3edf..41fa943c896c 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -3991,7 +3991,7 @@ TypedArray RasterizerSceneGLES3::bake_render_uv2(RID p_base, const TypedA // Consider rendering to RGBA8 encoded as RGBE, then manually convert to RGBAH on CPU. glBindTexture(GL_TEXTURE_2D, emission_tex); if (config->float_texture_supported) { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, p_image_size.width, p_image_size.height, 0, GL_RGBA, GL_FLOAT, nullptr); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, p_image_size.width, p_image_size.height, 0, GL_RGBA, GL_FLOAT, nullptr); GLES3::Utilities::get_singleton()->texture_allocated_data(emission_tex, p_image_size.width * p_image_size.height * 16, "Lightmap emission texture"); } else { // Fallback to RGBA8 on devices that don't support rendering to floating point textures. This will look bad, but we have no choice. @@ -4094,9 +4094,9 @@ TypedArray RasterizerSceneGLES3::bake_render_uv2(RID p_base, const TypedA { tex->tex_id = emission_tex; if (config->float_texture_supported) { - tex->format = Image::FORMAT_RGBAF; + tex->format = Image::FORMAT_RGBAH; tex->real_format = Image::FORMAT_RGBAH; - tex->gl_type_cache = GL_FLOAT; + tex->gl_type_cache = GL_HALF_FLOAT; } Ref img = GLES3::TextureStorage::get_singleton()->texture_2d_get(tex_rid); GLES3::Utilities::get_singleton()->texture_free_data(emission_tex); diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index b9ac92b70e4d..4d7a319fe0f3 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -250,6 +250,10 @@ class RasterizerSceneGLES3 : public RendererSceneRender { }; union { + struct { + uint64_t sort_key1; + uint64_t sort_key2; + }; struct { uint64_t lod_index : 8; uint64_t surface_index : 8; @@ -265,10 +269,6 @@ class RasterizerSceneGLES3 : public RendererSceneRender { uint64_t depth_layer : 4; uint64_t priority : 8; }; - struct { - uint64_t sort_key1; - uint64_t sort_key2; - }; } sort; RS::PrimitiveType primitive = RS::PRIMITIVE_MAX; diff --git a/drivers/gles3/shaders/particles.glsl b/drivers/gles3/shaders/particles.glsl index 2591937a1da8..03c94e566390 100644 --- a/drivers/gles3/shaders/particles.glsl +++ b/drivers/gles3/shaders/particles.glsl @@ -342,7 +342,7 @@ void main() { mediump float attractor_attenuation = attractors[i].attenuation; amount = pow(amount, attractor_attenuation); dir = safe_normalize(mix(dir, attractors[i].transform[2].xyz, attractors[i].directionality)); - attractor_force -= amount * dir * attractors[i].strength; + attractor_force -= mass * amount * dir * attractors[i].strength; } float particle_size = particle_size; @@ -453,14 +453,14 @@ void main() { vec3 uvw_pos = vec3(local_pos_bottom / colliders[i].extents.xyz) * 0.5 + 0.5; - float y = 1.0 - texture(height_field_texture, uvw_pos.xz).r; + float y = texture(height_field_texture, uvw_pos.xz).r; if (y + EPSILON > uvw_pos.y) { //inside heightfield vec3 pos1 = (vec3(uvw_pos.x, y, uvw_pos.z) * 2.0 - 1.0) * colliders[i].extents.xyz; - vec3 pos2 = (vec3(uvw_pos.x + DELTA, 1.0 - texture(height_field_texture, uvw_pos.xz + vec2(DELTA, 0)).r, uvw_pos.z) * 2.0 - 1.0) * colliders[i].extents.xyz; - vec3 pos3 = (vec3(uvw_pos.x, 1.0 - texture(height_field_texture, uvw_pos.xz + vec2(0, DELTA)).r, uvw_pos.z + DELTA) * 2.0 - 1.0) * colliders[i].extents.xyz; + vec3 pos2 = (vec3(uvw_pos.x + DELTA, texture(height_field_texture, uvw_pos.xz + vec2(DELTA, 0)).r, uvw_pos.z) * 2.0 - 1.0) * colliders[i].extents.xyz; + vec3 pos3 = (vec3(uvw_pos.x, texture(height_field_texture, uvw_pos.xz + vec2(0, DELTA)).r, uvw_pos.z + DELTA) * 2.0 - 1.0) * colliders[i].extents.xyz; normal = normalize(cross(pos1 - pos2, pos1 - pos3)); float local_y = (vec3(local_pos / colliders[i].extents.xyz) * 0.5 + 0.5).y; diff --git a/drivers/gles3/shaders/tonemap_inc.glsl b/drivers/gles3/shaders/tonemap_inc.glsl index ce98a62c23a4..dd7df09c38a3 100644 --- a/drivers/gles3/shaders/tonemap_inc.glsl +++ b/drivers/gles3/shaders/tonemap_inc.glsl @@ -86,7 +86,7 @@ vec3 tonemap_aces(vec3 color, float p_white) { // Polynomial approximation of EaryChow's AgX sigmoid curve. // x must be within the range [0.0, 1.0] -vec3 agx_default_contrast_approx(vec3 x) { +vec3 agx_contrast_approx(vec3 x) { // Generated with Excel trendline // Input data: Generated using python sigmoid with EaryChow's configuration and 57 steps // Additional padding values were added to give correct intersections at 0.0 and 1.0 @@ -96,25 +96,21 @@ vec3 agx_default_contrast_approx(vec3 x) { return 0.021 * x + 4.0111 * x2 - 25.682 * x2 * x + 70.359 * x4 - 74.778 * x4 * x + 27.069 * x4 * x2; } -const mat3 LINEAR_SRGB_TO_LINEAR_REC2020 = mat3( - vec3(0.6274, 0.0691, 0.0164), - vec3(0.3293, 0.9195, 0.0880), - vec3(0.0433, 0.0113, 0.8956)); - // This is an approximation and simplification of EaryChow's AgX implementation that is used by Blender. // This code is based off of the script that generates the AgX_Base_sRGB.cube LUT that Blender uses. // Source: https://github.com/EaryChow/AgX_LUT_Gen/blob/main/AgXBasesRGB.py vec3 tonemap_agx(vec3 color) { - const mat3 agx_inset_matrix = mat3( - 0.856627153315983, 0.137318972929847, 0.11189821299995, - 0.0951212405381588, 0.761241990602591, 0.0767994186031903, - 0.0482516061458583, 0.101439036467562, 0.811302368396859); + // Combined linear sRGB to linear Rec 2020 and Blender AgX inset matrices: + const mat3 srgb_to_rec2020_agx_inset_matrix = mat3( + 0.54490813676363087053, 0.14044005884001287035, 0.088827411851915368603, + 0.37377945959812267119, 0.75410959864013760045, 0.17887712465043811023, + 0.081384976686407536266, 0.10543358536857773485, 0.73224999956948382528); // Combined inverse AgX outset matrix and linear Rec 2020 to linear sRGB matrices. const mat3 agx_outset_rec2020_to_srgb_matrix = mat3( - 1.9648846919172409596, -0.29937618452442253746, -0.16440106280678278299, - -0.85594737466675834968, 1.3263980951083531115, -0.23819967517076844919, - -0.10883731725048386702, -0.02702191058393112346, 1.4025007379775505276); + 1.9645509602733325934, -0.29932243390911083839, -0.16436833806080403409, + -0.85585845117807513559, 1.3264510741502356555, -0.23822464068860595117, + -0.10886710826831608324, -0.027084020983874825605, 1.402665347143271889); // LOG2_MIN = -10.0 // LOG2_MAX = +6.5 @@ -122,26 +118,32 @@ vec3 tonemap_agx(vec3 color) { const float min_ev = -12.4739311883324; // log2(pow(2, LOG2_MIN) * MIDDLE_GRAY) const float max_ev = 4.02606881166759; // log2(pow(2, LOG2_MAX) * MIDDLE_GRAY) - // Do AGX in rec2020 to match Blender. - color = LINEAR_SRGB_TO_LINEAR_REC2020 * color; - - // Preventing negative values is required for the AgX inset matrix to behave correctly. - // This could also be done before the Rec. 2020 transform, allowing the transform to - // be combined with the AgX inset matrix, but doing this causes a loss of color information - // that could be correctly interpreted within the Rec. 2020 color space. - color = max(color, vec3(0.0)); - - color = agx_inset_matrix * color; + // Large negative values in one channel and large positive values in other + // channels can result in a colour that appears darker and more saturated than + // desired after passing it through the inset matrix. For this reason, it is + // best to prevent negative input values. + // This is done before the Rec. 2020 transform to allow the Rec. 2020 + // transform to be combined with the AgX inset matrix. This results in a loss + // of color information that could be correctly interpreted within the + // Rec. 2020 color space as positive RGB values, but it is less common for Godot + // to provide this function with negative sRGB values and therefore not worth + // the performance cost of an additional matrix multiplication. + // A value of 2e-10 intentionally introduces insignificant error to prevent + // log2(0.0) after the inset matrix is applied; color will be >= 1e-10 after + // the matrix transform. + color = max(color, 2e-10); + + // Do AGX in rec2020 to match Blender and then apply inset matrix. + color = srgb_to_rec2020_agx_inset_matrix * color; // Log2 space encoding. - color = max(color, 1e-10); // Prevent log2(0.0). Possibly unnecessary. - // Must be clamped because agx_blender_default_contrast_approx may not work + // Must be clamped because agx_contrast_approx may not work // well with values outside of the range [0.0, 1.0] color = clamp(log2(color), min_ev, max_ev); color = (color - min_ev) / (max_ev - min_ev); // Apply sigmoid function approximation. - color = agx_default_contrast_approx(color); + color = agx_contrast_approx(color); // Convert back to linear before applying outset matrix. color = pow(color, vec3(2.4)); @@ -149,9 +151,9 @@ vec3 tonemap_agx(vec3 color) { // Apply outset to make the result more chroma-laden and then go back to linear sRGB. color = agx_outset_rec2020_to_srgb_matrix * color; - // Simply hard clip instead of Blender's complex lusRGB.compensate_low_side. - color = max(color, vec3(0.0)); - + // Blender's lusRGB.compensate_low_side is too complex for this shader, so + // simply return the color, even if it has negative components. These negative + // components may be useful for subsequent color adjustments. return color; } diff --git a/drivers/gles3/storage/config.cpp b/drivers/gles3/storage/config.cpp index fb45fdeaca7b..8e1d08274f4f 100644 --- a/drivers/gles3/storage/config.cpp +++ b/drivers/gles3/storage/config.cpp @@ -84,12 +84,14 @@ Config::Config() { if (RasterizerGLES3::is_gles_over_gl()) { float_texture_supported = true; + float_texture_linear_supported = true; etc2_supported = false; s3tc_supported = true; rgtc_supported = true; //RGTC - core since OpenGL version 3.0 srgb_framebuffer_supported = true; } else { float_texture_supported = extensions.has("GL_EXT_color_buffer_float"); + float_texture_linear_supported = extensions.has("GL_OES_texture_float_linear"); etc2_supported = true; #if defined(ANDROID_ENABLED) || defined(IOS_ENABLED) // Some Android devices report support for S3TC but we don't expect that and don't export the textures. diff --git a/drivers/gles3/storage/config.h b/drivers/gles3/storage/config.h index e4dfe6a35ee9..36fae084b0e6 100644 --- a/drivers/gles3/storage/config.h +++ b/drivers/gles3/storage/config.h @@ -72,6 +72,7 @@ class Config { HashSet extensions; bool float_texture_supported = false; + bool float_texture_linear_supported = false; bool s3tc_supported = false; bool rgtc_supported = false; bool bptc_supported = false; diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index abaa507453e7..67dd9bcb4ee4 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -1392,7 +1392,7 @@ MaterialStorage::MaterialStorage() { actions.renames["COLOR"] = "out_color"; actions.renames["VELOCITY"] = "out_velocity_flags.xyz"; - //actions.renames["MASS"] = "mass"; ? + actions.renames["MASS"] = "mass"; actions.renames["ACTIVE"] = "particle_active"; actions.renames["RESTART"] = "restart"; actions.renames["CUSTOM"] = "out_custom"; diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index e74747954f51..de5938176857 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -334,7 +334,9 @@ void TextureStorage::canvas_texture_set_texture_repeat(RID p_canvas_texture, RS: /* Texture API */ -static inline Error _get_gl_uncompressed_format(Image::Format p_format, GLenum &r_gl_format, GLenum &r_gl_internal_format, GLenum &r_gl_type) { +static inline Error _get_gl_uncompressed_format(const Ref &p_image, Image::Format p_format, Image::Format &r_real_format, GLenum &r_gl_format, GLenum &r_gl_internal_format, GLenum &r_gl_type) { + Config *config = Config::get_singleton(); + switch (p_format) { case Image::FORMAT_L8: { if (RasterizerGLES3::is_gles_over_gl()) { @@ -389,24 +391,68 @@ static inline Error _get_gl_uncompressed_format(Image::Format p_format, GLenum & r_gl_type = GL_UNSIGNED_SHORT_5_6_5; } break; case Image::FORMAT_RF: { - r_gl_internal_format = GL_R32F; - r_gl_format = GL_RED; - r_gl_type = GL_FLOAT; + if (config->float_texture_linear_supported) { + r_gl_internal_format = GL_R32F; + r_gl_format = GL_RED; + r_gl_type = GL_FLOAT; + } else { + ERR_PRINT("R32 float texture not supported, converting to R16."); + if (p_image.is_valid()) { + p_image->convert(Image::FORMAT_RH); + } + r_real_format = Image::FORMAT_RH; + r_gl_internal_format = GL_R16F; + r_gl_format = GL_RED; + r_gl_type = GL_HALF_FLOAT; + } } break; case Image::FORMAT_RGF: { - r_gl_internal_format = GL_RG32F; - r_gl_format = GL_RG; - r_gl_type = GL_FLOAT; + if (config->float_texture_linear_supported) { + r_gl_internal_format = GL_RG32F; + r_gl_format = GL_RG; + r_gl_type = GL_FLOAT; + } else { + ERR_PRINT("RG32 float texture not supported, converting to RG16."); + if (p_image.is_valid()) { + p_image->convert(Image::FORMAT_RGH); + } + r_real_format = Image::FORMAT_RGH; + r_gl_internal_format = GL_RG16F; + r_gl_format = GL_RG; + r_gl_type = GL_HALF_FLOAT; + } } break; case Image::FORMAT_RGBF: { - r_gl_internal_format = GL_RGB32F; - r_gl_format = GL_RGB; - r_gl_type = GL_FLOAT; + if (config->float_texture_linear_supported) { + r_gl_internal_format = GL_RGB32F; + r_gl_format = GL_RGB; + r_gl_type = GL_FLOAT; + } else { + ERR_PRINT("RGB32 float texture not supported, converting to RGB16."); + if (p_image.is_valid()) { + p_image->convert(Image::FORMAT_RGBH); + } + r_real_format = Image::FORMAT_RGBH; + r_gl_internal_format = GL_RGB16F; + r_gl_format = GL_RGB; + r_gl_type = GL_HALF_FLOAT; + } } break; case Image::FORMAT_RGBAF: { - r_gl_internal_format = GL_RGBA32F; - r_gl_format = GL_RGBA; - r_gl_type = GL_FLOAT; + if (config->float_texture_linear_supported) { + r_gl_internal_format = GL_RGBA32F; + r_gl_format = GL_RGBA; + r_gl_type = GL_FLOAT; + } else { + ERR_PRINT("RGBA32 float texture not supported, converting to RGBA16."); + if (p_image.is_valid()) { + p_image->convert(Image::FORMAT_RGBAH); + } + r_real_format = Image::FORMAT_RGBAH; + r_gl_internal_format = GL_RGBA16F; + r_gl_format = GL_RGBA; + r_gl_type = GL_HALF_FLOAT; + } } break; case Image::FORMAT_RH: { r_gl_internal_format = GL_R16F; @@ -449,7 +495,7 @@ Ref TextureStorage::_get_gl_image_and_format(const Ref &p_image, I r_real_format = p_format; if (!Image::is_format_compressed(p_format)) { - Error err = _get_gl_uncompressed_format(p_format, r_gl_format, r_gl_internal_format, r_gl_type); + Error err = _get_gl_uncompressed_format(p_image, p_format, r_real_format, r_gl_format, r_gl_internal_format, r_gl_type); ERR_FAIL_COND_V_MSG(err != OK, Ref(), vformat("The image format %d is not supported by the Compatibility renderer.", p_format)); return p_image; } @@ -694,7 +740,7 @@ Ref TextureStorage::_get_gl_image_and_format(const Ref &p_image, I image->convert(Image::FORMAT_RG8); } - Error err = _get_gl_uncompressed_format(image->get_format(), r_gl_format, r_gl_internal_format, r_gl_type); + Error err = _get_gl_uncompressed_format(image, image->get_format(), r_real_format, r_gl_format, r_gl_internal_format, r_gl_type); ERR_FAIL_COND_V_MSG(err != OK, Ref(), vformat("The image format %d is not supported by the Compatibility renderer.", image->get_format())); r_real_format = image->get_format(); @@ -1509,8 +1555,17 @@ void TextureStorage::_texture_set_data(RID p_texture, const Ref &p_image, GLenum internal_format; bool compressed = false; + bool needs_decompress = texture->resize_to_po2; + + // Support for RGTC-compressed Texture Arrays isn't mandated by GLES3/WebGL. + if (!RasterizerGLES3::is_gles_over_gl() && texture->target == GL_TEXTURE_2D_ARRAY) { + if (p_image->get_format() == Image::FORMAT_RGTC_R || p_image->get_format() == Image::FORMAT_RGTC_RG) { + needs_decompress = true; + } + } + Image::Format real_format; - Ref img = _get_gl_image_and_format(p_image, p_image->get_format(), real_format, format, internal_format, type, compressed, texture->resize_to_po2); + Ref img = _get_gl_image_and_format(p_image, p_image->get_format(), real_format, format, internal_format, type, compressed, needs_decompress); ERR_FAIL_COND(img.is_null()); if (texture->resize_to_po2) { if (p_image->is_compressed()) { diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp index 669e6c2aa951..7198f4dd0d08 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp +++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp @@ -725,7 +725,8 @@ Error AudioDriverPulseAudio::init_input_device() { int input_buffer_frames = closest_power_of_2(input_latency * mix_rate / 1000); int input_buffer_size = input_buffer_frames * spec.channels; - pa_buffer_attr attr; + pa_buffer_attr attr = {}; + attr.maxlength = (uint32_t)-1; attr.fragsize = input_buffer_size * sizeof(int16_t); pa_rec_str = pa_stream_new(pa_ctx, "Record", &spec, &pa_rec_map); diff --git a/drivers/vulkan/rendering_device_driver_vulkan.cpp b/drivers/vulkan/rendering_device_driver_vulkan.cpp index 45c574914d65..f46015f1fc33 100644 --- a/drivers/vulkan/rendering_device_driver_vulkan.cpp +++ b/drivers/vulkan/rendering_device_driver_vulkan.cpp @@ -1530,10 +1530,12 @@ RDD::BufferID RenderingDeviceDriverVulkan::buffer_create(uint64_t p_size, BitFie if (is_src && !is_dst) { // Looks like a staging buffer: CPU maps, writes sequentially, then GPU copies to VRAM. alloc_create_info.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; + alloc_create_info.preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; } if (is_dst && !is_src) { // Looks like a readback buffer: GPU copies from VRAM, then CPU maps and reads. alloc_create_info.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT; + alloc_create_info.preferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT; } alloc_create_info.requiredFlags = (VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); } break; diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 4c934104d19b..ef5d878ca0aa 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -933,12 +933,14 @@ void CodeTextEditor::_text_editor_gui_input(const Ref &p_event) { } } +#ifndef ANDROID_ENABLED Ref magnify_gesture = p_event; if (magnify_gesture.is_valid()) { _zoom_to(zoom_factor * powf(magnify_gesture->get_factor(), 0.25f)); accept_event(); return; } +#endif Ref k = p_event; @@ -1909,7 +1911,9 @@ CodeTextEditor::CodeTextEditor() { zoom_button->set_flat(true); zoom_button->set_v_size_flags(SIZE_EXPAND | SIZE_SHRINK_CENTER); zoom_button->set_tooltip_text( - TTR("Zoom factor") + "\n" + vformat(TTR("%sMouse wheel, %s/%s: Finetune\n%s: Reset"), keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL), ED_GET_SHORTCUT("script_editor/zoom_in")->get_as_text(), ED_GET_SHORTCUT("script_editor/zoom_out")->get_as_text(), ED_GET_SHORTCUT("script_editor/reset_zoom")->get_as_text())); + TTR("Zoom factor") + "\n" + + // TRANSLATORS: The placeholders are keyboard shortcuts. The first one is in the form of "Ctrl+"/"Cmd+". + vformat(TTR("%sMouse wheel, %s/%s: Finetune\n%s: Reset"), keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL), ED_GET_SHORTCUT("script_editor/zoom_in")->get_as_text(), ED_GET_SHORTCUT("script_editor/zoom_out")->get_as_text(), ED_GET_SHORTCUT("script_editor/reset_zoom")->get_as_text())); zoom_button->set_text("100 %"); PopupMenu *zoom_menu = zoom_button->get_popup(); diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index 5b89cc8e13e4..1363fca3c2c9 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -826,6 +826,9 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread } } else if (p_msg == "evaluation_return") { expression_evaluator->add_value(p_data); + } else if (p_msg == "window:title") { + ERR_FAIL_COND(p_data.size() != 1); + emit_signal(SNAME("remote_window_title_changed"), p_data[0]); } else { int colon_index = p_msg.find_char(':'); ERR_FAIL_COND_MSG(colon_index < 1, "Invalid message received"); @@ -1784,6 +1787,7 @@ void ScriptEditorDebugger::_bind_methods() { ADD_SIGNAL(MethodInfo("remote_object_property_updated", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "property"))); ADD_SIGNAL(MethodInfo("remote_tree_updated")); ADD_SIGNAL(MethodInfo("remote_tree_select_requested", PropertyInfo(Variant::NODE_PATH, "path"))); + ADD_SIGNAL(MethodInfo("remote_window_title_changed", PropertyInfo(Variant::STRING, "title"))); ADD_SIGNAL(MethodInfo("output", PropertyInfo(Variant::STRING, "msg"), PropertyInfo(Variant::INT, "level"))); ADD_SIGNAL(MethodInfo("stack_dump", PropertyInfo(Variant::ARRAY, "stack_dump"))); ADD_SIGNAL(MethodInfo("stack_frame_vars", PropertyInfo(Variant::INT, "num_vars"))); diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp index ae685a824b27..9881087b3e39 100644 --- a/editor/editor_audio_buses.cpp +++ b/editor/editor_audio_buses.cpp @@ -1265,41 +1265,11 @@ void EditorAudioBuses::_load_layout() { } void EditorAudioBuses::_load_default_layout() { - String layout_path = GLOBAL_GET("audio/buses/default_bus_layout"); - - Ref state; - if (ResourceLoader::exists(layout_path)) { - state = ResourceLoader::load(layout_path, "", ResourceFormatLoader::CACHE_MODE_IGNORE); - } - if (state.is_null()) { - EditorNode::get_singleton()->show_warning(vformat(TTR("There is no '%s' file."), layout_path)); - return; - } - - edited_path = layout_path; - file->set_text(String(TTR("Layout:")) + " " + layout_path.get_file()); - AudioServer::get_singleton()->set_bus_layout(state); - _rebuild_buses(); - EditorUndoRedoManager::get_singleton()->clear_history(EditorUndoRedoManager::GLOBAL_HISTORY); - callable_mp(this, &EditorAudioBuses::_select_layout).call_deferred(); + open_layout(GLOBAL_GET("audio/buses/default_bus_layout")); } void EditorAudioBuses::_file_dialog_callback(const String &p_string) { - if (file_dialog->get_file_mode() == EditorFileDialog::FILE_MODE_OPEN_FILE) { - Ref state = ResourceLoader::load(p_string, "", ResourceFormatLoader::CACHE_MODE_IGNORE); - if (state.is_null()) { - EditorNode::get_singleton()->show_warning(TTR("Invalid file, not an audio bus layout.")); - return; - } - - edited_path = p_string; - file->set_text(String(TTR("Layout:")) + " " + p_string.get_file()); - AudioServer::get_singleton()->set_bus_layout(state); - _rebuild_buses(); - EditorUndoRedoManager::get_singleton()->clear_history(EditorUndoRedoManager::GLOBAL_HISTORY); - callable_mp(this, &EditorAudioBuses::_select_layout).call_deferred(); - - } else if (file_dialog->get_file_mode() == EditorFileDialog::FILE_MODE_SAVE_FILE) { + if (file_dialog->get_file_mode() == EditorFileDialog::FILE_MODE_SAVE_FILE) { if (new_layout) { Ref empty_state; empty_state.instantiate(); @@ -1307,18 +1277,12 @@ void EditorAudioBuses::_file_dialog_callback(const String &p_string) { } Error err = ResourceSaver::save(AudioServer::get_singleton()->generate_bus_layout(), p_string); - if (err != OK) { EditorNode::get_singleton()->show_warning(vformat(TTR("Error saving file: %s"), p_string)); return; } - - edited_path = p_string; - file->set_text(String(TTR("Layout:")) + " " + p_string.get_file()); - _rebuild_buses(); - EditorUndoRedoManager::get_singleton()->clear_history(EditorUndoRedoManager::GLOBAL_HISTORY); - callable_mp(this, &EditorAudioBuses::_select_layout).call_deferred(); } + open_layout(p_string); } void EditorAudioBuses::_bind_methods() { @@ -1330,9 +1294,10 @@ EditorAudioBuses::EditorAudioBuses() { top_hb = memnew(HBoxContainer); add_child(top_hb); + edited_path = ResourceUID::ensure_path(GLOBAL_GET("audio/buses/default_bus_layout")); + file = memnew(Label); - String layout_path = GLOBAL_GET("audio/buses/default_bus_layout"); - file->set_text(String(TTR("Layout:")) + " " + layout_path.get_file()); + file->set_text(vformat("%s %s", TTR("Layout:"), edited_path.get_file())); file->set_clip_text(true); file->set_h_size_flags(SIZE_EXPAND_FILL); top_hb->add_child(file); @@ -1386,8 +1351,6 @@ EditorAudioBuses::EditorAudioBuses() { set_v_size_flags(SIZE_EXPAND_FILL); - edited_path = GLOBAL_GET("audio/buses/default_bus_layout"); - file_dialog = memnew(EditorFileDialog); List ext; ResourceLoader::get_recognized_extensions_for_type("AudioBusLayout", &ext); @@ -1405,14 +1368,21 @@ EditorAudioBuses::EditorAudioBuses() { void EditorAudioBuses::open_layout(const String &p_path) { EditorNode::get_bottom_panel()->make_item_visible(this); - Ref state = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_IGNORE); + const String path = ResourceUID::ensure_path(p_path); + + if (!ResourceLoader::exists(path)) { + EditorNode::get_singleton()->show_warning(vformat(TTR(R"(Can't open audio bus layout: "%s" doesn't exist.)"), path)); + return; + } + + Ref state = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_IGNORE); if (state.is_null()) { - EditorNode::get_singleton()->show_warning(TTR("Invalid file, not an audio bus layout.")); + EditorNode::get_singleton()->show_warning(vformat(TTR(R"(Can't open audio bus layout: "%s" is not a valid audio bus layout.)"), path)); return; } - edited_path = p_path; - file->set_text(p_path.get_file()); + edited_path = path; + file->set_text(vformat("%s %s", TTR("Layout:"), path.get_file())); AudioServer::get_singleton()->set_bus_layout(state); _rebuild_buses(); EditorUndoRedoManager::get_singleton()->clear_history(EditorUndoRedoManager::GLOBAL_HISTORY); diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 8670cb490201..b8ce3ff65bf7 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -1747,7 +1747,7 @@ void EditorFileSystem::_save_filesystem_cache(EditorFileSystemDirectory *p_dir, if (!p_dir) { return; //none } - p_file->store_line("::" + p_dir->get_path() + "::" + String::num(p_dir->modified_time)); + p_file->store_line("::" + p_dir->get_path() + "::" + String::num_int64(p_dir->modified_time)); for (int i = 0; i < p_dir->files.size(); i++) { const EditorFileSystemDirectory::FileInfo *file_info = p_dir->files[i]; @@ -2348,16 +2348,17 @@ void EditorFileSystem::update_files(const Vector &p_script_paths) { _save_late_updated_files(); //files need to be updated in the re-scan } - const String old_script_class_icon_path = fs->files[cpos]->script_class_icon_path; - const String old_class_name = fs->files[cpos]->script_class_name; - fs->files[cpos]->type = type; - fs->files[cpos]->resource_script_class = script_class; - fs->files[cpos]->uid = uid; - fs->files[cpos]->script_class_name = _get_global_script_class(type, file, &fs->files[cpos]->script_class_extends, &fs->files[cpos]->script_class_icon_path); - fs->files[cpos]->import_group_file = ResourceLoader::get_import_group_file(file); - fs->files[cpos]->modified_time = FileAccess::get_modified_time(file); - fs->files[cpos]->deps = _get_dependencies(file); - fs->files[cpos]->import_valid = (type == "TextFile" || type == "OtherFile") ? true : ResourceLoader::is_import_valid(file); + EditorFileSystemDirectory::FileInfo *fi = fs->files[cpos]; + const String old_script_class_icon_path = fi->script_class_icon_path; + const String old_class_name = fi->script_class_name; + fi->type = type; + fi->resource_script_class = script_class; + fi->uid = uid; + fi->script_class_name = _get_global_script_class(type, file, &fi->script_class_extends, &fi->script_class_icon_path); + fi->import_group_file = ResourceLoader::get_import_group_file(file); + fi->modified_time = FileAccess::get_modified_time(file); + fi->deps = _get_dependencies(file); + fi->import_valid = type == "TextFile" || type == "OtherFile" || ResourceLoader::is_import_valid(file); if (uid != ResourceUID::INVALID_ID) { if (ResourceUID::get_singleton()->has_id(uid)) { @@ -2367,25 +2368,36 @@ void EditorFileSystem::update_files(const Vector &p_script_paths) { } ResourceUID::get_singleton()->update_cache(); + } else { + if (ResourceLoader::exists(file) && !ResourceLoader::has_custom_uid_support(file) && !FileAccess::exists(file + ".uid")) { + Ref f = FileAccess::open(file + ".uid", FileAccess::WRITE); + if (f.is_valid()) { + const ResourceUID::ID id = ResourceUID::get_singleton()->create_id(); + ResourceUID::get_singleton()->add_id(id, file); + f->store_line(ResourceUID::get_singleton()->id_to_text(id)); + fi->uid = id; + } + } } + // Update preview EditorResourcePreview::get_singleton()->check_for_invalidation(file); - if (ClassDB::is_parent_class(fs->files[cpos]->type, SNAME("Script"))) { - _queue_update_script_class(file, fs->files[cpos]->type, fs->files[cpos]->script_class_name, fs->files[cpos]->script_class_extends, fs->files[cpos]->script_class_icon_path); + if (ClassDB::is_parent_class(fi->type, SNAME("Script"))) { + _queue_update_script_class(file, fi->type, fi->script_class_name, fi->script_class_extends, fi->script_class_icon_path); } - if (fs->files[cpos]->type == SNAME("PackedScene")) { + if (fi->type == SNAME("PackedScene")) { _queue_update_scene_groups(file); } - if (ClassDB::is_parent_class(fs->files[cpos]->type, SNAME("Resource"))) { - files_to_update_icon_path.push_back(fs->files[cpos]); - } else if (old_script_class_icon_path != fs->files[cpos]->script_class_icon_path) { + if (ClassDB::is_parent_class(fi->type, SNAME("Resource"))) { + files_to_update_icon_path.push_back(fi); + } else if (old_script_class_icon_path != fi->script_class_icon_path) { update_files_icon_cache = true; } // Restore another script as the global class name if multiple scripts had the same old class name. - if (!old_class_name.is_empty() && fs->files[cpos]->script_class_name != old_class_name && ClassDB::is_parent_class(type, SNAME("Script"))) { + if (!old_class_name.is_empty() && fi->script_class_name != old_class_name && ClassDB::is_parent_class(type, SNAME("Script"))) { EditorFileSystemDirectory::FileInfo *old_fi = nullptr; String old_file = _get_file_by_class_name(filesystem, old_class_name, old_fi); if (!old_file.is_empty() && old_fi) { @@ -3012,7 +3024,7 @@ bool EditorFileSystem::_copy_directory(const String &p_from, const String &p_to, for (String F = old_dir->_get_next(); !F.is_empty(); F = old_dir->_get_next()) { if (old_dir->current_is_dir()) { success = _copy_directory(p_from.path_join(F), p_to.path_join(F), p_files) && success; - } else if (F.get_extension() != "import") { + } else if (F.get_extension() != "import" && F.get_extension() != "uid") { CopiedFile copy; copy.from = p_from.path_join(F); copy.to = p_to.path_join(F); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 3f3b06b428a6..b79d1243ba6c 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -2996,6 +2996,7 @@ void EditorInspector::update_tree() { int current_focusable = -1; // Temporarily disable focus following on the root inspector to avoid jumping while the inspector is updating. + bool was_following = get_root_inspector()->is_following_focus(); get_root_inspector()->set_follow_focus(false); if (property_focusable != -1) { @@ -3024,7 +3025,7 @@ void EditorInspector::update_tree() { _clear(!object); if (!object) { - get_root_inspector()->set_follow_focus(true); + get_root_inspector()->set_follow_focus(was_following); return; } @@ -3890,7 +3891,7 @@ void EditorInspector::update_tree() { EditorNode::get_singleton()->hide_unused_editors(); } - get_root_inspector()->set_follow_focus(true); + get_root_inspector()->set_follow_focus(was_following); } void EditorInspector::update_property(const String &p_prop) { @@ -4742,6 +4743,15 @@ void EditorInspector::_notification(int p_what) { update_tree(); } } break; + + case NOTIFICATION_FOCUS_ENTER: { + set_follow_focus(true); + } break; + + case NOTIFICATION_FOCUS_EXIT: { + // Don't follow focus when the inspector is not focused. Prevents potential jumping when gaining focus. + set_follow_focus(false); + } break; } } @@ -4898,7 +4908,6 @@ EditorInspector::EditorInspector() { base_vbox->add_child(main_vbox); set_horizontal_scroll_mode(SCROLL_MODE_DISABLED); - set_follow_focus(true); changing = 0; search_box = nullptr; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 4d4f8c2307d6..778257b3930a 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -163,6 +163,7 @@ #include "editor/surface_upgrade_tool.h" #include "editor/themes/editor_scale.h" #include "editor/themes/editor_theme_manager.h" +#include "editor/uid_upgrade_tool.h" #include "editor/window_wrapper.h" #include "modules/modules_enabled.gen.h" // For gdscript, mono. @@ -467,6 +468,7 @@ void EditorNode::_update_from_settings() { bool use_hdr_2d = GLOBAL_GET("rendering/viewport/hdr_2d"); scene_root->set_use_hdr_2d(use_hdr_2d); + get_viewport()->set_use_hdr_2d(use_hdr_2d); float mesh_lod_threshold = GLOBAL_GET("rendering/mesh_lod/lod_change/threshold_pixels"); scene_root->set_mesh_lod_threshold(mesh_lod_threshold); @@ -651,6 +653,10 @@ void EditorNode::_notification(int p_what) { run_surface_upgrade_tool = false; SurfaceUpgradeTool::get_singleton()->connect("upgrade_finished", callable_mp(EditorFileSystem::get_singleton(), &EditorFileSystem::scan), CONNECT_ONE_SHOT); SurfaceUpgradeTool::get_singleton()->finish_upgrade(); + } else if (run_uid_upgrade_tool) { + run_uid_upgrade_tool = false; + UIDUpgradeTool::get_singleton()->connect("upgrade_finished", callable_mp(EditorFileSystem::get_singleton(), &EditorFileSystem::scan), CONNECT_ONE_SHOT); + UIDUpgradeTool::get_singleton()->finish_upgrade(); } else { EditorFileSystem::get_singleton()->scan(); } @@ -735,6 +741,23 @@ void EditorNode::_notification(int p_what) { // Save the project after opening to mark it as last modified, except in headless mode. if (DisplayServer::get_singleton()->window_can_draw()) { + // Try to determine if this project's Godot version was less than 4.4 - if + // so, we'll ask the user if they want to upgrade the project. + PackedStringArray features = ProjectSettings::get_singleton()->get_setting("application/config/features"); + if (!features.is_empty()) { + String version_str = features[0]; + PackedStringArray version_parts = version_str.split(".", true, 1); + if (version_parts.size() >= 2) { + if (version_parts[0].is_valid_int() && version_parts[1].is_valid_int()) { + int major_ver = version_parts[0].to_int(); + int minor_ver = version_parts[1].to_int(); + if (major_ver < 4 || (major_ver == 4 && minor_ver < 4)) { + should_prompt_uid_upgrade_tool = true; + } + } + } + } + ProjectSettings::get_singleton()->save(); } @@ -1180,6 +1203,11 @@ void EditorNode::_sources_changed(bool p_exist) { EditorResourcePreview::get_singleton()->start(); } + if (should_prompt_uid_upgrade_tool) { + should_prompt_uid_upgrade_tool = false; + uid_upgrade_dialog->popup_on_demand(); + } + get_tree()->create_timer(1.0f)->connect("timeout", callable_mp(this, &EditorNode::_remove_lock_file)); } } @@ -1482,16 +1510,6 @@ void EditorNode::save_resource_as(const Ref &p_resource, const String file->popup_file_dialog(); } -void EditorNode::ensure_uid_file(const String &p_new_resource_path) { - if (ResourceLoader::exists(p_new_resource_path) && !ResourceLoader::has_custom_uid_support(p_new_resource_path) && !FileAccess::exists(p_new_resource_path + ".uid")) { - Ref f = FileAccess::open(p_new_resource_path + ".uid", FileAccess::WRITE); - if (f.is_valid()) { - const ResourceUID::ID id = ResourceUID::get_singleton()->create_id(); - f->store_line(ResourceUID::get_singleton()->id_to_text(id)); - } - } -} - void EditorNode::_menu_option(int p_option) { _menu_option_confirm(p_option, false); } @@ -1923,14 +1941,12 @@ void EditorNode::_save_scene(String p_file, int idx) { return; } - List>> anim_backups; - _reset_animation_mixers(scene, &anim_backups); - scene->propagate_notification(NOTIFICATION_EDITOR_PRE_SAVE); editor_data.apply_changes_in_editors(); save_default_environment(); - + List>> anim_backups; + _reset_animation_mixers(scene, &anim_backups); _save_editor_states(p_file, idx); Ref sdata; @@ -2233,11 +2249,6 @@ void EditorNode::_dialog_action(String p_file) { ERR_FAIL_COND(saving_resource.is_null()); save_resource_in_path(saving_resource, p_file); - if (current_menu_option == RESOURCE_SAVE_AS) { - // Create .uid file when making new Resource. - ensure_uid_file(p_file); - } - saving_resource = Ref(); ObjectID current_id = editor_history.get_current(); Object *current_obj = current_id.is_valid() ? ObjectDB::get_instance(current_id) : nullptr; @@ -3332,6 +3343,9 @@ void EditorNode::_tool_menu_option(int p_idx) { case TOOLS_SURFACE_UPGRADE: { surface_upgrade_dialog->popup_on_demand(); } break; + case TOOLS_UID_UPGRADE: { + uid_upgrade_dialog->popup_on_demand(); + } break; case TOOLS_CUSTOM: { if (tool_menu->get_item_submenu(p_idx) == "") { Callable callback = tool_menu->get_item_metadata(p_idx); @@ -5716,6 +5730,10 @@ void EditorNode::_cancel_close_scene_tab() { } } +void EditorNode::_prepare_save_confirmation_popup() { + save_confirmation->reparent(get_last_exclusive_window()); +} + void EditorNode::_toggle_distraction_free_mode() { if (EDITOR_GET("interface/editor/separate_distraction_mode")) { int screen = editor_main_screen->get_selected_index(); @@ -6574,7 +6592,7 @@ Vector> EditorNode::find_resource_conversion Vector> EditorNode::find_resource_conversion_plugin_for_type_name(const String &p_type) { Vector> ret; - if (ClassDB::can_instantiate(p_type)) { + if (ClassDB::class_exists(p_type) && ClassDB::can_instantiate(p_type)) { Ref temp = Object::cast_to(ClassDB::instantiate(p_type)); if (temp.is_valid()) { for (Ref resource_conversion_plugin : resource_conversion_plugins) { @@ -6879,6 +6897,13 @@ EditorNode::EditorNode() { SurfaceUpgradeTool::get_singleton()->begin_upgrade(); } + // Same for UID upgrade tool. + uid_upgrade_tool = memnew(UIDUpgradeTool); + run_uid_upgrade_tool = EditorSettings::get_singleton()->get_project_metadata(UIDUpgradeTool::META_UID_UPGRADE_TOOL, UIDUpgradeTool::META_RUN_ON_RESTART, false); + if (run_uid_upgrade_tool) { + UIDUpgradeTool::get_singleton()->begin_upgrade(); + } + { bool agile_input_event_flushing = EDITOR_GET("input/buffering/agile_event_flushing"); bool use_accumulated_input = EDITOR_GET("input/buffering/use_accumulated_input"); @@ -7421,6 +7446,7 @@ EditorNode::EditorNode() { tool_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/orphan_resource_explorer", TTRC("Orphan Resource Explorer...")), TOOLS_ORPHAN_RESOURCES); tool_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/engine_compilation_configuration_editor", TTRC("Engine Compilation Configuration Editor...")), TOOLS_BUILD_PROFILE_MANAGER); tool_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/upgrade_mesh_surfaces", TTRC("Upgrade Mesh Surfaces...")), TOOLS_SURFACE_UPGRADE); + tool_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/upgrade_uids", TTRC("Upgrade UIDs...")), TOOLS_UID_UPGRADE); project_menu->add_separator(); project_menu->add_shortcut(ED_SHORTCUT("editor/reload_current_project", TTRC("Reload Current Project")), PROJECT_RELOAD_CURRENT_PROJECT); @@ -7701,6 +7727,9 @@ EditorNode::EditorNode() { surface_upgrade_dialog = memnew(SurfaceUpgradeDialog); gui_base->add_child(surface_upgrade_dialog); + uid_upgrade_dialog = memnew(UIDUpgradeDialog); + gui_base->add_child(uid_upgrade_dialog); + confirmation = memnew(ConfirmationDialog); gui_base->add_child(confirmation); confirmation->connect(SceneStringName(confirmed), callable_mp(this, &EditorNode::_menu_confirm_current)); @@ -7712,6 +7741,7 @@ EditorNode::EditorNode() { save_confirmation->connect(SceneStringName(confirmed), callable_mp(this, &EditorNode::_menu_confirm_current)); save_confirmation->connect("custom_action", callable_mp(this, &EditorNode::_discard_changes)); save_confirmation->connect("canceled", callable_mp(this, &EditorNode::_cancel_close_scene_tab)); + save_confirmation->connect("about_to_popup", callable_mp(this, &EditorNode::_prepare_save_confirmation_popup)); gradle_build_manage_templates = memnew(ConfirmationDialog); gradle_build_manage_templates->set_text(TTR("Android build template is missing, please install relevant templates.")); @@ -8094,6 +8124,7 @@ EditorNode::~EditorNode() { memdelete(editor_plugins_force_input_forwarding); memdelete(progress_hb); memdelete(surface_upgrade_tool); + memdelete(uid_upgrade_tool); memdelete(editor_dock_manager); EditorSettings::destroy(); diff --git a/editor/editor_node.h b/editor/editor_node.h index e9ad3492fef2..563d7a79fcbe 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -100,6 +100,8 @@ class ProjectSettingsEditor; class SceneImportSettingsDialog; class SurfaceUpgradeTool; class SurfaceUpgradeDialog; +class UIDUpgradeTool; +class UIDUpgradeDialog; struct EditorProgress { String task; @@ -166,6 +168,7 @@ class EditorNode : public Node { TOOLS_ORPHAN_RESOURCES, TOOLS_BUILD_PROFILE_MANAGER, TOOLS_SURFACE_UPGRADE, + TOOLS_UID_UPGRADE, TOOLS_CUSTOM, VCS_METADATA, @@ -460,8 +463,15 @@ class EditorNode : public Node { SurfaceUpgradeTool *surface_upgrade_tool = nullptr; SurfaceUpgradeDialog *surface_upgrade_dialog = nullptr; + bool run_surface_upgrade_tool = false; + UIDUpgradeTool *uid_upgrade_tool = nullptr; + UIDUpgradeDialog *uid_upgrade_dialog = nullptr; + + bool run_uid_upgrade_tool = false; + bool should_prompt_uid_upgrade_tool = false; + bool was_window_windowed_last = false; bool unfocused_low_processor_usage_mode_enabled = true; @@ -566,6 +576,8 @@ class EditorNode : public Node { void _scene_tab_closed(int p_tab); void _cancel_close_scene_tab(); + void _prepare_save_confirmation_popup(); + void _inherit_request(String p_file); void _instantiate_request(const Vector &p_files); @@ -746,7 +758,6 @@ class EditorNode : public Node { void save_resource_in_path(const Ref &p_resource, const String &p_path); void save_resource(const Ref &p_resource); void save_resource_as(const Ref &p_resource, const String &p_at_path = String()); - void ensure_uid_file(const String &p_new_resource_path); void show_about() { _menu_option_confirm(HELP_ABOUT, false); } diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 69b44a3e8f88..b1a4bb76f325 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -2599,6 +2599,17 @@ void EditorPropertyColor::_color_changed(const Color &p_color) { get_edited_object()->set(get_edited_property(), p_color); } +void EditorPropertyColor::_picker_created() { + picker->get_popup()->connect("about_to_popup", callable_mp(this, &EditorPropertyColor::_popup_opening)); + picker->connect("popup_closed", callable_mp(this, &EditorPropertyColor::_popup_closed), CONNECT_DEFERRED); +} + +void EditorPropertyColor::_popup_opening() { + EditorNode::get_singleton()->setup_color_picker(picker->get_picker()); + last_color = picker->get_pick_color(); + was_checked = !is_checkable() || is_checked(); +} + void EditorPropertyColor::_popup_closed() { get_edited_object()->set(get_edited_property(), was_checked ? Variant(last_color) : Variant()); if (!picker->get_pick_color().is_equal_approx(last_color)) { @@ -2606,11 +2617,6 @@ void EditorPropertyColor::_popup_closed() { } } -void EditorPropertyColor::_picker_opening() { - last_color = picker->get_pick_color(); - was_checked = !is_checkable() || is_checked(); -} - void EditorPropertyColor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: @@ -2654,9 +2660,7 @@ EditorPropertyColor::EditorPropertyColor() { add_child(picker); picker->set_flat(true); picker->connect("color_changed", callable_mp(this, &EditorPropertyColor::_color_changed)); - picker->connect("popup_closed", callable_mp(this, &EditorPropertyColor::_popup_closed), CONNECT_DEFERRED); - picker->get_popup()->connect("about_to_popup", callable_mp(EditorNode::get_singleton(), &EditorNode::setup_color_picker).bind(picker->get_picker())); - picker->get_popup()->connect("about_to_popup", callable_mp(this, &EditorPropertyColor::_picker_opening)); + picker->connect("picker_created", callable_mp(this, &EditorPropertyColor::_picker_created), CONNECT_ONE_SHOT); } ////////////// NODE PATH ////////////////////// diff --git a/editor/editor_properties.h b/editor/editor_properties.h index bcdae5342d28..6fe8cc3c3f38 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -593,8 +593,9 @@ class EditorPropertyColor : public EditorProperty { GDCLASS(EditorPropertyColor, EditorProperty); ColorPickerButton *picker = nullptr; void _color_changed(const Color &p_color); + void _picker_created(); + void _popup_opening(); void _popup_closed(); - void _picker_opening(); Color last_color; bool live_changes_enabled = true; diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index 49ff31de7be1..9663157ff990 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -417,7 +417,7 @@ void EditorPropertyArray::update_property() { } else { edit->set_text_alignment(HORIZONTAL_ALIGNMENT_CENTER); edit->set_button_icon(Ref()); - edit->set_text(vformat(TTR("%s (size %s)"), array_type_name, itos(size))); + edit->set_text(vformat(TTR("%s (size %d)"), array_type_name, size)); } bool unfolded = get_edited_object()->editor_is_section_unfolded(get_edited_property()); @@ -881,6 +881,7 @@ void EditorPropertyArray::_reorder_button_up() { array.call("remove_at", reorder_slot.index); array.call("insert", reorder_to_index, value_to_move); + slots[reorder_to_index % page_length].reorder_button->grab_focus(); emit_changed(get_edited_property(), array); } @@ -951,7 +952,11 @@ void EditorPropertyDictionary::_property_changed(const String &p_property, Varia } object->set(p_property, p_value); - emit_changed(get_edited_property(), object->get_dict(), p_name, p_changing); + bool new_item_or_key = !p_property.begins_with("indices"); + emit_changed(get_edited_property(), object->get_dict(), p_name, p_changing || new_item_or_key); + if (new_item_or_key) { + update_property(); + } } void EditorPropertyDictionary::_change_type(Object *p_button, int p_slot_index) { diff --git a/editor/editor_run.cpp b/editor/editor_run.cpp index 5251f1e558d5..84367971ce5d 100644 --- a/editor/editor_run.cpp +++ b/editor/editor_run.cpp @@ -104,99 +104,16 @@ Error EditorRun::run(const String &p_scene, const String &p_write_movie, const V } } - int screen = EDITOR_GET("run/window_placement/screen"); - if (screen == -5) { - // Same as editor - screen = DisplayServer::get_singleton()->window_get_current_screen(); - } else if (screen == -4) { - // Previous monitor (wrap to the other end if needed) - screen = Math::wrapi( - DisplayServer::get_singleton()->window_get_current_screen() - 1, - 0, - DisplayServer::get_singleton()->get_screen_count()); - } else if (screen == -3) { - // Next monitor (wrap to the other end if needed) - screen = Math::wrapi( - DisplayServer::get_singleton()->window_get_current_screen() + 1, - 0, - DisplayServer::get_singleton()->get_screen_count()); + WindowPlacement window_placement = get_window_placement(); + if (window_placement.position != Point2i(INT_MAX, INT_MAX)) { + args.push_back("--position"); + args.push_back(itos(window_placement.position.x) + "," + itos(window_placement.position.y)); } - Rect2 screen_rect = DisplayServer::get_singleton()->screen_get_usable_rect(screen); - - int window_placement = EDITOR_GET("run/window_placement/rect"); - if (screen_rect != Rect2()) { - Size2 window_size; - window_size.x = GLOBAL_GET("display/window/size/viewport_width"); - window_size.y = GLOBAL_GET("display/window/size/viewport_height"); - - Size2 desired_size; - desired_size.x = GLOBAL_GET("display/window/size/window_width_override"); - desired_size.y = GLOBAL_GET("display/window/size/window_height_override"); - if (desired_size.x > 0 && desired_size.y > 0) { - window_size = desired_size; - } - - if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_HIDPI)) { - bool hidpi_proj = GLOBAL_GET("display/window/dpi/allow_hidpi"); - int display_scale = 1; - - if (OS::get_singleton()->is_hidpi_allowed()) { - if (hidpi_proj) { - display_scale = 1; // Both editor and project runs in hiDPI mode, do not scale. - } else { - display_scale = DisplayServer::get_singleton()->screen_get_max_scale(); // Editor is in hiDPI mode, project is not, scale down. - } - } else { - if (hidpi_proj) { - display_scale = (1.f / DisplayServer::get_singleton()->screen_get_max_scale()); // Editor is not in hiDPI mode, project is, scale up. - } else { - display_scale = 1; // Both editor and project runs in lowDPI mode, do not scale. - } - } - screen_rect.position /= display_scale; - screen_rect.size /= display_scale; - } - - switch (window_placement) { - case 0: { // top left - args.push_back("--position"); - args.push_back(itos(screen_rect.position.x) + "," + itos(screen_rect.position.y)); - } break; - case 1: { // centered - Vector2 pos = (screen_rect.position) + ((screen_rect.size - window_size) / 2).floor(); - args.push_back("--position"); - args.push_back(itos(pos.x) + "," + itos(pos.y)); - } break; - case 2: { // custom pos - Vector2 pos = EDITOR_GET("run/window_placement/rect_custom_position"); - pos += screen_rect.position; - args.push_back("--position"); - args.push_back(itos(pos.x) + "," + itos(pos.y)); - } break; - case 3: { // force maximized - Vector2 pos = screen_rect.position + screen_rect.size / 2; - args.push_back("--position"); - args.push_back(itos(pos.x) + "," + itos(pos.y)); - args.push_back("--maximized"); - } break; - case 4: { // force fullscreen - Vector2 pos = screen_rect.position + screen_rect.size / 2; - args.push_back("--position"); - args.push_back(itos(pos.x) + "," + itos(pos.y)); - args.push_back("--fullscreen"); - } break; - } - } else { - // Unable to get screen info, skip setting position. - switch (window_placement) { - case 3: { // force maximized - args.push_back("--maximized"); - } break; - case 4: { // force fullscreen - args.push_back("--fullscreen"); - } break; - } + if (window_placement.force_maximized) { + args.push_back("--maximized"); + } else if (window_placement.force_fullscreen) { + args.push_back("--fullscreen"); } List breakpoints; @@ -297,6 +214,98 @@ OS::ProcessID EditorRun::get_current_process() const { return pids.front()->get(); } +EditorRun::WindowPlacement EditorRun::get_window_placement() { + WindowPlacement placement = WindowPlacement(); + placement.screen = EDITOR_GET("run/window_placement/screen"); + if (placement.screen == -5) { + // Same as editor + placement.screen = DisplayServer::get_singleton()->window_get_current_screen(); + } else if (placement.screen == -4) { + // Previous monitor (wrap to the other end if needed) + placement.screen = Math::wrapi( + DisplayServer::get_singleton()->window_get_current_screen() - 1, + 0, + DisplayServer::get_singleton()->get_screen_count()); + } else if (placement.screen == -3) { + // Next monitor (wrap to the other end if needed) + placement.screen = Math::wrapi( + DisplayServer::get_singleton()->window_get_current_screen() + 1, + 0, + DisplayServer::get_singleton()->get_screen_count()); + } else if (placement.screen == -2) { + // Primary screen + placement.screen = DisplayServer::get_singleton()->get_primary_screen(); + } + + placement.size.x = GLOBAL_GET("display/window/size/viewport_width"); + placement.size.y = GLOBAL_GET("display/window/size/viewport_height"); + + Size2 desired_size; + desired_size.x = GLOBAL_GET("display/window/size/window_width_override"); + desired_size.y = GLOBAL_GET("display/window/size/window_height_override"); + if (desired_size.x > 0 && desired_size.y > 0) { + placement.size = desired_size; + } + + Rect2 screen_rect = DisplayServer::get_singleton()->screen_get_usable_rect(placement.screen); + + int window_placement = EDITOR_GET("run/window_placement/rect"); + if (screen_rect != Rect2()) { + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_HIDPI)) { + bool hidpi_proj = GLOBAL_GET("display/window/dpi/allow_hidpi"); + int display_scale = 1; + + if (OS::get_singleton()->is_hidpi_allowed()) { + if (hidpi_proj) { + display_scale = 1; // Both editor and project runs in hiDPI mode, do not scale. + } else { + display_scale = DisplayServer::get_singleton()->screen_get_max_scale(); // Editor is in hiDPI mode, project is not, scale down. + } + } else { + if (hidpi_proj) { + display_scale = (1.f / DisplayServer::get_singleton()->screen_get_max_scale()); // Editor is not in hiDPI mode, project is, scale up. + } else { + display_scale = 1; // Both editor and project runs in lowDPI mode, do not scale. + } + } + screen_rect.position /= display_scale; + screen_rect.size /= display_scale; + } + + switch (window_placement) { + case 0: { // top left + placement.position = screen_rect.position; + } break; + case 1: { // centered + placement.position = (screen_rect.position) + ((screen_rect.size - placement.size) / 2).floor(); + } break; + case 2: { // custom pos + Vector2 pos = EDITOR_GET("run/window_placement/rect_custom_position"); + pos += screen_rect.position; + placement.position = pos; + } break; + case 3: { // force maximized + placement.force_maximized = true; + } break; + case 4: { // force fullscreen + placement.force_fullscreen = true; + } break; + } + } else { + // Unable to get screen info, skip setting position. + switch (window_placement) { + case 3: { // force maximized + placement.force_maximized = true; + } break; + case 4: { // force fullscreen + placement.force_fullscreen = true; + } break; + } + } + + return placement; +} + EditorRun::EditorRun() { status = STATUS_STOP; running_scene = ""; diff --git a/editor/editor_run.h b/editor/editor_run.h index 94fb977e931b..5e342944130c 100644 --- a/editor/editor_run.h +++ b/editor/editor_run.h @@ -45,6 +45,14 @@ class EditorRun { List pids; + struct WindowPlacement { + int screen = 0; + Point2i position = Point2i(INT_MAX, INT_MAX); + Size2i size; + bool force_maximized = false; + bool force_fullscreen = false; + }; + private: Status status; String running_scene; @@ -64,6 +72,8 @@ class EditorRun { int get_child_process_count() const { return pids.size(); } OS::ProcessID get_current_process() const; + static WindowPlacement get_window_placement(); + EditorRun(); }; diff --git a/editor/editor_run_native.cpp b/editor/editor_run_native.cpp index 9e1c2fea3e26..faa809154982 100644 --- a/editor/editor_run_native.cpp +++ b/editor/editor_run_native.cpp @@ -145,6 +145,8 @@ Error EditorRunNative::start_run_native(int p_id) { } run_confirmed = false; + preset->update_value_overrides(); + emit_signal(SNAME("native_run"), preset); BitField flags = 0; diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 116950514396..9a5a51a18a25 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -940,6 +940,8 @@ void EditorSettings::_load_defaults(Ref p_extra_config) { String android_window_hints = "Auto (based on screen size):0,Same as Editor:1,Side-by-side with Editor:2,Launch in PiP mode:3"; EDITOR_SETTING_BASIC(Variant::INT, PROPERTY_HINT_ENUM, "run/window_placement/android_window", 0, android_window_hints) + EDITOR_SETTING_BASIC(Variant::INT, PROPERTY_HINT_ENUM, "run/window_placement/game_embed_mode", 0, "Use Per-Project Configuration:0,Embed Game:1,Make Game Workspace Floating:2,Disabled:3"); + int default_play_window_pip_mode = 0; #ifdef ANDROID_ENABLED default_play_window_pip_mode = 2; diff --git a/editor/editor_settings_dialog.cpp b/editor/editor_settings_dialog.cpp index 641c36943618..607c922c691c 100644 --- a/editor/editor_settings_dialog.cpp +++ b/editor/editor_settings_dialog.cpp @@ -439,6 +439,7 @@ TreeItem *EditorSettingsDialog::_create_shortcut_treeitem(TreeItem *p_parent, co TreeItem *event_item = shortcuts->create_item(shortcut_item); + // TRANSLATORS: This is the label for the main input event of a shortcut. event_item->set_text(0, shortcut_item->get_child_count() == 1 ? TTRC("Primary") : ""); event_item->set_text(1, ie->as_text()); event_item->set_auto_translate_mode(1, AUTO_TRANSLATE_MODE_DISABLED); diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp index 8abfd8d62dea..8b44a6f88ab0 100644 --- a/editor/export/editor_export_platform.cpp +++ b/editor/export/editor_export_platform.cpp @@ -35,6 +35,7 @@ #include "core/extension/gdextension.h" #include "core/io/file_access_encrypted.h" #include "core/io/file_access_pack.h" // PACK_HEADER_MAGIC, PACK_FORMAT_VERSION +#include "core/io/resource_uid.h" #include "core/io/zip_io.h" #include "core/version.h" #include "editor/editor_file_system.h" @@ -49,6 +50,26 @@ #include "scene/resources/image_texture.h" #include "scene/resources/packed_scene.h" +class EditorExportSaveProxy { + HashSet saved_paths; + EditorExportPlatform::EditorExportSaveFunction save_func; + bool tracking_saves = false; + +public: + bool has_saved(const String &p_path) const { return saved_paths.has(p_path); } + + Error save_file(void *p_userdata, const String &p_path, const Vector &p_data, int p_file, int p_total, const Vector &p_enc_in_filters, const Vector &p_enc_ex_filters, const Vector &p_key, uint64_t p_seed) { + if (tracking_saves) { + saved_paths.insert(p_path.simplify_path().trim_prefix("res://")); + } + + return save_func(p_userdata, p_path, p_data, p_file, p_total, p_enc_in_filters, p_enc_ex_filters, p_key, p_seed); + } + + EditorExportSaveProxy(EditorExportPlatform::EditorExportSaveFunction p_save_func, bool p_track_saves) : + save_func(p_save_func), tracking_saves(p_track_saves) {} +}; + static int _get_pad(int p_alignment, int p_n) { int rest = p_n % p_alignment; int pad = 0; @@ -59,17 +80,6 @@ static int _get_pad(int p_alignment, int p_n) { return pad; } -template -static bool _has_pack_path(const T &p_paths, const String &p_path) { - for (const String &E : p_paths) { - if (E.simplify_path().trim_prefix("res://") == p_path) { - return true; - } - } - - return false; -} - #define PCK_PADDING 16 bool EditorExportPlatform::fill_log_messages(RichTextLabel *p_log, Error p_err) { @@ -955,8 +965,8 @@ Vector EditorExportPlatform::get_forced_export_files() { files.push_back(ProjectSettings::get_singleton()->get_global_class_list_path()); - String icon = GLOBAL_GET("application/config/icon"); - String splash = GLOBAL_GET("application/boot_splash/image"); + String icon = ResourceUID::ensure_path(GLOBAL_GET("application/config/icon")); + String splash = ResourceUID::ensure_path(GLOBAL_GET("application/boot_splash/image")); if (!icon.is_empty() && FileAccess::exists(icon)) { files.push_back(icon); } @@ -1139,9 +1149,10 @@ Error EditorExportPlatform::export_project_files(const Ref & } } + EditorExportSaveProxy save_proxy(p_save_func, p_remove_func != nullptr); + Error err = OK; Vector> export_plugins = EditorExport::get_singleton()->get_export_plugins(); - Vector extra_paths; struct SortByName { bool operator()(const Ref &left, const Ref &right) const { @@ -1162,12 +1173,10 @@ Error EditorExportPlatform::export_project_files(const Ref & } } for (int j = 0; j < export_plugins[i]->extra_files.size(); j++) { - err = p_save_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, 0, paths.size(), enc_in_filters, enc_ex_filters, key, seed); + err = save_proxy.save_file(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, 0, paths.size(), enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; } - - extra_paths.push_back(export_plugins[i]->extra_files[j].path); } export_plugins.write[i]->_clear(); @@ -1280,7 +1289,7 @@ Error EditorExportPlatform::export_project_files(const Ref & } for (int j = 0; j < export_plugins[i]->extra_files.size(); j++) { - err = p_save_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, idx, total, enc_in_filters, enc_ex_filters, key, seed); + err = save_proxy.save_file(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, idx, total, enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; } @@ -1289,8 +1298,6 @@ Error EditorExportPlatform::export_project_files(const Ref & path_remaps.push_back(path); path_remaps.push_back(export_plugins[i]->extra_files[j].path); } - - extra_paths.push_back(export_plugins[i]->extra_files[j].path); } if (export_plugins[i]->skipped) { @@ -1312,7 +1319,7 @@ Error EditorExportPlatform::export_project_files(const Ref & if (importer_type == "keep") { // Just keep file as-is. Vector array = FileAccess::get_file_as_bytes(path); - err = p_save_func(p_udata, path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed); + err = save_proxy.save_file(p_udata, path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; @@ -1355,13 +1362,13 @@ Error EditorExportPlatform::export_project_files(const Ref & sarr.resize(cs.size()); memcpy(sarr.ptrw(), cs.ptr(), sarr.size()); - err = p_save_func(p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key, seed); + err = save_proxy.save_file(p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; } // Now actual remapped file: sarr = FileAccess::get_file_as_bytes(export_path); - err = p_save_func(p_udata, export_path, sarr, idx, total, enc_in_filters, enc_ex_filters, key, seed); + err = save_proxy.save_file(p_udata, export_path, sarr, idx, total, enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; } @@ -1391,14 +1398,14 @@ Error EditorExportPlatform::export_project_files(const Ref & if (remap == "path") { String remapped_path = config->get_value("remap", remap); Vector array = FileAccess::get_file_as_bytes(remapped_path); - err = p_save_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed); + err = save_proxy.save_file(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed); } else if (remap.begins_with("path.")) { String feature = remap.get_slice(".", 1); if (remap_features.has(feature)) { String remapped_path = config->get_value("remap", remap); Vector array = FileAccess::get_file_as_bytes(remapped_path); - err = p_save_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed); + err = save_proxy.save_file(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed); } else { // Remove paths if feature not enabled. config->erase_section_key("remap", remap); @@ -1424,7 +1431,7 @@ Error EditorExportPlatform::export_project_files(const Ref & sarr.resize(cs.size()); memcpy(sarr.ptrw(), cs.ptr(), sarr.size()); - err = p_save_func(p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key, seed); + err = save_proxy.save_file(p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; @@ -1445,7 +1452,7 @@ Error EditorExportPlatform::export_project_files(const Ref & } Vector array = FileAccess::get_file_as_bytes(export_path); - err = p_save_func(p_udata, export_path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed); + err = save_proxy.save_file(p_udata, export_path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; } @@ -1509,7 +1516,7 @@ Error EditorExportPlatform::export_project_files(const Ref & new_file.write[j] = utf8[j]; } - err = p_save_func(p_udata, from + ".remap", new_file, idx, total, enc_in_filters, enc_ex_filters, key, seed); + err = save_proxy.save_file(p_udata, from + ".remap", new_file, idx, total, enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; } @@ -1531,7 +1538,7 @@ Error EditorExportPlatform::export_project_files(const Ref & } else { array = FileAccess::get_file_as_bytes(forced_export[i]); } - err = p_save_func(p_udata, forced_export[i], array, idx, total, enc_in_filters, enc_ex_filters, key, seed); + err = save_proxy.save_file(p_udata, forced_export[i], array, idx, total, enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; } @@ -1540,7 +1547,7 @@ Error EditorExportPlatform::export_project_files(const Ref & Dictionary int_export = get_internal_export_files(p_preset, p_debug); for (const Variant &int_name : int_export.keys()) { const PackedByteArray &array = int_export[int_name]; - err = p_save_func(p_udata, int_name, array, idx, total, enc_in_filters, enc_ex_filters, key, seed); + err = save_proxy.save_file(p_udata, int_name, array, idx, total, enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; } @@ -1552,22 +1559,16 @@ Error EditorExportPlatform::export_project_files(const Ref & Vector data = FileAccess::get_file_as_bytes(engine_cfb); DirAccess::remove_file_or_error(engine_cfb); - err = p_save_func(p_udata, "res://" + config_file, data, idx, total, enc_in_filters, enc_ex_filters, key, seed); + err = save_proxy.save_file(p_udata, "res://" + config_file, data, idx, total, enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; } if (p_remove_func) { - for (const String &E : PackedData::get_singleton()->get_file_paths()) { - String simplified_path = E.simplify_path(); - if (simplified_path == config_file) { - continue; - } - - String pack_path = simplified_path.trim_suffix(".remap"); - - if (!_has_pack_path(paths, pack_path) && !_has_pack_path(extra_paths, pack_path) && !_has_pack_path(path_remaps, pack_path) && !_has_pack_path(forced_export, pack_path)) { - err = p_remove_func(p_udata, E); + HashSet currently_loaded_paths = PackedData::get_singleton()->get_file_paths(); + for (const String &path : currently_loaded_paths) { + if (!save_proxy.has_saved(path)) { + err = p_remove_func(p_udata, path); if (err != OK) { return err; } @@ -2209,7 +2210,7 @@ Vector EditorExportPlatform::gen_export_flags(BitField breakpoints; ScriptEditor::get_singleton()->get_breakpoints(&breakpoints); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index f626891bd8ea..b1156f61a3d9 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -303,6 +303,7 @@ void FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory file_item->set_icon(0, _get_tree_item_icon(!file_info.import_broken, file_info.type, file_info.icon_path)); if (da->is_link(file_metadata)) { file_item->set_icon_overlay(0, get_editor_theme_icon(SNAME("LinkOverlay"))); + // TRANSLATORS: This is a tooltip for a file that is a symbolic link to another file. file_item->set_tooltip_text(0, vformat(TTR("Link to: %s"), da->read_link(file_metadata))); } file_item->set_icon_max_width(0, icon_size); @@ -1486,6 +1487,11 @@ void FileSystemDock::_try_move_item(const FileOrFolder &p_item, const String &p_ break; } } + } else { + Ref res = ResourceCache::get_ref(old_path); + if (res.is_valid()) { + res->set_path_cache(new_path); + } } } diff --git a/editor/gui/editor_bottom_panel.cpp b/editor/gui/editor_bottom_panel.cpp index 30b618cdb4f6..a1bee6d54c37 100644 --- a/editor/gui/editor_bottom_panel.cpp +++ b/editor/gui/editor_bottom_panel.cpp @@ -39,6 +39,7 @@ #include "editor/themes/editor_scale.h" #include "scene/gui/box_container.h" #include "scene/gui/button.h" +#include "scene/gui/scroll_container.h" #include "scene/gui/split_container.h" void EditorBottomPanel::_notification(int p_what) { @@ -46,6 +47,19 @@ void EditorBottomPanel::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: { pin_button->set_button_icon(get_editor_theme_icon(SNAME("Pin"))); expand_button->set_button_icon(get_editor_theme_icon(SNAME("ExpandBottomDock"))); + left_button->set_button_icon(get_editor_theme_icon(SNAME("Back"))); + right_button->set_button_icon(get_editor_theme_icon(SNAME("Forward"))); + } break; + + case NOTIFICATION_TRANSLATION_CHANGED: + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { + if (is_layout_rtl()) { + bottom_hbox->move_child(left_button, button_scroll->get_index() + 1); + bottom_hbox->move_child(right_button, 0); + } else { + bottom_hbox->move_child(right_button, button_scroll->get_index() + 1); + bottom_hbox->move_child(left_button, 0); + } } break; } } @@ -59,6 +73,33 @@ void EditorBottomPanel::_switch_by_control(bool p_visible, Control *p_control, b } } +void EditorBottomPanel::_scroll(bool p_right) { + HScrollBar *h_scroll = button_scroll->get_h_scroll_bar(); + if (Input::get_singleton()->is_key_pressed(Key::CTRL)) { + h_scroll->set_value(p_right ? h_scroll->get_max() : 0); + } else if (Input::get_singleton()->is_key_pressed(Key::SHIFT)) { + h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() * (p_right ? 1 : -1)); + } else { + h_scroll->set_value(h_scroll->get_value() + (h_scroll->get_page() * 0.5) * (p_right ? 1 : -1)); + } +} + +void EditorBottomPanel::_update_scroll_buttons() { + bool show_arrows = button_hbox->get_size().width > button_scroll->get_size().width; + left_button->set_visible(show_arrows); + right_button->set_visible(show_arrows); + + if (show_arrows) { + _update_disabled_buttons(); + } +} + +void EditorBottomPanel::_update_disabled_buttons() { + HScrollBar *h_scroll = button_scroll->get_h_scroll_bar(); + left_button->set_disabled(h_scroll->get_value() == 0); + right_button->set_disabled(h_scroll->get_value() + h_scroll->get_page() == h_scroll->get_max()); +} + void EditorBottomPanel::_switch_to_item(bool p_visible, int p_idx, bool p_ignore_lock) { ERR_FAIL_INDEX(p_idx, items.size()); @@ -93,6 +134,7 @@ void EditorBottomPanel::_switch_to_item(bool p_visible, int p_idx, bool p_ignore if (expand_button->is_pressed()) { EditorNode::get_top_split()->hide(); } + callable_mp(button_scroll, &ScrollContainer::ensure_control_visible).call_deferred(items[p_idx].button); } else { add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles))); items[p_idx].button->set_pressed_no_signal(false); @@ -259,9 +301,35 @@ EditorBottomPanel::EditorBottomPanel() { bottom_hbox->set_custom_minimum_size(Size2(0, 24 * EDSCALE)); // Adjust for the height of the "Expand Bottom Dock" icon. item_vbox->add_child(bottom_hbox); + left_button = memnew(Button); + left_button->set_tooltip_text(TTR("Scroll Left\nHold Ctrl to scroll to the begin.\nHold Shift to scroll one page.")); + left_button->set_theme_type_variation("BottomPanelButton"); + left_button->set_focus_mode(Control::FOCUS_NONE); + left_button->connect(SceneStringName(pressed), callable_mp(this, &EditorBottomPanel::_scroll).bind(false)); + bottom_hbox->add_child(left_button); + left_button->hide(); + + button_scroll = memnew(ScrollContainer); + button_scroll->set_h_size_flags(Control::SIZE_EXPAND_FILL); + button_scroll->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_SHOW_NEVER); + button_scroll->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED); + button_scroll->get_h_scroll_bar()->connect(CoreStringName(changed), callable_mp(this, &EditorBottomPanel::_update_scroll_buttons), CONNECT_DEFERRED); + button_scroll->get_h_scroll_bar()->connect(SceneStringName(value_changed), callable_mp(this, &EditorBottomPanel::_update_disabled_buttons).unbind(1), CONNECT_DEFERRED); + bottom_hbox->add_child(button_scroll); + + right_button = memnew(Button); + right_button->set_tooltip_text(TTR("Scroll Right\nHold Ctrl to scroll to the end.\nHold Shift to scroll one page.")); + right_button->set_theme_type_variation("BottomPanelButton"); + right_button->set_focus_mode(Control::FOCUS_NONE); + right_button->connect(SceneStringName(pressed), callable_mp(this, &EditorBottomPanel::_scroll).bind(true)); + bottom_hbox->add_child(right_button); + right_button->hide(); + + callable_mp(this, &EditorBottomPanel::_update_scroll_buttons).call_deferred(); + button_hbox = memnew(HBoxContainer); - button_hbox->set_h_size_flags(Control::SIZE_EXPAND_FILL); - bottom_hbox->add_child(button_hbox); + button_hbox->set_h_size_flags(Control::SIZE_EXPAND | Control::SIZE_SHRINK_BEGIN); + button_scroll->add_child(button_hbox); editor_toaster = memnew(EditorToaster); bottom_hbox->add_child(editor_toaster); diff --git a/editor/gui/editor_bottom_panel.h b/editor/gui/editor_bottom_panel.h index e94aa941a627..c81d7da6fa8b 100644 --- a/editor/gui/editor_bottom_panel.h +++ b/editor/gui/editor_bottom_panel.h @@ -38,6 +38,7 @@ class ConfigFile; class EditorToaster; class HBoxContainer; class VBoxContainer; +class ScrollContainer; class EditorBottomPanel : public PanelContainer { GDCLASS(EditorBottomPanel, PanelContainer); @@ -53,6 +54,9 @@ class EditorBottomPanel : public PanelContainer { VBoxContainer *item_vbox = nullptr; HBoxContainer *bottom_hbox = nullptr; + Button *left_button = nullptr; + Button *right_button = nullptr; + ScrollContainer *button_scroll = nullptr; HBoxContainer *button_hbox = nullptr; EditorToaster *editor_toaster = nullptr; Button *pin_button = nullptr; @@ -63,6 +67,9 @@ class EditorBottomPanel : public PanelContainer { void _switch_to_item(bool p_visible, int p_idx, bool p_ignore_lock = false); void _pin_button_toggled(bool p_pressed); void _expand_button_toggled(bool p_pressed); + void _scroll(bool p_right); + void _update_scroll_buttons(); + void _update_disabled_buttons(); bool _button_drag_hover(const Vector2 &, const Variant &, Button *p_button, Control *p_control); diff --git a/editor/gui/editor_toaster.cpp b/editor/gui/editor_toaster.cpp index 60f02ccfd4ad..a05f8f5d391f 100644 --- a/editor/gui/editor_toaster.cpp +++ b/editor/gui/editor_toaster.cpp @@ -412,7 +412,7 @@ void EditorToaster::popup_str(const String &p_message, Severity p_severity, cons // Since "_popup_str" adds nodes to the tree, and since the "add_child" method is not // thread-safe, it's better to defer the call to the next cycle to be thread-safe. is_processing_error = true; - callable_mp(this, &EditorToaster::_popup_str).call_deferred(p_message, p_severity, p_tooltip); + MessageQueue::get_main_singleton()->push_callable(callable_mp(this, &EditorToaster::_popup_str), p_message, p_severity, p_tooltip); is_processing_error = false; } diff --git a/editor/icons/FixedSize.svg b/editor/icons/FixedSize.svg new file mode 100644 index 000000000000..1ca5752c23ca --- /dev/null +++ b/editor/icons/FixedSize.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/editor/icons/KeepAspect.svg b/editor/icons/KeepAspect.svg index 1cc831563fcb..f178a2aa879b 100644 --- a/editor/icons/KeepAspect.svg +++ b/editor/icons/KeepAspect.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/editor/icons/SCsub b/editor/icons/SCsub index a66ef56699c7..23ac6e695813 100644 --- a/editor/icons/SCsub +++ b/editor/icons/SCsub @@ -7,20 +7,18 @@ import os import editor_icons_builders -env["BUILDERS"]["MakeEditorIconsBuilder"] = Builder( - action=env.Run(editor_icons_builders.make_editor_icons_action), - suffix=".h", - src_suffix=".svg", -) - # Editor's own icons icon_sources = Glob("*.svg") # Module icons for path in env.module_icons_paths: if not os.path.isabs(path): - icon_sources += Glob("#" + path + "/*.svg") # Built-in. + icon_sources += Glob(f"#{path}/*.svg") # Built-in. else: - icon_sources += Glob(path + "/*.svg") # Custom. + icon_sources += Glob(f"{path}/*.svg") # Custom. -env.Alias("editor_icons", [env.MakeEditorIconsBuilder("#editor/themes/editor_icons.gen.h", icon_sources)]) +env.CommandNoCache( + "#editor/themes/editor_icons.gen.h", + icon_sources, + env.Run(editor_icons_builders.make_editor_icons_action), +) diff --git a/editor/icons/Stretch.svg b/editor/icons/Stretch.svg new file mode 100644 index 000000000000..f8cb75fe864a --- /dev/null +++ b/editor/icons/Stretch.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp index dc2fc72a48b5..036c0eca9b6b 100644 --- a/editor/inspector_dock.cpp +++ b/editor/inspector_dock.cpp @@ -750,7 +750,6 @@ InspectorDock::InspectorDock(EditorData &p_editor_data) { object_menu = memnew(MenuButton); object_menu->set_flat(false); object_menu->set_theme_type_variation("FlatMenuButton"); - object_menu->set_shortcut_context(this); property_tools_hb->add_child(object_menu); object_menu->set_tooltip_text(TTR("Manage object properties.")); object_menu->get_popup()->connect("about_to_popup", callable_mp(this, &InspectorDock::_prepare_menu)); diff --git a/editor/plugins/embedded_process.cpp b/editor/plugins/embedded_process.cpp index 75e532513464..10c7188b5c53 100644 --- a/editor/plugins/embedded_process.cpp +++ b/editor/plugins/embedded_process.cpp @@ -50,7 +50,7 @@ void EmbeddedProcess::_notification(int p_what) { Rect2i new_global_rect = get_global_rect(); if (last_global_rect != new_global_rect) { last_global_rect = new_global_rect; - _queue_update_embedded_process(); + queue_update_embedded_process(); } } break; @@ -60,7 +60,7 @@ void EmbeddedProcess::_notification(int p_what) { case NOTIFICATION_RESIZED: case NOTIFICATION_VISIBILITY_CHANGED: case NOTIFICATION_WM_POSITION_CHANGED: { - _queue_update_embedded_process(); + queue_update_embedded_process(); } break; case NOTIFICATION_THEME_CHANGED: { focus_style_box = get_theme_stylebox(SNAME("FocusViewport"), EditorStringName(EditorStyles)); @@ -77,7 +77,7 @@ void EmbeddedProcess::_notification(int p_what) { } } break; case NOTIFICATION_FOCUS_ENTER: { - _queue_update_embedded_process(); + queue_update_embedded_process(); } break; case NOTIFICATION_APPLICATION_FOCUS_IN: { application_has_focus = true; @@ -88,7 +88,7 @@ void EmbeddedProcess::_notification(int p_what) { // or if the current window is a different popup or secondary window. if (embedding_completed && current_process_id != focused_process_id && window && window->has_focus()) { grab_focus(); - _queue_update_embedded_process(); + queue_update_embedded_process(); } } } break; @@ -102,27 +102,33 @@ void EmbeddedProcess::_notification(int p_what) { void EmbeddedProcess::set_window_size(const Size2i p_window_size) { if (window_size != p_window_size) { window_size = p_window_size; - _queue_update_embedded_process(); + queue_update_embedded_process(); } } void EmbeddedProcess::set_keep_aspect(bool p_keep_aspect) { if (keep_aspect != p_keep_aspect) { keep_aspect = p_keep_aspect; - _queue_update_embedded_process(); + queue_update_embedded_process(); } } Rect2i EmbeddedProcess::_get_global_embedded_window_rect() { Rect2i control_rect = get_global_rect(); - control_rect = Rect2i(control_rect.position, control_rect.size.maxi(1)); - if (keep_aspect) { - Rect2i desired_rect = control_rect; - float ratio = MIN((float)control_rect.size.x / window_size.x, (float)control_rect.size.y / window_size.y); - desired_rect.size = Size2i(window_size.x * ratio, window_size.y * ratio).maxi(1); + control_rect = Rect2i(control_rect.position + margin_top_left, (control_rect.size - get_margins_size()).maxi(1)); + if (window_size != Size2i()) { + Rect2i desired_rect = Rect2i(); + if (!keep_aspect && control_rect.size.x >= window_size.x && control_rect.size.y >= window_size.y) { + // Fixed at the desired size. + desired_rect.size = window_size; + } else { + float ratio = MIN((float)control_rect.size.x / window_size.x, (float)control_rect.size.y / window_size.y); + desired_rect.size = Size2i(window_size.x * ratio, window_size.y * ratio).maxi(1); + } desired_rect.position = Size2i(control_rect.position.x + ((control_rect.size.x - desired_rect.size.x) / 2), control_rect.position.y + ((control_rect.size.y - desired_rect.size.y) / 2)); return desired_rect; } else { + // Stretch, use all the control area. return control_rect; } } @@ -133,8 +139,28 @@ Rect2i EmbeddedProcess::get_screen_embedded_window_rect() { rect.position += window->get_position(); } - // Removing margins to make space for the focus border style. - return Rect2i(rect.position.x + margin_top_left.x, rect.position.y + margin_top_left.y, MAX(rect.size.x - (margin_top_left.x + margin_bottom_right.x), 1), MAX(rect.size.y - (margin_top_left.y + margin_bottom_right.y), 1)); + return rect; +} + +int EmbeddedProcess::get_margin_size(Side p_side) const { + ERR_FAIL_INDEX_V((int)p_side, 4, 0); + + switch (p_side) { + case SIDE_LEFT: + return margin_top_left.x; + case SIDE_RIGHT: + return margin_bottom_right.x; + case SIDE_TOP: + return margin_top_left.y; + case SIDE_BOTTOM: + return margin_bottom_right.y; + } + + return 0; +} + +Size2 EmbeddedProcess::get_margins_size() { + return margin_top_left + margin_bottom_right; } bool EmbeddedProcess::is_embedding_in_progress() { @@ -145,6 +171,10 @@ bool EmbeddedProcess::is_embedding_completed() { return embedding_completed; } +int EmbeddedProcess::get_embedded_pid() const { + return current_process_id; +} + void EmbeddedProcess::embed_process(OS::ProcessID p_pid) { if (!window) { return; @@ -211,7 +241,7 @@ bool EmbeddedProcess::_is_embedded_process_updatable() { return window && current_process_id != 0 && embedding_completed; } -void EmbeddedProcess::_queue_update_embedded_process() { +void EmbeddedProcess::queue_update_embedded_process() { if (updated_embedded_process_queued || !_is_embedded_process_updatable()) { return; } @@ -283,7 +313,7 @@ void EmbeddedProcess::_check_mouse_over() { // When we already have the focus and the user moves the mouse over the embedded process, // we just need to refocus the process. if (focused) { - _queue_update_embedded_process(); + queue_update_embedded_process(); } else { grab_focus(); queue_redraw(); diff --git a/editor/plugins/embedded_process.h b/editor/plugins/embedded_process.h index d4265dc899c3..14ad44fdaedd 100644 --- a/editor/plugins/embedded_process.h +++ b/editor/plugins/embedded_process.h @@ -59,7 +59,6 @@ class EmbeddedProcess : public Control { Rect2i last_global_rect; void _try_embed_process(); - void _queue_update_embedded_process(); void _update_embedded_process(); void _timer_embedding_timeout(); void _draw(); @@ -78,10 +77,14 @@ class EmbeddedProcess : public Control { void set_window_size(const Size2i p_window_size); void set_keep_aspect(bool p_keep_aspect); + void queue_update_embedded_process(); Rect2i get_screen_embedded_window_rect(); + int get_margin_size(Side p_side) const; + Size2 get_margins_size(); bool is_embedding_in_progress(); bool is_embedding_completed(); + int get_embedded_pid() const; EmbeddedProcess(); ~EmbeddedProcess(); diff --git a/editor/plugins/game_view_plugin.cpp b/editor/plugins/game_view_plugin.cpp index 212957dd2d9f..6c4ecc8be9fc 100644 --- a/editor/plugins/game_view_plugin.cpp +++ b/editor/plugins/game_view_plugin.cpp @@ -32,13 +32,16 @@ #include "core/config/project_settings.h" #include "core/debugger/debugger_marshalls.h" +#include "core/string/translation_server.h" #include "editor/debugger/editor_debugger_node.h" +#include "editor/debugger/script_editor_debugger.h" #include "editor/editor_command_palette.h" #include "editor/editor_feature_profile.h" #include "editor/editor_interface.h" #include "editor/editor_main_screen.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" +#include "editor/editor_string_names.h" #include "editor/gui/editor_run_bar.h" #include "editor/plugins/embedded_process.h" #include "editor/themes/editor_scale.h" @@ -203,6 +206,12 @@ void GameView::_sessions_changed() { } _update_debugger_buttons(); + + if (embedded_process->is_embedding_completed()) { + if (!embedded_script_debugger || !embedded_script_debugger->is_session_active() || embedded_script_debugger->get_remote_pid() != embedded_process->get_embedded_pid()) { + _attach_script_debugger(); + } + } } void GameView::_instance_starting_static(int p_idx, List &r_arguments) { @@ -214,13 +223,49 @@ void GameView::_instance_starting(int p_idx, List &r_arguments) { if (!is_feature_enabled) { return; } - if (p_idx == 0 && embed_on_play && make_floating_on_play && !window_wrapper->get_window_enabled() && EditorNode::get_singleton()->is_multi_window_enabled()) { - window_wrapper->restore_window_from_saved_position(floating_window_rect, floating_window_screen, floating_window_screen_rect); + if (p_idx == 0 && embed_on_play && make_floating_on_play && !window_wrapper->get_window_enabled() && _get_embed_available() == EMBED_AVAILABLE) { + // Set the Floating Window default title. Always considered in DEBUG mode, same as in Window::set_title. + String appname = GLOBAL_GET("application/config/name"); + appname = vformat("%s (DEBUG)", TranslationServer::get_singleton()->translate(appname)); + window_wrapper->set_window_title(appname); + + _show_update_window_wrapper(); } _update_arguments_for_instance(p_idx, r_arguments); } +void GameView::_show_update_window_wrapper() { + EditorRun::WindowPlacement placement = EditorRun::get_window_placement(); + Point2 position = floating_window_rect.position; + Size2i size = floating_window_rect.size; + int screen = floating_window_screen; + + Size2 wrapped_margins_size = window_wrapper->get_margins_size(); + Point2 offset_embedded_process = embedded_process->get_global_position() - get_global_position(); + offset_embedded_process.x += embedded_process->get_margin_size(SIDE_LEFT); + offset_embedded_process.y += embedded_process->get_margin_size(SIDE_TOP); + + // Obtain the size around the embedded process control. Usually, the difference between the game view's get_size + // and the embedded control should work. However, when the control is hidden and has never been displayed, + // the size of the embedded control is not calculated. + Size2 old_min_size = embedded_process->get_custom_minimum_size(); + embedded_process->set_custom_minimum_size(Size2i()); + Size2 min_size = get_minimum_size(); + embedded_process->set_custom_minimum_size(old_min_size); + + Point2 size_diff_embedded_process = Point2(0, min_size.y) + embedded_process->get_margins_size(); + + if (placement.position != Point2i(INT_MAX, INT_MAX)) { + position = placement.position - offset_embedded_process; + screen = placement.screen; + } + if (placement.size != Size2i()) { + size = placement.size + size_diff_embedded_process + wrapped_margins_size; + } + window_wrapper->restore_window_from_saved_position(Rect2(position, size), screen, Rect2i()); +} + void GameView::_play_pressed() { if (!is_feature_enabled) { return; @@ -235,7 +280,7 @@ void GameView::_play_pressed() { screen_index_before_start = EditorNode::get_singleton()->get_editor_main_screen()->get_selected_index(); } - if (embed_on_play) { + if (embed_on_play && _get_embed_available() == EMBED_AVAILABLE) { // It's important to disable the low power mode when unfocused because otherwise // the button in the editor are not responsive and if the user moves the mouse quickly, // the mouse clicks are not registered. @@ -255,6 +300,9 @@ void GameView::_stop_pressed() { return; } + _detach_script_debugger(); + paused = false; + EditorNode::get_singleton()->set_unfocused_low_processor_usage_mode_enabled(true); embedded_process->reset(); _update_ui(); @@ -272,6 +320,7 @@ void GameView::_stop_pressed() { } void GameView::_embedding_completed() { + _attach_script_debugger(); _update_ui(); } @@ -290,9 +339,18 @@ void GameView::_embedded_process_focused() { } } -void GameView::_project_settings_changed() { +void GameView::_editor_or_project_settings_changed() { // Update the window size and aspect ratio. _update_embed_window_size(); + + if (window_wrapper->get_window_enabled()) { + _show_update_window_wrapper(); + if (embedded_process->is_embedding_completed()) { + embedded_process->queue_update_embedded_process(); + } + } + + _update_ui(); } void GameView::_update_debugger_buttons() { @@ -344,35 +402,103 @@ void GameView::_embed_options_menu_menu_id_pressed(int p_id) { switch (p_id) { case EMBED_RUN_GAME_EMBEDDED: { embed_on_play = !embed_on_play; - EditorSettings::get_singleton()->set_project_metadata("game_view", "embed_on_play", embed_on_play); + int game_mode = EDITOR_GET("run/window_placement/game_embed_mode"); + if (game_mode == 0) { // Save only if not overridden by editor. + EditorSettings::get_singleton()->set_project_metadata("game_view", "embed_on_play", embed_on_play); + } } break; case EMBED_MAKE_FLOATING_ON_PLAY: { make_floating_on_play = !make_floating_on_play; - EditorSettings::get_singleton()->set_project_metadata("game_view", "make_floating_on_play", make_floating_on_play); + int game_mode = EDITOR_GET("run/window_placement/game_embed_mode"); + if (game_mode == 0) { // Save only if not overridden by editor. + EditorSettings::get_singleton()->set_project_metadata("game_view", "make_floating_on_play", make_floating_on_play); + } } break; } _update_embed_menu_options(); + _update_ui(); } -void GameView::_keep_aspect_button_pressed() { - embedded_process->set_keep_aspect(keep_aspect_button->is_pressed()); +void GameView::_size_mode_button_pressed(int size_mode) { + embed_size_mode = (EmbedSizeMode)size_mode; + EditorSettings::get_singleton()->set_project_metadata("game_view", "embed_size_mode", size_mode); + + _update_embed_menu_options(); + _update_embed_window_size(); +} + +GameView::EmbedAvailability GameView::_get_embed_available() { + if (!DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_WINDOW_EMBEDDING)) { + return EMBED_NOT_AVAILABLE_FEATURE_NOT_SUPPORTED; + } + if (!EditorNode::get_singleton()->is_multi_window_enabled()) { + return EMBED_NOT_AVAILABLE_SINGLE_WINDOW_MODE; + } + + EditorRun::WindowPlacement placement = EditorRun::get_window_placement(); + if (placement.force_fullscreen) { + return EMBED_NOT_AVAILABLE_FULLSCREEN; + } + if (placement.force_maximized) { + return EMBED_NOT_AVAILABLE_MAXIMIZED; + } + + DisplayServer::WindowMode window_mode = (DisplayServer::WindowMode)(GLOBAL_GET("display/window/size/mode").operator int()); + if (window_mode == DisplayServer::WindowMode::WINDOW_MODE_MINIMIZED) { + return EMBED_NOT_AVAILABLE_MINIMIZED; + } + if (window_mode == DisplayServer::WindowMode::WINDOW_MODE_MAXIMIZED) { + return EMBED_NOT_AVAILABLE_MAXIMIZED; + } + if (window_mode == DisplayServer::WindowMode::WINDOW_MODE_FULLSCREEN || window_mode == DisplayServer::WindowMode::WINDOW_MODE_EXCLUSIVE_FULLSCREEN) { + return EMBED_NOT_AVAILABLE_FULLSCREEN; + } + + return EMBED_AVAILABLE; } void GameView::_update_ui() { bool show_game_size = false; - if (!DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_WINDOW_EMBEDDING)) { - state_label->set_text(TTR("Game embedding not available on your OS.")); - } else if (embedded_process->is_embedding_completed()) { - state_label->set_text(""); - show_game_size = true; - } else if (embedded_process->is_embedding_in_progress()) { - state_label->set_text(TTR("Game starting...")); - } else if (EditorRunBar::get_singleton()->is_playing()) { - state_label->set_text(TTR("Game running not embedded.")); - } else if (embed_on_play) { - state_label->set_text(TTR("Press play to start the game.")); + EmbedAvailability available = _get_embed_available(); + + switch (available) { + case EMBED_AVAILABLE: + if (embedded_process->is_embedding_completed()) { + state_label->set_text(""); + show_game_size = true; + } else if (embedded_process->is_embedding_in_progress()) { + state_label->set_text(TTR("Game starting...")); + } else if (EditorRunBar::get_singleton()->is_playing()) { + state_label->set_text(TTR("Game running not embedded.")); + } else if (embed_on_play) { + state_label->set_text(TTR("Press play to start the game.")); + } else { + state_label->set_text(TTR("Embedding is disabled.")); + } + break; + case EMBED_NOT_AVAILABLE_FEATURE_NOT_SUPPORTED: + state_label->set_text(TTR("Game embedding not available on your OS.")); + break; + case EMBED_NOT_AVAILABLE_MINIMIZED: + state_label->set_text(TTR("Game embedding not available when the game starts minimized.\nConsider overriding the window mode project setting with the editor feature tag to Windowed to use game embedding while leaving the exported project intact.")); + break; + case EMBED_NOT_AVAILABLE_MAXIMIZED: + state_label->set_text(TTR("Game embedding not available when the game starts maximized.\nConsider overriding the window mode project setting with the editor feature tag to Windowed to use game embedding while leaving the exported project intact.")); + break; + case EMBED_NOT_AVAILABLE_FULLSCREEN: + state_label->set_text(TTR("Game embedding not available when the game starts in fullscreen.\nConsider overriding the window mode project setting with the editor feature tag to Windowed to use game embedding while leaving the exported project intact.")); + break; + case EMBED_NOT_AVAILABLE_SINGLE_WINDOW_MODE: + state_label->set_text(TTR("Game embedding not available in single window mode.")); + break; + } + + if (available == EMBED_AVAILABLE) { + if (state_label->has_theme_color_override(SceneStringName(font_color))) { + state_label->remove_theme_color_override(SceneStringName(font_color)); + } } else { - state_label->set_text(TTR("Embedding is disabled.")); + state_label->add_theme_color_override(SceneStringName(font_color), state_label->get_theme_color(SNAME("warning_color"), EditorStringName(Editor))); } game_size_label->set_visible(show_game_size); @@ -383,22 +509,34 @@ void GameView::_update_embed_menu_options() { menu->set_item_checked(menu->get_item_index(EMBED_RUN_GAME_EMBEDDED), embed_on_play); menu->set_item_checked(menu->get_item_index(EMBED_MAKE_FLOATING_ON_PLAY), make_floating_on_play); - // When embed is Off or in single window mode, Make floating is not available. - menu->set_item_disabled(menu->get_item_index(EMBED_MAKE_FLOATING_ON_PLAY), !embed_on_play || !EditorNode::get_singleton()->is_multi_window_enabled()); + menu->set_item_disabled(menu->get_item_index(EMBED_MAKE_FLOATING_ON_PLAY), !embed_on_play); + + fixed_size_button->set_pressed(embed_size_mode == SIZE_MODE_FIXED); + keep_aspect_button->set_pressed(embed_size_mode == SIZE_MODE_KEEP_ASPECT); + stretch_button->set_pressed(embed_size_mode == SIZE_MODE_STRETCH); } void GameView::_update_embed_window_size() { - Size2 window_size; - window_size.x = GLOBAL_GET("display/window/size/viewport_width"); - window_size.y = GLOBAL_GET("display/window/size/viewport_height"); + if (paused) { + // When paused, Godot does not re-render. As a result, resizing the game window to a larger size + // causes artifacts and flickering. However, resizing to a smaller size seems fine. + // To prevent artifacts and flickering, we will force the game window to maintain its size. + // Using the same technique as SIZE_MODE_FIXED, the embedded process control will + // prevent resizing the game to a larger size while maintaining the aspect ratio. + embedded_process->set_window_size(size_paused); + embedded_process->set_keep_aspect(false); - Size2 desired_size; - desired_size.x = GLOBAL_GET("display/window/size/window_width_override"); - desired_size.y = GLOBAL_GET("display/window/size/window_height_override"); - if (desired_size.x > 0 && desired_size.y > 0) { - window_size = desired_size; + } else { + if (embed_size_mode == SIZE_MODE_FIXED || embed_size_mode == SIZE_MODE_KEEP_ASPECT) { + // The embedded process control will need the desired window size. + EditorRun::WindowPlacement placement = EditorRun::get_window_placement(); + embedded_process->set_window_size(placement.size); + } else { + // Stretch... No need for the window size. + embedded_process->set_window_size(Size2i()); + } + embedded_process->set_keep_aspect(embed_size_mode == SIZE_MODE_KEEP_ASPECT); } - embedded_process->set_window_size(window_size); } void GameView::_hide_selection_toggled(bool p_pressed) { @@ -459,7 +597,9 @@ void GameView::_notification(int p_what) { select_mode_button[RuntimeNodeSelect::SELECT_MODE_LIST]->set_button_icon(get_editor_theme_icon(SNAME("ListSelect"))); hide_selection->set_button_icon(get_editor_theme_icon(hide_selection->is_pressed() ? SNAME("GuiVisibilityHidden") : SNAME("GuiVisibilityVisible"))); + fixed_size_button->set_button_icon(get_editor_theme_icon(SNAME("FixedSize"))); keep_aspect_button->set_button_icon(get_editor_theme_icon(SNAME("KeepAspect"))); + stretch_button->set_button_icon(get_editor_theme_icon(SNAME("Stretch"))); embed_options_menu->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); camera_override_button->set_button_icon(get_editor_theme_icon(SNAME("Camera"))); @@ -469,8 +609,26 @@ void GameView::_notification(int p_what) { case NOTIFICATION_READY: { if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_WINDOW_EMBEDDING)) { // Embedding available. - embed_on_play = EditorSettings::get_singleton()->get_project_metadata("game_view", "embed_on_play", true); - make_floating_on_play = EditorSettings::get_singleton()->get_project_metadata("game_view", "make_floating_on_play", true); + int game_mode = EDITOR_GET("run/window_placement/game_embed_mode"); + switch (game_mode) { + case 1: { // Embed. + embed_on_play = true; + make_floating_on_play = false; + } break; + case 2: { // Floating. + embed_on_play = true; + make_floating_on_play = true; + } break; + case 3: { // Disabled. + embed_on_play = false; + make_floating_on_play = false; + } break; + default: { + embed_on_play = EditorSettings::get_singleton()->get_project_metadata("game_view", "embed_on_play", true); + make_floating_on_play = EditorSettings::get_singleton()->get_project_metadata("game_view", "make_floating_on_play", true); + } break; + } + embed_size_mode = (EmbedSizeMode)(int)EditorSettings::get_singleton()->get_project_metadata("game_view", "embed_size_mode", SIZE_MODE_FIXED); keep_aspect_button->set_pressed(EditorSettings::get_singleton()->get_project_metadata("game_view", "keep_aspect", true)); _update_embed_menu_options(); @@ -479,15 +637,15 @@ void GameView::_notification(int p_what) { EditorRun::instance_starting_callback = _instance_starting_static; // Listen for project settings changes to update the window size and aspect ratio. - ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &GameView::_project_settings_changed)); - - embedded_process->set_keep_aspect(keep_aspect_button->is_pressed()); + ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &GameView::_editor_or_project_settings_changed)); + EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &GameView::_editor_or_project_settings_changed)); } else { // Embedding not available. embedding_separator->hide(); embed_options_menu->hide(); + fixed_size_button->hide(); keep_aspect_button->hide(); - keep_aspect_button->hide(); + stretch_button->hide(); } _update_ui(); @@ -542,7 +700,6 @@ Dictionary GameView::get_state() const { void GameView::set_window_layout(Ref p_layout) { floating_window_rect = p_layout->get_value("GameView", "floating_window_rect", Rect2i()); floating_window_screen = p_layout->get_value("GameView", "floating_window_screen", -1); - floating_window_screen_rect = p_layout->get_value("GameView", "floating_window_screen_rect", Rect2i()); } void GameView::get_window_layout(Ref p_layout) { @@ -552,24 +709,53 @@ void GameView::get_window_layout(Ref p_layout) { p_layout->set_value("GameView", "floating_window_rect", floating_window_rect); p_layout->set_value("GameView", "floating_window_screen", floating_window_screen); - p_layout->set_value("GameView", "floating_window_screen_rect", floating_window_screen_rect); } void GameView::_update_floating_window_settings() { if (window_wrapper->get_window_enabled()) { floating_window_rect = window_wrapper->get_window_rect(); floating_window_screen = window_wrapper->get_window_screen(); - floating_window_screen_rect = DisplayServer::get_singleton()->screen_get_usable_rect(floating_window_screen); } } +void GameView::_attach_script_debugger() { + if (embedded_script_debugger) { + _detach_script_debugger(); + } + + embedded_script_debugger = nullptr; + for (int i = 0; EditorDebuggerNode::get_singleton()->get_debugger(i); i++) { + ScriptEditorDebugger *script_debugger = EditorDebuggerNode::get_singleton()->get_debugger(i); + if (script_debugger->is_session_active() && script_debugger->get_remote_pid() == embedded_process->get_embedded_pid()) { + embedded_script_debugger = script_debugger; + break; + } + } + + if (embedded_script_debugger) { + embedded_script_debugger->connect("remote_window_title_changed", callable_mp(this, &GameView::_remote_window_title_changed)); + } +} + +void GameView::_detach_script_debugger() { + if (embedded_script_debugger) { + embedded_script_debugger->disconnect("remote_window_title_changed", callable_mp(this, &GameView::_remote_window_title_changed)); + embedded_script_debugger = nullptr; + } +} + +void GameView::_remote_window_title_changed(String title) { + window_wrapper->set_window_title(title); +} + void GameView::_update_arguments_for_instance(int p_idx, List &r_arguments) { - if (p_idx != 0 || !embed_on_play || !DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_WINDOW_EMBEDDING)) { + if (p_idx != 0 || !embed_on_play || _get_embed_available() != EMBED_AVAILABLE) { return; } // Remove duplicates/unwanted parameters. List::Element *E = r_arguments.front(); + List::Element *user_args_element = nullptr; while (E) { List::Element *N = E->next(); @@ -583,34 +769,58 @@ void GameView::_update_arguments_for_instance(int p_idx, List &r_argumen } } else if (E->get() == "-f" || E->get() == "--fullscreen" || E->get() == "-m" || E->get() == "--maximized" || E->get() == "-t" || E->get() == "-always-on-top") { r_arguments.erase(E); + } else if (E->get() == "--" || E->get() == "++") { + user_args_element = E; + break; } E = N; } // Add the editor window's native ID so the started game can directly set it as its parent. - r_arguments.push_back("--wid"); - r_arguments.push_back(itos(DisplayServer::get_singleton()->window_get_native_handle(DisplayServer::WINDOW_HANDLE, get_window()->get_window_id()))); + List::Element *N = r_arguments.insert_before(user_args_element, "--wid"); + N = r_arguments.insert_after(N, itos(DisplayServer::get_singleton()->window_get_native_handle(DisplayServer::WINDOW_HANDLE, get_window()->get_window_id()))); // Be sure to have the correct window size in the embedded_process control. _update_embed_window_size(); Rect2i rect = embedded_process->get_screen_embedded_window_rect(); - r_arguments.push_back("--position"); - r_arguments.push_back(itos(rect.position.x) + "," + itos(rect.position.y)); - r_arguments.push_back("--resolution"); - r_arguments.push_back(itos(rect.size.x) + "x" + itos(rect.size.y)); + N = r_arguments.insert_after(N, "--position"); + N = r_arguments.insert_after(N, itos(rect.position.x) + "," + itos(rect.position.y)); + N = r_arguments.insert_after(N, "--resolution"); + r_arguments.insert_after(N, itos(rect.size.x) + "x" + itos(rect.size.y)); } -void GameView::_window_before_closing() { +void GameView::_window_close_request() { // Before the parent window closed, we close the embedded game. That prevents // the embedded game to be seen without a parent window for a fraction of second. if (EditorRunBar::get_singleton()->is_playing() && (embedded_process->is_embedding_completed() || embedded_process->is_embedding_in_progress())) { + // Try to gracefully close the window. That way, the NOTIFICATION_WM_CLOSE_REQUEST + // notification should be propagated in the game process. embedded_process->reset(); - // Call deferred to prevent the _stop_pressed callback to be executed before the wrapper window - // actually closes. - callable_mp(EditorRunBar::get_singleton(), &EditorRunBar::stop_playing).call_deferred(); + + // When the embedding is not complete, we need to kill the process. + // If the game is paused, the close request will not be processed by the game, so it's better to kill the process. + if (paused || embedded_process->is_embedding_in_progress()) { + // Call deferred to prevent the _stop_pressed callback to be executed before the wrapper window + // actually closes. + callable_mp(EditorRunBar::get_singleton(), &EditorRunBar::stop_playing).call_deferred(); + } + } +} + +void GameView::_debugger_breaked(bool p_breaked, bool p_can_debug) { + if (p_breaked == paused) { + return; } + + paused = p_breaked; + + if (paused) { + size_paused = embedded_process->get_screen_embedded_window_rect().size; + } + + _update_embed_window_size(); } GameView::GameView(Ref p_debugger, WindowWrapper *p_wrapper) { @@ -728,12 +938,26 @@ GameView::GameView(Ref p_debugger, WindowWrapper *p_wrapper) { embedding_separator = memnew(VSeparator); main_menu_hbox->add_child(embedding_separator); + fixed_size_button = memnew(Button); + main_menu_hbox->add_child(fixed_size_button); + fixed_size_button->set_toggle_mode(true); + fixed_size_button->set_theme_type_variation("FlatButton"); + fixed_size_button->set_tooltip_text(TTR("Embedded game size is based on project settings.\nThe 'Keep Aspect' mode is used when the Game Workspace is smaller than the desired size.")); + fixed_size_button->connect(SceneStringName(pressed), callable_mp(this, &GameView::_size_mode_button_pressed).bind(SIZE_MODE_FIXED)); + keep_aspect_button = memnew(Button); main_menu_hbox->add_child(keep_aspect_button); keep_aspect_button->set_toggle_mode(true); keep_aspect_button->set_theme_type_variation("FlatButton"); keep_aspect_button->set_tooltip_text(TTR("Keep the aspect ratio of the embedded game.")); - keep_aspect_button->connect(SceneStringName(pressed), callable_mp(this, &GameView::_keep_aspect_button_pressed)); + keep_aspect_button->connect(SceneStringName(pressed), callable_mp(this, &GameView::_size_mode_button_pressed).bind(SIZE_MODE_KEEP_ASPECT)); + + stretch_button = memnew(Button); + main_menu_hbox->add_child(stretch_button); + stretch_button->set_toggle_mode(true); + stretch_button->set_theme_type_variation("FlatButton"); + stretch_button->set_tooltip_text(TTR("Embedded game size stretches to fit the Game Workspace.")); + stretch_button->connect(SceneStringName(pressed), callable_mp(this, &GameView::_size_mode_button_pressed).bind(SIZE_MODE_STRETCH)); embed_options_menu = memnew(MenuButton); main_menu_hbox->add_child(embed_options_menu); @@ -767,8 +991,14 @@ GameView::GameView(Ref p_debugger, WindowWrapper *p_wrapper) { embedded_process->connect("embedded_process_focused", callable_mp(this, &GameView::_embedded_process_focused)); embedded_process->set_custom_minimum_size(Size2i(100, 100)); + MarginContainer *state_container = memnew(MarginContainer); + state_container->add_theme_constant_override("margin_left", 8 * EDSCALE); + state_container->add_theme_constant_override("margin_right", 8 * EDSCALE); + state_container->set_anchors_and_offsets_preset(PRESET_FULL_RECT); + panel->add_child(state_container); + state_label = memnew(Label()); - panel->add_child(state_label); + state_container->add_child(state_label); state_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); state_label->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER); state_label->set_autowrap_mode(TextServer::AUTOWRAP_WORD); @@ -779,8 +1009,11 @@ GameView::GameView(Ref p_debugger, WindowWrapper *p_wrapper) { p_debugger->connect("session_started", callable_mp(this, &GameView::_sessions_changed)); p_debugger->connect("session_stopped", callable_mp(this, &GameView::_sessions_changed)); - p_wrapper->connect("window_before_closing", callable_mp(this, &GameView::_window_before_closing)); + p_wrapper->set_override_close_request(true); + p_wrapper->connect("window_close_requested", callable_mp(this, &GameView::_window_close_request)); p_wrapper->connect("window_size_changed", callable_mp(this, &GameView::_update_floating_window_settings)); + + EditorDebuggerNode::get_singleton()->connect("breaked", callable_mp(this, &GameView::_debugger_breaked)); } /////// @@ -850,15 +1083,18 @@ void GameViewPlugin::_window_visibility_changed(bool p_visible) { } void GameViewPlugin::_save_last_editor(const String &p_editor) { - if (p_editor != get_name()) { + if (p_editor != get_plugin_name()) { last_editor = p_editor; } } void GameViewPlugin::_focus_another_editor() { if (window_wrapper->get_window_enabled()) { - ERR_FAIL_COND(last_editor.is_empty()); - EditorInterface::get_singleton()->set_main_screen_editor(last_editor); + if (last_editor.is_empty()) { + EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_2D); + } else { + EditorInterface::get_singleton()->set_main_screen_editor(last_editor); + } } } diff --git a/editor/plugins/game_view_plugin.h b/editor/plugins/game_view_plugin.h index 50d4170e92d3..dd384280b5fb 100644 --- a/editor/plugins/game_view_plugin.h +++ b/editor/plugins/game_view_plugin.h @@ -40,6 +40,7 @@ class EmbeddedProcess; class VSeparator; class WindowWrapper; +class ScriptEditorDebugger; class GameViewDebugger : public EditorDebuggerPlugin { GDCLASS(GameViewDebugger, EditorDebuggerPlugin); @@ -93,6 +94,21 @@ class GameView : public VBoxContainer { EMBED_MAKE_FLOATING_ON_PLAY, }; + enum EmbedSizeMode { + SIZE_MODE_FIXED, + SIZE_MODE_KEEP_ASPECT, + SIZE_MODE_STRETCH, + }; + + enum EmbedAvailability { + EMBED_AVAILABLE, + EMBED_NOT_AVAILABLE_FEATURE_NOT_SUPPORTED, + EMBED_NOT_AVAILABLE_MINIMIZED, + EMBED_NOT_AVAILABLE_MAXIMIZED, + EMBED_NOT_AVAILABLE_FULLSCREEN, + EMBED_NOT_AVAILABLE_SINGLE_WINDOW_MODE, + }; + inline static GameView *singleton = nullptr; Ref debugger; @@ -101,13 +117,16 @@ class GameView : public VBoxContainer { bool is_feature_enabled = true; int active_sessions = 0; int screen_index_before_start = -1; + ScriptEditorDebugger *embedded_script_debugger = nullptr; bool embed_on_play = true; bool make_floating_on_play = true; + EmbedSizeMode embed_size_mode = SIZE_MODE_FIXED; + bool paused = false; + Size2 size_paused; Rect2i floating_window_rect; int floating_window_screen = -1; - Rect2i floating_window_screen_rect; Button *suspend_button = nullptr; Button *next_frame_button = nullptr; @@ -121,10 +140,11 @@ class GameView : public VBoxContainer { MenuButton *camera_override_menu = nullptr; VSeparator *embedding_separator = nullptr; + Button *fixed_size_button = nullptr; Button *keep_aspect_button = nullptr; + Button *stretch_button = nullptr; MenuButton *embed_options_menu = nullptr; Label *game_size_label = nullptr; - Panel *panel = nullptr; EmbeddedProcess *embedded_process = nullptr; Label *state_label = nullptr; @@ -138,7 +158,7 @@ class GameView : public VBoxContainer { void _node_type_pressed(int p_option); void _select_mode_pressed(int p_option); void _embed_options_menu_menu_id_pressed(int p_id); - void _keep_aspect_button_pressed(); + void _size_mode_button_pressed(int size_mode); void _play_pressed(); static void _instance_starting_static(int p_idx, List &r_arguments); @@ -148,20 +168,27 @@ class GameView : public VBoxContainer { void _embedding_failed(); void _embedded_process_updated(); void _embedded_process_focused(); - void _project_settings_changed(); + void _editor_or_project_settings_changed(); + EmbedAvailability _get_embed_available(); void _update_ui(); void _update_embed_menu_options(); void _update_embed_window_size(); void _update_arguments_for_instance(int p_idx, List &r_arguments); + void _show_update_window_wrapper(); void _hide_selection_toggled(bool p_pressed); void _camera_override_button_toggled(bool p_pressed); void _camera_override_menu_id_pressed(int p_id); - void _window_before_closing(); + void _window_close_request(); void _update_floating_window_settings(); + void _attach_script_debugger(); + void _detach_script_debugger(); + void _remote_window_title_changed(String title); + + void _debugger_breaked(bool p_breaked, bool p_can_debug); protected: void _notification(int p_what); diff --git a/editor/plugins/gizmos/collision_polygon_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/collision_polygon_3d_gizmo_plugin.cpp index 4f9b93c91213..2a5dd571c061 100644 --- a/editor/plugins/gizmos/collision_polygon_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/collision_polygon_3d_gizmo_plugin.cpp @@ -30,14 +30,44 @@ #include "collision_polygon_3d_gizmo_plugin.h" +#include "core/math/geometry_2d.h" #include "scene/3d/physics/collision_polygon_3d.h" CollisionPolygon3DGizmoPlugin::CollisionPolygon3DGizmoPlugin() { - const Color gizmo_color = SceneTree::get_singleton()->get_debug_collisions_color(); - create_material("shape_material", gizmo_color); - const float gizmo_value = gizmo_color.get_v(); - const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65); - create_material("shape_material_disabled", gizmo_color_disabled); + create_collision_material("shape_material", 2.0); + create_collision_material("shape_material_arraymesh", 0.0625); + + create_collision_material("shape_material_disabled", 0.0625); + create_collision_material("shape_material_arraymesh_disabled", 0.015625); +} + +void CollisionPolygon3DGizmoPlugin::create_collision_material(const String &p_name, float p_alpha) { + Vector> mats; + + const Color collision_color(1.0, 1.0, 1.0, p_alpha); + + for (int i = 0; i < 4; i++) { + bool instantiated = i < 2; + + Ref material; + material.instantiate(); + + Color color = collision_color; + color.a *= instantiated ? 0.25 : 1.0; + + material->set_albedo(color); + material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); + material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 1); + material->set_cull_mode(StandardMaterial3D::CULL_BACK); + material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true); + material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); + + mats.push_back(material); + } + + materials[p_name] = mats; } bool CollisionPolygon3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { @@ -57,6 +87,13 @@ void CollisionPolygon3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { p_gizmo->clear(); + const Ref material = + get_material(!polygon->is_disabled() ? "shape_material" : "shape_material_disabled", p_gizmo); + const Ref material_arraymesh = + get_material(!polygon->is_disabled() ? "shape_material_arraymesh" : "shape_material_arraymesh_disabled", p_gizmo); + + const Color collision_color = polygon->is_disabled() ? Color(1.0, 1.0, 1.0, 0.75) : polygon->get_debug_color(); + Vector points = polygon->get_polygon(); float depth = polygon->get_depth() * 0.5; @@ -71,9 +108,125 @@ void CollisionPolygon3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { lines.push_back(Vector3(points[i].x, points[i].y, -depth)); } - const Ref material = - get_material(!polygon->is_disabled() ? "shape_material" : "shape_material_disabled", p_gizmo); + if (polygon->get_debug_fill_enabled()) { + Ref array_mesh; + array_mesh.instantiate(); + + Vector verts; + Vector colors; + Vector indices; + + // Determine orientation of the 2D polygon's vertices to determine + // which direction to draw outer polygons. + float signed_area = 0.0f; + for (int i = 0; i < points.size(); i++) { + const int j = (i + 1) % points.size(); + signed_area += points[i].x * points[j].y - points[j].x * points[i].y; + } + + // Generate triangles for the sides of the extruded polygon. + for (int i = 0; i < points.size(); i++) { + verts.push_back(Vector3(points[i].x, points[i].y, depth)); + verts.push_back(Vector3(points[i].x, points[i].y, -depth)); + + colors.push_back(collision_color); + colors.push_back(collision_color); + } + + for (int i = 0; i < verts.size(); i += 2) { + const int j = (i + 1) % verts.size(); + const int k = (i + 2) % verts.size(); + const int l = (i + 3) % verts.size(); + + indices.push_back(i); + if (signed_area < 0) { + indices.push_back(j); + indices.push_back(k); + } else { + indices.push_back(k); + indices.push_back(j); + } + + indices.push_back(j); + if (signed_area < 0) { + indices.push_back(l); + indices.push_back(k); + } else { + indices.push_back(k); + indices.push_back(l); + } + } + + Vector> decomp = Geometry2D::decompose_polygon_in_convex(polygon->get_polygon()); + + // Generate triangles for the bottom cap of the extruded polygon. + for (int i = 0; i < decomp.size(); i++) { + Vector cap_verts_bottom; + Vector cap_colours_bottom; + Vector cap_indices_bottom; + + const int index_offset = verts.size(); + + const Vector &convex = decomp[i]; + + for (int j = 0; j < convex.size(); j++) { + cap_verts_bottom.push_back(Vector3(convex[j].x, convex[j].y, -depth)); + cap_colours_bottom.push_back(collision_color); + } + + if (convex.size() >= 3) { + for (int j = 1; j < convex.size(); j++) { + const int k = (j + 1) % convex.size(); + + cap_indices_bottom.push_back(index_offset + 0); + cap_indices_bottom.push_back(index_offset + j); + cap_indices_bottom.push_back(index_offset + k); + } + } + verts.append_array(cap_verts_bottom); + colors.append_array(cap_colours_bottom); + indices.append_array(cap_indices_bottom); + } + + // Generate triangles for the top cap of the extruded polygon. + for (int i = 0; i < decomp.size(); i++) { + Vector cap_verts_top; + Vector cap_colours_top; + Vector cap_indices_top; + + const int index_offset = verts.size(); + + const Vector &convex = decomp[i]; + + for (int j = 0; j < convex.size(); j++) { + cap_verts_top.push_back(Vector3(convex[j].x, convex[j].y, depth)); + cap_colours_top.push_back(collision_color); + } + + if (convex.size() >= 3) { + for (int j = 1; j < convex.size(); j++) { + const int k = (j + 1) % convex.size(); + + cap_indices_top.push_back(index_offset + k); + cap_indices_top.push_back(index_offset + j); + cap_indices_top.push_back(index_offset + 0); + } + } + verts.append_array(cap_verts_top); + colors.append_array(cap_colours_top); + indices.append_array(cap_indices_top); + } + + Array a; + a.resize(Mesh::ARRAY_MAX); + a[RS::ARRAY_VERTEX] = verts; + a[RS::ARRAY_COLOR] = colors; + a[RS::ARRAY_INDEX] = indices; + array_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a); + + p_gizmo->add_mesh(array_mesh, material_arraymesh); + } - p_gizmo->add_lines(lines, material); + p_gizmo->add_lines(lines, material, false, collision_color); p_gizmo->add_collision_segments(lines); } diff --git a/editor/plugins/gizmos/collision_polygon_3d_gizmo_plugin.h b/editor/plugins/gizmos/collision_polygon_3d_gizmo_plugin.h index b8342a25ca5e..6c78a862cfc6 100644 --- a/editor/plugins/gizmos/collision_polygon_3d_gizmo_plugin.h +++ b/editor/plugins/gizmos/collision_polygon_3d_gizmo_plugin.h @@ -36,6 +36,8 @@ class CollisionPolygon3DGizmoPlugin : public EditorNode3DGizmoPlugin { GDCLASS(CollisionPolygon3DGizmoPlugin, EditorNode3DGizmoPlugin); + void create_collision_material(const String &p_name, float p_alpha); + public: bool has_gizmo(Node3D *p_spatial) override; String get_gizmo_name() const override; diff --git a/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp index 6db68f413ebd..db9a9bef04b2 100644 --- a/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp @@ -350,7 +350,7 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { if (cs->get_debug_fill_enabled()) { Ref array_mesh = s->get_debug_arraymesh_faces(collision_color); - if (array_mesh.is_valid()) { + if (array_mesh.is_valid() && array_mesh->get_surface_count() > 0) { p_gizmo->add_mesh(array_mesh, material_arraymesh); } } diff --git a/editor/plugins/gizmos/particles_3d_emission_shape_gizmo_plugin.cpp b/editor/plugins/gizmos/particles_3d_emission_shape_gizmo_plugin.cpp index b258b93d4467..65861b89ca4a 100644 --- a/editor/plugins/gizmos/particles_3d_emission_shape_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/particles_3d_emission_shape_gizmo_plugin.cpp @@ -85,8 +85,8 @@ void Particles3DEmissionShapeGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { if (Object::cast_to(p_gizmo->get_node_3d())) { const GPUParticles3D *particles = Object::cast_to(p_gizmo->get_node_3d()); - if (particles->get_process_material().is_valid()) { - const Ref mat = particles->get_process_material(); + const Ref mat = particles->get_process_material(); + if (mat.is_valid()) { const ParticleProcessMaterial::EmissionShape shape = mat->get_emission_shape(); const Ref material = get_material("particles_emission_shape_material", p_gizmo); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 77dd61b26346..1a1e5697c2d2 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -1170,11 +1170,10 @@ void ScriptEditor::_live_auto_reload_running_scripts() { bool ScriptEditor::_test_script_times_on_disk(Ref p_for_script) { disk_changed_list->clear(); TreeItem *r = disk_changed_list->create_item(); - disk_changed_list->set_hide_root(true); bool need_ask = false; bool need_reload = false; - bool use_autoreload = bool(EDITOR_GET("text_editor/behavior/files/auto_reload_scripts_on_external_change")); + bool use_autoreload = EDITOR_GET("text_editor/behavior/files/auto_reload_scripts_on_external_change"); for (int i = 0; i < tab_container->get_tab_count(); i++) { ScriptEditorBase *se = Object::cast_to(tab_container->get_tab_control(i)); @@ -1188,12 +1187,12 @@ bool ScriptEditor::_test_script_times_on_disk(Ref p_for_script) { continue; //internal script, who cares } - uint64_t last_date = edited_res->get_last_modified_time(); - uint64_t date = FileAccess::get_modified_time(edited_res->get_path()); + uint64_t last_date = se->edited_file_data.last_modified_time; + uint64_t date = FileAccess::get_modified_time(se->edited_file_data.path); if (last_date != date) { TreeItem *ti = disk_changed_list->create_item(r); - ti->set_text(0, edited_res->get_path().get_file()); + ti->set_text(0, se->edited_file_data.path.get_file()); if (!use_autoreload || se->is_unsaved()) { need_ask = true; @@ -2231,11 +2230,6 @@ void ScriptEditor::_update_script_names() { Ref icon = se->get_theme_icon(); String path = se->get_edited_resource()->get_path(); bool saved = !path.is_empty(); - if (saved) { - // The script might be deleted, moved, or renamed, so make sure - // to update original path to previously edited resource. - se->set_meta("_edit_res_path", path); - } String name = se->get_name(); Ref