diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 713c982123..9de8b01530 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2,10 +2,6 @@ # Each line is a file pattern followed by one or more owners. # Owners can be @users, @org/teams or emails. -# Buildsystem (Before everything to be overwritten) - -* @godotengine/buildsystem - # Core /core/ @godotengine/core @@ -20,8 +16,6 @@ # Drivers -/drivers/ @godotengine/_systems - ## Audio /drivers/alsa/ @godotengine/audio /drivers/alsamidi/ @godotengine/audio @@ -42,7 +36,7 @@ /drivers/vulkan/ @godotengine/rendering ## OS -/drivers/unix/ @godotengine/_platforms +/drivers/unix/ @godotengine/linux-bsd /drivers/windows/ @godotengine/windows ## Misc @@ -50,9 +44,9 @@ # Editor -/editor/ @godotengine/_editor /editor/**/*2d* @godotengine/2d-editor /editor/**/*3d* @godotengine/3d-editor +/editor/**/*audio* @godotengine/audio /editor/**/*code* @godotengine/script-editor /editor/**/*debugger* @godotengine/debugger /editor/**/*dock* @godotengine/docks @@ -75,11 +69,6 @@ # Modules -/modules/ @godotengine/_engine -/modules/**/doc_classes/ @godotengine/_engine @godotengine/documentation -/modules/**/icons/ @godotengine/_engine @godotengine/usability -/modules/**/tests/ @godotengine/_engine @godotengine/tests - ## Audio (+ video) /modules/interactive_music/ @godotengine/audio /modules/interactive_music/doc_classes/ @godotengine/audio @godotengine/documentation @@ -95,6 +84,7 @@ ## Import /modules/astcenc/ @godotengine/import /modules/basis_universal/ @godotengine/import +/modules/bcdec/ @godotengine/import /modules/betsy/ @godotengine/import /modules/bmp/ @godotengine/import /modules/cvtt/ @godotengine/import @@ -132,6 +122,7 @@ ## Physics /modules/godot_physics_2d/ @godotengine/physics /modules/godot_physics_3d/ @godotengine/physics +/modules/jolt_physics/ @godotengine/physics ## Rendering /modules/glslang/ @godotengine/rendering @@ -190,7 +181,6 @@ # Platform -/platform/ @godotengine/_platforms /platform/android/ @godotengine/android /platform/android/doc_classes/ @godotengine/android @godotengine/documentation /platform/ios/ @godotengine/ios @@ -206,7 +196,6 @@ # Scene -/scene/ @godotengine/_systems @godotengine/core /scene/2d/ @godotengine/2d-nodes /scene/2d/physics/ @godotengine/2d-nodes @godotengine/physics /scene/3d/ @godotengine/3d-nodes @@ -216,20 +205,23 @@ /scene/debugger/ @godotengine/debugger /scene/gui/ @godotengine/gui-nodes /scene/main/ @godotengine/core -/scene/resources/font.* @godotengine/gui-nodes -/scene/resources/text_line.* @godotengine/gui-nodes -/scene/resources/text_paragraph.* @godotengine/gui-nodes -/scene/resources/visual_shader*.* @godotengine/shaders +/scene/resources/2d/ @godotengine/2d-nodes +/scene/resources/3d/ @godotengine/3d-nodes +/scene/resources/animated* @godotengine/animation +/scene/resources/animation* @godotengine/animation +/scene/resources/audio* @godotengine/audio +/scene/resources/font* @godotengine/gui-nodes +/scene/resources/shader* @godotengine/shaders +/scene/resources/text_* @godotengine/gui-nodes +/scene/resources/visual_shader* @godotengine/shaders /scene/theme/ @godotengine/gui-nodes /scene/theme/icons/ @godotengine/gui-nodes @godotengine/usability # Servers -/servers/ @godotengine/_systems /servers/**/audio_* @godotengine/audio /servers/**/camera_* @godotengine/xr /servers/**/debugger_* @godotengine/debugger -/servers/**/display_* @godotengine/_platforms /servers/**/navigation_* @godotengine/navigation /servers/**/physics_* @godotengine/physics /servers/**/rendering_* @godotengine/rendering @@ -238,7 +230,6 @@ /servers/audio/ @godotengine/audio /servers/camera/ @godotengine/xr /servers/debugger/ @godotengine/debugger -/servers/display/ @godotengine/_platforms /servers/navigation/ @godotengine/navigation /servers/rendering/ @godotengine/rendering /servers/text/ @godotengine/gui-nodes @@ -254,6 +245,7 @@ # Buildsystem (After everything to catch all) +/*.* @godotengine/buildsystem *.py @godotengine/buildsystem SConstruct @godotengine/buildsystem SCsub @godotengine/buildsystem diff --git a/.github/workflows/android_builds.yml b/.github/workflows/android_builds.yml index 950e1e51cc..2803ff4b5e 100644 --- a/.github/workflows/android_builds.yml +++ b/.github/workflows/android_builds.yml @@ -25,18 +25,21 @@ jobs: target: editor tests: false sconsflags: arch=arm64 production=yes swappy=yes + cache-limit: 1 - name: Template arm32 (target=template_release, arch=arm32) cache-name: android-template-arm32 target: template_release tests: false sconsflags: arch=arm32 swappy=yes + cache-limit: 1 - name: Template arm64 (target=template_release, arch=arm64) cache-name: android-template-arm64 target: template_release tests: false sconsflags: arch=arm64 swappy=yes + cache-limit: 1 steps: - name: Checkout @@ -77,6 +80,7 @@ jobs: platform: android target: ${{ matrix.target }} tests: ${{ matrix.tests }} + scons-cache-limit: ${{ matrix.cache-limit }} - name: Save Godot build cache uses: ./.github/actions/godot-cache-save diff --git a/.github/workflows/ios_builds.yml b/.github/workflows/ios_builds.yml index 8513429f9a..270204ebe5 100644 --- a/.github/workflows/ios_builds.yml +++ b/.github/workflows/ios_builds.yml @@ -37,6 +37,7 @@ jobs: platform: ios target: template_release tests: false + scons-cache-limit: 1 - name: Save Godot build cache uses: ./.github/actions/godot-cache-save diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml index 6016c84af4..a1412761da 100644 --- a/.github/workflows/linux_builds.yml +++ b/.github/workflows/linux_builds.yml @@ -10,6 +10,7 @@ env: DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: true TSAN_OPTIONS: suppressions=misc/error_suppressions/tsan.txt + UBSAN_OPTIONS: suppressions=misc/error_suppressions/ubsan.txt concurrency: group: ci-${{ github.actor }}-${{ github.head_ref || github.run_number }}-${{ github.ref }}-linux @@ -35,6 +36,7 @@ jobs: proj-conv: true api-compat: true artifact: true + cache-limit: 1 - name: Editor with doubles and GCC sanitizers (target=editor, tests=yes, dev_build=yes, scu_build=yes, precision=double, use_asan=yes, use_ubsan=yes, linker=gold) cache-name: linux-editor-double-sanitizers @@ -49,6 +51,7 @@ jobs: api-dump: true # Skip 2GiB artifact speeding up action. artifact: false + cache-limit: 7 - name: Editor with clang sanitizers (target=editor, tests=yes, dev_build=yes, use_asan=yes, use_ubsan=yes, use_llvm=yes, linker=lld) cache-name: linux-editor-llvm-sanitizers @@ -61,6 +64,7 @@ jobs: artifact: false # Test our oldest supported SCons/Python versions on one arbitrary editor build. legacy-scons: true + cache-limit: 7 - name: Editor with ThreadSanitizer (target=editor, tests=yes, dev_build=yes, use_tsan=yes, use_llvm=yes, linker=lld) cache-name: linux-editor-thread-sanitizer @@ -71,6 +75,7 @@ jobs: build-mono: false # Skip 2GiB artifact speeding up action. artifact: false + cache-limit: 5 - name: Template w/ Mono (target=template_release, tests=yes) cache-name: linux-template-mono @@ -80,6 +85,7 @@ jobs: build-mono: false tests: true artifact: true + cache-limit: 1 - name: Minimal template (target=template_release, tests=yes, everything disabled) cache-name: linux-template-minimal @@ -88,6 +94,7 @@ jobs: bin: ./bin/redot.linuxbsd.template_release.x86_64 tests: true artifact: true + cache-limit: 1 steps: - name: Checkout @@ -140,6 +147,7 @@ jobs: platform: linuxbsd target: ${{ matrix.target }} tests: ${{ matrix.tests }} + scons-cache-limit: ${{ matrix.cache-limit }} - name: Save Godot build cache uses: ./.github/actions/godot-cache-save @@ -155,9 +163,6 @@ jobs: - name: Build .NET solutions if: matrix.build-mono run: | - # FIXME: C# warnings should be properly handled eventually, but we don't want to clutter - # the GitHub Actions annotations, so remove the associated problem matcher for now. - echo "::remove-matcher owner=msvc::" ./modules/mono/build_scripts/build_assemblies.py --godot-output-dir=./bin --godot-platform=linuxbsd - name: Prepare artifact diff --git a/.github/workflows/macos_builds.yml b/.github/workflows/macos_builds.yml index cc789d4c4a..95ff09a114 100644 --- a/.github/workflows/macos_builds.yml +++ b/.github/workflows/macos_builds.yml @@ -25,6 +25,7 @@ jobs: target: editor tests: true bin: ./bin/redot.macos.editor.universal + cache-limit: 1 - name: Template (target=template_release, tests=yes) cache-name: macos-template @@ -32,6 +33,7 @@ jobs: tests: true sconsflags: debug_symbols=no bin: ./bin/redot.macos.template_release.universal + cache-limit: 1 steps: - name: Checkout @@ -59,6 +61,7 @@ jobs: platform: macos target: ${{ matrix.target }} tests: ${{ matrix.tests }} + scons-cache-limit: 0 # Only cap on second run to avoid purging unnecessarily - name: Compilation (arm64) uses: ./.github/actions/godot-build @@ -67,6 +70,7 @@ jobs: platform: macos target: ${{ matrix.target }} tests: ${{ matrix.tests }} + scons-cache-limit: ${{ matrix.cache-limit }} - name: Save Godot build cache uses: ./.github/actions/godot-cache-save diff --git a/.github/workflows/static_checks.yml b/.github/workflows/static_checks.yml index 9b326cb43e..e832faaca4 100644 --- a/.github/workflows/static_checks.yml +++ b/.github/workflows/static_checks.yml @@ -56,7 +56,7 @@ jobs: - name: Class reference schema checks run: | - xmllint --noout --schema doc/class.xsd doc/classes/*.xml modules/*/doc_classes/*.xml platform/*/doc_classes/*.xml + xmllint --quiet --noout --schema doc/class.xsd doc/classes/*.xml modules/*/doc_classes/*.xml platform/*/doc_classes/*.xml - name: Run C compiler on `gdextension_interface.h` run: | diff --git a/.github/workflows/web_builds.yml b/.github/workflows/web_builds.yml index 9ed8475769..dac6270b2b 100644 --- a/.github/workflows/web_builds.yml +++ b/.github/workflows/web_builds.yml @@ -67,6 +67,7 @@ jobs: platform: web target: ${{ matrix.target }} tests: ${{ matrix.tests }} + scons-cache-limit: 0.5 - name: Save Godot build cache uses: ./.github/actions/godot-cache-save diff --git a/.github/workflows/windows_builds.yml b/.github/workflows/windows_builds.yml index f93474cf24..56c57624e5 100644 --- a/.github/workflows/windows_builds.yml +++ b/.github/workflows/windows_builds.yml @@ -31,6 +31,7 @@ jobs: sconsflags: debug_symbols=no vsproj=yes vsproj_gen_only=no windows_subsystem=console bin: ./bin/redot.windows.editor.x86_64.exe compiler: msvc + cache-limit: 2 - name: Editor w/ clang-cl (target=editor, tests=yes, use_llvm=yes) cache-name: windows-editor-clang @@ -39,6 +40,7 @@ jobs: sconsflags: debug_symbols=no windows_subsystem=console use_llvm=yes bin: ./bin/redot.windows.editor.x86_64.llvm.exe compiler: clang + cache-limit: 1 - name: Template (target=template_release, tests=yes) cache-name: windows-template @@ -47,6 +49,7 @@ jobs: sconsflags: debug_symbols=no bin: ./bin/redot.windows.template_release.x86_64.console.exe compiler: msvc + cache-limit: 2 - name: Template w/ GCC (target=template_release, tests=yes, use_mingw=yes) cache-name: windows-template-gcc @@ -56,6 +59,7 @@ jobs: sconsflags: debug_symbols=no use_mingw=yes bin: ./bin/redot.windows.template_release.x86_64.console.exe compiler: gcc + cache-limit: 1 steps: - name: Checkout @@ -93,6 +97,7 @@ jobs: platform: windows target: ${{ matrix.target }} tests: ${{ matrix.tests }} + scons-cache-limit: ${{ matrix.cache-limit }} - name: Generate Glue Code if: ${{ matrix.cache-name == 'windows-editor-mono' }} diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index fd4d98fad0..7b07592764 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -108,6 +108,13 @@ Copyright: 2007, Starbreeze Studios 2007-2014, Juan Linietsky, Ariel Manzur License: Expat and Zlib +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 Comment: Joint Non-Local Means (JNLM) denoiser Copyright: 2020, Manuel Prandini @@ -303,6 +310,11 @@ Comment: International Components for Unicode Copyright: 2016-2024, Unicode, Inc. License: Unicode +Files: ./thirdparty/jolt_physics/ +Comment: Jolt Physics +Copyright: 2021, Jorrit Rouwe +License: Expat + Files: ./thirdparty/jpeg-compressor/ Comment: jpeg-compressor Copyright: 2012, Rich Geldreich diff --git a/SConstruct b/SConstruct index d6131bc7d5..e372638fc3 100644 --- a/SConstruct +++ b/SConstruct @@ -501,14 +501,19 @@ else: # Disable assert() for production targets (only used in thirdparty code). env.Append(CPPDEFINES=["NDEBUG"]) +# This is not part of fast_unsafe because the only downside it has compared to +# the default is that SCons won't mark files that were changed in the last second +# as different. This is unlikely to be a problem in any real situation as just booting +# up scons takes more than that time. +# Renamed to `content-timestamp` in SCons >= 4.2, keeping MD5 for compat. +env.Decider("MD5-timestamp") + # SCons speed optimization controlled by the `fast_unsafe` option, which provide # more than 10 s speed up for incremental rebuilds. # Unsafe as they reduce the certainty of rebuilding all changed files, so it's # enabled by default for `debug` builds, and can be overridden from command line. # Ref: https://github.com/SCons/scons/wiki/GoFastButton if methods.get_cmdline_bool("fast_unsafe", env.dev_build): - # Renamed to `content-timestamp` in SCons >= 4.2, keeping MD5 for compat. - env.Decider("MD5-timestamp") env.SetOption("implicit_cache", 1) env.SetOption("max_drift", 60) @@ -708,6 +713,12 @@ elif env.msvc: ) Exit(255) +# Default architecture flags. +if env["arch"] == "x86_32": + if env.msvc: + env.Append(CCFLAGS=["/arch:SSE2"]) + else: + env.Append(CCFLAGS=["-msse2"]) # Set optimize and debug_symbols flags. # "custom" means do nothing and let users set their own optimization flags. @@ -731,9 +742,15 @@ if env.msvc: env.Append(CCFLAGS=["/Od"]) else: if env["debug_symbols"]: - # Adding dwarf-4 explicitly makes stacktraces work with clang builds, - # otherwise addr2line doesn't understand them - env.Append(CCFLAGS=["-gdwarf-4"]) + if env["platform"] == "windows": + if methods.using_clang(env): + env.Append(CCFLAGS=["-gdwarf-4"]) # clang dwarf-5 symbols are broken on Windows. + else: + env.Append(CCFLAGS=["-gdwarf-5"]) # For gcc, only dwarf-5 symbols seem usable by libbacktrace. + else: + # Adding dwarf-4 explicitly makes stacktraces work with clang builds, + # otherwise addr2line doesn't understand them + env.Append(CCFLAGS=["-gdwarf-4"]) if methods.using_emcc(env): # Emscripten only produces dwarf symbols when using "-g3". env.Append(CCFLAGS=["-g3"]) @@ -815,37 +832,36 @@ elif env.msvc: # Configure compiler warnings if env.msvc and not methods.using_clang(env): # MSVC - if env["warnings"] == "no": - env.Append(CCFLAGS=["/w"]) - else: - if env["warnings"] == "extra": - env.Append(CCFLAGS=["/W4"]) - elif env["warnings"] == "all": - # C4458 is like -Wshadow. Part of /W4 but let's apply it for the default /W3 too. - env.Append(CCFLAGS=["/W3", "/w34458"]) - elif env["warnings"] == "moderate": - env.Append(CCFLAGS=["/W2"]) - # Disable warnings which we don't plan to fix. + # Disable warnings which we don't plan to fix. + disabled_warnings = [ + "/wd4100", # C4100 (unreferenced formal parameter): Doesn't play nice with polymorphism. + "/wd4127", # C4127 (conditional expression is constant) + "/wd4201", # C4201 (non-standard nameless struct/union): Only relevant for C89. + "/wd4244", # C4244 C4245 C4267 (narrowing conversions): Unavoidable at this scale. + "/wd4245", + "/wd4267", + "/wd4305", # C4305 (truncation): double to float or real_t, too hard to avoid. + "/wd4324", # C4820 (structure was padded due to alignment specifier) + "/wd4514", # C4514 (unreferenced inline function has been removed) + "/wd4714", # C4714 (function marked as __forceinline not inlined) + "/wd4820", # C4820 (padding added after construct) + ] - env.Append( - CCFLAGS=[ - "/wd4100", # C4100 (unreferenced formal parameter): Doesn't play nice with polymorphism. - "/wd4127", # C4127 (conditional expression is constant) - "/wd4201", # C4201 (non-standard nameless struct/union): Only relevant for C89. - "/wd4244", # C4244 C4245 C4267 (narrowing conversions): Unavoidable at this scale. - "/wd4245", - "/wd4267", - "/wd4305", # C4305 (truncation): double to float or real_t, too hard to avoid. - "/wd4324", # C4820 (structure was padded due to alignment specifier) - "/wd4514", # C4514 (unreferenced inline function has been removed) - "/wd4714", # C4714 (function marked as __forceinline not inlined) - "/wd4820", # C4820 (padding added after construct) - ] - ) + if env["warnings"] == "extra": + env.Append(CCFLAGS=["/W4"] + disabled_warnings) + elif env["warnings"] == "all": + # C4458 is like -Wshadow. Part of /W4 but let's apply it for the default /W3 too. + env.Append(CCFLAGS=["/W3", "/w34458"] + disabled_warnings) + elif env["warnings"] == "moderate": + env.Append(CCFLAGS=["/W2"] + disabled_warnings) + else: # 'no' + # C4267 is particularly finicky & needs to be explicitly disabled. + env.Append(CCFLAGS=["/w", "/wd4267"]) if env["werror"]: env.Append(CCFLAGS=["/WX"]) env.Append(LINKFLAGS=["/WX"]) + else: # GCC, Clang common_warnings = [] diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 070b007921..e9ad2a3544 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -1565,6 +1565,7 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/staging_buffer/block_size_kb", PROPERTY_HINT_RANGE, "4,2048,1,or_greater"), 256); GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/staging_buffer/max_size_mb", PROPERTY_HINT_RANGE, "1,1024,1,or_greater"), 128); GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/staging_buffer/texture_upload_region_size_px", PROPERTY_HINT_RANGE, "1,256,1,or_greater"), 64); + GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/staging_buffer/texture_download_region_size_px", PROPERTY_HINT_RANGE, "1,256,1,or_greater"), 64); GLOBAL_DEF_RST(PropertyInfo(Variant::BOOL, "rendering/rendering_device/pipeline_cache/enable"), true); GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/rendering_device/pipeline_cache/save_chunk_size_mb", PROPERTY_HINT_RANGE, "0.000001,64.0,0.001,or_greater"), 3.0); GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/vulkan/max_descriptors_per_pool", PROPERTY_HINT_RANGE, "1,256,1,or_greater"), 64); diff --git a/core/input/input.cpp b/core/input/input.cpp index 166b8292ff..1322dd559c 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -310,6 +310,26 @@ bool Input::is_anything_pressed() const { return false; } +bool Input::is_anything_pressed_except_mouse() const { + _THREAD_SAFE_METHOD_ + + if (disable_input) { + return false; + } + + if (!keys_pressed.is_empty() || !joy_buttons_pressed.is_empty()) { + return true; + } + + for (const KeyValue &E : action_states) { + if (E.value.cache.pressed) { + return true; + } + } + + return false; +} + bool Input::is_key_pressed(Key p_keycode) const { _THREAD_SAFE_METHOD_ diff --git a/core/input/input.h b/core/input/input.h index a4c8bd1d6a..2c25d5fcd7 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -296,6 +296,7 @@ public: static Input *get_singleton(); bool is_anything_pressed() const; + bool is_anything_pressed_except_mouse() const; bool is_key_pressed(Key p_keycode) const; bool is_physical_key_pressed(Key p_keycode) const; bool is_key_label_pressed(Key p_keycode) const; diff --git a/core/io/image.cpp b/core/io/image.cpp index 8a5e6abbbd..fa8a43203e 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -574,21 +574,18 @@ static bool _are_formats_compatible(Image::Format p_format0, Image::Format p_for void Image::convert(Format p_new_format) { ERR_FAIL_INDEX_MSG(p_new_format, FORMAT_MAX, vformat("The Image format specified (%d) is out of range. See Image's Format enum.", p_new_format)); - if (data.size() == 0) { + + if (data.size() == 0 || p_new_format == format) { return; } - if (p_new_format == format) { - return; - } + ERR_FAIL_COND_MSG(Image::is_format_compressed(format) || Image::is_format_compressed(p_new_format), + "Cannot convert to (or from) compressed formats. Use compress() and decompress() instead."); // Includes the main image. const int mipmap_count = get_mipmap_count() + 1; - if (Image::is_format_compressed(format) || Image::is_format_compressed(p_new_format)) { - ERR_FAIL_MSG("Cannot convert to <-> from compressed formats. Use compress() and decompress() instead."); - - } else if (!_are_formats_compatible(format, p_new_format)) { + if (!_are_formats_compatible(format, p_new_format)) { // Use put/set pixel which is slower but works with non-byte formats. Image new_img(width, height, mipmaps, p_new_format); @@ -614,9 +611,10 @@ void Image::convert(Format p_new_format) { return; } + // Convert the formats in an optimized way by removing/adding color channels if necessary. Image new_img(width, height, mipmaps, p_new_format); - int conversion_type = format | p_new_format << 8; + const int conversion_type = format | p_new_format << 8; for (int mip = 0; mip < mipmap_count; mip++) { int64_t mip_offset = 0; @@ -1803,7 +1801,7 @@ template static void _generate_po2_mipmap(const Component *p_src, Component *p_dst, uint32_t p_width, uint32_t p_height) { - //fast power of 2 mipmap generation + // Fast power of 2 mipmap generation. uint32_t dst_w = MAX(p_width >> 1, 1u); uint32_t dst_h = MAX(p_height >> 1, 1u); @@ -1833,99 +1831,116 @@ static void _generate_po2_mipmap(const Component *p_src, Component *p_dst, uint3 } } +void Image::_generate_mipmap_from_format(Image::Format p_format, const uint8_t *p_src, uint8_t *p_dst, uint32_t p_width, uint32_t p_height, bool p_renormalize) { + const float *src_float = reinterpret_cast(p_src); + float *dst_float = reinterpret_cast(p_dst); + + const uint16_t *src_u16 = reinterpret_cast(p_src); + uint16_t *dst_u16 = reinterpret_cast(p_dst); + + const uint32_t *src_u32 = reinterpret_cast(p_src); + uint32_t *dst_u32 = reinterpret_cast(p_dst); + + switch (p_format) { + case Image::FORMAT_L8: + case Image::FORMAT_R8: + _generate_po2_mipmap(p_src, p_dst, p_width, p_height); + break; + case Image::FORMAT_LA8: + _generate_po2_mipmap(p_src, p_dst, p_width, p_height); + break; + case Image::FORMAT_RG8: + _generate_po2_mipmap(p_src, p_dst, p_width, p_height); + break; + case Image::FORMAT_RGB8: { + if (p_renormalize) { + _generate_po2_mipmap(p_src, p_dst, p_width, p_height); + } else { + _generate_po2_mipmap(p_src, p_dst, p_width, p_height); + } + } break; + case Image::FORMAT_RGBA8: { + if (p_renormalize) { + _generate_po2_mipmap(p_src, p_dst, p_width, p_height); + } else { + _generate_po2_mipmap(p_src, p_dst, p_width, p_height); + } + } break; + case Image::FORMAT_RF: + _generate_po2_mipmap(src_float, dst_float, p_width, p_height); + break; + case Image::FORMAT_RGF: + _generate_po2_mipmap(src_float, dst_float, p_width, p_height); + break; + case Image::FORMAT_RGBF: { + if (p_renormalize) { + _generate_po2_mipmap(src_float, dst_float, p_width, p_height); + } else { + _generate_po2_mipmap(src_float, dst_float, p_width, p_height); + } + } break; + case Image::FORMAT_RGBAF: { + if (p_renormalize) { + _generate_po2_mipmap(src_float, dst_float, p_width, p_height); + } else { + _generate_po2_mipmap(src_float, dst_float, p_width, p_height); + } + } break; + case Image::FORMAT_RH: + _generate_po2_mipmap(src_u16, dst_u16, p_width, p_height); + break; + case Image::FORMAT_RGH: + _generate_po2_mipmap(src_u16, dst_u16, p_width, p_height); + break; + case Image::FORMAT_RGBH: { + if (p_renormalize) { + _generate_po2_mipmap(src_u16, dst_u16, p_width, p_height); + } else { + _generate_po2_mipmap(src_u16, dst_u16, p_width, p_height); + } + } break; + case Image::FORMAT_RGBAH: { + if (p_renormalize) { + _generate_po2_mipmap(src_u16, dst_u16, p_width, p_height); + } else { + _generate_po2_mipmap(src_u16, dst_u16, p_width, p_height); + } + } break; + case Image::FORMAT_RGBE9995: + _generate_po2_mipmap(src_u32, dst_u32, p_width, p_height); + break; + + default: + return; + } +} + void Image::shrink_x2() { ERR_FAIL_COND(data.is_empty()); + Vector new_data; if (mipmaps) { - //just use the lower mipmap as base and copy all - Vector new_img; + // Just use the lower mipmap as base and copy all. + int64_t ofs = get_mipmap_offset(1); + int64_t new_size = data.size() - ofs; - int ofs = get_mipmap_offset(1); - - int new_size = data.size() - ofs; - new_img.resize(new_size); - ERR_FAIL_COND(new_img.is_empty()); - - { - uint8_t *w = new_img.ptrw(); - const uint8_t *r = data.ptr(); - - memcpy(w, &r[ofs], new_size); - } - - width = MAX(width / 2, 1); - height = MAX(height / 2, 1); - data = new_img; + new_data.resize(new_size); + ERR_FAIL_COND(new_data.is_empty()); + memcpy(new_data.ptrw(), data.ptr() + ofs, new_size); } else { - Vector new_img; - + // Generate a mipmap and replace the original. ERR_FAIL_COND(!_can_modify(format)); - int ps = get_format_pixel_size(format); - new_img.resize((width / 2) * (height / 2) * ps); - ERR_FAIL_COND(new_img.is_empty()); - ERR_FAIL_COND(data.is_empty()); - { - uint8_t *w = new_img.ptrw(); - const uint8_t *r = data.ptr(); + new_data.resize((width / 2) * (height / 2) * get_format_pixel_size(format)); + ERR_FAIL_COND(data.is_empty() || new_data.is_empty()); - switch (format) { - case FORMAT_L8: - case FORMAT_R8: - _generate_po2_mipmap(r, w, width, height); - break; - case FORMAT_LA8: - _generate_po2_mipmap(r, w, width, height); - break; - case FORMAT_RG8: - _generate_po2_mipmap(r, w, width, height); - break; - case FORMAT_RGB8: - _generate_po2_mipmap(r, w, width, height); - break; - case FORMAT_RGBA8: - _generate_po2_mipmap(r, w, width, height); - break; - - case FORMAT_RF: - _generate_po2_mipmap(reinterpret_cast(r), reinterpret_cast(w), width, height); - break; - case FORMAT_RGF: - _generate_po2_mipmap(reinterpret_cast(r), reinterpret_cast(w), width, height); - break; - case FORMAT_RGBF: - _generate_po2_mipmap(reinterpret_cast(r), reinterpret_cast(w), width, height); - break; - case FORMAT_RGBAF: - _generate_po2_mipmap(reinterpret_cast(r), reinterpret_cast(w), width, height); - break; - - case FORMAT_RH: - _generate_po2_mipmap(reinterpret_cast(r), reinterpret_cast(w), width, height); - break; - case FORMAT_RGH: - _generate_po2_mipmap(reinterpret_cast(r), reinterpret_cast(w), width, height); - break; - case FORMAT_RGBH: - _generate_po2_mipmap(reinterpret_cast(r), reinterpret_cast(w), width, height); - break; - case FORMAT_RGBAH: - _generate_po2_mipmap(reinterpret_cast(r), reinterpret_cast(w), width, height); - break; - - case FORMAT_RGBE9995: - _generate_po2_mipmap(reinterpret_cast(r), reinterpret_cast(w), width, height); - break; - default: { - } - } - } - - width /= 2; - height /= 2; - data = new_img; + _generate_mipmap_from_format(format, data.ptr(), new_data.ptrw(), width, height, false); } + + width = MAX(width / 2, 1); + height = MAX(height / 2, 1); + data = new_data; } void Image::normalize() { @@ -1953,107 +1968,25 @@ void Image::normalize() { Error Image::generate_mipmaps(bool p_renormalize) { ERR_FAIL_COND_V_MSG(!_can_modify(format), ERR_UNAVAILABLE, "Cannot generate mipmaps in compressed or custom image formats."); - ERR_FAIL_COND_V_MSG(format == FORMAT_RGBA4444, ERR_UNAVAILABLE, "Cannot generate mipmaps from RGBA4444 format."); - ERR_FAIL_COND_V_MSG(width == 0 || height == 0, ERR_UNCONFIGURED, "Cannot generate mipmaps with width or height equal to 0."); - int mmcount; - - int size = _get_dst_image_size(width, height, format, mmcount); + int gen_mipmap_count; + int64_t size = _get_dst_image_size(width, height, format, gen_mipmap_count); data.resize(size); - uint8_t *wp = data.ptrw(); int prev_ofs = 0; int prev_h = height; int prev_w = width; - for (int i = 1; i <= mmcount; i++) { + for (int i = 1; i <= gen_mipmap_count; i++) { int64_t ofs; int w, h; _get_mipmap_offset_and_size(i, ofs, w, h); - switch (format) { - case FORMAT_L8: - case FORMAT_R8: - _generate_po2_mipmap(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); - break; - case FORMAT_LA8: - case FORMAT_RG8: - _generate_po2_mipmap(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); - break; - case FORMAT_RGB8: - if (p_renormalize) { - _generate_po2_mipmap(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); - } else { - _generate_po2_mipmap(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); - } - - break; - case FORMAT_RGBA8: - if (p_renormalize) { - _generate_po2_mipmap(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); - } else { - _generate_po2_mipmap(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); - } - break; - case FORMAT_RF: - _generate_po2_mipmap(reinterpret_cast(&wp[prev_ofs]), reinterpret_cast(&wp[ofs]), prev_w, prev_h); - break; - case FORMAT_RGF: - _generate_po2_mipmap(reinterpret_cast(&wp[prev_ofs]), reinterpret_cast(&wp[ofs]), prev_w, prev_h); - break; - case FORMAT_RGBF: - if (p_renormalize) { - _generate_po2_mipmap(reinterpret_cast(&wp[prev_ofs]), reinterpret_cast(&wp[ofs]), prev_w, prev_h); - } else { - _generate_po2_mipmap(reinterpret_cast(&wp[prev_ofs]), reinterpret_cast(&wp[ofs]), prev_w, prev_h); - } - - break; - case FORMAT_RGBAF: - if (p_renormalize) { - _generate_po2_mipmap(reinterpret_cast(&wp[prev_ofs]), reinterpret_cast(&wp[ofs]), prev_w, prev_h); - } else { - _generate_po2_mipmap(reinterpret_cast(&wp[prev_ofs]), reinterpret_cast(&wp[ofs]), prev_w, prev_h); - } - - break; - case FORMAT_RH: - _generate_po2_mipmap(reinterpret_cast(&wp[prev_ofs]), reinterpret_cast(&wp[ofs]), prev_w, prev_h); - break; - case FORMAT_RGH: - _generate_po2_mipmap(reinterpret_cast(&wp[prev_ofs]), reinterpret_cast(&wp[ofs]), prev_w, prev_h); - break; - case FORMAT_RGBH: - if (p_renormalize) { - _generate_po2_mipmap(reinterpret_cast(&wp[prev_ofs]), reinterpret_cast(&wp[ofs]), prev_w, prev_h); - } else { - _generate_po2_mipmap(reinterpret_cast(&wp[prev_ofs]), reinterpret_cast(&wp[ofs]), prev_w, prev_h); - } - - break; - case FORMAT_RGBAH: - if (p_renormalize) { - _generate_po2_mipmap(reinterpret_cast(&wp[prev_ofs]), reinterpret_cast(&wp[ofs]), prev_w, prev_h); - } else { - _generate_po2_mipmap(reinterpret_cast(&wp[prev_ofs]), reinterpret_cast(&wp[ofs]), prev_w, prev_h); - } - - break; - case FORMAT_RGBE9995: - if (p_renormalize) { - _generate_po2_mipmap(reinterpret_cast(&wp[prev_ofs]), reinterpret_cast(&wp[ofs]), prev_w, prev_h); - } else { - _generate_po2_mipmap(reinterpret_cast(&wp[prev_ofs]), reinterpret_cast(&wp[ofs]), prev_w, prev_h); - } - - break; - default: { - } - } + _generate_mipmap_from_format(format, wp + prev_ofs, wp + ofs, prev_w, prev_h, p_renormalize); prev_ofs = ofs; prev_w = w; @@ -2082,8 +2015,7 @@ Error Image::generate_mipmap_roughness(RoughnessChannel p_roughness_channel, con normal_sat_vec.resize(normal_w * normal_h * 3); double *normal_sat = normal_sat_vec.ptr(); - //create summed area table - + // Create summed area table. for (int y = 0; y < normal_h; y++) { double line_sum[3] = { 0, 0, 0 }; for (int x = 0; x < normal_w; x++) { @@ -2112,15 +2044,6 @@ Error Image::generate_mipmap_roughness(RoughnessChannel p_roughness_channel, con } } -#if 0 - { - Vector3 beg(normal_sat_vec[0], normal_sat_vec[1], normal_sat_vec[2]); - Vector3 end(normal_sat_vec[normal_sat_vec.size() - 3], normal_sat_vec[normal_sat_vec.size() - 2], normal_sat_vec[normal_sat_vec.size() - 1]); - Vector3 avg = (end - beg) / (normal_w * normal_h); - print_line("average: " + avg); - } -#endif - int mmcount; _get_dst_image_size(width, height, format, mmcount); @@ -2522,8 +2445,7 @@ void Image::initialize_data(const char **p_xpm) { } bool Image::is_invisible() const { - if (format == FORMAT_L8 || - format == FORMAT_RGB8 || format == FORMAT_RG8) { + if (format == FORMAT_L8 || format == FORMAT_RGB8 || format == FORMAT_RG8) { return false; } @@ -2786,9 +2708,7 @@ Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels return OK; } } - } break; - case COMPRESS_S3TC: { if (_image_compress_bc_rd_func) { Error result = _image_compress_bc_rd_func(this, p_channels); @@ -2798,7 +2718,6 @@ Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels return OK; } } - } break; default: { @@ -3315,6 +3234,7 @@ Color Image::_get_color_at_ofs(const uint8_t *ptr, uint32_t ofs) const { case FORMAT_RGBE9995: { return Color::from_rgbe9995(((uint32_t *)ptr)[ofs]); } + default: { ERR_FAIL_V_MSG(Color(), "Can't get_pixel() on compressed image, sorry."); } @@ -3347,7 +3267,6 @@ void Image::_set_color_at_ofs(uint8_t *ptr, uint32_t ofs, const Color &p_color) ptr[ofs * 4 + 1] = uint8_t(CLAMP(p_color.g * 255.0, 0, 255)); ptr[ofs * 4 + 2] = uint8_t(CLAMP(p_color.b * 255.0, 0, 255)); ptr[ofs * 4 + 3] = uint8_t(CLAMP(p_color.a * 255.0, 0, 255)); - } break; case FORMAT_RGBA4444: { uint16_t rgba = 0; @@ -3358,7 +3277,6 @@ void Image::_set_color_at_ofs(uint8_t *ptr, uint32_t ofs, const Color &p_color) rgba |= uint16_t(CLAMP(p_color.a * 15.0, 0, 15)); ((uint16_t *)ptr)[ofs] = rgba; - } break; case FORMAT_RGB565: { uint16_t rgba = 0; @@ -3368,7 +3286,6 @@ void Image::_set_color_at_ofs(uint8_t *ptr, uint32_t ofs, const Color &p_color) rgba |= uint16_t(CLAMP(p_color.b * 31.0, 0, 31)) << 11; ((uint16_t *)ptr)[ofs] = rgba; - } break; case FORMAT_RF: { ((float *)ptr)[ofs] = p_color.r; @@ -3408,8 +3325,8 @@ void Image::_set_color_at_ofs(uint8_t *ptr, uint32_t ofs, const Color &p_color) } break; case FORMAT_RGBE9995: { ((uint32_t *)ptr)[ofs] = p_color.to_rgbe9995(); - } break; + default: { ERR_FAIL_MSG("Can't set_pixel() on compressed image, sorry."); } @@ -3477,30 +3394,50 @@ void Image::adjust_bcs(float p_brightness, float p_contrast, float p_saturation) Image::UsedChannels Image::detect_used_channels(CompressSource p_source) const { ERR_FAIL_COND_V(data.is_empty(), USED_CHANNELS_RGBA); ERR_FAIL_COND_V(is_compressed(), USED_CHANNELS_RGBA); + + if (p_source == COMPRESS_SOURCE_NORMAL) { + return USED_CHANNELS_RG; // Normal maps only use RG channels. + } + + if (format == FORMAT_L8) { + return USED_CHANNELS_L; // Grayscale only cannot have any channel less. + } else if (format == FORMAT_R8 || format == FORMAT_RH || format == FORMAT_RF) { + return USED_CHANNELS_R; // Red only cannot have any channel less. + } + + const bool supports_alpha = format == FORMAT_RGBA8 || format == FORMAT_RGBA4444 || format == FORMAT_RGBAH || format == FORMAT_RGBAF; bool r = false, g = false, b = false, a = false, c = false; const uint8_t *data_ptr = data.ptr(); - - uint32_t data_total = width * height; + const uint32_t data_total = width * height; for (uint32_t i = 0; i < data_total; i++) { Color col = _get_color_at_ofs(data_ptr, i); - if (col.r > 0.001) { + if (!r && col.r > 0.001) { r = true; } - if (col.g > 0.001) { + if (!g && col.g > 0.001) { g = true; } - if (col.b > 0.001) { + if (!b && col.b > 0.001) { b = true; } - if (col.a < 0.999) { + if (!a && col.a < 0.999) { a = true; } if (col.r != col.b || col.r != col.g || col.b != col.g) { - c = true; + c = true; // The image is not grayscale. + } + + if (r && g && b && c) { + // All channels are used, no need to continue. + if (!supports_alpha) { + break; + } else if (a) { + break; + } } } @@ -3521,13 +3458,7 @@ Image::UsedChannels Image::detect_used_channels(CompressSource p_source) const { } if (p_source == COMPRESS_SOURCE_SRGB && (used_channels == USED_CHANNELS_R || used_channels == USED_CHANNELS_RG)) { - //R and RG do not support SRGB - used_channels = USED_CHANNELS_RGB; - } - - if (p_source == COMPRESS_SOURCE_NORMAL) { - //use RG channels only for normal - used_channels = USED_CHANNELS_RG; + used_channels = USED_CHANNELS_RGB; // R and RG do not support SRGB. } return used_channels; @@ -3732,14 +3663,6 @@ void Image::_bind_methods() { BIND_ENUM_CONSTANT(ASTC_FORMAT_8x8); } -void Image::set_compress_bc_func(void (*p_compress_func)(Image *, UsedChannels)) { - _image_compress_bc_func = p_compress_func; -} - -void Image::set_compress_bptc_func(void (*p_compress_func)(Image *, UsedChannels)) { - _image_compress_bptc_func = p_compress_func; -} - void Image::normal_map_to_xy() { convert(Image::FORMAT_RGBA8); diff --git a/core/io/image.h b/core/io/image.h index d11e9b9c9c..8c607e8324 100644 --- a/core/io/image.h +++ b/core/io/image.h @@ -268,6 +268,8 @@ private: Error _load_from_buffer(const Vector &p_array, ImageMemLoadFunc p_loader); + _FORCE_INLINE_ void _generate_mipmap_from_format(Image::Format p_format, const uint8_t *p_src, uint8_t *p_dst, uint32_t p_width, uint32_t p_height, bool p_renormalize = false); + static void average_4_uint8(uint8_t &p_out, const uint8_t &p_a, const uint8_t &p_b, const uint8_t &p_c, const uint8_t &p_d); static void average_4_float(float &p_out, const float &p_a, const float &p_b, const float &p_c, const float &p_d); static void average_4_half(uint16_t &p_out, const uint16_t &p_a, const uint16_t &p_b, const uint16_t &p_c, const uint16_t &p_d); @@ -395,8 +397,6 @@ public: Rect2i get_used_rect() const; Ref get_region(const Rect2i &p_area) const; - static void set_compress_bc_func(void (*p_compress_func)(Image *, UsedChannels)); - static void set_compress_bptc_func(void (*p_compress_func)(Image *, UsedChannels)); static String get_format_name(Format p_format); Error load_png_from_buffer(const Vector &p_array); diff --git a/core/io/json.cpp b/core/io/json.cpp index 989b292cbd..1f70a39cbc 100644 --- a/core/io/json.cpp +++ b/core/io/json.cpp @@ -33,7 +33,8 @@ #include "json.h" #include "core/config/engine.h" -#include "core/string/print_string.h" +#include "core/object/script_language.h" +#include "core/variant/container_type_validate.h" const char *JSON::tk_name[TK_MAX] = { "'{'", @@ -565,18 +566,18 @@ String JSON::get_parsed_text() const { } String JSON::stringify(const Variant &p_var, const String &p_indent, bool p_sort_keys, bool p_full_precision) { - Ref jason; - jason.instantiate(); + Ref json; + json.instantiate(); HashSet markers; - return jason->_stringify(p_var, p_indent, 0, p_sort_keys, markers, p_full_precision); + return json->_stringify(p_var, p_indent, 0, p_sort_keys, markers, p_full_precision); } Variant JSON::parse_string(const String &p_json_string) { - Ref jason; - jason.instantiate(); - Error error = jason->parse(p_json_string); - ERR_FAIL_COND_V_MSG(error != Error::OK, Variant(), vformat("Parse JSON failed. Error at line %d: %s", jason->get_error_line(), jason->get_error_message())); - return jason->get_data(); + Ref json; + json.instantiate(); + Error error = json->parse(p_json_string); + ERR_FAIL_COND_V_MSG(error != Error::OK, Variant(), vformat("Parse JSON failed. Error at line %d: %s", json->get_error_line(), json->get_error_message())); + return json->get_data(); } void JSON::_bind_methods() { @@ -590,756 +591,1015 @@ void JSON::_bind_methods() { ClassDB::bind_method(D_METHOD("get_error_line"), &JSON::get_error_line); ClassDB::bind_method(D_METHOD("get_error_message"), &JSON::get_error_message); - ClassDB::bind_static_method("JSON", D_METHOD("to_native", "json", "allow_classes", "allow_scripts"), &JSON::to_native, DEFVAL(false), DEFVAL(false)); - ClassDB::bind_static_method("JSON", D_METHOD("from_native", "variant", "allow_classes", "allow_scripts"), &JSON::from_native, DEFVAL(false), DEFVAL(false)); + ClassDB::bind_static_method("JSON", D_METHOD("from_native", "variant", "full_objects"), &JSON::from_native, DEFVAL(false)); + ClassDB::bind_static_method("JSON", D_METHOD("to_native", "json", "allow_objects"), &JSON::to_native, DEFVAL(false)); ADD_PROPERTY(PropertyInfo(Variant::NIL, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT), "set_data", "get_data"); // Ensures that it can be serialized as binary. } -#define GDTYPE "__gdtype" -#define VALUES "values" -#define PASS_ARG p_allow_classes, p_allow_scripts +#define TYPE "type" +#define ELEM_TYPE "elem_type" +#define KEY_TYPE "key_type" +#define VALUE_TYPE "value_type" +#define ARGS "args" +#define PROPS "props" + +static bool _encode_container_type(Dictionary &r_dict, const String &p_key, const ContainerType &p_type, bool p_full_objects) { + if (p_type.builtin_type != Variant::NIL) { + if (p_type.script.is_valid()) { + ERR_FAIL_COND_V(!p_full_objects, false); + const String path = p_type.script->get_path(); + ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://"), false, "Failed to encode a path to a custom script for a container type."); + r_dict[p_key] = path; + } else if (p_type.class_name != StringName()) { + ERR_FAIL_COND_V(!p_full_objects, false); + r_dict[p_key] = String(p_type.class_name); + } else { + // No need to check `p_full_objects` since `class_name` should be non-empty for `builtin_type == Variant::OBJECT`. + r_dict[p_key] = Variant::get_type_name(p_type.builtin_type); + } + } + return true; +} + +Variant JSON::_from_native(const Variant &p_variant, bool p_full_objects, int p_depth) { +#define RETURN_ARGS \ + Dictionary ret; \ + ret[TYPE] = Variant::get_type_name(p_variant.get_type()); \ + ret[ARGS] = args; \ + return ret -Variant JSON::from_native(const Variant &p_variant, bool p_allow_classes, bool p_allow_scripts) { switch (p_variant.get_type()) { - case Variant::NIL: { - Dictionary nil; - nil[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return nil; - } break; + case Variant::NIL: case Variant::BOOL: { return p_variant; } break; + case Variant::INT: { - return p_variant; + return "i:" + String(p_variant); } break; case Variant::FLOAT: { - return p_variant; + return "f:" + String(p_variant); } break; case Variant::STRING: { - return p_variant; - } break; - case Variant::VECTOR2: { - Dictionary d; - Vector2 v = p_variant; - Array values; - values.push_back(v.x); - values.push_back(v.y); - d[VALUES] = values; - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; - } break; - case Variant::VECTOR2I: { - Dictionary d; - Vector2i v = p_variant; - Array values; - values.push_back(v.x); - values.push_back(v.y); - d[VALUES] = values; - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; - } break; - case Variant::RECT2: { - Dictionary d; - Rect2 r = p_variant; - d["position"] = from_native(r.position); - d["size"] = from_native(r.size); - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; - } break; - case Variant::RECT2I: { - Dictionary d; - Rect2i r = p_variant; - d["position"] = from_native(r.position); - d["size"] = from_native(r.size); - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; - } break; - case Variant::VECTOR3: { - Dictionary d; - Vector3 v = p_variant; - Array values; - values.push_back(v.x); - values.push_back(v.y); - values.push_back(v.z); - d[VALUES] = values; - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; - } break; - case Variant::VECTOR3I: { - Dictionary d; - Vector3i v = p_variant; - Array values; - values.push_back(v.x); - values.push_back(v.y); - values.push_back(v.z); - d[VALUES] = values; - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; - } break; - case Variant::TRANSFORM2D: { - Dictionary d; - Transform2D t = p_variant; - d["x"] = from_native(t[0]); - d["y"] = from_native(t[1]); - d["origin"] = from_native(t[2]); - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; - } break; - case Variant::VECTOR4: { - Dictionary d; - Vector4 v = p_variant; - Array values; - values.push_back(v.x); - values.push_back(v.y); - values.push_back(v.z); - values.push_back(v.w); - d[VALUES] = values; - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; - } break; - case Variant::VECTOR4I: { - Dictionary d; - Vector4i v = p_variant; - Array values; - values.push_back(v.x); - values.push_back(v.y); - values.push_back(v.z); - values.push_back(v.w); - d[VALUES] = values; - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; - } break; - case Variant::PLANE: { - Dictionary d; - Plane p = p_variant; - d["normal"] = from_native(p.normal); - d["d"] = p.d; - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; - } break; - case Variant::QUATERNION: { - Dictionary d; - Quaternion q = p_variant; - Array values; - values.push_back(q.x); - values.push_back(q.y); - values.push_back(q.z); - values.push_back(q.w); - d[VALUES] = values; - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; - } break; - case Variant::AABB: { - Dictionary d; - AABB aabb = p_variant; - d["position"] = from_native(aabb.position); - d["size"] = from_native(aabb.size); - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; - } break; - case Variant::BASIS: { - Dictionary d; - Basis t = p_variant; - d["x"] = from_native(t.get_column(0)); - d["y"] = from_native(t.get_column(1)); - d["z"] = from_native(t.get_column(2)); - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; - } break; - case Variant::TRANSFORM3D: { - Dictionary d; - Transform3D t = p_variant; - d["basis"] = from_native(t.basis); - d["origin"] = from_native(t.origin); - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; - } break; - case Variant::PROJECTION: { - Dictionary d; - Projection t = p_variant; - d["x"] = from_native(t[0]); - d["y"] = from_native(t[1]); - d["z"] = from_native(t[2]); - d["w"] = from_native(t[3]); - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; - } break; - case Variant::COLOR: { - Dictionary d; - Color c = p_variant; - Array values; - values.push_back(c.r); - values.push_back(c.g); - values.push_back(c.b); - values.push_back(c.a); - d[VALUES] = values; - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; + return "s:" + String(p_variant); } break; case Variant::STRING_NAME: { - Dictionary d; - d["name"] = String(p_variant); - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; + return "sn:" + String(p_variant); } break; case Variant::NODE_PATH: { - Dictionary d; - d["path"] = String(p_variant); - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; + return "np:" + String(p_variant); } break; - case Variant::RID: { - Dictionary d; - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; - } break; - case Variant::OBJECT: { - Object *obj = p_variant.get_validated_object(); - if (p_allow_classes && obj) { - Dictionary d; - List property_list; - obj->get_property_list(&property_list); - - d["type"] = obj->get_class(); - Dictionary p; - for (const PropertyInfo &P : property_list) { - if (P.usage & PROPERTY_USAGE_STORAGE) { - if (P.name == "script" && !p_allow_scripts) { - continue; - } - p[P.name] = from_native(obj->get(P.name), PASS_ARG); - } - } - d["properties"] = p; - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; - } else { - Dictionary nil; - nil[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return nil; - } - } break; + case Variant::RID: case Variant::CALLABLE: case Variant::SIGNAL: { - Dictionary nil; - nil[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return nil; - } break; - case Variant::DICTIONARY: { - Dictionary d = p_variant; - List keys; - d.get_key_list(&keys); - bool all_strings = true; - for (const Variant &K : keys) { - if (K.get_type() != Variant::STRING) { - all_strings = false; - break; - } - } - - if (all_strings) { - Dictionary ret_dict; - for (const Variant &K : keys) { - ret_dict[K] = from_native(d[K], PASS_ARG); - } - return ret_dict; - } else { - Dictionary ret; - Array pairs; - for (const Variant &K : keys) { - Dictionary pair; - pair["key"] = from_native(K, PASS_ARG); - pair["value"] = from_native(d[K], PASS_ARG); - pairs.push_back(pair); - } - ret["pairs"] = pairs; - ret[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return ret; - } - } break; - case Variant::ARRAY: { - Array arr = p_variant; - Array ret; - for (int i = 0; i < arr.size(); i++) { - ret.push_back(from_native(arr[i], PASS_ARG)); - } + Dictionary ret; + ret[TYPE] = Variant::get_type_name(p_variant.get_type()); return ret; } break; - case Variant::PACKED_BYTE_ARRAY: { - Dictionary d; - PackedByteArray arr = p_variant; - Array values; - for (int i = 0; i < arr.size(); i++) { - values.push_back(arr[i]); + + case Variant::VECTOR2: { + const Vector2 v = p_variant; + + Array args; + args.push_back(v.x); + args.push_back(v.y); + + RETURN_ARGS; + } break; + case Variant::VECTOR2I: { + const Vector2i v = p_variant; + + Array args; + args.push_back(v.x); + args.push_back(v.y); + + RETURN_ARGS; + } break; + case Variant::RECT2: { + const Rect2 r = p_variant; + + Array args; + args.push_back(r.position.x); + args.push_back(r.position.y); + args.push_back(r.size.width); + args.push_back(r.size.height); + + RETURN_ARGS; + } break; + case Variant::RECT2I: { + const Rect2i r = p_variant; + + Array args; + args.push_back(r.position.x); + args.push_back(r.position.y); + args.push_back(r.size.width); + args.push_back(r.size.height); + + RETURN_ARGS; + } break; + case Variant::VECTOR3: { + const Vector3 v = p_variant; + + Array args; + args.push_back(v.x); + args.push_back(v.y); + args.push_back(v.z); + + RETURN_ARGS; + } break; + case Variant::VECTOR3I: { + const Vector3i v = p_variant; + + Array args; + args.push_back(v.x); + args.push_back(v.y); + args.push_back(v.z); + + RETURN_ARGS; + } break; + case Variant::TRANSFORM2D: { + const Transform2D t = p_variant; + + Array args; + args.push_back(t[0].x); + args.push_back(t[0].y); + args.push_back(t[1].x); + args.push_back(t[1].y); + args.push_back(t[2].x); + args.push_back(t[2].y); + + RETURN_ARGS; + } break; + case Variant::VECTOR4: { + const Vector4 v = p_variant; + + Array args; + args.push_back(v.x); + args.push_back(v.y); + args.push_back(v.z); + args.push_back(v.w); + + RETURN_ARGS; + } break; + case Variant::VECTOR4I: { + const Vector4i v = p_variant; + + Array args; + args.push_back(v.x); + args.push_back(v.y); + args.push_back(v.z); + args.push_back(v.w); + + RETURN_ARGS; + } break; + case Variant::PLANE: { + const Plane p = p_variant; + + Array args; + args.push_back(p.normal.x); + args.push_back(p.normal.y); + args.push_back(p.normal.z); + args.push_back(p.d); + + RETURN_ARGS; + } break; + case Variant::QUATERNION: { + const Quaternion q = p_variant; + + Array args; + args.push_back(q.x); + args.push_back(q.y); + args.push_back(q.z); + args.push_back(q.w); + + RETURN_ARGS; + } break; + case Variant::AABB: { + const AABB aabb = p_variant; + + Array args; + args.push_back(aabb.position.x); + args.push_back(aabb.position.y); + args.push_back(aabb.position.z); + args.push_back(aabb.size.x); + args.push_back(aabb.size.y); + args.push_back(aabb.size.z); + + RETURN_ARGS; + } break; + case Variant::BASIS: { + const Basis b = p_variant; + + Array args; + args.push_back(b.get_column(0).x); + args.push_back(b.get_column(0).y); + args.push_back(b.get_column(0).z); + args.push_back(b.get_column(1).x); + args.push_back(b.get_column(1).y); + args.push_back(b.get_column(1).z); + args.push_back(b.get_column(2).x); + args.push_back(b.get_column(2).y); + args.push_back(b.get_column(2).z); + + RETURN_ARGS; + } break; + case Variant::TRANSFORM3D: { + const Transform3D t = p_variant; + + Array args; + args.push_back(t.basis.get_column(0).x); + args.push_back(t.basis.get_column(0).y); + args.push_back(t.basis.get_column(0).z); + args.push_back(t.basis.get_column(1).x); + args.push_back(t.basis.get_column(1).y); + args.push_back(t.basis.get_column(1).z); + args.push_back(t.basis.get_column(2).x); + args.push_back(t.basis.get_column(2).y); + args.push_back(t.basis.get_column(2).z); + args.push_back(t.origin.x); + args.push_back(t.origin.y); + args.push_back(t.origin.z); + + RETURN_ARGS; + } break; + case Variant::PROJECTION: { + const Projection p = p_variant; + + Array args; + args.push_back(p[0].x); + args.push_back(p[0].y); + args.push_back(p[0].z); + args.push_back(p[0].w); + args.push_back(p[1].x); + args.push_back(p[1].y); + args.push_back(p[1].z); + args.push_back(p[1].w); + args.push_back(p[2].x); + args.push_back(p[2].y); + args.push_back(p[2].z); + args.push_back(p[2].w); + args.push_back(p[3].x); + args.push_back(p[3].y); + args.push_back(p[3].z); + args.push_back(p[3].w); + + RETURN_ARGS; + } break; + case Variant::COLOR: { + const Color c = p_variant; + + Array args; + args.push_back(c.r); + args.push_back(c.g); + args.push_back(c.b); + args.push_back(c.a); + + RETURN_ARGS; + } break; + + case Variant::OBJECT: { + ERR_FAIL_COND_V(!p_full_objects, Variant()); + + ERR_FAIL_COND_V_MSG(p_depth > Variant::MAX_RECURSION_DEPTH, Variant(), "Variant is too deep. Bailing."); + + const Object *obj = p_variant.get_validated_object(); + if (obj == nullptr) { + return Variant(); } - d[VALUES] = values; - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; + + ERR_FAIL_COND_V(!ClassDB::can_instantiate(obj->get_class()), Variant()); + + List prop_list; + obj->get_property_list(&prop_list); + + Array props; + for (const PropertyInfo &pi : prop_list) { + if (!(pi.usage & PROPERTY_USAGE_STORAGE)) { + continue; + } + + Variant value; + if (pi.name == CoreStringName(script)) { + const Ref