summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.pre-commit-config.yaml2
-rw-r--r--SConstruct1
-rw-r--r--core/SCsub1
-rw-r--r--core/config/SCsub1
-rw-r--r--core/crypto/SCsub1
-rw-r--r--core/debugger/SCsub1
-rw-r--r--core/error/SCsub1
-rw-r--r--core/extension/SCsub1
-rw-r--r--core/input/SCsub1
-rw-r--r--core/input/input_builders.py2
-rw-r--r--core/io/SCsub1
-rw-r--r--core/io/file_access_pack.cpp16
-rw-r--r--core/io/file_access_pack.h3
-rw-r--r--core/io/packet_peer.cpp2
-rw-r--r--core/io/resource_importer.cpp2
-rw-r--r--core/io/resource_importer.h2
-rw-r--r--core/math/SCsub1
-rw-r--r--core/math/convex_hull.cpp2
-rw-r--r--core/math/transform_2d.h13
-rw-r--r--core/object/SCsub1
-rw-r--r--core/object/callable_method_pointer.h137
-rw-r--r--core/object/undo_redo.cpp8
-rw-r--r--core/object/undo_redo.h1
-rw-r--r--core/os/SCsub1
-rw-r--r--core/string/SCsub1
-rw-r--r--core/templates/SCsub1
-rw-r--r--core/variant/SCsub1
-rw-r--r--core/variant/binder_common.h4
-rw-r--r--doc/classes/CameraFeed.xml26
-rw-r--r--doc/classes/CameraServer.xml2
-rw-r--r--doc/classes/EditorExportPlatform.xml42
-rw-r--r--doc/classes/EditorExportPlatformExtension.xml32
-rw-r--r--doc/classes/EditorExportPreset.xml6
-rw-r--r--doc/classes/Environment.xml2
-rw-r--r--doc/classes/RenderingServer.xml5
-rw-r--r--drivers/SCsub1
-rw-r--r--drivers/alsa/SCsub1
-rw-r--r--drivers/alsamidi/SCsub1
-rw-r--r--drivers/backtrace/SCsub1
-rw-r--r--drivers/coreaudio/SCsub1
-rw-r--r--drivers/coremidi/SCsub1
-rw-r--r--drivers/d3d12/SCsub1
-rw-r--r--drivers/d3d12/rendering_device_driver_d3d12.cpp2
-rw-r--r--drivers/egl/SCsub1
-rw-r--r--drivers/gl_context/SCsub1
-rw-r--r--drivers/gles3/SCsub1
-rw-r--r--drivers/gles3/effects/SCsub1
-rw-r--r--drivers/gles3/environment/SCsub1
-rw-r--r--drivers/gles3/shaders/SCsub1
-rw-r--r--drivers/gles3/shaders/effects/SCsub1
-rw-r--r--drivers/gles3/shaders/tonemap_inc.glsl8
-rw-r--r--drivers/gles3/storage/SCsub1
-rw-r--r--drivers/metal/SCsub1
-rw-r--r--drivers/metal/metal_objects.mm2
-rw-r--r--drivers/png/SCsub1
-rw-r--r--drivers/pulseaudio/SCsub1
-rw-r--r--drivers/unix/SCsub1
-rw-r--r--drivers/unix/file_access_unix.cpp1
-rw-r--r--drivers/vulkan/SCsub1
-rw-r--r--drivers/vulkan/rendering_context_driver_vulkan.h4
-rw-r--r--drivers/wasapi/SCsub1
-rw-r--r--drivers/windows/SCsub1
-rw-r--r--drivers/windows/file_access_windows.cpp1
-rw-r--r--drivers/winmidi/SCsub1
-rw-r--r--drivers/xaudio2/SCsub1
-rw-r--r--drivers/xaudio2/audio_driver_xaudio2.cpp4
-rw-r--r--editor/SCsub1
-rw-r--r--editor/debugger/SCsub1
-rw-r--r--editor/debugger/debug_adapter/SCsub1
-rw-r--r--editor/editor_file_system.cpp304
-rw-r--r--editor/editor_file_system.h8
-rw-r--r--editor/editor_node.cpp16
-rw-r--r--editor/editor_node.h4
-rw-r--r--editor/editor_undo_redo_manager.cpp16
-rw-r--r--editor/export/SCsub1
-rw-r--r--editor/export/editor_export.cpp3
-rw-r--r--editor/export/editor_export_platform.cpp174
-rw-r--r--editor/export/editor_export_platform.h20
-rw-r--r--editor/export/editor_export_platform_extension.cpp40
-rw-r--r--editor/export/editor_export_platform_extension.h6
-rw-r--r--editor/export/editor_export_platform_pc.cpp2
-rw-r--r--editor/export/editor_export_preset.cpp37
-rw-r--r--editor/export/editor_export_preset.h9
-rw-r--r--editor/export/project_export.cpp189
-rw-r--r--editor/export/project_export.h13
-rw-r--r--editor/gui/SCsub1
-rw-r--r--editor/icons/SCsub1
-rw-r--r--editor/import/SCsub1
-rw-r--r--editor/import/resource_importer_layered_texture.cpp12
-rw-r--r--editor/import/resource_importer_layered_texture.h2
-rw-r--r--editor/import/resource_importer_texture.cpp14
-rw-r--r--editor/import/resource_importer_texture.h2
-rw-r--r--editor/plugins/SCsub1
-rw-r--r--editor/plugins/gizmos/SCsub1
-rw-r--r--editor/plugins/tiles/SCsub1
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.h2
-rw-r--r--editor/project_manager/SCsub1
-rw-r--r--editor/themes/SCsub1
-rw-r--r--main/SCsub1
-rw-r--r--main/main.cpp33
-rw-r--r--misc/utility/scons_hints.py98
-rw-r--r--modules/SCsub1
-rw-r--r--modules/astcenc/SCsub1
-rw-r--r--modules/basis_universal/SCsub1
-rw-r--r--modules/betsy/SCsub4
-rw-r--r--modules/bmp/SCsub1
-rw-r--r--modules/camera/SCsub11
-rw-r--r--modules/camera/buffer_decoder.cpp212
-rw-r--r--modules/camera/buffer_decoder.h116
-rw-r--r--modules/camera/camera_feed_linux.cpp363
-rw-r--r--modules/camera/camera_feed_linux.h78
-rw-r--r--modules/camera/camera_linux.cpp169
-rw-r--r--modules/camera/camera_linux.h60
-rw-r--r--modules/camera/config.py2
-rw-r--r--modules/camera/register_types.cpp6
-rw-r--r--modules/csg/SCsub1
-rw-r--r--modules/csg/register_types.cpp4
-rw-r--r--modules/cvtt/SCsub1
-rw-r--r--modules/dds/SCsub1
-rw-r--r--modules/enet/SCsub1
-rw-r--r--modules/etcpak/SCsub1
-rw-r--r--modules/fbx/SCsub1
-rw-r--r--modules/freetype/SCsub1
-rw-r--r--modules/gdscript/SCsub1
-rw-r--r--modules/gdscript/editor/script_templates/SCsub1
-rw-r--r--modules/gdscript/gdscript_compiler.cpp7
-rw-r--r--modules/glslang/SCsub1
-rw-r--r--modules/gltf/SCsub1
-rw-r--r--modules/gltf/extensions/SCsub1
-rw-r--r--modules/godot_physics_2d/SCsub3
-rw-r--r--modules/godot_physics_2d/godot_body_pair_2d.cpp12
-rw-r--r--modules/godot_physics_2d/godot_step_2d.cpp4
-rw-r--r--modules/godot_physics_3d/SCsub3
-rw-r--r--modules/godot_physics_3d/godot_body_pair_3d.cpp12
-rw-r--r--modules/godot_physics_3d/godot_step_3d.cpp4
-rw-r--r--modules/godot_physics_3d/joints/SCsub3
-rw-r--r--modules/gridmap/SCsub1
-rw-r--r--modules/hdr/SCsub1
-rw-r--r--modules/interactive_music/SCsub1
-rw-r--r--modules/jpg/SCsub1
-rw-r--r--modules/jsonrpc/SCsub1
-rw-r--r--modules/ktx/SCsub1
-rw-r--r--modules/lightmapper_rd/SCsub1
-rw-r--r--modules/lightmapper_rd/lm_compute.glsl2
-rw-r--r--modules/mbedtls/SCsub1
-rw-r--r--modules/meshoptimizer/SCsub1
-rw-r--r--modules/minimp3/SCsub1
-rw-r--r--modules/mobile_vr/SCsub1
-rw-r--r--modules/mono/SCsub1
-rw-r--r--modules/mono/editor/script_templates/SCsub1
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp2
-rw-r--r--modules/msdfgen/SCsub1
-rw-r--r--modules/multiplayer/SCsub1
-rw-r--r--modules/multiplayer/editor/editor_network_profiler.cpp13
-rw-r--r--modules/navigation/SCsub1
-rw-r--r--modules/noise/SCsub1
-rw-r--r--modules/ogg/SCsub1
-rw-r--r--modules/openxr/SCsub1
-rw-r--r--modules/openxr/action_map/SCsub1
-rw-r--r--modules/openxr/editor/SCsub1
-rw-r--r--modules/openxr/editor/openxr_select_action_dialog.cpp3
-rw-r--r--modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp32
-rw-r--r--modules/openxr/editor/openxr_select_interaction_profile_dialog.h1
-rw-r--r--modules/openxr/extensions/SCsub1
-rw-r--r--modules/openxr/scene/SCsub1
-rw-r--r--modules/raycast/SCsub1
-rw-r--r--modules/regex/SCsub1
-rw-r--r--modules/squish/SCsub1
-rw-r--r--modules/svg/SCsub1
-rw-r--r--modules/text_server_adv/SCsub1
-rw-r--r--modules/text_server_adv/gdextension_build/SConstruct2
-rw-r--r--modules/text_server_fb/SCsub1
-rw-r--r--modules/text_server_fb/gdextension_build/SConstruct2
-rw-r--r--modules/tga/SCsub1
-rw-r--r--modules/theora/SCsub1
-rw-r--r--modules/tinyexr/SCsub1
-rw-r--r--modules/upnp/SCsub1
-rw-r--r--modules/vhacd/SCsub1
-rw-r--r--modules/vorbis/SCsub1
-rw-r--r--modules/webp/SCsub1
-rw-r--r--modules/webrtc/SCsub1
-rw-r--r--modules/websocket/SCsub1
-rw-r--r--modules/webxr/SCsub1
-rw-r--r--modules/xatlas_unwrap/SCsub1
-rw-r--r--modules/zip/SCsub1
-rw-r--r--platform/SCsub1
-rw-r--r--platform/android/SCsub1
-rw-r--r--platform/android/api/java_class_wrapper.h2
-rw-r--r--platform/ios/SCsub1
-rw-r--r--platform/linuxbsd/SCsub1
-rw-r--r--platform/linuxbsd/wayland/SCsub1
-rw-r--r--platform/linuxbsd/wayland/wayland_thread.h4
-rw-r--r--platform/linuxbsd/x11/SCsub1
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp10
-rw-r--r--platform/macos/SCsub1
-rw-r--r--platform/web/SCsub1
-rw-r--r--platform/windows/SCsub1
-rw-r--r--platform/windows/display_server_windows.cpp4
-rw-r--r--platform/windows/gl_manager_windows_native.cpp4
-rw-r--r--pyproject.toml15
-rw-r--r--scene/2d/SCsub1
-rw-r--r--scene/2d/parallax_2d.cpp6
-rw-r--r--scene/2d/physics/SCsub1
-rw-r--r--scene/2d/physics/joints/SCsub1
-rw-r--r--scene/3d/SCsub1
-rw-r--r--scene/3d/physics/SCsub1
-rw-r--r--scene/3d/physics/joints/SCsub1
-rw-r--r--scene/SCsub1
-rw-r--r--scene/animation/SCsub1
-rw-r--r--scene/audio/SCsub1
-rw-r--r--scene/debugger/SCsub1
-rw-r--r--scene/gui/SCsub1
-rw-r--r--scene/gui/color_picker.cpp15
-rw-r--r--scene/gui/line_edit.cpp2
-rw-r--r--scene/gui/scroll_bar.cpp2
-rw-r--r--scene/gui/text_edit.cpp2
-rw-r--r--scene/main/SCsub1
-rw-r--r--scene/resources/2d/SCsub1
-rw-r--r--scene/resources/3d/SCsub1
-rw-r--r--scene/resources/SCsub1
-rw-r--r--scene/resources/animation.cpp14
-rw-r--r--scene/resources/camera_texture.cpp24
-rw-r--r--scene/resources/camera_texture.h1
-rw-r--r--scene/theme/SCsub1
-rw-r--r--scene/theme/icons/SCsub1
-rw-r--r--servers/SCsub1
-rw-r--r--servers/audio/SCsub1
-rw-r--r--servers/audio/audio_driver_dummy.cpp2
-rw-r--r--servers/audio/effects/SCsub1
-rw-r--r--servers/camera/SCsub1
-rw-r--r--servers/camera/camera_feed.cpp28
-rw-r--r--servers/camera/camera_feed.h20
-rw-r--r--servers/debugger/SCsub1
-rw-r--r--servers/display/SCsub1
-rw-r--r--servers/extensions/SCsub1
-rw-r--r--servers/movie_writer/SCsub1
-rw-r--r--servers/navigation/SCsub1
-rw-r--r--servers/rendering/SCsub1
-rw-r--r--servers/rendering/dummy/SCsub1
-rw-r--r--servers/rendering/dummy/storage/SCsub1
-rw-r--r--servers/rendering/renderer_rd/SCsub1
-rw-r--r--servers/rendering/renderer_rd/effects/SCsub1
-rw-r--r--servers/rendering/renderer_rd/environment/SCsub1
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/SCsub1
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/SCsub1
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.cpp1
-rw-r--r--servers/rendering/renderer_rd/shaders/SCsub1
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/SCsub1
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/fsr2/SCsub1
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/ssao.glsl9
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/ssil.glsl5
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/tonemap.glsl8
-rw-r--r--servers/rendering/renderer_rd/shaders/environment/SCsub1
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_clustered/SCsub1
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl16
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_mobile/SCsub1
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl36
-rw-r--r--servers/rendering/renderer_rd/shaders/scene_data_inc.glsl4
-rw-r--r--servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl28
-rw-r--r--servers/rendering/renderer_rd/spirv-reflect/SCsub1
-rw-r--r--servers/rendering/renderer_rd/storage_rd/SCsub1
-rw-r--r--servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp1
-rw-r--r--servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h3
-rw-r--r--servers/rendering/renderer_scene_cull.cpp6
-rw-r--r--servers/rendering/renderer_scene_render.cpp3
-rw-r--r--servers/rendering/renderer_scene_render.h3
-rw-r--r--servers/rendering/renderer_viewport.cpp17
-rw-r--r--servers/rendering/rendering_device.h2
-rw-r--r--servers/rendering/shader_language.cpp5
-rw-r--r--servers/rendering/storage/SCsub1
-rw-r--r--servers/rendering_server.h2
-rw-r--r--servers/text/SCsub1
-rw-r--r--servers/xr/SCsub1
-rw-r--r--tests/SCsub3
274 files changed, 2533 insertions, 402 deletions
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 8cfb00fd8e..572eaf6791 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -9,7 +9,7 @@ exclude: |
repos:
- repo: https://github.com/pre-commit/mirrors-clang-format
- rev: v18.1.8
+ rev: v19.1.0
hooks:
- id: clang-format
files: \.(c|h|cpp|hpp|cc|hh|cxx|hxx|m|mm|inc|java|glsl)$
diff --git a/SConstruct b/SConstruct
index 0245531b45..5566770148 100644
--- a/SConstruct
+++ b/SConstruct
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
EnsureSConsVersion(3, 1, 2)
EnsurePythonVersion(3, 6)
diff --git a/core/SCsub b/core/SCsub
index c8267ae960..8bda230b87 100644
--- a/core/SCsub
+++ b/core/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/core/config/SCsub b/core/config/SCsub
index bf70285490..1417a258c1 100644
--- a/core/config/SCsub
+++ b/core/config/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/core/crypto/SCsub b/core/crypto/SCsub
index 8cff3cf679..3cea6bfb47 100644
--- a/core/crypto/SCsub
+++ b/core/crypto/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/core/debugger/SCsub b/core/debugger/SCsub
index 19a6549225..ab81175894 100644
--- a/core/debugger/SCsub
+++ b/core/debugger/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/core/error/SCsub b/core/error/SCsub
index dfd6248a94..08089d31b0 100644
--- a/core/error/SCsub
+++ b/core/error/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/core/extension/SCsub b/core/extension/SCsub
index 6ab2d2b0a6..8688ca5b6e 100644
--- a/core/extension/SCsub
+++ b/core/extension/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/core/input/SCsub b/core/input/SCsub
index d8e6f33156..521f7702e4 100644
--- a/core/input/SCsub
+++ b/core/input/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/core/input/input_builders.py b/core/input/input_builders.py
index ae848f4e7c..3685e726b4 100644
--- a/core/input/input_builders.py
+++ b/core/input/input_builders.py
@@ -33,7 +33,7 @@ def make_default_controller_mappings(target, source, env):
guid = line_parts[0]
if guid in platform_mappings[current_platform]:
g.write(
- "// WARNING - DATABASE {} OVERWROTE PRIOR MAPPING: {} {}\n".format(
+ "// WARNING: DATABASE {} OVERWROTE PRIOR MAPPING: {} {}\n".format(
src_path, current_platform, platform_mappings[current_platform][guid]
)
)
diff --git a/core/io/SCsub b/core/io/SCsub
index 19a6549225..ab81175894 100644
--- a/core/io/SCsub
+++ b/core/io/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp
index eec27ce0aa..1340382eaa 100644
--- a/core/io/file_access_pack.cpp
+++ b/core/io/file_access_pack.cpp
@@ -102,6 +102,22 @@ void PackedData::add_pack_source(PackSource *p_source) {
}
}
+uint8_t *PackedData::get_file_hash(const String &p_path) {
+ PathMD5 pmd5(p_path.md5_buffer());
+ HashMap<PathMD5, PackedFile, PathMD5>::Iterator E = files.find(pmd5);
+ if (!E || E->value.offset == 0) {
+ return nullptr;
+ }
+
+ return E->value.md5;
+}
+
+void PackedData::clear() {
+ files.clear();
+ _free_packed_dirs(root);
+ root = memnew(PackedDir);
+}
+
PackedData *PackedData::singleton = nullptr;
PackedData::PackedData() {
diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h
index 595a36bca4..57b7a5f87f 100644
--- a/core/io/file_access_pack.h
+++ b/core/io/file_access_pack.h
@@ -111,6 +111,7 @@ private:
public:
void add_pack_source(PackSource *p_source);
void add_path(const String &p_pkg_path, const String &p_path, uint64_t p_ofs, uint64_t p_size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files, bool p_encrypted = false); // for PackSource
+ uint8_t *get_file_hash(const String &p_path);
void set_disabled(bool p_disabled) { disabled = p_disabled; }
_FORCE_INLINE_ bool is_disabled() const { return disabled; }
@@ -118,6 +119,8 @@ public:
static PackedData *get_singleton() { return singleton; }
Error add_pack(const String &p_path, bool p_replace_files, uint64_t p_offset);
+ void clear();
+
_FORCE_INLINE_ Ref<FileAccess> try_open_path(const String &p_path);
_FORCE_INLINE_ bool has_path(const String &p_path);
diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp
index 0329ace313..614f81c42a 100644
--- a/core/io/packet_peer.cpp
+++ b/core/io/packet_peer.cpp
@@ -299,7 +299,7 @@ Ref<StreamPeer> PacketPeerStream::get_stream_peer() const {
void PacketPeerStream::set_input_buffer_max_size(int p_max_size) {
ERR_FAIL_COND_MSG(p_max_size < 0, "Max size of input buffer size cannot be smaller than 0.");
- //warning may lose packets
+ // WARNING: May lose packets.
ERR_FAIL_COND_MSG(ring_buffer.data_left(), "Buffer in use, resizing would cause loss of data.");
ring_buffer.resize(nearest_shift(next_power_of_2(p_max_size + 4)) - 1);
input_buffer.resize(next_power_of_2(p_max_size + 4));
diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp
index a572dd562e..1ae50d2d0d 100644
--- a/core/io/resource_importer.cpp
+++ b/core/io/resource_importer.cpp
@@ -507,7 +507,7 @@ bool ResourceFormatImporter::are_import_settings_valid(const String &p_path) con
for (int i = 0; i < importers.size(); i++) {
if (importers[i]->get_importer_name() == pat.importer) {
- if (!importers[i]->are_import_settings_valid(p_path)) { //importer thinks this is not valid
+ if (!importers[i]->are_import_settings_valid(p_path, pat.metadata)) { //importer thinks this is not valid
return false;
}
}
diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h
index 6ea5d0972a..221f38494b 100644
--- a/core/io/resource_importer.h
+++ b/core/io/resource_importer.h
@@ -153,7 +153,7 @@ public:
virtual void import_threaded_end() {}
virtual Error import_group_file(const String &p_group_file, const HashMap<String, HashMap<StringName, Variant>> &p_source_file_options, const HashMap<String, String> &p_base_paths) { return ERR_UNAVAILABLE; }
- virtual bool are_import_settings_valid(const String &p_path) const { return true; }
+ virtual bool are_import_settings_valid(const String &p_path, const Dictionary &p_meta) const { return true; }
virtual String get_import_settings_string() const { return String(); }
};
diff --git a/core/math/SCsub b/core/math/SCsub
index c8fdac207e..6ea3ab6b12 100644
--- a/core/math/SCsub
+++ b/core/math/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/core/math/convex_hull.cpp b/core/math/convex_hull.cpp
index 80662c1b07..620a7541e4 100644
--- a/core/math/convex_hull.cpp
+++ b/core/math/convex_hull.cpp
@@ -204,7 +204,7 @@ public:
static Int128 mul(uint64_t a, uint64_t b);
Int128 operator-() const {
- return Int128((uint64_t) - (int64_t)low, ~high + (low == 0));
+ return Int128(uint64_t(-int64_t(low)), ~high + (low == 0));
}
Int128 operator+(const Int128 &b) const {
diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h
index 476577508f..1ee7d3d84f 100644
--- a/core/math/transform_2d.h
+++ b/core/math/transform_2d.h
@@ -39,16 +39,19 @@
class String;
struct [[nodiscard]] Transform2D {
- // Warning #1: basis of Transform2D is stored differently from Basis. In terms of columns array, the basis matrix looks like "on paper":
+ // WARNING: The basis of Transform2D is stored differently from Basis.
+ // In terms of columns array, the basis matrix looks like "on paper":
// M = (columns[0][0] columns[1][0])
// (columns[0][1] columns[1][1])
- // This is such that the columns, which can be interpreted as basis vectors of the coordinate system "painted" on the object, can be accessed as columns[i].
- // Note that this is the opposite of the indices in mathematical texts, meaning: $M_{12}$ in a math book corresponds to columns[1][0] here.
+ // This is such that the columns, which can be interpreted as basis vectors
+ // of the coordinate system "painted" on the object, can be accessed as columns[i].
+ // NOTE: This is the opposite of the indices in mathematical texts,
+ // meaning: $M_{12}$ in a math book corresponds to columns[1][0] here.
// This requires additional care when working with explicit indices.
// See https://en.wikipedia.org/wiki/Row-_and_column-major_order for further reading.
- // Warning #2: 2D be aware that unlike 3D code, 2D code uses a left-handed coordinate system: Y-axis points down,
- // and angle is measure from +X to +Y in a clockwise-fashion.
+ // WARNING: Be aware that unlike 3D code, 2D code uses a left-handed coordinate system:
+ // Y-axis points down, and angle is measure from +X to +Y in a clockwise-fashion.
Vector2 columns[3];
diff --git a/core/object/SCsub b/core/object/SCsub
index 7c00bb719e..3d0d2c14dd 100644
--- a/core/object/SCsub
+++ b/core/object/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/core/object/callable_method_pointer.h b/core/object/callable_method_pointer.h
index 1b29e1778a..86c66593bd 100644
--- a/core/object/callable_method_pointer.h
+++ b/core/object/callable_method_pointer.h
@@ -37,6 +37,8 @@
#include "core/variant/binder_common.h"
#include "core/variant/callable.h"
+#include <type_traits>
+
class CallableCustomMethodPointerBase : public CallableCustom {
uint32_t *comp_ptr = nullptr;
uint32_t comp_size;
@@ -77,12 +79,13 @@ public:
virtual uint32_t hash() const;
};
-template <typename T, typename... P>
+template <typename T, typename R, typename... P>
class CallableCustomMethodPointer : public CallableCustomMethodPointerBase {
struct Data {
T *instance;
uint64_t object_id;
- void (T::*method)(P...);
+ R(T::*method)
+ (P...);
} data;
public:
@@ -100,10 +103,14 @@ public:
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
- call_with_variant_args(data.instance, data.method, p_arguments, p_argcount, r_call_error);
+ if constexpr (std::is_same<R, void>::value) {
+ call_with_variant_args(data.instance, data.method, p_arguments, p_argcount, r_call_error);
+ } else {
+ call_with_variant_args_ret(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error);
+ }
}
- CallableCustomMethodPointer(T *p_instance, void (T::*p_method)(P...)) {
+ CallableCustomMethodPointer(T *p_instance, R (T::*p_method)(P...)) {
memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes.
data.instance = p_instance;
data.object_id = p_instance->get_instance_id();
@@ -118,7 +125,7 @@ Callable create_custom_callable_function_pointer(T *p_instance,
const char *p_func_text,
#endif
void (T::*p_method)(P...)) {
- typedef CallableCustomMethodPointer<T, P...> CCMP; // Messes with memnew otherwise.
+ typedef CallableCustomMethodPointer<T, void, P...> CCMP; // Messes with memnew otherwise.
CCMP *ccmp = memnew(CCMP(p_instance, p_method));
#ifdef DEBUG_METHODS_ENABLED
ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand.
@@ -126,51 +133,13 @@ Callable create_custom_callable_function_pointer(T *p_instance,
return Callable(ccmp);
}
-// VERSION WITH RETURN
-
-template <typename T, typename R, typename... P>
-class CallableCustomMethodPointerRet : public CallableCustomMethodPointerBase {
- struct Data {
- T *instance;
- uint64_t object_id;
- R(T::*method)
- (P...);
- } data;
-
-public:
- virtual ObjectID get_object() const {
- if (ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr) {
- return ObjectID();
- }
- return data.instance->get_instance_id();
- }
-
- virtual int get_argument_count(bool &r_is_valid) const {
- r_is_valid = true;
- return sizeof...(P);
- }
-
- virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
- ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
- call_with_variant_args_ret(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error);
- }
-
- CallableCustomMethodPointerRet(T *p_instance, R (T::*p_method)(P...)) {
- memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes.
- data.instance = p_instance;
- data.object_id = p_instance->get_instance_id();
- data.method = p_method;
- _setup((uint32_t *)&data, sizeof(Data));
- }
-};
-
template <typename T, typename R, typename... P>
Callable create_custom_callable_function_pointer(T *p_instance,
#ifdef DEBUG_METHODS_ENABLED
const char *p_func_text,
#endif
R (T::*p_method)(P...)) {
- typedef CallableCustomMethodPointerRet<T, R, P...> CCMP; // Messes with memnew otherwise.
+ typedef CallableCustomMethodPointer<T, R, P...> CCMP; // Messes with memnew otherwise.
CCMP *ccmp = memnew(CCMP(p_instance, p_method));
#ifdef DEBUG_METHODS_ENABLED
ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand.
@@ -178,10 +147,10 @@ Callable create_custom_callable_function_pointer(T *p_instance,
return Callable(ccmp);
}
-// CONST VERSION WITH RETURN
+// CONST VERSION
template <typename T, typename R, typename... P>
-class CallableCustomMethodPointerRetC : public CallableCustomMethodPointerBase {
+class CallableCustomMethodPointerC : public CallableCustomMethodPointerBase {
struct Data {
T *instance;
uint64_t object_id;
@@ -204,10 +173,14 @@ public:
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override {
ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
- call_with_variant_args_retc(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error);
+ if constexpr (std::is_same<R, void>::value) {
+ call_with_variant_argsc(data.instance, data.method, p_arguments, p_argcount, r_call_error);
+ } else {
+ call_with_variant_args_retc(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error);
+ }
}
- CallableCustomMethodPointerRetC(T *p_instance, R (T::*p_method)(P...) const) {
+ CallableCustomMethodPointerC(T *p_instance, R (T::*p_method)(P...) const) {
memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes.
data.instance = p_instance;
data.object_id = p_instance->get_instance_id();
@@ -216,13 +189,27 @@ public:
}
};
+template <typename T, typename... P>
+Callable create_custom_callable_function_pointer(T *p_instance,
+#ifdef DEBUG_METHODS_ENABLED
+ const char *p_func_text,
+#endif
+ void (T::*p_method)(P...) const) {
+ typedef CallableCustomMethodPointerC<T, void, P...> CCMP; // Messes with memnew otherwise.
+ CCMP *ccmp = memnew(CCMP(p_instance, p_method));
+#ifdef DEBUG_METHODS_ENABLED
+ ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand.
+#endif
+ return Callable(ccmp);
+}
+
template <typename T, typename R, typename... P>
Callable create_custom_callable_function_pointer(T *p_instance,
#ifdef DEBUG_METHODS_ENABLED
const char *p_func_text,
#endif
R (T::*p_method)(P...) const) {
- typedef CallableCustomMethodPointerRetC<T, R, P...> CCMP; // Messes with memnew otherwise.
+ typedef CallableCustomMethodPointerC<T, R, P...> CCMP; // Messes with memnew otherwise.
CCMP *ccmp = memnew(CCMP(p_instance, p_method));
#ifdef DEBUG_METHODS_ENABLED
ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand.
@@ -238,10 +225,11 @@ Callable create_custom_callable_function_pointer(T *p_instance,
// STATIC VERSIONS
-template <typename... P>
+template <typename R, typename... P>
class CallableCustomStaticMethodPointer : public CallableCustomMethodPointerBase {
struct Data {
- void (*method)(P...);
+ R(*method)
+ (P...);
} data;
public:
@@ -259,24 +247,27 @@ public:
}
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override {
- call_with_variant_args_static_ret(data.method, p_arguments, p_argcount, r_return_value, r_call_error);
- r_return_value = Variant();
+ if constexpr (std::is_same<R, void>::value) {
+ call_with_variant_args_static(data.method, p_arguments, p_argcount, r_call_error);
+ } else {
+ call_with_variant_args_static_ret(data.method, p_arguments, p_argcount, r_return_value, r_call_error);
+ }
}
- CallableCustomStaticMethodPointer(void (*p_method)(P...)) {
+ CallableCustomStaticMethodPointer(R (*p_method)(P...)) {
memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes.
data.method = p_method;
_setup((uint32_t *)&data, sizeof(Data));
}
};
-template <typename T, typename... P>
+template <typename... P>
Callable create_custom_callable_static_function_pointer(
#ifdef DEBUG_METHODS_ENABLED
const char *p_func_text,
#endif
void (*p_method)(P...)) {
- typedef CallableCustomStaticMethodPointer<P...> CCMP; // Messes with memnew otherwise.
+ typedef CallableCustomStaticMethodPointer<void, P...> CCMP; // Messes with memnew otherwise.
CCMP *ccmp = memnew(CCMP(p_method));
#ifdef DEBUG_METHODS_ENABLED
ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand.
@@ -285,44 +276,12 @@ Callable create_custom_callable_static_function_pointer(
}
template <typename R, typename... P>
-class CallableCustomStaticMethodPointerRet : public CallableCustomMethodPointerBase {
- struct Data {
- R(*method)
- (P...);
- } data;
-
-public:
- virtual bool is_valid() const override {
- return true;
- }
-
- virtual ObjectID get_object() const override {
- return ObjectID();
- }
-
- virtual int get_argument_count(bool &r_is_valid) const override {
- r_is_valid = true;
- return sizeof...(P);
- }
-
- virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override {
- call_with_variant_args_static_ret(data.method, p_arguments, p_argcount, r_return_value, r_call_error);
- }
-
- CallableCustomStaticMethodPointerRet(R (*p_method)(P...)) {
- memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes.
- data.method = p_method;
- _setup((uint32_t *)&data, sizeof(Data));
- }
-};
-
-template <typename R, typename... P>
Callable create_custom_callable_static_function_pointer(
#ifdef DEBUG_METHODS_ENABLED
const char *p_func_text,
#endif
R (*p_method)(P...)) {
- typedef CallableCustomStaticMethodPointerRet<R, P...> CCMP; // Messes with memnew otherwise.
+ typedef CallableCustomStaticMethodPointer<R, P...> CCMP; // Messes with memnew otherwise.
CCMP *ccmp = memnew(CCMP(p_method));
#ifdef DEBUG_METHODS_ENABLED
ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand.
diff --git a/core/object/undo_redo.cpp b/core/object/undo_redo.cpp
index 4d67cd930e..03537dbeb1 100644
--- a/core/object/undo_redo.cpp
+++ b/core/object/undo_redo.cpp
@@ -48,7 +48,7 @@ void UndoRedo::Operation::delete_reference() {
}
}
-void UndoRedo::_discard_redo() {
+void UndoRedo::discard_redo() {
if (current_action == actions.size() - 1) {
return;
}
@@ -89,7 +89,7 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode, bool p_back
uint64_t ticks = OS::get_singleton()->get_ticks_msec();
if (action_level == 0) {
- _discard_redo();
+ discard_redo();
// Check if the merge operation is valid
if (p_mode != MERGE_DISABLE && actions.size() && actions[actions.size() - 1].name == p_name && actions[actions.size() - 1].backward_undo_ops == p_backward_undo_ops && actions[actions.size() - 1].last_tick + 800 > ticks) {
@@ -288,7 +288,7 @@ void UndoRedo::end_force_keep_in_merge_ends() {
}
void UndoRedo::_pop_history_tail() {
- _discard_redo();
+ discard_redo();
if (!actions.size()) {
return;
@@ -455,7 +455,7 @@ String UndoRedo::get_action_name(int p_id) {
void UndoRedo::clear_history(bool p_increase_version) {
ERR_FAIL_COND(action_level > 0);
- _discard_redo();
+ discard_redo();
while (actions.size()) {
_pop_history_tail();
diff --git a/core/object/undo_redo.h b/core/object/undo_redo.h
index 19d178635c..ded962670c 100644
--- a/core/object/undo_redo.h
+++ b/core/object/undo_redo.h
@@ -129,6 +129,7 @@ public:
int get_current_action();
String get_action_name(int p_id);
void clear_history(bool p_increase_version = true);
+ void discard_redo();
bool has_undo() const;
bool has_redo() const;
diff --git a/core/os/SCsub b/core/os/SCsub
index 19a6549225..ab81175894 100644
--- a/core/os/SCsub
+++ b/core/os/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/core/string/SCsub b/core/string/SCsub
index 3217166f18..b06e32eb88 100644
--- a/core/string/SCsub
+++ b/core/string/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/core/templates/SCsub b/core/templates/SCsub
index 8c4c843a33..7f806d5609 100644
--- a/core/templates/SCsub
+++ b/core/templates/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/core/variant/SCsub b/core/variant/SCsub
index 7f4c8b7788..8264503a22 100644
--- a/core/variant/SCsub
+++ b/core/variant/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/core/variant/binder_common.h b/core/variant/binder_common.h
index fa49767d46..0aa49f6d68 100644
--- a/core/variant/binder_common.h
+++ b/core/variant/binder_common.h
@@ -466,7 +466,7 @@ void call_with_variant_argsc(T *p_instance, void (T::*p_method)(P...) const, con
return;
}
#endif
- call_with_variant_args_helper<T, P...>(p_instance, p_method, p_args, r_error, BuildIndexSequence<sizeof...(P)>{});
+ call_with_variant_argsc_helper<T, P...>(p_instance, p_method, p_args, r_error, BuildIndexSequence<sizeof...(P)>{});
}
template <typename T, typename... P>
@@ -830,7 +830,7 @@ void call_with_variant_args_static_ret(R (*p_method)(P...), const Variant **p_ar
}
template <typename... P>
-void call_with_variant_args_static_ret(void (*p_method)(P...), const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error) {
+void call_with_variant_args_static(void (*p_method)(P...), const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
#ifdef DEBUG_METHODS_ENABLED
if ((size_t)p_argcount > sizeof...(P)) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
diff --git a/doc/classes/CameraFeed.xml b/doc/classes/CameraFeed.xml
index 974f6d4a33..8033c0880b 100644
--- a/doc/classes/CameraFeed.xml
+++ b/doc/classes/CameraFeed.xml
@@ -34,6 +34,17 @@
Returns the position of camera on the device.
</description>
</method>
+ <method name="set_format">
+ <return type="bool" />
+ <param index="0" name="index" type="int" />
+ <param index="1" name="parameters" type="Dictionary" />
+ <description>
+ Sets the feed format parameters for the given index in the [member formats] array. Returns [code]true[/code] on success. By default YUYV encoded stream is transformed to FEED_RGB. YUYV encoded stream output format can be changed with [param parameters].output value:
+ [code]separate[/code] will result in FEED_YCBCR_SEP
+ [code]grayscale[/code] will result in desaturated FEED_RGB
+ [code]copy[/code] will result in FEED_YCBCR
+ </description>
+ </method>
</methods>
<members>
<member name="feed_is_active" type="bool" setter="set_active" getter="is_active" default="false">
@@ -42,7 +53,22 @@
<member name="feed_transform" type="Transform2D" setter="set_transform" getter="get_transform" default="Transform2D(1, 0, 0, -1, 0, 1)">
The transform applied to the camera's image.
</member>
+ <member name="formats" type="Array" setter="" getter="get_formats" default="[]">
+ Formats supported by the feed. Each entry is a [Dictionary] describing format parameters.
+ </member>
</members>
+ <signals>
+ <signal name="format_changed">
+ <description>
+ Emitted when the format has changed.
+ </description>
+ </signal>
+ <signal name="frame_changed">
+ <description>
+ Emitted when a new frame is available.
+ </description>
+ </signal>
+ </signals>
<constants>
<constant name="FEED_NOIMAGE" value="0" enum="FeedDataType">
No image set for the feed.
diff --git a/doc/classes/CameraServer.xml b/doc/classes/CameraServer.xml
index 020b5d887b..b09010147e 100644
--- a/doc/classes/CameraServer.xml
+++ b/doc/classes/CameraServer.xml
@@ -6,7 +6,7 @@
<description>
The [CameraServer] keeps track of different cameras accessible in Godot. These are external cameras such as webcams or the cameras on your phone.
It is notably used to provide AR modules with a video feed from the camera.
- [b]Note:[/b] This class is currently only implemented on macOS and iOS. To get a [CameraFeed] on iOS, the camera plugin from [url=https://github.com/godotengine/godot-ios-plugins]godot-ios-plugins[/url] is required. On other platforms, no [CameraFeed]s will be available.
+ [b]Note:[/b] This class is currently only implemented on Linux, macOS, and iOS, on other platforms no [CameraFeed]s will be available. To get a [CameraFeed] on iOS, the camera plugin from [url=https://github.com/godotengine/godot-ios-plugins]godot-ios-plugins[/url] is required.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/EditorExportPlatform.xml b/doc/classes/EditorExportPlatform.xml
index d4084e84a0..8792bbedc3 100644
--- a/doc/classes/EditorExportPlatform.xml
+++ b/doc/classes/EditorExportPlatform.xml
@@ -42,6 +42,18 @@
Creates a PCK archive at [param path] for the specified [param preset].
</description>
</method>
+ <method name="export_pack_patch">
+ <return type="int" enum="Error" />
+ <param index="0" name="preset" type="EditorExportPreset" />
+ <param index="1" name="debug" type="bool" />
+ <param index="2" name="path" type="String" />
+ <param index="3" name="patches" type="PackedStringArray" default="PackedStringArray()" />
+ <param index="4" name="flags" type="int" enum="EditorExportPlatform.DebugFlags" is_bitfield="true" default="0" />
+ <description>
+ Creates a patch PCK archive at [param path] for the specified [param preset], containing only the files that have changed since the last patch.
+ [b]Note:[/b] [param patches] is an optional override of the set of patches defined in the export preset. When empty the patches defined in the export preset will be used instead.
+ </description>
+ </method>
<method name="export_project">
<return type="int" enum="Error" />
<param index="0" name="preset" type="EditorExportPreset" />
@@ -75,6 +87,18 @@
Create a ZIP archive at [param path] for the specified [param preset].
</description>
</method>
+ <method name="export_zip_patch">
+ <return type="int" enum="Error" />
+ <param index="0" name="preset" type="EditorExportPreset" />
+ <param index="1" name="debug" type="bool" />
+ <param index="2" name="path" type="String" />
+ <param index="3" name="patches" type="PackedStringArray" default="PackedStringArray()" />
+ <param index="4" name="flags" type="int" enum="EditorExportPlatform.DebugFlags" is_bitfield="true" default="0" />
+ <description>
+ Create a patch ZIP archive at [param path] for the specified [param preset], containing only the files that have changed since the last patch.
+ [b]Note:[/b] [param patches] is an optional override of the set of patches defined in the export preset. When empty the patches defined in the export preset will be used instead.
+ </description>
+ </method>
<method name="find_export_template" qualifiers="const">
<return type="Dictionary" />
<param index="0" name="template_file_name" type="String" />
@@ -151,6 +175,15 @@
If [param embed] is [code]true[/code], PCK content is appended to the end of [param path] file and return [Dictionary] additionally include following keys: [code]embedded_start: int[/code] (embedded PCK offset) and [code]embedded_size: int[/code] (embedded PCK size).
</description>
</method>
+ <method name="save_pack_patch">
+ <return type="Dictionary" />
+ <param index="0" name="preset" type="EditorExportPreset" />
+ <param index="1" name="debug" type="bool" />
+ <param index="2" name="path" type="String" />
+ <description>
+ Saves patch PCK archive and returns [Dictionary] with the following keys: [code]result: Error[/code], [code]so_files: Array[/code] (array of the shared/static objects which contains dictionaries with the following keys: [code]path: String[/code], [code]tags: PackedStringArray[/code], and [code]target_folder: String[/code]).
+ </description>
+ </method>
<method name="save_zip">
<return type="Dictionary" />
<param index="0" name="preset" type="EditorExportPreset" />
@@ -160,6 +193,15 @@
Saves ZIP archive and returns [Dictionary] with the following keys: [code]result: Error[/code], [code]so_files: Array[/code] (array of the shared/static objects which contains dictionaries with the following keys: [code]path: String[/code], [code]tags: PackedStringArray[/code], and [code]target_folder: String[/code]).
</description>
</method>
+ <method name="save_zip_patch">
+ <return type="Dictionary" />
+ <param index="0" name="preset" type="EditorExportPreset" />
+ <param index="1" name="debug" type="bool" />
+ <param index="2" name="path" type="String" />
+ <description>
+ Saves patch ZIP archive and returns [Dictionary] with the following keys: [code]result: Error[/code], [code]so_files: Array[/code] (array of the shared/static objects which contains dictionaries with the following keys: [code]path: String[/code], [code]tags: PackedStringArray[/code], and [code]target_folder: String[/code]).
+ </description>
+ </method>
<method name="ssh_push_to_remote" qualifiers="const">
<return type="int" enum="Error" />
<param index="0" name="host" type="String" />
diff --git a/doc/classes/EditorExportPlatformExtension.xml b/doc/classes/EditorExportPlatformExtension.xml
index ef589e2f58..f2d14b1710 100644
--- a/doc/classes/EditorExportPlatformExtension.xml
+++ b/doc/classes/EditorExportPlatformExtension.xml
@@ -36,7 +36,21 @@
<description>
[b]Optional.[/b]
Creates a PCK archive at [param path] for the specified [param preset].
- This method is called when "Export PCK/ZIP" button is pressed in the export dialog, and PCK is selected as a file type.
+ This method is called when "Export PCK/ZIP" button is pressed in the export dialog, with "Export as Patch" disabled, and PCK is selected as a file type.
+ </description>
+ </method>
+ <method name="_export_pack_patch" qualifiers="virtual">
+ <return type="int" enum="Error" />
+ <param index="0" name="preset" type="EditorExportPreset" />
+ <param index="1" name="debug" type="bool" />
+ <param index="2" name="path" type="String" />
+ <param index="3" name="patches" type="PackedStringArray" />
+ <param index="4" name="flags" type="int" enum="EditorExportPlatform.DebugFlags" is_bitfield="true" />
+ <description>
+ [b]Optional.[/b]
+ Creates a patch PCK archive at [param path] for the specified [param preset], containing only the files that have changed since the last patch.
+ This method is called when "Export PCK/ZIP" button is pressed in the export dialog, with "Export as Patch" enabled, and PCK is selected as a file type.
+ [b]Note:[/b] The patches provided in [param patches] have already been loaded when this method is called and are merely provided as context. When empty the patches defined in the export preset have been loaded instead.
</description>
</method>
<method name="_export_project" qualifiers="virtual">
@@ -61,7 +75,21 @@
<description>
[b]Optional.[/b]
Create a ZIP archive at [param path] for the specified [param preset].
- This method is called when "Export PCK/ZIP" button is pressed in the export dialog, and ZIP is selected as a file type.
+ This method is called when "Export PCK/ZIP" button is pressed in the export dialog, with "Export as Patch" disabled, and ZIP is selected as a file type.
+ </description>
+ </method>
+ <method name="_export_zip_patch" qualifiers="virtual">
+ <return type="int" enum="Error" />
+ <param index="0" name="preset" type="EditorExportPreset" />
+ <param index="1" name="debug" type="bool" />
+ <param index="2" name="path" type="String" />
+ <param index="3" name="patches" type="PackedStringArray" />
+ <param index="4" name="flags" type="int" enum="EditorExportPlatform.DebugFlags" is_bitfield="true" />
+ <description>
+ [b]Optional.[/b]
+ Create a ZIP archive at [param path] for the specified [param preset], containing only the files that have changed since the last patch.
+ This method is called when "Export PCK/ZIP" button is pressed in the export dialog, with "Export as Patch" enabled, and ZIP is selected as a file type.
+ [b]Note:[/b] The patches provided in [param patches] have already been loaded when this method is called and are merely provided as context. When empty the patches defined in the export preset have been loaded instead.
</description>
</method>
<method name="_get_binary_extensions" qualifiers="virtual const">
diff --git a/doc/classes/EditorExportPreset.xml b/doc/classes/EditorExportPreset.xml
index bba79364e4..314f74340a 100644
--- a/doc/classes/EditorExportPreset.xml
+++ b/doc/classes/EditorExportPreset.xml
@@ -109,6 +109,12 @@
Returns export option value or value of environment variable if it is set.
</description>
</method>
+ <method name="get_patches" qualifiers="const">
+ <return type="PackedStringArray" />
+ <description>
+ Returns the list of packs on which to base a patch export on.
+ </description>
+ </method>
<method name="get_preset_name" qualifiers="const">
<return type="String" />
<description>
diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml
index 8c26509812..de3295fbe0 100644
--- a/doc/classes/Environment.xml
+++ b/doc/classes/Environment.xml
@@ -414,7 +414,7 @@
Linear tonemapper operator. Reads the linear data and passes it on unmodified. This can cause bright lighting to look blown out, with noticeable clipping in the output colors.
</constant>
<constant name="TONE_MAPPER_REINHARDT" value="1" enum="ToneMapper">
- Reinhardt tonemapper operator. Performs a variation on rendered pixels' colors by this formula: [code]color = color / (1 + color)[/code]. This avoids clipping bright highlights, but the resulting image can look a bit dull.
+ Reinhard tonemapper operator. Performs a variation on rendered pixels' colors by this formula: [code]color = color * (1 + color / (white * white)) / (1 + color)[/code]. This avoids clipping bright highlights, but the resulting image can look a bit dull. When [member tonemap_white] is left at the default value of [code]1.0[/code] this is identical to [constant TONE_MAPPER_LINEAR] while also being slightly less performant.
</constant>
<constant name="TONE_MAPPER_FILMIC" value="2" enum="ToneMapper">
Filmic tonemapper operator. This avoids clipping bright highlights, with a resulting image that usually looks more vivid than [constant TONE_MAPPER_REINHARDT].
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 144f78349f..a57f6adec8 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -2639,6 +2639,9 @@
- Position + Custom data: 16 floats (12 floats for Transform3D, 4 floats of custom data)
- Position + Vertex color + Custom data: 20 floats (12 floats for Transform3D, 4 floats for Color, 4 floats of custom data)
[/codeblock]
+ Instance transforms are in row-major order. Specifically:
+ - For [Transform2D] the float-order is: [code](x.x, y.x, padding_float, origin.x, x.y, y.y, padding_float, origin.y)[/code].
+ - For [Transform3D] the float-order is: [code](basis.x.x, basis.y.x, basis.z.x, origin.x, basis.x.y, basis.y.y, basis.z.y, origin.y, basis.x.z, basis.y.z, basis.z.z, origin.z)[/code].
</description>
</method>
<method name="multimesh_set_buffer_interpolated">
@@ -5219,7 +5222,7 @@
Output color as they came in. This can cause bright lighting to look blown out, with noticeable clipping in the output colors.
</constant>
<constant name="ENV_TONE_MAPPER_REINHARD" value="1" enum="EnvironmentToneMapper">
- Use the Reinhard tonemapper. Performs a variation on rendered pixels' colors by this formula: [code]color = color / (1 + color)[/code]. This avoids clipping bright highlights, but the resulting image can look a bit dull.
+ Use the Reinhard tonemapper. Performs a variation on rendered pixels' colors by this formula: [code]color = color * (1 + color / (white * white)) / (1 + color)[/code]. This avoids clipping bright highlights, but the resulting image can look a bit dull. When [member Environment.tonemap_white] is left at the default value of [code]1.0[/code] this is identical to [constant ENV_TONE_MAPPER_LINEAR] while also being slightly less performant.
</constant>
<constant name="ENV_TONE_MAPPER_FILMIC" value="2" enum="EnvironmentToneMapper">
Use the filmic tonemapper. This avoids clipping bright highlights, with a resulting image that usually looks more vivid than [constant ENV_TONE_MAPPER_REINHARD].
diff --git a/drivers/SCsub b/drivers/SCsub
index 44d29fb7c1..219c4451ee 100644
--- a/drivers/SCsub
+++ b/drivers/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/drivers/alsa/SCsub b/drivers/alsa/SCsub
index f17acb0f91..6242d0e359 100644
--- a/drivers/alsa/SCsub
+++ b/drivers/alsa/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/drivers/alsamidi/SCsub b/drivers/alsamidi/SCsub
index 4e1b5f2a36..69d667c57b 100644
--- a/drivers/alsamidi/SCsub
+++ b/drivers/alsamidi/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/drivers/backtrace/SCsub b/drivers/backtrace/SCsub
index f61fb21581..cc2cf0a6d8 100644
--- a/drivers/backtrace/SCsub
+++ b/drivers/backtrace/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/drivers/coreaudio/SCsub b/drivers/coreaudio/SCsub
index 4e1b5f2a36..69d667c57b 100644
--- a/drivers/coreaudio/SCsub
+++ b/drivers/coreaudio/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/drivers/coremidi/SCsub b/drivers/coremidi/SCsub
index 4e1b5f2a36..69d667c57b 100644
--- a/drivers/coremidi/SCsub
+++ b/drivers/coremidi/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/drivers/d3d12/SCsub b/drivers/d3d12/SCsub
index 482a549189..b6ceed23ac 100644
--- a/drivers/d3d12/SCsub
+++ b/drivers/d3d12/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
import os
from pathlib import Path
diff --git a/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp
index a445006058..52883de45e 100644
--- a/drivers/d3d12/rendering_device_driver_d3d12.cpp
+++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp
@@ -3244,7 +3244,7 @@ Vector<uint8_t> RenderingDeviceDriverD3D12::shader_compile_binary_from_spirv(Vec
DEV_ASSERT(binding_info.res_class == (uint32_t)RES_CLASS_INVALID || binding_info.res_class == (uint32_t)res_class);
binding_info.res_class = res_class;
} else if (p_dxil_type == DXIL_RES_SAMPLER) {
- binding_info.has_sampler = (uint32_t) true;
+ binding_info.has_sampler = (uint32_t)true;
} else {
CRASH_NOW();
}
diff --git a/drivers/egl/SCsub b/drivers/egl/SCsub
index 1fd15b4bae..3a9b484b83 100644
--- a/drivers/egl/SCsub
+++ b/drivers/egl/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/drivers/gl_context/SCsub b/drivers/gl_context/SCsub
index ce6ea747b1..a2ba425990 100644
--- a/drivers/gl_context/SCsub
+++ b/drivers/gl_context/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/drivers/gles3/SCsub b/drivers/gles3/SCsub
index 506312df80..4f4b33de03 100644
--- a/drivers/gles3/SCsub
+++ b/drivers/gles3/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/drivers/gles3/effects/SCsub b/drivers/gles3/effects/SCsub
index 91e1140b75..9ad6234fbe 100644
--- a/drivers/gles3/effects/SCsub
+++ b/drivers/gles3/effects/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/drivers/gles3/environment/SCsub b/drivers/gles3/environment/SCsub
index 91e1140b75..9ad6234fbe 100644
--- a/drivers/gles3/environment/SCsub
+++ b/drivers/gles3/environment/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/drivers/gles3/shaders/SCsub b/drivers/gles3/shaders/SCsub
index e70912cb4d..df2c515035 100644
--- a/drivers/gles3/shaders/SCsub
+++ b/drivers/gles3/shaders/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/drivers/gles3/shaders/effects/SCsub b/drivers/gles3/shaders/effects/SCsub
index 38b185ed88..387c317b90 100644
--- a/drivers/gles3/shaders/effects/SCsub
+++ b/drivers/gles3/shaders/effects/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/drivers/gles3/shaders/tonemap_inc.glsl b/drivers/gles3/shaders/tonemap_inc.glsl
index fb915aeb38..6738bdf748 100644
--- a/drivers/gles3/shaders/tonemap_inc.glsl
+++ b/drivers/gles3/shaders/tonemap_inc.glsl
@@ -76,8 +76,12 @@ vec3 tonemap_aces(vec3 color, float p_white) {
return color_tonemapped / p_white_tonemapped;
}
+// Based on Reinhard's extended formula, see equation 4 in https://doi.org/cjbgrt
vec3 tonemap_reinhard(vec3 color, float p_white) {
- return (p_white * color + color) / (color * p_white + p_white);
+ float white_squared = p_white * p_white;
+ vec3 white_squared_color = white_squared * color;
+ // Equivalent to color * (1 + color / white_squared) / (1 + color)
+ return (white_squared_color + color * color) / (white_squared_color + white_squared);
}
#define TONEMAPPER_LINEAR 0
@@ -85,7 +89,7 @@ vec3 tonemap_reinhard(vec3 color, float p_white) {
#define TONEMAPPER_FILMIC 2
#define TONEMAPPER_ACES 3
-vec3 apply_tonemapping(vec3 color, float p_white) { // inputs are LINEAR, always outputs clamped [0;1] color
+vec3 apply_tonemapping(vec3 color, float p_white) { // inputs are LINEAR
// Ensure color values passed to tonemappers are positive.
// They can be negative in the case of negative lights, which leads to undesired behavior.
if (tonemapper == TONEMAPPER_LINEAR) {
diff --git a/drivers/gles3/storage/SCsub b/drivers/gles3/storage/SCsub
index 91e1140b75..9ad6234fbe 100644
--- a/drivers/gles3/storage/SCsub
+++ b/drivers/gles3/storage/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/drivers/metal/SCsub b/drivers/metal/SCsub
index 30129b7806..f597580763 100644
--- a/drivers/metal/SCsub
+++ b/drivers/metal/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/drivers/metal/metal_objects.mm b/drivers/metal/metal_objects.mm
index 1d08a10781..596728212a 100644
--- a/drivers/metal/metal_objects.mm
+++ b/drivers/metal/metal_objects.mm
@@ -1212,7 +1212,7 @@ vertex VaryingsPos vertClear(AttributesPos attributes [[stage_in]], constant Cle
return varyings;
}
)",
- ClearAttKey::DEPTH_INDEX];
+ ClearAttKey::DEPTH_INDEX];
return new_func(msl, @"vertClear", nil);
}
diff --git a/drivers/png/SCsub b/drivers/png/SCsub
index e38f3c4760..fce37257b1 100644
--- a/drivers/png/SCsub
+++ b/drivers/png/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/drivers/pulseaudio/SCsub b/drivers/pulseaudio/SCsub
index f48489d787..6a76ff6d85 100644
--- a/drivers/pulseaudio/SCsub
+++ b/drivers/pulseaudio/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/drivers/unix/SCsub b/drivers/unix/SCsub
index 146563a3b6..bca4acfd74 100644
--- a/drivers/unix/SCsub
+++ b/drivers/unix/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp
index 32f2d7dd79..3d584341ed 100644
--- a/drivers/unix/file_access_unix.cpp
+++ b/drivers/unix/file_access_unix.cpp
@@ -294,7 +294,6 @@ uint64_t FileAccessUnix::_get_modified_time(const String &p_file) {
if (!err) {
return status.st_mtime;
} else {
- WARN_PRINT("Failed to get modified time for: " + p_file);
return 0;
}
}
diff --git a/drivers/vulkan/SCsub b/drivers/vulkan/SCsub
index 1efef5ad77..6ea7cc9a3b 100644
--- a/drivers/vulkan/SCsub
+++ b/drivers/vulkan/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/drivers/vulkan/rendering_context_driver_vulkan.h b/drivers/vulkan/rendering_context_driver_vulkan.h
index 4fbca012c6..f9352d617b 100644
--- a/drivers/vulkan/rendering_context_driver_vulkan.h
+++ b/drivers/vulkan/rendering_context_driver_vulkan.h
@@ -169,7 +169,7 @@ public:
static VkAllocationCallbacks *get_allocation_callbacks(VkObjectType p_type);
#if defined(VK_TRACK_DRIVER_MEMORY) || defined(VK_TRACK_DEVICE_MEMORY)
- enum VkTrackedObjectType{
+ enum VkTrackedObjectType {
VK_TRACKED_OBJECT_DESCRIPTOR_UPDATE_TEMPLATE_KHR = VK_OBJECT_TYPE_COMMAND_POOL + 1,
VK_TRACKED_OBJECT_TYPE_SURFACE,
VK_TRACKED_OBJECT_TYPE_SWAPCHAIN,
@@ -180,7 +180,7 @@ public:
VK_TRACKED_OBJECT_TYPE_COUNT
};
- enum VkTrackedSystemAllocationScope{
+ enum VkTrackedSystemAllocationScope {
VK_TRACKED_SYSTEM_ALLOCATION_SCOPE_COUNT = VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE + 1
};
#endif
diff --git a/drivers/wasapi/SCsub b/drivers/wasapi/SCsub
index 4e1b5f2a36..69d667c57b 100644
--- a/drivers/wasapi/SCsub
+++ b/drivers/wasapi/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/drivers/windows/SCsub b/drivers/windows/SCsub
index 91e1140b75..9ad6234fbe 100644
--- a/drivers/windows/SCsub
+++ b/drivers/windows/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp
index 9d6aa13332..a8a2ea6b5e 100644
--- a/drivers/windows/file_access_windows.cpp
+++ b/drivers/windows/file_access_windows.cpp
@@ -445,7 +445,6 @@ uint64_t FileAccessWindows::_get_modified_time(const String &p_file) {
}
}
- print_verbose("Failed to get modified time for: " + p_file);
return 0;
}
diff --git a/drivers/winmidi/SCsub b/drivers/winmidi/SCsub
index 4e1b5f2a36..69d667c57b 100644
--- a/drivers/winmidi/SCsub
+++ b/drivers/winmidi/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/drivers/xaudio2/SCsub b/drivers/xaudio2/SCsub
index 6778ad281e..cd210466a2 100644
--- a/drivers/xaudio2/SCsub
+++ b/drivers/xaudio2/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/drivers/xaudio2/audio_driver_xaudio2.cpp b/drivers/xaudio2/audio_driver_xaudio2.cpp
index 3fe3ea058b..0c2fdb64df 100644
--- a/drivers/xaudio2/audio_driver_xaudio2.cpp
+++ b/drivers/xaudio2/audio_driver_xaudio2.cpp
@@ -48,9 +48,9 @@ Error AudioDriverXAudio2::init() {
int latency = Engine::get_singleton()->get_audio_output_latency();
buffer_size = closest_power_of_2(latency * mix_rate / 1000);
- samples_in = memnew_arr(int32_t, buffer_size * channels);
+ samples_in = memnew_arr(int32_t, size_t(buffer_size) * channels);
for (int i = 0; i < AUDIO_BUFFERS; i++) {
- samples_out[i] = memnew_arr(int16_t, buffer_size * channels);
+ samples_out[i] = memnew_arr(int16_t, size_t(buffer_size) * channels);
xaudio_buffer[i].AudioBytes = buffer_size * channels * sizeof(int16_t);
xaudio_buffer[i].pAudioData = (const BYTE *)(samples_out[i]);
xaudio_buffer[i].Flags = 0;
diff --git a/editor/SCsub b/editor/SCsub
index 029048969a..9fcaf61245 100644
--- a/editor/SCsub
+++ b/editor/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/editor/debugger/SCsub b/editor/debugger/SCsub
index 99f1c888f0..e26d09d88b 100644
--- a/editor/debugger/SCsub
+++ b/editor/debugger/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/editor/debugger/debug_adapter/SCsub b/editor/debugger/debug_adapter/SCsub
index 359d04e5df..b3cff5b9dc 100644
--- a/editor/debugger/debug_adapter/SCsub
+++ b/editor/debugger/debug_adapter/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index 2b51071b15..bc46d01892 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -282,6 +282,21 @@ void EditorFileSystem::_first_scan_process_scripts(const ScannedDirectory *p_sca
}
for (const String &scan_file : p_scan_dir->files) {
+ // Optimization to skip the ResourceLoader::get_resource_type for files
+ // that are not scripts. Some loader get_resource_type methods read the file
+ // which can be very slow on large projects.
+ String ext = scan_file.get_extension().to_lower();
+ bool is_script = false;
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ if (ScriptServer::get_language(i)->get_extension() == ext) {
+ is_script = true;
+ break;
+ }
+ }
+ if (!is_script) {
+ continue; // Not a script.
+ }
+
String path = p_scan_dir->full_path.path_join(scan_file);
String type = ResourceLoader::get_resource_type(path);
@@ -371,6 +386,11 @@ void EditorFileSystem::_scan_filesystem() {
fc.script_class_name = split[7].get_slice("<>", 0);
fc.script_class_extends = split[7].get_slice("<>", 1);
fc.script_class_icon_path = split[7].get_slice("<>", 2);
+ fc.import_md5 = split[7].get_slice("<>", 3);
+ String dest_paths = split[7].get_slice("<>", 4);
+ if (!dest_paths.is_empty()) {
+ fc.import_dest_paths = dest_paths.split("<*>");
+ }
String deps = split[8].strip_edges();
if (deps.length()) {
@@ -463,12 +483,33 @@ void EditorFileSystem::_thread_func(void *_userdata) {
sd->_scan_filesystem();
}
-bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_imported_files) {
- if (!reimport_on_missing_imported_files && p_only_imported_files) {
- return false;
+bool EditorFileSystem::_is_test_for_reimport_needed(const String &p_path, uint64_t p_last_modification_time, uint64_t p_modification_time, uint64_t p_last_import_modification_time, uint64_t p_import_modification_time, const Vector<String> &p_import_dest_paths) {
+ // The idea here is to trust the cache. If the last modification times in the cache correspond
+ // to the last modification times of the files on disk, it means the files have not changed since
+ // the last import, and the files in .godot/imported (p_import_dest_paths) should all be valid.
+ if (p_last_modification_time != p_modification_time) {
+ return true;
+ }
+ if (p_last_import_modification_time != p_import_modification_time) {
+ return true;
+ }
+ if (reimport_on_missing_imported_files) {
+ for (const String &path : p_import_dest_paths) {
+ if (!FileAccess::exists(path)) {
+ return true;
+ }
+ }
}
+ return false;
+}
- if (!FileAccess::exists(p_path + ".import")) {
+bool EditorFileSystem::_test_for_reimport(const String &p_path, const String &p_expected_import_md5) {
+ if (p_expected_import_md5.is_empty()) {
+ // Marked as reimportation needed.
+ return true;
+ }
+ String new_md5 = FileAccess::get_md5(p_path + ".import");
+ if (p_expected_import_md5 != new_md5) {
return true;
}
@@ -489,7 +530,7 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo
int lines = 0;
String error_text;
- List<String> to_check;
+ Vector<String> to_check;
String importer_name;
String source_file = "";
@@ -498,6 +539,7 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo
String dest_md5 = "";
int version = 0;
bool found_uid = false;
+ Variant meta;
while (true) {
assign = Variant();
@@ -522,8 +564,8 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo
to_check.push_back(value);
} else if (assign == "files") {
Array fa = value;
- for (int i = 0; i < fa.size(); i++) {
- to_check.push_back(fa[i]);
+ for (const Variant &check_path : fa) {
+ to_check.push_back(check_path);
}
} else if (assign == "importer_version") {
version = value;
@@ -531,12 +573,12 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo
importer_name = value;
} else if (assign == "uid") {
found_uid = true;
- } else if (!p_only_imported_files) {
- if (assign == "source_file") {
- source_file = value;
- } else if (assign == "dest_files") {
- dest_files = value;
- }
+ } else if (assign == "source_file") {
+ source_file = value;
+ } else if (assign == "dest_files") {
+ dest_files = value;
+ } else if (assign == "metadata") {
+ meta = value;
}
} else if (next_tag.name != "remap" && next_tag.name != "deps") {
@@ -544,33 +586,40 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo
}
}
- if (!ResourceFormatImporter::get_singleton()->are_import_settings_valid(p_path)) {
- // Reimport settings are out of sync with project settings, reimport.
- return true;
- }
-
if (importer_name == "keep" || importer_name == "skip") {
- return false; //keep mode, do not reimport
+ return false; // Keep mode, do not reimport.
}
if (!found_uid) {
- return true; //UID not found, old format, reimport.
+ return true; // UID not found, old format, reimport.
+ }
+
+ // Imported files are gone, reimport.
+ for (const String &E : to_check) {
+ if (!FileAccess::exists(E)) {
+ return true;
+ }
}
Ref<ResourceImporter> importer = ResourceFormatImporter::get_singleton()->get_importer_by_name(importer_name);
if (importer.is_null()) {
- return true; // the importer has possibly changed, try to reimport.
+ return true; // The importer has possibly changed, try to reimport.
}
if (importer->get_format_version() > version) {
- return true; // version changed, reimport
+ return true; // Version changed, reimport.
+ }
+
+ if (!importer->are_import_settings_valid(p_path, meta)) {
+ // Reimport settings are out of sync with project settings, reimport.
+ return true;
}
- // Read the md5's from a separate file (so the import parameters aren't dependent on the file version
+ // Read the md5's from a separate file (so the import parameters aren't dependent on the file version).
String base_path = ResourceFormatImporter::get_singleton()->get_import_base_path(p_path);
Ref<FileAccess> md5s = FileAccess::open(base_path + ".md5", FileAccess::READ, &err);
- if (md5s.is_null()) { // No md5's stored for this resource
+ if (md5s.is_null()) { // No md5's stored for this resource.
return true;
}
@@ -588,50 +637,101 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo
break;
} else if (err != OK) {
ERR_PRINT("ResourceFormatImporter::load - '" + p_path + ".import.md5:" + itos(lines) + "' error '" + error_text + "'.");
- return false; // parse error
+ return false; // Parse error.
}
if (!assign.is_empty()) {
- if (!p_only_imported_files) {
- if (assign == "source_md5") {
- source_md5 = value;
- } else if (assign == "dest_md5") {
- dest_md5 = value;
- }
+ if (assign == "source_md5") {
+ source_md5 = value;
+ } else if (assign == "dest_md5") {
+ dest_md5 = value;
}
}
}
- //imported files are gone, reimport
- for (const String &E : to_check) {
- if (!FileAccess::exists(E)) {
+ // Check source md5 matching.
+ if (!source_file.is_empty() && source_file != p_path) {
+ return true; // File was moved, reimport.
+ }
+
+ if (source_md5.is_empty()) {
+ return true; // Lacks md5, so just reimport.
+ }
+
+ String md5 = FileAccess::get_md5(p_path);
+ if (md5 != source_md5) {
+ return true;
+ }
+
+ if (!dest_files.is_empty() && !dest_md5.is_empty()) {
+ md5 = FileAccess::get_multiple_md5(dest_files);
+ if (md5 != dest_md5) {
return true;
}
}
- //check source md5 matching
- if (!p_only_imported_files) {
- if (!source_file.is_empty() && source_file != p_path) {
- return true; //file was moved, reimport
- }
+ return false; // Nothing changed.
+}
- if (source_md5.is_empty()) {
- return true; //lacks md5, so just reimport
- }
+Vector<String> EditorFileSystem::_get_import_dest_paths(const String &p_path) {
+ Error err;
+ Ref<FileAccess> f = FileAccess::open(p_path + ".import", FileAccess::READ, &err);
- String md5 = FileAccess::get_md5(p_path);
- if (md5 != source_md5) {
- return true;
+ if (f.is_null()) { // No import file, reimport.
+ return Vector<String>();
+ }
+
+ VariantParser::StreamFile stream;
+ stream.f = f;
+
+ String assign;
+ Variant value;
+ VariantParser::Tag next_tag;
+
+ int lines = 0;
+ String error_text;
+
+ Vector<String> dest_paths;
+ String importer_name;
+
+ while (true) {
+ assign = Variant();
+ next_tag.fields.clear();
+ next_tag.name = String();
+
+ err = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, nullptr, true);
+ if (err == ERR_FILE_EOF) {
+ break;
+ } else if (err != OK) {
+ ERR_PRINT("ResourceFormatImporter::load - '" + p_path + ".import:" + itos(lines) + "' error '" + error_text + "'.");
+ // Parse error, skip and let user attempt manual reimport to avoid reimport loop.
+ return Vector<String>();
}
- if (dest_files.size() && !dest_md5.is_empty()) {
- md5 = FileAccess::get_multiple_md5(dest_files);
- if (md5 != dest_md5) {
- return true;
+ if (!assign.is_empty()) {
+ if (assign == "valid" && value.operator bool() == false) {
+ // Invalid import (failed previous import), skip and let user attempt manual reimport to avoid reimport loop.
+ return Vector<String>();
+ }
+ if (assign.begins_with("path")) {
+ dest_paths.push_back(value);
+ } else if (assign == "files") {
+ Array fa = value;
+ for (const Variant &dest_path : fa) {
+ dest_paths.push_back(dest_path);
+ }
+ } else if (assign == "importer") {
+ importer_name = value;
}
+ } else if (next_tag.name != "remap" && next_tag.name != "deps") {
+ break;
}
}
- return false; //nothing changed
+ if (importer_name == "keep" || importer_name == "skip") {
+ return Vector<String>();
+ }
+
+ return dest_paths;
}
bool EditorFileSystem::_scan_import_support(const Vector<String> &reimports) {
@@ -774,20 +874,9 @@ bool EditorFileSystem::_update_scan_actions() {
ERR_CONTINUE(idx == -1);
String full_path = ia.dir->get_file_path(idx);
- bool need_reimport = _test_for_reimport(full_path, false);
- // Workaround GH-94416 for the Android editor for now.
- // `import_mt` seems to always be 0 and force a reimport on any fs scan.
-#ifndef ANDROID_ENABLED
- if (!need_reimport && FileAccess::exists(full_path + ".import")) {
- uint64_t import_mt = ia.dir->get_file_import_modified_time(idx);
- if (import_mt != FileAccess::get_modified_time(full_path + ".import")) {
- need_reimport = true;
- }
- }
-#endif
-
+ bool need_reimport = _test_for_reimport(full_path, ia.dir->files[idx]->import_md5);
if (need_reimport) {
- //must reimport
+ // Must reimport.
reimports.push_back(full_path);
Vector<String> dependencies = _get_dependencies(full_path);
for (const String &dep : dependencies) {
@@ -797,10 +886,14 @@ bool EditorFileSystem::_update_scan_actions() {
}
}
} else {
- //must not reimport, all was good
- //update modified times, to avoid reimport
+ // Must not reimport, all was good.
+ // Update modified times, md5 and destination paths, to avoid reimport.
ia.dir->files[idx]->modified_time = FileAccess::get_modified_time(full_path);
ia.dir->files[idx]->import_modified_time = FileAccess::get_modified_time(full_path + ".import");
+ if (ia.dir->files[idx]->import_md5.is_empty()) {
+ ia.dir->files[idx]->import_md5 = FileAccess::get_md5(full_path + ".import");
+ }
+ ia.dir->files[idx]->import_dest_paths = _get_import_dest_paths(full_path);
}
fs_changed = true;
@@ -1029,26 +1122,36 @@ void EditorFileSystem::_process_file_system(const ScannedDirectory *p_scan_dir,
if (import_extensions.has(ext)) {
//is imported
- uint64_t import_mt = 0;
- if (FileAccess::exists(path + ".import")) {
- import_mt = FileAccess::get_modified_time(path + ".import");
- }
+ uint64_t import_mt = FileAccess::get_modified_time(path + ".import");
- if (fc && fc->modification_time == mt && fc->import_modification_time == import_mt && !_test_for_reimport(path, true)) {
+ if (fc) {
fi->type = fc->type;
fi->resource_script_class = fc->resource_script_class;
fi->uid = fc->uid;
fi->deps = fc->deps;
- fi->modified_time = fc->modification_time;
- fi->import_modified_time = fc->import_modification_time;
-
+ fi->modified_time = mt;
+ fi->import_modified_time = import_mt;
+ fi->import_md5 = fc->import_md5;
+ fi->import_dest_paths = fc->import_dest_paths;
fi->import_valid = fc->import_valid;
fi->script_class_name = fc->script_class_name;
fi->import_group_file = fc->import_group_file;
fi->script_class_extends = fc->script_class_extends;
fi->script_class_icon_path = fc->script_class_icon_path;
- if (revalidate_import_files && !ResourceFormatImporter::get_singleton()->are_import_settings_valid(path)) {
+ // Ensures backward compatibility when the project is loaded for the first time with the added import_md5
+ // and import_dest_paths properties in the file cache.
+ if (fc->import_md5.is_empty()) {
+ fi->import_md5 = FileAccess::get_md5(path + ".import");
+ fi->import_dest_paths = _get_import_dest_paths(path);
+ }
+
+ // The method _is_test_for_reimport_needed checks if the files were modified and ensures that
+ // all the destination files still exist without reading the .import file.
+ // If something is different, we will queue a test for reimportation that will check
+ // the md5 of all files and import settings and, if necessary, execute a reimportation.
+ if (_is_test_for_reimport_needed(path, fc->modification_time, mt, fc->import_modification_time, import_mt, fi->import_dest_paths) ||
+ (revalidate_import_files && !ResourceFormatImporter::get_singleton()->are_import_settings_valid(path))) {
ItemAction ia;
ia.action = ItemAction::ACTION_FILE_TEST_REIMPORT;
ia.dir = p_dir;
@@ -1075,6 +1178,8 @@ void EditorFileSystem::_process_file_system(const ScannedDirectory *p_scan_dir,
fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends, &fi->script_class_icon_path);
fi->modified_time = 0;
fi->import_modified_time = 0;
+ fi->import_md5 = "";
+ fi->import_dest_paths = Vector<String>();
fi->import_valid = (fi->type == "TextFile" || fi->type == "OtherFile") ? true : ResourceLoader::is_import_valid(path);
ItemAction ia;
@@ -1089,9 +1194,11 @@ void EditorFileSystem::_process_file_system(const ScannedDirectory *p_scan_dir,
fi->type = fc->type;
fi->resource_script_class = fc->resource_script_class;
fi->uid = fc->uid;
- fi->modified_time = fc->modification_time;
+ fi->modified_time = mt;
fi->deps = fc->deps;
fi->import_modified_time = 0;
+ fi->import_md5 = "";
+ fi->import_dest_paths = Vector<String>();
fi->import_valid = true;
fi->script_class_name = fc->script_class_name;
fi->script_class_extends = fc->script_class_extends;
@@ -1126,6 +1233,8 @@ void EditorFileSystem::_process_file_system(const ScannedDirectory *p_scan_dir,
fi->deps = _get_dependencies(path);
fi->modified_time = mt;
fi->import_modified_time = 0;
+ fi->import_md5 = "";
+ fi->import_dest_paths = Vector<String>();
fi->import_valid = true;
// Files in dep_update_list are forced for rescan to update dependencies. They don't need other updates.
@@ -1269,6 +1378,8 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanPr
String path = cd.path_join(fi->file);
fi->modified_time = FileAccess::get_modified_time(path);
fi->import_modified_time = 0;
+ fi->import_md5 = "";
+ fi->import_dest_paths = Vector<String>();
fi->type = ResourceLoader::get_resource_type(path);
fi->resource_script_class = ResourceLoader::get_resource_script_class(path);
if (fi->type == "" && textfile_extensions.has(ext)) {
@@ -1323,26 +1434,13 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanPr
String path = cd.path_join(p_dir->files[i]->file);
if (import_extensions.has(p_dir->files[i]->file.get_extension().to_lower())) {
- //check here if file must be imported or not
-
+ // Check here if file must be imported or not.
+ // Same logic as in _process_file_system, the last modifications dates
+ // needs to be trusted to prevent reading all the .import files and the md5
+ // each time the user switch back to Godot.
uint64_t mt = FileAccess::get_modified_time(path);
-
- bool reimport = false;
-
- if (mt != p_dir->files[i]->modified_time) {
- reimport = true; //it was modified, must be reimported.
- } else if (!FileAccess::exists(path + ".import")) {
- reimport = true; //no .import file, obviously reimport
- } else {
- uint64_t import_mt = FileAccess::get_modified_time(path + ".import");
- if (import_mt != p_dir->files[i]->import_modified_time) {
- reimport = true;
- } else if (_test_for_reimport(path, true)) {
- reimport = true;
- }
- }
-
- if (reimport) {
+ uint64_t import_mt = FileAccess::get_modified_time(path + ".import");
+ if (_is_test_for_reimport_needed(path, p_dir->files[i]->modified_time, mt, p_dir->files[i]->import_modified_time, import_mt, p_dir->files[i]->import_dest_paths)) {
ItemAction ia;
ia.action = ItemAction::ACTION_FILE_TEST_REIMPORT;
ia.dir = p_dir;
@@ -1625,7 +1723,7 @@ void EditorFileSystem::_save_filesystem_cache(EditorFileSystemDirectory *p_dir,
cache_string.append(itos(file_info->import_modified_time));
cache_string.append(itos(file_info->import_valid));
cache_string.append(file_info->import_group_file);
- cache_string.append(String("<>").join({ file_info->script_class_name, file_info->script_class_extends, file_info->script_class_icon_path }));
+ cache_string.append(String("<>").join({ file_info->script_class_name, file_info->script_class_extends, file_info->script_class_icon_path, file_info->import_md5, String("<*>").join(file_info->import_dest_paths) }));
cache_string.append(String("<>").join(file_info->deps));
p_file->store_line(String("::").join(cache_string));
@@ -1879,6 +1977,10 @@ void EditorFileSystem::_update_file_icon_path(EditorFileSystemDirectory::FileInf
}
}
+ if (icon_path.is_empty() && !file_info->type.is_empty()) {
+ icon_path = EditorNode::get_singleton()->get_class_icon(file_info->type)->get_path();
+ }
+
file_info->icon_path = icon_path;
}
@@ -2183,6 +2285,8 @@ void EditorFileSystem::update_files(const Vector<String> &p_script_paths) {
fi->file = file_name;
fi->import_modified_time = 0;
fi->import_valid = (type == "TextFile" || type == "OtherFile") ? true : ResourceLoader::is_import_valid(file);
+ fi->import_md5 = "";
+ fi->import_dest_paths = Vector<String>();
if (idx == fs->files.size()) {
fs->files.push_back(fi);
@@ -2227,7 +2331,7 @@ void EditorFileSystem::update_files(const Vector<String> &p_script_paths) {
_queue_update_scene_groups(file);
}
- if (fs->files[cpos]->type == SNAME("Resource")) {
+ 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) {
update_files_icon_cache = true;
@@ -2458,6 +2562,8 @@ Error EditorFileSystem::_reimport_group(const String &p_group_file, const Vector
//update modified times, to avoid reimport
fs->files[cpos]->modified_time = FileAccess::get_modified_time(file);
fs->files[cpos]->import_modified_time = FileAccess::get_modified_time(file + ".import");
+ fs->files[cpos]->import_md5 = FileAccess::get_md5(file + ".import");
+ fs->files[cpos]->import_dest_paths = dest_paths;
fs->files[cpos]->deps = _get_dependencies(file);
fs->files[cpos]->uid = uid;
fs->files[cpos]->type = importer->get_resource_type();
@@ -2559,11 +2665,13 @@ Error EditorFileSystem::_reimport_file(const String &p_file, const HashMap<Strin
if (p_update_file_system) {
fs->files[cpos]->modified_time = FileAccess::get_modified_time(p_file);
fs->files[cpos]->import_modified_time = FileAccess::get_modified_time(p_file + ".import");
+ fs->files[cpos]->import_md5 = FileAccess::get_md5(p_file + ".import");
+ fs->files[cpos]->import_dest_paths = Vector<String>();
fs->files[cpos]->deps.clear();
fs->files[cpos]->type = "";
fs->files[cpos]->import_valid = false;
+ EditorResourcePreview::get_singleton()->check_for_invalidation(p_file);
}
- EditorResourcePreview::get_singleton()->check_for_invalidation(p_file);
return OK;
}
Ref<ResourceImporter> importer;
@@ -2729,6 +2837,8 @@ Error EditorFileSystem::_reimport_file(const String &p_file, const HashMap<Strin
// Update modified times, to avoid reimport.
fs->files[cpos]->modified_time = FileAccess::get_modified_time(p_file);
fs->files[cpos]->import_modified_time = FileAccess::get_modified_time(p_file + ".import");
+ fs->files[cpos]->import_md5 = FileAccess::get_md5(p_file + ".import");
+ fs->files[cpos]->import_dest_paths = dest_paths;
fs->files[cpos]->deps = _get_dependencies(p_file);
fs->files[cpos]->type = importer->get_resource_type();
fs->files[cpos]->uid = uid;
diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h
index e53187c1d7..255eaff10d 100644
--- a/editor/editor_file_system.h
+++ b/editor/editor_file_system.h
@@ -60,6 +60,8 @@ class EditorFileSystemDirectory : public Object {
ResourceUID::ID uid = ResourceUID::INVALID_ID;
uint64_t modified_time = 0;
uint64_t import_modified_time = 0;
+ String import_md5;
+ Vector<String> import_dest_paths;
bool import_valid = false;
String import_group_file;
Vector<String> deps;
@@ -206,6 +208,8 @@ class EditorFileSystem : public Node {
ResourceUID::ID uid = ResourceUID::INVALID_ID;
uint64_t modification_time = 0;
uint64_t import_modification_time = 0;
+ String import_md5;
+ Vector<String> import_dest_paths;
Vector<String> deps;
bool import_valid = false;
String import_group_file;
@@ -264,7 +268,9 @@ class EditorFileSystem : public Node {
Error _reimport_file(const String &p_file, const HashMap<StringName, Variant> &p_custom_options = HashMap<StringName, Variant>(), const String &p_custom_importer = String(), Variant *generator_parameters = nullptr, bool p_update_file_system = true);
Error _reimport_group(const String &p_group_file, const Vector<String> &p_files);
- bool _test_for_reimport(const String &p_path, bool p_only_imported_files);
+ bool _test_for_reimport(const String &p_path, const String &p_expected_import_md5);
+ bool _is_test_for_reimport_needed(const String &p_path, uint64_t p_last_modification_time, uint64_t p_modification_time, uint64_t p_last_import_modification_time, uint64_t p_import_modification_time, const Vector<String> &p_import_dest_paths);
+ Vector<String> _get_import_dest_paths(const String &p_path);
bool reimport_on_missing_imported_files;
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index f248d03140..665255b9b2 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -1007,9 +1007,17 @@ void EditorNode::_fs_changed() {
export_preset->update_value_overrides();
if (export_defer.pack_only) { // Only export .pck or .zip data pack.
if (export_path.ends_with(".zip")) {
- err = platform->export_zip(export_preset, export_defer.debug, export_path);
+ if (export_defer.patch) {
+ err = platform->export_zip_patch(export_preset, export_defer.debug, export_path, export_defer.patches);
+ } else {
+ err = platform->export_zip(export_preset, export_defer.debug, export_path);
+ }
} else if (export_path.ends_with(".pck")) {
- err = platform->export_pack(export_preset, export_defer.debug, export_path);
+ if (export_defer.patch) {
+ err = platform->export_pack_patch(export_preset, export_defer.debug, export_path, export_defer.patches);
+ } else {
+ err = platform->export_pack(export_preset, export_defer.debug, export_path);
+ }
} else {
ERR_PRINT(vformat("Export path \"%s\" doesn't end with a supported extension.", export_path));
err = FAILED;
@@ -5149,12 +5157,14 @@ void EditorNode::_begin_first_scan() {
requested_first_scan = true;
}
-Error EditorNode::export_preset(const String &p_preset, const String &p_path, bool p_debug, bool p_pack_only, bool p_android_build_template) {
+Error EditorNode::export_preset(const String &p_preset, const String &p_path, bool p_debug, bool p_pack_only, bool p_android_build_template, bool p_patch, const Vector<String> &p_patches) {
export_defer.preset = p_preset;
export_defer.path = p_path;
export_defer.debug = p_debug;
export_defer.pack_only = p_pack_only;
export_defer.android_build_template = p_android_build_template;
+ export_defer.patch = p_patch;
+ export_defer.patches = p_patches;
cmdline_export_mode = true;
return OK;
}
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 7ef38b4edb..36332e3d78 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -246,6 +246,8 @@ private:
bool debug = false;
bool pack_only = false;
bool android_build_template = false;
+ bool patch = false;
+ Vector<String> patches;
} export_defer;
static EditorNode *singleton;
@@ -880,7 +882,7 @@ public:
void _copy_warning(const String &p_str);
- Error export_preset(const String &p_preset, const String &p_path, bool p_debug, bool p_pack_only, bool p_android_build_template);
+ Error export_preset(const String &p_preset, const String &p_path, bool p_debug, bool p_pack_only, bool p_android_build_template, bool p_patch, const Vector<String> &p_patches);
bool is_project_exporting() const;
Control *get_gui_base() { return gui_base; }
diff --git a/editor/editor_undo_redo_manager.cpp b/editor/editor_undo_redo_manager.cpp
index 2e96ae82fc..57e55e7bac 100644
--- a/editor/editor_undo_redo_manager.cpp
+++ b/editor/editor_undo_redo_manager.cpp
@@ -264,6 +264,22 @@ void EditorUndoRedoManager::commit_action(bool p_execute) {
history.undo_stack.push_back(pending_action);
}
+ if (history.id != GLOBAL_HISTORY) {
+ // Clear global redo, to avoid unexpected actions when redoing.
+ History &global = get_or_create_history(GLOBAL_HISTORY);
+ global.redo_stack.clear();
+ global.undo_redo->discard_redo();
+ } else {
+ // On global actions, clear redo of all scenes instead.
+ for (KeyValue<int, History> &E : history_map) {
+ if (E.key == GLOBAL_HISTORY) {
+ continue;
+ }
+ E.value.redo_stack.clear();
+ E.value.undo_redo->discard_redo();
+ }
+ }
+
pending_action = Action();
is_committing = false;
emit_signal(SNAME("history_changed"));
diff --git a/editor/export/SCsub b/editor/export/SCsub
index 359d04e5df..b3cff5b9dc 100644
--- a/editor/export/SCsub
+++ b/editor/export/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/editor/export/editor_export.cpp b/editor/export/editor_export.cpp
index 975a601ae1..6ca83c5e25 100644
--- a/editor/export/editor_export.cpp
+++ b/editor/export/editor_export.cpp
@@ -83,6 +83,8 @@ void EditorExport::_save() {
config->set_value(section, "include_filter", preset->get_include_filter());
config->set_value(section, "exclude_filter", preset->get_exclude_filter());
config->set_value(section, "export_path", preset->get_export_path());
+ config->set_value(section, "patches", preset->get_patches());
+
config->set_value(section, "encryption_include_filters", preset->get_enc_in_filter());
config->set_value(section, "encryption_exclude_filters", preset->get_enc_ex_filter());
config->set_value(section, "encrypt_pck", preset->get_enc_pck());
@@ -303,6 +305,7 @@ void EditorExport::load_config() {
preset->set_exclude_filter(config->get_value(section, "exclude_filter"));
preset->set_export_path(config->get_value(section, "export_path", ""));
preset->set_script_export_mode(config->get_value(section, "script_export_mode", EditorExportPreset::MODE_SCRIPT_BINARY_TOKENS_COMPRESSED));
+ preset->set_patches(config->get_value(section, "patches", Vector<String>()));
if (config->has_section_key(section, "encrypt_pck")) {
preset->set_enc_pck(config->get_value(section, "encrypt_pck"));
diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp
index 983f4ee36a..58737c53ed 100644
--- a/editor/export/editor_export_platform.cpp
+++ b/editor/export/editor_export_platform.cpp
@@ -167,6 +167,44 @@ bool EditorExportPlatform::fill_log_messages(RichTextLabel *p_log, Error p_err)
return has_messages;
}
+bool EditorExportPlatform::_check_hash(const uint8_t *p_hash, const Vector<uint8_t> &p_data) {
+ if (p_hash == nullptr) {
+ return false;
+ }
+
+ unsigned char hash[16];
+ Error err = CryptoCore::md5(p_data.ptr(), p_data.size(), hash);
+ if (err != OK) {
+ return false;
+ }
+
+ for (int i = 0; i < 16; i++) {
+ if (p_hash[i] != hash[i]) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+Error EditorExportPlatform::_load_patches(const Vector<String> &p_patches) {
+ Error err = OK;
+ if (!p_patches.is_empty()) {
+ for (const String &path : p_patches) {
+ err = PackedData::get_singleton()->add_pack(path, true, 0);
+ if (err != OK) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Patch Creation"), vformat(TTR("Could not load patch pack with path \"%s\"."), path));
+ return err;
+ }
+ }
+ }
+ return err;
+}
+
+void EditorExportPlatform::_unload_patches() {
+ PackedData::get_singleton()->clear();
+}
+
Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) {
ERR_FAIL_COND_V_MSG(p_total < 1, ERR_PARAMETER_RANGE_ERROR, "Must select at least one file to export.");
@@ -237,6 +275,14 @@ Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_pa
return OK;
}
+Error EditorExportPlatform::_save_pack_patch_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) {
+ if (_check_hash(PackedData::get_singleton()->get_file_hash(p_path), p_data)) {
+ return OK;
+ }
+
+ return _save_pack_file(p_userdata, p_path, p_data, p_file, p_total, p_enc_in_filters, p_enc_ex_filters, p_key);
+}
+
Error EditorExportPlatform::_save_zip_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) {
ERR_FAIL_COND_V_MSG(p_total < 1, ERR_PARAMETER_RANGE_ERROR, "Must select at least one file to export.");
@@ -260,6 +306,8 @@ Error EditorExportPlatform::_save_zip_file(void *p_userdata, const String &p_pat
zipWriteInFileInZip(zip, p_data.ptr(), p_data.size());
zipCloseFileInZip(zip);
+ zd->file_count += 1;
+
if (zd->ep->step(TTR("Storing File:") + " " + p_path, 2 + p_file * 100 / p_total, false)) {
return ERR_SKIP;
}
@@ -267,6 +315,14 @@ Error EditorExportPlatform::_save_zip_file(void *p_userdata, const String &p_pat
return OK;
}
+Error EditorExportPlatform::_save_zip_patch_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) {
+ if (_check_hash(PackedData::get_singleton()->get_file_hash(p_path), p_data)) {
+ return OK;
+ }
+
+ return _save_zip_file(p_userdata, p_path, p_data, p_file, p_total, p_enc_in_filters, p_enc_ex_filters, p_key);
+}
+
Ref<ImageTexture> EditorExportPlatform::get_option_icon(int p_index) const {
Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();
ERR_FAIL_COND_V(theme.is_null(), Ref<ImageTexture>());
@@ -1561,7 +1617,7 @@ Dictionary EditorExportPlatform::_save_pack(const Ref<EditorExportPreset> &p_pre
Vector<SharedObject> so_files;
int64_t embedded_start = 0;
int64_t embedded_size = 0;
- Error err_code = save_pack(p_preset, p_debug, p_path, &so_files, p_embed, &embedded_start, &embedded_size);
+ Error err_code = save_pack(p_preset, p_debug, p_path, &so_files, nullptr, p_embed, &embedded_start, &embedded_size);
Dictionary ret;
ret["result"] = err_code;
@@ -1605,9 +1661,55 @@ Dictionary EditorExportPlatform::_save_zip(const Ref<EditorExportPreset> &p_pres
return ret;
}
-Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files, bool p_embed, int64_t *r_embedded_start, int64_t *r_embedded_size) {
+Dictionary EditorExportPlatform::_save_pack_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) {
+ Vector<SharedObject> so_files;
+ Error err_code = save_pack_patch(p_preset, p_debug, p_path, &so_files);
+
+ Dictionary ret;
+ ret["result"] = err_code;
+ if (err_code == OK) {
+ Array arr;
+ for (const SharedObject &E : so_files) {
+ Dictionary so;
+ so["path"] = E.path;
+ so["tags"] = E.tags;
+ so["target_folder"] = E.target;
+ arr.push_back(so);
+ }
+ ret["so_files"] = arr;
+ }
+
+ return ret;
+}
+
+Dictionary EditorExportPlatform::_save_zip_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) {
+ Vector<SharedObject> so_files;
+ Error err_code = save_zip_patch(p_preset, p_debug, p_path, &so_files);
+
+ Dictionary ret;
+ ret["result"] = err_code;
+ if (err_code == OK) {
+ Array arr;
+ for (const SharedObject &E : so_files) {
+ Dictionary so;
+ so["path"] = E.path;
+ so["tags"] = E.tags;
+ so["target_folder"] = E.target;
+ arr.push_back(so);
+ }
+ ret["so_files"] = arr;
+ }
+
+ return ret;
+}
+
+Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files, EditorExportSaveFunction p_save_func, bool p_embed, int64_t *r_embedded_start, int64_t *r_embedded_size) {
EditorProgress ep("savepack", TTR("Packing"), 102, true);
+ if (p_save_func == nullptr) {
+ p_save_func = _save_pack_file;
+ }
+
// Create the temporary export directory if it doesn't exist.
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
da->make_dir_recursive(EditorPaths::get_singleton()->get_cache_dir());
@@ -1624,7 +1726,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b
pd.f = ftmp;
pd.so_files = p_so_files;
- Error err = export_project_files(p_preset, p_debug, _save_pack_file, &pd, _pack_add_shared_object);
+ Error err = export_project_files(p_preset, p_debug, p_save_func, &pd, _pack_add_shared_object);
// Close temp file.
pd.f.unref();
@@ -1636,6 +1738,12 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b
return err;
}
+ if (pd.file_ofs.is_empty()) {
+ DirAccess::remove_file_or_error(tmppath);
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Save PCK"), TTR("No files or changes to export."));
+ return FAILED;
+ }
+
pd.file_ofs.sort(); //do sort, so we can do binary search later
Ref<FileAccess> f;
@@ -1831,28 +1939,56 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b
return OK;
}
-Error EditorExportPlatform::save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files) {
+Error EditorExportPlatform::save_pack_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files, bool p_embed, int64_t *r_embedded_start, int64_t *r_embedded_size) {
+ return save_pack(p_preset, p_debug, p_path, p_so_files, _save_pack_patch_file, p_embed, r_embedded_start, r_embedded_size);
+}
+
+Error EditorExportPlatform::save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files, EditorExportSaveFunction p_save_func) {
EditorProgress ep("savezip", TTR("Packing"), 102, true);
+ if (p_save_func == nullptr) {
+ p_save_func = _save_zip_file;
+ }
+
+ String tmppath = EditorPaths::get_singleton()->get_cache_dir().path_join("packtmp");
+
Ref<FileAccess> io_fa;
zlib_filefunc_def io = zipio_create_io(&io_fa);
- zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io);
+ zipFile zip = zipOpen2(tmppath.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io);
ZipData zd;
zd.ep = &ep;
zd.zip = zip;
zd.so_files = p_so_files;
- Error err = export_project_files(p_preset, p_debug, _save_zip_file, &zd, _zip_add_shared_object);
+ Error err = export_project_files(p_preset, p_debug, p_save_func, &zd, _zip_add_shared_object);
if (err != OK && err != ERR_SKIP) {
add_message(EXPORT_MESSAGE_ERROR, TTR("Save ZIP"), TTR("Failed to export project files."));
}
zipClose(zip, nullptr);
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+
+ if (zd.file_count == 0) {
+ da->remove(tmppath);
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Save PCK"), TTR("No files or changes to export."));
+ return FAILED;
+ }
+
+ err = da->rename(tmppath, p_path);
+ if (err != OK) {
+ da->remove(tmppath);
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Save ZIP"), vformat(TTR("Failed to move temporary file \"%s\" to \"%s\"."), tmppath, p_path));
+ }
+
return OK;
}
+Error EditorExportPlatform::save_zip_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files) {
+ return save_zip(p_preset, p_debug, p_path, p_so_files, _save_zip_patch_file);
+}
+
Error EditorExportPlatform::export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) {
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
return save_pack(p_preset, p_debug, p_path);
@@ -1863,6 +1999,28 @@ Error EditorExportPlatform::export_zip(const Ref<EditorExportPreset> &p_preset,
return save_zip(p_preset, p_debug, p_path);
}
+Error EditorExportPlatform::export_pack_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, const Vector<String> &p_patches, BitField<EditorExportPlatform::DebugFlags> p_flags) {
+ ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
+ Error err = _load_patches(p_patches.is_empty() ? p_preset->get_patches() : p_patches);
+ if (err != OK) {
+ return err;
+ }
+ err = save_pack_patch(p_preset, p_debug, p_path);
+ _unload_patches();
+ return err;
+}
+
+Error EditorExportPlatform::export_zip_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, const Vector<String> &p_patches, BitField<EditorExportPlatform::DebugFlags> p_flags) {
+ ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
+ Error err = _load_patches(p_patches.is_empty() ? p_preset->get_patches() : p_patches);
+ if (err != OK) {
+ return err;
+ }
+ err = save_zip_patch(p_preset, p_debug, p_path);
+ _unload_patches();
+ return err;
+}
+
Vector<String> EditorExportPlatform::gen_export_flags(BitField<EditorExportPlatform::DebugFlags> p_flags) {
Vector<String> ret;
String host = EDITOR_GET("network/debug/remote_host");
@@ -2115,6 +2273,8 @@ void EditorExportPlatform::_bind_methods() {
ClassDB::bind_method(D_METHOD("save_pack", "preset", "debug", "path", "embed"), &EditorExportPlatform::_save_pack, DEFVAL(false));
ClassDB::bind_method(D_METHOD("save_zip", "preset", "debug", "path"), &EditorExportPlatform::_save_zip);
+ ClassDB::bind_method(D_METHOD("save_pack_patch", "preset", "debug", "path"), &EditorExportPlatform::_save_pack_patch);
+ ClassDB::bind_method(D_METHOD("save_zip_patch", "preset", "debug", "path"), &EditorExportPlatform::_save_zip_patch);
ClassDB::bind_method(D_METHOD("gen_export_flags", "flags"), &EditorExportPlatform::gen_export_flags);
@@ -2123,6 +2283,8 @@ void EditorExportPlatform::_bind_methods() {
ClassDB::bind_method(D_METHOD("export_project", "preset", "debug", "path", "flags"), &EditorExportPlatform::export_project, DEFVAL(0));
ClassDB::bind_method(D_METHOD("export_pack", "preset", "debug", "path", "flags"), &EditorExportPlatform::export_pack, DEFVAL(0));
ClassDB::bind_method(D_METHOD("export_zip", "preset", "debug", "path", "flags"), &EditorExportPlatform::export_zip, DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("export_pack_patch", "preset", "debug", "path", "patches", "flags"), &EditorExportPlatform::export_pack_patch, DEFVAL(PackedStringArray()), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("export_zip_patch", "preset", "debug", "path", "patches", "flags"), &EditorExportPlatform::export_zip_patch, DEFVAL(PackedStringArray()), DEFVAL(0));
ClassDB::bind_method(D_METHOD("clear_messages"), &EditorExportPlatform::clear_messages);
ClassDB::bind_method(D_METHOD("add_message", "type", "category", "message"), &EditorExportPlatform::add_message);
diff --git a/editor/export/editor_export_platform.h b/editor/export/editor_export_platform.h
index 95f21ceafb..ef3274c5e4 100644
--- a/editor/export/editor_export_platform.h
+++ b/editor/export/editor_export_platform.h
@@ -101,6 +101,7 @@ private:
void *zip = nullptr;
EditorProgress *ep = nullptr;
Vector<SharedObject> *so_files = nullptr;
+ int file_count = 0;
};
Vector<ExportMessage> messages;
@@ -109,10 +110,14 @@ private:
void _export_find_customized_resources(const Ref<EditorExportPreset> &p_preset, EditorFileSystemDirectory *p_dir, EditorExportPreset::FileExportMode p_mode, HashSet<String> &p_paths);
void _export_find_dependencies(const String &p_path, HashSet<String> &p_paths);
+ static bool _check_hash(const uint8_t *p_hash, const Vector<uint8_t> &p_data);
+
static Error _save_pack_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key);
+ static Error _save_pack_patch_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key);
static Error _pack_add_shared_object(void *p_userdata, const SharedObject &p_so);
static Error _save_zip_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key);
+ static Error _save_zip_patch_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key);
static Error _zip_add_shared_object(void *p_userdata, const SharedObject &p_so);
struct ScriptCallbackData {
@@ -188,6 +193,9 @@ protected:
Error ssh_run_on_remote_no_wait(const String &p_host, const String &p_port, const Vector<String> &p_ssh_args, const String &p_cmd_args, OS::ProcessID *r_pid = nullptr, int p_port_fwd = -1) const;
Error ssh_push_to_remote(const String &p_host, const String &p_port, const Vector<String> &p_scp_args, const String &p_src_file, const String &p_dst_file) const;
+ Error _load_patches(const Vector<String> &p_patches);
+ void _unload_patches();
+
public:
virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const = 0;
@@ -284,8 +292,14 @@ public:
Dictionary _save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, bool p_embed = false);
Dictionary _save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path);
- Error save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files = nullptr, bool p_embed = false, int64_t *r_embedded_start = nullptr, int64_t *r_embedded_size = nullptr);
- Error save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files = nullptr);
+ Dictionary _save_pack_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path);
+ Dictionary _save_zip_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path);
+
+ Error save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files = nullptr, EditorExportSaveFunction p_save_func = nullptr, bool p_embed = false, int64_t *r_embedded_start = nullptr, int64_t *r_embedded_size = nullptr);
+ Error save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files = nullptr, EditorExportSaveFunction p_save_func = nullptr);
+
+ Error save_pack_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files = nullptr, bool p_embed = false, int64_t *r_embedded_start = nullptr, int64_t *r_embedded_size = nullptr);
+ Error save_zip_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files = nullptr);
virtual bool poll_export() { return false; }
virtual int get_options_count() const { return 0; }
@@ -307,6 +321,8 @@ public:
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) = 0;
virtual Error export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0);
virtual Error export_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0);
+ virtual Error export_pack_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, const Vector<String> &p_patches = Vector<String>(), BitField<EditorExportPlatform::DebugFlags> p_flags = 0);
+ virtual Error export_zip_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, const Vector<String> &p_patches = Vector<String>(), BitField<EditorExportPlatform::DebugFlags> p_flags = 0);
virtual void get_platform_features(List<String> *r_features) const = 0;
virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, HashSet<String> &p_features) {}
virtual String get_debug_protocol() const { return "tcp://"; }
diff --git a/editor/export/editor_export_platform_extension.cpp b/editor/export/editor_export_platform_extension.cpp
index 808a2076e2..5b75ff19a4 100644
--- a/editor/export/editor_export_platform_extension.cpp
+++ b/editor/export/editor_export_platform_extension.cpp
@@ -71,6 +71,8 @@ void EditorExportPlatformExtension::_bind_methods() {
GDVIRTUAL_BIND(_export_project, "preset", "debug", "path", "flags");
GDVIRTUAL_BIND(_export_pack, "preset", "debug", "path", "flags");
GDVIRTUAL_BIND(_export_zip, "preset", "debug", "path", "flags");
+ GDVIRTUAL_BIND(_export_pack_patch, "preset", "debug", "path", "patches", "flags");
+ GDVIRTUAL_BIND(_export_zip_patch, "preset", "debug", "path", "patches", "flags");
GDVIRTUAL_BIND(_get_platform_features);
@@ -291,6 +293,44 @@ Error EditorExportPlatformExtension::export_zip(const Ref<EditorExportPreset> &p
return save_zip(p_preset, p_debug, p_path);
}
+Error EditorExportPlatformExtension::export_pack_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, const Vector<String> &p_patches, BitField<EditorExportPlatform::DebugFlags> p_flags) {
+ ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
+
+ Error err = _load_patches(p_patches.is_empty() ? p_preset->get_patches() : p_patches);
+ if (err != OK) {
+ return err;
+ }
+
+ Error ret = FAILED;
+ if (GDVIRTUAL_CALL(_export_pack_patch, p_preset, p_debug, p_path, p_patches, p_flags, ret)) {
+ _unload_patches();
+ return ret;
+ }
+
+ err = save_pack_patch(p_preset, p_debug, p_path);
+ _unload_patches();
+ return err;
+}
+
+Error EditorExportPlatformExtension::export_zip_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, const Vector<String> &p_patches, BitField<EditorExportPlatform::DebugFlags> p_flags) {
+ ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
+
+ Error err = _load_patches(p_patches.is_empty() ? p_preset->get_patches() : p_patches);
+ if (err != OK) {
+ return err;
+ }
+
+ Error ret = FAILED;
+ if (GDVIRTUAL_CALL(_export_zip_patch, p_preset, p_debug, p_path, p_patches, p_flags, ret)) {
+ _unload_patches();
+ return ret;
+ }
+
+ err = save_zip_patch(p_preset, p_debug, p_path);
+ _unload_patches();
+ return err;
+}
+
void EditorExportPlatformExtension::get_platform_features(List<String> *r_features) const {
Vector<String> ret;
if (GDVIRTUAL_REQUIRED_CALL(_get_platform_features, ret) && r_features) {
diff --git a/editor/export/editor_export_platform_extension.h b/editor/export/editor_export_platform_extension.h
index 6391e65ac1..d1db922698 100644
--- a/editor/export/editor_export_platform_extension.h
+++ b/editor/export/editor_export_platform_extension.h
@@ -136,6 +136,12 @@ public:
virtual Error export_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
GDVIRTUAL4R(Error, _export_zip, Ref<EditorExportPreset>, bool, const String &, BitField<EditorExportPlatform::DebugFlags>);
+ virtual Error export_pack_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, const Vector<String> &p_patches = Vector<String>(), BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
+ GDVIRTUAL5R(Error, _export_pack_patch, Ref<EditorExportPreset>, bool, const String &, const Vector<String> &, BitField<EditorExportPlatform::DebugFlags>);
+
+ virtual Error export_zip_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, const Vector<String> &p_patches = Vector<String>(), BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
+ GDVIRTUAL5R(Error, _export_zip_patch, Ref<EditorExportPreset>, bool, const String &, const Vector<String> &, BitField<EditorExportPlatform::DebugFlags>);
+
virtual void get_platform_features(List<String> *r_features) const override;
GDVIRTUAL0RC(Vector<String>, _get_platform_features);
diff --git a/editor/export/editor_export_platform_pc.cpp b/editor/export/editor_export_platform_pc.cpp
index 24d89b7f34..31a5b60d77 100644
--- a/editor/export/editor_export_platform_pc.cpp
+++ b/editor/export/editor_export_platform_pc.cpp
@@ -194,7 +194,7 @@ Error EditorExportPlatformPC::export_project_data(const Ref<EditorExportPreset>
int64_t embedded_pos;
int64_t embedded_size;
- Error err = save_pack(p_preset, p_debug, pck_path, &so_files, p_preset->get("binary_format/embed_pck"), &embedded_pos, &embedded_size);
+ Error err = save_pack(p_preset, p_debug, pck_path, &so_files, nullptr, p_preset->get("binary_format/embed_pck"), &embedded_pos, &embedded_size);
if (err == OK && p_preset->get("binary_format/embed_pck")) {
if (embedded_size >= 0x100000000 && String(p_preset->get("binary_format/architecture")).contains("32")) {
add_message(EXPORT_MESSAGE_ERROR, TTR("PCK Embedding"), TTR("On 32-bit exports the embedded PCK cannot be bigger than 4 GiB."));
diff --git a/editor/export/editor_export_preset.cpp b/editor/export/editor_export_preset.cpp
index 9f805666d0..1ca72348e2 100644
--- a/editor/export/editor_export_preset.cpp
+++ b/editor/export/editor_export_preset.cpp
@@ -79,6 +79,7 @@ void EditorExportPreset::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_include_filter"), &EditorExportPreset::get_include_filter);
ClassDB::bind_method(D_METHOD("get_exclude_filter"), &EditorExportPreset::get_exclude_filter);
ClassDB::bind_method(D_METHOD("get_custom_features"), &EditorExportPreset::get_custom_features);
+ ClassDB::bind_method(D_METHOD("get_patches"), &EditorExportPreset::get_patches);
ClassDB::bind_method(D_METHOD("get_export_path"), &EditorExportPreset::get_export_path);
ClassDB::bind_method(D_METHOD("get_encryption_in_filter"), &EditorExportPreset::get_enc_in_filter);
ClassDB::bind_method(D_METHOD("get_encryption_ex_filter"), &EditorExportPreset::get_enc_ex_filter);
@@ -366,6 +367,42 @@ EditorExportPreset::FileExportMode EditorExportPreset::get_file_export_mode(cons
return p_default;
}
+void EditorExportPreset::add_patch(const String &p_path, int p_at_pos) {
+ ERR_FAIL_COND_EDMSG(patches.has(p_path), vformat("Failed to add patch \"%s\". Patches must be unique.", p_path));
+
+ if (p_at_pos < 0) {
+ patches.push_back(p_path);
+ } else {
+ patches.insert(p_at_pos, p_path);
+ }
+
+ EditorExport::singleton->save_presets();
+}
+
+void EditorExportPreset::set_patch(int p_index, const String &p_path) {
+ remove_patch(p_index);
+ add_patch(p_path, p_index);
+}
+
+String EditorExportPreset::get_patch(int p_index) {
+ ERR_FAIL_INDEX_V(p_index, patches.size(), String());
+ return patches[p_index];
+}
+
+void EditorExportPreset::remove_patch(int p_index) {
+ ERR_FAIL_INDEX(p_index, patches.size());
+ patches.remove_at(p_index);
+ EditorExport::singleton->save_presets();
+}
+
+void EditorExportPreset::set_patches(const Vector<String> &p_patches) {
+ patches = p_patches;
+}
+
+Vector<String> EditorExportPreset::get_patches() const {
+ return patches;
+}
+
void EditorExportPreset::set_custom_features(const String &p_custom_features) {
custom_features = p_custom_features;
EditorExport::singleton->save_presets();
diff --git a/editor/export/editor_export_preset.h b/editor/export/editor_export_preset.h
index f220477461..af3a23fc50 100644
--- a/editor/export/editor_export_preset.h
+++ b/editor/export/editor_export_preset.h
@@ -74,6 +74,8 @@ private:
bool advanced_options_enabled = false;
bool dedicated_server = false;
+ Vector<String> patches;
+
friend class EditorExport;
friend class EditorExportPlatform;
@@ -144,6 +146,13 @@ public:
void set_exclude_filter(const String &p_exclude);
String get_exclude_filter() const;
+ void add_patch(const String &p_path, int p_at_pos = -1);
+ void set_patch(int p_index, const String &p_path);
+ String get_patch(int p_index);
+ void remove_patch(int p_index);
+ void set_patches(const Vector<String> &p_patches);
+ Vector<String> get_patches() const;
+
void set_custom_features(const String &p_custom_features);
String get_custom_features() const;
diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp
index be9e0f78ec..f9137082d7 100644
--- a/editor/export/project_export.cpp
+++ b/editor/export/project_export.cpp
@@ -102,11 +102,13 @@ void ProjectExportDialog::_notification(int p_what) {
case NOTIFICATION_THEME_CHANGED: {
duplicate_preset->set_icon(presets->get_editor_theme_icon(SNAME("Duplicate")));
delete_preset->set_icon(presets->get_editor_theme_icon(SNAME("Remove")));
+ patch_add_btn->set_icon(get_editor_theme_icon(SNAME("Add")));
} break;
case NOTIFICATION_READY: {
duplicate_preset->set_icon(presets->get_editor_theme_icon(SNAME("Duplicate")));
delete_preset->set_icon(presets->get_editor_theme_icon(SNAME("Remove")));
+ patch_add_btn->set_icon(get_editor_theme_icon(SNAME("Add")));
connect(SceneStringName(confirmed), callable_mp(this, &ProjectExportDialog::_export_pck_zip));
_update_export_all();
} break;
@@ -248,6 +250,7 @@ void ProjectExportDialog::_edit_preset(int p_index) {
duplicate_preset->set_disabled(true);
delete_preset->set_disabled(true);
sections->hide();
+ patches->clear();
export_error->hide();
export_templates_error->hide();
return;
@@ -292,6 +295,21 @@ void ProjectExportDialog::_edit_preset(int p_index) {
exclude_filters->set_text(current->get_exclude_filter());
server_strip_message->set_visible(current->get_export_filter() == EditorExportPreset::EXPORT_CUSTOMIZED);
+ patches->clear();
+ TreeItem *patch_root = patches->create_item();
+ Vector<String> patch_list = current->get_patches();
+ for (int i = 0; i < patch_list.size(); i++) {
+ TreeItem *patch = patches->create_item(patch_root);
+ const String &patch_path = patch_list[i];
+ patch->set_cell_mode(0, TreeItem::CELL_MODE_STRING);
+ patch->set_editable(0, true);
+ patch->set_text(0, patch_path.get_file());
+ patch->set_tooltip_text(0, patch_path);
+ patch->set_metadata(0, i);
+ patch->add_button(0, get_editor_theme_icon(SNAME("Remove")), 0);
+ patch->add_button(0, get_editor_theme_icon(SNAME("FileBrowse")), 1);
+ }
+
_fill_resource_tree();
bool needs_templates;
@@ -664,6 +682,7 @@ void ProjectExportDialog::_duplicate_preset() {
preset->set_export_filter(current->get_export_filter());
preset->set_include_filter(current->get_include_filter());
preset->set_exclude_filter(current->get_exclude_filter());
+ preset->set_patches(current->get_patches());
preset->set_custom_features(current->get_custom_features());
for (const KeyValue<StringName, Variant> &E : current->get_values()) {
@@ -720,8 +739,22 @@ Variant ProjectExportDialog::get_drag_data_fw(const Point2 &p_point, Control *p_
return d;
}
- }
+ } else if (p_from == patches) {
+ TreeItem *item = patches->get_item_at_position(p_point);
+
+ if (item) {
+ int item_metadata = item->get_metadata(0);
+ Dictionary d;
+ d["type"] = "export_patch";
+ d["patch"] = item_metadata;
+ Label *label = memnew(Label);
+ label->set_text(item->get_text(0));
+ patches->set_drag_preview(label);
+
+ return d;
+ }
+ }
return Variant();
}
@@ -735,6 +768,18 @@ bool ProjectExportDialog::can_drop_data_fw(const Point2 &p_point, const Variant
if (presets->get_item_at_position(p_point, true) < 0 && !presets->is_pos_at_end_of_items(p_point)) {
return false;
}
+ } else if (p_from == patches) {
+ Dictionary d = p_data;
+ if (d.get("type", "") != "export_patch") {
+ return false;
+ }
+
+ TreeItem *item = patches->get_item_at_position(p_point);
+ if (!item) {
+ return false;
+ }
+
+ patches->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN);
}
return true;
@@ -771,6 +816,31 @@ void ProjectExportDialog::drop_data_fw(const Point2 &p_point, const Variant &p_d
} else {
_edit_preset(presets->get_item_count() - 1);
}
+ } else if (p_from == patches) {
+ Dictionary d = p_data;
+ int from_pos = d["patch"];
+
+ TreeItem *item = patches->get_item_at_position(p_point);
+ if (!item) {
+ return;
+ }
+
+ int to_pos = item->get_metadata(0);
+
+ if (patches->get_drop_section_at_position(p_point) > 0) {
+ to_pos++;
+ }
+
+ if (to_pos > from_pos) {
+ to_pos--;
+ }
+
+ Ref<EditorExportPreset> preset = get_current_preset();
+ String patch = preset->get_patch(from_pos);
+ preset->remove_patch(from_pos);
+ preset->add_patch(patch, to_pos);
+
+ _update_current_preset();
}
}
@@ -1026,6 +1096,75 @@ void ProjectExportDialog::_set_file_export_mode(int p_id) {
_propagate_file_export_mode(include_files->get_root(), EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED);
}
+void ProjectExportDialog::_patch_tree_button_clicked(Object *p_item, int p_column, int p_id, int p_mouse_button_index) {
+ TreeItem *ti = Object::cast_to<TreeItem>(p_item);
+
+ patch_index = ti->get_metadata(0);
+
+ Ref<EditorExportPreset> current = get_current_preset();
+ ERR_FAIL_COND(current.is_null());
+
+ if (p_id == 0) {
+ Vector<String> preset_patches = current->get_patches();
+ ERR_FAIL_INDEX(patch_index, preset_patches.size());
+ patch_erase->set_text(vformat(TTR("Delete patch '%s' from list?"), preset_patches[patch_index].get_file()));
+ patch_erase->popup_centered();
+ } else {
+ patch_dialog->popup_file_dialog();
+ }
+}
+
+void ProjectExportDialog::_patch_tree_item_edited() {
+ TreeItem *item = patches->get_edited();
+ if (!item) {
+ return;
+ }
+
+ Ref<EditorExportPreset> current = get_current_preset();
+ ERR_FAIL_COND(current.is_null());
+
+ int index = item->get_metadata(0);
+ String patch_path = item->get_text(0);
+
+ current->set_patch(index, patch_path);
+ item->set_tooltip_text(0, patch_path);
+}
+
+void ProjectExportDialog::_patch_file_selected(const String &p_path) {
+ Ref<EditorExportPreset> current = get_current_preset();
+ ERR_FAIL_COND(current.is_null());
+
+ String relative_path = ProjectSettings::get_singleton()->get_resource_path().path_to_file(p_path);
+
+ Vector<String> preset_patches = current->get_patches();
+ if (patch_index >= preset_patches.size()) {
+ current->add_patch(relative_path);
+ } else {
+ current->set_patch(patch_index, relative_path);
+ }
+
+ _update_current_preset();
+}
+
+void ProjectExportDialog::_patch_delete_confirmed() {
+ Ref<EditorExportPreset> current = get_current_preset();
+ ERR_FAIL_COND(current.is_null());
+
+ Vector<String> preset_patches = current->get_patches();
+ if (patch_index < preset_patches.size()) {
+ current->remove_patch(patch_index);
+ _update_current_preset();
+ }
+}
+
+void ProjectExportDialog::_patch_add_pack_pressed() {
+ Ref<EditorExportPreset> current = get_current_preset();
+ ERR_FAIL_COND(current.is_null());
+
+ patch_index = current->get_patches().size();
+ patch_dialog->popup_file_dialog();
+}
+
void ProjectExportDialog::_export_pck_zip() {
Ref<EditorExportPreset> current = get_current_preset();
ERR_FAIL_COND(current.is_null());
@@ -1044,11 +1183,20 @@ void ProjectExportDialog::_export_pck_zip_selected(const String &p_path) {
const Dictionary &fd_option = export_pck_zip->get_selected_options();
bool export_debug = fd_option.get(TTR("Export With Debug"), true);
+ bool export_as_patch = fd_option.get(TTR("Export As Patch"), true);
if (p_path.ends_with(".zip")) {
- platform->export_zip(current, export_debug, p_path);
+ if (export_as_patch) {
+ platform->export_zip_patch(current, export_debug, p_path);
+ } else {
+ platform->export_zip(current, export_debug, p_path);
+ }
} else if (p_path.ends_with(".pck")) {
- platform->export_pack(current, export_debug, p_path);
+ if (export_as_patch) {
+ platform->export_pack_patch(current, export_debug, p_path);
+ } else {
+ platform->export_pack(current, export_debug, p_path);
+ }
} else {
ERR_FAIL_MSG("Path must end with .pck or .zip");
}
@@ -1386,6 +1534,40 @@ ProjectExportDialog::ProjectExportDialog() {
exclude_filters);
exclude_filters->connect(SceneStringName(text_changed), callable_mp(this, &ProjectExportDialog::_filter_changed));
+ // Patch packages.
+
+ VBoxContainer *patch_vb = memnew(VBoxContainer);
+ sections->add_child(patch_vb);
+ patch_vb->set_name(TTR("Patches"));
+
+ patches = memnew(Tree);
+ patches->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ patches->set_hide_root(true);
+ patches->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
+ patches->connect("button_clicked", callable_mp(this, &ProjectExportDialog::_patch_tree_button_clicked));
+ patches->connect("item_edited", callable_mp(this, &ProjectExportDialog::_patch_tree_item_edited));
+ SET_DRAG_FORWARDING_GCD(patches, ProjectExportDialog);
+ patches->set_edit_checkbox_cell_only_when_checkbox_is_pressed(true);
+ patch_vb->add_margin_child(TTR("Base Packs:"), patches, true);
+
+ patch_dialog = memnew(EditorFileDialog);
+ patch_dialog->add_filter("*.pck", TTR("Godot Project Pack"));
+ patch_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
+ patch_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
+ patch_dialog->connect("file_selected", callable_mp(this, &ProjectExportDialog::_patch_file_selected));
+ add_child(patch_dialog);
+
+ patch_erase = memnew(ConfirmationDialog);
+ patch_erase->set_ok_button_text(TTR("Delete"));
+ patch_erase->connect(SceneStringName(confirmed), callable_mp(this, &ProjectExportDialog::_patch_delete_confirmed));
+ add_child(patch_erase);
+
+ patch_add_btn = memnew(Button);
+ patch_add_btn->set_text(TTR("Add Pack"));
+ patch_add_btn->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
+ patch_add_btn->connect(SceneStringName(pressed), callable_mp(this, &ProjectExportDialog::_patch_add_pack_pressed));
+ patch_vb->add_child(patch_add_btn);
+
// Feature tags.
VBoxContainer *feature_vb = memnew(VBoxContainer);
@@ -1569,6 +1751,7 @@ ProjectExportDialog::ProjectExportDialog() {
export_project->add_option(TTR("Export With Debug"), Vector<String>(), true);
export_pck_zip->add_option(TTR("Export With Debug"), Vector<String>(), true);
+ export_pck_zip->add_option(TTR("Export As Patch"), Vector<String>(), true);
set_hide_on_ok(false);
diff --git a/editor/export/project_export.h b/editor/export/project_export.h
index c3499177f3..e360596be6 100644
--- a/editor/export/project_export.h
+++ b/editor/export/project_export.h
@@ -105,6 +105,13 @@ class ProjectExportDialog : public ConfirmationDialog {
AcceptDialog *export_all_dialog = nullptr;
RBSet<String> feature_set;
+
+ Tree *patches = nullptr;
+ int patch_index = -1;
+ EditorFileDialog *patch_dialog = nullptr;
+ ConfirmationDialog *patch_erase = nullptr;
+ Button *patch_add_btn = nullptr;
+
LineEdit *custom_features = nullptr;
RichTextLabel *custom_feature_display = nullptr;
@@ -148,6 +155,12 @@ class ProjectExportDialog : public ConfirmationDialog {
void _tree_popup_edited(bool p_arrow_clicked);
void _set_file_export_mode(int p_id);
+ void _patch_tree_button_clicked(Object *p_item, int p_column, int p_id, int p_mouse_button_index);
+ void _patch_tree_item_edited();
+ void _patch_file_selected(const String &p_path);
+ void _patch_delete_confirmed();
+ void _patch_add_pack_pressed();
+
Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
diff --git a/editor/gui/SCsub b/editor/gui/SCsub
index 359d04e5df..b3cff5b9dc 100644
--- a/editor/gui/SCsub
+++ b/editor/gui/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/editor/icons/SCsub b/editor/icons/SCsub
index 0d9ac43c46..a66ef56699 100644
--- a/editor/icons/SCsub
+++ b/editor/icons/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/editor/import/SCsub b/editor/import/SCsub
index a8c06cc406..3d3b7780ba 100644
--- a/editor/import/SCsub
+++ b/editor/import/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/editor/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp
index 552815a6af..72d715ac2d 100644
--- a/editor/import/resource_importer_layered_texture.cpp
+++ b/editor/import/resource_importer_layered_texture.cpp
@@ -431,22 +431,20 @@ String ResourceImporterLayeredTexture::get_import_settings_string() const {
return s;
}
-bool ResourceImporterLayeredTexture::are_import_settings_valid(const String &p_path) const {
+bool ResourceImporterLayeredTexture::are_import_settings_valid(const String &p_path, const Dictionary &p_meta) const {
//will become invalid if formats are missing to import
- Dictionary meta = ResourceFormatImporter::get_singleton()->get_resource_metadata(p_path);
-
- if (!meta.has("vram_texture")) {
+ if (!p_meta.has("vram_texture")) {
return false;
}
- bool vram = meta["vram_texture"];
+ bool vram = p_meta["vram_texture"];
if (!vram) {
return true; //do not care about non vram
}
Vector<String> formats_imported;
- if (meta.has("imported_formats")) {
- formats_imported = meta["imported_formats"];
+ if (p_meta.has("imported_formats")) {
+ formats_imported = p_meta["imported_formats"];
}
int index = 0;
diff --git a/editor/import/resource_importer_layered_texture.h b/editor/import/resource_importer_layered_texture.h
index 5a21651de3..26495eed8d 100644
--- a/editor/import/resource_importer_layered_texture.h
+++ b/editor/import/resource_importer_layered_texture.h
@@ -114,7 +114,7 @@ public:
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
- virtual bool are_import_settings_valid(const String &p_path) const override;
+ virtual bool are_import_settings_valid(const String &p_path, const Dictionary &p_meta) const override;
virtual String get_import_settings_string() const override;
void set_mode(Mode p_mode) { mode = p_mode; }
diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp
index 487b8fc175..a205123df1 100644
--- a/editor/import/resource_importer_texture.cpp
+++ b/editor/import/resource_importer_texture.cpp
@@ -740,10 +740,8 @@ String ResourceImporterTexture::get_import_settings_string() const {
return s;
}
-bool ResourceImporterTexture::are_import_settings_valid(const String &p_path) const {
- Dictionary meta = ResourceFormatImporter::get_singleton()->get_resource_metadata(p_path);
-
- if (meta.has("has_editor_variant")) {
+bool ResourceImporterTexture::are_import_settings_valid(const String &p_path, const Dictionary &p_meta) const {
+ if (p_meta.has("has_editor_variant")) {
String imported_path = ResourceFormatImporter::get_singleton()->get_internal_resource_path(p_path);
if (!FileAccess::exists(imported_path)) {
return false;
@@ -760,19 +758,19 @@ bool ResourceImporterTexture::are_import_settings_valid(const String &p_path) co
}
}
- if (!meta.has("vram_texture")) {
+ if (!p_meta.has("vram_texture")) {
return false;
}
- bool vram = meta["vram_texture"];
+ bool vram = p_meta["vram_texture"];
if (!vram) {
return true; // Do not care about non-VRAM.
}
// Will become invalid if formats are missing to import.
Vector<String> formats_imported;
- if (meta.has("imported_formats")) {
- formats_imported = meta["imported_formats"];
+ if (p_meta.has("imported_formats")) {
+ formats_imported = p_meta["imported_formats"];
}
int index = 0;
diff --git a/editor/import/resource_importer_texture.h b/editor/import/resource_importer_texture.h
index f2539a8f52..6d74c4e2f9 100644
--- a/editor/import/resource_importer_texture.h
+++ b/editor/import/resource_importer_texture.h
@@ -104,7 +104,7 @@ public:
void update_imports();
- virtual bool are_import_settings_valid(const String &p_path) const override;
+ virtual bool are_import_settings_valid(const String &p_path, const Dictionary &p_meta) const override;
virtual String get_import_settings_string() const override;
ResourceImporterTexture(bool p_singleton = false);
diff --git a/editor/plugins/SCsub b/editor/plugins/SCsub
index 4b6abf18f9..2d3066c7c9 100644
--- a/editor/plugins/SCsub
+++ b/editor/plugins/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/editor/plugins/gizmos/SCsub b/editor/plugins/gizmos/SCsub
index 359d04e5df..b3cff5b9dc 100644
--- a/editor/plugins/gizmos/SCsub
+++ b/editor/plugins/gizmos/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/editor/plugins/tiles/SCsub b/editor/plugins/tiles/SCsub
index 359d04e5df..b3cff5b9dc 100644
--- a/editor/plugins/tiles/SCsub
+++ b/editor/plugins/tiles/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.h b/editor/plugins/tiles/tile_set_atlas_source_editor.h
index 957f768429..c1a8338f81 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.h
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.h
@@ -177,7 +177,7 @@ private:
DRAG_TYPE_MAY_POPUP_MENU,
- // Warning: keep in this order.
+ // WARNING: Keep in this order.
DRAG_TYPE_RESIZE_TOP_LEFT,
DRAG_TYPE_RESIZE_TOP,
DRAG_TYPE_RESIZE_TOP_RIGHT,
diff --git a/editor/project_manager/SCsub b/editor/project_manager/SCsub
index 359d04e5df..b3cff5b9dc 100644
--- a/editor/project_manager/SCsub
+++ b/editor/project_manager/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/editor/themes/SCsub b/editor/themes/SCsub
index e8f96e4299..5a9949dfa7 100644
--- a/editor/themes/SCsub
+++ b/editor/themes/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/main/SCsub b/main/SCsub
index f3807167a2..71bee465f5 100644
--- a/main/SCsub
+++ b/main/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/main/main.cpp b/main/main.cpp
index 75797e31de..9014bfad29 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -612,7 +612,9 @@ void Main::print_help(const char *p_binary) {
print_help_option("--position <X>,<Y>", "Request window position.\n");
print_help_option("--screen <N>", "Request window screen.\n");
print_help_option("--single-window", "Use a single window (no separate subwindows).\n");
+#ifndef _3D_DISABLED
print_help_option("--xr-mode <mode>", "Select XR (Extended Reality) mode [\"default\", \"off\", \"on\"].\n");
+#endif
print_help_title("Debug options");
print_help_option("-d, --debug", "Debug (local stdout debugger).\n");
@@ -659,6 +661,8 @@ void Main::print_help(const char *p_binary) {
print_help_option("", "The target directory must exist.\n");
print_help_option("--export-debug <preset> <path>", "Export the project in debug mode using the given preset and output path. See --export-release description for other considerations.\n", CLI_OPTION_AVAILABILITY_EDITOR);
print_help_option("--export-pack <preset> <path>", "Export the project data only using the given preset and output path. The <path> extension determines whether it will be in PCK or ZIP format.\n", CLI_OPTION_AVAILABILITY_EDITOR);
+ print_help_option("--export-patch <preset> <path>", "Export pack with changed files only. See --export-pack description for other considerations.\n", CLI_OPTION_AVAILABILITY_EDITOR);
+ print_help_option("--patches <paths>", "List of patches to use with --export-patch. The list is comma-separated.\n", CLI_OPTION_AVAILABILITY_EDITOR);
print_help_option("--install-android-build-template", "Install the Android build template. Used in conjunction with --export-release or --export-debug.\n", CLI_OPTION_AVAILABILITY_EDITOR);
#ifndef DISABLE_DEPRECATED
// Commands are long; split the description to a second line.
@@ -1478,12 +1482,23 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
wait_for_import = true;
quit_after = 1;
} else if (arg == "--export-release" || arg == "--export-debug" ||
- arg == "--export-pack") { // Export project
+ arg == "--export-pack" || arg == "--export-patch") { // Export project
// Actually handling is done in start().
editor = true;
cmdline_tool = true;
wait_for_import = true;
main_args.push_back(arg);
+ } else if (arg == "--patches") {
+ if (N) {
+ // Actually handling is done in start().
+ main_args.push_back(arg);
+ main_args.push_back(N->get());
+
+ N = N->next();
+ } else {
+ OS::get_singleton()->print("Missing comma-separated list of patches after --patches, aborting.\n");
+ goto error;
+ }
#ifndef DISABLE_DEPRECATED
} else if (arg == "--export") { // For users used to 3.x syntax.
OS::get_singleton()->print("The Godot 3 --export option was changed to more explicit --export-release / --export-debug / --export-pack options.\nSee the --help output for details.\n");
@@ -2558,6 +2573,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
GLOBAL_DEF("display/window/ios/hide_status_bar", true);
GLOBAL_DEF("display/window/ios/suppress_ui_gesture", true);
+#ifndef _3D_DISABLED
// XR project settings.
GLOBAL_DEF_RST_BASIC("xr/openxr/enabled", false);
GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "xr/openxr/default_action_map", PROPERTY_HINT_FILE, "*.tres"), "res://openxr_action_map.tres");
@@ -2586,7 +2602,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
// editor settings (it seems we're too early in the process when setting up rendering, to access editor settings...)
// EDITOR_DEF_RST("xr/openxr/in_editor", false);
// GLOBAL_DEF("xr/openxr/in_editor", false);
-#endif
+#endif // TOOLS_ENABLED
+#endif // _3D_DISABLED
Engine::get_singleton()->set_frame_delay(frame_delay);
@@ -3489,9 +3506,11 @@ int Main::start() {
bool doc_tool_implicit_cwd = false;
BitField<DocTools::GenerateFlags> gen_flags;
String _export_preset;
+ Vector<String> patches;
bool export_debug = false;
bool export_pack_only = false;
bool install_android_build_template = false;
+ bool export_patch = false;
#ifdef MODULE_GDSCRIPT_ENABLED
String gdscript_docs_path;
#endif
@@ -3581,6 +3600,14 @@ int Main::start() {
editor = true;
_export_preset = E->next()->get();
export_pack_only = true;
+ } else if (E->get() == "--export-patch") {
+ ERR_FAIL_COND_V_MSG(!editor && !found_project, EXIT_FAILURE, "Please provide a valid project path when exporting, aborting.");
+ editor = true;
+ _export_preset = E->next()->get();
+ export_pack_only = true;
+ export_patch = true;
+ } else if (E->get() == "--patches") {
+ patches = E->next()->get().split(",", false);
#endif
} else {
// The parameter does not match anything known, don't skip the next argument
@@ -3984,7 +4011,7 @@ int Main::start() {
sml->get_root()->add_child(editor_node);
if (!_export_preset.is_empty()) {
- editor_node->export_preset(_export_preset, positional_arg, export_debug, export_pack_only, install_android_build_template);
+ editor_node->export_preset(_export_preset, positional_arg, export_debug, export_pack_only, install_android_build_template, export_patch, patches);
game_path = ""; // Do not load anything.
}
diff --git a/misc/utility/scons_hints.py b/misc/utility/scons_hints.py
new file mode 100644
index 0000000000..fe380e399d
--- /dev/null
+++ b/misc/utility/scons_hints.py
@@ -0,0 +1,98 @@
+"""
+Adds type hints to SCons scripts. Implemented via
+`from misc.utility.scons_hints import *`.
+
+This is NOT a 1-1 representation of what the defines will represent in an
+SCons build, as proxies are almost always utilized instead. Rather, this is
+a means of tracing back what those proxies are calling to in the first place.
+"""
+
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ # ruff: noqa: F401
+ from SCons.Action import Action
+ from SCons.Builder import Builder
+ from SCons.Defaults import Chmod, Copy, CScan, DefaultEnvironment, Delete, DirScanner, Mkdir, Move, Touch
+ from SCons.Environment import Base
+ from SCons.Platform import Platform
+ from SCons.Platform.virtualenv import Virtualenv
+ from SCons.Scanner import FindPathDirs, ScannerBase
+ from SCons.Script import ARGLIST, ARGUMENTS, BUILD_TARGETS, COMMAND_LINE_TARGETS, DEFAULT_TARGETS
+ from SCons.Script.Main import (
+ AddOption,
+ BuildTask,
+ CleanTask,
+ DebugOptions,
+ GetBuildFailures,
+ GetOption,
+ PrintHelp,
+ Progress,
+ QuestionTask,
+ SetOption,
+ ValidateOptions,
+ )
+ from SCons.Script.SConscript import Configure, Return, SConsEnvironment, call_stack
+ from SCons.Script.SConscript import SConsEnvironment as Environment
+ from SCons.Subst import SetAllowableExceptions as AllowSubstExceptions
+ from SCons.Tool import CScanner, DScanner, ProgramScanner, SourceFileScanner, Tool
+ from SCons.Util import AddMethod, WhereIs
+ from SCons.Variables import BoolVariable, EnumVariable, ListVariable, PackageVariable, PathVariable, Variables
+
+ # Global functions
+ GetSConsVersion = SConsEnvironment.GetSConsVersion
+ EnsurePythonVersion = SConsEnvironment.EnsurePythonVersion
+ EnsureSConsVersion = SConsEnvironment.EnsureSConsVersion
+ Exit = SConsEnvironment.Exit
+ GetLaunchDir = SConsEnvironment.GetLaunchDir
+ SConscriptChdir = SConsEnvironment.SConscriptChdir
+
+ # SConsEnvironment functions
+ Default = SConsEnvironment(DefaultEnvironment()).Default
+ Export = SConsEnvironment(DefaultEnvironment()).Export
+ Help = SConsEnvironment(DefaultEnvironment()).Help
+ Import = SConsEnvironment(DefaultEnvironment()).Import
+ SConscript = SConsEnvironment(DefaultEnvironment()).SConscript
+
+ # Environment functions
+ AddPostAction = DefaultEnvironment().AddPostAction
+ AddPreAction = DefaultEnvironment().AddPreAction
+ Alias = DefaultEnvironment().Alias
+ AlwaysBuild = DefaultEnvironment().AlwaysBuild
+ CacheDir = DefaultEnvironment().CacheDir
+ Clean = DefaultEnvironment().Clean
+ Command = DefaultEnvironment().Command
+ Decider = DefaultEnvironment().Decider
+ Depends = DefaultEnvironment().Depends
+ Dir = DefaultEnvironment().Dir
+ Entry = DefaultEnvironment().Entry
+ Execute = DefaultEnvironment().Execute
+ File = DefaultEnvironment().File
+ FindFile = DefaultEnvironment().FindFile
+ FindInstalledFiles = DefaultEnvironment().FindInstalledFiles
+ FindSourceFiles = DefaultEnvironment().FindSourceFiles
+ Flatten = DefaultEnvironment().Flatten
+ GetBuildPath = DefaultEnvironment().GetBuildPath
+ Glob = DefaultEnvironment().Glob
+ Ignore = DefaultEnvironment().Ignore
+ Install = DefaultEnvironment().Install
+ InstallAs = DefaultEnvironment().InstallAs
+ InstallVersionedLib = DefaultEnvironment().InstallVersionedLib
+ Literal = DefaultEnvironment().Literal
+ Local = DefaultEnvironment().Local
+ NoCache = DefaultEnvironment().NoCache
+ NoClean = DefaultEnvironment().NoClean
+ ParseDepends = DefaultEnvironment().ParseDepends
+ Precious = DefaultEnvironment().Precious
+ PyPackageDir = DefaultEnvironment().PyPackageDir
+ Repository = DefaultEnvironment().Repository
+ Requires = DefaultEnvironment().Requires
+ SConsignFile = DefaultEnvironment().SConsignFile
+ SideEffect = DefaultEnvironment().SideEffect
+ Split = DefaultEnvironment().Split
+ Tag = DefaultEnvironment().Tag
+ Value = DefaultEnvironment().Value
+ VariantDir = DefaultEnvironment().VariantDir
+
+ env: SConsEnvironment
+ env_modules: SConsEnvironment
diff --git a/modules/SCsub b/modules/SCsub
index e16cc17b67..fea2f2eeb8 100644
--- a/modules/SCsub
+++ b/modules/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
import os
diff --git a/modules/astcenc/SCsub b/modules/astcenc/SCsub
index 691c74b4a7..23e9fa87fc 100644
--- a/modules/astcenc/SCsub
+++ b/modules/astcenc/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/basis_universal/SCsub b/modules/basis_universal/SCsub
index 80bfd7e858..0142317e1e 100644
--- a/modules/basis_universal/SCsub
+++ b/modules/basis_universal/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/betsy/SCsub b/modules/betsy/SCsub
index ed5dcbf58b..2735116cc3 100644
--- a/modules/betsy/SCsub
+++ b/modules/betsy/SCsub
@@ -1,4 +1,6 @@
-# !/ usr / bin / env python
+#!/usr/bin/env python
+from misc.utility.scons_hints import *
+
Import("env")
Import("env_modules")
diff --git a/modules/bmp/SCsub b/modules/bmp/SCsub
index 9d317887c3..cc3684b94b 100644
--- a/modules/bmp/SCsub
+++ b/modules/bmp/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/camera/SCsub b/modules/camera/SCsub
index 9a6147d433..aed5efd0d2 100644
--- a/modules/camera/SCsub
+++ b/modules/camera/SCsub
@@ -1,14 +1,21 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
env_camera = env_modules.Clone()
-if env["platform"] == "windows":
+if env["platform"] in ["windows", "macos", "linuxbsd"]:
env_camera.add_source_files(env.modules_sources, "register_types.cpp")
+
+if env["platform"] == "windows":
env_camera.add_source_files(env.modules_sources, "camera_win.cpp")
elif env["platform"] == "macos":
- env_camera.add_source_files(env.modules_sources, "register_types.cpp")
env_camera.add_source_files(env.modules_sources, "camera_macos.mm")
+
+elif env["platform"] == "linuxbsd":
+ env_camera.add_source_files(env.modules_sources, "camera_linux.cpp")
+ env_camera.add_source_files(env.modules_sources, "camera_feed_linux.cpp")
+ env_camera.add_source_files(env.modules_sources, "buffer_decoder.cpp")
diff --git a/modules/camera/buffer_decoder.cpp b/modules/camera/buffer_decoder.cpp
new file mode 100644
index 0000000000..024a68f080
--- /dev/null
+++ b/modules/camera/buffer_decoder.cpp
@@ -0,0 +1,212 @@
+/**************************************************************************/
+/* buffer_decoder.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "buffer_decoder.h"
+
+#include "servers/camera/camera_feed.h"
+
+#include <linux/videodev2.h>
+
+BufferDecoder::BufferDecoder(CameraFeed *p_camera_feed) {
+ camera_feed = p_camera_feed;
+ width = camera_feed->get_format().width;
+ height = camera_feed->get_format().height;
+ image.instantiate();
+}
+
+AbstractYuyvBufferDecoder::AbstractYuyvBufferDecoder(CameraFeed *p_camera_feed) :
+ BufferDecoder(p_camera_feed) {
+ switch (camera_feed->get_format().pixel_format) {
+ case V4L2_PIX_FMT_YYUV:
+ component_indexes = new int[4]{ 0, 1, 2, 3 };
+ break;
+ case V4L2_PIX_FMT_YVYU:
+ component_indexes = new int[4]{ 0, 2, 3, 1 };
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ component_indexes = new int[4]{ 1, 3, 0, 2 };
+ break;
+ case V4L2_PIX_FMT_VYUY:
+ component_indexes = new int[4]{ 1, 3, 2, 0 };
+ break;
+ default:
+ component_indexes = new int[4]{ 0, 2, 1, 3 };
+ }
+}
+
+AbstractYuyvBufferDecoder::~AbstractYuyvBufferDecoder() {
+ delete[] component_indexes;
+}
+
+SeparateYuyvBufferDecoder::SeparateYuyvBufferDecoder(CameraFeed *p_camera_feed) :
+ AbstractYuyvBufferDecoder(p_camera_feed) {
+ y_image_data.resize(width * height);
+ cbcr_image_data.resize(width * height);
+ y_image.instantiate();
+ cbcr_image.instantiate();
+}
+
+void SeparateYuyvBufferDecoder::decode(StreamingBuffer p_buffer) {
+ uint8_t *y_dst = (uint8_t *)y_image_data.ptrw();
+ uint8_t *uv_dst = (uint8_t *)cbcr_image_data.ptrw();
+ uint8_t *src = (uint8_t *)p_buffer.start;
+ uint8_t *y0_src = src + component_indexes[0];
+ uint8_t *y1_src = src + component_indexes[1];
+ uint8_t *u_src = src + component_indexes[2];
+ uint8_t *v_src = src + component_indexes[3];
+
+ for (int i = 0; i < width * height; i += 2) {
+ *y_dst++ = *y0_src;
+ *y_dst++ = *y1_src;
+ *uv_dst++ = *u_src;
+ *uv_dst++ = *v_src;
+
+ y0_src += 4;
+ y1_src += 4;
+ u_src += 4;
+ v_src += 4;
+ }
+
+ if (y_image.is_valid()) {
+ y_image->set_data(width, height, false, Image::FORMAT_L8, y_image_data);
+ } else {
+ y_image.instantiate(width, height, false, Image::FORMAT_RGB8, y_image_data);
+ }
+ if (cbcr_image.is_valid()) {
+ cbcr_image->set_data(width, height, false, Image::FORMAT_L8, cbcr_image_data);
+ } else {
+ cbcr_image.instantiate(width, height, false, Image::FORMAT_RGB8, cbcr_image_data);
+ }
+
+ camera_feed->set_YCbCr_imgs(y_image, cbcr_image);
+}
+
+YuyvToGrayscaleBufferDecoder::YuyvToGrayscaleBufferDecoder(CameraFeed *p_camera_feed) :
+ AbstractYuyvBufferDecoder(p_camera_feed) {
+ image_data.resize(width * height);
+}
+
+void YuyvToGrayscaleBufferDecoder::decode(StreamingBuffer p_buffer) {
+ uint8_t *dst = (uint8_t *)image_data.ptrw();
+ uint8_t *src = (uint8_t *)p_buffer.start;
+ uint8_t *y0_src = src + component_indexes[0];
+ uint8_t *y1_src = src + component_indexes[1];
+
+ for (int i = 0; i < width * height; i += 2) {
+ *dst++ = *y0_src;
+ *dst++ = *y1_src;
+
+ y0_src += 4;
+ y1_src += 4;
+ }
+
+ if (image.is_valid()) {
+ image->set_data(width, height, false, Image::FORMAT_L8, image_data);
+ } else {
+ image.instantiate(width, height, false, Image::FORMAT_RGB8, image_data);
+ }
+
+ camera_feed->set_RGB_img(image);
+}
+
+YuyvToRgbBufferDecoder::YuyvToRgbBufferDecoder(CameraFeed *p_camera_feed) :
+ AbstractYuyvBufferDecoder(p_camera_feed) {
+ image_data.resize(width * height * 3);
+}
+
+void YuyvToRgbBufferDecoder::decode(StreamingBuffer p_buffer) {
+ uint8_t *src = (uint8_t *)p_buffer.start;
+ uint8_t *y0_src = src + component_indexes[0];
+ uint8_t *y1_src = src + component_indexes[1];
+ uint8_t *u_src = src + component_indexes[2];
+ uint8_t *v_src = src + component_indexes[3];
+ uint8_t *dst = (uint8_t *)image_data.ptrw();
+
+ for (int i = 0; i < width * height; i += 2) {
+ int u = *u_src;
+ int v = *v_src;
+ int u1 = (((u - 128) << 7) + (u - 128)) >> 6;
+ int rg = (((u - 128) << 1) + (u - 128) + ((v - 128) << 2) + ((v - 128) << 1)) >> 3;
+ int v1 = (((v - 128) << 1) + (v - 128)) >> 1;
+
+ *dst++ = CLAMP(*y0_src + v1, 0, 255);
+ *dst++ = CLAMP(*y0_src - rg, 0, 255);
+ *dst++ = CLAMP(*y0_src + u1, 0, 255);
+
+ *dst++ = CLAMP(*y1_src + v1, 0, 255);
+ *dst++ = CLAMP(*y1_src - rg, 0, 255);
+ *dst++ = CLAMP(*y1_src + u1, 0, 255);
+
+ y0_src += 4;
+ y1_src += 4;
+ u_src += 4;
+ v_src += 4;
+ }
+
+ if (image.is_valid()) {
+ image->set_data(width, height, false, Image::FORMAT_RGB8, image_data);
+ } else {
+ image.instantiate(width, height, false, Image::FORMAT_RGB8, image_data);
+ }
+
+ camera_feed->set_RGB_img(image);
+}
+
+CopyBufferDecoder::CopyBufferDecoder(CameraFeed *p_camera_feed, bool p_rgba) :
+ BufferDecoder(p_camera_feed) {
+ rgba = p_rgba;
+ image_data.resize(width * height * (rgba ? 4 : 2));
+}
+
+void CopyBufferDecoder::decode(StreamingBuffer p_buffer) {
+ uint8_t *dst = (uint8_t *)image_data.ptrw();
+ memcpy(dst, p_buffer.start, p_buffer.length);
+
+ if (image.is_valid()) {
+ image->set_data(width, height, false, rgba ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8, image_data);
+ } else {
+ image.instantiate(width, height, false, rgba ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8, image_data);
+ }
+
+ camera_feed->set_RGB_img(image);
+}
+
+JpegBufferDecoder::JpegBufferDecoder(CameraFeed *p_camera_feed) :
+ BufferDecoder(p_camera_feed) {
+}
+
+void JpegBufferDecoder::decode(StreamingBuffer p_buffer) {
+ image_data.resize(p_buffer.length);
+ uint8_t *dst = (uint8_t *)image_data.ptrw();
+ memcpy(dst, p_buffer.start, p_buffer.length);
+ if (image->load_jpg_from_buffer(image_data) == OK) {
+ camera_feed->set_RGB_img(image);
+ }
+}
diff --git a/modules/camera/buffer_decoder.h b/modules/camera/buffer_decoder.h
new file mode 100644
index 0000000000..97cc66b6da
--- /dev/null
+++ b/modules/camera/buffer_decoder.h
@@ -0,0 +1,116 @@
+/**************************************************************************/
+/* buffer_decoder.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef BUFFER_DECODER_H
+#define BUFFER_DECODER_H
+
+#include "core/io/image.h"
+#include "core/templates/vector.h"
+
+class CameraFeed;
+
+struct StreamingBuffer {
+ void *start = nullptr;
+ size_t length = 0;
+};
+
+class BufferDecoder {
+protected:
+ CameraFeed *camera_feed = nullptr;
+ Ref<Image> image;
+ int width = 0;
+ int height = 0;
+
+public:
+ virtual void decode(StreamingBuffer p_buffer) = 0;
+
+ BufferDecoder(CameraFeed *p_camera_feed);
+ virtual ~BufferDecoder() {}
+};
+
+class AbstractYuyvBufferDecoder : public BufferDecoder {
+protected:
+ int *component_indexes = nullptr;
+
+public:
+ AbstractYuyvBufferDecoder(CameraFeed *p_camera_feed);
+ ~AbstractYuyvBufferDecoder();
+};
+
+class SeparateYuyvBufferDecoder : public AbstractYuyvBufferDecoder {
+private:
+ Vector<uint8_t> y_image_data;
+ Vector<uint8_t> cbcr_image_data;
+ Ref<Image> y_image;
+ Ref<Image> cbcr_image;
+
+public:
+ SeparateYuyvBufferDecoder(CameraFeed *p_camera_feed);
+ virtual void decode(StreamingBuffer p_buffer) override;
+};
+
+class YuyvToGrayscaleBufferDecoder : public AbstractYuyvBufferDecoder {
+private:
+ Vector<uint8_t> image_data;
+
+public:
+ YuyvToGrayscaleBufferDecoder(CameraFeed *p_camera_feed);
+ virtual void decode(StreamingBuffer p_buffer) override;
+};
+
+class YuyvToRgbBufferDecoder : public AbstractYuyvBufferDecoder {
+private:
+ Vector<uint8_t> image_data;
+
+public:
+ YuyvToRgbBufferDecoder(CameraFeed *p_camera_feed);
+ virtual void decode(StreamingBuffer p_buffer) override;
+};
+
+class CopyBufferDecoder : public BufferDecoder {
+private:
+ Vector<uint8_t> image_data;
+ bool rgba = false;
+
+public:
+ CopyBufferDecoder(CameraFeed *p_camera_feed, bool p_rgba);
+ virtual void decode(StreamingBuffer p_buffer) override;
+};
+
+class JpegBufferDecoder : public BufferDecoder {
+private:
+ Vector<uint8_t> image_data;
+
+public:
+ JpegBufferDecoder(CameraFeed *p_camera_feed);
+ virtual void decode(StreamingBuffer p_buffer) override;
+};
+
+#endif // BUFFER_DECODER_H
diff --git a/modules/camera/camera_feed_linux.cpp b/modules/camera/camera_feed_linux.cpp
new file mode 100644
index 0000000000..9ed8eb0d0a
--- /dev/null
+++ b/modules/camera/camera_feed_linux.cpp
@@ -0,0 +1,363 @@
+/**************************************************************************/
+/* camera_feed_linux.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "camera_feed_linux.h"
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+void CameraFeedLinux::update_buffer_thread_func(void *p_func) {
+ if (p_func) {
+ CameraFeedLinux *camera_feed_linux = (CameraFeedLinux *)p_func;
+ camera_feed_linux->_update_buffer();
+ }
+}
+
+void CameraFeedLinux::_update_buffer() {
+ while (!exit_flag.is_set()) {
+ _read_frame();
+ usleep(10000);
+ }
+}
+
+void CameraFeedLinux::_query_device(const String &p_device_name) {
+ file_descriptor = open(p_device_name.ascii(), O_RDWR | O_NONBLOCK, 0);
+ ERR_FAIL_COND_MSG(file_descriptor == -1, vformat("Cannot open file descriptor for %s. Error: %d.", p_device_name, errno));
+
+ struct v4l2_capability capability;
+ if (ioctl(file_descriptor, VIDIOC_QUERYCAP, &capability) == -1) {
+ ERR_FAIL_MSG(vformat("Cannot query device. Error: %d.", errno));
+ }
+ name = String((char *)capability.card);
+
+ for (int index = 0;; index++) {
+ struct v4l2_fmtdesc fmtdesc;
+ memset(&fmtdesc, 0, sizeof(fmtdesc));
+ fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ fmtdesc.index = index;
+
+ if (ioctl(file_descriptor, VIDIOC_ENUM_FMT, &fmtdesc) == -1) {
+ break;
+ }
+
+ for (int res_index = 0;; res_index++) {
+ struct v4l2_frmsizeenum frmsizeenum;
+ memset(&frmsizeenum, 0, sizeof(frmsizeenum));
+ frmsizeenum.pixel_format = fmtdesc.pixelformat;
+ frmsizeenum.index = res_index;
+
+ if (ioctl(file_descriptor, VIDIOC_ENUM_FRAMESIZES, &frmsizeenum) == -1) {
+ break;
+ }
+
+ for (int framerate_index = 0;; framerate_index++) {
+ struct v4l2_frmivalenum frmivalenum;
+ memset(&frmivalenum, 0, sizeof(frmivalenum));
+ frmivalenum.pixel_format = fmtdesc.pixelformat;
+ frmivalenum.width = frmsizeenum.discrete.width;
+ frmivalenum.height = frmsizeenum.discrete.height;
+ frmivalenum.index = framerate_index;
+
+ if (ioctl(file_descriptor, VIDIOC_ENUM_FRAMEINTERVALS, &frmivalenum) == -1) {
+ if (framerate_index == 0) {
+ _add_format(fmtdesc, frmsizeenum.discrete, -1, 1);
+ }
+ break;
+ }
+
+ _add_format(fmtdesc, frmsizeenum.discrete, frmivalenum.discrete.numerator, frmivalenum.discrete.denominator);
+ }
+ }
+ }
+
+ close(file_descriptor);
+}
+
+void CameraFeedLinux::_add_format(v4l2_fmtdesc p_description, v4l2_frmsize_discrete p_size, int p_frame_numerator, int p_frame_denominator) {
+ FeedFormat feed_format;
+ feed_format.width = p_size.width;
+ feed_format.height = p_size.height;
+ feed_format.format = String((char *)p_description.description);
+ feed_format.frame_numerator = p_frame_numerator;
+ feed_format.frame_denominator = p_frame_denominator;
+ feed_format.pixel_format = p_description.pixelformat;
+ print_verbose(vformat("%s %dx%d@%d/%dfps", (char *)p_description.description, p_size.width, p_size.height, p_frame_denominator, p_frame_numerator));
+ formats.push_back(feed_format);
+}
+
+bool CameraFeedLinux::_request_buffers() {
+ struct v4l2_requestbuffers requestbuffers;
+
+ memset(&requestbuffers, 0, sizeof(requestbuffers));
+ requestbuffers.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ requestbuffers.memory = V4L2_MEMORY_MMAP;
+ requestbuffers.count = 4;
+
+ if (ioctl(file_descriptor, VIDIOC_REQBUFS, &requestbuffers) == -1) {
+ ERR_FAIL_V_MSG(false, vformat("ioctl(VIDIOC_REQBUFS) error: %d.", errno));
+ }
+
+ ERR_FAIL_COND_V_MSG(requestbuffers.count < 2, false, "Not enough buffers granted.");
+
+ buffer_count = requestbuffers.count;
+ buffers = new StreamingBuffer[buffer_count];
+
+ for (unsigned int i = 0; i < buffer_count; i++) {
+ struct v4l2_buffer buffer;
+
+ memset(&buffer, 0, sizeof(buffer));
+ buffer.type = requestbuffers.type;
+ buffer.memory = V4L2_MEMORY_MMAP;
+ buffer.index = i;
+
+ if (ioctl(file_descriptor, VIDIOC_QUERYBUF, &buffer) == -1) {
+ delete[] buffers;
+ ERR_FAIL_V_MSG(false, vformat("ioctl(VIDIOC_QUERYBUF) error: %d.", errno));
+ }
+
+ buffers[i].length = buffer.length;
+ buffers[i].start = mmap(NULL, buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED, file_descriptor, buffer.m.offset);
+
+ if (buffers[i].start == MAP_FAILED) {
+ for (unsigned int b = 0; b < i; b++) {
+ _unmap_buffers(i);
+ }
+ delete[] buffers;
+ ERR_FAIL_V_MSG(false, "Mapping buffers failed.");
+ }
+ }
+
+ return true;
+}
+
+bool CameraFeedLinux::_start_capturing() {
+ for (unsigned int i = 0; i < buffer_count; i++) {
+ struct v4l2_buffer buffer;
+
+ memset(&buffer, 0, sizeof(buffer));
+ buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buffer.memory = V4L2_MEMORY_MMAP;
+ buffer.index = i;
+
+ if (ioctl(file_descriptor, VIDIOC_QBUF, &buffer) == -1) {
+ ERR_FAIL_V_MSG(false, vformat("ioctl(VIDIOC_QBUF) error: %d.", errno));
+ }
+ }
+
+ enum v4l2_buf_type type;
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ if (ioctl(file_descriptor, VIDIOC_STREAMON, &type) == -1) {
+ ERR_FAIL_V_MSG(false, vformat("ioctl(VIDIOC_STREAMON) error: %d.", errno));
+ }
+
+ return true;
+}
+
+void CameraFeedLinux::_read_frame() {
+ struct v4l2_buffer buffer;
+ memset(&buffer, 0, sizeof(buffer));
+ buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buffer.memory = V4L2_MEMORY_MMAP;
+
+ if (ioctl(file_descriptor, VIDIOC_DQBUF, &buffer) == -1) {
+ if (errno != EAGAIN) {
+ print_error(vformat("ioctl(VIDIOC_DQBUF) error: %d.", errno));
+ exit_flag.set();
+ }
+ return;
+ }
+
+ buffer_decoder->decode(buffers[buffer.index]);
+
+ if (ioctl(file_descriptor, VIDIOC_QBUF, &buffer) == -1) {
+ print_error(vformat("ioctl(VIDIOC_QBUF) error: %d.", errno));
+ }
+
+ emit_signal(SNAME("frame_changed"));
+}
+
+void CameraFeedLinux::_stop_capturing() {
+ enum v4l2_buf_type type;
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ if (ioctl(file_descriptor, VIDIOC_STREAMOFF, &type) == -1) {
+ print_error(vformat("ioctl(VIDIOC_STREAMOFF) error: %d.", errno));
+ }
+}
+
+void CameraFeedLinux::_unmap_buffers(unsigned int p_count) {
+ for (unsigned int i = 0; i < p_count; i++) {
+ munmap(buffers[i].start, buffers[i].length);
+ }
+}
+
+void CameraFeedLinux::_start_thread() {
+ exit_flag.clear();
+ thread = memnew(Thread);
+ thread->start(CameraFeedLinux::update_buffer_thread_func, this);
+}
+
+String CameraFeedLinux::get_device_name() const {
+ return device_name;
+}
+
+bool CameraFeedLinux::activate_feed() {
+ file_descriptor = open(device_name.ascii(), O_RDWR | O_NONBLOCK, 0);
+ if (_request_buffers() && _start_capturing()) {
+ buffer_decoder = _create_buffer_decoder();
+ _start_thread();
+ return true;
+ }
+ ERR_FAIL_V_MSG(false, "Could not activate feed.");
+}
+
+BufferDecoder *CameraFeedLinux::_create_buffer_decoder() {
+ switch (formats[selected_format].pixel_format) {
+ case V4L2_PIX_FMT_MJPEG:
+ case V4L2_PIX_FMT_JPEG:
+ return memnew(JpegBufferDecoder(this));
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YYUV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY: {
+ String output = parameters["output"];
+ if (output == "separate") {
+ return memnew(SeparateYuyvBufferDecoder(this));
+ }
+ if (output == "grayscale") {
+ return memnew(YuyvToGrayscaleBufferDecoder(this));
+ }
+ if (output == "copy") {
+ return memnew(CopyBufferDecoder(this, false));
+ }
+ return memnew(YuyvToRgbBufferDecoder(this));
+ }
+ default:
+ return memnew(CopyBufferDecoder(this, true));
+ }
+}
+
+void CameraFeedLinux::deactivate_feed() {
+ exit_flag.set();
+ thread->wait_to_finish();
+ memdelete(thread);
+ _stop_capturing();
+ _unmap_buffers(buffer_count);
+ delete[] buffers;
+ memdelete(buffer_decoder);
+ for (int i = 0; i < CameraServer::FEED_IMAGES; i++) {
+ RID placeholder = RenderingServer::get_singleton()->texture_2d_placeholder_create();
+ RenderingServer::get_singleton()->texture_replace(texture[i], placeholder);
+ }
+ base_width = 0;
+ base_height = 0;
+ close(file_descriptor);
+
+ emit_signal(SNAME("format_changed"));
+}
+
+Array CameraFeedLinux::get_formats() const {
+ Array result;
+ for (const FeedFormat &format : formats) {
+ Dictionary dictionary;
+ dictionary["width"] = format.width;
+ dictionary["height"] = format.height;
+ dictionary["format"] = format.format;
+ dictionary["frame_numerator"] = format.frame_numerator;
+ dictionary["frame_denominator"] = format.frame_denominator;
+ result.push_back(dictionary);
+ }
+ return result;
+}
+
+CameraFeed::FeedFormat CameraFeedLinux::get_format() const {
+ return formats[selected_format];
+}
+
+bool CameraFeedLinux::set_format(int p_index, const Dictionary &p_parameters) {
+ ERR_FAIL_COND_V_MSG(active, false, "Feed is active.");
+ ERR_FAIL_INDEX_V_MSG(p_index, formats.size(), false, "Invalid format index.");
+
+ parameters = p_parameters.duplicate();
+ selected_format = p_index;
+
+ FeedFormat feed_format = formats[p_index];
+
+ file_descriptor = open(device_name.ascii(), O_RDWR | O_NONBLOCK, 0);
+
+ struct v4l2_format format;
+ memset(&format, 0, sizeof(format));
+ format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ format.fmt.pix.width = feed_format.width;
+ format.fmt.pix.height = feed_format.height;
+ format.fmt.pix.pixelformat = feed_format.pixel_format;
+
+ if (ioctl(file_descriptor, VIDIOC_S_FMT, &format) == -1) {
+ close(file_descriptor);
+ ERR_FAIL_V_MSG(false, vformat("Cannot set format, error: %d.", errno));
+ }
+
+ if (feed_format.frame_numerator > 0) {
+ struct v4l2_streamparm param;
+ memset(&param, 0, sizeof(param));
+
+ param.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ param.parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ param.parm.capture.timeperframe.numerator = feed_format.frame_numerator;
+ param.parm.capture.timeperframe.denominator = feed_format.frame_denominator;
+
+ if (ioctl(file_descriptor, VIDIOC_S_PARM, &param) == -1) {
+ close(file_descriptor);
+ ERR_FAIL_V_MSG(false, vformat("Cannot set framerate, error: %d.", errno));
+ }
+ }
+ close(file_descriptor);
+
+ emit_signal(SNAME("format_changed"));
+
+ return true;
+}
+
+CameraFeedLinux::CameraFeedLinux(const String &p_device_name) :
+ CameraFeed() {
+ device_name = p_device_name;
+ _query_device(device_name);
+ set_format(0, Dictionary());
+}
+
+CameraFeedLinux::~CameraFeedLinux() {
+ if (is_active()) {
+ deactivate_feed();
+ }
+}
diff --git a/modules/camera/camera_feed_linux.h b/modules/camera/camera_feed_linux.h
new file mode 100644
index 0000000000..bf29201c99
--- /dev/null
+++ b/modules/camera/camera_feed_linux.h
@@ -0,0 +1,78 @@
+/**************************************************************************/
+/* camera_feed_linux.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef CAMERA_FEED_LINUX_H
+#define CAMERA_FEED_LINUX_H
+
+#include "buffer_decoder.h"
+
+#include "core/os/thread.h"
+#include "servers/camera/camera_feed.h"
+
+#include <linux/videodev2.h>
+
+struct StreamingBuffer;
+
+class CameraFeedLinux : public CameraFeed {
+private:
+ SafeFlag exit_flag;
+ Thread *thread = nullptr;
+ String device_name;
+ int file_descriptor = -1;
+ StreamingBuffer *buffers = nullptr;
+ unsigned int buffer_count = 0;
+ BufferDecoder *buffer_decoder = nullptr;
+
+ static void update_buffer_thread_func(void *p_func);
+
+ void _update_buffer();
+ void _query_device(const String &p_device_name);
+ void _add_format(v4l2_fmtdesc description, v4l2_frmsize_discrete size, int frame_numerator, int frame_denominator);
+ bool _request_buffers();
+ bool _start_capturing();
+ void _read_frame();
+ void _stop_capturing();
+ void _unmap_buffers(unsigned int p_count);
+ BufferDecoder *_create_buffer_decoder();
+ void _start_thread();
+
+public:
+ String get_device_name() const;
+ bool activate_feed();
+ void deactivate_feed();
+ bool set_format(int p_index, const Dictionary &p_parameters);
+ Array get_formats() const;
+ FeedFormat get_format() const;
+
+ CameraFeedLinux(const String &p_device_name);
+ virtual ~CameraFeedLinux();
+};
+
+#endif // CAMERA_FEED_LINUX_H
diff --git a/modules/camera/camera_linux.cpp b/modules/camera/camera_linux.cpp
new file mode 100644
index 0000000000..0cfb6b7b9e
--- /dev/null
+++ b/modules/camera/camera_linux.cpp
@@ -0,0 +1,169 @@
+/**************************************************************************/
+/* camera_linux.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "camera_linux.h"
+
+#include "camera_feed_linux.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+void CameraLinux::camera_thread_func(void *p_camera_linux) {
+ if (p_camera_linux) {
+ CameraLinux *camera_linux = (CameraLinux *)p_camera_linux;
+ camera_linux->_update_devices();
+ }
+}
+
+void CameraLinux::_update_devices() {
+ while (!exit_flag.is_set()) {
+ {
+ MutexLock lock(camera_mutex);
+
+ for (int i = feeds.size() - 1; i >= 0; i--) {
+ Ref<CameraFeedLinux> feed = (Ref<CameraFeedLinux>)feeds[i];
+ String device_name = feed->get_device_name();
+ if (!_is_active(device_name)) {
+ remove_feed(feed);
+ }
+ }
+
+ DIR *devices = opendir("/dev");
+
+ if (devices) {
+ struct dirent *device;
+
+ while ((device = readdir(devices)) != nullptr) {
+ if (strncmp(device->d_name, "video", 5) != 0) {
+ continue;
+ }
+ String device_name = String("/dev/") + String(device->d_name);
+ if (!_has_device(device_name)) {
+ _add_device(device_name);
+ }
+ }
+ }
+
+ closedir(devices);
+ }
+
+ usleep(1000000);
+ }
+}
+
+bool CameraLinux::_has_device(const String &p_device_name) {
+ for (int i = 0; i < feeds.size(); i++) {
+ Ref<CameraFeedLinux> feed = (Ref<CameraFeedLinux>)feeds[i];
+ if (feed->get_device_name() == p_device_name) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void CameraLinux::_add_device(const String &p_device_name) {
+ int file_descriptor = _open_device(p_device_name);
+
+ if (file_descriptor != -1) {
+ if (_is_video_capture_device(file_descriptor)) {
+ Ref<CameraFeedLinux> feed = memnew(CameraFeedLinux(p_device_name));
+ add_feed(feed);
+ }
+ }
+
+ close(file_descriptor);
+}
+
+int CameraLinux::_open_device(const String &p_device_name) {
+ struct stat s;
+
+ if (stat(p_device_name.ascii(), &s) == -1) {
+ return -1;
+ }
+
+ if (!S_ISCHR(s.st_mode)) {
+ return -1;
+ }
+
+ return open(p_device_name.ascii(), O_RDWR | O_NONBLOCK, 0);
+}
+
+// TODO any cheaper/cleaner way to check if file descriptor is invalid?
+bool CameraLinux::_is_active(const String &p_device_name) {
+ struct v4l2_capability capability;
+ bool result = false;
+ int file_descriptor = _open_device(p_device_name);
+ if (file_descriptor != -1 && ioctl(file_descriptor, VIDIOC_QUERYCAP, &capability) != -1) {
+ result = true;
+ }
+ close(file_descriptor);
+ return result;
+}
+
+bool CameraLinux::_is_video_capture_device(int p_file_descriptor) {
+ struct v4l2_capability capability;
+
+ if (ioctl(p_file_descriptor, VIDIOC_QUERYCAP, &capability) == -1) {
+ print_verbose("Cannot query device");
+ return false;
+ }
+
+ if (!(capability.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
+ print_verbose(vformat("%s is no video capture device\n", String((char *)capability.card)));
+ return false;
+ }
+
+ if (!(capability.capabilities & V4L2_CAP_STREAMING)) {
+ print_verbose(vformat("%s does not support streaming", String((char *)capability.card)));
+ return false;
+ }
+
+ return _can_query_format(p_file_descriptor, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+}
+
+bool CameraLinux::_can_query_format(int p_file_descriptor, int p_type) {
+ struct v4l2_format format;
+ memset(&format, 0, sizeof(format));
+ format.type = p_type;
+
+ return ioctl(p_file_descriptor, VIDIOC_G_FMT, &format) != -1;
+}
+
+CameraLinux::CameraLinux() {
+ camera_thread.start(CameraLinux::camera_thread_func, this);
+};
+
+CameraLinux::~CameraLinux() {
+ exit_flag.set();
+ camera_thread.wait_to_finish();
+}
diff --git a/modules/camera/camera_linux.h b/modules/camera/camera_linux.h
new file mode 100644
index 0000000000..66f6aa0ffb
--- /dev/null
+++ b/modules/camera/camera_linux.h
@@ -0,0 +1,60 @@
+/**************************************************************************/
+/* camera_linux.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef CAMERA_LINUX_H
+#define CAMERA_LINUX_H
+
+#include "core/os/mutex.h"
+#include "core/os/thread.h"
+#include "servers/camera_server.h"
+
+class CameraLinux : public CameraServer {
+private:
+ SafeFlag exit_flag;
+ Thread camera_thread;
+ Mutex camera_mutex;
+
+ static void camera_thread_func(void *p_camera_linux);
+
+ void _update_devices();
+ bool _has_device(const String &p_device_name);
+ void _add_device(const String &p_device_name);
+ void _remove_device(const String &p_device_name);
+ int _open_device(const String &p_device_name);
+ bool _is_active(const String &p_device_name);
+ bool _is_video_capture_device(int p_file_descriptor);
+ bool _can_query_format(int p_file_descriptor, int p_type);
+
+public:
+ CameraLinux();
+ ~CameraLinux();
+};
+
+#endif // CAMERA_LINUX_H
diff --git a/modules/camera/config.py b/modules/camera/config.py
index d2b2542dd9..7b368d2193 100644
--- a/modules/camera/config.py
+++ b/modules/camera/config.py
@@ -1,5 +1,5 @@
def can_build(env, platform):
- return platform == "macos" or platform == "windows"
+ return platform == "macos" or platform == "windows" or platform == "linuxbsd"
def configure(env):
diff --git a/modules/camera/register_types.cpp b/modules/camera/register_types.cpp
index feee6769f8..666ea8ba65 100644
--- a/modules/camera/register_types.cpp
+++ b/modules/camera/register_types.cpp
@@ -30,6 +30,9 @@
#include "register_types.h"
+#if defined(LINUXBSD_ENABLED)
+#include "camera_linux.h"
+#endif
#if defined(WINDOWS_ENABLED)
#include "camera_win.h"
#endif
@@ -42,6 +45,9 @@ void initialize_camera_module(ModuleInitializationLevel p_level) {
return;
}
+#if defined(LINUXBSD_ENABLED)
+ CameraServer::make_default<CameraLinux>();
+#endif
#if defined(WINDOWS_ENABLED)
CameraServer::make_default<CameraWindows>();
#endif
diff --git a/modules/csg/SCsub b/modules/csg/SCsub
index 1cf9974fc1..f71618ab22 100644
--- a/modules/csg/SCsub
+++ b/modules/csg/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/csg/register_types.cpp b/modules/csg/register_types.cpp
index de949e30d8..19e6be4be1 100644
--- a/modules/csg/register_types.cpp
+++ b/modules/csg/register_types.cpp
@@ -30,8 +30,6 @@
#include "register_types.h"
-#ifndef _3D_DISABLED
-
#include "csg_shape.h"
#ifdef TOOLS_ENABLED
@@ -62,5 +60,3 @@ void uninitialize_csg_module(ModuleInitializationLevel p_level) {
return;
}
}
-
-#endif // _3D_DISABLED
diff --git a/modules/cvtt/SCsub b/modules/cvtt/SCsub
index 1d5a7ff6a3..44e56ab6a7 100644
--- a/modules/cvtt/SCsub
+++ b/modules/cvtt/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/dds/SCsub b/modules/dds/SCsub
index 06980bd670..d1c67c31ea 100644
--- a/modules/dds/SCsub
+++ b/modules/dds/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/enet/SCsub b/modules/enet/SCsub
index 580e5a3eb0..0c31638e46 100644
--- a/modules/enet/SCsub
+++ b/modules/enet/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/etcpak/SCsub b/modules/etcpak/SCsub
index 2d3b69be75..a872e1cd03 100644
--- a/modules/etcpak/SCsub
+++ b/modules/etcpak/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/fbx/SCsub b/modules/fbx/SCsub
index 6a791094c6..6f9fbba0b4 100644
--- a/modules/fbx/SCsub
+++ b/modules/fbx/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/freetype/SCsub b/modules/freetype/SCsub
index 2813eaecd5..5edce96680 100644
--- a/modules/freetype/SCsub
+++ b/modules/freetype/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/gdscript/SCsub b/modules/gdscript/SCsub
index 61accd4fc9..8f50bf9588 100644
--- a/modules/gdscript/SCsub
+++ b/modules/gdscript/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/gdscript/editor/script_templates/SCsub b/modules/gdscript/editor/script_templates/SCsub
index 5db7e3fc3b..28a27db3fa 100644
--- a/modules/gdscript/editor/script_templates/SCsub
+++ b/modules/gdscript/editor/script_templates/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index eebf282633..f4f445e096 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -2629,9 +2629,10 @@ Error GDScriptCompiler::_parse_setter_getter(GDScript *p_script, const GDScriptP
return err;
}
-// Prepares given script, and inner class scripts, for compilation. It populates class members and initializes method
-// RPC info for its base classes first, then for itself, then for inner classes.
-// Warning: this function cannot initiate compilation of other classes, or it will result in cyclic dependency issues.
+// Prepares given script, and inner class scripts, for compilation. It populates class members and
+// initializes method RPC info for its base classes first, then for itself, then for inner classes.
+// WARNING: This function cannot initiate compilation of other classes, or it will result in
+// cyclic dependency issues.
Error GDScriptCompiler::_prepare_compilation(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
if (parsed_classes.has(p_script)) {
return OK;
diff --git a/modules/glslang/SCsub b/modules/glslang/SCsub
index 3068377e60..b6e3da2316 100644
--- a/modules/glslang/SCsub
+++ b/modules/glslang/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/gltf/SCsub b/modules/gltf/SCsub
index 9d263cccac..1075116863 100644
--- a/modules/gltf/SCsub
+++ b/modules/gltf/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/gltf/extensions/SCsub b/modules/gltf/extensions/SCsub
index fdf14300f1..e403cd6fdc 100644
--- a/modules/gltf/extensions/SCsub
+++ b/modules/gltf/extensions/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/godot_physics_2d/SCsub b/modules/godot_physics_2d/SCsub
index 5d93da5ecf..39eb469978 100644
--- a/modules/godot_physics_2d/SCsub
+++ b/modules/godot_physics_2d/SCsub
@@ -1,5 +1,6 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
-Import('env')
+Import("env")
env.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/godot_physics_2d/godot_body_pair_2d.cpp b/modules/godot_physics_2d/godot_body_pair_2d.cpp
index 6c2d28dc92..98f11d6c07 100644
--- a/modules/godot_physics_2d/godot_body_pair_2d.cpp
+++ b/modules/godot_physics_2d/godot_body_pair_2d.cpp
@@ -161,11 +161,13 @@ void GodotBodyPair2D::_validate_contacts() {
}
}
-// _test_ccd prevents tunneling by slowing down a high velocity body that is about to collide so that next frame it will be at an appropriate location to collide (i.e. slight overlap)
-// Warning: the way velocity is adjusted down to cause a collision means the momentum will be weaker than it should for a bounce!
-// Process: only proceed if body A's motion is high relative to its size.
-// cast forward along motion vector to see if A is going to enter/pass B's collider next frame, only proceed if it does.
-// adjust the velocity of A down so that it will just slightly intersect the collider instead of blowing right past it.
+// `_test_ccd` prevents tunneling by slowing down a high velocity body that is about to collide so
+// that next frame it will be at an appropriate location to collide (i.e. slight overlap).
+// WARNING: The way velocity is adjusted down to cause a collision means the momentum will be
+// weaker than it should for a bounce!
+// Process: Only proceed if body A's motion is high relative to its size.
+// Cast forward along motion vector to see if A is going to enter/pass B's collider next frame, only proceed if it does.
+// Adjust the velocity of A down so that it will just slightly intersect the collider instead of blowing right past it.
bool GodotBodyPair2D::_test_ccd(real_t p_step, GodotBody2D *p_A, int p_shape_A, const Transform2D &p_xform_A, GodotBody2D *p_B, int p_shape_B, const Transform2D &p_xform_B) {
Vector2 motion = p_A->get_linear_velocity() * p_step;
real_t mlen = motion.length();
diff --git a/modules/godot_physics_2d/godot_step_2d.cpp b/modules/godot_physics_2d/godot_step_2d.cpp
index bbaec8be2b..418b9313e8 100644
--- a/modules/godot_physics_2d/godot_step_2d.cpp
+++ b/modules/godot_physics_2d/godot_step_2d.cpp
@@ -251,14 +251,14 @@ void GodotStep2D::step(GodotSpace2D *p_space, real_t p_delta) {
/* PRE-SOLVE CONSTRAINT ISLANDS */
- // Warning: This doesn't run on threads, because it involves thread-unsafe processing.
+ // WARNING: This doesn't run on threads, because it involves thread-unsafe processing.
for (uint32_t island_index = 0; island_index < island_count; ++island_index) {
_pre_solve_island(constraint_islands[island_index]);
}
/* SOLVE CONSTRAINT ISLANDS */
- // Warning: _solve_island modifies the constraint islands for optimization purpose,
+ // WARNING: `_solve_island` modifies the constraint islands for optimization purpose,
// their content is not reliable after these calls and shouldn't be used anymore.
group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &GodotStep2D::_solve_island, nullptr, island_count, -1, true, SNAME("Physics2DConstraintSolveIslands"));
WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
diff --git a/modules/godot_physics_3d/SCsub b/modules/godot_physics_3d/SCsub
index 41a59cd24e..1502eb39ee 100644
--- a/modules/godot_physics_3d/SCsub
+++ b/modules/godot_physics_3d/SCsub
@@ -1,6 +1,7 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
-Import('env')
+Import("env")
env.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/godot_physics_3d/godot_body_pair_3d.cpp b/modules/godot_physics_3d/godot_body_pair_3d.cpp
index 84fae73616..d689271ac7 100644
--- a/modules/godot_physics_3d/godot_body_pair_3d.cpp
+++ b/modules/godot_physics_3d/godot_body_pair_3d.cpp
@@ -161,11 +161,13 @@ void GodotBodyPair3D::validate_contacts() {
}
}
-// _test_ccd prevents tunneling by slowing down a high velocity body that is about to collide so that next frame it will be at an appropriate location to collide (i.e. slight overlap)
-// Warning: the way velocity is adjusted down to cause a collision means the momentum will be weaker than it should for a bounce!
-// Process: only proceed if body A's motion is high relative to its size.
-// cast forward along motion vector to see if A is going to enter/pass B's collider next frame, only proceed if it does.
-// adjust the velocity of A down so that it will just slightly intersect the collider instead of blowing right past it.
+// `_test_ccd` prevents tunneling by slowing down a high velocity body that is about to collide so
+// that next frame it will be at an appropriate location to collide (i.e. slight overlap).
+// WARNING: The way velocity is adjusted down to cause a collision means the momentum will be
+// weaker than it should for a bounce!
+// Process: Only proceed if body A's motion is high relative to its size.
+// Cast forward along motion vector to see if A is going to enter/pass B's collider next frame, only proceed if it does.
+// Adjust the velocity of A down so that it will just slightly intersect the collider instead of blowing right past it.
bool GodotBodyPair3D::_test_ccd(real_t p_step, GodotBody3D *p_A, int p_shape_A, const Transform3D &p_xform_A, GodotBody3D *p_B, int p_shape_B, const Transform3D &p_xform_B) {
GodotShape3D *shape_A_ptr = p_A->get_shape(p_shape_A);
diff --git a/modules/godot_physics_3d/godot_step_3d.cpp b/modules/godot_physics_3d/godot_step_3d.cpp
index d09a3b4e6d..b6cec4ba59 100644
--- a/modules/godot_physics_3d/godot_step_3d.cpp
+++ b/modules/godot_physics_3d/godot_step_3d.cpp
@@ -355,14 +355,14 @@ void GodotStep3D::step(GodotSpace3D *p_space, real_t p_delta) {
/* PRE-SOLVE CONSTRAINT ISLANDS */
- // Warning: This doesn't run on threads, because it involves thread-unsafe processing.
+ // WARNING: This doesn't run on threads, because it involves thread-unsafe processing.
for (uint32_t island_index = 0; island_index < island_count; ++island_index) {
_pre_solve_island(constraint_islands[island_index]);
}
/* SOLVE CONSTRAINT ISLANDS */
- // Warning: _solve_island modifies the constraint islands for optimization purpose,
+ // WARNING: `_solve_island` modifies the constraint islands for optimization purpose,
// their content is not reliable after these calls and shouldn't be used anymore.
group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &GodotStep3D::_solve_island, nullptr, island_count, -1, true, SNAME("Physics3DConstraintSolveIslands"));
WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
diff --git a/modules/godot_physics_3d/joints/SCsub b/modules/godot_physics_3d/joints/SCsub
index 5d93da5ecf..39eb469978 100644
--- a/modules/godot_physics_3d/joints/SCsub
+++ b/modules/godot_physics_3d/joints/SCsub
@@ -1,5 +1,6 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
-Import('env')
+Import("env")
env.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/gridmap/SCsub b/modules/gridmap/SCsub
index 282d772592..d4baa9000e 100644
--- a/modules/gridmap/SCsub
+++ b/modules/gridmap/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/hdr/SCsub b/modules/hdr/SCsub
index 10629bda3c..739b2caecf 100644
--- a/modules/hdr/SCsub
+++ b/modules/hdr/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/interactive_music/SCsub b/modules/interactive_music/SCsub
index 2950a30854..f2546747a0 100644
--- a/modules/interactive_music/SCsub
+++ b/modules/interactive_music/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/jpg/SCsub b/modules/jpg/SCsub
index b840542c1b..2d948d3355 100644
--- a/modules/jpg/SCsub
+++ b/modules/jpg/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/jsonrpc/SCsub b/modules/jsonrpc/SCsub
index 8ee4f8bfea..923567b138 100644
--- a/modules/jsonrpc/SCsub
+++ b/modules/jsonrpc/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/ktx/SCsub b/modules/ktx/SCsub
index c4cb732498..f4c394d734 100644
--- a/modules/ktx/SCsub
+++ b/modules/ktx/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/lightmapper_rd/SCsub b/modules/lightmapper_rd/SCsub
index fe9737b36f..157381ae98 100644
--- a/modules/lightmapper_rd/SCsub
+++ b/modules/lightmapper_rd/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/lightmapper_rd/lm_compute.glsl b/modules/lightmapper_rd/lm_compute.glsl
index 2c85fff6f3..31b721bb20 100644
--- a/modules/lightmapper_rd/lm_compute.glsl
+++ b/modules/lightmapper_rd/lm_compute.glsl
@@ -485,7 +485,7 @@ void trace_direct_light(vec3 p_position, vec3 p_normal, uint p_light_index, bool
float a = randomize(r_noise) * 2.0 * PI;
float vogel_index = float(total_ray_count - 1 - (i * shadowing_ray_count + j)); // Start from (total_ray_count - 1) so we check the outer points first.
- vec2 light_disk_sample = (get_vogel_disk(vogel_index, a, shadowing_ray_count_sqrt)) * soft_shadowing_disk_size * light_data.shadow_blur;
+ vec2 light_disk_sample = get_vogel_disk(vogel_index, a, shadowing_ray_count_sqrt) * soft_shadowing_disk_size * light_data.shadow_blur;
vec3 light_disk_to_point = normalize(light_to_point + light_disk_sample.x * light_to_point_tan + light_disk_sample.y * light_to_point_bitan);
// Offset the ray origin for AA, offset the light position for soft shadows.
if (trace_ray_any_hit(origin - light_disk_to_point * (bake_params.bias + length(disk_sample)), p_position - light_disk_to_point * dist) == RAY_MISS) {
diff --git a/modules/mbedtls/SCsub b/modules/mbedtls/SCsub
index 90ce98c751..e217ca5ca4 100644
--- a/modules/mbedtls/SCsub
+++ b/modules/mbedtls/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/meshoptimizer/SCsub b/modules/meshoptimizer/SCsub
index 3f86bb4f00..b335b5db3a 100644
--- a/modules/meshoptimizer/SCsub
+++ b/modules/meshoptimizer/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/minimp3/SCsub b/modules/minimp3/SCsub
index 09e84f71e9..e9491bb72f 100644
--- a/modules/minimp3/SCsub
+++ b/modules/minimp3/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/mobile_vr/SCsub b/modules/mobile_vr/SCsub
index e6c43228b4..b237f31209 100644
--- a/modules/mobile_vr/SCsub
+++ b/modules/mobile_vr/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/mono/SCsub b/modules/mono/SCsub
index d267df938a..f74f0fb9c1 100644
--- a/modules/mono/SCsub
+++ b/modules/mono/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
import build_scripts.mono_configure as mono_configure
diff --git a/modules/mono/editor/script_templates/SCsub b/modules/mono/editor/script_templates/SCsub
index 01c293c25d..f465374758 100644
--- a/modules/mono/editor/script_templates/SCsub
+++ b/modules/mono/editor/script_templates/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index 6abf0193cf..c81aa91fa5 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -388,7 +388,7 @@ godot_plugins_initialize_fn initialize_hostfxr_and_godot_plugins(bool &r_runtime
String assembly_name = path::get_csharp_project_name();
HostFxrCharString assembly_path = str_to_hostfxr(GodotSharpDirs::get_api_assemblies_dir()
- .path_join(assembly_name + ".dll"));
+ .path_join(assembly_name + ".dll"));
load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer =
initialize_hostfxr_self_contained(get_data(assembly_path));
diff --git a/modules/msdfgen/SCsub b/modules/msdfgen/SCsub
index f4316a74e7..844b0980ac 100644
--- a/modules/msdfgen/SCsub
+++ b/modules/msdfgen/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/multiplayer/SCsub b/modules/multiplayer/SCsub
index e89038c3e0..97f91c5674 100644
--- a/modules/multiplayer/SCsub
+++ b/modules/multiplayer/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/multiplayer/editor/editor_network_profiler.cpp b/modules/multiplayer/editor/editor_network_profiler.cpp
index 3a51712c70..f5f20d6931 100644
--- a/modules/multiplayer/editor/editor_network_profiler.cpp
+++ b/modules/multiplayer/editor/editor_network_profiler.cpp
@@ -193,6 +193,9 @@ void EditorNetworkProfiler::_update_button_text() {
}
void EditorNetworkProfiler::started() {
+ _clear_pressed();
+ activate->set_disabled(false);
+
if (EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_network_profiler", false)) {
set_profiling(true);
refresh_timer->start();
@@ -200,6 +203,7 @@ void EditorNetworkProfiler::started() {
}
void EditorNetworkProfiler::stopped() {
+ activate->set_disabled(true);
set_profiling(false);
refresh_timer->stop();
}
@@ -218,6 +222,7 @@ void EditorNetworkProfiler::_clear_pressed() {
set_bandwidth(0, 0);
refresh_rpc_data();
refresh_replication_data();
+ clear_button->set_disabled(true);
}
void EditorNetworkProfiler::_autostart_toggled(bool p_toggled_on) {
@@ -235,6 +240,9 @@ void EditorNetworkProfiler::_replication_button_clicked(TreeItem *p_item, int p_
}
void EditorNetworkProfiler::add_rpc_frame_data(const RPCNodeInfo &p_frame) {
+ if (clear_button->is_disabled()) {
+ clear_button->set_disabled(false);
+ }
dirty = true;
if (!rpc_data.has(p_frame.node)) {
rpc_data.insert(p_frame.node, p_frame);
@@ -251,6 +259,9 @@ void EditorNetworkProfiler::add_rpc_frame_data(const RPCNodeInfo &p_frame) {
}
void EditorNetworkProfiler::add_sync_frame_data(const SyncInfo &p_frame) {
+ if (clear_button->is_disabled()) {
+ clear_button->set_disabled(false);
+ }
dirty = true;
if (!sync_data.has(p_frame.synchronizer)) {
sync_data[p_frame.synchronizer] = p_frame;
@@ -292,11 +303,13 @@ EditorNetworkProfiler::EditorNetworkProfiler() {
activate = memnew(Button);
activate->set_toggle_mode(true);
activate->set_text(TTR("Start"));
+ activate->set_disabled(true);
activate->connect(SceneStringName(pressed), callable_mp(this, &EditorNetworkProfiler::_activate_pressed));
hb->add_child(activate);
clear_button = memnew(Button);
clear_button->set_text(TTR("Clear"));
+ clear_button->set_disabled(true);
clear_button->connect(SceneStringName(pressed), callable_mp(this, &EditorNetworkProfiler::_clear_pressed));
hb->add_child(clear_button);
diff --git a/modules/navigation/SCsub b/modules/navigation/SCsub
index 02d3b7487e..ab578252c1 100644
--- a/modules/navigation/SCsub
+++ b/modules/navigation/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/noise/SCsub b/modules/noise/SCsub
index f309fd2dd4..dcf51b03e3 100644
--- a/modules/noise/SCsub
+++ b/modules/noise/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/ogg/SCsub b/modules/ogg/SCsub
index f15525648f..fabd4f936a 100644
--- a/modules/ogg/SCsub
+++ b/modules/ogg/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/openxr/SCsub b/modules/openxr/SCsub
index 77922045eb..dd6a921440 100644
--- a/modules/openxr/SCsub
+++ b/modules/openxr/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/openxr/action_map/SCsub b/modules/openxr/action_map/SCsub
index 7a493011ec..d659be1d99 100644
--- a/modules/openxr/action_map/SCsub
+++ b/modules/openxr/action_map/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_openxr")
diff --git a/modules/openxr/editor/SCsub b/modules/openxr/editor/SCsub
index ccf67a80d0..39eb469978 100644
--- a/modules/openxr/editor/SCsub
+++ b/modules/openxr/editor/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/modules/openxr/editor/openxr_select_action_dialog.cpp b/modules/openxr/editor/openxr_select_action_dialog.cpp
index a4ccc98408..89dea09be4 100644
--- a/modules/openxr/editor/openxr_select_action_dialog.cpp
+++ b/modules/openxr/editor/openxr_select_action_dialog.cpp
@@ -66,7 +66,7 @@ void OpenXRSelectActionDialog::_on_select_action(const String p_action) {
void OpenXRSelectActionDialog::open() {
ERR_FAIL_COND(action_map.is_null());
- // out with the old...
+ // Out with the old.
while (main_vb->get_child_count() > 0) {
memdelete(main_vb->get_child(0));
}
@@ -74,6 +74,7 @@ void OpenXRSelectActionDialog::open() {
selected_action = "";
action_buttons.clear();
+ // In with the new.
Array action_sets = action_map->get_action_sets();
for (int i = 0; i < action_sets.size(); i++) {
Ref<OpenXRActionSet> action_set = action_sets[i];
diff --git a/modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp b/modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp
index 53b8cbd401..ee8940f30b 100644
--- a/modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp
+++ b/modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp
@@ -66,15 +66,15 @@ void OpenXRSelectInteractionProfileDialog::_on_select_interaction_profile(const
void OpenXRSelectInteractionProfileDialog::open(PackedStringArray p_do_not_include) {
int available_count = 0;
- // out with the old...
- while (main_vb->get_child_count() > 0) {
- memdelete(main_vb->get_child(0));
+ // Out with the old.
+ while (main_vb->get_child_count() > 1) {
+ memdelete(main_vb->get_child(1));
}
selected_interaction_profile = "";
ip_buttons.clear();
- // in with the new
+ // In with the new.
PackedStringArray interaction_profiles = OpenXRInteractionProfileMetadata::get_singleton()->get_interaction_profile_paths();
for (int i = 0; i < interaction_profiles.size(); i++) {
const String &path = interaction_profiles[i];
@@ -82,6 +82,7 @@ void OpenXRSelectInteractionProfileDialog::open(PackedStringArray p_do_not_inclu
Button *ip_button = memnew(Button);
ip_button->set_flat(true);
ip_button->set_text(OpenXRInteractionProfileMetadata::get_singleton()->get_profile(path)->display_name);
+ ip_button->set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
ip_button->connect(SceneStringName(pressed), callable_mp(this, &OpenXRSelectInteractionProfileDialog::_on_select_interaction_profile).bind(path));
main_vb->add_child(ip_button);
@@ -90,23 +91,16 @@ void OpenXRSelectInteractionProfileDialog::open(PackedStringArray p_do_not_inclu
}
}
- if (available_count == 0) {
- // give warning that we have all profiles selected
-
- } else {
- // TODO maybe if we only have one, auto select it?
-
- popup_centered();
- }
+ all_selected->set_visible(available_count == 0);
+ get_cancel_button()->set_visible(available_count > 0);
+ popup_centered();
}
void OpenXRSelectInteractionProfileDialog::ok_pressed() {
- if (selected_interaction_profile == "") {
- return;
+ if (selected_interaction_profile != "") {
+ emit_signal("interaction_profile_selected", selected_interaction_profile);
}
- emit_signal("interaction_profile_selected", selected_interaction_profile);
-
hide();
}
@@ -118,6 +112,10 @@ OpenXRSelectInteractionProfileDialog::OpenXRSelectInteractionProfileDialog() {
add_child(scroll);
main_vb = memnew(VBoxContainer);
- // main_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ main_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
scroll->add_child(main_vb);
+
+ all_selected = memnew(Label);
+ all_selected->set_text(TTR("All interaction profiles have been added to the action map."));
+ main_vb->add_child(all_selected);
}
diff --git a/modules/openxr/editor/openxr_select_interaction_profile_dialog.h b/modules/openxr/editor/openxr_select_interaction_profile_dialog.h
index 1d1615321c..d85e4cd4d6 100644
--- a/modules/openxr/editor/openxr_select_interaction_profile_dialog.h
+++ b/modules/openxr/editor/openxr_select_interaction_profile_dialog.h
@@ -51,6 +51,7 @@ private:
VBoxContainer *main_vb = nullptr;
ScrollContainer *scroll = nullptr;
+ Label *all_selected = nullptr;
protected:
static void _bind_methods();
diff --git a/modules/openxr/extensions/SCsub b/modules/openxr/extensions/SCsub
index 1bd9cfaa22..95b75ccd65 100644
--- a/modules/openxr/extensions/SCsub
+++ b/modules/openxr/extensions/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_openxr")
diff --git a/modules/openxr/scene/SCsub b/modules/openxr/scene/SCsub
index 7a493011ec..d659be1d99 100644
--- a/modules/openxr/scene/SCsub
+++ b/modules/openxr/scene/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_openxr")
diff --git a/modules/raycast/SCsub b/modules/raycast/SCsub
index f3a8e30763..bbf5ff7983 100644
--- a/modules/raycast/SCsub
+++ b/modules/raycast/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/regex/SCsub b/modules/regex/SCsub
index f5e2dd5dfc..5d70604e76 100644
--- a/modules/regex/SCsub
+++ b/modules/regex/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/squish/SCsub b/modules/squish/SCsub
index c9e29911d8..d8e7fbc142 100644
--- a/modules/squish/SCsub
+++ b/modules/squish/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/svg/SCsub b/modules/svg/SCsub
index a32be0e41a..af8f6c14f4 100644
--- a/modules/svg/SCsub
+++ b/modules/svg/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub
index 4112b81622..304a09515c 100644
--- a/modules/text_server_adv/SCsub
+++ b/modules/text_server_adv/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/text_server_adv/gdextension_build/SConstruct b/modules/text_server_adv/gdextension_build/SConstruct
index effed1e772..8f4f2cba40 100644
--- a/modules/text_server_adv/gdextension_build/SConstruct
+++ b/modules/text_server_adv/gdextension_build/SConstruct
@@ -1,4 +1,6 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
+
import atexit
import sys
import time
diff --git a/modules/text_server_fb/SCsub b/modules/text_server_fb/SCsub
index fc0a8727c6..b56df192c2 100644
--- a/modules/text_server_fb/SCsub
+++ b/modules/text_server_fb/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/text_server_fb/gdextension_build/SConstruct b/modules/text_server_fb/gdextension_build/SConstruct
index a3c2052040..dc849d5814 100644
--- a/modules/text_server_fb/gdextension_build/SConstruct
+++ b/modules/text_server_fb/gdextension_build/SConstruct
@@ -1,4 +1,6 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
+
import atexit
import sys
import time
diff --git a/modules/tga/SCsub b/modules/tga/SCsub
index ccd7d2ee37..c7f58e87f7 100644
--- a/modules/tga/SCsub
+++ b/modules/tga/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/theora/SCsub b/modules/theora/SCsub
index ca666050dd..be557c1c24 100644
--- a/modules/theora/SCsub
+++ b/modules/theora/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/tinyexr/SCsub b/modules/tinyexr/SCsub
index bf9242cc16..434e99bf84 100644
--- a/modules/tinyexr/SCsub
+++ b/modules/tinyexr/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/upnp/SCsub b/modules/upnp/SCsub
index 98c03e9ee9..6657d75cae 100644
--- a/modules/upnp/SCsub
+++ b/modules/upnp/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/vhacd/SCsub b/modules/vhacd/SCsub
index 1ff4122114..926cc5b16f 100644
--- a/modules/vhacd/SCsub
+++ b/modules/vhacd/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/vorbis/SCsub b/modules/vorbis/SCsub
index 322314487f..f063d97fee 100644
--- a/modules/vorbis/SCsub
+++ b/modules/vorbis/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/webp/SCsub b/modules/webp/SCsub
index dde4450c23..a939e2f90e 100644
--- a/modules/webp/SCsub
+++ b/modules/webp/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/webrtc/SCsub b/modules/webrtc/SCsub
index e315633f55..0c5f2c9dda 100644
--- a/modules/webrtc/SCsub
+++ b/modules/webrtc/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/websocket/SCsub b/modules/websocket/SCsub
index 8b469fe5be..acaa0d3ceb 100644
--- a/modules/websocket/SCsub
+++ b/modules/websocket/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/webxr/SCsub b/modules/webxr/SCsub
index 81caa4a279..9fe4e03ea6 100644
--- a/modules/webxr/SCsub
+++ b/modules/webxr/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/xatlas_unwrap/SCsub b/modules/xatlas_unwrap/SCsub
index aa6bdaea33..ae82a53bd9 100644
--- a/modules/xatlas_unwrap/SCsub
+++ b/modules/xatlas_unwrap/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/modules/zip/SCsub b/modules/zip/SCsub
index b7710123fd..0bab3ff5f9 100644
--- a/modules/zip/SCsub
+++ b/modules/zip/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
diff --git a/platform/SCsub b/platform/SCsub
index cdaa6074ba..7c9d07f6ef 100644
--- a/platform/SCsub
+++ b/platform/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
from glob import glob
from pathlib import Path
diff --git a/platform/android/SCsub b/platform/android/SCsub
index 8c88b419b3..3bc8959351 100644
--- a/platform/android/SCsub
+++ b/platform/android/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
import subprocess
import sys
diff --git a/platform/android/api/java_class_wrapper.h b/platform/android/api/java_class_wrapper.h
index 52df1644be..71f9c32318 100644
--- a/platform/android/api/java_class_wrapper.h
+++ b/platform/android/api/java_class_wrapper.h
@@ -47,7 +47,7 @@ class JavaClass : public RefCounted {
GDCLASS(JavaClass, RefCounted);
#ifdef ANDROID_ENABLED
- enum ArgumentType{
+ enum ArgumentType {
ARG_TYPE_VOID,
ARG_TYPE_BOOLEAN,
ARG_TYPE_BYTE,
diff --git a/platform/ios/SCsub b/platform/ios/SCsub
index cff7dcc1fd..959a657aac 100644
--- a/platform/ios/SCsub
+++ b/platform/ios/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/platform/linuxbsd/SCsub b/platform/linuxbsd/SCsub
index 0802b528f4..4def765e9c 100644
--- a/platform/linuxbsd/SCsub
+++ b/platform/linuxbsd/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/platform/linuxbsd/wayland/SCsub b/platform/linuxbsd/wayland/SCsub
index 89b586845c..1a8e243728 100644
--- a/platform/linuxbsd/wayland/SCsub
+++ b/platform/linuxbsd/wayland/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/platform/linuxbsd/wayland/wayland_thread.h b/platform/linuxbsd/wayland/wayland_thread.h
index ad2e843a74..819a1205d6 100644
--- a/platform/linuxbsd/wayland/wayland_thread.h
+++ b/platform/linuxbsd/wayland/wayland_thread.h
@@ -665,7 +665,7 @@ private:
.preferred_buffer_transform = _wl_surface_on_preferred_buffer_transform,
};
- static constexpr struct wl_callback_listener frame_wl_callback_listener {
+ static constexpr struct wl_callback_listener frame_wl_callback_listener = {
.done = _frame_wl_callback_on_done,
};
@@ -683,7 +683,7 @@ private:
.name = _wl_seat_on_name,
};
- static constexpr struct wl_callback_listener cursor_frame_callback_listener {
+ static constexpr struct wl_callback_listener cursor_frame_callback_listener = {
.done = _cursor_frame_callback_on_done,
};
diff --git a/platform/linuxbsd/x11/SCsub b/platform/linuxbsd/x11/SCsub
index 75fe584ad5..b76b98447f 100644
--- a/platform/linuxbsd/x11/SCsub
+++ b/platform/linuxbsd/x11/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index 7949f80f24..97ea1147ec 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -2998,11 +2998,7 @@ bool DisplayServerX11::window_is_focused(WindowID p_window) const {
const WindowData &wd = windows[p_window];
- Window focused_window;
- int focus_ret_state;
- XGetInputFocus(x11_display, &focused_window, &focus_ret_state);
-
- return wd.x11_window == focused_window;
+ return wd.focused;
}
bool DisplayServerX11::window_can_draw(WindowID p_window) const {
@@ -3050,7 +3046,7 @@ void DisplayServerX11::window_set_ime_active(const bool p_active, WindowID p_win
XWindowAttributes xwa;
XSync(x11_display, False);
XGetWindowAttributes(x11_display, wd.x11_xim_window, &xwa);
- if (xwa.map_state == IsViewable) {
+ if (xwa.map_state == IsViewable && _window_focus_check()) {
_set_input_focus(wd.x11_xim_window, RevertToParent);
}
XSetICFocus(wd.xic);
@@ -4319,7 +4315,7 @@ bool DisplayServerX11::_window_focus_check() {
bool has_focus = false;
for (const KeyValue<int, DisplayServerX11::WindowData> &wid : windows) {
- if (wid.value.x11_window == focused_window) {
+ if (wid.value.x11_window == focused_window || (wid.value.xic && wid.value.ime_active && wid.value.x11_xim_window == focused_window)) {
has_focus = true;
break;
}
diff --git a/platform/macos/SCsub b/platform/macos/SCsub
index a10262c524..3924e79fb6 100644
--- a/platform/macos/SCsub
+++ b/platform/macos/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/platform/web/SCsub b/platform/web/SCsub
index e81f2ec516..b30bf20f26 100644
--- a/platform/web/SCsub
+++ b/platform/web/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
from methods import print_error
diff --git a/platform/windows/SCsub b/platform/windows/SCsub
index f8ed8b73f5..1d17e7b325 100644
--- a/platform/windows/SCsub
+++ b/platform/windows/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index ed9d5244a3..5b166d0075 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -3962,9 +3962,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
}
- // WARNING: we get called with events before the window is registered in our collection
+ // WARNING: We get called with events before the window is registered in our collection
// specifically, even the call to CreateWindowEx already calls here while still on the stack,
- // so there is no way to store the window handle in our collection before we get here
+ // so there is no way to store the window handle in our collection before we get here.
if (!window_created) {
// don't let code below operate on incompletely initialized window objects or missing window_id
return _handle_early_window_message(hWnd, uMsg, wParam, lParam);
diff --git a/platform/windows/gl_manager_windows_native.cpp b/platform/windows/gl_manager_windows_native.cpp
index 8590c46d12..af703f4dfa 100644
--- a/platform/windows/gl_manager_windows_native.cpp
+++ b/platform/windows/gl_manager_windows_native.cpp
@@ -452,8 +452,8 @@ Error GLManagerNative_Windows::window_create(DisplayServer::WindowID p_window_id
return FAILED;
}
- // WARNING: p_window_id is an eternally growing integer since popup windows keep coming and going
- // and each of them has a higher id than the previous, so it must be used in a map not a vector
+ // WARNING: `p_window_id` is an eternally growing integer since popup windows keep coming and going
+ // and each of them has a higher id than the previous, so it must be used in a map not a vector.
_windows[p_window_id] = win;
// make current
diff --git a/pyproject.toml b/pyproject.toml
index 1cf789b460..a4bfd27816 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -26,7 +26,19 @@ extend-select = [
[tool.ruff.lint.per-file-ignores]
"{SConstruct,SCsub}" = [
"E402", # Module level import not at top of file
- "F821", # Undefined name
+ "F403", # Undefined local with import star
+ "F405", # Undefined local with import star usage
+]
+
+[tool.ruff.lint.isort]
+sections = { metadata = ["misc.utility.scons_hints"] }
+section-order = [
+ "future",
+ "metadata",
+ "standard-library",
+ "third-party",
+ "first-party",
+ "local-folder",
]
[tool.codespell]
@@ -70,6 +82,7 @@ ignore-words-list = """\
numer,
ot,
outin,
+ parm,
requestor,
te,
textin,
diff --git a/scene/2d/SCsub b/scene/2d/SCsub
index 94e1ab6c96..6f6bf9818c 100644
--- a/scene/2d/SCsub
+++ b/scene/2d/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/2d/parallax_2d.cpp b/scene/2d/parallax_2d.cpp
index fdb2d2cdd0..c6176390dc 100644
--- a/scene/2d/parallax_2d.cpp
+++ b/scene/2d/parallax_2d.cpp
@@ -83,7 +83,11 @@ void Parallax2D::_validate_property(PropertyInfo &p_property) const {
void Parallax2D::_camera_moved(const Transform2D &p_transform, const Point2 &p_screen_offset, const Point2 &p_adj_screen_pos) {
if (!ignore_camera_scroll) {
if (get_viewport() && get_viewport()->is_snap_2d_transforms_to_pixel_enabled()) {
- set_screen_offset((p_adj_screen_pos + Vector2(0.5, 0.5)).floor());
+ Size2 vps = get_viewport_rect().size;
+ Vector2 offset;
+ offset.x = ((int)vps.width % 2) ? 0.0 : 0.5;
+ offset.y = ((int)vps.height % 2) ? 0.0 : 0.5;
+ set_screen_offset((p_adj_screen_pos + offset).floor());
} else {
set_screen_offset(p_adj_screen_pos);
}
diff --git a/scene/2d/physics/SCsub b/scene/2d/physics/SCsub
index e7fd3fe643..5f9747514a 100644
--- a/scene/2d/physics/SCsub
+++ b/scene/2d/physics/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/2d/physics/joints/SCsub b/scene/2d/physics/joints/SCsub
index fc61250247..374dc2119d 100644
--- a/scene/2d/physics/joints/SCsub
+++ b/scene/2d/physics/joints/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/3d/SCsub b/scene/3d/SCsub
index 94e1ab6c96..6f6bf9818c 100644
--- a/scene/3d/SCsub
+++ b/scene/3d/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/3d/physics/SCsub b/scene/3d/physics/SCsub
index e7fd3fe643..5f9747514a 100644
--- a/scene/3d/physics/SCsub
+++ b/scene/3d/physics/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/3d/physics/joints/SCsub b/scene/3d/physics/joints/SCsub
index fc61250247..374dc2119d 100644
--- a/scene/3d/physics/joints/SCsub
+++ b/scene/3d/physics/joints/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/SCsub b/scene/SCsub
index b4b2d6dd0a..1eb4ffa53d 100644
--- a/scene/SCsub
+++ b/scene/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/animation/SCsub b/scene/animation/SCsub
index d0aa0bc8aa..dd2b22c2e3 100644
--- a/scene/animation/SCsub
+++ b/scene/animation/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/audio/SCsub b/scene/audio/SCsub
index fc61250247..374dc2119d 100644
--- a/scene/audio/SCsub
+++ b/scene/audio/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/debugger/SCsub b/scene/debugger/SCsub
index fc61250247..374dc2119d 100644
--- a/scene/debugger/SCsub
+++ b/scene/debugger/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/gui/SCsub b/scene/gui/SCsub
index fc61250247..374dc2119d 100644
--- a/scene/gui/SCsub
+++ b/scene/gui/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index c92dcbc153..002a738b83 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -33,8 +33,6 @@
#include "core/input/input.h"
#include "core/io/image.h"
#include "core/math/color.h"
-#include "core/os/keyboard.h"
-#include "core/os/os.h"
#include "scene/gui/color_mode.h"
#include "scene/gui/margin_container.h"
#include "scene/resources/image_texture.h"
@@ -42,7 +40,6 @@
#include "scene/resources/style_box_texture.h"
#include "scene/theme/theme_db.h"
#include "servers/display_server.h"
-#include "thirdparty/misc/ok_color.h"
#include "thirdparty/misc/ok_color_shader.h"
List<Color> ColorPicker::preset_cache;
@@ -1567,7 +1564,7 @@ void ColorPicker::_pick_button_pressed_legacy() {
picker_preview_label = memnew(Label);
picker_preview->set_anchors_preset(Control::PRESET_CENTER_TOP);
- picker_preview_label->set_text("Color Picking active");
+ picker_preview_label->set_text(ETR("Color Picking active"));
picker_preview->add_child(picker_preview_label);
picker_preview_style_box = (Ref<StyleBoxFlat>)memnew(StyleBoxFlat);
@@ -1905,7 +1902,7 @@ ColorPicker::ColorPicker() {
mode_popup->add_radio_check_item(modes[i]->get_name(), i);
}
mode_popup->add_separator();
- mode_popup->add_check_item("Colorized Sliders", MODE_MAX);
+ mode_popup->add_check_item(ETR("Colorized Sliders"), MODE_MAX);
mode_popup->set_item_checked(current_mode, true);
mode_popup->set_item_checked(MODE_MAX + 1, true);
mode_popup->connect(SceneStringName(id_pressed), callable_mp(this, &ColorPicker::_set_mode_popup_value));
@@ -1933,7 +1930,7 @@ ColorPicker::ColorPicker() {
hex_hbc->set_alignment(ALIGNMENT_BEGIN);
vbr->add_child(hex_hbc);
- hex_hbc->add_child(memnew(Label("Hex")));
+ hex_hbc->add_child(memnew(Label(ETR("Hex"))));
text_type = memnew(Button);
hex_hbc->add_child(text_type);
@@ -1997,8 +1994,7 @@ ColorPicker::ColorPicker() {
preset_group.instantiate();
- btn_preset = memnew(Button);
- btn_preset->set_text("Swatches");
+ btn_preset = memnew(Button(ETR("Swatches")));
btn_preset->set_flat(true);
btn_preset->set_toggle_mode(true);
btn_preset->set_focus_mode(FOCUS_NONE);
@@ -2014,8 +2010,7 @@ ColorPicker::ColorPicker() {
recent_preset_group.instantiate();
- btn_recent_preset = memnew(Button);
- btn_recent_preset->set_text("Recent Colors");
+ btn_recent_preset = memnew(Button(ETR("Recent Colors")));
btn_recent_preset->set_flat(true);
btn_recent_preset->set_toggle_mode(true);
btn_recent_preset->set_focus_mode(FOCUS_NONE);
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 43782409a8..6e5b555cdf 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -103,7 +103,7 @@ void LineEdit::_close_ime_window() {
void LineEdit::_update_ime_window_position() {
DisplayServer::WindowID wid = get_window() ? get_window()->get_window_id() : DisplayServer::INVALID_WINDOW_ID;
- if (wid == DisplayServer::INVALID_WINDOW_ID || !DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME) || !DisplayServer::get_singleton()->window_is_focused(wid)) {
+ if (wid == DisplayServer::INVALID_WINDOW_ID || !DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) {
return;
}
DisplayServer::get_singleton()->window_set_ime_active(true, wid);
diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp
index af9f08e389..eb69dab478 100644
--- a/scene/gui/scroll_bar.cpp
+++ b/scene/gui/scroll_bar.cpp
@@ -472,7 +472,7 @@ double ScrollBar::get_area_size() const {
}
double ScrollBar::get_grabber_offset() const {
- return (get_area_size()) * get_as_ratio();
+ return get_area_size() * get_as_ratio();
}
Size2 ScrollBar::get_minimum_size() const {
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 0e8d76d294..6b5ff23436 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -2958,7 +2958,7 @@ void TextEdit::_close_ime_window() {
void TextEdit::_update_ime_window_position() {
DisplayServer::WindowID wid = get_window() ? get_window()->get_window_id() : DisplayServer::INVALID_WINDOW_ID;
- if (wid == DisplayServer::INVALID_WINDOW_ID || !DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME) || !DisplayServer::get_singleton()->window_is_focused(wid)) {
+ if (wid == DisplayServer::INVALID_WINDOW_ID || !DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) {
return;
}
DisplayServer::get_singleton()->window_set_ime_active(true, wid);
diff --git a/scene/main/SCsub b/scene/main/SCsub
index fc61250247..374dc2119d 100644
--- a/scene/main/SCsub
+++ b/scene/main/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/resources/2d/SCsub b/scene/resources/2d/SCsub
index fdf20e0bde..408aa3cf7e 100644
--- a/scene/resources/2d/SCsub
+++ b/scene/resources/2d/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/resources/3d/SCsub b/scene/resources/3d/SCsub
index fdf20e0bde..408aa3cf7e 100644
--- a/scene/resources/3d/SCsub
+++ b/scene/resources/3d/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/resources/SCsub b/scene/resources/SCsub
index 2b6aa88d2c..46f6251b91 100644
--- a/scene/resources/SCsub
+++ b/scene/resources/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index a2ed6af23c..9abc6a02d2 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -321,8 +321,12 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
Vector<real_t> times = d["times"];
Vector<real_t> values = d["points"];
#ifdef TOOLS_ENABLED
- ERR_FAIL_COND_V(!d.has("handle_modes"), false);
- Vector<int> handle_modes = d["handle_modes"];
+ Vector<int> handle_modes;
+ if (d.has("handle_modes")) {
+ handle_modes = d["handle_modes"];
+ } else {
+ handle_modes.resize_zeroed(times.size());
+ }
#endif // TOOLS_ENABLED
ERR_FAIL_COND_V(times.size() * 5 != values.size(), false);
@@ -4804,9 +4808,9 @@ void Animation::compress(uint32_t p_page_size, uint32_t p_fps, float p_split_tol
continue; // This track is exhausted (all keys were added already), don't consider.
}
}
-
- uint32_t key_frame = double(track_get_key_time(uncomp_track, time_tracks[i].key_index)) / frame_len;
-
+ double key_time = track_get_key_time(uncomp_track, time_tracks[i].key_index);
+ double result = key_time / frame_len;
+ uint32_t key_frame = Math::fast_ftoi(result);
if (time_tracks[i].needs_start_frame && key_frame > base_page_frame) {
start_frame = true;
best_frame = base_page_frame;
diff --git a/scene/resources/camera_texture.cpp b/scene/resources/camera_texture.cpp
index b575a099ed..b219f89e59 100644
--- a/scene/resources/camera_texture.cpp
+++ b/scene/resources/camera_texture.cpp
@@ -47,6 +47,11 @@ void CameraTexture::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "camera_is_active"), "set_camera_active", "get_camera_active");
}
+void CameraTexture::_on_format_changed() {
+ // FIXME: `emit_changed` is more appropriate, but causes errors for some reason.
+ callable_mp((Resource *)this, &Resource::emit_changed).call_deferred();
+}
+
int CameraTexture::get_width() const {
Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
if (feed.is_valid()) {
@@ -82,13 +87,26 @@ RID CameraTexture::get_rid() const {
}
Ref<Image> CameraTexture::get_image() const {
- // not (yet) supported
- return Ref<Image>();
+ return RenderingServer::get_singleton()->texture_2d_get(get_rid());
}
void CameraTexture::set_camera_feed_id(int p_new_id) {
+ Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
+ if (feed.is_valid()) {
+ if (feed->is_connected("format_changed", callable_mp(this, &CameraTexture::_on_format_changed))) {
+ feed->disconnect("format_changed", callable_mp(this, &CameraTexture::_on_format_changed));
+ }
+ }
+
camera_feed_id = p_new_id;
+
+ feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
+ if (feed.is_valid()) {
+ feed->connect("format_changed", callable_mp(this, &CameraTexture::_on_format_changed));
+ }
+
notify_property_list_changed();
+ callable_mp((Resource *)this, &Resource::emit_changed).call_deferred();
}
int CameraTexture::get_camera_feed_id() const {
@@ -98,6 +116,7 @@ int CameraTexture::get_camera_feed_id() const {
void CameraTexture::set_which_feed(CameraServer::FeedImage p_which) {
which_feed = p_which;
notify_property_list_changed();
+ callable_mp((Resource *)this, &Resource::emit_changed).call_deferred();
}
CameraServer::FeedImage CameraTexture::get_which_feed() const {
@@ -109,6 +128,7 @@ void CameraTexture::set_camera_active(bool p_active) {
if (feed.is_valid()) {
feed->set_active(p_active);
notify_property_list_changed();
+ callable_mp((Resource *)this, &Resource::emit_changed).call_deferred();
}
}
diff --git a/scene/resources/camera_texture.h b/scene/resources/camera_texture.h
index 521121f9ea..dd216a72d6 100644
--- a/scene/resources/camera_texture.h
+++ b/scene/resources/camera_texture.h
@@ -43,6 +43,7 @@ private:
protected:
static void _bind_methods();
+ void _on_format_changed();
public:
virtual int get_width() const override;
diff --git a/scene/theme/SCsub b/scene/theme/SCsub
index 2372d1820a..fb0914c0ee 100644
--- a/scene/theme/SCsub
+++ b/scene/theme/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/theme/icons/SCsub b/scene/theme/icons/SCsub
index 1f3b7f6d17..19aca74e57 100644
--- a/scene/theme/icons/SCsub
+++ b/scene/theme/icons/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/servers/SCsub b/servers/SCsub
index 28180a7bb2..7abe53b9e1 100644
--- a/servers/SCsub
+++ b/servers/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/servers/audio/SCsub b/servers/audio/SCsub
index 5021e578c3..7d293c628d 100644
--- a/servers/audio/SCsub
+++ b/servers/audio/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/servers/audio/audio_driver_dummy.cpp b/servers/audio/audio_driver_dummy.cpp
index e1bfcbaca1..d41aa752ed 100644
--- a/servers/audio/audio_driver_dummy.cpp
+++ b/servers/audio/audio_driver_dummy.cpp
@@ -45,7 +45,7 @@ Error AudioDriverDummy::init() {
}
channels = get_channels();
- samples_in = memnew_arr(int32_t, (size_t)buffer_frames * channels);
+ samples_in = memnew_arr(int32_t, size_t(buffer_frames) * channels);
if (use_threads) {
thread.start(AudioDriverDummy::thread_func, this);
diff --git a/servers/audio/effects/SCsub b/servers/audio/effects/SCsub
index 86681f9c74..98f918b245 100644
--- a/servers/audio/effects/SCsub
+++ b/servers/audio/effects/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/servers/camera/SCsub b/servers/camera/SCsub
index 86681f9c74..98f918b245 100644
--- a/servers/camera/SCsub
+++ b/servers/camera/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/servers/camera/camera_feed.cpp b/servers/camera/camera_feed.cpp
index 0661ffd576..8f6a40481d 100644
--- a/servers/camera/camera_feed.cpp
+++ b/servers/camera/camera_feed.cpp
@@ -56,9 +56,16 @@ void CameraFeed::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_datatype"), &CameraFeed::get_datatype);
+ ClassDB::bind_method(D_METHOD("get_formats"), &CameraFeed::get_formats);
+ ClassDB::bind_method(D_METHOD("set_format", "index", "parameters"), &CameraFeed::set_format);
+
+ ADD_SIGNAL(MethodInfo("frame_changed"));
+ ADD_SIGNAL(MethodInfo("format_changed"));
+
ADD_GROUP("Feed", "feed_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "feed_is_active"), "set_active", "is_active");
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "feed_transform"), "set_transform", "get_transform");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "formats"), "", "get_formats");
BIND_ENUM_CONSTANT(FEED_NOIMAGE);
BIND_ENUM_CONSTANT(FEED_RGB);
@@ -84,13 +91,11 @@ void CameraFeed::set_active(bool p_is_active) {
} else if (p_is_active) {
// attempt to activate this feed
if (activate_feed()) {
- print_line("Activate " + name);
active = true;
}
} else {
// just deactivate it
deactivate_feed();
- print_line("Deactivate " + name);
active = false;
}
}
@@ -183,6 +188,8 @@ void CameraFeed::set_RGB_img(const Ref<Image> &p_rgb_img) {
RID new_texture = RenderingServer::get_singleton()->texture_2d_create(p_rgb_img);
RenderingServer::get_singleton()->texture_replace(texture[CameraServer::FEED_RGBA_IMAGE], new_texture);
+
+ emit_signal(SNAME("format_changed"));
} else {
RenderingServer::get_singleton()->texture_2d_update(texture[CameraServer::FEED_RGBA_IMAGE], p_rgb_img);
}
@@ -204,6 +211,8 @@ void CameraFeed::set_YCbCr_img(const Ref<Image> &p_ycbcr_img) {
RID new_texture = RenderingServer::get_singleton()->texture_2d_create(p_ycbcr_img);
RenderingServer::get_singleton()->texture_replace(texture[CameraServer::FEED_RGBA_IMAGE], new_texture);
+
+ emit_signal(SNAME("format_changed"));
} else {
RenderingServer::get_singleton()->texture_2d_update(texture[CameraServer::FEED_RGBA_IMAGE], p_ycbcr_img);
}
@@ -235,6 +244,8 @@ void CameraFeed::set_YCbCr_imgs(const Ref<Image> &p_y_img, const Ref<Image> &p_c
RID new_texture = RenderingServer::get_singleton()->texture_2d_create(p_cbcr_img);
RenderingServer::get_singleton()->texture_replace(texture[CameraServer::FEED_CBCR_IMAGE], new_texture);
}
+
+ emit_signal(SNAME("format_changed"));
} else {
RenderingServer::get_singleton()->texture_2d_update(texture[CameraServer::FEED_Y_IMAGE], p_y_img);
RenderingServer::get_singleton()->texture_2d_update(texture[CameraServer::FEED_CBCR_IMAGE], p_cbcr_img);
@@ -252,3 +263,16 @@ bool CameraFeed::activate_feed() {
void CameraFeed::deactivate_feed() {
// nothing to do here
}
+
+bool CameraFeed::set_format(int p_index, const Dictionary &p_parameters) {
+ return false;
+}
+
+Array CameraFeed::get_formats() const {
+ return Array();
+}
+
+CameraFeed::FeedFormat CameraFeed::get_format() const {
+ FeedFormat feed_format = {};
+ return feed_format;
+}
diff --git a/servers/camera/camera_feed.h b/servers/camera/camera_feed.h
index b85a44cfae..5d1f54be07 100644
--- a/servers/camera/camera_feed.h
+++ b/servers/camera/camera_feed.h
@@ -60,14 +60,26 @@ public:
private:
int id; // unique id for this, for internal use in case feeds are removed
- int base_width;
- int base_height;
protected:
+ struct FeedFormat {
+ int width = 0;
+ int height = 0;
+ String format;
+ int frame_numerator = 0;
+ int frame_denominator = 0;
+ uint32_t pixel_format = 0;
+ };
+
String name; // name of our camera feed
FeedDataType datatype; // type of texture data stored
FeedPosition position; // position of camera on the device
Transform2D transform; // display transform
+ int base_width = 0;
+ int base_height = 0;
+ Vector<FeedFormat> formats;
+ Dictionary parameters;
+ int selected_format = -1;
bool active; // only when active do we actually update the camera texture each frame
RID texture[CameraServer::FEED_IMAGES]; // texture images needed for this
@@ -102,6 +114,10 @@ public:
void set_YCbCr_img(const Ref<Image> &p_ycbcr_img);
void set_YCbCr_imgs(const Ref<Image> &p_y_img, const Ref<Image> &p_cbcr_img);
+ virtual bool set_format(int p_index, const Dictionary &p_parameters);
+ virtual Array get_formats() const;
+ virtual FeedFormat get_format() const;
+
virtual bool activate_feed();
virtual void deactivate_feed();
};
diff --git a/servers/debugger/SCsub b/servers/debugger/SCsub
index 86681f9c74..98f918b245 100644
--- a/servers/debugger/SCsub
+++ b/servers/debugger/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/servers/display/SCsub b/servers/display/SCsub
index 86681f9c74..98f918b245 100644
--- a/servers/display/SCsub
+++ b/servers/display/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/servers/extensions/SCsub b/servers/extensions/SCsub
index 95c7f5d319..e7bb57e9f3 100644
--- a/servers/extensions/SCsub
+++ b/servers/extensions/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/servers/movie_writer/SCsub b/servers/movie_writer/SCsub
index 86681f9c74..98f918b245 100644
--- a/servers/movie_writer/SCsub
+++ b/servers/movie_writer/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/servers/navigation/SCsub b/servers/navigation/SCsub
index 86681f9c74..98f918b245 100644
--- a/servers/navigation/SCsub
+++ b/servers/navigation/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/servers/rendering/SCsub b/servers/rendering/SCsub
index cf26ca029d..9971761818 100644
--- a/servers/rendering/SCsub
+++ b/servers/rendering/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/servers/rendering/dummy/SCsub b/servers/rendering/dummy/SCsub
index aa688af6cd..3e1f338227 100644
--- a/servers/rendering/dummy/SCsub
+++ b/servers/rendering/dummy/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/servers/rendering/dummy/storage/SCsub b/servers/rendering/dummy/storage/SCsub
index 86681f9c74..98f918b245 100644
--- a/servers/rendering/dummy/storage/SCsub
+++ b/servers/rendering/dummy/storage/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/servers/rendering/renderer_rd/SCsub b/servers/rendering/renderer_rd/SCsub
index a27439e931..c6d9e3ef36 100644
--- a/servers/rendering/renderer_rd/SCsub
+++ b/servers/rendering/renderer_rd/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/servers/rendering/renderer_rd/effects/SCsub b/servers/rendering/renderer_rd/effects/SCsub
index 8e13715447..9f330c9f0f 100644
--- a/servers/rendering/renderer_rd/effects/SCsub
+++ b/servers/rendering/renderer_rd/effects/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/servers/rendering/renderer_rd/environment/SCsub b/servers/rendering/renderer_rd/environment/SCsub
index 86681f9c74..98f918b245 100644
--- a/servers/rendering/renderer_rd/environment/SCsub
+++ b/servers/rendering/renderer_rd/environment/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/servers/rendering/renderer_rd/forward_clustered/SCsub b/servers/rendering/renderer_rd/forward_clustered/SCsub
index 86681f9c74..98f918b245 100644
--- a/servers/rendering/renderer_rd/forward_clustered/SCsub
+++ b/servers/rendering/renderer_rd/forward_clustered/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/servers/rendering/renderer_rd/forward_mobile/SCsub b/servers/rendering/renderer_rd/forward_mobile/SCsub
index 86681f9c74..98f918b245 100644
--- a/servers/rendering/renderer_rd/forward_mobile/SCsub
+++ b/servers/rendering/renderer_rd/forward_mobile/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
index 7d6d5018d0..0c21fec282 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
@@ -1131,6 +1131,7 @@ void RendererSceneRenderRD::render_scene(const Ref<RenderSceneBuffers> &p_render
scene_data.cam_orthogonal = p_camera_data->is_orthogonal;
scene_data.camera_visible_layers = p_camera_data->visible_layers;
scene_data.taa_jitter = p_camera_data->taa_jitter;
+ scene_data.taa_frame_count = p_camera_data->taa_frame_count;
scene_data.main_cam_transform = p_camera_data->main_transform;
scene_data.flip_y = !p_reflection_probe.is_valid();
diff --git a/servers/rendering/renderer_rd/shaders/SCsub b/servers/rendering/renderer_rd/shaders/SCsub
index 5405985741..e102b839b5 100644
--- a/servers/rendering/renderer_rd/shaders/SCsub
+++ b/servers/rendering/renderer_rd/shaders/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/servers/rendering/renderer_rd/shaders/effects/SCsub b/servers/rendering/renderer_rd/shaders/effects/SCsub
index 810f781340..e5517e52eb 100644
--- a/servers/rendering/renderer_rd/shaders/effects/SCsub
+++ b/servers/rendering/renderer_rd/shaders/effects/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/servers/rendering/renderer_rd/shaders/effects/fsr2/SCsub b/servers/rendering/renderer_rd/shaders/effects/fsr2/SCsub
index 5b8bbc343b..53f3ee3977 100644
--- a/servers/rendering/renderer_rd/shaders/effects/fsr2/SCsub
+++ b/servers/rendering/renderer_rd/shaders/effects/fsr2/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/servers/rendering/renderer_rd/shaders/effects/ssao.glsl b/servers/rendering/renderer_rd/shaders/effects/ssao.glsl
index 8c50bb544d..41a262b2e8 100644
--- a/servers/rendering/renderer_rd/shaders/effects/ssao.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/ssao.glsl
@@ -50,11 +50,14 @@ const int num_taps[5] = { 3, 5, 12, 0, 0 };
//
#define SSAO_DETAIL_AO_ENABLE_AT_QUALITY_PRESET (1) // whether to use detail; to disable simply set to 99 or similar
//
-#define SSAO_DEPTH_MIPS_ENABLE_AT_QUALITY_PRESET (2) // !!warning!! the MIP generation on the C++ side will be enabled on quality preset 2 regardless of this value, so if changing here, change the C++ side too
+// WARNING: The MIP generation on the C++ side will be enabled on quality preset 2 regardless of
+// this value, so if changing here, change the C++ side too.
+#define SSAO_DEPTH_MIPS_ENABLE_AT_QUALITY_PRESET (2)
#define SSAO_DEPTH_MIPS_GLOBAL_OFFSET (-4.3) // best noise/quality/performance tradeoff, found empirically
//
-// !!warning!! the edge handling is hard-coded to 'disabled' on quality level 0, and enabled above, on the C++ side; while toggling it here will work for
-// testing purposes, it will not yield performance gains (or correct results)
+// WARNING: The edge handling is hard-coded to 'disabled' on quality level 0, and enabled above,
+// on the C++ side; while toggling it here will work for testing purposes, it will not yield
+// performance gains (or correct results).
#define SSAO_DEPTH_BASED_EDGES_ENABLE_AT_QUALITY_PRESET (1)
//
#define SSAO_REDUCE_RADIUS_NEAR_SCREEN_BORDER_ENABLE_AT_QUALITY_PRESET (1)
diff --git a/servers/rendering/renderer_rd/shaders/effects/ssil.glsl b/servers/rendering/renderer_rd/shaders/effects/ssil.glsl
index 0b623c1d4e..6dbd6c82cb 100644
--- a/servers/rendering/renderer_rd/shaders/effects/ssil.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/ssil.glsl
@@ -49,8 +49,9 @@ const int num_taps[5] = { 3, 5, 12, 0, 0 };
#define SSIL_DEPTH_MIPS_ENABLE_AT_QUALITY_PRESET (2)
#define SSIL_DEPTH_MIPS_GLOBAL_OFFSET (-4.3) // best noise/quality/performance tradeoff, found empirically
//
-// !!warning!! the edge handling is hard-coded to 'disabled' on quality level 0, and enabled above, on the C++ side; while toggling it here will work for
-// testing purposes, it will not yield performance gains (or correct results)
+// WARNING: The edge handling is hard-coded to 'disabled' on quality level 0, and enabled above,
+// on the C++ side; while toggling it here will work for testing purposes, it will not yield
+// performance gains (or correct results).
#define SSIL_DEPTH_BASED_EDGES_ENABLE_AT_QUALITY_PRESET (1)
//
#define SSIL_REDUCE_RADIUS_NEAR_SCREEN_BORDER_ENABLE_AT_QUALITY_PRESET (1)
diff --git a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl
index 841f02f673..fa3b45a962 100644
--- a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl
@@ -256,8 +256,12 @@ vec3 tonemap_aces(vec3 color, float white) {
return color_tonemapped / white_tonemapped;
}
+// Based on Reinhard's extended formula, see equation 4 in https://doi.org/cjbgrt
vec3 tonemap_reinhard(vec3 color, float white) {
- return (white * color + color) / (color * white + white);
+ float white_squared = white * white;
+ vec3 white_squared_color = white_squared * color;
+ // Equivalent to color * (1 + color / white_squared) / (1 + color)
+ return (white_squared_color + color * color) / (white_squared_color + white_squared);
}
vec3 linear_to_srgb(vec3 color) {
@@ -272,7 +276,7 @@ vec3 linear_to_srgb(vec3 color) {
#define TONEMAPPER_FILMIC 2
#define TONEMAPPER_ACES 3
-vec3 apply_tonemapping(vec3 color, float white) { // inputs are LINEAR, always outputs clamped [0;1] color
+vec3 apply_tonemapping(vec3 color, float white) { // inputs are LINEAR
// Ensure color values passed to tonemappers are positive.
// They can be negative in the case of negative lights, which leads to undesired behavior.
if (params.tonemapper == TONEMAPPER_LINEAR) {
diff --git a/servers/rendering/renderer_rd/shaders/environment/SCsub b/servers/rendering/renderer_rd/shaders/environment/SCsub
index f06a2d86e2..2c3e7d39ef 100644
--- a/servers/rendering/renderer_rd/shaders/environment/SCsub
+++ b/servers/rendering/renderer_rd/shaders/environment/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/SCsub b/servers/rendering/renderer_rd/shaders/forward_clustered/SCsub
index f06a2d86e2..2c3e7d39ef 100644
--- a/servers/rendering/renderer_rd/shaders/forward_clustered/SCsub
+++ b/servers/rendering/renderer_rd/shaders/forward_clustered/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl
index a441b1667d..400451ec36 100644
--- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl
+++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl
@@ -1886,7 +1886,7 @@ void fragment_shader(in SceneData scene_data) {
float range_begin = directional_lights.data[i].shadow_range_begin.x;
float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle;
vec2 tex_scale = directional_lights.data[i].uv_scale1 * test_radius;
- shadow = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale);
+ shadow = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count);
blend_count++;
}
@@ -1902,7 +1902,7 @@ void fragment_shader(in SceneData scene_data) {
float range_begin = directional_lights.data[i].shadow_range_begin.y;
float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle;
vec2 tex_scale = directional_lights.data[i].uv_scale2 * test_radius;
- float s = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale);
+ float s = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count);
if (blend_count == 0) {
shadow = s;
@@ -1927,7 +1927,7 @@ void fragment_shader(in SceneData scene_data) {
float range_begin = directional_lights.data[i].shadow_range_begin.z;
float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle;
vec2 tex_scale = directional_lights.data[i].uv_scale3 * test_radius;
- float s = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale);
+ float s = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count);
if (blend_count == 0) {
shadow = s;
@@ -1952,7 +1952,7 @@ void fragment_shader(in SceneData scene_data) {
float range_begin = directional_lights.data[i].shadow_range_begin.w;
float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle;
vec2 tex_scale = directional_lights.data[i].uv_scale4 * test_radius;
- float s = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale);
+ float s = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count);
if (blend_count == 0) {
shadow = s;
@@ -2003,7 +2003,7 @@ void fragment_shader(in SceneData scene_data) {
pssm_coord /= pssm_coord.w;
- shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * (blur_factor + (1.0 - blur_factor) * float(directional_lights.data[i].blend_splits)), pssm_coord);
+ shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * (blur_factor + (1.0 - blur_factor) * float(directional_lights.data[i].blend_splits)), pssm_coord, scene_data.taa_frame_count);
if (directional_lights.data[i].blend_splits) {
float pssm_blend;
@@ -2037,7 +2037,7 @@ void fragment_shader(in SceneData scene_data) {
pssm_coord /= pssm_coord.w;
- float shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * (blur_factor2 + (1.0 - blur_factor2) * float(directional_lights.data[i].blend_splits)), pssm_coord);
+ float shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * (blur_factor2 + (1.0 - blur_factor2) * float(directional_lights.data[i].blend_splits)), pssm_coord, scene_data.taa_frame_count);
shadow = mix(shadow, shadow2, pssm_blend);
}
}
@@ -2220,7 +2220,7 @@ void fragment_shader(in SceneData scene_data) {
continue; // Statically baked light and object uses lightmap, skip
}
- float shadow = light_process_omni_shadow(light_index, vertex, normal);
+ float shadow = light_process_omni_shadow(light_index, vertex, normal, scene_data.taa_frame_count);
shadow = blur_shadow(shadow);
@@ -2292,7 +2292,7 @@ void fragment_shader(in SceneData scene_data) {
continue; // Statically baked light and object uses lightmap, skip
}
- float shadow = light_process_spot_shadow(light_index, vertex, normal);
+ float shadow = light_process_spot_shadow(light_index, vertex, normal, scene_data.taa_frame_count);
shadow = blur_shadow(shadow);
diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/SCsub b/servers/rendering/renderer_rd/shaders/forward_mobile/SCsub
index f06a2d86e2..2c3e7d39ef 100644
--- a/servers/rendering/renderer_rd/shaders/forward_mobile/SCsub
+++ b/servers/rendering/renderer_rd/shaders/forward_mobile/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl
index 92a8e2be32..17c7b756c3 100644
--- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl
+++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl
@@ -1450,9 +1450,9 @@ void main() {
float range_begin = directional_lights.data[i].shadow_range_begin.x;
float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle;
vec2 tex_scale = directional_lights.data[i].uv_scale1 * test_radius;
- shadow = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale);
+ shadow = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count);
} else {
- shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale, pssm_coord);
+ shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale, pssm_coord, scene_data.taa_frame_count);
}
} else if (depth_z < directional_lights.data[i].shadow_split_offsets.y) {
vec4 v = vec4(vertex, 1.0);
@@ -1467,9 +1467,9 @@ void main() {
float range_begin = directional_lights.data[i].shadow_range_begin.y;
float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle;
vec2 tex_scale = directional_lights.data[i].uv_scale2 * test_radius;
- shadow = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale);
+ shadow = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count);
} else {
- shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale, pssm_coord);
+ shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale, pssm_coord, scene_data.taa_frame_count);
}
} else if (depth_z < directional_lights.data[i].shadow_split_offsets.z) {
vec4 v = vec4(vertex, 1.0);
@@ -1484,9 +1484,9 @@ void main() {
float range_begin = directional_lights.data[i].shadow_range_begin.z;
float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle;
vec2 tex_scale = directional_lights.data[i].uv_scale3 * test_radius;
- shadow = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale);
+ shadow = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count);
} else {
- shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale, pssm_coord);
+ shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale, pssm_coord, scene_data.taa_frame_count);
}
} else {
vec4 v = vec4(vertex, 1.0);
@@ -1501,9 +1501,9 @@ void main() {
float range_begin = directional_lights.data[i].shadow_range_begin.w;
float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle;
vec2 tex_scale = directional_lights.data[i].uv_scale4 * test_radius;
- shadow = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale);
+ shadow = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count);
} else {
- shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale, pssm_coord);
+ shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale, pssm_coord, scene_data.taa_frame_count);
}
}
@@ -1522,9 +1522,9 @@ void main() {
float range_begin = directional_lights.data[i].shadow_range_begin.y;
float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle;
vec2 tex_scale = directional_lights.data[i].uv_scale2 * test_radius;
- shadow2 = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale);
+ shadow2 = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count);
} else {
- shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale, pssm_coord);
+ shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale, pssm_coord, scene_data.taa_frame_count);
}
pssm_blend = smoothstep(0.0, directional_lights.data[i].shadow_split_offsets.x, depth_z);
@@ -1539,9 +1539,9 @@ void main() {
float range_begin = directional_lights.data[i].shadow_range_begin.z;
float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle;
vec2 tex_scale = directional_lights.data[i].uv_scale3 * test_radius;
- shadow2 = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale);
+ shadow2 = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count);
} else {
- shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale, pssm_coord);
+ shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale, pssm_coord, scene_data.taa_frame_count);
}
pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.x, directional_lights.data[i].shadow_split_offsets.y, depth_z);
@@ -1555,9 +1555,9 @@ void main() {
float range_begin = directional_lights.data[i].shadow_range_begin.w;
float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle;
vec2 tex_scale = directional_lights.data[i].uv_scale4 * test_radius;
- shadow2 = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale);
+ shadow2 = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count);
} else {
- shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale, pssm_coord);
+ shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale, pssm_coord, scene_data.taa_frame_count);
}
pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.y, directional_lights.data[i].shadow_split_offsets.z, depth_z);
@@ -1627,7 +1627,7 @@ void main() {
pssm_coord /= pssm_coord.w;
- shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * (blur_factor + (1.0 - blur_factor) * float(directional_lights.data[i].blend_splits)), pssm_coord);
+ shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * (blur_factor + (1.0 - blur_factor) * float(directional_lights.data[i].blend_splits)), pssm_coord, scene_data.taa_frame_count);
if (directional_lights.data[i].blend_splits) {
float pssm_blend;
@@ -1661,7 +1661,7 @@ void main() {
pssm_coord /= pssm_coord.w;
- float shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * (blur_factor2 + (1.0 - blur_factor2) * float(directional_lights.data[i].blend_splits)), pssm_coord);
+ float shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * (blur_factor2 + (1.0 - blur_factor2) * float(directional_lights.data[i].blend_splits)), pssm_coord, scene_data.taa_frame_count);
shadow = mix(shadow, shadow2, pssm_blend);
}
@@ -1767,7 +1767,7 @@ void main() {
break;
}
- float shadow = light_process_omni_shadow(light_index, vertex, normal);
+ float shadow = light_process_omni_shadow(light_index, vertex, normal, scene_data.taa_frame_count);
shadow = blur_shadow(shadow);
@@ -1812,7 +1812,7 @@ void main() {
break;
}
- float shadow = light_process_spot_shadow(light_index, vertex, normal);
+ float shadow = light_process_spot_shadow(light_index, vertex, normal, scene_data.taa_frame_count);
shadow = blur_shadow(shadow);
diff --git a/servers/rendering/renderer_rd/shaders/scene_data_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_data_inc.glsl
index 67542d61fd..a2fef216f5 100644
--- a/servers/rendering/renderer_rd/shaders/scene_data_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_data_inc.glsl
@@ -52,11 +52,11 @@ struct SceneData {
uint fog_mode;
highp float fog_density;
highp float fog_height;
- highp float fog_height_density;
+ highp float fog_height_density;
highp float fog_depth_curve;
- highp float pad;
highp float fog_depth_begin;
+ highp float taa_frame_count;
mediump vec3 fog_light_color;
highp float fog_depth_end;
diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
index 748fb59531..14a4dc7089 100644
--- a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
@@ -264,7 +264,7 @@ float quick_hash(vec2 pos) {
return fract(magic.z * fract(dot(pos, magic.xy)));
}
-float sample_directional_pcf_shadow(texture2D shadow, vec2 shadow_pixel_size, vec4 coord) {
+float sample_directional_pcf_shadow(texture2D shadow, vec2 shadow_pixel_size, vec4 coord, float taa_frame_count) {
vec2 pos = coord.xy;
float depth = coord.z;
@@ -275,7 +275,7 @@ float sample_directional_pcf_shadow(texture2D shadow, vec2 shadow_pixel_size, ve
mat2 disk_rotation;
{
- float r = quick_hash(gl_FragCoord.xy) * 2.0 * M_PI;
+ float r = quick_hash(gl_FragCoord.xy + vec2(taa_frame_count * 5.588238)) * 2.0 * M_PI;
float sr = sin(r);
float cr = cos(r);
disk_rotation = mat2(vec2(cr, -sr), vec2(sr, cr));
@@ -290,7 +290,7 @@ float sample_directional_pcf_shadow(texture2D shadow, vec2 shadow_pixel_size, ve
return avg * (1.0 / float(sc_directional_soft_shadow_samples));
}
-float sample_pcf_shadow(texture2D shadow, vec2 shadow_pixel_size, vec3 coord) {
+float sample_pcf_shadow(texture2D shadow, vec2 shadow_pixel_size, vec3 coord, float taa_frame_count) {
vec2 pos = coord.xy;
float depth = coord.z;
@@ -301,7 +301,7 @@ float sample_pcf_shadow(texture2D shadow, vec2 shadow_pixel_size, vec3 coord) {
mat2 disk_rotation;
{
- float r = quick_hash(gl_FragCoord.xy) * 2.0 * M_PI;
+ float r = quick_hash(gl_FragCoord.xy + vec2(taa_frame_count * 5.588238)) * 2.0 * M_PI;
float sr = sin(r);
float cr = cos(r);
disk_rotation = mat2(vec2(cr, -sr), vec2(sr, cr));
@@ -316,7 +316,7 @@ float sample_pcf_shadow(texture2D shadow, vec2 shadow_pixel_size, vec3 coord) {
return avg * (1.0 / float(sc_soft_shadow_samples));
}
-float sample_omni_pcf_shadow(texture2D shadow, float blur_scale, vec2 coord, vec4 uv_rect, vec2 flip_offset, float depth) {
+float sample_omni_pcf_shadow(texture2D shadow, float blur_scale, vec2 coord, vec4 uv_rect, vec2 flip_offset, float depth, float taa_frame_count) {
//if only one sample is taken, take it from the center
if (sc_soft_shadow_samples == 0) {
vec2 pos = coord * 0.5 + 0.5;
@@ -326,7 +326,7 @@ float sample_omni_pcf_shadow(texture2D shadow, float blur_scale, vec2 coord, vec
mat2 disk_rotation;
{
- float r = quick_hash(gl_FragCoord.xy) * 2.0 * M_PI;
+ float r = quick_hash(gl_FragCoord.xy + vec2(taa_frame_count * 5.588238)) * 2.0 * M_PI;
float sr = sin(r);
float cr = cos(r);
disk_rotation = mat2(vec2(cr, -sr), vec2(sr, cr));
@@ -359,14 +359,14 @@ float sample_omni_pcf_shadow(texture2D shadow, float blur_scale, vec2 coord, vec
return avg * (1.0 / float(sc_soft_shadow_samples));
}
-float sample_directional_soft_shadow(texture2D shadow, vec3 pssm_coord, vec2 tex_scale) {
+float sample_directional_soft_shadow(texture2D shadow, vec3 pssm_coord, vec2 tex_scale, float taa_frame_count) {
//find blocker
float blocker_count = 0.0;
float blocker_average = 0.0;
mat2 disk_rotation;
{
- float r = quick_hash(gl_FragCoord.xy) * 2.0 * M_PI;
+ float r = quick_hash(gl_FragCoord.xy + vec2(taa_frame_count * 5.588238)) * 2.0 * M_PI;
float sr = sin(r);
float cr = cos(r);
disk_rotation = mat2(vec2(cr, -sr), vec2(sr, cr));
@@ -412,7 +412,7 @@ float get_omni_attenuation(float distance, float inv_range, float decay) {
return nd * pow(max(distance, 0.0001), -decay);
}
-float light_process_omni_shadow(uint idx, vec3 vertex, vec3 normal) {
+float light_process_omni_shadow(uint idx, vec3 vertex, vec3 normal, float taa_frame_count) {
#ifndef SHADOWS_DISABLED
if (omni_lights.data[idx].shadow_opacity > 0.001) {
// there is a shadowmap
@@ -444,7 +444,7 @@ float light_process_omni_shadow(uint idx, vec3 vertex, vec3 normal) {
mat2 disk_rotation;
{
- float r = quick_hash(gl_FragCoord.xy) * 2.0 * M_PI;
+ float r = quick_hash(gl_FragCoord.xy + vec2(taa_frame_count * 5.588238)) * 2.0 * M_PI;
float sr = sin(r);
float cr = cos(r);
disk_rotation = mat2(vec2(cr, -sr), vec2(sr, cr));
@@ -537,7 +537,7 @@ float light_process_omni_shadow(uint idx, vec3 vertex, vec3 normal) {
float depth = shadow_len - omni_lights.data[idx].shadow_bias;
depth *= omni_lights.data[idx].inv_radius;
depth = 1.0 - depth;
- shadow = mix(1.0, sample_omni_pcf_shadow(shadow_atlas, omni_lights.data[idx].soft_shadow_scale / shadow_sample.z, pos, uv_rect, flip_offset, depth), omni_lights.data[idx].shadow_opacity);
+ shadow = mix(1.0, sample_omni_pcf_shadow(shadow_atlas, omni_lights.data[idx].soft_shadow_scale / shadow_sample.z, pos, uv_rect, flip_offset, depth, taa_frame_count), omni_lights.data[idx].shadow_opacity);
}
return shadow;
@@ -698,7 +698,7 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 v
specular_light);
}
-float light_process_spot_shadow(uint idx, vec3 vertex, vec3 normal) {
+float light_process_spot_shadow(uint idx, vec3 vertex, vec3 normal, float taa_frame_count) {
#ifndef SHADOWS_DISABLED
if (spot_lights.data[idx].shadow_opacity > 0.001) {
vec3 light_rel_vec = spot_lights.data[idx].position - vertex;
@@ -729,7 +729,7 @@ float light_process_spot_shadow(uint idx, vec3 vertex, vec3 normal) {
mat2 disk_rotation;
{
- float r = quick_hash(gl_FragCoord.xy) * 2.0 * M_PI;
+ float r = quick_hash(gl_FragCoord.xy + vec2(taa_frame_count * 5.588238)) * 2.0 * M_PI;
float sr = sin(r);
float cr = cos(r);
disk_rotation = mat2(vec2(cr, -sr), vec2(sr, cr));
@@ -770,7 +770,7 @@ float light_process_spot_shadow(uint idx, vec3 vertex, vec3 normal) {
} else {
//hard shadow
vec3 shadow_uv = vec3(splane.xy * spot_lights.data[idx].atlas_rect.zw + spot_lights.data[idx].atlas_rect.xy, splane.z);
- shadow = mix(1.0, sample_pcf_shadow(shadow_atlas, spot_lights.data[idx].soft_shadow_scale * scene_data_block.data.shadow_atlas_pixel_size, shadow_uv), spot_lights.data[idx].shadow_opacity);
+ shadow = mix(1.0, sample_pcf_shadow(shadow_atlas, spot_lights.data[idx].soft_shadow_scale * scene_data_block.data.shadow_atlas_pixel_size, shadow_uv, taa_frame_count), spot_lights.data[idx].shadow_opacity);
}
return shadow;
diff --git a/servers/rendering/renderer_rd/spirv-reflect/SCsub b/servers/rendering/renderer_rd/spirv-reflect/SCsub
index 4c27e5bef7..8d3d8560a5 100644
--- a/servers/rendering/renderer_rd/spirv-reflect/SCsub
+++ b/servers/rendering/renderer_rd/spirv-reflect/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/servers/rendering/renderer_rd/storage_rd/SCsub b/servers/rendering/renderer_rd/storage_rd/SCsub
index 86681f9c74..98f918b245 100644
--- a/servers/rendering/renderer_rd/storage_rd/SCsub
+++ b/servers/rendering/renderer_rd/storage_rd/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp
index 148a556b46..660836facf 100644
--- a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp
@@ -112,6 +112,7 @@ void RenderSceneDataRD::update_ubo(RID p_uniform_buffer, RS::ViewportDebugDraw p
ubo.taa_jitter[0] = taa_jitter.x;
ubo.taa_jitter[1] = taa_jitter.y;
+ ubo.taa_frame_count = taa_frame_count;
ubo.z_far = z_far;
ubo.z_near = z_near;
diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h
index b2c93acd44..4a70482d72 100644
--- a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h
+++ b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h
@@ -48,6 +48,7 @@ public:
Transform3D cam_transform;
Projection cam_projection;
Vector2 taa_jitter;
+ float taa_frame_count = 0.0f;
uint32_t camera_visible_layers;
bool cam_orthogonal = false;
bool flip_y = false;
@@ -148,8 +149,8 @@ private:
float fog_height_density;
float fog_depth_curve;
- float pad;
float fog_depth_begin;
+ float taa_frame_count; // Used to add break up samples over multiple frames. Value is an integer from 0 to taa_phase_count -1.
float fog_light_color[3];
float fog_depth_end;
diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp
index 286944641c..43abb22e3d 100644
--- a/servers/rendering/renderer_scene_cull.cpp
+++ b/servers/rendering/renderer_scene_cull.cpp
@@ -2700,6 +2700,7 @@ void RendererSceneCull::render_camera(const Ref<RenderSceneBuffers> &p_render_bu
ERR_FAIL_NULL(camera);
Vector2 jitter;
+ float taa_frame_count = 0.0f;
if (p_jitter_phase_count > 0) {
uint32_t current_jitter_count = camera_jitter_array.size();
if (p_jitter_phase_count != current_jitter_count) {
@@ -2713,6 +2714,7 @@ void RendererSceneCull::render_camera(const Ref<RenderSceneBuffers> &p_render_bu
}
jitter = camera_jitter_array[RSG::rasterizer->get_frame_number() % p_jitter_phase_count] / p_viewport_size;
+ taa_frame_count = float(RSG::rasterizer->get_frame_number() % p_jitter_phase_count);
}
RendererSceneRender::CameraData camera_data;
@@ -2755,7 +2757,7 @@ void RendererSceneCull::render_camera(const Ref<RenderSceneBuffers> &p_render_bu
} break;
}
- camera_data.set_camera(transform, projection, is_orthogonal, vaspect, jitter, camera->visible_layers);
+ camera_data.set_camera(transform, projection, is_orthogonal, vaspect, jitter, taa_frame_count, camera->visible_layers);
} else {
// Setup our camera for our XR interface.
// We can support multiple views here each with their own camera
@@ -2777,7 +2779,7 @@ void RendererSceneCull::render_camera(const Ref<RenderSceneBuffers> &p_render_bu
}
if (view_count == 1) {
- camera_data.set_camera(transforms[0], projections[0], false, camera->vaspect, jitter, camera->visible_layers);
+ camera_data.set_camera(transforms[0], projections[0], false, camera->vaspect, jitter, p_jitter_phase_count, camera->visible_layers);
} else if (view_count == 2) {
camera_data.set_multiview_camera(view_count, transforms, projections, false, camera->vaspect);
} else {
diff --git a/servers/rendering/renderer_scene_render.cpp b/servers/rendering/renderer_scene_render.cpp
index 76c779900f..797ba7eaf7 100644
--- a/servers/rendering/renderer_scene_render.cpp
+++ b/servers/rendering/renderer_scene_render.cpp
@@ -33,7 +33,7 @@
/////////////////////////////////////////////////////////////////////////////
// CameraData
-void RendererSceneRender::CameraData::set_camera(const Transform3D p_transform, const Projection p_projection, bool p_is_orthogonal, bool p_vaspect, const Vector2 &p_taa_jitter, const uint32_t p_visible_layers) {
+void RendererSceneRender::CameraData::set_camera(const Transform3D p_transform, const Projection p_projection, bool p_is_orthogonal, bool p_vaspect, const Vector2 &p_taa_jitter, float p_taa_frame_count, const uint32_t p_visible_layers) {
view_count = 1;
is_orthogonal = p_is_orthogonal;
vaspect = p_vaspect;
@@ -45,6 +45,7 @@ void RendererSceneRender::CameraData::set_camera(const Transform3D p_transform,
view_offset[0] = Transform3D();
view_projection[0] = p_projection;
taa_jitter = p_taa_jitter;
+ taa_frame_count = p_taa_frame_count;
}
void RendererSceneRender::CameraData::set_multiview_camera(uint32_t p_view_count, const Transform3D *p_transforms, const Projection *p_projections, bool p_is_orthogonal, bool p_vaspect) {
diff --git a/servers/rendering/renderer_scene_render.h b/servers/rendering/renderer_scene_render.h
index 3446f5dd1b..72ccbcdf11 100644
--- a/servers/rendering/renderer_scene_render.h
+++ b/servers/rendering/renderer_scene_render.h
@@ -307,8 +307,9 @@ public:
Transform3D view_offset[RendererSceneRender::MAX_RENDER_VIEWS];
Projection view_projection[RendererSceneRender::MAX_RENDER_VIEWS];
Vector2 taa_jitter;
+ float taa_frame_count = 0.0f;
- void set_camera(const Transform3D p_transform, const Projection p_projection, bool p_is_orthogonal, bool p_vaspect, const Vector2 &p_taa_jitter = Vector2(), uint32_t p_visible_layers = 0xFFFFFFFF);
+ void set_camera(const Transform3D p_transform, const Projection p_projection, bool p_is_orthogonal, bool p_vaspect, const Vector2 &p_taa_jitter = Vector2(), float p_taa_frame_count = 0.0f, uint32_t p_visible_layers = 0xFFFFFFFF);
void set_multiview_camera(uint32_t p_view_count, const Transform3D *p_transforms, const Projection *p_projections, bool p_is_orthogonal, bool p_vaspect);
};
diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp
index 781d29ffaa..4d6435f48a 100644
--- a/servers/rendering/renderer_viewport.cpp
+++ b/servers/rendering/renderer_viewport.cpp
@@ -41,14 +41,31 @@
static Transform2D _canvas_get_transform(RendererViewport::Viewport *p_viewport, RendererCanvasCull::Canvas *p_canvas, RendererViewport::Viewport::CanvasData *p_canvas_data, const Vector2 &p_vp_size) {
Transform2D xf = p_viewport->global_transform;
+ Vector2 pixel_snap_offset;
+ if (p_viewport->snap_2d_transforms_to_pixel) {
+ // We use `floor(p + 0.5)` to snap canvas items, but `ceil(p - 0.5)`
+ // to snap viewport transform because the viewport transform is inverse
+ // to the camera transform. Also, if the viewport size is not divisible
+ // by 2, the center point is offset by 0.5 px and we need to add 0.5
+ // before rounding to cancel it out.
+ pixel_snap_offset.x = (p_viewport->size.width % 2) ? 0.0 : -0.5;
+ pixel_snap_offset.y = (p_viewport->size.height % 2) ? 0.0 : -0.5;
+ }
+
float scale = 1.0;
if (p_viewport->canvas_map.has(p_canvas->parent)) {
Transform2D c_xform = p_viewport->canvas_map[p_canvas->parent].transform;
+ if (p_viewport->snap_2d_transforms_to_pixel) {
+ c_xform.columns[2] = (c_xform.columns[2] * p_canvas->parent_scale + pixel_snap_offset).ceil() / p_canvas->parent_scale;
+ }
xf = xf * c_xform;
scale = p_canvas->parent_scale;
}
Transform2D c_xform = p_canvas_data->transform;
+ if (p_viewport->snap_2d_transforms_to_pixel) {
+ c_xform.columns[2] = (c_xform.columns[2] + pixel_snap_offset).ceil();
+ }
xf = xf * c_xform;
if (scale != 1.0 && !RSG::canvas->disable_scale) {
diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h
index 1405f585b2..d8bf845756 100644
--- a/servers/rendering/rendering_device.h
+++ b/servers/rendering/rendering_device.h
@@ -800,7 +800,7 @@ private:
#ifndef DISABLE_DEPRECATED
public:
- enum BarrierMask{
+ enum BarrierMask {
BARRIER_MASK_VERTEX = 1,
BARRIER_MASK_FRAGMENT = 8,
BARRIER_MASK_COMPUTE = 2,
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index 14bd3841df..879a83f519 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -8476,6 +8476,11 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
return ERR_PARSE_ERROR;
}
} else {
+ if (b->parent_function->return_type == TYPE_VOID) {
+ _set_error(vformat(RTR("'%s' function cannot return a value."), "void"));
+ return ERR_PARSE_ERROR;
+ }
+
_set_tkpos(pos); //rollback, wants expression
#ifdef DEBUG_ENABLED
diff --git a/servers/rendering/storage/SCsub b/servers/rendering/storage/SCsub
index 86681f9c74..98f918b245 100644
--- a/servers/rendering/storage/SCsub
+++ b/servers/rendering/storage/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index a130ae0ba2..878b02eaf1 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -1740,7 +1740,7 @@ public:
#ifndef DISABLE_DEPRECATED
// Never actually used, should be removed when we can break compatibility.
- enum Features{
+ enum Features {
FEATURE_SHADERS,
FEATURE_MULTITHREADED,
};
diff --git a/servers/text/SCsub b/servers/text/SCsub
index 86681f9c74..98f918b245 100644
--- a/servers/text/SCsub
+++ b/servers/text/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/servers/xr/SCsub b/servers/xr/SCsub
index 86681f9c74..98f918b245 100644
--- a/servers/xr/SCsub
+++ b/servers/xr/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/tests/SCsub b/tests/SCsub
index d96a1142e4..169c7c1efa 100644
--- a/tests/SCsub
+++ b/tests/SCsub
@@ -1,4 +1,5 @@
-#!/usr/bin/python
+#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")