diff --git a/.github/workflows/android_builds.yml b/.github/workflows/android_builds.yml index 20c65e128c..bb4ee25f7c 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 index e4013f0493..a1b085604e 100644 --- a/.github/workflows/godot_cpp_test.yml +++ b/.github/workflows/godot_cpp_test.yml @@ -13,6 +13,7 @@ jobs: godot-cpp-tests: runs-on: ubuntu-24.04 name: Build and test Godot CPP + timeout-minutes: 30 steps: - name: Checkout uses: actions/checkout@v4 diff --git a/.github/workflows/ios_builds.yml b/.github/workflows/ios_builds.yml index b1b96e56eb..f5a4f755de 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 c3f2a7fe86..c0f29aeab3 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: diff --git a/.github/workflows/macos_builds.yml b/.github/workflows/macos_builds.yml index 8ad705cd70..0e3b8b35d5 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/static_checks.yml b/.github/workflows/static_checks.yml index 65344a32c4..ad7f7aa6ce 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 2f253fb82f..2f08d8c2f9 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 0562d5168d..351d00699a 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 96a074fac8..253bcd3ede 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -1,42 +1,40 @@ -# Exhaustive licensing information for files in the Redot Engine repository -# ========================================================================= -# -# This file aims at documenting the copyright and license for every source -# file in the Redot Engine repository, and especially outline the files -# whose license differs from the MIT/Expat license used by Redot 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 Redot 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 Redot 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 Redot Engine repository + ========================================================================= + . + This file aims at documenting the copyright and license for every source + file in the Redot Engine repository, and especially outline the files + whose license differs from the MIT/Expat license used by Redot 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 Redot 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 Redot binary, you may remove the corresponding + license details from this file. Upstream-Name: Redot Engine Upstream-Contact: Andevrs Source: https://github.com/Redot-Engine/redot-engine @@ -48,19 +46,19 @@ Copyright: 2024-present, Redot Engine contributors 2007-2014, Juan Linietsky, Ariel Manzur License: Expat -Files: ./logo.png - ./logo.svg - ./logo_outlined.png - ./logo_outlined.svg - ./editor/icons/TitleBarLogo.svg - ./icon.png - ./icon.svg +Files: logo.png + logo.svg + logo_outlined.png + logo_outlined.svg + editor/icons/TitleBarLogo.svg + icon.png + icon.svg Comment: Redot Engine Logos and Icons Copyright: 2024, Asrorul Irsyad 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 2024-present, Redot Engine contributors @@ -68,25 +66,25 @@ Copyright: 2011, Ole Kniemeyer, MAXON, www.maxon.net 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 2024-present, Redot Engine contributors @@ -94,13 +92,13 @@ Copyright: 2003-2008, Erwin Coumans 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 2024-present, Redot Engine contributors @@ -108,14 +106,14 @@ Copyright: 2007, Starbreeze Studios 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 2024-present, Redot Engine contributors @@ -123,23 +121,23 @@ Copyright: 2020, Manuel Prandini 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 2024-present, Redot Engine contributors @@ -147,149 +145,149 @@ Copyright: 2001, Robert Penner 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 @@ -310,38 +308,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. @@ -350,223 +348,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 @@ -574,7 +572,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 @@ -586,24 +584,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/core/core_builders.py b/core/core_builders.py index 0c161db364..cb33bedf60 100644 --- a/core/core_builders.py +++ b/core/core_builders.py @@ -249,8 +249,8 @@ def make_license_header(target, source, env): 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/math/quaternion.h b/core/math/quaternion.h index 3f6410fef2..e6c76227b9 100644 --- a/core/math/quaternion.h +++ b/core/math/quaternion.h @@ -143,16 +143,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/doc/classes/CollisionPolygon3D.xml b/doc/classes/CollisionPolygon3D.xml index 4f5866c348..5758e17996 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/PhysicsDirectSpaceState2D.xml b/doc/classes/PhysicsDirectSpaceState2D.xml index 94e9096334..059073a936 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 b00dc4236d..2f09b4f928 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/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 95fd752818..e3a69f1889 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -917,8 +917,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) { diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 5f236c1ef7..43522e67c5 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -3993,7 +3993,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. @@ -4096,9 +4096,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/shaders/particles.glsl b/drivers/gles3/shaders/particles.glsl index 2591937a1d..bdc28e292e 100644 --- a/drivers/gles3/shaders/particles.glsl +++ b/drivers/gles3/shaders/particles.glsl @@ -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 ce98a62c23..dd7df09c38 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; + // 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); - // 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; + // 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/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index 72d20a02b2..4cd3179999 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -828,6 +828,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"); @@ -1786,6 +1789,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_file_system.cpp b/editor/editor_file_system.cpp index f08750177b..990d496c21 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -2350,16 +2350,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)) { @@ -2369,25 +2370,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) { @@ -3014,7 +3026,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_node.cpp b/editor/editor_node.cpp index 315db7fcce..f61ea04c0a 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -1484,16 +1484,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); } @@ -2235,11 +2225,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; diff --git a/editor/editor_node.h b/editor/editor_node.h index 7c8349b9ee..1cddb1b3d3 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -748,7 +748,6 @@ public: 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 a0d501ea28..37616fd904 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -2601,6 +2601,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)) { @@ -2608,11 +2619,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: @@ -2656,9 +2662,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 61f7ab7cc5..8644fcb790 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -595,8 +595,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_run_native.cpp b/editor/editor_run_native.cpp index 9ebe57f51c..2dafea51ac 100644 --- a/editor/editor_run_native.cpp +++ b/editor/editor_run_native.cpp @@ -147,6 +147,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/gui/editor_bottom_panel.cpp b/editor/gui/editor_bottom_panel.cpp index 7573d1aa7c..e61feb71d8 100644 --- a/editor/gui/editor_bottom_panel.cpp +++ b/editor/gui/editor_bottom_panel.cpp @@ -41,6 +41,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) { @@ -48,6 +49,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; } } @@ -61,6 +75,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()); @@ -95,6 +136,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); @@ -261,9 +303,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 246837753f..2726e69df5 100644 --- a/editor/gui/editor_bottom_panel.h +++ b/editor/gui/editor_bottom_panel.h @@ -40,6 +40,7 @@ class ConfigFile; class EditorToaster; class HBoxContainer; class VBoxContainer; +class ScrollContainer; class EditorBottomPanel : public PanelContainer { GDCLASS(EditorBottomPanel, PanelContainer); @@ -55,6 +56,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; @@ -65,6 +69,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/plugins/embedded_process.cpp b/editor/plugins/embedded_process.cpp index 37cc82d0dd..95a7a2da7a 100644 --- a/editor/plugins/embedded_process.cpp +++ b/editor/plugins/embedded_process.cpp @@ -147,6 +147,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; diff --git a/editor/plugins/embedded_process.h b/editor/plugins/embedded_process.h index 8695b906ef..891e991409 100644 --- a/editor/plugins/embedded_process.h +++ b/editor/plugins/embedded_process.h @@ -84,6 +84,7 @@ public: Rect2i get_screen_embedded_window_rect(); 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 918a32a97d..03d2c0fa9d 100644 --- a/editor/plugins/game_view_plugin.cpp +++ b/editor/plugins/game_view_plugin.cpp @@ -34,7 +34,9 @@ #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" @@ -205,6 +207,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) { @@ -217,6 +225,11 @@ void GameView::_instance_starting(int p_idx, List &r_arguments) { return; } if (p_idx == 0 && embed_on_play && make_floating_on_play && !window_wrapper->get_window_enabled() && EditorNode::get_singleton()->is_multi_window_enabled()) { + // 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); + window_wrapper->restore_window_from_saved_position(floating_window_rect, floating_window_screen, floating_window_screen_rect); } @@ -257,6 +270,8 @@ void GameView::_stop_pressed() { return; } + _detach_script_debugger(); + EditorNode::get_singleton()->set_unfocused_low_processor_usage_mode_enabled(true); embedded_process->reset(); _update_ui(); @@ -274,6 +289,7 @@ void GameView::_stop_pressed() { } void GameView::_embedding_completed() { + _attach_script_debugger(); _update_ui(); } @@ -565,6 +581,36 @@ void GameView::_update_floating_window_settings() { } } +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)) { return; @@ -572,6 +618,7 @@ void GameView::_update_arguments_for_instance(int p_idx, List &r_argumen // Remove duplicates/unwanted parameters. List::Element *E = r_arguments.front(); + List::Element *user_args_element = nullptr; while (E) { List::Element *N = E->next(); @@ -585,23 +632,26 @@ 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() { diff --git a/editor/plugins/game_view_plugin.h b/editor/plugins/game_view_plugin.h index 350068c549..799664e56f 100644 --- a/editor/plugins/game_view_plugin.h +++ b/editor/plugins/game_view_plugin.h @@ -42,6 +42,7 @@ class EmbeddedProcess; class VSeparator; class WindowWrapper; +class ScriptEditorDebugger; class GameViewDebugger : public EditorDebuggerPlugin { GDCLASS(GameViewDebugger, EditorDebuggerPlugin); @@ -103,6 +104,7 @@ 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; @@ -164,6 +166,9 @@ class GameView : public VBoxContainer { void _window_before_closing(); void _update_floating_window_settings(); + void _attach_script_debugger(); + void _detach_script_debugger(); + void _remote_window_title_changed(String title); 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 29df816397..ef3d6f304f 100644 --- a/editor/plugins/gizmos/collision_polygon_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/collision_polygon_3d_gizmo_plugin.cpp @@ -32,14 +32,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) { @@ -59,6 +89,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; @@ -73,9 +110,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(); - p_gizmo->add_lines(lines, material); + 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, 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 8d32d1b231..e76f36cee1 100644 --- a/editor/plugins/gizmos/collision_polygon_3d_gizmo_plugin.h +++ b/editor/plugins/gizmos/collision_polygon_3d_gizmo_plugin.h @@ -38,6 +38,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 415368edce..e815ff28a9 100644 --- a/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp @@ -352,7 +352,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/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 2a1588bd38..61ce7743a8 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -2339,7 +2339,7 @@ void ScriptTextEditor::_make_context_menu(bool p_selection, bool p_color, bool p if (p_color || p_open_docs || p_goto_definition) { context_menu->add_separator(); if (p_open_docs) { - context_menu->add_item(TTR("Lookup Symbol"), LOOKUP_SYMBOL); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_symbol"), LOOKUP_SYMBOL); } if (p_color) { context_menu->add_item(TTR("Pick Color"), EDIT_PICK_COLOR); @@ -2493,6 +2493,7 @@ void ScriptTextEditor::_enable_code_editor() { edit_hb->add_child(goto_menu); goto_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_function"), SEARCH_LOCATE_FUNCTION); goto_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_line"), SEARCH_GOTO_LINE); + goto_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_symbol"), LOOKUP_SYMBOL); goto_menu->get_popup()->add_separator(); goto_menu->get_popup()->add_submenu_node_item(TTR("Bookmarks"), bookmarks_menu); @@ -2677,6 +2678,7 @@ void ScriptTextEditor::register_editor() { ED_SHORTCUT_OVERRIDE("script_text_editor/goto_function", "macos", KeyModifierMask::CTRL | KeyModifierMask::META | Key::J); ED_SHORTCUT("script_text_editor/goto_line", TTRC("Go to Line..."), KeyModifierMask::CMD_OR_CTRL | Key::L); + ED_SHORTCUT("script_text_editor/goto_symbol", TTRC("Lookup Symbol")); ED_SHORTCUT("script_text_editor/toggle_breakpoint", TTRC("Toggle Breakpoint"), Key::F9); ED_SHORTCUT_OVERRIDE("script_text_editor/toggle_breakpoint", "macos", KeyModifierMask::META | KeyModifierMask::SHIFT | Key::B); diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 39cf55e806..60e57a73df 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -77,18 +77,18 @@ struct FloatConstantDef { String name; float value = 0; - String desc; + const char *desc_key; }; static FloatConstantDef float_constant_defs[] = { - { "E", Math_E, TTR("E constant (2.718282). Represents the base of the natural logarithm.") }, - { "Epsilon", CMP_EPSILON, TTR("Epsilon constant (0.00001). Smallest possible scalar number.") }, - { "Phi", 1.618034f, TTR("Phi constant (1.618034). Golden ratio.") }, - { "Pi/4", Math_PI / 4, TTR("Pi/4 constant (0.785398) or 45 degrees.") }, - { "Pi/2", Math_PI / 2, TTR("Pi/2 constant (1.570796) or 90 degrees.") }, - { "Pi", Math_PI, TTR("Pi constant (3.141593) or 180 degrees.") }, - { "Tau", Math_TAU, TTR("Tau constant (6.283185) or 360 degrees.") }, - { "Sqrt2", Math_SQRT2, TTR("Sqrt2 constant (1.414214). Square root of 2.") } + { "E", Math_E, TTRC("E constant (2.718282). Represents the base of the natural logarithm.") }, + { "Epsilon", CMP_EPSILON, TTRC("Epsilon constant (0.00001). Smallest possible scalar number.") }, + { "Phi", 1.618034f, TTRC("Phi constant (1.618034). Golden ratio.") }, + { "Pi/4", Math_PI / 4, TTRC("Pi/4 constant (0.785398) or 45 degrees.") }, + { "Pi/2", Math_PI / 2, TTRC("Pi/2 constant (1.570796) or 90 degrees.") }, + { "Pi", Math_PI, TTRC("Pi constant (3.141593) or 180 degrees.") }, + { "Tau", Math_TAU, TTRC("Tau constant (6.283185) or 360 degrees.") }, + { "Sqrt2", Math_SQRT2, TTRC("Sqrt2 constant (1.414214). Square root of 2.") } }; const int MAX_FLOAT_CONST_DEFS = sizeof(float_constant_defs) / sizeof(FloatConstantDef); @@ -7200,7 +7200,7 @@ VisualShaderEditor::VisualShaderEditor() { // CONSTANTS for (int i = 0; i < MAX_FLOAT_CONST_DEFS; i++) { - add_options.push_back(AddOption(float_constant_defs[i].name, "Scalar/Constants", "VisualShaderNodeFloatConstant", float_constant_defs[i].desc, { float_constant_defs[i].value }, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption(float_constant_defs[i].name, "Scalar/Constants", "VisualShaderNodeFloatConstant", TTRGET(float_constant_defs[i].desc_key), { float_constant_defs[i].value }, VisualShaderNode::PORT_TYPE_SCALAR)); } // FUNCTIONS diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp index 5496101210..0774143b70 100644 --- a/editor/script_create_dialog.cpp +++ b/editor/script_create_dialog.cpp @@ -376,7 +376,6 @@ void ScriptCreateDialog::_create_new() { alert->popup_centered(); return; } - EditorNode::get_singleton()->ensure_uid_file(lpath); } emit_signal(SNAME("script_created"), scr); diff --git a/editor/shader_create_dialog.cpp b/editor/shader_create_dialog.cpp index cb5e27f7f6..bac1e203c2 100644 --- a/editor/shader_create_dialog.cpp +++ b/editor/shader_create_dialog.cpp @@ -243,7 +243,6 @@ void fog() { alert->popup_centered(); return; } - EditorNode::get_singleton()->ensure_uid_file(lpath); emit_signal(SNAME("shader_include_created"), shader_inc); } else { @@ -262,7 +261,6 @@ void fog() { alert->popup_centered(); return; } - EditorNode::get_singleton()->ensure_uid_file(lpath); } emit_signal(SNAME("shader_created"), shader); diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 101cefa1e0..7c62fb2670 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -2236,24 +2236,35 @@ CSGBrush *CSGPolygon3D::_build_brush() { base_xform = path->get_global_transform(); } - Vector3 current_point = curve->sample_baked(0); - Vector3 next_point = curve->sample_baked(extrusion_step); + Vector3 current_point; Vector3 current_up = Vector3(0, 1, 0); - Vector3 direction = next_point - current_point; - - if (path_joined) { - Vector3 last_point = curve->sample_baked(curve->get_baked_length()); - direction = next_point - last_point; - } + Vector3 direction; switch (path_rotation) { case PATH_ROTATION_POLYGON: + current_point = curve->sample_baked(0); direction = Vector3(0, 0, -1); break; case PATH_ROTATION_PATH: - break; case PATH_ROTATION_PATH_FOLLOW: - current_up = curve->sample_baked_up_vector(0, true); + if (!path_rotation_accurate) { + current_point = curve->sample_baked(0); + Vector3 next_point = curve->sample_baked(extrusion_step); + direction = next_point - current_point; + + if (path_joined) { + Vector3 last_point = curve->sample_baked(curve->get_baked_length()); + direction = next_point - last_point; + } + } else { + Transform3D current_sample_xform = curve->sample_baked_with_rotation(0); + current_point = current_sample_xform.get_origin(); + direction = current_sample_xform.get_basis().xform(Vector3(0, 0, -1)); + } + + if (path_rotation == PATH_ROTATION_PATH_FOLLOW) { + current_up = curve->sample_baked_up_vector(0, true); + } break; } @@ -2309,32 +2320,26 @@ CSGBrush *CSGPolygon3D::_build_brush() { case MODE_PATH: { double previous_offset = x0 * extrusion_step; double current_offset = (x0 + 1) * extrusion_step; - double next_offset = (x0 + 2) * extrusion_step; - if (x0 == extrusions - 1) { - if (path_joined) { - current_offset = 0; - next_offset = extrusion_step; - } else { - next_offset = current_offset; - } + if (path_joined && x0 == extrusions - 1) { + current_offset = 0; } Vector3 previous_point = curve->sample_baked(previous_offset); - Vector3 current_point = curve->sample_baked(current_offset); - Vector3 next_point = curve->sample_baked(next_offset); + Transform3D current_sample_xform = curve->sample_baked_with_rotation(current_offset); + Vector3 current_point = current_sample_xform.get_origin(); Vector3 current_up = Vector3(0, 1, 0); - Vector3 direction = next_point - previous_point; - Vector3 current_dir = (current_point - previous_point).normalized(); + Vector3 current_extrusion_dir = (current_point - previous_point).normalized(); + Vector3 direction; // If the angles are similar, remove the previous face and replace it with this one. - if (path_simplify_angle > 0.0 && x0 > 0 && previous_simplify_dir.dot(current_dir) > angle_simplify_dot) { + if (path_simplify_angle > 0.0 && x0 > 0 && previous_simplify_dir.dot(current_extrusion_dir) > angle_simplify_dot) { faces_combined += 1; previous_xform = previous_previous_xform; face -= extrusion_face_count; faces_removed += extrusion_face_count; } else { faces_combined = 0; - previous_simplify_dir = current_dir; + previous_simplify_dir = current_extrusion_dir; } switch (path_rotation) { @@ -2342,9 +2347,21 @@ CSGBrush *CSGPolygon3D::_build_brush() { direction = Vector3(0, 0, -1); break; case PATH_ROTATION_PATH: - break; case PATH_ROTATION_PATH_FOLLOW: - current_up = curve->sample_baked_up_vector(current_offset, true); + if (!path_rotation_accurate) { + double next_offset = (x0 + 2) * extrusion_step; + if (x0 == extrusions - 1) { + next_offset = path_joined ? extrusion_step : current_offset; + } + Vector3 next_point = curve->sample_baked(next_offset); + direction = next_point - previous_point; + } else { + direction = current_sample_xform.get_basis().xform(Vector3(0, 0, -1)); + } + + if (path_rotation == PATH_ROTATION_PATH_FOLLOW) { + current_up = curve->sample_baked_up_vector(current_offset, true); + } break; } @@ -2514,6 +2531,9 @@ void CSGPolygon3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_path_rotation", "path_rotation"), &CSGPolygon3D::set_path_rotation); ClassDB::bind_method(D_METHOD("get_path_rotation"), &CSGPolygon3D::get_path_rotation); + ClassDB::bind_method(D_METHOD("set_path_rotation_accurate", "enable"), &CSGPolygon3D::set_path_rotation_accurate); + ClassDB::bind_method(D_METHOD("get_path_rotation_accurate"), &CSGPolygon3D::get_path_rotation_accurate); + ClassDB::bind_method(D_METHOD("set_path_local", "enable"), &CSGPolygon3D::set_path_local); ClassDB::bind_method(D_METHOD("is_path_local"), &CSGPolygon3D::is_path_local); @@ -2545,6 +2565,7 @@ void CSGPolygon3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_interval", PROPERTY_HINT_RANGE, "0.01,1.0,0.01,exp,or_greater"), "set_path_interval", "get_path_interval"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_simplify_angle", PROPERTY_HINT_RANGE, "0.0,180.0,0.1"), "set_path_simplify_angle", "get_path_simplify_angle"); ADD_PROPERTY(PropertyInfo(Variant::INT, "path_rotation", PROPERTY_HINT_ENUM, "Polygon,Path,PathFollow"), "set_path_rotation", "get_path_rotation"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_rotation_accurate"), "set_path_rotation_accurate", "get_path_rotation_accurate"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_local"), "set_path_local", "is_path_local"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_continuous_u"), "set_path_continuous_u", "is_path_continuous_u"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_u_distance", PROPERTY_HINT_RANGE, "0.0,10.0,0.01,or_greater,suffix:m"), "set_path_u_distance", "get_path_u_distance"); @@ -2687,6 +2708,16 @@ CSGPolygon3D::PathRotation CSGPolygon3D::get_path_rotation() const { return path_rotation; } +void CSGPolygon3D::set_path_rotation_accurate(bool p_enabled) { + path_rotation_accurate = p_enabled; + _make_dirty(); + update_gizmos(); +} + +bool CSGPolygon3D::get_path_rotation_accurate() const { + return path_rotation_accurate; +} + void CSGPolygon3D::set_path_local(bool p_enable) { path_local = p_enable; _make_dirty(); @@ -2748,6 +2779,7 @@ CSGPolygon3D::CSGPolygon3D() { path_interval = 1.0; path_simplify_angle = 0.0; path_rotation = PATH_ROTATION_PATH_FOLLOW; + path_rotation_accurate = false; path_local = false; path_continuous_u = true; path_u_distance = 1.0; diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h index 753edba3fc..492a6b6feb 100644 --- a/modules/csg/csg_shape.h +++ b/modules/csg/csg_shape.h @@ -403,6 +403,7 @@ private: float path_interval; float path_simplify_angle; PathRotation path_rotation; + bool path_rotation_accurate; bool path_local; Path3D *path = nullptr; @@ -454,6 +455,9 @@ public: void set_path_rotation(PathRotation p_rotation); PathRotation get_path_rotation() const; + void set_path_rotation_accurate(bool p_enable); + bool get_path_rotation_accurate() const; + void set_path_local(bool p_enable); bool is_path_local() const; diff --git a/modules/csg/doc_classes/CSGPolygon3D.xml b/modules/csg/doc_classes/CSGPolygon3D.xml index 5d35c04e25..2f26f13aa1 100644 --- a/modules/csg/doc_classes/CSGPolygon3D.xml +++ b/modules/csg/doc_classes/CSGPolygon3D.xml @@ -41,6 +41,9 @@ When [member mode] is [constant MODE_PATH], the path rotation method used to rotate the [member polygon] as it is extruded. + + When [member mode] is [constant MODE_PATH], if [code]true[/code] the polygon will be rotated according to the proper tangent of the path at the sampled points. If [code]false[/code] an approximation is used, which decreases in accuracy as the number of subdivisions decreases. + When [member mode] is [constant MODE_PATH], extrusions that are less than this angle, will be merged together to reduce polygon count. diff --git a/modules/csg/tests/test_csg.h b/modules/csg/tests/test_csg.h new file mode 100644 index 0000000000..488152b9c7 --- /dev/null +++ b/modules/csg/tests/test_csg.h @@ -0,0 +1,116 @@ +/**************************************************************************/ +/* test_csg.h */ +/**************************************************************************/ +/* This file is part of: */ +/* REDOT ENGINE */ +/* https://redotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2024-present Redot Engine contributors */ +/* (see REDOT_AUTHORS.md) */ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef TEST_CSG_H +#define TEST_CSG_H + +#include "../csg.h" +#include "../csg_shape.h" + +#include "tests/test_macros.h" + +namespace TestCSG { + +TEST_CASE("[SceneTree][CSG] CSGPolygon3D") { + SUBCASE("[SceneTree][CSG] CSGPolygon3D: using accurate path tangent for polygon rotation") { + const float polygon_radius = 10.0f; + + const Vector3 expected_min_bounds = Vector3(-polygon_radius, -polygon_radius, 0); + const Vector3 expected_max_bounds = Vector3(100 + polygon_radius, polygon_radius, 100); + const AABB expected_aabb = AABB(expected_min_bounds, expected_max_bounds - expected_min_bounds); + + Ref curve; + curve.instantiate(); + curve->add_point( + // p_position + Vector3(0, 0, 0), + // p_in + Vector3(), + // p_out + Vector3(0, 0, 60)); + curve->add_point( + // p_position + Vector3(100, 0, 100), + // p_in + Vector3(0, 0, -60), + // p_out + Vector3()); + + Path3D *path = memnew(Path3D); + path->set_curve(curve); + + CSGPolygon3D *csg_polygon_3d = memnew(CSGPolygon3D); + SceneTree::get_singleton()->get_root()->add_child(csg_polygon_3d); + + csg_polygon_3d->add_child(path); + csg_polygon_3d->set_path_node(csg_polygon_3d->get_path_to(path)); + csg_polygon_3d->set_mode(CSGPolygon3D::Mode::MODE_PATH); + + PackedVector2Array polygon; + polygon.append(Vector2(-polygon_radius, 0)); + polygon.append(Vector2(0, polygon_radius)); + polygon.append(Vector2(polygon_radius, 0)); + polygon.append(Vector2(0, -polygon_radius)); + csg_polygon_3d->set_polygon(polygon); + + csg_polygon_3d->set_path_rotation(CSGPolygon3D::PathRotation::PATH_ROTATION_PATH); + csg_polygon_3d->set_path_rotation_accurate(true); + + // Minimize the number of extrusions. + // This decreases the number of samples taken from the curve. + // Having fewer samples increases the inaccuracy of the line between samples as an approximation of the tangent of the curve. + // With correct polygon orientation, the bounding box for the given curve should be independent of the number of extrusions. + csg_polygon_3d->set_path_interval_type(CSGPolygon3D::PathIntervalType::PATH_INTERVAL_DISTANCE); + csg_polygon_3d->set_path_interval(1000.0f); + + // Call get_brush_faces to force the bounding box to update. + csg_polygon_3d->get_brush_faces(); + + CHECK(csg_polygon_3d->get_aabb().is_equal_approx(expected_aabb)); + + // Perform the bounding box check again with a greater number of extrusions. + csg_polygon_3d->set_path_interval(1.0f); + csg_polygon_3d->get_brush_faces(); + + CHECK(csg_polygon_3d->get_aabb().is_equal_approx(expected_aabb)); + + csg_polygon_3d->remove_child(path); + SceneTree::get_singleton()->get_root()->remove_child(csg_polygon_3d); + + memdelete(csg_polygon_3d); + memdelete(path); + } +} + +} // namespace TestCSG + +#endif // TEST_CSG_H diff --git a/modules/jolt_physics/objects/jolt_body_3d.cpp b/modules/jolt_physics/objects/jolt_body_3d.cpp index 380e32baa4..8494cea96a 100644 --- a/modules/jolt_physics/objects/jolt_body_3d.cpp +++ b/modules/jolt_physics/objects/jolt_body_3d.cpp @@ -212,20 +212,6 @@ void JoltBody3D::_move_kinematic(float p_step, JPH::Body &p_jolt_body) { p_jolt_body.MoveKinematic(new_position, new_rotation, p_step); } -void JoltBody3D::_pre_step_rigid(float p_step, JPH::Body &p_jolt_body) { - _integrate_forces(p_step, p_jolt_body); - _enqueue_call_queries(); -} - -void JoltBody3D::_pre_step_kinematic(float p_step, JPH::Body &p_jolt_body) { - _update_gravity(p_jolt_body); - _move_kinematic(p_step, p_jolt_body); - - if (reports_contacts()) { - _enqueue_call_queries(); - } -} - JPH::EAllowedDOFs JoltBody3D::_calculate_allowed_dofs() const { if (is_static()) { return JPH::EAllowedDOFs::All; @@ -1239,13 +1225,18 @@ void JoltBody3D::pre_step(float p_step, JPH::Body &p_jolt_body) { } break; case PhysicsServer3D::BODY_MODE_RIGID: case PhysicsServer3D::BODY_MODE_RIGID_LINEAR: { - _pre_step_rigid(p_step, p_jolt_body); + _integrate_forces(p_step, p_jolt_body); } break; case PhysicsServer3D::BODY_MODE_KINEMATIC: { - _pre_step_kinematic(p_step, p_jolt_body); + _update_gravity(p_jolt_body); + _move_kinematic(p_step, p_jolt_body); } break; } + if (_should_call_queries()) { + _enqueue_call_queries(); + } + contact_count = 0; } diff --git a/modules/jolt_physics/objects/jolt_body_3d.h b/modules/jolt_physics/objects/jolt_body_3d.h index 9dcd24fd9d..42622b1514 100644 --- a/modules/jolt_physics/objects/jolt_body_3d.h +++ b/modules/jolt_physics/objects/jolt_body_3d.h @@ -112,6 +112,7 @@ private: virtual void _add_to_space() override; + bool _should_call_queries() const { return state_sync_callback.is_valid() || custom_integration_callback.is_valid(); } void _enqueue_call_queries(); void _dequeue_call_queries(); @@ -119,9 +120,6 @@ private: void _move_kinematic(float p_step, JPH::Body &p_jolt_body); - void _pre_step_rigid(float p_step, JPH::Body &p_jolt_body); - void _pre_step_kinematic(float p_step, JPH::Body &p_jolt_body); - JPH::EAllowedDOFs _calculate_allowed_dofs() const; JPH::MassProperties _calculate_mass_properties(const JPH::Shape &p_shape) const; diff --git a/modules/websocket/wsl_peer.cpp b/modules/websocket/wsl_peer.cpp index 543f275d6d..1229055bbe 100644 --- a/modules/websocket/wsl_peer.cpp +++ b/modules/websocket/wsl_peer.cpp @@ -862,10 +862,12 @@ void WSLPeer::close(int p_code, String p_reason) { } } - heartbeat_waiting = false; - in_buffer.clear(); - packet_buffer.resize(0); - pending_message.clear(); + if (ready_state == STATE_CLOSED) { + heartbeat_waiting = false; + in_buffer.clear(); + packet_buffer.resize(0); + pending_message.clear(); + } } IPAddress WSLPeer::get_connected_host() const { diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/DialogUtils.kt b/platform/android/java/lib/src/org/godotengine/godot/utils/DialogUtils.kt index 0161236455..da702fd557 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/utils/DialogUtils.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/utils/DialogUtils.kt @@ -151,6 +151,7 @@ internal class DialogUtils { builder.setView(parentLayout) val dialog = builder.create() dismissDialog = {dialog.dismiss()} + dialog.setCancelable(false) dialog.show() } } @@ -178,6 +179,7 @@ internal class DialogUtils { dialog.dismiss() } val dialog = builder.create() + dialog.setCancelable(false) dialog.show() } } diff --git a/platform/linuxbsd/wayland/display_server_wayland.cpp b/platform/linuxbsd/wayland/display_server_wayland.cpp index 7a3de359a1..219f539565 100644 --- a/platform/linuxbsd/wayland/display_server_wayland.cpp +++ b/platform/linuxbsd/wayland/display_server_wayland.cpp @@ -1176,6 +1176,7 @@ void DisplayServerWayland::process_events() { if (OS::get_singleton()->get_main_loop()) { OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT); } + Input::get_singleton()->release_pressed_events(); } } diff --git a/platform/linuxbsd/wayland/wayland_thread.cpp b/platform/linuxbsd/wayland/wayland_thread.cpp index b35ad57f08..12422ec3b1 100644 --- a/platform/linuxbsd/wayland/wayland_thread.cpp +++ b/platform/linuxbsd/wayland/wayland_thread.cpp @@ -195,12 +195,20 @@ Vector WaylandThread::_wp_primary_selection_offer_read(struct wl_displa // Sets up an `InputEventKey` and returns whether it has any meaningful value. bool WaylandThread::_seat_state_configure_key_event(SeatState &p_ss, Ref p_event, xkb_keycode_t p_keycode, bool p_pressed) { - // TODO: Handle keys that release multiple symbols? - Key keycode = KeyMappingXKB::get_keycode(xkb_state_key_get_one_sym(p_ss.xkb_state, p_keycode)); + // NOTE: xkbcommon's API really encourages to apply the modifier state but we + // only want a "plain" symbol so that we can convert it into a godot keycode. + const xkb_keysym_t *syms = nullptr; + int num_sys = xkb_keymap_key_get_syms_by_level(p_ss.xkb_keymap, p_keycode, p_ss.current_layout_index, 0, &syms); + Key physical_keycode = KeyMappingXKB::get_scancode(p_keycode); KeyLocation key_location = KeyMappingXKB::get_location(p_keycode); uint32_t unicode = xkb_state_key_get_utf32(p_ss.xkb_state, p_keycode); + Key keycode = Key::NONE; + if (num_sys > 0 && syms) { + keycode = KeyMappingXKB::get_keycode(syms[0]); + } + if (keycode == Key::NONE) { keycode = physical_keycode; } diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index a9a0210c86..81413af0e6 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -3053,8 +3053,14 @@ Error DisplayServerWindows::dialog_show(String p_title, String p_description, Ve buttons.push_back(s.utf16()); } + WindowID window_id = _get_focused_window_or_popup(); + if (!windows.has(window_id)) { + window_id = MAIN_WINDOW_ID; + } + config.pszWindowTitle = (LPCWSTR)(title.get_data()); config.pszContent = (LPCWSTR)(message.get_data()); + config.hwndParent = windows[window_id].hWnd; const int button_count = buttons.size(); config.cButtons = button_count; @@ -3063,7 +3069,7 @@ Error DisplayServerWindows::dialog_show(String p_title, String p_description, Ve TASKDIALOG_BUTTON *tbuttons = button_count != 0 ? (TASKDIALOG_BUTTON *)alloca(sizeof(TASKDIALOG_BUTTON) * button_count) : nullptr; if (tbuttons) { for (int i = 0; i < button_count; i++) { - tbuttons[i].nButtonID = i; + tbuttons[i].nButtonID = i + 100; tbuttons[i].pszButtonText = (LPCWSTR)(buttons[i].get_data()); } } @@ -3080,7 +3086,7 @@ Error DisplayServerWindows::dialog_show(String p_title, String p_description, Ve if (task_dialog_indirect && SUCCEEDED(task_dialog_indirect(&config, &button_pressed, nullptr, nullptr))) { if (p_callback.is_valid()) { - Variant button = button_pressed; + Variant button = button_pressed - 100; const Variant *args[1] = { &button }; Variant ret; Callable::CallError ce; @@ -3230,7 +3236,7 @@ Error DisplayServerWindows::dialog_input_text(String p_title, String p_descripti WCHAR font[13]; // must be "MS Shell Dlg" } template_base = { 1, 0xFFFF, 0, 0, - DS_SYSMODAL | DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU, + DS_SYSMODAL | DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION, 3, 0, 0, 20, 20, L"", L"#32770", L"", 8, FW_NORMAL, 0, DEFAULT_CHARSET, L"MS Shell Dlg" }; @@ -4686,7 +4692,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA // If multiple Shifts are held down at the same time, // Windows natively only sends a KEYUP for the last one to be released. if (raw->data.keyboard.Flags & RI_KEY_BREAK) { - if (!mods.has_flag(WinKeyModifierMask::SHIFT)) { + // Make sure to check the latest key state since + // we're in the middle of the message queue. + if (GetAsyncKeyState(VK_SHIFT) < 0) { // A Shift is released, but another Shift is still held ERR_BREAK(key_event_pos >= KEY_EVENT_BUFFER_SIZE); diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index 224266bcb6..86e6553078 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -33,6 +33,7 @@ #include "cpu_particles_2d.h" #include "cpu_particles_2d.compat.inc" +#include "core/math/random_number_generator.h" #include "scene/2d/gpu_particles_2d.h" #include "scene/resources/atlas_texture.h" #include "scene/resources/canvas_item_material.h" @@ -771,22 +772,22 @@ void CPUParticles2D::_particles_process(double p_delta) { } p.seed = seed + uint32_t(i) + i + cycle; - uint32_t _seed = p.seed; + rng->set_seed(p.seed); - p.angle_rand = rand_from_seed(_seed); - p.scale_rand = rand_from_seed(_seed); - p.hue_rot_rand = rand_from_seed(_seed); - p.anim_offset_rand = rand_from_seed(_seed); + p.angle_rand = rng->randf(); + p.scale_rand = rng->randf(); + p.hue_rot_rand = rng->randf(); + p.anim_offset_rand = rng->randf(); if (color_initial_ramp.is_valid()) { - p.start_color_rand = color_initial_ramp->get_color_at_offset(rand_from_seed(_seed)); + p.start_color_rand = color_initial_ramp->get_color_at_offset(rng->randf()); } else { p.start_color_rand = Color(1, 1, 1, 1); } - real_t angle1_rad = direction.angle() + Math::deg_to_rad((rand_from_seed(_seed) * 2.0 - 1.0) * spread); + real_t angle1_rad = direction.angle() + Math::deg_to_rad((rng->randf() * 2.0 - 1.0) * spread); Vector2 rot = Vector2(Math::cos(angle1_rad), Math::sin(angle1_rad)); - p.velocity = rot * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_max[PARAM_INITIAL_LINEAR_VELOCITY], (real_t)rand_from_seed(_seed)); + p.velocity = rot * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_max[PARAM_INITIAL_LINEAR_VELOCITY], rng->randf()); real_t base_angle = tex_angle * Math::lerp(parameters_min[PARAM_ANGLE], parameters_max[PARAM_ANGLE], p.angle_rand); p.rotation = Math::deg_to_rad(base_angle); @@ -794,7 +795,7 @@ void CPUParticles2D::_particles_process(double p_delta) { p.custom[0] = 0.0; // unused p.custom[1] = 0.0; // phase [0..1] p.custom[2] = tex_anim_offset * Math::lerp(parameters_min[PARAM_ANIM_OFFSET], parameters_max[PARAM_ANIM_OFFSET], p.anim_offset_rand); - p.custom[3] = (1.0 - rand_from_seed(_seed) * lifetime_randomness); + p.custom[3] = (1.0 - rng->randf() * lifetime_randomness); p.transform = Transform2D(); p.time = 0; p.lifetime = lifetime * p.custom[3]; @@ -805,17 +806,17 @@ void CPUParticles2D::_particles_process(double p_delta) { //do none } break; case EMISSION_SHAPE_SPHERE: { - real_t t = Math_TAU * rand_from_seed(_seed); - real_t radius = emission_sphere_radius * rand_from_seed(_seed); + real_t t = Math_TAU * rng->randf(); + real_t radius = emission_sphere_radius * rng->randf(); p.transform[2] = Vector2(Math::cos(t), Math::sin(t)) * radius; } break; case EMISSION_SHAPE_SPHERE_SURFACE: { - real_t s = rand_from_seed(_seed), t = Math_TAU * rand_from_seed(_seed); + real_t s = rng->randf(), t = Math_TAU * rng->randf(); real_t radius = emission_sphere_radius * Math::sqrt(1.0 - s * s); p.transform[2] = Vector2(Math::cos(t), Math::sin(t)) * radius; } break; case EMISSION_SHAPE_RECTANGLE: { - p.transform[2] = Vector2(rand_from_seed(_seed) * 2.0 - 1.0, rand_from_seed(_seed) * 2.0 - 1.0) * emission_rect_extents; + p.transform[2] = Vector2(rng->randf() * 2.0 - 1.0, rng->randf() * 2.0 - 1.0) * emission_rect_extents; } break; case EMISSION_SHAPE_POINTS: case EMISSION_SHAPE_DIRECTED_POINTS: { @@ -1525,6 +1526,8 @@ CPUParticles2D::CPUParticles2D() { set_amount(8); set_use_local_coordinates(false); + rng.instantiate(); + set_param_min(PARAM_INITIAL_LINEAR_VELOCITY, 0); set_param_min(PARAM_ANGULAR_VELOCITY, 0); set_param_min(PARAM_ORBIT_VELOCITY, 0); diff --git a/scene/2d/cpu_particles_2d.h b/scene/2d/cpu_particles_2d.h index c52ea76a93..68879d7ab0 100644 --- a/scene/2d/cpu_particles_2d.h +++ b/scene/2d/cpu_particles_2d.h @@ -35,6 +35,8 @@ #include "scene/2d/node_2d.h" +class RandomNumberGenerator; + class CPUParticles2D : public Node2D { private: GDCLASS(CPUParticles2D, Node2D); @@ -181,6 +183,8 @@ private: Vector2 gravity = Vector2(0, 980); + Ref rng; + void _update_internal(); void _particles_process(double p_delta); void _update_particle_data_buffer(); diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index 99abce0e13..10b1ed0c6d 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -33,6 +33,7 @@ #include "cpu_particles_3d.h" #include "cpu_particles_3d.compat.inc" +#include "core/math/random_number_generator.h" #include "scene/3d/camera_3d.h" #include "scene/3d/gpu_particles_3d.h" #include "scene/main/viewport.h" @@ -820,27 +821,27 @@ void CPUParticles3D::_particles_process(double p_delta) { tex_anim_offset = curve_parameters[PARAM_ANGLE]->sample(tv); } - p.seed = seed; - uint32_t _seed = seed + uint32_t(1) + i + cycle; - p.angle_rand = rand_from_seed(_seed); - p.scale_rand = rand_from_seed(_seed); - p.hue_rot_rand = rand_from_seed(_seed); - p.anim_offset_rand = rand_from_seed(_seed); + p.seed = seed + uint32_t(1) + i + cycle; + rng->set_seed(p.seed); + p.angle_rand = rng->randf(); + p.scale_rand = rng->randf(); + p.hue_rot_rand = rng->randf(); + p.anim_offset_rand = rng->randf(); if (color_initial_ramp.is_valid()) { - p.start_color_rand = color_initial_ramp->get_color_at_offset(rand_from_seed(_seed)); + p.start_color_rand = color_initial_ramp->get_color_at_offset(rng->randf()); } else { p.start_color_rand = Color(1, 1, 1, 1); } if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { - real_t angle1_rad = Math::atan2(direction.y, direction.x) + Math::deg_to_rad((rand_from_seed(_seed) * 2.0 - 1.0) * spread); + real_t angle1_rad = Math::atan2(direction.y, direction.x) + Math::deg_to_rad((rng->randf() * 2.0 - 1.0) * spread); Vector3 rot = Vector3(Math::cos(angle1_rad), Math::sin(angle1_rad), 0.0); - p.velocity = rot * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_max[PARAM_INITIAL_LINEAR_VELOCITY], rand_from_seed(_seed)); + p.velocity = rot * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_max[PARAM_INITIAL_LINEAR_VELOCITY], rng->randf()); } else { //initiate velocity spread in 3D - real_t angle1_rad = Math::deg_to_rad((rand_from_seed(_seed) * (real_t)2.0 - (real_t)1.0) * spread); - real_t angle2_rad = Math::deg_to_rad((rand_from_seed(_seed) * (real_t)2.0 - (real_t)1.0) * ((real_t)1.0 - flatness) * spread); + real_t angle1_rad = Math::deg_to_rad((rng->randf() * (real_t)2.0 - (real_t)1.0) * spread); + real_t angle2_rad = Math::deg_to_rad((rng->randf() * (real_t)2.0 - (real_t)1.0) * ((real_t)1.0 - flatness) * spread); Vector3 direction_xz = Vector3(Math::sin(angle1_rad), 0, Math::cos(angle1_rad)); Vector3 direction_yz = Vector3(0, Math::sin(angle2_rad), Math::cos(angle2_rad)); @@ -860,14 +861,14 @@ void CPUParticles3D::_particles_process(double p_delta) { binormal.normalize(); Vector3 normal = binormal.cross(direction_nrm); spread_direction = binormal * spread_direction.x + normal * spread_direction.y + direction_nrm * spread_direction.z; - p.velocity = spread_direction * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_max[PARAM_INITIAL_LINEAR_VELOCITY], rand_from_seed(_seed)); + p.velocity = spread_direction * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_max[PARAM_INITIAL_LINEAR_VELOCITY], rng->randf()); } real_t base_angle = tex_angle * Math::lerp(parameters_min[PARAM_ANGLE], parameters_max[PARAM_ANGLE], p.angle_rand); p.custom[0] = Math::deg_to_rad(base_angle); //angle p.custom[1] = 0.0; //phase p.custom[2] = tex_anim_offset * Math::lerp(parameters_min[PARAM_ANIM_OFFSET], parameters_max[PARAM_ANIM_OFFSET], p.anim_offset_rand); //animation offset (0-1) - p.custom[3] = (1.0 - rand_from_seed(_seed) * lifetime_randomness); + p.custom[3] = (1.0 - rng->randf() * lifetime_randomness); p.transform = Transform3D(); p.time = 0; p.lifetime = lifetime * p.custom[3]; @@ -878,20 +879,20 @@ void CPUParticles3D::_particles_process(double p_delta) { //do none } break; case EMISSION_SHAPE_SPHERE: { - real_t s = 2.0 * rand_from_seed(_seed) - 1.0; - real_t t = Math_TAU * rand_from_seed(_seed); - real_t x = rand_from_seed(_seed); + real_t s = 2.0 * rng->randf() - 1.0; + real_t t = Math_TAU * rng->randf(); + real_t x = rng->randf(); real_t radius = emission_sphere_radius * Math::sqrt(1.0 - s * s); p.transform.origin = Vector3(0, 0, 0).lerp(Vector3(radius * Math::cos(t), radius * Math::sin(t), emission_sphere_radius * s), x); } break; case EMISSION_SHAPE_SPHERE_SURFACE: { - real_t s = 2.0 * rand_from_seed(_seed) - 1.0; - real_t t = Math_TAU * rand_from_seed(_seed); + real_t s = 2.0 * rng->randf() - 1.0; + real_t t = Math_TAU * rng->randf(); real_t radius = emission_sphere_radius * Math::sqrt(1.0 - s * s); p.transform.origin = Vector3(radius * Math::cos(t), radius * Math::sin(t), emission_sphere_radius * s); } break; case EMISSION_SHAPE_BOX: { - p.transform.origin = Vector3(rand_from_seed(_seed) * 2.0 - 1.0, rand_from_seed(_seed) * 2.0 - 1.0, rand_from_seed(_seed) * 2.0 - 1.0) * emission_box_extents; + p.transform.origin = Vector3(rng->randf() * 2.0 - 1.0, rng->randf() * 2.0 - 1.0, rng->randf() * 2.0 - 1.0) * emission_box_extents; } break; case EMISSION_SHAPE_POINTS: case EMISSION_SHAPE_DIRECTED_POINTS: { @@ -935,11 +936,11 @@ void CPUParticles3D::_particles_process(double p_delta) { case EMISSION_SHAPE_RING: { real_t radius_clamped = MAX(0.001, emission_ring_radius); real_t top_radius = MAX(radius_clamped - Math::tan(Math::deg_to_rad(90.0 - emission_ring_cone_angle)) * emission_ring_height, 0.0); - real_t y_pos = rand_from_seed(_seed); + real_t y_pos = rng->randf(); real_t skew = MAX(MIN(radius_clamped, top_radius) / MAX(radius_clamped, top_radius), 0.5); y_pos = radius_clamped < top_radius ? Math::pow(y_pos, skew) : 1.0 - Math::pow(y_pos, skew); - real_t ring_random_angle = rand_from_seed(_seed) * Math_TAU; - real_t ring_random_radius = Math::sqrt(rand_from_seed(_seed) * (radius_clamped * radius_clamped - emission_ring_inner_radius * emission_ring_inner_radius) + emission_ring_inner_radius * emission_ring_inner_radius); + real_t ring_random_angle = rng->randf() * Math_TAU; + real_t ring_random_radius = Math::sqrt(rng->randf() * (radius_clamped * radius_clamped - emission_ring_inner_radius * emission_ring_inner_radius) + emission_ring_inner_radius * emission_ring_inner_radius); ring_random_radius = Math::lerp(ring_random_radius, ring_random_radius * (top_radius / radius_clamped), y_pos); Vector3 axis = emission_ring_axis == Vector3(0.0, 0.0, 0.0) ? Vector3(0.0, 0.0, 1.0) : emission_ring_axis.normalized(); Vector3 ortho_axis; @@ -1766,6 +1767,8 @@ CPUParticles3D::CPUParticles3D() { set_emitting(true); set_amount(8); + rng.instantiate(); + set_param_min(PARAM_INITIAL_LINEAR_VELOCITY, 0); set_param_min(PARAM_ANGULAR_VELOCITY, 0); set_param_min(PARAM_ORBIT_VELOCITY, 0); diff --git a/scene/3d/cpu_particles_3d.h b/scene/3d/cpu_particles_3d.h index 196140f752..257e5aea7a 100644 --- a/scene/3d/cpu_particles_3d.h +++ b/scene/3d/cpu_particles_3d.h @@ -35,6 +35,8 @@ #include "scene/3d/visual_instance_3d.h" +class RandomNumberGenerator; + class CPUParticles3D : public GeometryInstance3D { private: GDCLASS(CPUParticles3D, GeometryInstance3D); @@ -192,6 +194,8 @@ private: Vector3 gravity = Vector3(0, -9.8, 0); + Ref rng; + void _update_internal(); void _particles_process(double p_delta); void _update_particle_data_buffer(); diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index 0ed5c6519b..2dd9277c49 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -94,7 +94,11 @@ void GPUParticles3D::set_one_shot(bool p_one_shot) { if (is_emitting()) { if (!one_shot) { - restart(); + if (!use_fixed_seed) { + set_seed(Math::rand()); + } + + RenderingServer::get_singleton()->particles_restart(particles); } } } diff --git a/scene/3d/navigation_obstacle_3d.cpp b/scene/3d/navigation_obstacle_3d.cpp index 43b3996b32..7af8d3aa45 100644 --- a/scene/3d/navigation_obstacle_3d.cpp +++ b/scene/3d/navigation_obstacle_3d.cpp @@ -118,7 +118,7 @@ void NavigationObstacle3D::_notification(int p_what) { set_physics_process_internal(false); _update_map(RID()); #ifdef DEBUG_ENABLED - _update_debug(); + _clear_debug(); #endif // DEBUG_ENABLED } break; @@ -712,3 +712,14 @@ void NavigationObstacle3D::_update_static_obstacle_debug() { } } #endif // DEBUG_ENABLED + +#ifdef DEBUG_ENABLED +void NavigationObstacle3D::_clear_debug() { + RenderingServer *rs = RenderingServer::get_singleton(); + ERR_FAIL_NULL(rs); + rs->mesh_clear(fake_agent_radius_debug_mesh_rid); + rs->mesh_clear(static_obstacle_debug_mesh_rid); + rs->instance_set_scenario(fake_agent_radius_debug_instance_rid, RID()); + rs->instance_set_scenario(static_obstacle_debug_instance_rid, RID()); +} +#endif // DEBUG_ENABLED diff --git a/scene/3d/navigation_obstacle_3d.h b/scene/3d/navigation_obstacle_3d.h index f614fbe272..6ed291ef60 100644 --- a/scene/3d/navigation_obstacle_3d.h +++ b/scene/3d/navigation_obstacle_3d.h @@ -76,6 +76,7 @@ private: void _update_debug(); void _update_fake_agent_radius_debug(); void _update_static_obstacle_debug(); + void _clear_debug(); #endif // DEBUG_ENABLED protected: diff --git a/scene/3d/physics/collision_polygon_3d.cpp b/scene/3d/physics/collision_polygon_3d.cpp index 863a9b25b3..85041410f9 100644 --- a/scene/3d/physics/collision_polygon_3d.cpp +++ b/scene/3d/physics/collision_polygon_3d.cpp @@ -72,6 +72,8 @@ void CollisionPolygon3D::_build_polygon() { convex->set_points(cp); convex->set_margin(margin); + convex->set_debug_color(debug_color); + convex->set_debug_fill(debug_fill); collision_object->shape_owner_add_shape(owner_id, convex); collision_object->shape_owner_set_disabled(owner_id, disabled); } @@ -159,6 +161,68 @@ bool CollisionPolygon3D::is_disabled() const { return disabled; } +Color CollisionPolygon3D::_get_default_debug_color() const { + const SceneTree *st = SceneTree::get_singleton(); + return st ? st->get_debug_collisions_color() : Color(0.0, 0.0, 0.0, 0.0); +} + +void CollisionPolygon3D::set_debug_color(const Color &p_color) { + if (debug_color == p_color) { + return; + } + + debug_color = p_color; + + update_gizmos(); +} + +Color CollisionPolygon3D::get_debug_color() const { + return debug_color; +} + +void CollisionPolygon3D::set_debug_fill_enabled(bool p_enable) { + if (debug_fill == p_enable) { + return; + } + + debug_fill = p_enable; + + update_gizmos(); +} + +bool CollisionPolygon3D::get_debug_fill_enabled() const { + return debug_fill; +} + +#ifdef DEBUG_ENABLED + +bool CollisionPolygon3D::_property_can_revert(const StringName &p_name) const { + if (p_name == "debug_color") { + return true; + } + return false; +} + +bool CollisionPolygon3D::_property_get_revert(const StringName &p_name, Variant &r_property) const { + if (p_name == "debug_color") { + r_property = _get_default_debug_color(); + return true; + } + return false; +} + +void CollisionPolygon3D::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "debug_color") { + if (debug_color == _get_default_debug_color()) { + p_property.usage = PROPERTY_USAGE_DEFAULT & ~PROPERTY_USAGE_STORAGE; + } else { + p_property.usage = PROPERTY_USAGE_DEFAULT; + } + } +} + +#endif // DEBUG_ENABLED + real_t CollisionPolygon3D::get_margin() const { return margin; } @@ -203,6 +267,12 @@ void CollisionPolygon3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_disabled", "disabled"), &CollisionPolygon3D::set_disabled); ClassDB::bind_method(D_METHOD("is_disabled"), &CollisionPolygon3D::is_disabled); + ClassDB::bind_method(D_METHOD("set_debug_color", "color"), &CollisionPolygon3D::set_debug_color); + ClassDB::bind_method(D_METHOD("get_debug_color"), &CollisionPolygon3D::get_debug_color); + + ClassDB::bind_method(D_METHOD("set_enable_debug_fill", "enable"), &CollisionPolygon3D::set_debug_fill_enabled); + ClassDB::bind_method(D_METHOD("get_enable_debug_fill"), &CollisionPolygon3D::get_debug_fill_enabled); + ClassDB::bind_method(D_METHOD("set_margin", "margin"), &CollisionPolygon3D::set_margin); ClassDB::bind_method(D_METHOD("get_margin"), &CollisionPolygon3D::get_margin); @@ -212,8 +282,15 @@ void CollisionPolygon3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0.001,10,0.001,suffix:m"), "set_margin", "get_margin"); + + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "debug_color"), "set_debug_color", "get_debug_color"); + // Default value depends on a project setting, override for doc generation purposes. + ADD_PROPERTY_DEFAULT("debug_color", Color(0.0, 0.0, 0.0, 0.0)); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "debug_fill"), "set_enable_debug_fill", "get_enable_debug_fill"); } CollisionPolygon3D::CollisionPolygon3D() { set_notify_local_transform(true); + debug_color = _get_default_debug_color(); } diff --git a/scene/3d/physics/collision_polygon_3d.h b/scene/3d/physics/collision_polygon_3d.h index d8c9973351..705cbffb3a 100644 --- a/scene/3d/physics/collision_polygon_3d.h +++ b/scene/3d/physics/collision_polygon_3d.h @@ -48,6 +48,11 @@ protected: uint32_t owner_id = 0; CollisionObject3D *collision_object = nullptr; + Color debug_color; + bool debug_fill = true; + + Color _get_default_debug_color() const; + bool disabled = false; void _build_polygon(); @@ -60,6 +65,12 @@ protected: void _notification(int p_what); static void _bind_methods(); +#ifdef DEBUG_ENABLED + bool _property_can_revert(const StringName &p_name) const; + bool _property_get_revert(const StringName &p_name, Variant &r_property) const; + void _validate_property(PropertyInfo &p_property) const; +#endif // DEBUG_ENABLED + public: void set_depth(real_t p_depth); real_t get_depth() const; @@ -70,6 +81,12 @@ public: void set_disabled(bool p_disabled); bool is_disabled() const; + void set_debug_color(const Color &p_color); + Color get_debug_color() const; + + void set_debug_fill_enabled(bool p_enable); + bool get_debug_fill_enabled() const; + virtual AABB get_item_rect() const; real_t get_margin() const; diff --git a/scene/3d/spring_bone_simulator_3d.cpp b/scene/3d/spring_bone_simulator_3d.cpp index 45579dcf70..83b12ae28e 100644 --- a/scene/3d/spring_bone_simulator_3d.cpp +++ b/scene/3d/spring_bone_simulator_3d.cpp @@ -1359,12 +1359,12 @@ void SpringBoneSimulator3D::_make_collisions_dirty() { void SpringBoneSimulator3D::_update_joint_array(int p_index) { _make_joints_dirty(p_index); - set_joint_count(p_index, 0); Skeleton3D *sk = get_skeleton(); int current_bone = settings[p_index]->end_bone; int root_bone = settings[p_index]->root_bone; if (!sk || current_bone < 0 || root_bone < 0) { + set_joint_count(p_index, 0); return; } @@ -1377,7 +1377,11 @@ void SpringBoneSimulator3D::_update_joint_array(int p_index) { } current_bone = sk->get_bone_parent(current_bone); } - ERR_FAIL_COND_EDMSG(!valid, "End bone must be the same as or a child of root bone."); + + if (!valid) { + set_joint_count(p_index, 0); + ERR_FAIL_EDMSG("End bone must be the same as or a child of root bone."); + } Vector new_joints; current_bone = settings[p_index]->end_bone; @@ -1403,6 +1407,7 @@ void SpringBoneSimulator3D::_update_joints() { continue; } if (settings[i]->individual_config) { + settings[i]->simulation_dirty = true; settings[i]->joints_dirty = false; continue; // Abort. } @@ -1522,7 +1527,7 @@ void SpringBoneSimulator3D::_init_joints(Skeleton3D *p_skeleton, SpringBone3DSet setting->joints[i]->verlet->prev_tail = setting->joints[i]->verlet->current_tail; setting->joints[i]->verlet->forward_vector = axis.normalized(); setting->joints[i]->verlet->length = axis.length(); - setting->joints[i]->verlet->prev_rot = Quaternion(0, 0, 0, 1); + setting->joints[i]->verlet->current_rot = Quaternion(0, 0, 0, 1); } else if (setting->extend_end_bone && setting->end_bone_length > 0) { Vector3 axis = get_end_bone_axis(setting->end_bone, setting->end_bone_direction); if (axis.is_zero_approx()) { @@ -1533,7 +1538,7 @@ void SpringBoneSimulator3D::_init_joints(Skeleton3D *p_skeleton, SpringBone3DSet setting->joints[i]->verlet->length = setting->end_bone_length; setting->joints[i]->verlet->current_tail = setting->cached_center.xform(p_skeleton->get_bone_global_pose(setting->joints[i]->bone).xform(axis * setting->end_bone_length)); setting->joints[i]->verlet->prev_tail = setting->joints[i]->verlet->current_tail; - setting->joints[i]->verlet->prev_rot = Quaternion(0, 0, 0, 1); + setting->joints[i]->verlet->current_rot = Quaternion(0, 0, 0, 1); } } setting->simulation_dirty = false; @@ -1599,8 +1604,8 @@ void SpringBoneSimulator3D::_process_joints(double p_delta, Skeleton3D *p_skelet // Convert position to rotation. Vector3 from = current_rot.xform(verlet->forward_vector); Vector3 to = p_inverted_center_transform.basis.xform(next_tail - current_origin).normalized(); - Quaternion from_to = get_from_to_rotation(from, to, verlet->prev_rot); - verlet->prev_rot = from_to; + Quaternion from_to = get_from_to_rotation(from, to, verlet->current_rot); + verlet->current_rot = from_to; // Apply rotation. from_to *= current_rot; diff --git a/scene/3d/spring_bone_simulator_3d.h b/scene/3d/spring_bone_simulator_3d.h index 750f14f849..f233a6e2ad 100644 --- a/scene/3d/spring_bone_simulator_3d.h +++ b/scene/3d/spring_bone_simulator_3d.h @@ -74,7 +74,7 @@ public: Vector3 prev_tail; Vector3 current_tail; Vector3 forward_vector; - Quaternion prev_rot; + Quaternion current_rot; float length = 0.0; }; @@ -106,7 +106,6 @@ public: bool extend_end_bone = false; BoneDirection end_bone_direction = BONE_DIRECTION_FROM_PARENT; float end_bone_length = 0.0; - float end_bone_tip_radius = 0.02; CenterFrom center_from = CENTER_FROM_WORLD_ORIGIN; NodePath center_node; diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp index 4c3574fa24..14f2da3508 100644 --- a/scene/3d/visual_instance_3d.cpp +++ b/scene/3d/visual_instance_3d.cpp @@ -115,19 +115,6 @@ void VisualInstance3D::_notification(int p_what) { RenderingServer::get_singleton()->instance_reset_physics_interpolation(instance); } -#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) - else if (GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) { - - String node_name = is_inside_tree() ? String(get_path()) : String(get_name()); - if (!_is_vi_visible()) { - WARN_PRINT("[Physics interpolation] NOTIFICATION_RESET_PHYSICS_INTERPOLATION only works with unhidden nodes: \"" + node_name + "\"."); - } - if (!is_physics_interpolated()) { - WARN_PRINT("[Physics interpolation] NOTIFICATION_RESET_PHYSICS_INTERPOLATION only works with interpolated nodes: \"" + node_name + "\"."); - } - } -#endif - } break; case NOTIFICATION_EXIT_WORLD: { diff --git a/scene/3d/voxelizer.cpp b/scene/3d/voxelizer.cpp index 6256d8da61..aafe34c1ba 100644 --- a/scene/3d/voxelizer.cpp +++ b/scene/3d/voxelizer.cpp @@ -873,7 +873,7 @@ Voxelizer::BakeResult Voxelizer::get_sdf_3d_image(Vector &r_image, Bake uint32_t cell_count = bake_cells.size(); for (uint32_t i = 0; i < cell_count; i++) { - if (cells[i].level < (cell_subdiv - 1)) { + if (cells[i].level < cell_subdiv) { continue; //do not care about this level } diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp index 65ecdc942c..9a494b026d 100644 --- a/scene/debugger/scene_debugger.cpp +++ b/scene/debugger/scene_debugger.cpp @@ -1367,7 +1367,7 @@ void RuntimeNodeSelect::_root_window_input(const Ref &p_event) { } } else if (node_select_type == NODE_TYPE_3D) { #ifndef _3D_DISABLED - if (root->get_camera_3d() && _handle_3d_input(p_event)) { + if (_handle_3d_input(p_event)) { return; } #endif // _3D_DISABLED @@ -1549,23 +1549,27 @@ void RuntimeNodeSelect::_select_node(Node *p_node) { selected_node = p_node; - sbox_3d_instance = RS::get_singleton()->instance_create2(sbox_3d_mesh->get_rid(), node_3d->get_world_3d()->get_scenario()); - sbox_3d_instance_ofs = RS::get_singleton()->instance_create2(sbox_3d_mesh->get_rid(), node_3d->get_world_3d()->get_scenario()); - RS::get_singleton()->instance_geometry_set_cast_shadows_setting(sbox_3d_instance, RS::SHADOW_CASTING_SETTING_OFF); - RS::get_singleton()->instance_geometry_set_cast_shadows_setting(sbox_3d_instance_ofs, RS::SHADOW_CASTING_SETTING_OFF); - RS::get_singleton()->instance_geometry_set_flag(sbox_3d_instance, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true); - RS::get_singleton()->instance_geometry_set_flag(sbox_3d_instance, RS::INSTANCE_FLAG_USE_BAKED_LIGHT, false); - RS::get_singleton()->instance_geometry_set_flag(sbox_3d_instance_ofs, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true); - RS::get_singleton()->instance_geometry_set_flag(sbox_3d_instance_ofs, RS::INSTANCE_FLAG_USE_BAKED_LIGHT, false); + if (sbox_3d_mesh.is_valid()) { + sbox_3d_instance = RS::get_singleton()->instance_create2(sbox_3d_mesh->get_rid(), node_3d->get_world_3d()->get_scenario()); + sbox_3d_instance_ofs = RS::get_singleton()->instance_create2(sbox_3d_mesh->get_rid(), node_3d->get_world_3d()->get_scenario()); + RS::get_singleton()->instance_geometry_set_cast_shadows_setting(sbox_3d_instance, RS::SHADOW_CASTING_SETTING_OFF); + RS::get_singleton()->instance_geometry_set_cast_shadows_setting(sbox_3d_instance_ofs, RS::SHADOW_CASTING_SETTING_OFF); + RS::get_singleton()->instance_geometry_set_flag(sbox_3d_instance, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true); + RS::get_singleton()->instance_geometry_set_flag(sbox_3d_instance, RS::INSTANCE_FLAG_USE_BAKED_LIGHT, false); + RS::get_singleton()->instance_geometry_set_flag(sbox_3d_instance_ofs, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true); + RS::get_singleton()->instance_geometry_set_flag(sbox_3d_instance_ofs, RS::INSTANCE_FLAG_USE_BAKED_LIGHT, false); + } - sbox_3d_instance_xray = RS::get_singleton()->instance_create2(sbox_3d_mesh_xray->get_rid(), node_3d->get_world_3d()->get_scenario()); - sbox_3d_instance_xray_ofs = RS::get_singleton()->instance_create2(sbox_3d_mesh_xray->get_rid(), node_3d->get_world_3d()->get_scenario()); - RS::get_singleton()->instance_geometry_set_cast_shadows_setting(sbox_3d_instance_xray, RS::SHADOW_CASTING_SETTING_OFF); - RS::get_singleton()->instance_geometry_set_cast_shadows_setting(sbox_3d_instance_xray_ofs, RS::SHADOW_CASTING_SETTING_OFF); - RS::get_singleton()->instance_geometry_set_flag(sbox_3d_instance_xray, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true); - RS::get_singleton()->instance_geometry_set_flag(sbox_3d_instance_xray, RS::INSTANCE_FLAG_USE_BAKED_LIGHT, false); - RS::get_singleton()->instance_geometry_set_flag(sbox_3d_instance_xray_ofs, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true); - RS::get_singleton()->instance_geometry_set_flag(sbox_3d_instance_xray_ofs, RS::INSTANCE_FLAG_USE_BAKED_LIGHT, false); + if (sbox_3d_mesh_xray.is_valid()) { + sbox_3d_instance_xray = RS::get_singleton()->instance_create2(sbox_3d_mesh_xray->get_rid(), node_3d->get_world_3d()->get_scenario()); + sbox_3d_instance_xray_ofs = RS::get_singleton()->instance_create2(sbox_3d_mesh_xray->get_rid(), node_3d->get_world_3d()->get_scenario()); + RS::get_singleton()->instance_geometry_set_cast_shadows_setting(sbox_3d_instance_xray, RS::SHADOW_CASTING_SETTING_OFF); + RS::get_singleton()->instance_geometry_set_cast_shadows_setting(sbox_3d_instance_xray_ofs, RS::SHADOW_CASTING_SETTING_OFF); + RS::get_singleton()->instance_geometry_set_flag(sbox_3d_instance_xray, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true); + RS::get_singleton()->instance_geometry_set_flag(sbox_3d_instance_xray, RS::INSTANCE_FLAG_USE_BAKED_LIGHT, false); + RS::get_singleton()->instance_geometry_set_flag(sbox_3d_instance_xray_ofs, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true); + RS::get_singleton()->instance_geometry_set_flag(sbox_3d_instance_xray_ofs, RS::INSTANCE_FLAG_USE_BAKED_LIGHT, false); + } } #endif // _3D_DISABLED } @@ -1867,10 +1871,6 @@ void RuntimeNodeSelect::_update_view_2d() { void RuntimeNodeSelect::_find_3d_items_at_pos(const Point2 &p_pos, Vector &r_items) { Window *root = SceneTree::get_singleton()->get_root(); Camera3D *camera = root->get_viewport()->get_camera_3d(); - if (!camera) { - return; - } - Vector3 ray, pos, to; if (root->get_viewport()->is_camera_3d_override_enabled()) { Viewport *vp = root->get_viewport(); @@ -2129,22 +2129,22 @@ Transform3D RuntimeNodeSelect::_get_cursor_transform() { void RuntimeNodeSelect::_reset_camera_3d() { camera_first_override = true; + cursor = Cursor(); Window *root = SceneTree::get_singleton()->get_root(); Camera3D *camera = root->get_camera_3d(); - if (!camera) { - return; + if (camera) { + Transform3D transform = camera->get_global_transform(); + transform.translate_local(0, 0, -cursor.distance); + cursor.pos = transform.origin; + + cursor.x_rot = -camera->get_global_rotation().x; + cursor.y_rot = -camera->get_global_rotation().y; + + cursor.fov_scale = CLAMP(camera->get_fov() / CAMERA_BASE_FOV, CAMERA_MIN_FOV_SCALE, CAMERA_MAX_FOV_SCALE); + } else { + cursor.fov_scale = 1.0; } - cursor = Cursor(); - Transform3D transform = camera->get_global_transform(); - transform.translate_local(0, 0, -cursor.distance); - cursor.pos = transform.origin; - - cursor.x_rot = -camera->get_global_rotation().x; - cursor.y_rot = -camera->get_global_rotation().y; - - cursor.fov_scale = CLAMP(camera->get_fov() / CAMERA_BASE_FOV, CAMERA_MIN_FOV_SCALE, CAMERA_MAX_FOV_SCALE); - SceneTree::get_singleton()->get_root()->set_camera_3d_override_transform(_get_cursor_transform()); SceneTree::get_singleton()->get_root()->set_camera_3d_override_perspective(CAMERA_BASE_FOV * cursor.fov_scale, CAMERA_ZNEAR, CAMERA_ZFAR); } diff --git a/scene/gui/aspect_ratio_container.cpp b/scene/gui/aspect_ratio_container.cpp index acfcfeb59c..a6328a54f5 100644 --- a/scene/gui/aspect_ratio_container.cpp +++ b/scene/gui/aspect_ratio_container.cpp @@ -37,7 +37,7 @@ Size2 AspectRatioContainer::get_minimum_size() const { Size2 ms; for (int i = 0; i < get_child_count(); i++) { - Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE); + Control *c = as_sortable_control(get_child(i), SortableVisibilityMode::VISIBLE); if (!c) { continue; } diff --git a/scene/gui/box_container.cpp b/scene/gui/box_container.cpp index 809b390563..7d4e006d93 100644 --- a/scene/gui/box_container.cpp +++ b/scene/gui/box_container.cpp @@ -245,7 +245,7 @@ Size2 BoxContainer::get_minimum_size() const { bool first = true; for (int i = 0; i < get_child_count(); i++) { - Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE); + Control *c = as_sortable_control(get_child(i), SortableVisibilityMode::VISIBLE); if (!c) { continue; } diff --git a/scene/gui/center_container.cpp b/scene/gui/center_container.cpp index 836967db8d..10d41bcf67 100644 --- a/scene/gui/center_container.cpp +++ b/scene/gui/center_container.cpp @@ -38,7 +38,7 @@ Size2 CenterContainer::get_minimum_size() const { } Size2 ms; for (int i = 0; i < get_child_count(); i++) { - Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE); + Control *c = as_sortable_control(get_child(i), SortableVisibilityMode::VISIBLE); if (!c) { continue; } diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 000489a558..ba4b48c9a8 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -1264,6 +1264,7 @@ void ColorPicker::_sample_input(const Ref &p_event) { if (!display_old_color) { return; } + const Ref mb = p_event; if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) { const Rect2 rect_old = Rect2(Point2(), Size2(sample->get_size().width * 0.5, sample->get_size().height * 0.95)); diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h index b8e0cf8d61..844cc77e5c 100644 --- a/scene/gui/color_picker.h +++ b/scene/gui/color_picker.h @@ -40,6 +40,7 @@ class AspectRatioContainer; class ColorMode; class ColorPickerShape; +class FileDialog; class GridContainer; class HSlider; class Label; @@ -51,7 +52,6 @@ class PopupMenu; class SpinBox; class StyleBoxFlat; class TextureRect; -class FileDialog; class ColorPresetButton : public BaseButton { GDCLASS(ColorPresetButton, BaseButton); @@ -106,7 +106,7 @@ public: SHAPE_MAX }; - static const int SLIDER_COUNT = 4; + static const int SLIDER_COUNT = 3; private: enum class MenuOption { diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp index 0e0c1d033d..9df5746782 100644 --- a/scene/gui/container.cpp +++ b/scene/gui/container.cpp @@ -142,15 +142,15 @@ void Container::queue_sort() { pending_sort = true; } -Control *Container::as_sortable_control(Node *p_node, SortableVisbilityMode p_visibility_mode) const { +Control *Container::as_sortable_control(Node *p_node, SortableVisibilityMode p_visibility_mode) const { Control *c = Object::cast_to(p_node); if (!c || c->is_set_as_top_level()) { return nullptr; } - if (p_visibility_mode == SortableVisbilityMode::VISIBLE && !c->is_visible()) { + if (p_visibility_mode == SortableVisibilityMode::VISIBLE && !c->is_visible()) { return nullptr; } - if (p_visibility_mode == SortableVisbilityMode::VISIBLE_IN_TREE && !c->is_visible_in_tree()) { + if (p_visibility_mode == SortableVisibilityMode::VISIBLE_IN_TREE && !c->is_visible_in_tree()) { return nullptr; } return c; diff --git a/scene/gui/container.h b/scene/gui/container.h index d77293d388..f7b67ca7ac 100644 --- a/scene/gui/container.h +++ b/scene/gui/container.h @@ -43,14 +43,14 @@ class Container : public Control { void _child_minsize_changed(); protected: - enum class SortableVisbilityMode { + enum class SortableVisibilityMode { VISIBLE, VISIBLE_IN_TREE, IGNORE, }; void queue_sort(); - Control *as_sortable_control(Node *p_node, SortableVisbilityMode p_visibility_mode = SortableVisbilityMode::VISIBLE_IN_TREE) const; + Control *as_sortable_control(Node *p_node, SortableVisibilityMode p_visibility_mode = SortableVisibilityMode::VISIBLE_IN_TREE) const; virtual void add_child_notify(Node *p_child) override; virtual void move_child_notify(Node *p_child) override; diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 3637bc8e9f..f02cdf6850 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -34,9 +34,9 @@ #include "container.h" #include "core/config/project_settings.h" -#include "core/math/geometry_2d.h" #include "core/os/os.h" #include "core/string/translation_server.h" +#include "scene/gui/scroll_container.h" #include "scene/main/canvas_layer.h" #include "scene/main/window.h" #include "scene/theme/theme_db.h" @@ -2283,18 +2283,9 @@ Control *Control::_get_focus_neighbor(Side p_side, int p_count) { return c; } - real_t dist = 1e7; + real_t square_of_dist = 1e14; Control *result = nullptr; - Point2 points[4]; - - Transform2D xform = get_global_transform(); - - points[0] = xform.xform(Point2()); - points[1] = xform.xform(Point2(get_size().x, 0)); - points[2] = xform.xform(get_size()); - points[3] = xform.xform(Point2(0, get_size().y)); - const Vector2 dir[4] = { Vector2(-1, 0), Vector2(0, -1), @@ -2304,18 +2295,73 @@ Control *Control::_get_focus_neighbor(Side p_side, int p_count) { Vector2 vdir = dir[p_side]; - real_t maxd = -1e7; + Rect2 r = get_global_rect(); + real_t begin_d = vdir.dot(r.get_position()); + real_t end_d = vdir.dot(r.get_end()); + real_t maxd = MAX(begin_d, end_d); - for (int i = 0; i < 4; i++) { - real_t d = vdir.dot(points[i]); - if (d > maxd) { - maxd = d; - } - } + Rect2 clamp = Rect2(-1e7, -1e7, 2e7, 2e7); + Rect2 result_rect; Node *base = this; while (base) { + ScrollContainer *sc = Object::cast_to(base); + + if (sc) { + Rect2 sc_r = sc->get_global_rect(); + bool follow_focus = sc->is_following_focus(); + + if (result && !follow_focus && !sc_r.intersects(result_rect)) { + result = nullptr; // Skip invisible control. + } + + if (result == nullptr) { + real_t sc_begin_d = vdir.dot(sc_r.get_position()); + real_t sc_end_d = vdir.dot(sc_r.get_end()); + real_t sc_maxd = sc_begin_d; + real_t sc_mind = sc_end_d; + if (sc_begin_d < sc_end_d) { + sc_maxd = sc_end_d; + sc_mind = sc_begin_d; + } + + if (!follow_focus && maxd < sc_mind) { + // Reposition to find visible control. + maxd = sc_mind; + r.set_position(r.get_position() + (sc_mind - maxd) * vdir); + } + + if (follow_focus || sc_maxd > maxd) { + _window_find_focus_neighbor(vdir, base, r, clamp, maxd, square_of_dist, &result); + } + + if (result == nullptr) { + // Reposition to search upwards. + maxd = sc_maxd; + r.set_position(r.get_position() + (sc_maxd - maxd) * vdir); + } else { + result_rect = result->get_global_rect(); + if (follow_focus) { + real_t r_begin_d = vdir.dot(result_rect.get_position()); + real_t r_end_d = vdir.dot(result_rect.get_end()); + real_t r_maxd = r_begin_d; + real_t r_mind = r_end_d; + if (r_begin_d < r_end_d) { + r_maxd = r_end_d; + r_mind = r_begin_d; + } + + if (r_maxd > sc_maxd) { + result_rect.set_position(result_rect.get_position() + (sc_maxd - r_maxd) * vdir); + } else if (r_mind < sc_mind) { + result_rect.set_position(result_rect.get_position() + (sc_mind - r_mind) * vdir); + } + } + } + } + } + Control *c = Object::cast_to(base); if (c) { if (c->data.RI) { @@ -2325,11 +2371,15 @@ Control *Control::_get_focus_neighbor(Side p_side, int p_count) { base = base->get_parent(); } + if (result) { + return result; + } + if (!base) { return nullptr; } - _window_find_focus_neighbor(vdir, base, points, maxd, dist, &result); + _window_find_focus_neighbor(vdir, base, r, clamp, maxd, square_of_dist, &result); return result; } @@ -2338,72 +2388,79 @@ Control *Control::find_valid_focus_neighbor(Side p_side) const { return const_cast(this)->_get_focus_neighbor(p_side); } -void Control::_window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, real_t p_min, real_t &r_closest_dist, Control **r_closest) { +void Control::_window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Rect2 &p_rect, const Rect2 &p_clamp, real_t p_min, real_t &r_closest_dist_squared, Control **r_closest) { if (Object::cast_to(p_at)) { - return; //bye + return; // Bye. } Control *c = Object::cast_to(p_at); + Container *container = Object::cast_to(p_at); + bool in_container = container ? container->is_ancestor_of(this) : false; - if (c && c != this && c->get_focus_mode() == FOCUS_ALL && c->is_visible_in_tree()) { - Point2 points[4]; + if (c && c != this && c->get_focus_mode() == FOCUS_ALL && !in_container && p_clamp.intersects(c->get_global_rect())) { + Rect2 r_c = c->get_global_rect(); + r_c = r_c.intersection(p_clamp); + real_t begin_d = p_dir.dot(r_c.get_position()); + real_t end_d = p_dir.dot(r_c.get_end()); + real_t max = MAX(begin_d, end_d); - Transform2D xform = c->get_global_transform(); + // Use max to allow navigation to overlapping controls (for ScrollContainer case). + if (max > (p_min + CMP_EPSILON)) { + // Calculate the shortest distance. (No shear transform) + // Flip along axis(es) so that C falls in the first quadrant of c (as origin) for easy calculation. + // The same transformation would put the direction vector in the positive direction (+x or +y). + // | ------------- + // | | | | + // | |-----C-----| + // ----|---a | | | + // | | | b------------ + // -|---c---|-----------------------> + // | | | + // ----|---- + // cC = ca + ab + bC + // The shortest distance is the vector ab's length or its positive projection length. - points[0] = xform.xform(Point2()); - points[1] = xform.xform(Point2(c->get_size().x, 0)); - points[2] = xform.xform(c->get_size()); - points[3] = xform.xform(Point2(0, c->get_size().y)); + Vector2 cC_origin = r_c.get_center() - p_rect.get_center(); + Vector2 cC = cC_origin.abs(); // Converted to fall in the first quadrant of c. - // Tie-breaking aims to address situations where a potential focus neighbor's bounding rect - // is right next to the currently focused control (e.g. in BoxContainer with - // separation overridden to 0). This needs specific handling so that the correct - // focus neighbor is selected. + Vector2 ab = cC - 0.5 * r_c.get_size() - 0.5 * p_rect.get_size(); - // Calculate centers of the potential neighbor, currently focused, and closest controls. - Point2 center = xform.xform(0.5 * c->get_size()); - // We only have the points, not an actual reference. - Point2 p_center = 0.25 * (p_points[0] + p_points[1] + p_points[2] + p_points[3]); - Point2 closest_center; - bool should_tiebreak = false; - if (*r_closest != nullptr) { - should_tiebreak = true; - Control *closest = *r_closest; - Transform2D closest_xform = closest->get_global_transform(); - closest_center = closest_xform.xform(0.5 * closest->get_size()); - } + real_t min_d_squared = 0.0; + if (ab.x > 0.0) { + min_d_squared += ab.x * ab.x; + } + if (ab.y > 0.0) { + min_d_squared += ab.y * ab.y; + } - real_t min = 1e7; + if (min_d_squared < r_closest_dist_squared || *r_closest == nullptr) { + r_closest_dist_squared = min_d_squared; + *r_closest = c; + } else if (min_d_squared == r_closest_dist_squared) { + // Tie-breaking aims to address situations where a potential focus neighbor's bounding rect + // is right next to the currently focused control (e.g. in BoxContainer with + // separation overridden to 0). This needs specific handling so that the correct + // focus neighbor is selected. - for (int i = 0; i < 4; i++) { - real_t d = p_dir.dot(points[i]); - if (d < min) { - min = d; + Point2 p_center = p_rect.get_center(); + Control *closest = *r_closest; + Point2 closest_center = closest->get_global_rect().get_center(); + + // Tie-break in favor of the control most aligned with p_dir. + if (Math::abs(p_dir.cross(cC_origin)) < Math::abs(p_dir.cross(closest_center - p_center))) { + *r_closest = c; + } } } + } - if (min > (p_min - CMP_EPSILON)) { - for (int i = 0; i < 4; i++) { - Vector2 la = p_points[i]; - Vector2 lb = p_points[(i + 1) % 4]; - - for (int j = 0; j < 4; j++) { - Vector2 fa = points[j]; - Vector2 fb = points[(j + 1) % 4]; - - Vector2 pa, pb; - real_t d = Geometry2D::get_closest_points_between_segments(la, lb, fa, fb, pa, pb); - if (d < r_closest_dist) { - r_closest_dist = d; - *r_closest = c; - } else if (should_tiebreak && d == r_closest_dist) { - // Tie-break in favor of the control most aligned with p_dir. - if (p_dir.dot((center - p_center).normalized()) > p_dir.dot((closest_center - p_center).normalized())) { - r_closest_dist = d; - *r_closest = c; - } - } - } + ScrollContainer *sc = Object::cast_to(c); + Rect2 intersection = p_clamp; + if (sc) { + if (!sc->is_following_focus() || !in_container) { + intersection = p_clamp.intersection(sc->get_global_rect()); + if (!intersection.has_area()) { + return; } } } @@ -2411,10 +2468,18 @@ void Control::_window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, cons for (int i = 0; i < p_at->get_child_count(); i++) { Node *child = p_at->get_child(i); Control *childc = Object::cast_to(child); - if (childc && childc->data.RI) { - continue; //subwindow, ignore + if (childc) { + if (childc->data.RI) { + continue; // Subwindow, ignore. + } + if (!childc->is_visible_in_tree()) { + continue; // Skip invisible node trees. + } + if (Object::cast_to(childc) && childc->is_ancestor_of(this)) { + continue; // Already searched in it, skip it. + } } - _window_find_focus_neighbor(p_dir, p_at->get_child(i), p_points, p_min, r_closest_dist, r_closest); + _window_find_focus_neighbor(p_dir, child, p_rect, intersection, p_min, r_closest_dist_squared, r_closest); } } diff --git a/scene/gui/control.h b/scene/gui/control.h index 880f216ca9..c484efbfb9 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -316,7 +316,7 @@ private: // Focus. - void _window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, real_t p_min, real_t &r_closest_dist, Control **r_closest); + void _window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Rect2 &p_rect, const Rect2 &p_clamp, real_t p_min, real_t &r_closest_dist_squared, Control **r_closest); Control *_get_focus_neighbor(Side p_side, int p_count = 0); // Theming. diff --git a/scene/gui/flow_container.cpp b/scene/gui/flow_container.cpp index 0de40185c8..3aa97d96fb 100644 --- a/scene/gui/flow_container.cpp +++ b/scene/gui/flow_container.cpp @@ -270,7 +270,7 @@ Size2 FlowContainer::get_minimum_size() const { Size2i minimum; for (int i = 0; i < get_child_count(); i++) { - Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE); + Control *c = as_sortable_control(get_child(i), SortableVisibilityMode::VISIBLE); if (!c) { continue; } diff --git a/scene/gui/graph_element.cpp b/scene/gui/graph_element.cpp index aadfe1f821..4df6e80a2d 100644 --- a/scene/gui/graph_element.cpp +++ b/scene/gui/graph_element.cpp @@ -61,7 +61,7 @@ void GraphElement::_resort() { Size2 GraphElement::get_minimum_size() const { Size2 minsize; for (int i = 0; i < get_child_count(); i++) { - Control *child = as_sortable_control(get_child(i), SortableVisbilityMode::IGNORE); + Control *child = as_sortable_control(get_child(i), SortableVisibilityMode::IGNORE); if (!child) { continue; } diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp index 5b82be92c7..14d2f286f4 100644 --- a/scene/gui/graph_node.cpp +++ b/scene/gui/graph_node.cpp @@ -131,7 +131,7 @@ bool GraphNode::_get(const StringName &p_name, Variant &r_ret) const { void GraphNode::_get_property_list(List *p_list) const { int idx = 0; for (int i = 0; i < get_child_count(false); i++) { - Control *child = as_sortable_control(get_child(i, false), SortableVisbilityMode::IGNORE); + Control *child = as_sortable_control(get_child(i, false), SortableVisibilityMode::IGNORE); if (!child) { continue; } @@ -659,7 +659,7 @@ void GraphNode::_port_pos_update() { int slot_index = 0; for (int i = 0; i < get_child_count(false); i++) { - Control *child = as_sortable_control(get_child(i, false), SortableVisbilityMode::IGNORE); + Control *child = as_sortable_control(get_child(i, false), SortableVisibilityMode::IGNORE); if (!child) { continue; } diff --git a/scene/gui/grid_container.cpp b/scene/gui/grid_container.cpp index 2e1266849c..ab440f18af 100644 --- a/scene/gui/grid_container.cpp +++ b/scene/gui/grid_container.cpp @@ -281,7 +281,7 @@ Size2 GridContainer::get_minimum_size() const { int valid_controls_index = 0; for (int i = 0; i < get_child_count(); i++) { - Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE); + Control *c = as_sortable_control(get_child(i), SortableVisibilityMode::VISIBLE); if (!c) { continue; } diff --git a/scene/gui/margin_container.cpp b/scene/gui/margin_container.cpp index b6d13c7235..ad61b136cc 100644 --- a/scene/gui/margin_container.cpp +++ b/scene/gui/margin_container.cpp @@ -38,7 +38,7 @@ Size2 MarginContainer::get_minimum_size() const { Size2 max; for (int i = 0; i < get_child_count(); i++) { - Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE); + Control *c = as_sortable_control(get_child(i), SortableVisibilityMode::VISIBLE); if (!c) { continue; } diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index f20fa658b3..412bfeb171 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -470,12 +470,6 @@ void OptionButton::show_popup() { return; } - Rect2 rect = get_screen_rect(); - rect.position.y += rect.size.height; - rect.size.height = 0; - popup->set_position(rect.position); - popup->set_size(rect.size); - // If not triggered by the mouse, start the popup with the checked item (or the first enabled one) focused. if (current != NONE_SELECTED && !popup->is_item_disabled(current)) { if (!_was_pressed_by_mouse()) { @@ -497,7 +491,10 @@ void OptionButton::show_popup() { } } - popup->popup(); + Rect2 rect = get_screen_rect(); + rect.position.y += rect.size.height; + rect.size.height = 0; + popup->popup(rect); } void OptionButton::_validate_property(PropertyInfo &p_property) const { diff --git a/scene/gui/panel_container.cpp b/scene/gui/panel_container.cpp index 35a8fd4d0e..f56c818e9a 100644 --- a/scene/gui/panel_container.cpp +++ b/scene/gui/panel_container.cpp @@ -37,7 +37,7 @@ Size2 PanelContainer::get_minimum_size() const { Size2 ms; for (int i = 0; i < get_child_count(); i++) { - Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE); + Control *c = as_sortable_control(get_child(i), SortableVisibilityMode::VISIBLE); if (!c) { continue; } diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index 8cd6fc63ee..1381756ee1 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -42,7 +42,7 @@ Size2 ScrollContainer::get_minimum_size() const { largest_child_min_size = Size2(); for (int i = 0; i < get_child_count(); i++) { - Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE); + Control *c = as_sortable_control(get_child(i), SortableVisibilityMode::VISIBLE); if (!c) { continue; } @@ -564,7 +564,7 @@ PackedStringArray ScrollContainer::get_configuration_warnings() const { int found = 0; for (int i = 0; i < get_child_count(); i++) { - Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE); + Control *c = as_sortable_control(get_child(i), SortableVisibilityMode::VISIBLE); if (!c) { continue; } diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp index 0a9806a254..73a9ffdfc8 100644 --- a/scene/gui/split_container.cpp +++ b/scene/gui/split_container.cpp @@ -126,7 +126,7 @@ void SplitContainerDragger::_notification(int p_what) { } } -Control *SplitContainer::_get_sortable_child(int p_idx, SortableVisbilityMode p_visibility_mode) const { +Control *SplitContainer::_get_sortable_child(int p_idx, SortableVisibilityMode p_visibility_mode) const { int idx = 0; for (int i = 0; i < get_child_count(false); i++) { Control *c = as_sortable_control(get_child(i, false), p_visibility_mode); @@ -259,7 +259,7 @@ Size2 SplitContainer::get_minimum_size() const { int sep = _get_separation(); for (int i = 0; i < 2; i++) { - Control *child = _get_sortable_child(i, SortableVisbilityMode::VISIBLE); + Control *child = _get_sortable_child(i, SortableVisibilityMode::VISIBLE); if (!child) { break; } diff --git a/scene/gui/split_container.h b/scene/gui/split_container.h index 6b4973443a..696c423b2f 100644 --- a/scene/gui/split_container.h +++ b/scene/gui/split_container.h @@ -94,7 +94,7 @@ private: void _compute_split_offset(bool p_clamp); int _get_separation() const; void _resort(); - Control *_get_sortable_child(int p_idx, SortableVisbilityMode p_visibility_mode = SortableVisbilityMode::VISIBLE_IN_TREE) const; + Control *_get_sortable_child(int p_idx, SortableVisibilityMode p_visibility_mode = SortableVisibilityMode::VISIBLE_IN_TREE) const; protected: bool is_fixed = false; diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index d73ab15134..8b2d30c599 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -367,7 +367,7 @@ void TabContainer::_on_mouse_exited() { Vector TabContainer::_get_tab_controls() const { Vector controls; for (int i = 0; i < get_child_count(); i++) { - Control *control = as_sortable_control(get_child(i), SortableVisbilityMode::IGNORE); + Control *control = as_sortable_control(get_child(i), SortableVisibilityMode::IGNORE); if (!control || control == tab_bar || children_removing.has(control)) { continue; } @@ -544,7 +544,7 @@ void TabContainer::add_child_notify(Node *p_child) { return; } - Control *c = as_sortable_control(p_child, SortableVisbilityMode::IGNORE); + Control *c = as_sortable_control(p_child, SortableVisibilityMode::IGNORE); if (!c) { return; } @@ -574,7 +574,7 @@ void TabContainer::move_child_notify(Node *p_child) { return; } - Control *c = as_sortable_control(p_child, SortableVisbilityMode::IGNORE); + Control *c = as_sortable_control(p_child, SortableVisibilityMode::IGNORE); if (c) { tab_bar->move_tab(c->get_meta("_tab_index"), get_tab_idx_from_control(c)); } @@ -589,7 +589,7 @@ void TabContainer::remove_child_notify(Node *p_child) { return; } - Control *c = as_sortable_control(p_child, SortableVisbilityMode::IGNORE); + Control *c = as_sortable_control(p_child, SortableVisibilityMode::IGNORE); if (!c) { return; } diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 95224cdade..a075b52e15 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -3453,7 +3453,8 @@ void Tree::_go_up() { return; } - select_single_item(prev, get_root(), col); + selected_item = prev; + emit_signal(SNAME("cell_selected")); queue_redraw(); } else { while (prev && !prev->cells[col].selectable) { @@ -3486,7 +3487,8 @@ void Tree::_go_down() { return; } - select_single_item(next, get_root(), col); + selected_item = next; + emit_signal(SNAME("cell_selected")); queue_redraw(); } else { while (next && !next->cells[col].selectable) { @@ -3682,15 +3684,6 @@ void Tree::gui_input(const Ref &p_event) { prev->select(selected_col); } ensure_cursor_is_visible(); - } else if (p_event->is_action("ui_accept") && p_event->is_pressed()) { - if (selected_item) { - //bring up editor if possible - if (!edit_selected()) { - emit_signal(SNAME("item_activated")); - incr_search.clear(); - } - } - accept_event(); } else if (p_event->is_action("ui_select") && p_event->is_pressed()) { if (select_mode == SELECT_MULTI) { if (!selected_item) { @@ -3705,6 +3698,15 @@ void Tree::gui_input(const Ref &p_event) { } } accept_event(); + } else if (p_event->is_action("ui_accept") && p_event->is_pressed()) { + if (selected_item) { + //bring up editor if possible + if (!edit_selected()) { + emit_signal(SNAME("item_activated")); + incr_search.clear(); + } + } + accept_event(); } if (allow_search && k.is_valid()) { // Incremental search diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index de2e6940c3..6c87d0d8dd 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -128,7 +128,10 @@ int ViewportTexture::get_width() const { _err_print_viewport_not_set(); return 0; } - return get_size().width; + if (vp->is_sub_viewport()) { + return vp->size.width; + } + return vp->size.width * vp->get_stretch_transform().get_scale().width; } int ViewportTexture::get_height() const { @@ -136,7 +139,10 @@ int ViewportTexture::get_height() const { _err_print_viewport_not_set(); return 0; } - return get_size().height; + if (vp->is_sub_viewport()) { + return vp->size.height; + } + return vp->size.height * vp->get_stretch_transform().get_scale().height; } Size2 ViewportTexture::get_size() const { @@ -144,8 +150,11 @@ Size2 ViewportTexture::get_size() const { _err_print_viewport_not_set(); return Size2(); } - float scale = MIN(vp->get_screen_transform().get_scale().width, vp->get_screen_transform().get_scale().height); - return Size2(vp->size.width * scale, vp->size.height * scale).ceil(); + if (vp->is_sub_viewport()) { + return vp->size; + } + Size2 scale = vp->get_stretch_transform().get_scale(); + return Size2(vp->size.width * scale.width, vp->size.height * scale.height).ceil(); } RID ViewportTexture::get_rid() const { diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 88760f7a1e..35bb4f46af 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -33,6 +33,7 @@ #include "window.h" #include "core/config/project_settings.h" +#include "core/debugger/engine_debugger.h" #include "core/input/shortcut.h" #include "core/string/translation_server.h" #include "scene/gui/control.h" @@ -308,6 +309,14 @@ void Window::set_title(const String &p_title) { } } emit_signal("title_changed"); + +#ifdef DEBUG_ENABLED + if (EngineDebugger::get_singleton() && window_id == DisplayServer::MAIN_WINDOW_ID && !Engine::get_singleton()->is_project_manager_hint()) { + Array arr; + arr.push_back(tr_title); + EngineDebugger::get_singleton()->send_message("window:title", arr); + } +#endif } String Window::get_title() const { diff --git a/scene/resources/2d/capsule_shape_2d.cpp b/scene/resources/2d/capsule_shape_2d.cpp index fb98517525..03b5b12829 100644 --- a/scene/resources/2d/capsule_shape_2d.cpp +++ b/scene/resources/2d/capsule_shape_2d.cpp @@ -62,6 +62,9 @@ void CapsuleShape2D::_update_shape() { void CapsuleShape2D::set_radius(real_t p_radius) { ERR_FAIL_COND_MSG(p_radius < 0, "CapsuleShape2D radius cannot be negative."); + if (radius == p_radius) { + return; + } radius = p_radius; if (radius > height * 0.5) { height = radius * 2.0; @@ -75,6 +78,9 @@ real_t CapsuleShape2D::get_radius() const { void CapsuleShape2D::set_height(real_t p_height) { ERR_FAIL_COND_MSG(p_height < 0, "CapsuleShape2D height cannot be negative."); + if (height == p_height) { + return; + } height = p_height; if (radius > height * 0.5) { radius = height * 0.5; diff --git a/scene/resources/2d/circle_shape_2d.cpp b/scene/resources/2d/circle_shape_2d.cpp index 344828cfc4..9a0b1ec686 100644 --- a/scene/resources/2d/circle_shape_2d.cpp +++ b/scene/resources/2d/circle_shape_2d.cpp @@ -46,6 +46,9 @@ void CircleShape2D::_update_shape() { void CircleShape2D::set_radius(real_t p_radius) { ERR_FAIL_COND_MSG(p_radius < 0, "CircleShape2D radius cannot be negative."); + if (radius == p_radius) { + return; + } radius = p_radius; _update_shape(); } diff --git a/scene/resources/2d/rectangle_shape_2d.cpp b/scene/resources/2d/rectangle_shape_2d.cpp index c245852dd0..189a05934c 100644 --- a/scene/resources/2d/rectangle_shape_2d.cpp +++ b/scene/resources/2d/rectangle_shape_2d.cpp @@ -61,6 +61,9 @@ bool RectangleShape2D::_get(const StringName &p_name, Variant &r_property) const void RectangleShape2D::set_size(const Size2 &p_size) { ERR_FAIL_COND_MSG(p_size.x < 0 || p_size.y < 0, "RectangleShape2D size cannot be negative."); + if (size == p_size) { + return; + } size = p_size; _update_shape(); } diff --git a/scene/resources/2d/segment_shape_2d.cpp b/scene/resources/2d/segment_shape_2d.cpp index efcc32b576..5d40cc2092 100644 --- a/scene/resources/2d/segment_shape_2d.cpp +++ b/scene/resources/2d/segment_shape_2d.cpp @@ -51,6 +51,9 @@ void SegmentShape2D::_update_shape() { } void SegmentShape2D::set_a(const Vector2 &p_a) { + if (a == p_a) { + return; + } a = p_a; _update_shape(); } @@ -60,6 +63,9 @@ Vector2 SegmentShape2D::get_a() const { } void SegmentShape2D::set_b(const Vector2 &p_b) { + if (b == p_b) { + return; + } b = p_b; _update_shape(); } diff --git a/scene/resources/2d/separation_ray_shape_2d.cpp b/scene/resources/2d/separation_ray_shape_2d.cpp index 2bae04bb20..4f26dacf7c 100644 --- a/scene/resources/2d/separation_ray_shape_2d.cpp +++ b/scene/resources/2d/separation_ray_shape_2d.cpp @@ -96,6 +96,9 @@ void SeparationRayShape2D::_bind_methods() { } void SeparationRayShape2D::set_length(real_t p_length) { + if (length == p_length) { + return; + } length = p_length; _update_shape(); } @@ -105,6 +108,9 @@ real_t SeparationRayShape2D::get_length() const { } void SeparationRayShape2D::set_slide_on_slope(bool p_active) { + if (slide_on_slope == p_active) { + return; + } slide_on_slope = p_active; _update_shape(); } diff --git a/scene/resources/3d/shape_3d.cpp b/scene/resources/3d/shape_3d.cpp index b1d636d992..0623d56057 100644 --- a/scene/resources/3d/shape_3d.cpp +++ b/scene/resources/3d/shape_3d.cpp @@ -134,9 +134,12 @@ Ref Shape3D::get_debug_mesh() { debug_mesh_cache->surface_set_material(0, material); if (debug_fill) { - Array solid_array = get_debug_arraymesh_faces(debug_color * Color(1.0, 1.0, 1.0, 0.0625))->surface_get_arrays(0); - debug_mesh_cache->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, solid_array); - debug_mesh_cache->surface_set_material(1, material); + Ref array_mesh = get_debug_arraymesh_faces(debug_color * Color(1.0, 1.0, 1.0, 0.0625)); + if (array_mesh.is_valid() && array_mesh->get_surface_count() > 0) { + Array solid_array = array_mesh->surface_get_arrays(0); + debug_mesh_cache->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, solid_array); + debug_mesh_cache->surface_set_material(1, material); + } } } diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 4c7df22497..add5084e94 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -2798,7 +2798,7 @@ void VisualShader::_update_shader() const { if (varying_setters.has(i)) { for (int &E : varying_setters[i]) { - err = _write_node(Type(i), &global_code, nullptr, nullptr, func_code, default_tex_params, input_connections, output_connections, E, processed, false, classes); + err = _write_node(Type(i), &global_code, &global_code_per_node, nullptr, func_code, default_tex_params, input_connections, output_connections, E, processed, false, classes); ERR_FAIL_COND(err != OK); } } diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index c4db457cb5..2883e57aef 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -7263,7 +7263,7 @@ String VisualShaderNodeFresnel::generate_code(Shader::Mode p_mode, VisualShader: normal = p_input_vars[0]; } if (p_input_vars[1].is_empty()) { - if (p_mode == Shader::MODE_SPATIAL) { + if (p_mode == Shader::MODE_SPATIAL && !p_for_preview) { view = "VIEW"; } else { view = "vec3(0.0)"; diff --git a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl index c4d2f8f321..3bb26d29d1 100644 --- a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl @@ -266,7 +266,7 @@ vec3 tonemap_aces(vec3 color, float 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 @@ -276,25 +276,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 @@ -302,26 +298,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; + // 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); - // 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; + // 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)); @@ -329,9 +331,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/tests/core/math/test_quaternion.h b/tests/core/math/test_quaternion.h index 321302a091..4fc5cd4454 100644 --- a/tests/core/math/test_quaternion.h +++ b/tests/core/math/test_quaternion.h @@ -265,6 +265,9 @@ TEST_CASE("[Quaternion] Construct Shortest Arc For 180 Degree Arc") { // For the consistency of the rotation direction, they should be symmetrical to the plane. CHECK(left_to_right.is_equal_approx(right_to_left.inverse())); + + // If vectors are same, no rotation. + CHECK(Quaternion(diagonal_up, diagonal_up).is_equal_approx(Quaternion())); } TEST_CASE("[Quaternion] Get Euler Orders") {