summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/static_checks.yml2
-rw-r--r--core/config/project_settings.cpp2
-rw-r--r--core/core_bind.cpp21
-rw-r--r--core/core_bind.h1
-rw-r--r--core/input/input.cpp2
-rw-r--r--core/io/file_access.cpp7
-rw-r--r--core/io/file_access.h1
-rw-r--r--core/io/marshalls.cpp212
-rw-r--r--core/math/a_star.compat.inc59
-rw-r--r--core/math/a_star.cpp65
-rw-r--r--core/math/a_star.h25
-rw-r--r--core/math/a_star_grid_2d.compat.inc48
-rw-r--r--core/math/a_star_grid_2d.cpp34
-rw-r--r--core/math/a_star_grid_2d.h15
-rw-r--r--core/math/projection.cpp7
-rw-r--r--core/math/projection.h2
-rw-r--r--core/os/os.h1
-rw-r--r--core/string/translation.cpp4
-rw-r--r--core/string/translation.h2
-rw-r--r--doc/classes/AStar2D.xml4
-rw-r--r--doc/classes/AStar3D.xml4
-rw-r--r--doc/classes/AStarGrid2D.xml4
-rw-r--r--doc/classes/DisplayServer.xml16
-rw-r--r--doc/classes/HeightMapShape3D.xml24
-rw-r--r--doc/classes/Node.xml3
-rw-r--r--doc/classes/OS.xml25
-rw-r--r--doc/classes/PhysicsServer3D.xml31
-rw-r--r--doc/classes/ProjectSettings.xml3
-rw-r--r--doc/classes/TileData.xml11
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp43
-rw-r--r--drivers/gles3/shaders/cube_to_dp.glsl4
-rw-r--r--drivers/gles3/shaders/scene.glsl4
-rw-r--r--drivers/gles3/shaders/sky.glsl6
-rw-r--r--drivers/gles3/storage/light_storage.cpp8
-rw-r--r--drivers/gles3/storage/texture_storage.cpp2
-rw-r--r--drivers/unix/file_access_unix_pipe.cpp185
-rw-r--r--drivers/unix/file_access_unix_pipe.h96
-rw-r--r--drivers/unix/os_unix.cpp102
-rw-r--r--drivers/unix/os_unix.h1
-rw-r--r--drivers/windows/file_access_windows_pipe.cpp159
-rw-r--r--drivers/windows/file_access_windows_pipe.h95
-rw-r--r--editor/animation_track_editor.cpp9
-rw-r--r--editor/connections_dialog.cpp4
-rw-r--r--editor/debugger/editor_debugger_tree.cpp1
-rw-r--r--editor/dependency_editor.cpp2
-rw-r--r--editor/editor_data.cpp9
-rw-r--r--editor/editor_feature_profile.cpp4
-rw-r--r--editor/editor_inspector.cpp10
-rw-r--r--editor/editor_node.cpp16
-rw-r--r--editor/editor_properties.cpp11
-rw-r--r--editor/editor_property_name_processor.cpp43
-rw-r--r--editor/editor_property_name_processor.h8
-rw-r--r--editor/editor_sectioned_inspector.cpp6
-rw-r--r--editor/editor_settings_dialog.cpp4
-rw-r--r--editor/group_settings_editor.cpp2
-rw-r--r--editor/gui/scene_tree_editor.cpp27
-rw-r--r--editor/gui/scene_tree_editor.h6
-rw-r--r--editor/inspector_dock.cpp1
-rw-r--r--editor/plugins/animation_tree_editor_plugin.cpp11
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp25
-rw-r--r--editor/plugins/asset_library_editor_plugin.h1
-rw-r--r--editor/plugins/cpu_particles_3d_editor_plugin.cpp1
-rw-r--r--editor/plugins/multimesh_editor_plugin.cpp6
-rw-r--r--editor/plugins/script_text_editor.cpp13
-rw-r--r--editor/plugins/tiles/tile_data_editors.cpp2
-rw-r--r--editor/reparent_dialog.cpp7
-rw-r--r--editor/scene_tree_dock.cpp3
-rw-r--r--misc/extension_api_validation/4.2-stable.expected20
-rw-r--r--modules/mono/csharp_script.cpp34
-rw-r--r--modules/mono/csharp_script.h2
-rw-r--r--modules/mono/glue/runtime_interop.cpp4
-rw-r--r--modules/multiplayer/editor/replication_editor.cpp2
-rw-r--r--modules/raycast/raycast_occlusion_cull.cpp63
-rw-r--r--modules/raycast/raycast_occlusion_cull.h2
-rw-r--r--platform/android/display_server_android.cpp2
-rw-r--r--platform/ios/display_server_ios.mm2
-rw-r--r--platform/linuxbsd/wayland/display_server_wayland.cpp4
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp6
-rw-r--r--platform/macos/display_server_macos.h2
-rw-r--r--platform/macos/display_server_macos.mm6
-rw-r--r--platform/macos/native_menu_macos.mm5
-rw-r--r--platform/web/display_server_web.cpp2
-rw-r--r--platform/web/os_web.cpp4
-rw-r--r--platform/web/os_web.h1
-rw-r--r--platform/windows/display_server_windows.cpp2
-rw-r--r--platform/windows/os_windows.cpp101
-rw-r--r--platform/windows/os_windows.h1
-rw-r--r--scene/gui/file_dialog.cpp4
-rw-r--r--scene/main/node.compat.inc41
-rw-r--r--scene/main/node.cpp55
-rw-r--r--scene/main/node.h7
-rw-r--r--scene/resources/2d/tile_set.cpp1
-rw-r--r--scene/resources/3d/height_map_shape_3d.cpp101
-rw-r--r--scene/resources/3d/height_map_shape_3d.h4
-rw-r--r--scene/theme/default_theme.cpp2
-rw-r--r--servers/display_server.cpp2
-rw-r--r--servers/display_server.h2
-rw-r--r--servers/physics_server_3d.cpp10
-rw-r--r--servers/rendering/renderer_rd/effects/copy_effects.cpp4
-rw-r--r--servers/rendering/renderer_rd/effects/debug_effects.cpp4
-rw-r--r--servers/rendering/renderer_rd/effects/fsr2.cpp3
-rw-r--r--servers/rendering/renderer_rd/effects/ss_effects.cpp8
-rw-r--r--servers/rendering/renderer_rd/environment/gi.cpp11
-rw-r--r--servers/rendering/renderer_rd/environment/sky.cpp7
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp12
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h2
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp4
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp8
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h2
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp4
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/bokeh_dof.glsl4
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl4
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/cube_to_dp.glsl6
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_scale.glsl4
-rw-r--r--servers/rendering/renderer_rd/shaders/environment/gi.glsl4
-rw-r--r--servers/rendering/renderer_rd/shaders/environment/sky.glsl8
-rw-r--r--servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl6
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl4
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl4
-rw-r--r--servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl11
-rw-r--r--servers/rendering/renderer_rd/storage_rd/light_storage.cpp8
-rw-r--r--servers/rendering/renderer_scene_cull.cpp4
-rw-r--r--servers/rendering/renderer_scene_cull.h7
-rw-r--r--servers/rendering/renderer_scene_occlusion_cull.cpp7
-rw-r--r--servers/rendering/renderer_scene_occlusion_cull.h50
-rw-r--r--servers/rendering/rendering_device.h2
-rw-r--r--tests/core/io/test_marshalls.h78
127 files changed, 2003 insertions, 343 deletions
diff --git a/.github/workflows/static_checks.yml b/.github/workflows/static_checks.yml
index 74935bb618..0ed7432833 100644
--- a/.github/workflows/static_checks.yml
+++ b/.github/workflows/static_checks.yml
@@ -32,7 +32,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
if [ "${{ github.event_name }}" == "pull_request" ]; then
- files=$(git diff-tree --no-commit-id --name-only -r HEAD~${{ github.event.pull_request.commits }}..HEAD 2> /dev/null || true)
+ files=$(git diff-tree --no-commit-id --name-only -r HEAD^1..HEAD 2> /dev/null || true)
elif [ "${{ github.event_name }}" == "push" -a "${{ github.event.forced }}" == "false" -a "${{ github.event.created }}" == "false" ]; then
files=$(git diff-tree --no-commit-id --name-only -r ${{ github.event.before }}..${{ github.event.after }} 2> /dev/null || true)
fi
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index ce7fa1074b..14023c5c75 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -1523,6 +1523,8 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF("debug/settings/crash_handler/message.editor",
String("Please include this when reporting the bug on: https://github.com/godotengine/godot/issues"));
GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/occlusion_culling/bvh_build_quality", PROPERTY_HINT_ENUM, "Low,Medium,High"), 2);
+ GLOBAL_DEF_RST("rendering/occlusion_culling/jitter_projection", true);
+
GLOBAL_DEF_RST("internationalization/rendering/force_right_to_left_layout_direction", false);
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "internationalization/rendering/root_node_layout_direction", PROPERTY_HINT_ENUM, "Based on Application Locale,Left-to-Right,Right-to-Left,Based on System Locale"), 0);
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index 6927db002b..8c85030783 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -283,8 +283,8 @@ String OS::read_string_from_stdin() {
int OS::execute(const String &p_path, const Vector<String> &p_arguments, Array r_output, bool p_read_stderr, bool p_open_console) {
List<String> args;
- for (int i = 0; i < p_arguments.size(); i++) {
- args.push_back(p_arguments[i]);
+ for (const String &arg : p_arguments) {
+ args.push_back(arg);
}
String pipe;
int exitcode = 0;
@@ -296,10 +296,18 @@ int OS::execute(const String &p_path, const Vector<String> &p_arguments, Array r
return exitcode;
}
+Dictionary OS::execute_with_pipe(const String &p_path, const Vector<String> &p_arguments) {
+ List<String> args;
+ for (const String &arg : p_arguments) {
+ args.push_back(arg);
+ }
+ return ::OS::get_singleton()->execute_with_pipe(p_path, args);
+}
+
int OS::create_instance(const Vector<String> &p_arguments) {
List<String> args;
- for (int i = 0; i < p_arguments.size(); i++) {
- args.push_back(p_arguments[i]);
+ for (const String &arg : p_arguments) {
+ args.push_back(arg);
}
::OS::ProcessID pid = 0;
Error err = ::OS::get_singleton()->create_instance(args, &pid);
@@ -311,8 +319,8 @@ int OS::create_instance(const Vector<String> &p_arguments) {
int OS::create_process(const String &p_path, const Vector<String> &p_arguments, bool p_open_console) {
List<String> args;
- for (int i = 0; i < p_arguments.size(); i++) {
- args.push_back(p_arguments[i]);
+ for (const String &arg : p_arguments) {
+ args.push_back(arg);
}
::OS::ProcessID pid = 0;
Error err = ::OS::get_singleton()->create_process(p_path, args, &pid, p_open_console);
@@ -587,6 +595,7 @@ void OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_executable_path"), &OS::get_executable_path);
ClassDB::bind_method(D_METHOD("read_string_from_stdin"), &OS::read_string_from_stdin);
ClassDB::bind_method(D_METHOD("execute", "path", "arguments", "output", "read_stderr", "open_console"), &OS::execute, DEFVAL(Array()), DEFVAL(false), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("execute_with_pipe", "path", "arguments"), &OS::execute_with_pipe);
ClassDB::bind_method(D_METHOD("create_process", "path", "arguments", "open_console"), &OS::create_process, DEFVAL(false));
ClassDB::bind_method(D_METHOD("create_instance", "arguments"), &OS::create_instance);
ClassDB::bind_method(D_METHOD("kill", "pid"), &OS::kill);
diff --git a/core/core_bind.h b/core/core_bind.h
index 305f616cef..d46321cf5c 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -156,6 +156,7 @@ public:
String get_executable_path() const;
String read_string_from_stdin();
int execute(const String &p_path, const Vector<String> &p_arguments, Array r_output = Array(), bool p_read_stderr = false, bool p_open_console = false);
+ Dictionary execute_with_pipe(const String &p_path, const Vector<String> &p_arguments);
int create_process(const String &p_path, const Vector<String> &p_arguments, bool p_open_console = false);
int create_instance(const Vector<String> &p_arguments);
Error kill(int p_pid);
diff --git a/core/input/input.cpp b/core/input/input.cpp
index 3de0ed39ec..c24a59203f 100644
--- a/core/input/input.cpp
+++ b/core/input/input.cpp
@@ -894,7 +894,7 @@ void Input::action_press(const StringName &p_action, float p_strength) {
}
action_state.exact = true;
action_state.api_pressed = true;
- action_state.api_strength = p_strength;
+ action_state.api_strength = CLAMP(p_strength, 0.0f, 1.0f);
_update_action_cache(p_action, action_state);
}
diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp
index 55286277fa..d2a5103953 100644
--- a/core/io/file_access.cpp
+++ b/core/io/file_access.cpp
@@ -47,6 +47,7 @@ thread_local Error FileAccess::last_file_open_error = OK;
Ref<FileAccess> FileAccess::create(AccessType p_access) {
ERR_FAIL_INDEX_V(p_access, ACCESS_MAX, nullptr);
+ ERR_FAIL_NULL_V(create_func[p_access], nullptr);
Ref<FileAccess> ret = create_func[p_access]();
ret->_set_access_type(p_access);
@@ -75,7 +76,8 @@ Ref<FileAccess> FileAccess::create_for_path(const String &p_path) {
ret = create(ACCESS_RESOURCES);
} else if (p_path.begins_with("user://")) {
ret = create(ACCESS_USERDATA);
-
+ } else if (p_path.begins_with("pipe://")) {
+ ret = create(ACCESS_PIPE);
} else {
ret = create(ACCESS_FILESYSTEM);
}
@@ -209,6 +211,9 @@ String FileAccess::fix_path(const String &p_path) const {
}
} break;
+ case ACCESS_PIPE: {
+ return r_path;
+ } break;
case ACCESS_FILESYSTEM: {
return r_path;
} break;
diff --git a/core/io/file_access.h b/core/io/file_access.h
index 122ae3b190..0b631f68b1 100644
--- a/core/io/file_access.h
+++ b/core/io/file_access.h
@@ -50,6 +50,7 @@ public:
ACCESS_RESOURCES,
ACCESS_USERDATA,
ACCESS_FILESYSTEM,
+ ACCESS_PIPE,
ACCESS_MAX
};
diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp
index bc2493d360..b25fcccd7f 100644
--- a/core/io/marshalls.cpp
+++ b/core/io/marshalls.cpp
@@ -30,7 +30,10 @@
#include "marshalls.h"
+#include "core/core_string_names.h"
+#include "core/io/resource_loader.h"
#include "core/object/ref_counted.h"
+#include "core/object/script_language.h"
#include "core/os/keyboard.h"
#include "core/string/print_string.h"
@@ -55,9 +58,22 @@ ObjectID EncodedObjectAsID::get_object_id() const {
#define ERR_FAIL_ADD_OF(a, b, err) ERR_FAIL_COND_V(((int32_t)(b)) < 0 || ((int32_t)(a)) < 0 || ((int32_t)(a)) > INT_MAX - ((int32_t)(b)), err)
#define ERR_FAIL_MUL_OF(a, b, err) ERR_FAIL_COND_V(((int32_t)(a)) < 0 || ((int32_t)(b)) <= 0 || ((int32_t)(a)) > INT_MAX / ((int32_t)(b)), err)
-#define ENCODE_MASK 0xFF
-#define ENCODE_FLAG_64 1 << 16
-#define ENCODE_FLAG_OBJECT_AS_ID 1 << 16
+// Byte 0: `Variant::Type`, byte 1: unused, bytes 2 and 3: additional data.
+#define HEADER_TYPE_MASK 0xFF
+
+// For `Variant::INT`, `Variant::FLOAT` and other math types.
+#define HEADER_DATA_FLAG_64 (1 << 16)
+
+// For `Variant::OBJECT`.
+#define HEADER_DATA_FLAG_OBJECT_AS_ID (1 << 16)
+
+// For `Variant::ARRAY`.
+// Occupies bits 16 and 17.
+#define HEADER_DATA_FIELD_TYPED_ARRAY_MASK (0b11 << 16)
+#define HEADER_DATA_FIELD_TYPED_ARRAY_NONE (0b00 << 16)
+#define HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN (0b01 << 16)
+#define HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME (0b10 << 16)
+#define HEADER_DATA_FIELD_TYPED_ARRAY_SCRIPT (0b11 << 16)
static Error _decode_string(const uint8_t *&buf, int &len, int *r_len, String &r_string) {
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
@@ -101,9 +117,9 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
- uint32_t type = decode_uint32(buf);
+ uint32_t header = decode_uint32(buf);
- ERR_FAIL_COND_V((type & ENCODE_MASK) >= Variant::VARIANT_MAX, ERR_INVALID_DATA);
+ ERR_FAIL_COND_V((header & HEADER_TYPE_MASK) >= Variant::VARIANT_MAX, ERR_INVALID_DATA);
buf += 4;
len -= 4;
@@ -114,7 +130,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
// Note: We cannot use sizeof(real_t) for decoding, in case a different size is encoded.
// Decoding math types always checks for the encoded size, while encoding always uses compilation setting.
// This does lead to some code duplication for decoding, but compatibility is the priority.
- switch (type & ENCODE_MASK) {
+ switch (header & HEADER_TYPE_MASK) {
case Variant::NIL: {
r_variant = Variant();
} break;
@@ -127,7 +143,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
}
} break;
case Variant::INT: {
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA);
int64_t val = decode_uint64(buf);
r_variant = val;
@@ -146,7 +162,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::FLOAT: {
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_COND_V((size_t)len < sizeof(double), ERR_INVALID_DATA);
double val = decode_double(buf);
r_variant = val;
@@ -176,7 +192,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
// math types
case Variant::VECTOR2: {
Vector2 val;
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_COND_V((size_t)len < sizeof(double) * 2, ERR_INVALID_DATA);
val.x = decode_double(&buf[0]);
val.y = decode_double(&buf[sizeof(double)]);
@@ -210,7 +226,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::RECT2: {
Rect2 val;
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_COND_V((size_t)len < sizeof(double) * 4, ERR_INVALID_DATA);
val.position.x = decode_double(&buf[0]);
val.position.y = decode_double(&buf[sizeof(double)]);
@@ -250,7 +266,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::VECTOR3: {
Vector3 val;
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_COND_V((size_t)len < sizeof(double) * 3, ERR_INVALID_DATA);
val.x = decode_double(&buf[0]);
val.y = decode_double(&buf[sizeof(double)]);
@@ -287,7 +303,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::VECTOR4: {
Vector4 val;
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_COND_V((size_t)len < sizeof(double) * 4, ERR_INVALID_DATA);
val.x = decode_double(&buf[0]);
val.y = decode_double(&buf[sizeof(double)]);
@@ -327,7 +343,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::TRANSFORM2D: {
Transform2D val;
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_COND_V((size_t)len < sizeof(double) * 6, ERR_INVALID_DATA);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 2; j++) {
@@ -355,7 +371,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::PLANE: {
Plane val;
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_COND_V((size_t)len < sizeof(double) * 4, ERR_INVALID_DATA);
val.normal.x = decode_double(&buf[0]);
val.normal.y = decode_double(&buf[sizeof(double)]);
@@ -381,7 +397,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::QUATERNION: {
Quaternion val;
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_COND_V((size_t)len < sizeof(double) * 4, ERR_INVALID_DATA);
val.x = decode_double(&buf[0]);
val.y = decode_double(&buf[sizeof(double)]);
@@ -407,7 +423,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::AABB: {
AABB val;
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_COND_V((size_t)len < sizeof(double) * 6, ERR_INVALID_DATA);
val.position.x = decode_double(&buf[0]);
val.position.y = decode_double(&buf[sizeof(double)]);
@@ -437,7 +453,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::BASIS: {
Basis val;
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_COND_V((size_t)len < sizeof(double) * 9, ERR_INVALID_DATA);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
@@ -465,7 +481,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::TRANSFORM3D: {
Transform3D val;
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_COND_V((size_t)len < sizeof(double) * 12, ERR_INVALID_DATA);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
@@ -499,7 +515,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::PROJECTION: {
Projection val;
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_COND_V((size_t)len < sizeof(double) * 16, ERR_INVALID_DATA);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
@@ -560,12 +576,12 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
uint32_t namecount = strlen &= 0x7FFFFFFF;
uint32_t subnamecount = decode_uint32(buf + 4);
- uint32_t flags = decode_uint32(buf + 8);
+ uint32_t np_flags = decode_uint32(buf + 8);
len -= 12;
buf += 12;
- if (flags & 2) { // Obsolete format with property separate from subpath
+ if (np_flags & 2) { // Obsolete format with property separate from subpath.
subnamecount++;
}
@@ -589,7 +605,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
}
}
- r_variant = NodePath(names, subnames, flags & 1);
+ r_variant = NodePath(names, subnames, np_flags & 1);
} else {
//old format, just a string
@@ -608,8 +624,8 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
r_variant = RID::from_uint64(id);
} break;
case Variant::OBJECT: {
- if (type & ENCODE_FLAG_OBJECT_AS_ID) {
- //this _is_ allowed
+ if (header & HEADER_DATA_FLAG_OBJECT_AS_ID) {
+ // This _is_ allowed.
ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA);
ObjectID val = ObjectID(decode_uint64(buf));
if (r_len) {
@@ -625,7 +641,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
r_variant = obj_as_id;
}
-
} else {
ERR_FAIL_COND_V(!p_allow_objects, ERR_UNAUTHORIZED);
@@ -672,7 +687,16 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
(*r_len) += used;
}
- obj->set(str, value);
+ if (str == "script") {
+ ERR_FAIL_COND_V_MSG(value.get_type() != Variant::STRING, ERR_INVALID_DATA, "Invalid value for \"script\" property, expected script path as String.");
+ String path = value;
+ ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"), ERR_INVALID_DATA, "Invalid script path: '" + path + "'.");
+ Ref<Script> script = ResourceLoader::load(path, "Script");
+ ERR_FAIL_COND_V_MSG(script.is_null(), ERR_INVALID_DATA, "Can't load script at path: '" + path + "'.");
+ obj->set_script(script);
+ } else {
+ obj->set(str, value);
+ }
}
if (Object::cast_to<RefCounted>(obj)) {
@@ -747,7 +771,60 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::ARRAY: {
+ Variant::Type builtin_type = Variant::VARIANT_MAX;
+ StringName class_name;
+ Ref<Script> script;
+
+ switch (header & HEADER_DATA_FIELD_TYPED_ARRAY_MASK) {
+ case HEADER_DATA_FIELD_TYPED_ARRAY_NONE:
+ break; // Untyped array.
+ case HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN: {
+ ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
+
+ int32_t bt = decode_uint32(buf);
+ buf += 4;
+ len -= 4;
+ if (r_len) {
+ (*r_len) += 4;
+ }
+
+ ERR_FAIL_INDEX_V(bt, Variant::VARIANT_MAX, ERR_INVALID_DATA);
+ builtin_type = (Variant::Type)bt;
+ ERR_FAIL_COND_V(!p_allow_objects && builtin_type == Variant::OBJECT, ERR_UNAUTHORIZED);
+ } break;
+ case HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME: {
+ ERR_FAIL_COND_V(!p_allow_objects, ERR_UNAUTHORIZED);
+
+ String str;
+ Error err = _decode_string(buf, len, r_len, str);
+ if (err) {
+ return err;
+ }
+
+ builtin_type = Variant::OBJECT;
+ class_name = str;
+ } break;
+ case HEADER_DATA_FIELD_TYPED_ARRAY_SCRIPT: {
+ ERR_FAIL_COND_V(!p_allow_objects, ERR_UNAUTHORIZED);
+
+ String path;
+ Error err = _decode_string(buf, len, r_len, path);
+ if (err) {
+ return err;
+ }
+ ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"), ERR_INVALID_DATA, "Invalid script path: '" + path + "'.");
+ script = ResourceLoader::load(path, "Script");
+ ERR_FAIL_COND_V_MSG(script.is_null(), ERR_INVALID_DATA, "Can't load script at path: '" + path + "'.");
+
+ builtin_type = Variant::OBJECT;
+ class_name = script->get_instance_base_type();
+ } break;
+ default:
+ ERR_FAIL_V(ERR_INVALID_DATA); // Future proofing.
+ }
+
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
+
int32_t count = decode_uint32(buf);
// bool shared = count&0x80000000;
count &= 0x7FFFFFFF;
@@ -760,6 +837,9 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
}
Array varr;
+ if (builtin_type != Variant::VARIANT_MAX) {
+ varr.set_typed(builtin_type, class_name, script);
+ }
for (int i = 0; i < count; i++) {
int used = 0;
@@ -936,7 +1016,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
Vector<Vector2> varray;
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_MUL_OF(count, sizeof(double) * 2, ERR_INVALID_DATA);
ERR_FAIL_COND_V(count < 0 || count * sizeof(double) * 2 > (size_t)len, ERR_INVALID_DATA);
@@ -996,7 +1076,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
Vector<Vector3> varray;
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_MUL_OF(count, sizeof(double) * 3, ERR_INVALID_DATA);
ERR_FAIL_COND_V(count < 0 || count * sizeof(double) * 3 > (size_t)len, ERR_INVALID_DATA);
@@ -1122,20 +1202,20 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
r_len = 0;
- uint32_t flags = 0;
+ uint32_t header = p_variant.get_type();
switch (p_variant.get_type()) {
case Variant::INT: {
int64_t val = p_variant;
if (val > (int64_t)INT_MAX || val < (int64_t)INT_MIN) {
- flags |= ENCODE_FLAG_64;
+ header |= HEADER_DATA_FLAG_64;
}
} break;
case Variant::FLOAT: {
double d = p_variant;
float f = d;
if (double(f) != d) {
- flags |= ENCODE_FLAG_64;
+ header |= HEADER_DATA_FLAG_64;
}
} break;
case Variant::OBJECT: {
@@ -1151,7 +1231,23 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
}
if (!p_full_objects) {
- flags |= ENCODE_FLAG_OBJECT_AS_ID;
+ header |= HEADER_DATA_FLAG_OBJECT_AS_ID;
+ }
+ } break;
+ case Variant::ARRAY: {
+ Array array = p_variant;
+ if (array.is_typed()) {
+ Ref<Script> script = array.get_typed_script();
+ if (script.is_valid()) {
+ ERR_FAIL_COND_V(!p_full_objects, ERR_UNAVAILABLE);
+ header |= HEADER_DATA_FIELD_TYPED_ARRAY_SCRIPT;
+ } else if (array.get_typed_class_name() != StringName()) {
+ ERR_FAIL_COND_V(!p_full_objects, ERR_UNAVAILABLE);
+ header |= HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME;
+ } else {
+ ERR_FAIL_COND_V(!p_full_objects && array.get_typed_builtin() == Variant::OBJECT, ERR_UNAVAILABLE);
+ header |= HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN;
+ }
}
} break;
#ifdef REAL_T_IS_DOUBLE
@@ -1168,7 +1264,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
case Variant::BASIS:
case Variant::RECT2:
case Variant::AABB: {
- flags |= ENCODE_FLAG_64;
+ header |= HEADER_DATA_FLAG_64;
} break;
#endif // REAL_T_IS_DOUBLE
default: {
@@ -1176,7 +1272,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
}
if (buf) {
- encode_uint32(p_variant.get_type() | flags, buf);
+ encode_uint32(header, buf);
buf += 4;
}
r_len += 4;
@@ -1194,7 +1290,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
} break;
case Variant::INT: {
- if (flags & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
//64 bits
if (buf) {
encode_uint64(p_variant.operator int64_t(), buf);
@@ -1210,7 +1306,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
}
} break;
case Variant::FLOAT: {
- if (flags & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
if (buf) {
encode_double(p_variant.operator double(), buf);
}
@@ -1523,8 +1619,21 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
_encode_string(E.name, buf, r_len);
+ Variant value;
+
+ if (E.name == CoreStringNames::get_singleton()->_script) {
+ Ref<Script> script = obj->get_script();
+ if (script.is_valid()) {
+ String path = script->get_path();
+ ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://"), ERR_UNAVAILABLE, "Failed to encode a path to a custom script.");
+ value = path;
+ }
+ } else {
+ value = obj->get(E.name);
+ }
+
int len;
- Error err = encode_variant(obj->get(E.name), buf, len, p_full_objects, p_depth + 1);
+ Error err = encode_variant(value, buf, len, p_full_objects, p_depth + 1);
ERR_FAIL_COND_V(err, err);
ERR_FAIL_COND_V(len % 4, ERR_BUG);
r_len += len;
@@ -1594,24 +1703,41 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
} break;
case Variant::ARRAY: {
- Array v = p_variant;
+ Array array = p_variant;
+
+ if (array.is_typed()) {
+ Variant variant = array.get_typed_script();
+ Ref<Script> script = variant;
+ if (script.is_valid()) {
+ String path = script->get_path();
+ ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://"), ERR_UNAVAILABLE, "Failed to encode a path to a custom script for an array type.");
+ _encode_string(path, buf, r_len);
+ } else if (array.get_typed_class_name() != StringName()) {
+ _encode_string(array.get_typed_class_name(), buf, r_len);
+ } else {
+ if (buf) {
+ encode_uint32(array.get_typed_builtin(), buf);
+ buf += 4;
+ }
+ r_len += 4;
+ }
+ }
if (buf) {
- encode_uint32(uint32_t(v.size()), buf);
+ encode_uint32(uint32_t(array.size()), buf);
buf += 4;
}
-
r_len += 4;
- for (int i = 0; i < v.size(); i++) {
+ for (int i = 0; i < array.size(); i++) {
int len;
- Error err = encode_variant(v.get(i), buf, len, p_full_objects, p_depth + 1);
+ Error err = encode_variant(array.get(i), buf, len, p_full_objects, p_depth + 1);
ERR_FAIL_COND_V(err, err);
ERR_FAIL_COND_V(len % 4, ERR_BUG);
- r_len += len;
if (buf) {
buf += len;
}
+ r_len += len;
}
} break;
diff --git a/core/math/a_star.compat.inc b/core/math/a_star.compat.inc
new file mode 100644
index 0000000000..664d7ffd5e
--- /dev/null
+++ b/core/math/a_star.compat.inc
@@ -0,0 +1,59 @@
+/**************************************************************************/
+/* a_star.compat.inc */
+/**************************************************************************/
+/* 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 DISABLE_DEPRECATED
+
+Vector<int64_t> AStar3D::_get_id_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id) {
+ return get_id_path(p_from_id, p_to_id, false);
+}
+
+Vector<Vector3> AStar3D::_get_point_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id) {
+ return get_point_path(p_from_id, p_to_id, false);
+}
+
+void AStar3D::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStar3D::_get_id_path_bind_compat_88047);
+ ClassDB::bind_compatibility_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStar3D::_get_point_path_bind_compat_88047);
+}
+
+Vector<int64_t> AStar2D::_get_id_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id) {
+ return get_id_path(p_from_id, p_to_id, false);
+}
+
+Vector<Vector2> AStar2D::_get_point_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id) {
+ return get_point_path(p_from_id, p_to_id, false);
+}
+
+void AStar2D::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStar2D::_get_id_path_bind_compat_88047);
+ ClassDB::bind_compatibility_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStar2D::_get_point_path_bind_compat_88047);
+}
+
+#endif // DISABLE_DEPRECATED
diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp
index fb54058bd9..4497604947 100644
--- a/core/math/a_star.cpp
+++ b/core/math/a_star.cpp
@@ -29,6 +29,7 @@
/**************************************************************************/
#include "a_star.h"
+#include "a_star.compat.inc"
#include "core/math/geometry_3d.h"
#include "core/object/script_language.h"
@@ -319,6 +320,7 @@ Vector3 AStar3D::get_closest_position_in_segment(const Vector3 &p_point) const {
}
bool AStar3D::_solve(Point *begin_point, Point *end_point) {
+ last_closest_point = nullptr;
pass++;
if (!end_point->enabled) {
@@ -332,11 +334,18 @@ bool AStar3D::_solve(Point *begin_point, Point *end_point) {
begin_point->g_score = 0;
begin_point->f_score = _estimate_cost(begin_point->id, end_point->id);
+ begin_point->abs_g_score = 0;
+ begin_point->abs_f_score = _estimate_cost(begin_point->id, end_point->id);
open_list.push_back(begin_point);
while (!open_list.is_empty()) {
Point *p = open_list[0]; // The currently processed point.
+ // Find point closer to end_point, or same distance to end_point but closer to begin_point.
+ if (last_closest_point == nullptr || last_closest_point->abs_f_score > p->abs_f_score || (last_closest_point->abs_f_score >= p->abs_f_score && last_closest_point->abs_g_score > p->abs_g_score)) {
+ last_closest_point = p;
+ }
+
if (p == end_point) {
found_route = true;
break;
@@ -368,6 +377,8 @@ bool AStar3D::_solve(Point *begin_point, Point *end_point) {
e->prev_point = p;
e->g_score = tentative_g_score;
e->f_score = e->g_score + _estimate_cost(e->id, end_point->id);
+ e->abs_g_score = tentative_g_score;
+ e->abs_f_score = e->f_score - e->g_score;
if (new_point) { // The position of the new points is already known.
sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptr());
@@ -414,7 +425,7 @@ real_t AStar3D::_compute_cost(int64_t p_from_id, int64_t p_to_id) {
return from_point->pos.distance_to(to_point->pos);
}
-Vector<Vector3> AStar3D::get_point_path(int64_t p_from_id, int64_t p_to_id) {
+Vector<Vector3> AStar3D::get_point_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path) {
Point *a = nullptr;
bool from_exists = points.lookup(p_from_id, a);
ERR_FAIL_COND_V_MSG(!from_exists, Vector<Vector3>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_from_id));
@@ -434,7 +445,12 @@ Vector<Vector3> AStar3D::get_point_path(int64_t p_from_id, int64_t p_to_id) {
bool found_route = _solve(begin_point, end_point);
if (!found_route) {
- return Vector<Vector3>();
+ if (!p_allow_partial_path || last_closest_point == nullptr) {
+ return Vector<Vector3>();
+ }
+
+ // Use closest point instead.
+ end_point = last_closest_point;
}
Point *p = end_point;
@@ -463,7 +479,7 @@ Vector<Vector3> AStar3D::get_point_path(int64_t p_from_id, int64_t p_to_id) {
return path;
}
-Vector<int64_t> AStar3D::get_id_path(int64_t p_from_id, int64_t p_to_id) {
+Vector<int64_t> AStar3D::get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path) {
Point *a = nullptr;
bool from_exists = points.lookup(p_from_id, a);
ERR_FAIL_COND_V_MSG(!from_exists, Vector<int64_t>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_from_id));
@@ -483,7 +499,12 @@ Vector<int64_t> AStar3D::get_id_path(int64_t p_from_id, int64_t p_to_id) {
bool found_route = _solve(begin_point, end_point);
if (!found_route) {
- return Vector<int64_t>();
+ if (!p_allow_partial_path || last_closest_point == nullptr) {
+ return Vector<int64_t>();
+ }
+
+ // Use closest point instead.
+ end_point = last_closest_point;
}
Point *p = end_point;
@@ -555,8 +576,8 @@ void AStar3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_closest_point", "to_position", "include_disabled"), &AStar3D::get_closest_point, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_closest_position_in_segment", "to_position"), &AStar3D::get_closest_position_in_segment);
- ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStar3D::get_point_path);
- ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStar3D::get_id_path);
+ ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id", "allow_partial_path"), &AStar3D::get_point_path, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id", "allow_partial_path"), &AStar3D::get_id_path, DEFVAL(false));
GDVIRTUAL_BIND(_estimate_cost, "from_id", "to_id")
GDVIRTUAL_BIND(_compute_cost, "from_id", "to_id")
@@ -688,7 +709,7 @@ real_t AStar2D::_compute_cost(int64_t p_from_id, int64_t p_to_id) {
return from_point->pos.distance_to(to_point->pos);
}
-Vector<Vector2> AStar2D::get_point_path(int64_t p_from_id, int64_t p_to_id) {
+Vector<Vector2> AStar2D::get_point_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path) {
AStar3D::Point *a = nullptr;
bool from_exists = astar.points.lookup(p_from_id, a);
ERR_FAIL_COND_V_MSG(!from_exists, Vector<Vector2>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_from_id));
@@ -707,7 +728,12 @@ Vector<Vector2> AStar2D::get_point_path(int64_t p_from_id, int64_t p_to_id) {
bool found_route = _solve(begin_point, end_point);
if (!found_route) {
- return Vector<Vector2>();
+ if (!p_allow_partial_path || astar.last_closest_point == nullptr) {
+ return Vector<Vector2>();
+ }
+
+ // Use closest point instead.
+ end_point = astar.last_closest_point;
}
AStar3D::Point *p = end_point;
@@ -736,7 +762,7 @@ Vector<Vector2> AStar2D::get_point_path(int64_t p_from_id, int64_t p_to_id) {
return path;
}
-Vector<int64_t> AStar2D::get_id_path(int64_t p_from_id, int64_t p_to_id) {
+Vector<int64_t> AStar2D::get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path) {
AStar3D::Point *a = nullptr;
bool from_exists = astar.points.lookup(p_from_id, a);
ERR_FAIL_COND_V_MSG(!from_exists, Vector<int64_t>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_from_id));
@@ -756,7 +782,12 @@ Vector<int64_t> AStar2D::get_id_path(int64_t p_from_id, int64_t p_to_id) {
bool found_route = _solve(begin_point, end_point);
if (!found_route) {
- return Vector<int64_t>();
+ if (!p_allow_partial_path || astar.last_closest_point == nullptr) {
+ return Vector<int64_t>();
+ }
+
+ // Use closest point instead.
+ end_point = astar.last_closest_point;
}
AStar3D::Point *p = end_point;
@@ -786,6 +817,7 @@ Vector<int64_t> AStar2D::get_id_path(int64_t p_from_id, int64_t p_to_id) {
}
bool AStar2D::_solve(AStar3D::Point *begin_point, AStar3D::Point *end_point) {
+ astar.last_closest_point = nullptr;
astar.pass++;
if (!end_point->enabled) {
@@ -799,11 +831,18 @@ bool AStar2D::_solve(AStar3D::Point *begin_point, AStar3D::Point *end_point) {
begin_point->g_score = 0;
begin_point->f_score = _estimate_cost(begin_point->id, end_point->id);
+ begin_point->abs_g_score = 0;
+ begin_point->abs_f_score = _estimate_cost(begin_point->id, end_point->id);
open_list.push_back(begin_point);
while (!open_list.is_empty()) {
AStar3D::Point *p = open_list[0]; // The currently processed point.
+ // Find point closer to end_point, or same distance to end_point but closer to begin_point.
+ if (astar.last_closest_point == nullptr || astar.last_closest_point->abs_f_score > p->abs_f_score || (astar.last_closest_point->abs_f_score >= p->abs_f_score && astar.last_closest_point->abs_g_score > p->abs_g_score)) {
+ astar.last_closest_point = p;
+ }
+
if (p == end_point) {
found_route = true;
break;
@@ -835,6 +874,8 @@ bool AStar2D::_solve(AStar3D::Point *begin_point, AStar3D::Point *end_point) {
e->prev_point = p;
e->g_score = tentative_g_score;
e->f_score = e->g_score + _estimate_cost(e->id, end_point->id);
+ e->abs_g_score = tentative_g_score;
+ e->abs_f_score = e->f_score - e->g_score;
if (new_point) { // The position of the new points is already known.
sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptr());
@@ -874,8 +915,8 @@ void AStar2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_closest_point", "to_position", "include_disabled"), &AStar2D::get_closest_point, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_closest_position_in_segment", "to_position"), &AStar2D::get_closest_position_in_segment);
- ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStar2D::get_point_path);
- ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStar2D::get_id_path);
+ ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id", "allow_partial_path"), &AStar2D::get_point_path, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id", "allow_partial_path"), &AStar2D::get_id_path, DEFVAL(false));
GDVIRTUAL_BIND(_estimate_cost, "from_id", "to_id")
GDVIRTUAL_BIND(_compute_cost, "from_id", "to_id")
diff --git a/core/math/a_star.h b/core/math/a_star.h
index 0758500c8a..8e054c4789 100644
--- a/core/math/a_star.h
+++ b/core/math/a_star.h
@@ -60,6 +60,10 @@ class AStar3D : public RefCounted {
real_t f_score = 0;
uint64_t open_pass = 0;
uint64_t closed_pass = 0;
+
+ // Used for getting closest_point_of_last_pathing_call.
+ real_t abs_g_score = 0;
+ real_t abs_f_score = 0;
};
struct SortPoints {
@@ -109,6 +113,7 @@ class AStar3D : public RefCounted {
OAHashMap<int64_t, Point *> points;
HashSet<Segment, Segment> segments;
+ Point *last_closest_point = nullptr;
bool _solve(Point *begin_point, Point *end_point);
@@ -121,6 +126,12 @@ protected:
GDVIRTUAL2RC(real_t, _estimate_cost, int64_t, int64_t)
GDVIRTUAL2RC(real_t, _compute_cost, int64_t, int64_t)
+#ifndef DISABLE_DEPRECATED
+ Vector<int64_t> _get_id_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id);
+ Vector<Vector3> _get_point_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id);
+ static void _bind_compatibility_methods();
+#endif
+
public:
int64_t get_available_point_id() const;
@@ -149,8 +160,8 @@ public:
int64_t get_closest_point(const Vector3 &p_point, bool p_include_disabled = false) const;
Vector3 get_closest_position_in_segment(const Vector3 &p_point) const;
- Vector<Vector3> get_point_path(int64_t p_from_id, int64_t p_to_id);
- Vector<int64_t> get_id_path(int64_t p_from_id, int64_t p_to_id);
+ Vector<Vector3> get_point_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path = false);
+ Vector<int64_t> get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path = false);
AStar3D() {}
~AStar3D();
@@ -171,6 +182,12 @@ protected:
GDVIRTUAL2RC(real_t, _estimate_cost, int64_t, int64_t)
GDVIRTUAL2RC(real_t, _compute_cost, int64_t, int64_t)
+#ifndef DISABLE_DEPRECATED
+ Vector<int64_t> _get_id_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id);
+ Vector<Vector2> _get_point_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id);
+ static void _bind_compatibility_methods();
+#endif
+
public:
int64_t get_available_point_id() const;
@@ -199,8 +216,8 @@ public:
int64_t get_closest_point(const Vector2 &p_point, bool p_include_disabled = false) const;
Vector2 get_closest_position_in_segment(const Vector2 &p_point) const;
- Vector<Vector2> get_point_path(int64_t p_from_id, int64_t p_to_id);
- Vector<int64_t> get_id_path(int64_t p_from_id, int64_t p_to_id);
+ Vector<Vector2> get_point_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path = false);
+ Vector<int64_t> get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path = false);
AStar2D() {}
~AStar2D() {}
diff --git a/core/math/a_star_grid_2d.compat.inc b/core/math/a_star_grid_2d.compat.inc
new file mode 100644
index 0000000000..e7124c2477
--- /dev/null
+++ b/core/math/a_star_grid_2d.compat.inc
@@ -0,0 +1,48 @@
+/**************************************************************************/
+/* a_star_grid_2d.compat.inc */
+/**************************************************************************/
+/* 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 DISABLE_DEPRECATED
+
+#include "core/variant/typed_array.h"
+
+TypedArray<Vector2i> AStarGrid2D::_get_id_path_bind_compat_88047(const Vector2i &p_from_id, const Vector2i &p_to_id) {
+ return get_id_path(p_from_id, p_to_id, false);
+}
+
+Vector<Vector2> AStarGrid2D::_get_point_path_bind_compat_88047(const Vector2i &p_from_id, const Vector2i &p_to_id) {
+ return get_point_path(p_from_id, p_to_id, false);
+}
+
+void AStarGrid2D::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStarGrid2D::_get_id_path_bind_compat_88047);
+ ClassDB::bind_compatibility_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStarGrid2D::_get_point_path_bind_compat_88047);
+}
+
+#endif // DISABLE_DEPRECATED
diff --git a/core/math/a_star_grid_2d.cpp b/core/math/a_star_grid_2d.cpp
index d17f465ab8..f272407869 100644
--- a/core/math/a_star_grid_2d.cpp
+++ b/core/math/a_star_grid_2d.cpp
@@ -29,6 +29,7 @@
/**************************************************************************/
#include "a_star_grid_2d.h"
+#include "a_star_grid_2d.compat.inc"
#include "core/variant/typed_array.h"
@@ -446,6 +447,7 @@ void AStarGrid2D::_get_nbors(Point *p_point, LocalVector<Point *> &r_nbors) {
}
bool AStarGrid2D::_solve(Point *p_begin_point, Point *p_end_point) {
+ last_closest_point = nullptr;
pass++;
if (p_end_point->solid) {
@@ -459,12 +461,19 @@ bool AStarGrid2D::_solve(Point *p_begin_point, Point *p_end_point) {
p_begin_point->g_score = 0;
p_begin_point->f_score = _estimate_cost(p_begin_point->id, p_end_point->id);
+ p_begin_point->abs_g_score = 0;
+ p_begin_point->abs_f_score = _estimate_cost(p_begin_point->id, p_end_point->id);
open_list.push_back(p_begin_point);
end = p_end_point;
while (!open_list.is_empty()) {
Point *p = open_list[0]; // The currently processed point.
+ // Find point closer to end_point, or same distance to end_point but closer to begin_point.
+ if (last_closest_point == nullptr || last_closest_point->abs_f_score > p->abs_f_score || (last_closest_point->abs_f_score >= p->abs_f_score && last_closest_point->abs_g_score > p->abs_g_score)) {
+ last_closest_point = p;
+ }
+
if (p == p_end_point) {
found_route = true;
break;
@@ -508,6 +517,9 @@ bool AStarGrid2D::_solve(Point *p_begin_point, Point *p_end_point) {
e->g_score = tentative_g_score;
e->f_score = e->g_score + _estimate_cost(e->id, p_end_point->id);
+ e->abs_g_score = tentative_g_score;
+ e->abs_f_score = e->f_score - e->g_score;
+
if (new_point) { // The position of the new points is already known.
sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptr());
} else {
@@ -546,7 +558,7 @@ Vector2 AStarGrid2D::get_point_position(const Vector2i &p_id) const {
return _get_point_unchecked(p_id)->pos;
}
-Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vector2i &p_to_id) {
+Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vector2i &p_to_id, bool p_allow_partial_path) {
ERR_FAIL_COND_V_MSG(dirty, Vector<Vector2>(), "Grid is not initialized. Call the update method.");
ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_from_id), Vector<Vector2>(), vformat("Can't get id path. Point %s out of bounds %s.", p_from_id, region));
ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_to_id), Vector<Vector2>(), vformat("Can't get id path. Point %s out of bounds %s.", p_to_id, region));
@@ -565,7 +577,12 @@ Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vec
bool found_route = _solve(begin_point, end_point);
if (!found_route) {
- return Vector<Vector2>();
+ if (!p_allow_partial_path || last_closest_point == nullptr) {
+ return Vector<Vector2>();
+ }
+
+ // Use closest point instead.
+ end_point = last_closest_point;
}
Point *p = end_point;
@@ -594,7 +611,7 @@ Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vec
return path;
}
-TypedArray<Vector2i> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const Vector2i &p_to_id) {
+TypedArray<Vector2i> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const Vector2i &p_to_id, bool p_allow_partial_path) {
ERR_FAIL_COND_V_MSG(dirty, TypedArray<Vector2i>(), "Grid is not initialized. Call the update method.");
ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_from_id), TypedArray<Vector2i>(), vformat("Can't get id path. Point %s out of bounds %s.", p_from_id, region));
ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_to_id), TypedArray<Vector2i>(), vformat("Can't get id path. Point %s out of bounds %s.", p_to_id, region));
@@ -613,7 +630,12 @@ TypedArray<Vector2i> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const V
bool found_route = _solve(begin_point, end_point);
if (!found_route) {
- return TypedArray<Vector2i>();
+ if (!p_allow_partial_path || last_closest_point == nullptr) {
+ return TypedArray<Vector2i>();
+ }
+
+ // Use closest point instead.
+ end_point = last_closest_point;
}
Point *p = end_point;
@@ -672,8 +694,8 @@ void AStarGrid2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear"), &AStarGrid2D::clear);
ClassDB::bind_method(D_METHOD("get_point_position", "id"), &AStarGrid2D::get_point_position);
- ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStarGrid2D::get_point_path);
- ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStarGrid2D::get_id_path);
+ ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id", "allow_partial_path"), &AStarGrid2D::get_point_path, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id", "allow_partial_path"), &AStarGrid2D::get_id_path, DEFVAL(false));
GDVIRTUAL_BIND(_estimate_cost, "from_id", "to_id")
GDVIRTUAL_BIND(_compute_cost, "from_id", "to_id")
diff --git a/core/math/a_star_grid_2d.h b/core/math/a_star_grid_2d.h
index 69cb77dd3e..1a9f6dcc11 100644
--- a/core/math/a_star_grid_2d.h
+++ b/core/math/a_star_grid_2d.h
@@ -89,6 +89,10 @@ private:
uint64_t open_pass = 0;
uint64_t closed_pass = 0;
+ // Used for getting last_closest_point.
+ real_t abs_g_score = 0;
+ real_t abs_f_score = 0;
+
Point() {}
Point(const Vector2i &p_id, const Vector2 &p_pos) :
@@ -109,6 +113,7 @@ private:
LocalVector<LocalVector<Point>> points;
Point *end = nullptr;
+ Point *last_closest_point = nullptr;
uint64_t pass = 1;
@@ -152,6 +157,12 @@ protected:
GDVIRTUAL2RC(real_t, _estimate_cost, Vector2i, Vector2i)
GDVIRTUAL2RC(real_t, _compute_cost, Vector2i, Vector2i)
+#ifndef DISABLE_DEPRECATED
+ TypedArray<Vector2i> _get_id_path_bind_compat_88047(const Vector2i &p_from, const Vector2i &p_to);
+ Vector<Vector2> _get_point_path_bind_compat_88047(const Vector2i &p_from, const Vector2i &p_to);
+ static void _bind_compatibility_methods();
+#endif
+
public:
void set_region(const Rect2i &p_region);
Rect2i get_region() const;
@@ -198,8 +209,8 @@ public:
void clear();
Vector2 get_point_position(const Vector2i &p_id) const;
- Vector<Vector2> get_point_path(const Vector2i &p_from, const Vector2i &p_to);
- TypedArray<Vector2i> get_id_path(const Vector2i &p_from, const Vector2i &p_to);
+ Vector<Vector2> get_point_path(const Vector2i &p_from, const Vector2i &p_to, bool p_allow_partial_path = false);
+ TypedArray<Vector2i> get_id_path(const Vector2i &p_from, const Vector2i &p_to, bool p_allow_partial_path = false);
};
VARIANT_ENUM_CAST(AStarGrid2D::DiagonalMode);
diff --git a/core/math/projection.cpp b/core/math/projection.cpp
index 9d5dc8b4d6..d3cf11f91a 100644
--- a/core/math/projection.cpp
+++ b/core/math/projection.cpp
@@ -719,7 +719,8 @@ Projection Projection::operator*(const Projection &p_matrix) const {
return new_matrix;
}
-void Projection::set_depth_correction(bool p_flip_y) {
+void Projection::set_depth_correction(bool p_flip_y, bool p_reverse_z, bool p_remap_z) {
+ // p_remap_z is used to convert from OpenGL-style clip space (-1 - 1) to Vulkan style (0 - 1).
real_t *m = &columns[0][0];
m[0] = 1;
@@ -732,11 +733,11 @@ void Projection::set_depth_correction(bool p_flip_y) {
m[7] = 0.0;
m[8] = 0.0;
m[9] = 0.0;
- m[10] = 0.5;
+ m[10] = p_remap_z ? (p_reverse_z ? -0.5 : 0.5) : (p_reverse_z ? -1.0 : 1.0);
m[11] = 0.0;
m[12] = 0.0;
m[13] = 0.0;
- m[14] = 0.5;
+ m[14] = p_remap_z ? 0.5 : 0.0;
m[15] = 1.0;
}
diff --git a/core/math/projection.h b/core/math/projection.h
index b98f636344..7bba9b337e 100644
--- a/core/math/projection.h
+++ b/core/math/projection.h
@@ -69,7 +69,7 @@ struct _NO_DISCARD_ Projection {
void set_identity();
void set_zero();
void set_light_bias();
- void set_depth_correction(bool p_flip_y = true);
+ void set_depth_correction(bool p_flip_y = true, bool p_reverse_z = true, bool p_remap_z = true);
void set_light_atlas_rect(const Rect2 &p_rect);
void set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov = false);
diff --git a/core/os/os.h b/core/os/os.h
index 4f0df1543f..370e724c53 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -170,6 +170,7 @@ public:
virtual Vector<String> get_system_font_path_for_text(const String &p_font_name, const String &p_text, const String &p_locale = String(), const String &p_script = String(), int p_weight = 400, int p_stretch = 100, bool p_italic = false) const { return Vector<String>(); };
virtual String get_executable_path() const;
virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) = 0;
+ virtual Dictionary execute_with_pipe(const String &p_path, const List<String> &p_arguments) { return Dictionary(); }
virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) = 0;
virtual Error create_instance(const List<String> &p_arguments, ProcessID *r_child_id = nullptr) { return create_process(get_executable_path(), p_arguments, r_child_id); };
virtual Error kill(const ProcessID &p_pid) = 0;
diff --git a/core/string/translation.cpp b/core/string/translation.cpp
index 0a0052d6cb..613edd11cd 100644
--- a/core/string/translation.cpp
+++ b/core/string/translation.cpp
@@ -776,9 +776,9 @@ void TranslationServer::set_property_translation(const Ref<Translation> &p_trans
property_translation = p_translation;
}
-StringName TranslationServer::property_translate(const StringName &p_message) const {
+StringName TranslationServer::property_translate(const StringName &p_message, const StringName &p_context) const {
if (property_translation.is_valid()) {
- StringName r = property_translation->get_message(p_message);
+ StringName r = property_translation->get_message(p_message, p_context);
if (r) {
return r;
}
diff --git a/core/string/translation.h b/core/string/translation.h
index 470ba88232..78d6721347 100644
--- a/core/string/translation.h
+++ b/core/string/translation.h
@@ -183,7 +183,7 @@ public:
StringName tool_translate(const StringName &p_message, const StringName &p_context = "") const;
StringName tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
void set_property_translation(const Ref<Translation> &p_translation);
- StringName property_translate(const StringName &p_message) const;
+ StringName property_translate(const StringName &p_message, const StringName &p_context = "") const;
void set_doc_translation(const Ref<Translation> &p_translation);
StringName doc_translate(const StringName &p_message, const StringName &p_context = "") const;
StringName doc_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
diff --git a/doc/classes/AStar2D.xml b/doc/classes/AStar2D.xml
index f10e80e048..c877b3de78 100644
--- a/doc/classes/AStar2D.xml
+++ b/doc/classes/AStar2D.xml
@@ -139,8 +139,10 @@
<return type="PackedInt64Array" />
<param index="0" name="from_id" type="int" />
<param index="1" name="to_id" type="int" />
+ <param index="2" name="allow_partial_path" type="bool" default="false" />
<description>
Returns an array with the IDs of the points that form the path found by AStar2D between the given points. The array is ordered from the starting point to the ending point of the path.
+ If there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached.
[codeblocks]
[gdscript]
var astar = AStar2D.new()
@@ -228,8 +230,10 @@
<return type="PackedVector2Array" />
<param index="0" name="from_id" type="int" />
<param index="1" name="to_id" type="int" />
+ <param index="2" name="allow_partial_path" type="bool" default="false" />
<description>
Returns an array with the points that are in the path found by AStar2D between the given points. The array is ordered from the starting point to the ending point of the path.
+ If there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached.
[b]Note:[/b] This method is not thread-safe. If called from a [Thread], it will return an empty [PackedVector2Array] and will print an error message.
</description>
</method>
diff --git a/doc/classes/AStar3D.xml b/doc/classes/AStar3D.xml
index e2afeef377..c8a80a9012 100644
--- a/doc/classes/AStar3D.xml
+++ b/doc/classes/AStar3D.xml
@@ -168,8 +168,10 @@
<return type="PackedInt64Array" />
<param index="0" name="from_id" type="int" />
<param index="1" name="to_id" type="int" />
+ <param index="2" name="allow_partial_path" type="bool" default="false" />
<description>
Returns an array with the IDs of the points that form the path found by AStar3D between the given points. The array is ordered from the starting point to the ending point of the path.
+ If there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached.
[codeblocks]
[gdscript]
var astar = AStar3D.new()
@@ -255,8 +257,10 @@
<return type="PackedVector3Array" />
<param index="0" name="from_id" type="int" />
<param index="1" name="to_id" type="int" />
+ <param index="2" name="allow_partial_path" type="bool" default="false" />
<description>
Returns an array with the points that are in the path found by AStar3D between the given points. The array is ordered from the starting point to the ending point of the path.
+ If there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached.
[b]Note:[/b] This method is not thread-safe. If called from a [Thread], it will return an empty [PackedVector3Array] and will print an error message.
</description>
</method>
diff --git a/doc/classes/AStarGrid2D.xml b/doc/classes/AStarGrid2D.xml
index 4501bec314..794e40fde9 100644
--- a/doc/classes/AStarGrid2D.xml
+++ b/doc/classes/AStarGrid2D.xml
@@ -75,16 +75,20 @@
<return type="Vector2i[]" />
<param index="0" name="from_id" type="Vector2i" />
<param index="1" name="to_id" type="Vector2i" />
+ <param index="2" name="allow_partial_path" type="bool" default="false" />
<description>
Returns an array with the IDs of the points that form the path found by AStar2D between the given points. The array is ordered from the starting point to the ending point of the path.
+ If there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached.
</description>
</method>
<method name="get_point_path">
<return type="PackedVector2Array" />
<param index="0" name="from_id" type="Vector2i" />
<param index="1" name="to_id" type="Vector2i" />
+ <param index="2" name="allow_partial_path" type="bool" default="false" />
<description>
Returns an array with the points that are in the path found by [AStarGrid2D] between the given points. The array is ordered from the starting point to the ending point of the path.
+ If there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached.
[b]Note:[/b] This method is not thread-safe. If called from a [Thread], it will return an empty [PackedVector3Array] and will print an error message.
</description>
</method>
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index ae0dc53f5d..9eb5f9e334 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -103,7 +103,7 @@
<param index="3" name="callback" type="Callable" />
<description>
Shows a text input dialog which uses the operating system's native look-and-feel. [param callback] should accept a single [String] parameter which contains the text field's contents.
- [b]Note:[/b] This method is implemented only on macOS and Windows.
+ [b]Note:[/b] This method is implemented if the display server has the [constant FEATURE_NATIVE_DIALOG_INPUT] feature. Supported platforms include macOS and Windows.
</description>
</method>
<method name="dialog_show">
@@ -114,7 +114,7 @@
<param index="3" name="callback" type="Callable" />
<description>
Shows a text dialog which uses the operating system's native look-and-feel. [param callback] should accept a single [int] parameter which corresponds to the index of the pressed button.
- [b]Note:[/b] This method is implemented only on macOS and Windows.
+ [b]Note:[/b] This method is implemented if the display server has the [constant FEATURE_NATIVE_DIALOG] feature. Supported platforms include macOS and Windows.
</description>
</method>
<method name="enable_for_stealing_focus">
@@ -138,7 +138,7 @@
Displays OS native dialog for selecting files or directories in the file system.
Each filter string in the [param filters] array should be formatted like this: [code]*.txt,*.doc;Text Files[/code]. The description text of the filter is optional and can be omitted. See also [member FileDialog.filters].
Callbacks have the following arguments: [code]status: bool, selected_paths: PackedStringArray, selected_filter_index: int[/code].
- [b]Note:[/b] This method is implemented if the display server has the [constant FEATURE_NATIVE_DIALOG] feature. Supported platforms include Linux (X11/Wayland), Windows, and macOS.
+ [b]Note:[/b] This method is implemented if the display server has the [constant FEATURE_NATIVE_DIALOG_FILE] feature. Supported platforms include Linux (X11/Wayland), Windows, and macOS.
[b]Note:[/b] [param current_directory] might be ignored.
[b]Note:[/b] On Linux, [param show_hidden] is ignored.
[b]Note:[/b] On macOS, native file dialogs have no title.
@@ -164,7 +164,7 @@
- [code]"values"[/code] - [PackedStringArray] of values. If empty, boolean option (check box) is used.
- [code]"default"[/code] - default selected option index ([int]) or default boolean value ([bool]).
Callbacks have the following arguments: [code]status: bool, selected_paths: PackedStringArray, selected_filter_index: int, selected_option: Dictionary[/code].
- [b]Note:[/b] This method is implemented if the display server has the [constant FEATURE_NATIVE_DIALOG] feature. Supported platforms include Linux (X11/Wayland), Windows, and macOS.
+ [b]Note:[/b] This method is implemented if the display server has the [constant FEATURE_NATIVE_DIALOG_FILE] feature. Supported platforms include Linux (X11/Wayland), Windows, and macOS.
[b]Note:[/b] [param current_directory] might be ignored.
[b]Note:[/b] On Linux (X11), [param show_hidden] is ignored.
[b]Note:[/b] On macOS, native file dialogs have no title.
@@ -1784,7 +1784,7 @@
Display server supports setting the mouse cursor shape to a custom image. [b]Windows, macOS, Linux (X11/Wayland), Web[/b]
</constant>
<constant name="FEATURE_NATIVE_DIALOG" value="9" enum="Feature">
- Display server supports spawning dialogs using the operating system's native look-and-feel. [b]Windows, macOS, Linux (X11/Wayland)[/b]
+ Display server supports spawning text dialogs using the operating system's native look-and-feel. See [method dialog_show]. [b]Windows, macOS[/b]
</constant>
<constant name="FEATURE_IME" value="10" enum="Feature">
Display server supports [url=https://en.wikipedia.org/wiki/Input_method]Input Method Editor[/url], which is commonly used for inputting Chinese/Japanese/Korean text. This is handled by the operating system, rather than by Godot. [b]Windows, macOS, Linux (X11)[/b]
@@ -1825,6 +1825,12 @@
<constant name="FEATURE_NATIVE_HELP" value="23" enum="Feature">
Display server supports native help system search callbacks. See [method help_set_search_callbacks].
</constant>
+ <constant name="FEATURE_NATIVE_DIALOG_INPUT" value="24" enum="Feature">
+ Display server supports spawning text input dialogs using the operating system's native look-and-feel. See [method dialog_input_text]. [b]Windows, macOS[/b]
+ </constant>
+ <constant name="FEATURE_NATIVE_DIALOG_FILE" value="25" enum="Feature">
+ Display server supports spawning dialogs for selecting files or directories using the operating system's native look-and-feel. See [method file_dialog_show] and [method file_dialog_with_options_show]. [b]Windows, macOS, Linux (X11/Wayland)[/b]
+ </constant>
<constant name="MOUSE_MODE_VISIBLE" value="0" enum="MouseMode">
Makes the mouse cursor visible if it is hidden.
</constant>
diff --git a/doc/classes/HeightMapShape3D.xml b/doc/classes/HeightMapShape3D.xml
index ba79cbc89a..7e3055b34e 100644
--- a/doc/classes/HeightMapShape3D.xml
+++ b/doc/classes/HeightMapShape3D.xml
@@ -6,6 +6,19 @@
<description>
A 3D heightmap shape, intended for use in physics. Usually used to provide a shape for a [CollisionShape3D]. This is useful for terrain, but it is limited as overhangs (such as caves) cannot be stored. Holes in a [HeightMapShape3D] are created by assigning very low values to points in the desired area.
[b]Performance:[/b] [HeightMapShape3D] is faster to check collisions against than [ConcavePolygonShape3D], but it is significantly slower than primitive shapes like [BoxShape3D].
+ A heightmap collision shape can also be build by using an [Image] reference:
+ [codeblocks]
+ [gdscript]
+ var heightmap_texture: Texture = ResourceLoader.load("res://heightmap_image.exr")
+ var heightmap_image: Image = heightmap_texture.get_image()
+ heightmap_image.convert(Image.FORMAT_RF)
+
+ var height_min: float = 0.0
+ var height_max: float = 10.0
+
+ update_map_data_from_image(heightmap_image, height_min, height_max)
+ [/gdscript]
+ [/codeblocks]
</description>
<tutorials>
</tutorials>
@@ -22,6 +35,17 @@
Returns the smallest height value found in [member map_data]. Recalculates only when [member map_data] changes.
</description>
</method>
+ <method name="update_map_data_from_image">
+ <return type="void" />
+ <param index="0" name="image" type="Image" />
+ <param index="1" name="height_min" type="float" />
+ <param index="2" name="height_max" type="float" />
+ <description>
+ Updates [member map_data] with data read from an [Image] reference. Automatically resizes heightmap [member map_width] and [member map_depth] to fit the full image width and height.
+ The image needs to be in either [constant Image.FORMAT_RF] (32 bit), [constant Image.FORMAT_RH] (16 bit), or [constant Image.FORMAT_R8] (8 bit).
+ Each image pixel is read in as a float on the range from [code]0.0[/code] (black pixel) to [code]1.0[/code] (white pixel). This range value gets remapped to [param height_min] and [param height_max] to form the final height value.
+ </description>
+ </method>
</methods>
<members>
<member name="map_data" type="PackedFloat32Array" setter="set_map_data" getter="get_map_data" default="PackedFloat32Array(0, 0, 0, 0)">
diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml
index e6fdd229bf..ae6cd9596c 100644
--- a/doc/classes/Node.xml
+++ b/doc/classes/Node.xml
@@ -795,8 +795,9 @@
<return type="void" />
<param index="0" name="node" type="Node" />
<param index="1" name="keep_groups" type="bool" default="false" />
+ <param index="2" name="keep_children" type="bool" default="true" />
<description>
- Replaces this node by the given [param node]. All children of this node are moved to [param node].
+ Replaces this node by the given [param node]. If [param keep_children] is [code]true[/code] all children of this node are moved to [param node].
If [param keep_groups] is [code]true[/code], the [param node] is added to the same groups that the replaced node is in (see [method add_to_group]).
[b]Warning:[/b] The replaced node is removed from the tree, but it is [b]not[/b] deleted. To prevent memory leaks, store a reference to the node in a variable, or use [method Object.free].
</description>
diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml
index be7394a30b..bd1bd9afa7 100644
--- a/doc/classes/OS.xml
+++ b/doc/classes/OS.xml
@@ -50,9 +50,9 @@
<param index="1" name="arguments" type="PackedStringArray" />
<param index="2" name="open_console" type="bool" default="false" />
<description>
- Creates a new process that runs independently of Godot. It will not terminate when Godot terminates. The path specified in [param path] must exist and be executable file or macOS .app bundle. Platform path resolution will be used. The [param arguments] are used in the given order and separated by a space.
+ Creates a new process that runs independently of Godot. It will not terminate when Godot terminates. The path specified in [param path] must exist and be an executable file or macOS [code].app[/code] bundle. The path is resolved based on the current platform. The [param arguments] are used in the given order and separated by a space.
On Windows, if [param open_console] is [code]true[/code] and the process is a console app, a new terminal window will be opened.
- If the process is successfully created, this method returns its process ID, which you can use to monitor the process (and potentially terminate it with [method kill]). Otherwise this method returns [code]-1[/code].
+ If the process is successfully created, this method returns its process ID, which you can use to monitor the process (and potentially terminate it with [method kill]). Otherwise, this method returns [code]-1[/code].
For example, running another instance of the project:
[codeblocks]
[gdscript]
@@ -63,7 +63,7 @@
[/csharp]
[/codeblocks]
See [method execute] if you wish to run an external command and retrieve the results.
- [b]Note:[/b] This method is implemented on Android, iOS, Linux, macOS and Windows.
+ [b]Note:[/b] This method is implemented on Android, Linux, macOS, and Windows.
[b]Note:[/b] On macOS, sandboxed applications are limited to run only embedded helper executables, specified during export or system .app bundle, system .app bundles will ignore arguments.
</description>
</method>
@@ -120,7 +120,7 @@
OS.Execute("CMD.exe", new string[] {"/C", "cd %TEMP% &amp;&amp; dir"}, output);
[/csharp]
[/codeblocks]
- [b]Note:[/b] This method is implemented on Android, iOS, Linux, macOS and Windows.
+ [b]Note:[/b] This method is implemented on Android, Linux, macOS, and Windows.
[b]Note:[/b] To execute a Windows command interpreter built-in command, specify [code]cmd.exe[/code] in [param path], [code]/c[/code] as the first argument, and the desired command as the second argument.
[b]Note:[/b] To execute a PowerShell built-in command, specify [code]powershell.exe[/code] in [param path], [code]-Command[/code] as the first argument, and the desired command as the second argument.
[b]Note:[/b] To execute a Unix shell built-in command, specify shell executable name in [param path], [code]-c[/code] as the first argument, and the desired command as the second argument.
@@ -128,6 +128,23 @@
[b]Note:[/b] On Android, system commands such as [code]dumpsys[/code] can only be run on a rooted device.
</description>
</method>
+ <method name="execute_with_pipe">
+ <return type="Dictionary" />
+ <param index="0" name="path" type="String" />
+ <param index="1" name="arguments" type="PackedStringArray" />
+ <description>
+ Creates a new process that runs independently of Godot with redirected IO. It will not terminate when Godot terminates. The path specified in [param path] must exist and be an executable file or macOS [code].app[/code] bundle. The path is resolved based on the current platform. The [param arguments] are used in the given order and separated by a space.
+ If the process cannot be created, this method returns an empty [Dictionary]. Otherwise, this method returns a [Dictionary] with the following keys:
+ - [code]"stdio"[/code] - [FileAccess] to access the process stdin and stdout pipes (read/write).
+ - [code]"stderr"[/code] - [FileAccess] to access the process stderr pipe (read only).
+ - [code]"pid"[/code] - Process ID as an [int], which you can use to monitor the process (and potentially terminate it with [method kill]).
+ [b]Note:[/b] This method is implemented on Android, Linux, macOS, and Windows.
+ [b]Note:[/b] To execute a Windows command interpreter built-in command, specify [code]cmd.exe[/code] in [param path], [code]/c[/code] as the first argument, and the desired command as the second argument.
+ [b]Note:[/b] To execute a PowerShell built-in command, specify [code]powershell.exe[/code] in [param path], [code]-Command[/code] as the first argument, and the desired command as the second argument.
+ [b]Note:[/b] To execute a Unix shell built-in command, specify shell executable name in [param path], [code]-c[/code] as the first argument, and the desired command as the second argument.
+ [b]Note:[/b] On macOS, sandboxed applications are limited to run only embedded helper executables, specified during export or system .app bundle, system .app bundles will ignore arguments.
+ </description>
+ </method>
<method name="find_keycode_from_string" qualifiers="const">
<return type="int" enum="Key" />
<param index="0" name="string" type="String" />
diff --git a/doc/classes/PhysicsServer3D.xml b/doc/classes/PhysicsServer3D.xml
index 4735091f20..e40d73862b 100644
--- a/doc/classes/PhysicsServer3D.xml
+++ b/doc/classes/PhysicsServer3D.xml
@@ -740,7 +740,7 @@
<param index="1" name="axis" type="int" enum="Vector3.Axis" />
<param index="2" name="flag" type="int" enum="PhysicsServer3D.G6DOFJointAxisFlag" />
<description>
- Gets a generic_6_DOF_joint flag (see [enum G6DOFJointAxisFlag] constants).
+ Returns the value of a generic 6DOF joint flag. See [enum G6DOFJointAxisFlag] for the list of available flags.
</description>
</method>
<method name="generic_6dof_joint_get_param" qualifiers="const">
@@ -749,7 +749,7 @@
<param index="1" name="axis" type="int" enum="Vector3.Axis" />
<param index="2" name="param" type="int" enum="PhysicsServer3D.G6DOFJointAxisParam" />
<description>
- Gets a generic_6_DOF_joint parameter (see [enum G6DOFJointAxisParam] constants).
+ Returns the value of a generic 6DOF joint parameter. See [enum G6DOFJointAxisParam] for the list of available parameters.
</description>
</method>
<method name="generic_6dof_joint_set_flag">
@@ -759,7 +759,7 @@
<param index="2" name="flag" type="int" enum="PhysicsServer3D.G6DOFJointAxisFlag" />
<param index="3" name="enable" type="bool" />
<description>
- Sets a generic_6_DOF_joint flag (see [enum G6DOFJointAxisFlag] constants).
+ Sets the value of a given generic 6DOF joint flag. See [enum G6DOFJointAxisFlag] for the list of available flags.
</description>
</method>
<method name="generic_6dof_joint_set_param">
@@ -769,7 +769,7 @@
<param index="2" name="param" type="int" enum="PhysicsServer3D.G6DOFJointAxisParam" />
<param index="3" name="value" type="float" />
<description>
- Sets a generic_6_DOF_joint parameter (see [enum G6DOFJointAxisParam] constants).
+ Sets the value of a given generic 6DOF joint parameter. See [enum G6DOFJointAxisParam] for the list of available parameters.
</description>
</method>
<method name="get_process_info">
@@ -876,6 +876,7 @@
<param index="3" name="body_B" type="RID" />
<param index="4" name="local_ref_B" type="Transform3D" />
<description>
+ Make the joint a generic six degrees of freedom (6DOF) joint. Use [method generic_6dof_joint_set_flag] and [method generic_6dof_joint_set_param] to set the joint's flags and parameters respectively.
</description>
</method>
<method name="joint_make_hinge">
@@ -1497,6 +1498,12 @@
<constant name="G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT" value="6" enum="G6DOFJointAxisParam">
The maximum force that the linear motor can apply while trying to reach the target velocity.
</constant>
+ <constant name="G6DOF_JOINT_LINEAR_SPRING_STIFFNESS" value="7" enum="G6DOFJointAxisParam">
+ </constant>
+ <constant name="G6DOF_JOINT_LINEAR_SPRING_DAMPING" value="8" enum="G6DOFJointAxisParam">
+ </constant>
+ <constant name="G6DOF_JOINT_LINEAR_SPRING_EQUILIBRIUM_POINT" value="9" enum="G6DOFJointAxisParam">
+ </constant>
<constant name="G6DOF_JOINT_ANGULAR_LOWER_LIMIT" value="10" enum="G6DOFJointAxisParam">
The minimum rotation in negative direction to break loose and rotate around the axes.
</constant>
@@ -1524,18 +1531,34 @@
<constant name="G6DOF_JOINT_ANGULAR_MOTOR_FORCE_LIMIT" value="18" enum="G6DOFJointAxisParam">
Maximum acceleration for the motor at the axes.
</constant>
+ <constant name="G6DOF_JOINT_ANGULAR_SPRING_STIFFNESS" value="19" enum="G6DOFJointAxisParam">
+ </constant>
+ <constant name="G6DOF_JOINT_ANGULAR_SPRING_DAMPING" value="20" enum="G6DOFJointAxisParam">
+ </constant>
+ <constant name="G6DOF_JOINT_ANGULAR_SPRING_EQUILIBRIUM_POINT" value="21" enum="G6DOFJointAxisParam">
+ </constant>
+ <constant name="G6DOF_JOINT_MAX" value="22" enum="G6DOFJointAxisParam">
+ Represents the size of the [enum G6DOFJointAxisParam] enum.
+ </constant>
<constant name="G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT" value="0" enum="G6DOFJointAxisFlag">
If set, linear motion is possible within the given limits.
</constant>
<constant name="G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT" value="1" enum="G6DOFJointAxisFlag">
If set, rotational motion is possible.
</constant>
+ <constant name="G6DOF_JOINT_FLAG_ENABLE_ANGULAR_SPRING" value="2" enum="G6DOFJointAxisFlag">
+ </constant>
+ <constant name="G6DOF_JOINT_FLAG_ENABLE_LINEAR_SPRING" value="3" enum="G6DOFJointAxisFlag">
+ </constant>
<constant name="G6DOF_JOINT_FLAG_ENABLE_MOTOR" value="4" enum="G6DOFJointAxisFlag">
If set, there is a rotational motor across these axes.
</constant>
<constant name="G6DOF_JOINT_FLAG_ENABLE_LINEAR_MOTOR" value="5" enum="G6DOFJointAxisFlag">
If set, there is a linear motor on this axis that targets a specific velocity.
</constant>
+ <constant name="G6DOF_JOINT_FLAG_MAX" value="6" enum="G6DOFJointAxisFlag">
+ Represents the size of the [enum G6DOFJointAxisFlag] enum.
+ </constant>
<constant name="SHAPE_WORLD_BOUNDARY" value="0" enum="ShapeType">
The [Shape3D] is a [WorldBoundaryShape3D].
</constant>
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 48ac96a25f..4d3e838bb1 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -2643,6 +2643,9 @@
The [url=https://en.wikipedia.org/wiki/Bounding_volume_hierarchy]Bounding Volume Hierarchy[/url] quality to use when rendering the occlusion culling buffer. Higher values will result in more accurate occlusion culling, at the cost of higher CPU usage. See also [member rendering/occlusion_culling/occlusion_rays_per_thread].
[b]Note:[/b] This property is only read when the project starts. To adjust the BVH build quality at runtime, use [method RenderingServer.viewport_set_occlusion_culling_build_quality].
</member>
+ <member name="rendering/occlusion_culling/jitter_projection" type="bool" setter="" getter="" default="true">
+ If [code]true[/code], the projection used for rendering the occlusion buffer will be jittered. This can help prevent objects being incorrectly culled when visible through small gaps.
+ </member>
<member name="rendering/occlusion_culling/occlusion_rays_per_thread" type="int" setter="" getter="" default="512">
The number of occlusion rays traced per CPU thread. Higher values will result in more accurate occlusion culling, at the cost of higher CPU usage. The occlusion culling buffer's pixel count is roughly equal to [code]occlusion_rays_per_thread * number_of_logical_cpu_cores[/code], so it will depend on the system's CPU. Therefore, CPUs with fewer cores will use a lower resolution to attempt keeping performance costs even across devices. See also [member rendering/occlusion_culling/bvh_build_quality].
[b]Note:[/b] This property is only read when the project starts. To adjust the number of occlusion rays traced per thread at runtime, use [method RenderingServer.viewport_set_occlusion_rays_per_thread].
diff --git a/doc/classes/TileData.xml b/doc/classes/TileData.xml
index c5b86f079e..91df90580c 100644
--- a/doc/classes/TileData.xml
+++ b/doc/classes/TileData.xml
@@ -93,7 +93,7 @@
<return type="int" />
<param index="0" name="peering_bit" type="int" enum="TileSet.CellNeighbor" />
<description>
- Returns the tile's terrain bit for the given [param peering_bit] direction.
+ Returns the tile's terrain bit for the given [param peering_bit] direction. To check that a direction is valid, use [method is_valid_terrain_peering_bit].
</description>
</method>
<method name="is_collision_polygon_one_way" qualifiers="const">
@@ -104,6 +104,13 @@
Returns whether one-way collisions are enabled for the polygon at index [param polygon_index] for TileSet physics layer with index [param layer_id].
</description>
</method>
+ <method name="is_valid_terrain_peering_bit" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="peering_bit" type="int" enum="TileSet.CellNeighbor" />
+ <description>
+ Returns whether the given [param peering_bit] direction is valid for this tile.
+ </description>
+ </method>
<method name="remove_collision_polygon">
<return type="void" />
<param index="0" name="layer_id" type="int" />
@@ -200,7 +207,7 @@
<param index="0" name="peering_bit" type="int" enum="TileSet.CellNeighbor" />
<param index="1" name="terrain" type="int" />
<description>
- Sets the tile's terrain bit for the given [param peering_bit] direction.
+ Sets the tile's terrain bit for the given [param peering_bit] direction. To check that a direction is valid, use [method is_valid_terrain_peering_bit].
</description>
</method>
</methods>
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index 29cfa251d6..b8cc3928eb 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -823,6 +823,11 @@ void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection,
} else {
camera = p_projection;
}
+
+ Projection correction;
+ correction.set_depth_correction(false, true, false);
+ camera = correction * camera;
+
Basis sky_transform = environment_get_sky_orientation(p_env);
sky_transform.invert();
sky_transform = sky_transform * p_transform.basis;
@@ -933,7 +938,7 @@ void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_p
Projection cm;
cm.set_perspective(90, 1, 0.01, 10.0);
Projection correction;
- correction.columns[1][1] = -1.0;
+ correction.set_depth_correction(true, true, false);
cm = correction * cm;
bool success = material_storage->shaders.sky_shader.version_bind_shader(shader_data->version, SkyShaderGLES3::MODE_CUBEMAP);
@@ -1546,7 +1551,7 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const
// Needs to be called after _setup_lights so that directional_light_count is accurate.
void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_data, bool p_no_fog, const Size2i &p_screen_size, bool p_flip_y, const Color &p_default_bg_color, bool p_pancake_shadows, float p_shadow_bias) {
Projection correction;
- correction.columns[1][1] = p_flip_y ? -1.0 : 1.0;
+ correction.set_depth_correction(p_flip_y, true, false);
Projection projection = correction * p_render_data->cam_projection;
//store camera into ubo
GLES3::MaterialStorage::store_camera(projection, scene_state.ubo.projection_matrix);
@@ -1801,7 +1806,9 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b
shadow_data.blend_splits = uint32_t((shadow_mode != RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL) && light_storage->light_directional_get_blend_splits(base));
for (int j = 0; j < 4; j++) {
Rect2 atlas_rect = li->shadow_transform[j].atlas_rect;
- Projection matrix = li->shadow_transform[j].camera;
+ Projection correction;
+ correction.set_depth_correction(false, true, false);
+ Projection matrix = correction * li->shadow_transform[j].camera;
float split = li->shadow_transform[MIN(limit, j)].split;
Projection bias;
@@ -2027,7 +2034,9 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b
Projection bias;
bias.set_light_bias();
- Projection cm = li->shadow_transform[0].camera;
+ Projection correction;
+ correction.set_depth_correction(false, true, false);
+ Projection cm = correction * li->shadow_transform[0].camera;
Projection shadow_mtx = bias * cm * modelview;
GLES3::MaterialStorage::store_camera(shadow_mtx, shadow_data.shadow_matrix);
}
@@ -2274,11 +2283,11 @@ void RasterizerSceneGLES3::_render_shadow_pass(RID p_light, RID p_shadow_atlas,
scene_state.reset_gl_state();
scene_state.enable_gl_depth_test(true);
scene_state.enable_gl_depth_draw(true);
- glDepthFunc(GL_LESS);
+ glDepthFunc(GL_GREATER);
glColorMask(0, 0, 0, 0);
glDrawBuffers(0, nullptr);
- RasterizerGLES3::clear_depth(1.0);
+ RasterizerGLES3::clear_depth(0.0);
if (needs_clear) {
glClear(GL_DEPTH_BUFFER_BIT);
}
@@ -2515,7 +2524,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
Projection projection = render_data.cam_projection;
if (render_data.reflection_probe.is_valid()) {
Projection correction;
- correction.columns[1][1] = -1.0;
+ correction.set_depth_correction(true, true, false);
projection = correction * render_data.cam_projection;
}
@@ -2554,11 +2563,11 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
scene_state.enable_gl_depth_test(true);
scene_state.enable_gl_depth_draw(true);
scene_state.enable_gl_blend(false);
- glDepthFunc(GL_LEQUAL);
+ glDepthFunc(GL_GEQUAL);
scene_state.enable_gl_scissor_test(false);
glColorMask(0, 0, 0, 0);
- RasterizerGLES3::clear_depth(1.0);
+ RasterizerGLES3::clear_depth(0.0);
glClear(GL_DEPTH_BUFFER_BIT);
glDrawBuffers(0, nullptr);
@@ -2590,7 +2599,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
scene_state.enable_gl_scissor_test(false);
scene_state.enable_gl_depth_test(true);
scene_state.enable_gl_depth_draw(true);
- glDepthFunc(GL_LEQUAL);
+ glDepthFunc(GL_GEQUAL);
{
GLuint db = GL_COLOR_ATTACHMENT0;
@@ -2598,7 +2607,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
}
if (!fb_cleared) {
- RasterizerGLES3::clear_depth(1.0);
+ RasterizerGLES3::clear_depth(0.0);
glClear(GL_DEPTH_BUFFER_BIT);
}
@@ -3559,12 +3568,12 @@ void RasterizerSceneGLES3::render_particle_collider_heightfield(RID p_collider,
scene_state.reset_gl_state();
scene_state.enable_gl_depth_test(true);
scene_state.enable_gl_depth_draw(true);
- glDepthFunc(GL_LESS);
+ glDepthFunc(GL_GREATER);
glDrawBuffers(0, nullptr);
glColorMask(0, 0, 0, 0);
- RasterizerGLES3::clear_depth(1.0);
+ RasterizerGLES3::clear_depth(0.0);
glClear(GL_DEPTH_BUFFER_BIT);
@@ -3605,7 +3614,7 @@ void RasterizerSceneGLES3::_render_uv2(const PagedArray<RenderGeometryInstance *
scene_state.reset_gl_state();
scene_state.enable_gl_depth_test(true);
scene_state.enable_gl_depth_draw(true);
- glDepthFunc(GL_LESS);
+ glDepthFunc(GL_GREATER);
TightLocalVector<GLenum> draw_buffers;
draw_buffers.push_back(GL_COLOR_ATTACHMENT0);
@@ -3738,7 +3747,7 @@ void RasterizerSceneGLES3::_render_buffers_debug_draw(Ref<RenderSceneBuffersGLES
copy_effects->copy_cube_to_rect(atlas_uv_rect);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
- glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, GL_GREATER);
} else {
glBindTexture(GL_TEXTURE_2D, shadow_tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
@@ -3746,7 +3755,7 @@ void RasterizerSceneGLES3::_render_buffers_debug_draw(Ref<RenderSceneBuffersGLES
copy_effects->copy_to_rect(atlas_uv_rect);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_GREATER);
}
}
}
@@ -3782,7 +3791,7 @@ void RasterizerSceneGLES3::_render_buffers_debug_draw(Ref<RenderSceneBuffersGLES
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_BLUE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ALPHA);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_GREATER);
glBindTexture(GL_TEXTURE_2D, 0);
}
}
diff --git a/drivers/gles3/shaders/cube_to_dp.glsl b/drivers/gles3/shaders/cube_to_dp.glsl
index 2384529a89..ec1982738a 100644
--- a/drivers/gles3/shaders/cube_to_dp.glsl
+++ b/drivers/gles3/shaders/cube_to_dp.glsl
@@ -95,6 +95,6 @@ void main() {
float depth_fix = 1.0 / dot(normal, unorm);
depth = 2.0 * depth - 1.0;
- float linear_depth = 2.0 * z_near * z_far / (z_far + z_near - depth * (z_far - z_near));
- gl_FragDepth = (linear_depth * depth_fix + bias) / z_far;
+ float linear_depth = 2.0 * z_near * z_far / (z_far + z_near + depth * (z_far - z_near));
+ gl_FragDepth = (z_far - (linear_depth * depth_fix + bias)) / z_far;
}
diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl
index d73407d674..2fdd4b0031 100644
--- a/drivers/gles3/shaders/scene.glsl
+++ b/drivers/gles3/shaders/scene.glsl
@@ -1710,7 +1710,7 @@ void main() {
#ifdef MODE_RENDER_DEPTH
#ifdef RENDER_SHADOWS_LINEAR
// Linearize the depth buffer if rendering cubemap shadows.
- gl_FragDepth = (length(vertex) + scene_data.shadow_bias) / scene_data.z_far;
+ gl_FragDepth = (scene_data.z_far - (length(vertex) + scene_data.shadow_bias)) / scene_data.z_far;
#endif
// Nothing happens, so a tree-ssa optimizer will result in no fragment shader :)
@@ -1914,7 +1914,7 @@ void main() {
float omni_shadow = 1.0f;
#ifndef SHADOWS_DISABLED
vec3 light_ray = ((positional_shadows[positional_shadow_index].shadow_matrix * vec4(shadow_coord.xyz, 1.0))).xyz;
- omni_shadow = texture(omni_shadow_texture, vec4(light_ray, length(light_ray) * omni_lights[omni_light_index].inv_radius));
+ omni_shadow = texture(omni_shadow_texture, vec4(light_ray, 1.0 - length(light_ray) * omni_lights[omni_light_index].inv_radius));
omni_shadow = mix(1.0, omni_shadow, omni_lights[omni_light_index].shadow_opacity);
#endif // SHADOWS_DISABLED
light_process_omni(omni_light_index, vertex, view, normal, f0, roughness, metallic, omni_shadow, albedo, alpha,
diff --git a/drivers/gles3/shaders/sky.glsl b/drivers/gles3/shaders/sky.glsl
index b10ea12e6e..6c33bf7123 100644
--- a/drivers/gles3/shaders/sky.glsl
+++ b/drivers/gles3/shaders/sky.glsl
@@ -28,7 +28,7 @@ void main() {
// We're doing clockwise culling so flip the order
uv_interp = vec2(vertex_attrib.x, vertex_attrib.y * -1.0);
#endif
- gl_Position = vec4(uv_interp, 1.0, 1.0);
+ gl_Position = vec4(uv_interp, -1.0, 1.0);
}
/* clang-format off */
@@ -139,9 +139,11 @@ void main() {
vec3 cube_normal;
#ifdef USE_MULTIVIEW
// In multiview our projection matrices will contain positional and rotational offsets that we need to properly unproject.
- vec4 unproject = vec4(uv_interp.x, uv_interp.y, 1.0, 1.0);
+ vec4 unproject = vec4(uv_interp.xy, -1.0, 1.0); // unproject at the far plane
vec4 unprojected = multiview_data.inv_projection_matrix_view[ViewIndex] * unproject;
cube_normal = unprojected.xyz / unprojected.w;
+
+ // Unproject will give us the position between the eyes, need to re-offset.
cube_normal += multiview_data.eye_offset[ViewIndex].xyz;
#else
cube_normal.z = -1.0;
diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp
index f5d1f8dabd..d7d77c6b8f 100644
--- a/drivers/gles3/storage/light_storage.cpp
+++ b/drivers/gles3/storage/light_storage.cpp
@@ -1020,7 +1020,7 @@ bool LightStorage::_shadow_atlas_find_shadow(ShadowAtlas *shadow_atlas, int *p_i
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
- glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, GL_GREATER);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X, texture_id, 0);
@@ -1042,7 +1042,7 @@ bool LightStorage::_shadow_atlas_find_shadow(ShadowAtlas *shadow_atlas, int *p_i
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_GREATER);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texture_id, 0);
@@ -1128,14 +1128,14 @@ void LightStorage::update_directional_shadow_atlas() {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_GREATER);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, directional_shadow.depth, 0);
}
glUseProgram(0);
glDepthMask(GL_TRUE);
glBindFramebuffer(GL_FRAMEBUFFER, directional_shadow.fbo);
- RasterizerGLES3::clear_depth(1.0);
+ RasterizerGLES3::clear_depth(0.0);
glClear(GL_DEPTH_BUFFER_BIT);
glBindTexture(GL_TEXTURE_2D, 0);
diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp
index c955b3f708..6f32e4d49d 100644
--- a/drivers/gles3/storage/texture_storage.cpp
+++ b/drivers/gles3/storage/texture_storage.cpp
@@ -1077,7 +1077,7 @@ Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const {
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glDisable(GL_BLEND);
- glDepthFunc(GL_LEQUAL);
+ glDepthFunc(GL_GEQUAL);
glColorMask(1, 1, 1, 1);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture->tex_id);
diff --git a/drivers/unix/file_access_unix_pipe.cpp b/drivers/unix/file_access_unix_pipe.cpp
new file mode 100644
index 0000000000..5d9a27ad05
--- /dev/null
+++ b/drivers/unix/file_access_unix_pipe.cpp
@@ -0,0 +1,185 @@
+/**************************************************************************/
+/* file_access_unix_pipe.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 "file_access_unix_pipe.h"
+
+#if defined(UNIX_ENABLED)
+
+#include "core/os/os.h"
+#include "core/string/print_string.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+Error FileAccessUnixPipe::open_existing(int p_rfd, int p_wfd) {
+ // Open pipe using handles created by pipe(fd) call in the OS.execute_with_pipe.
+ _close();
+
+ path_src = String();
+ unlink_on_close = false;
+ ERR_FAIL_COND_V_MSG(fd[0] >= 0 || fd[1] >= 0, ERR_ALREADY_IN_USE, "Pipe is already in use.");
+ fd[0] = p_rfd;
+ fd[1] = p_wfd;
+
+ last_error = OK;
+ return OK;
+}
+
+Error FileAccessUnixPipe::open_internal(const String &p_path, int p_mode_flags) {
+ _close();
+
+ path_src = p_path;
+ ERR_FAIL_COND_V_MSG(fd[0] >= 0 || fd[1] >= 0, ERR_ALREADY_IN_USE, "Pipe is already in use.");
+
+ path = String("/tmp/") + p_path.replace("pipe://", "").replace("/", "_");
+ struct stat st = {};
+ int err = stat(path.utf8().get_data(), &st);
+ if (err) {
+ if (mkfifo(path.utf8().get_data(), 0666) != 0) {
+ last_error = ERR_FILE_CANT_OPEN;
+ return last_error;
+ }
+ unlink_on_close = true;
+ } else {
+ ERR_FAIL_COND_V_MSG(!S_ISFIFO(st.st_mode), ERR_ALREADY_IN_USE, "Pipe name is already used by file.");
+ }
+
+ int f = ::open(path.utf8().get_data(), O_RDWR | O_CLOEXEC);
+ if (f < 0) {
+ switch (errno) {
+ case ENOENT: {
+ last_error = ERR_FILE_NOT_FOUND;
+ } break;
+ default: {
+ last_error = ERR_FILE_CANT_OPEN;
+ } break;
+ }
+ return last_error;
+ }
+
+ // Set close on exec to avoid leaking it to subprocesses.
+ fd[0] = f;
+ fd[1] = f;
+
+ last_error = OK;
+ return OK;
+}
+
+void FileAccessUnixPipe::_close() {
+ if (fd[0] < 0) {
+ return;
+ }
+
+ if (fd[1] != fd[0]) {
+ ::close(fd[1]);
+ }
+ ::close(fd[0]);
+ fd[0] = -1;
+ fd[1] = -1;
+
+ if (unlink_on_close) {
+ ::unlink(path.utf8().ptr());
+ }
+ unlink_on_close = false;
+}
+
+bool FileAccessUnixPipe::is_open() const {
+ return (fd[0] >= 0 || fd[1] >= 0);
+}
+
+String FileAccessUnixPipe::get_path() const {
+ return path_src;
+}
+
+String FileAccessUnixPipe::get_path_absolute() const {
+ return path_src;
+}
+
+uint8_t FileAccessUnixPipe::get_8() const {
+ ERR_FAIL_COND_V_MSG(fd[0] < 0, 0, "Pipe must be opened before use.");
+
+ uint8_t b;
+ if (::read(fd[0], &b, 1) == 0) {
+ last_error = ERR_FILE_CANT_READ;
+ b = '\0';
+ } else {
+ last_error = OK;
+ }
+ return b;
+}
+
+uint64_t FileAccessUnixPipe::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
+ ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
+ ERR_FAIL_COND_V_MSG(fd[0] < 0, -1, "Pipe must be opened before use.");
+
+ uint64_t read = ::read(fd[0], p_dst, p_length);
+ if (read == p_length) {
+ last_error = ERR_FILE_CANT_READ;
+ } else {
+ last_error = OK;
+ }
+ return read;
+}
+
+Error FileAccessUnixPipe::get_error() const {
+ return last_error;
+}
+
+void FileAccessUnixPipe::store_8(uint8_t p_src) {
+ ERR_FAIL_COND_MSG(fd[1] < 0, "Pipe must be opened before use.");
+ if (::write(fd[1], &p_src, 1) != 1) {
+ last_error = ERR_FILE_CANT_WRITE;
+ } else {
+ last_error = OK;
+ }
+}
+
+void FileAccessUnixPipe::store_buffer(const uint8_t *p_src, uint64_t p_length) {
+ ERR_FAIL_COND_MSG(fd[1] < 0, "Pipe must be opened before use.");
+ ERR_FAIL_COND(!p_src && p_length > 0);
+ if (::write(fd[1], p_src, p_length) != (ssize_t)p_length) {
+ last_error = ERR_FILE_CANT_WRITE;
+ } else {
+ last_error = OK;
+ }
+}
+
+void FileAccessUnixPipe::close() {
+ _close();
+}
+
+FileAccessUnixPipe::~FileAccessUnixPipe() {
+ _close();
+}
+
+#endif // UNIX_ENABLED
diff --git a/drivers/unix/file_access_unix_pipe.h b/drivers/unix/file_access_unix_pipe.h
new file mode 100644
index 0000000000..d14f897d8f
--- /dev/null
+++ b/drivers/unix/file_access_unix_pipe.h
@@ -0,0 +1,96 @@
+/**************************************************************************/
+/* file_access_unix_pipe.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 FILE_ACCESS_UNIX_PIPE_H
+#define FILE_ACCESS_UNIX_PIPE_H
+
+#include "core/io/file_access.h"
+#include "core/os/memory.h"
+
+#include <stdio.h>
+
+#if defined(UNIX_ENABLED)
+
+class FileAccessUnixPipe : public FileAccess {
+ bool unlink_on_close = false;
+
+ int fd[2] = { -1, -1 };
+
+ mutable Error last_error = OK;
+ String path;
+ String path_src;
+
+ void _close();
+
+public:
+ Error open_existing(int p_rfd, int p_wfd);
+ virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file
+
+ virtual bool is_open() const override; ///< true when file is open
+
+ virtual String get_path() const override; /// returns the path for the current open file
+ virtual String get_path_absolute() const override; /// returns the absolute path for the current open file
+
+ virtual void seek(uint64_t p_position) override {}
+ virtual void seek_end(int64_t p_position = 0) override {}
+ virtual uint64_t get_position() const override { return 0; }
+ virtual uint64_t get_length() const override { return 0; }
+
+ virtual bool eof_reached() const override { return false; }
+
+ virtual uint8_t get_8() const override; ///< get a byte
+ virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
+
+ virtual Error get_error() const override; ///< get last error
+
+ virtual void flush() override {}
+ virtual void store_8(uint8_t p_src) override; ///< store a byte
+ virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
+
+ virtual bool file_exists(const String &p_path) override { return false; }
+
+ virtual uint64_t _get_modified_time(const String &p_file) override { return 0; }
+ virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; }
+ virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return ERR_UNAVAILABLE; }
+
+ virtual bool _get_hidden_attribute(const String &p_file) override { return false; }
+ virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; }
+ virtual bool _get_read_only_attribute(const String &p_file) override { return false; }
+ virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; }
+
+ virtual void close() override;
+
+ FileAccessUnixPipe() {}
+ virtual ~FileAccessUnixPipe();
+};
+
+#endif // UNIX_ENABLED
+
+#endif // FILE_ACCESS_UNIX_PIPE_H
diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp
index 74b703b09e..597e41fd22 100644
--- a/drivers/unix/os_unix.cpp
+++ b/drivers/unix/os_unix.cpp
@@ -37,6 +37,7 @@
#include "core/debugger/script_debugger.h"
#include "drivers/unix/dir_access_unix.h"
#include "drivers/unix/file_access_unix.h"
+#include "drivers/unix/file_access_unix_pipe.h"
#include "drivers/unix/net_socket_posix.h"
#include "drivers/unix/thread_posix.h"
#include "servers/rendering_server.h"
@@ -160,6 +161,7 @@ void OS_Unix::initialize_core() {
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_RESOURCES);
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_USERDATA);
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_FILESYSTEM);
+ FileAccess::make_default<FileAccessUnixPipe>(FileAccess::ACCESS_PIPE);
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_RESOURCES);
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_USERDATA);
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_FILESYSTEM);
@@ -489,6 +491,106 @@ Dictionary OS_Unix::get_memory_info() const {
return meminfo;
}
+Dictionary OS_Unix::execute_with_pipe(const String &p_path, const List<String> &p_arguments) {
+#define CLEAN_PIPES \
+ if (pipe_in[0] >= 0) { \
+ ::close(pipe_in[0]); \
+ } \
+ if (pipe_in[1] >= 0) { \
+ ::close(pipe_in[1]); \
+ } \
+ if (pipe_out[0] >= 0) { \
+ ::close(pipe_out[0]); \
+ } \
+ if (pipe_out[1] >= 0) { \
+ ::close(pipe_out[1]); \
+ } \
+ if (pipe_err[0] >= 0) { \
+ ::close(pipe_err[0]); \
+ } \
+ if (pipe_err[1] >= 0) { \
+ ::close(pipe_err[1]); \
+ }
+
+ Dictionary ret;
+#ifdef __EMSCRIPTEN__
+ // Don't compile this code at all to avoid undefined references.
+ // Actual virtual call goes to OS_Web.
+ ERR_FAIL_V(ret);
+#else
+ // Create pipes.
+ int pipe_in[2] = { -1, -1 };
+ int pipe_out[2] = { -1, -1 };
+ int pipe_err[2] = { -1, -1 };
+
+ ERR_FAIL_COND_V(pipe(pipe_in) != 0, ret);
+ if (pipe(pipe_out) != 0) {
+ CLEAN_PIPES
+ ERR_FAIL_V(ret);
+ }
+ if (pipe(pipe_err) != 0) {
+ CLEAN_PIPES
+ ERR_FAIL_V(ret);
+ }
+
+ // Create process.
+ pid_t pid = fork();
+ if (pid < 0) {
+ CLEAN_PIPES
+ ERR_FAIL_V(ret);
+ }
+
+ if (pid == 0) {
+ // The child process.
+ Vector<CharString> cs;
+ cs.push_back(p_path.utf8());
+ for (int i = 0; i < p_arguments.size(); i++) {
+ cs.push_back(p_arguments[i].utf8());
+ }
+
+ Vector<char *> args;
+ for (int i = 0; i < cs.size(); i++) {
+ args.push_back((char *)cs[i].get_data());
+ }
+ args.push_back(0);
+
+ ::close(STDIN_FILENO);
+ ::dup2(pipe_in[0], STDIN_FILENO);
+
+ ::close(STDOUT_FILENO);
+ ::dup2(pipe_out[1], STDOUT_FILENO);
+
+ ::close(STDERR_FILENO);
+ ::dup2(pipe_err[1], STDERR_FILENO);
+
+ CLEAN_PIPES
+
+ execvp(p_path.utf8().get_data(), &args[0]);
+ // The execvp() function only returns if an error occurs.
+ ERR_PRINT("Could not create child process: " + p_path);
+ raise(SIGKILL);
+ }
+ ::close(pipe_in[0]);
+ ::close(pipe_out[1]);
+ ::close(pipe_err[1]);
+
+ Ref<FileAccessUnixPipe> main_pipe;
+ main_pipe.instantiate();
+ main_pipe->open_existing(pipe_out[0], pipe_in[1]);
+
+ Ref<FileAccessUnixPipe> err_pipe;
+ err_pipe.instantiate();
+ err_pipe->open_existing(pipe_err[0], 0);
+
+ ret["stdio"] = main_pipe;
+ ret["stderr"] = err_pipe;
+ ret["pid"] = pid;
+
+#undef CLEAN_PIPES
+ return ret;
+#endif
+}
+
Error OS_Unix::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex, bool p_open_console) {
#ifdef __EMSCRIPTEN__
// Don't compile this code at all to avoid undefined references.
diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h
index d3393c98ec..abbaf9b8cd 100644
--- a/drivers/unix/os_unix.h
+++ b/drivers/unix/os_unix.h
@@ -76,6 +76,7 @@ public:
virtual Dictionary get_memory_info() const override;
virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) override;
+ virtual Dictionary execute_with_pipe(const String &p_path, const List<String> &p_arguments) override;
virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
virtual Error kill(const ProcessID &p_pid) override;
virtual int get_process_id() const override;
diff --git a/drivers/windows/file_access_windows_pipe.cpp b/drivers/windows/file_access_windows_pipe.cpp
new file mode 100644
index 0000000000..7902c8e1d8
--- /dev/null
+++ b/drivers/windows/file_access_windows_pipe.cpp
@@ -0,0 +1,159 @@
+/**************************************************************************/
+/* file_access_windows_pipe.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. */
+/**************************************************************************/
+
+#ifdef WINDOWS_ENABLED
+
+#include "file_access_windows_pipe.h"
+
+#include "core/os/os.h"
+#include "core/string/print_string.h"
+
+Error FileAccessWindowsPipe::open_existing(HANDLE p_rfd, HANDLE p_wfd) {
+ // Open pipe using handles created by CreatePipe(rfd, wfd, NULL, 4096) call in the OS.execute_with_pipe.
+ _close();
+
+ path_src = String();
+ ERR_FAIL_COND_V_MSG(fd[0] != 0 || fd[1] != 0, ERR_ALREADY_IN_USE, "Pipe is already in use.");
+ fd[0] = p_rfd;
+ fd[1] = p_wfd;
+
+ last_error = OK;
+ return OK;
+}
+
+Error FileAccessWindowsPipe::open_internal(const String &p_path, int p_mode_flags) {
+ _close();
+
+ path_src = p_path;
+ ERR_FAIL_COND_V_MSG(fd[0] != 0 || fd[1] != 0, ERR_ALREADY_IN_USE, "Pipe is already in use.");
+
+ path = String("\\\\.\\pipe\\LOCAL\\") + p_path.replace("pipe://", "").replace("/", "_");
+
+ HANDLE h = CreateFileW((LPCWSTR)path.utf16().get_data(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (h == INVALID_HANDLE_VALUE) {
+ h = CreateNamedPipeW((LPCWSTR)path.utf16().get_data(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 4096, 4096, 0, nullptr);
+ if (h == INVALID_HANDLE_VALUE) {
+ last_error = ERR_FILE_CANT_OPEN;
+ return last_error;
+ }
+ ConnectNamedPipe(h, NULL);
+ }
+ fd[0] = h;
+ fd[1] = h;
+
+ last_error = OK;
+ return OK;
+}
+
+void FileAccessWindowsPipe::_close() {
+ if (fd[0] == 0) {
+ return;
+ }
+ if (fd[1] != fd[0]) {
+ CloseHandle(fd[1]);
+ }
+ CloseHandle(fd[0]);
+ fd[0] = 0;
+ fd[1] = 0;
+}
+
+bool FileAccessWindowsPipe::is_open() const {
+ return (fd[0] != 0 || fd[1] != 0);
+}
+
+String FileAccessWindowsPipe::get_path() const {
+ return path_src;
+}
+
+String FileAccessWindowsPipe::get_path_absolute() const {
+ return path_src;
+}
+
+uint8_t FileAccessWindowsPipe::get_8() const {
+ ERR_FAIL_COND_V_MSG(fd[0] == 0, 0, "Pipe must be opened before use.");
+
+ uint8_t b;
+ if (!ReadFile(fd[0], &b, 1, nullptr, nullptr)) {
+ last_error = ERR_FILE_CANT_READ;
+ b = '\0';
+ } else {
+ last_error = OK;
+ }
+ return b;
+}
+
+uint64_t FileAccessWindowsPipe::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
+ ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
+ ERR_FAIL_COND_V_MSG(fd[0] == 0, -1, "Pipe must be opened before use.");
+
+ DWORD read = -1;
+ if (!ReadFile(fd[0], p_dst, p_length, &read, nullptr) || read != p_length) {
+ last_error = ERR_FILE_CANT_READ;
+ } else {
+ last_error = OK;
+ }
+ return read;
+}
+
+Error FileAccessWindowsPipe::get_error() const {
+ return last_error;
+}
+
+void FileAccessWindowsPipe::store_8(uint8_t p_src) {
+ ERR_FAIL_COND_MSG(fd[1] == 0, "Pipe must be opened before use.");
+ if (!WriteFile(fd[1], &p_src, 1, nullptr, nullptr)) {
+ last_error = ERR_FILE_CANT_WRITE;
+ } else {
+ last_error = OK;
+ }
+}
+
+void FileAccessWindowsPipe::store_buffer(const uint8_t *p_src, uint64_t p_length) {
+ ERR_FAIL_COND_MSG(fd[1] == 0, "Pipe must be opened before use.");
+ ERR_FAIL_COND(!p_src && p_length > 0);
+
+ DWORD read = -1;
+ bool ok = WriteFile(fd[1], p_src, p_length, &read, nullptr);
+ if (!ok || read != p_length) {
+ last_error = ERR_FILE_CANT_WRITE;
+ } else {
+ last_error = OK;
+ }
+}
+
+void FileAccessWindowsPipe::close() {
+ _close();
+}
+
+FileAccessWindowsPipe::~FileAccessWindowsPipe() {
+ _close();
+}
+
+#endif // WINDOWS_ENABLED
diff --git a/drivers/windows/file_access_windows_pipe.h b/drivers/windows/file_access_windows_pipe.h
new file mode 100644
index 0000000000..e6abe61fa3
--- /dev/null
+++ b/drivers/windows/file_access_windows_pipe.h
@@ -0,0 +1,95 @@
+/**************************************************************************/
+/* file_access_windows_pipe.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 FILE_ACCESS_WINDOWS_PIPE_H
+#define FILE_ACCESS_WINDOWS_PIPE_H
+
+#ifdef WINDOWS_ENABLED
+
+#include "core/io/file_access.h"
+#include "core/os/memory.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+class FileAccessWindowsPipe : public FileAccess {
+ HANDLE fd[2] = { 0, 0 };
+
+ mutable Error last_error = OK;
+
+ String path;
+ String path_src;
+
+ void _close();
+
+public:
+ Error open_existing(HANDLE p_rfd, HANDLE p_wfd);
+
+ virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file
+ virtual bool is_open() const override; ///< true when file is open
+
+ virtual String get_path() const override; /// returns the path for the current open file
+ virtual String get_path_absolute() const override; /// returns the absolute path for the current open file
+
+ virtual void seek(uint64_t p_position) override {}
+ virtual void seek_end(int64_t p_position = 0) override {}
+ virtual uint64_t get_position() const override { return 0; }
+ virtual uint64_t get_length() const override { return 0; }
+
+ virtual bool eof_reached() const override { return false; }
+
+ virtual uint8_t get_8() const override; ///< get a byte
+ virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
+
+ virtual Error get_error() const override; ///< get last error
+
+ virtual void flush() override {}
+ virtual void store_8(uint8_t p_src) override; ///< store a byte
+ virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
+
+ virtual bool file_exists(const String &p_name) override { return false; }
+
+ uint64_t _get_modified_time(const String &p_file) override { return 0; }
+ virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; }
+ virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return ERR_UNAVAILABLE; }
+
+ virtual bool _get_hidden_attribute(const String &p_file) override { return false; }
+ virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; }
+ virtual bool _get_read_only_attribute(const String &p_file) override { return false; }
+ virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; }
+
+ virtual void close() override;
+
+ FileAccessWindowsPipe() {}
+ virtual ~FileAccessWindowsPipe();
+};
+
+#endif // WINDOWS_ENABLED
+
+#endif // FILE_ACCESS_WINDOWS_PIPE_H
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index 99ba35a6d0..bec95d40c6 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -5006,12 +5006,17 @@ void AnimationTrackEditor::_new_track_node_selected(NodePath p_path) {
}
void AnimationTrackEditor::_add_track(int p_type) {
- if (!root) {
+ AnimationPlayer *ap = AnimationPlayerEditor::get_singleton()->get_player();
+ if (!ap) {
+ ERR_FAIL_EDMSG("No AnimationPlayer is currently being edited.");
+ }
+ Node *root_node = ap->get_node_or_null(ap->get_root_node());
+ if (!root_node) {
EditorNode::get_singleton()->show_warning(TTR("Not possible to add a new track without a root"));
return;
}
adding_track_type = p_type;
- pick_track->popup_scenetree_dialog();
+ pick_track->popup_scenetree_dialog(nullptr, root_node);
pick_track->get_filter_line_edit()->clear();
pick_track->get_filter_line_edit()->grab_focus();
}
diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp
index 362834d265..2db5b02d63 100644
--- a/editor/connections_dialog.cpp
+++ b/editor/connections_dialog.cpp
@@ -50,7 +50,6 @@
#include "scene/gui/option_button.h"
#include "scene/gui/popup_menu.h"
#include "scene/gui/spin_box.h"
-#include "scene/resources/packed_scene.h"
static Node *_find_first_script(Node *p_root, Node *p_node) {
if (p_node != p_root && p_node->get_owner() != p_root) {
@@ -617,7 +616,7 @@ void ConnectDialog::init(const ConnectionData &p_cd, const PackedStringArray &p_
signal_args = p_signal_args;
tree->set_selected(nullptr);
- tree->set_marked(source, true);
+ tree->set_marked(source);
if (p_cd.target) {
set_dst_node(static_cast<Node *>(p_cd.target));
@@ -1562,6 +1561,7 @@ ConnectionsDock::ConnectionsDock() {
vbc->add_child(search_box);
tree = memnew(ConnectionsDockTree);
+ tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
tree->set_columns(1);
tree->set_select_mode(Tree::SELECT_ROW);
tree->set_hide_root(true);
diff --git a/editor/debugger/editor_debugger_tree.cpp b/editor/debugger/editor_debugger_tree.cpp
index 7d3ea33fb5..17a23982a7 100644
--- a/editor/debugger/editor_debugger_tree.cpp
+++ b/editor/debugger/editor_debugger_tree.cpp
@@ -41,6 +41,7 @@
EditorDebuggerTree::EditorDebuggerTree() {
set_v_size_flags(SIZE_EXPAND_FILL);
set_allow_rmb_select(true);
+ set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
// Popup
item_menu = memnew(PopupMenu);
diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp
index a178f8e3ec..8c3cad3e89 100644
--- a/editor/dependency_editor.cpp
+++ b/editor/dependency_editor.cpp
@@ -66,7 +66,7 @@ void DependencyEditor::_load_pressed(Object *p_item, int p_cell, int p_button, M
List<String> ext;
ResourceLoader::get_recognized_extensions_for_type(ti->get_metadata(0), &ext);
for (const String &E : ext) {
- search->add_filter("*" + E);
+ search->add_filter("*." + E);
}
search->popup_file_dialog();
}
diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp
index 4b68a21cb9..6ab29c1850 100644
--- a/editor/editor_data.cpp
+++ b/editor/editor_data.cpp
@@ -722,15 +722,8 @@ bool EditorData::check_and_update_scene(int p_idx) {
new_scene->set_scene_file_path(edited_scene[p_idx].root->get_scene_file_path());
Node *old_root = edited_scene[p_idx].root;
- for (int i = 0; i < old_root->get_child_count(); i++) {
- memdelete(old_root->get_child(i));
- }
- old_root->replace_by(new_scene);
+ old_root->replace_by(new_scene, false, false);
memdelete(old_root);
- edited_scene.write[p_idx].root = new_scene;
- if (!new_scene->get_scene_file_path().is_empty()) {
- edited_scene.write[p_idx].path = new_scene->get_scene_file_path();
- }
edited_scene.write[p_idx].selection = new_selection;
return true;
diff --git a/editor/editor_feature_profile.cpp b/editor/editor_feature_profile.cpp
index 5236f9e254..541bcd5e02 100644
--- a/editor/editor_feature_profile.cpp
+++ b/editor/editor_feature_profile.cpp
@@ -619,8 +619,8 @@ void EditorFeatureProfileManager::_class_list_item_selected() {
if (!(E.usage & PROPERTY_USAGE_EDITOR)) {
continue;
}
- const String text = EditorPropertyNameProcessor::get_singleton()->process_name(name, text_style);
- const String tooltip = EditorPropertyNameProcessor::get_singleton()->process_name(name, tooltip_style);
+ const String text = EditorPropertyNameProcessor::get_singleton()->process_name(name, text_style, name, class_name);
+ const String tooltip = EditorPropertyNameProcessor::get_singleton()->process_name(name, tooltip_style, name, class_name);
TreeItem *property = property_list->create_item(properties);
property->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index c5ce815d6b..7919d61f26 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -57,7 +57,7 @@ bool EditorInspector::_property_path_matches(const String &p_property_path, cons
const Vector<String> prop_sections = p_property_path.split("/");
for (int i = 0; i < prop_sections.size(); i++) {
- if (p_filter.is_subsequence_ofn(EditorPropertyNameProcessor::get_singleton()->process_name(prop_sections[i], p_style))) {
+ if (p_filter.is_subsequence_ofn(EditorPropertyNameProcessor::get_singleton()->process_name(prop_sections[i], p_style, p_property_path))) {
return true;
}
}
@@ -3012,7 +3012,7 @@ void EditorInspector::update_tree() {
if ((p.usage & PROPERTY_USAGE_SCRIPT_VARIABLE) && name_style == EditorPropertyNameProcessor::STYLE_LOCALIZED) {
name_style = EditorPropertyNameProcessor::STYLE_CAPITALIZED;
}
- const String property_label_string = EditorPropertyNameProcessor::get_singleton()->process_name(name_override, name_style) + feature_tag;
+ const String property_label_string = EditorPropertyNameProcessor::get_singleton()->process_name(name_override, name_style, p.name, doc_name) + feature_tag;
// Remove the property from the path.
int idx = path.rfind("/");
@@ -3081,8 +3081,8 @@ void EditorInspector::update_tree() {
tooltip = EditorPropertyNameProcessor::get_singleton()->translate_group_name(component);
}
} else {
- label = EditorPropertyNameProcessor::get_singleton()->process_name(component, section_name_style);
- tooltip = EditorPropertyNameProcessor::get_singleton()->process_name(component, EditorPropertyNameProcessor::get_tooltip_style(section_name_style));
+ label = EditorPropertyNameProcessor::get_singleton()->process_name(component, section_name_style, p.name, doc_name);
+ tooltip = EditorPropertyNameProcessor::get_singleton()->process_name(component, EditorPropertyNameProcessor::get_tooltip_style(section_name_style), p.name, doc_name);
}
Color c = sscolor;
@@ -3145,7 +3145,7 @@ void EditorInspector::update_tree() {
editor_inspector_array = memnew(EditorInspectorArray(all_read_only));
String array_label = path.contains("/") ? path.substr(path.rfind("/") + 1) : path;
- array_label = EditorPropertyNameProcessor::get_singleton()->process_name(property_label_string, property_name_style);
+ array_label = EditorPropertyNameProcessor::get_singleton()->process_name(property_label_string, property_name_style, p.name, doc_name);
int page = per_array_page.has(array_element_prefix) ? per_array_page[array_element_prefix] : 0;
editor_inspector_array->setup_with_move_element_function(object, array_label, array_element_prefix, page, c, use_folding);
editor_inspector_array->connect("page_change_request", callable_mp(this, &EditorInspector::_page_change_request).bind(array_element_prefix));
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index edb6fee75c..db4d46cd18 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -3818,6 +3818,14 @@ void EditorNode::_set_current_scene_nocheck(int p_idx) {
}
}
+ if (editor_data.check_and_update_scene(p_idx)) {
+ if (!editor_data.get_scene_path(p_idx).is_empty()) {
+ editor_folding.load_scene_folding(editor_data.get_edited_scene_root(p_idx), editor_data.get_scene_path(p_idx));
+ }
+
+ EditorUndoRedoManager::get_singleton()->clear_history(false, editor_data.get_scene_history_id(p_idx));
+ }
+
Dictionary state = editor_data.restore_edited_scene_state(editor_selection, &editor_history);
_edit_current(true);
@@ -3827,14 +3835,6 @@ void EditorNode::_set_current_scene_nocheck(int p_idx) {
if (tabs_to_close.is_empty()) {
callable_mp(this, &EditorNode::_set_main_scene_state).call_deferred(state, get_edited_scene()); // Do after everything else is done setting up.
}
-
- if (editor_data.check_and_update_scene(p_idx)) {
- if (!editor_data.get_scene_path(p_idx).is_empty()) {
- editor_folding.load_scene_folding(editor_data.get_edited_scene_root(p_idx), editor_data.get_scene_path(p_idx));
- }
-
- EditorUndoRedoManager::get_singleton()->clear_history(false, editor_data.get_scene_history_id(p_idx));
- }
}
void EditorNode::setup_color_picker(ColorPicker *p_picker) {
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index b7380c9fc2..c6a6163a4e 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -2751,7 +2751,15 @@ void EditorPropertyNodePath::_node_assign() {
add_child(scene_tree);
scene_tree->connect("selected", callable_mp(this, &EditorPropertyNodePath::_node_selected));
}
- scene_tree->popup_scenetree_dialog();
+
+ Variant val = get_edited_property_value();
+ Node *n = nullptr;
+ if (val.get_type() == Variant::Type::NODE_PATH) {
+ n = get_base_node()->get_node_or_null(val);
+ } else {
+ n = Object::cast_to<Node>(val);
+ }
+ scene_tree->popup_scenetree_dialog(n, get_base_node());
}
void EditorPropertyNodePath::_update_menu() {
@@ -3184,7 +3192,6 @@ void EditorPropertyResource::_resource_changed(const Ref<Resource> &p_resource)
add_child(scene_tree);
scene_tree->connect("selected", callable_mp(this, &EditorPropertyResource::_viewport_selected));
}
-
scene_tree->popup_scenetree_dialog();
}
}
diff --git a/editor/editor_property_name_processor.cpp b/editor/editor_property_name_processor.cpp
index a892ea0f85..1318b84d60 100644
--- a/editor/editor_property_name_processor.cpp
+++ b/editor/editor_property_name_processor.cpp
@@ -91,7 +91,27 @@ String EditorPropertyNameProcessor::_capitalize_name(const String &p_name) const
return capitalized;
}
-String EditorPropertyNameProcessor::process_name(const String &p_name, Style p_style) const {
+StringName EditorPropertyNameProcessor::_get_context(const String &p_name, const String &p_property, const StringName &p_class) const {
+ if (p_property.is_empty() && p_class == StringName()) {
+ return StringName();
+ }
+ const HashMap<String, StringName> *context_map = translation_contexts.getptr(p_name);
+ if (context_map == nullptr) {
+ return StringName();
+ }
+ // It's expected that full property path is enough to distinguish between usages.
+ // In case a class name is needed, all usages should be prefixed with the class name.
+ const StringName *context = context_map->getptr(p_property);
+ if (context == nullptr && p_class != StringName()) {
+ context = context_map->getptr(String(p_class) + "::" + p_property);
+ }
+ if (context == nullptr) {
+ return StringName();
+ }
+ return *context;
+}
+
+String EditorPropertyNameProcessor::process_name(const String &p_name, Style p_style, const String &p_property, const StringName &p_class) const {
switch (p_style) {
case STYLE_RAW: {
return p_name;
@@ -104,7 +124,7 @@ String EditorPropertyNameProcessor::process_name(const String &p_name, Style p_s
case STYLE_LOCALIZED: {
const String capitalized = _capitalize_name(p_name);
if (TranslationServer::get_singleton()) {
- return TranslationServer::get_singleton()->property_translate(capitalized);
+ return TranslationServer::get_singleton()->property_translate(capitalized, _get_context(p_name, p_property, p_class));
}
return capitalized;
} break;
@@ -320,6 +340,25 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() {
"then",
"to",
});
+
+ // Translation context associated with a name.
+ // The second key is either:
+ // - `full/property/path`
+ // - `Class::full/property/path`
+ // In case a class name is needed to distinguish between usages, all usages should use the second format.
+ //
+ // The following initialization is parsed in `editor/translations/scripts/common.py` with a regex.
+ // The map name and value definition format should be kept synced with the regex.
+ translation_contexts["force"]["constant_force"] = "Physics";
+ translation_contexts["force"]["force/8_bit"] = "Enforce";
+ translation_contexts["force"]["force/mono"] = "Enforce";
+ translation_contexts["force"]["force/max_rate"] = "Enforce";
+ translation_contexts["force"]["force/max_rate_hz"] = "Enforce";
+ translation_contexts["normal"]["theme_override_styles/normal"] = "Ordinary";
+ translation_contexts["normal"]["TextureButton::texture_normal"] = "Ordinary";
+ translation_contexts["normal"]["Decal::texture_normal"] = "Geometry";
+ translation_contexts["normal"]["detail_normal"] = "Geometry";
+ translation_contexts["normal"]["normal"] = "Geometry";
}
EditorPropertyNameProcessor::~EditorPropertyNameProcessor() {
diff --git a/editor/editor_property_name_processor.h b/editor/editor_property_name_processor.h
index 8e3cecb45b..2c68423c84 100644
--- a/editor/editor_property_name_processor.h
+++ b/editor/editor_property_name_processor.h
@@ -42,9 +42,14 @@ class EditorPropertyNameProcessor : public Node {
HashMap<String, String> capitalize_string_remaps;
LocalVector<String> stop_words; // Exceptions that shouldn't be capitalized.
+ HashMap<String, HashMap<String, StringName>> translation_contexts;
+
// Capitalizes property path segments.
String _capitalize_name(const String &p_name) const;
+ // Returns the translation context for the given name.
+ StringName _get_context(const String &p_name, const String &p_property, const StringName &p_class) const;
+
public:
// Matches `interface/inspector/capitalize_properties` editor setting.
enum Style {
@@ -62,7 +67,8 @@ public:
static bool is_localization_available();
// Turns property path segment into the given style.
- String process_name(const String &p_name, Style p_style) const;
+ // `p_class` and `p_property` are only used for `STYLE_LOCALIZED`, associating the name with a translation context.
+ String process_name(const String &p_name, Style p_style, const String &p_property = "", const StringName &p_class = "") const;
// Translate plain text group names.
String translate_group_name(const String &p_name) const;
diff --git a/editor/editor_sectioned_inspector.cpp b/editor/editor_sectioned_inspector.cpp
index 8e8908faaf..f13af8e4ca 100644
--- a/editor/editor_sectioned_inspector.cpp
+++ b/editor/editor_sectioned_inspector.cpp
@@ -42,7 +42,7 @@ static bool _property_path_matches(const String &p_property_path, const String &
const Vector<String> sections = p_property_path.split("/");
for (int i = 0; i < sections.size(); i++) {
- if (p_filter.is_subsequence_ofn(EditorPropertyNameProcessor::get_singleton()->process_name(sections[i], p_style))) {
+ if (p_filter.is_subsequence_ofn(EditorPropertyNameProcessor::get_singleton()->process_name(sections[i], p_style, p_property_path))) {
return true;
}
}
@@ -278,8 +278,8 @@ void SectionedInspector::update_category_list() {
TreeItem *ms = sections->create_item(parent);
section_map[metasection] = ms;
- const String text = EditorPropertyNameProcessor::get_singleton()->process_name(sectionarr[i], name_style);
- const String tooltip = EditorPropertyNameProcessor::get_singleton()->process_name(sectionarr[i], tooltip_style);
+ const String text = EditorPropertyNameProcessor::get_singleton()->process_name(sectionarr[i], name_style, pi.name);
+ const String tooltip = EditorPropertyNameProcessor::get_singleton()->process_name(sectionarr[i], tooltip_style, pi.name);
ms->set_text(0, text);
ms->set_tooltip_text(0, tooltip);
diff --git a/editor/editor_settings_dialog.cpp b/editor/editor_settings_dialog.cpp
index d6946afbc1..7faab03a35 100644
--- a/editor/editor_settings_dialog.cpp
+++ b/editor/editor_settings_dialog.cpp
@@ -452,8 +452,8 @@ void EditorSettingsDialog::_update_shortcuts() {
TreeItem *section = shortcuts->create_item(root);
- const String item_name = EditorPropertyNameProcessor::get_singleton()->process_name(section_name, name_style);
- const String tooltip = EditorPropertyNameProcessor::get_singleton()->process_name(section_name, tooltip_style);
+ const String item_name = EditorPropertyNameProcessor::get_singleton()->process_name(section_name, name_style, E);
+ const String tooltip = EditorPropertyNameProcessor::get_singleton()->process_name(section_name, tooltip_style, E);
section->set_text(0, item_name);
section->set_tooltip_text(0, tooltip);
diff --git a/editor/group_settings_editor.cpp b/editor/group_settings_editor.cpp
index da169b36b2..45da907d8a 100644
--- a/editor/group_settings_editor.cpp
+++ b/editor/group_settings_editor.cpp
@@ -31,7 +31,6 @@
#include "group_settings_editor.h"
#include "core/config/project_settings.h"
-#include "editor/editor_file_system.h"
#include "editor/editor_node.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/filesystem_dock.h"
@@ -521,6 +520,7 @@ GroupSettingsEditor::GroupSettingsEditor() {
hbc->add_child(add_button);
tree = memnew(Tree);
+ tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
tree->set_hide_root(true);
tree->set_select_mode(Tree::SELECT_SINGLE);
tree->set_allow_reselect(true);
diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp
index e87ddd6915..0dd75ea033 100644
--- a/editor/gui/scene_tree_editor.cpp
+++ b/editor/gui/scene_tree_editor.cpp
@@ -45,7 +45,6 @@
#include "editor/themes/editor_scale.h"
#include "scene/gui/flow_container.h"
#include "scene/gui/label.h"
-#include "scene/gui/tab_container.h"
#include "scene/gui/texture_rect.h"
#include "scene/main/window.h"
#include "scene/resources/packed_scene.h"
@@ -926,6 +925,27 @@ void SceneTreeEditor::_notification(int p_what) {
_update_tree();
} break;
+
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+ if (is_visible()) {
+ TreeItem *item = nullptr;
+ if (selected) {
+ // Scroll to selected node.
+ item = _find(tree->get_root(), selected->get_path());
+ } else if (marked.size() == 1) {
+ // Scroll to a single marked node.
+ Node *marked_node = *marked.begin();
+ if (marked_node) {
+ item = _find(tree->get_root(), marked_node->get_path());
+ }
+ }
+
+ if (item) {
+ // Must wait until tree is properly sized before scrolling.
+ callable_mp(tree, &Tree::scroll_to_item).call_deferred(item, true);
+ }
+ }
+ } break;
}
}
@@ -1513,6 +1533,7 @@ SceneTreeEditor::SceneTreeEditor(bool p_label, bool p_can_rename, bool p_can_ope
}
tree = memnew(Tree);
+ tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
tree->set_anchor(SIDE_RIGHT, ANCHOR_END);
tree->set_anchor(SIDE_BOTTOM, ANCHOR_END);
tree->set_begin(Point2(0, p_label ? 18 : 0));
@@ -1567,7 +1588,9 @@ SceneTreeEditor::~SceneTreeEditor() {
/******** DIALOG *********/
-void SceneTreeDialog::popup_scenetree_dialog() {
+void SceneTreeDialog::popup_scenetree_dialog(Node *p_selected_node, Node *p_marked_node, bool p_marked_node_selectable, bool p_marked_node_children_selectable) {
+ get_scene_tree()->set_marked(p_marked_node, p_marked_node_selectable, p_marked_node_children_selectable);
+ get_scene_tree()->set_selected(p_selected_node);
popup_centered_clamped(Size2(350, 700) * EDSCALE);
}
diff --git a/editor/gui/scene_tree_editor.h b/editor/gui/scene_tree_editor.h
index a869e867b8..c1abdcef8b 100644
--- a/editor/gui/scene_tree_editor.h
+++ b/editor/gui/scene_tree_editor.h
@@ -157,8 +157,8 @@ public:
void set_as_scene_tree_dock();
void set_display_foreign_nodes(bool p_display);
- void set_marked(const HashSet<Node *> &p_marked, bool p_selectable = false, bool p_children_selectable = true);
- void set_marked(Node *p_marked, bool p_selectable = false, bool p_children_selectable = true);
+ void set_marked(const HashSet<Node *> &p_marked, bool p_selectable = true, bool p_children_selectable = true);
+ void set_marked(Node *p_marked, bool p_selectable = true, bool p_children_selectable = true);
void set_selected(Node *p_node, bool p_emit_selected = true);
Node *get_selected();
void set_can_rename(bool p_can_rename) { can_rename = p_can_rename; }
@@ -201,7 +201,7 @@ protected:
static void _bind_methods();
public:
- void popup_scenetree_dialog();
+ void popup_scenetree_dialog(Node *p_selected_node = nullptr, Node *p_marked_node = nullptr, bool p_marked_node_selectable = true, bool p_marked_node_children_selectable = true);
void set_valid_types(const Vector<StringName> &p_valid);
SceneTreeEditor *get_scene_tree() { return tree; }
diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp
index c2707e3a3d..55fc52e1db 100644
--- a/editor/inspector_dock.cpp
+++ b/editor/inspector_dock.cpp
@@ -702,6 +702,7 @@ InspectorDock::InspectorDock(EditorData &p_editor_data) {
forward_button->connect("pressed", callable_mp(this, &InspectorDock::_edit_forward));
history_menu = memnew(MenuButton);
+ history_menu->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
history_menu->set_flat(false);
history_menu->set_theme_type_variation("FlatMenuButton");
history_menu->set_tooltip_text(TTR("History of recently edited objects."));
diff --git a/editor/plugins/animation_tree_editor_plugin.cpp b/editor/plugins/animation_tree_editor_plugin.cpp
index 074fea49bd..c7f132bec1 100644
--- a/editor/plugins/animation_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_tree_editor_plugin.cpp
@@ -34,25 +34,15 @@
#include "animation_blend_space_2d_editor.h"
#include "animation_blend_tree_editor_plugin.h"
#include "animation_state_machine_editor.h"
-#include "core/config/project_settings.h"
-#include "core/input/input.h"
-#include "core/io/resource_loader.h"
-#include "core/math/delaunay_2d.h"
-#include "core/os/keyboard.h"
#include "editor/editor_command_palette.h"
#include "editor/editor_node.h"
#include "editor/gui/editor_bottom_panel.h"
-#include "editor/gui/editor_file_dialog.h"
#include "editor/themes/editor_scale.h"
#include "scene/animation/animation_blend_tree.h"
-#include "scene/animation/animation_player.h"
#include "scene/gui/button.h"
#include "scene/gui/margin_container.h"
-#include "scene/gui/menu_button.h"
-#include "scene/gui/panel.h"
#include "scene/gui/scroll_container.h"
#include "scene/gui/separator.h"
-#include "scene/main/window.h"
#include "scene/scene_string_names.h"
void AnimationTreeEditor::edit(AnimationTree *p_tree) {
@@ -115,6 +105,7 @@ void AnimationTreeEditor::_update_path() {
path_hb->add_child(b);
for (int i = 0; i < button_path.size(); i++) {
b = memnew(Button);
+ b->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
b->set_text(button_path[i]);
b->set_toggle_mode(true);
b->set_button_group(group);
diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp
index 13bdc366bf..184241e6db 100644
--- a/editor/plugins/asset_library_editor_plugin.cpp
+++ b/editor/plugins/asset_library_editor_plugin.cpp
@@ -55,7 +55,9 @@ static inline void setup_http_request(HTTPRequest *request) {
}
void EditorAssetLibraryItem::configure(const String &p_title, int p_asset_id, const String &p_category, int p_category_id, const String &p_author, int p_author_id, const String &p_cost) {
- title->set_text(p_title);
+ title_text = p_title;
+ title->set_text(title_text);
+ title->set_tooltip_text(title_text);
asset_id = p_asset_id;
category->set_text(p_category);
category_id = p_category_id;
@@ -66,16 +68,15 @@ void EditorAssetLibraryItem::configure(const String &p_title, int p_asset_id, co
// TODO: Refactor this method to use the TextServer.
void EditorAssetLibraryItem::clamp_width(int p_max_width) {
- int text_pixel_width = title->get_button_font().ptr()->get_string_size(title->get_text()).x * EDSCALE;
-
- String full_text = title->get_text();
- title->set_tooltip_text(full_text);
+ int text_pixel_width = title->get_button_font()->get_string_size(title_text).x * EDSCALE;
if (text_pixel_width > p_max_width) {
// Truncate title text to within the current column width.
- int max_length = p_max_width / (text_pixel_width / full_text.length());
- String truncated_text = full_text.left(max_length - 3) + "...";
+ int max_length = p_max_width / (text_pixel_width / title_text.length());
+ String truncated_text = title_text.left(max_length - 3) + "...";
title->set_text(truncated_text);
+ } else {
+ title->set_text(title_text);
}
}
@@ -1525,7 +1526,15 @@ void EditorAssetLibrary::_update_asset_items_columns() {
asset_items->set_columns(new_columns);
}
- asset_items_column_width = (get_size().x / new_columns) - (100 * EDSCALE);
+ asset_items_column_width = (get_size().x / new_columns) - (120 * EDSCALE);
+
+ for (int i = 0; i < asset_items->get_child_count(); i++) {
+ EditorAssetLibraryItem *item = Object::cast_to<EditorAssetLibraryItem>(asset_items->get_child(i));
+ if (!item || !item->is_visible()) {
+ continue;
+ }
+ item->clamp_width(asset_items_column_width);
+ }
}
void EditorAssetLibrary::_set_library_message(const String &p_message) {
diff --git a/editor/plugins/asset_library_editor_plugin.h b/editor/plugins/asset_library_editor_plugin.h
index d4a1411c18..16b784d629 100644
--- a/editor/plugins/asset_library_editor_plugin.h
+++ b/editor/plugins/asset_library_editor_plugin.h
@@ -62,6 +62,7 @@ class EditorAssetLibraryItem : public PanelContainer {
LinkButton *author = nullptr;
Label *price = nullptr;
+ String title_text;
int asset_id = 0;
int category_id = 0;
int author_id = 0;
diff --git a/editor/plugins/cpu_particles_3d_editor_plugin.cpp b/editor/plugins/cpu_particles_3d_editor_plugin.cpp
index bf427f733b..b5e3f102cf 100644
--- a/editor/plugins/cpu_particles_3d_editor_plugin.cpp
+++ b/editor/plugins/cpu_particles_3d_editor_plugin.cpp
@@ -56,7 +56,6 @@ void CPUParticles3DEditor::_menu_option(int p_option) {
switch (p_option) {
case MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE: {
emission_tree_dialog->popup_scenetree_dialog();
-
} break;
case MENU_OPTION_RESTART: {
diff --git a/editor/plugins/multimesh_editor_plugin.cpp b/editor/plugins/multimesh_editor_plugin.cpp
index 086abc0859..9fec263af3 100644
--- a/editor/plugins/multimesh_editor_plugin.cpp
+++ b/editor/plugins/multimesh_editor_plugin.cpp
@@ -255,13 +255,15 @@ void MultiMeshEditor::edit(MultiMeshInstance3D *p_multimesh) {
void MultiMeshEditor::_browse(bool p_source) {
browsing_source = p_source;
- std->get_scene_tree()->set_marked(node, false);
- std->popup_scenetree_dialog();
+ Node *browsed_node = nullptr;
if (p_source) {
+ browsed_node = node->get_node_or_null(mesh_source->get_text());
std->set_title(TTR("Select a Source Mesh:"));
} else {
+ browsed_node = node->get_node_or_null(surface_source->get_text());
std->set_title(TTR("Select a Target Surface:"));
}
+ std->popup_scenetree_dialog(browsed_node);
}
void MultiMeshEditor::_bind_methods() {
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 640c755ccf..0a6eacf11d 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -1170,14 +1170,11 @@ void ScriptTextEditor::_update_connected_methods() {
continue;
}
- // Account for inner classes
- if (raw_name.contains(".")) {
- // Strip inner class name from the method, and start from the right since
- // our inner class might be inside another inner class
- int pos = raw_name.rfind(".");
- if (pos != -1) {
- name = raw_name.substr(pos + 1);
- }
+ // Account for inner classes by stripping the class names from the method,
+ // starting from the right since our inner class might be inside of another inner class.
+ int pos = raw_name.rfind(".");
+ if (pos != -1) {
+ name = raw_name.substr(pos + 1);
}
String found_base_class;
diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp
index 597fd7393f..f047e4ff16 100644
--- a/editor/plugins/tiles/tile_data_editors.cpp
+++ b/editor/plugins/tiles/tile_data_editors.cpp
@@ -1280,7 +1280,7 @@ void TileDataDefaultEditor::setup_property_editor(Variant::Type p_type, const St
property_editor = EditorInspectorDefaultPlugin::get_editor_for_property(dummy_object, p_type, p_property, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT);
property_editor->set_object_and_property(dummy_object, p_property);
if (p_label.is_empty()) {
- property_editor->set_label(EditorPropertyNameProcessor::get_singleton()->process_name(p_property, EditorPropertyNameProcessor::get_default_inspector_style()));
+ property_editor->set_label(EditorPropertyNameProcessor::get_singleton()->process_name(p_property, EditorPropertyNameProcessor::get_default_inspector_style(), p_property));
} else {
property_editor->set_label(p_label);
}
diff --git a/editor/reparent_dialog.cpp b/editor/reparent_dialog.cpp
index ec5208b549..a139d180f0 100644
--- a/editor/reparent_dialog.cpp
+++ b/editor/reparent_dialog.cpp
@@ -60,7 +60,7 @@ void ReparentDialog::_reparent() {
void ReparentDialog::set_current(const HashSet<Node *> &p_selection) {
tree->set_marked(p_selection, false, false);
- //tree->set_selected(p_node->get_parent());
+ tree->set_selected(nullptr);
}
void ReparentDialog::_bind_methods() {
@@ -74,7 +74,6 @@ ReparentDialog::ReparentDialog() {
VBoxContainer *vbc = memnew(VBoxContainer);
add_child(vbc);
- //set_child_rect(vbc);
tree = memnew(SceneTreeEditor(false));
tree->set_show_enabled_subscene(true);
@@ -86,10 +85,6 @@ ReparentDialog::ReparentDialog() {
keep_transform->set_pressed(true);
vbc->add_child(keep_transform);
- //vbc->add_margin_child("Options:",node_only);
-
- //cancel->connect("pressed", this,"_cancel");
-
set_ok_button_text(TTR("Reparent"));
}
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index c6e122dde8..26ddec6603 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -3156,7 +3156,7 @@ void SceneTreeDock::_files_dropped(const Vector<String> &p_files, NodePath p_to,
const EditorPropertyNameProcessor::Style style = InspectorDock::get_singleton()->get_property_name_style();
menu_properties->clear();
for (const String &p : valid_properties) {
- menu_properties->add_item(EditorPropertyNameProcessor::get_singleton()->process_name(p, style));
+ menu_properties->add_item(EditorPropertyNameProcessor::get_singleton()->process_name(p, style, p, node->get_class_name()));
menu_properties->set_item_metadata(-1, p);
}
@@ -3511,6 +3511,7 @@ void SceneTreeDock::_update_tree_menu() {
tree_menu->set_item_tooltip(tree_menu->get_item_index(TOOL_CENTER_PARENT), TTR("If enabled, Reparent to New Node will create the new node in the center of the selected nodes, if possible."));
PopupMenu *resource_list = memnew(PopupMenu);
+ resource_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
resource_list->connect("about_to_popup", callable_mp(this, &SceneTreeDock::_list_all_subresources).bind(resource_list));
resource_list->connect("index_pressed", callable_mp(this, &SceneTreeDock::_edit_subresource).bind(resource_list));
tree_menu->add_submenu_node_item(TTR("All Scene Sub-Resources"), resource_list);
diff --git a/misc/extension_api_validation/4.2-stable.expected b/misc/extension_api_validation/4.2-stable.expected
index 8623e8eb25..3df19bb83a 100644
--- a/misc/extension_api_validation/4.2-stable.expected
+++ b/misc/extension_api_validation/4.2-stable.expected
@@ -248,3 +248,23 @@ Validate extension JSON: Error: Field 'classes/AcceptDialog/methods/register_tex
Validate extension JSON: Error: Field 'classes/AcceptDialog/methods/remove_button/arguments/0': type changed value in new API, from "Control" to "Button".
Changed argument type to the more specific one actually expected by the method. Compatibility method registered.
+
+
+GH-89992
+--------
+Validate extension JSON: Error: Field 'classes/Node/methods/replace_by/arguments': size changed value in new API, from 2 to 3.
+
+Added optional argument to prevent children to be reparented during replace_by. Compatibility method registered.
+
+
+GH-88047
+--------
+Validate extension JSON: Error: Field 'classes/AStar2D/methods/get_id_path/arguments': size changed value in new API, from 2 to 3.
+Validate extension JSON: Error: Field 'classes/AStar2D/methods/get_point_path/arguments': size changed value in new API, from 2 to 3.
+Validate extension JSON: Error: Field 'classes/AStar3D/methods/get_id_path/arguments': size changed value in new API, from 2 to 3.
+Validate extension JSON: Error: Field 'classes/AStar3D/methods/get_point_path/arguments': size changed value in new API, from 2 to 3.
+Validate extension JSON: Error: Field 'classes/AStarGrid2D/methods/get_id_path/arguments': size changed value in new API, from 2 to 3.
+Validate extension JSON: Error: Field 'classes/AStarGrid2D/methods/get_point_path/arguments': size changed value in new API, from 2 to 3.
+
+Added optional "allow_partial_path" argument to get_id_path and get_point_path methods in AStar classes.
+Compatibility methods registered.
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 858d1d3e4e..0dd1dc7c12 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -1408,7 +1408,11 @@ GDExtensionBool CSharpLanguage::_instance_binding_reference_callback(void *p_tok
}
void *CSharpLanguage::get_instance_binding(Object *p_object) {
- void *binding = p_object->get_instance_binding(get_singleton(), &_instance_binding_callbacks);
+ return p_object->get_instance_binding(get_singleton(), &_instance_binding_callbacks);
+}
+
+void *CSharpLanguage::get_instance_binding_with_setup(Object *p_object) {
+ void *binding = get_instance_binding(p_object);
// Initially this was in `_instance_binding_create_callback`. However, after the new instance
// binding re-write it was resulting in a deadlock in `_instance_binding_reference`, as
@@ -1433,11 +1437,7 @@ void *CSharpLanguage::get_existing_instance_binding(Object *p_object) {
#ifdef DEBUG_ENABLED
CRASH_COND(p_object->has_instance_binding(p_object));
#endif
- return p_object->get_instance_binding(get_singleton(), &_instance_binding_callbacks);
-}
-
-void CSharpLanguage::set_instance_binding(Object *p_object, void *p_binding) {
- p_object->set_instance_binding(get_singleton(), p_binding, &_instance_binding_callbacks);
+ return get_instance_binding(p_object);
}
bool CSharpLanguage::has_instance_binding(Object *p_object) {
@@ -1464,13 +1464,6 @@ void CSharpLanguage::tie_native_managed_to_unmanaged(GCHandleIntPtr p_gchandle_i
// Another reason for doing this is that this instance could outlive CSharpLanguage, which would
// be problematic when using a script. See: https://github.com/godotengine/godot/issues/25621
- CSharpScriptBinding script_binding;
-
- script_binding.inited = true;
- script_binding.type_name = *p_native_name;
- script_binding.gchandle = gchandle;
- script_binding.owner = p_unmanaged;
-
if (p_ref_counted) {
// Unsafe refcount increment. The managed instance also counts as a reference.
// This way if the unmanaged world has no references to our owner
@@ -1486,14 +1479,13 @@ void CSharpLanguage::tie_native_managed_to_unmanaged(GCHandleIntPtr p_gchandle_i
// The object was just created, no script instance binding should have been attached
CRASH_COND(CSharpLanguage::has_instance_binding(p_unmanaged));
- void *data;
- {
- MutexLock lock(CSharpLanguage::get_singleton()->get_language_bind_mutex());
- data = (void *)CSharpLanguage::get_singleton()->insert_script_binding(p_unmanaged, script_binding);
- }
+ void *binding = CSharpLanguage::get_singleton()->get_instance_binding(p_unmanaged);
- // Should be thread safe because the object was just created and nothing else should be referencing it
- CSharpLanguage::set_instance_binding(p_unmanaged, data);
+ CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)binding)->value();
+ script_binding.inited = true;
+ script_binding.type_name = *p_native_name;
+ script_binding.gchandle = gchandle;
+ script_binding.owner = p_unmanaged;
}
void CSharpLanguage::tie_user_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, Ref<CSharpScript> *p_script, bool p_ref_counted) {
@@ -2092,7 +2084,7 @@ CSharpInstance::~CSharpInstance() {
bool die = _unreference_owner_unsafe();
CRASH_COND(die); // `owner_keep_alive` holds a reference, so it can't die
- void *data = CSharpLanguage::get_instance_binding(owner);
+ void *data = CSharpLanguage::get_instance_binding_with_setup(owner);
CRASH_COND(data == nullptr);
CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->get();
CRASH_COND(!script_binding.inited);
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index 06d526f494..e3f39c50f4 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -442,7 +442,7 @@ class CSharpLanguage : public ScriptLanguage {
public:
static void *get_instance_binding(Object *p_object);
static void *get_existing_instance_binding(Object *p_object);
- static void set_instance_binding(Object *p_object, void *p_binding);
+ static void *get_instance_binding_with_setup(Object *p_object);
static bool has_instance_binding(Object *p_object);
const Mutex &get_language_bind_mutex() {
diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp
index 0089e9c2a2..4bb324c0ee 100644
--- a/modules/mono/glue/runtime_interop.cpp
+++ b/modules/mono/glue/runtime_interop.cpp
@@ -239,7 +239,7 @@ GCHandleIntPtr godotsharp_internal_unmanaged_get_instance_binding_managed(Object
CRASH_COND(!p_unmanaged);
#endif
- void *data = CSharpLanguage::get_instance_binding(p_unmanaged);
+ void *data = CSharpLanguage::get_instance_binding_with_setup(p_unmanaged);
ERR_FAIL_NULL_V(data, { nullptr });
CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->value();
ERR_FAIL_COND_V(!script_binding.inited, { nullptr });
@@ -252,7 +252,7 @@ GCHandleIntPtr godotsharp_internal_unmanaged_instance_binding_create_managed(Obj
CRASH_COND(!p_unmanaged);
#endif
- void *data = CSharpLanguage::get_instance_binding(p_unmanaged);
+ void *data = CSharpLanguage::get_instance_binding_with_setup(p_unmanaged);
ERR_FAIL_NULL_V(data, { nullptr });
CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->value();
ERR_FAIL_COND_V(!script_binding.inited, { nullptr });
diff --git a/modules/multiplayer/editor/replication_editor.cpp b/modules/multiplayer/editor/replication_editor.cpp
index 58803124cf..6000e2b1d3 100644
--- a/modules/multiplayer/editor/replication_editor.cpp
+++ b/modules/multiplayer/editor/replication_editor.cpp
@@ -131,7 +131,7 @@ void ReplicationEditor::_pick_new_property() {
EditorNode::get_singleton()->show_warning(TTR("Not possible to add a new property to synchronize without a root."));
return;
}
- pick_node->popup_scenetree_dialog();
+ pick_node->popup_scenetree_dialog(nullptr, current);
pick_node->get_filter_line_edit()->clear();
pick_node->get_filter_line_edit()->grab_focus();
}
diff --git a/modules/raycast/raycast_occlusion_cull.cpp b/modules/raycast/raycast_occlusion_cull.cpp
index 04b2600b53..94d8b267d1 100644
--- a/modules/raycast/raycast_occlusion_cull.cpp
+++ b/modules/raycast/raycast_occlusion_cull.cpp
@@ -538,6 +538,64 @@ void RaycastOcclusionCull::buffer_set_size(RID p_buffer, const Vector2i &p_size)
buffers[p_buffer].resize(p_size);
}
+Projection RaycastOcclusionCull::_jitter_projection(const Projection &p_cam_projection, const Size2i &p_viewport_size) {
+ if (!_jitter_enabled) {
+ return p_cam_projection;
+ }
+
+ // Prevent divide by zero when using NULL viewport.
+ if ((p_viewport_size.x <= 0) || (p_viewport_size.y <= 0)) {
+ return p_cam_projection;
+ }
+
+ Projection p = p_cam_projection;
+
+ int32_t frame = Engine::get_singleton()->get_frames_drawn();
+ frame %= 9;
+
+ Vector2 jitter;
+
+ switch (frame) {
+ default:
+ break;
+ case 1: {
+ jitter = Vector2(-1, -1);
+ } break;
+ case 2: {
+ jitter = Vector2(1, -1);
+ } break;
+ case 3: {
+ jitter = Vector2(-1, 1);
+ } break;
+ case 4: {
+ jitter = Vector2(1, 1);
+ } break;
+ case 5: {
+ jitter = Vector2(-0.5f, -0.5f);
+ } break;
+ case 6: {
+ jitter = Vector2(0.5f, -0.5f);
+ } break;
+ case 7: {
+ jitter = Vector2(-0.5f, 0.5f);
+ } break;
+ case 8: {
+ jitter = Vector2(0.5f, 0.5f);
+ } break;
+ }
+
+ // The multiplier here determines the divergence from center,
+ // and is to some extent a balancing act.
+ // Higher divergence gives fewer false hidden, but more false shown.
+ // False hidden is obvious to viewer, false shown is not.
+ // False shown can lower percentage that are occluded, and therefore performance.
+ jitter *= Vector2(1 / (float)p_viewport_size.x, 1 / (float)p_viewport_size.y) * 0.05f;
+
+ p.add_jitter_offset(jitter);
+
+ return p;
+}
+
void RaycastOcclusionCull::buffer_update(RID p_buffer, const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal) {
if (!buffers.has(p_buffer)) {
return;
@@ -552,7 +610,9 @@ void RaycastOcclusionCull::buffer_update(RID p_buffer, const Transform3D &p_cam_
Scenario &scenario = scenarios[buffer.scenario_rid];
scenario.update();
- buffer.update_camera_rays(p_cam_transform, p_cam_projection, p_cam_orthogonal);
+ Projection jittered_proj = _jitter_projection(p_cam_projection, buffer.get_occlusion_buffer_size());
+
+ buffer.update_camera_rays(p_cam_transform, jittered_proj, p_cam_orthogonal);
scenario.raycast(buffer.camera_rays, buffer.camera_ray_masks.ptr(), buffer.camera_rays_tile_count);
buffer.sort_rays(-p_cam_transform.basis.get_column(2), p_cam_orthogonal);
@@ -598,6 +658,7 @@ void RaycastOcclusionCull::_init_embree() {
RaycastOcclusionCull::RaycastOcclusionCull() {
raycast_singleton = this;
int default_quality = GLOBAL_GET("rendering/occlusion_culling/bvh_build_quality");
+ _jitter_enabled = GLOBAL_GET("rendering/occlusion_culling/jitter_projection");
build_quality = RS::ViewportOcclusionCullingBuildQuality(default_quality);
}
diff --git a/modules/raycast/raycast_occlusion_cull.h b/modules/raycast/raycast_occlusion_cull.h
index b61730fd78..335a685672 100644
--- a/modules/raycast/raycast_occlusion_cull.h
+++ b/modules/raycast/raycast_occlusion_cull.h
@@ -163,8 +163,10 @@ private:
HashMap<RID, Scenario> scenarios;
HashMap<RID, RaycastHZBuffer> buffers;
RS::ViewportOcclusionCullingBuildQuality build_quality;
+ bool _jitter_enabled = false;
void _init_embree();
+ Projection _jitter_projection(const Projection &p_cam_projection, const Size2i &p_viewport_size);
public:
virtual bool is_occluder(RID p_rid) override;
diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp
index 90759810b1..c6f2f82117 100644
--- a/platform/android/display_server_android.cpp
+++ b/platform/android/display_server_android.cpp
@@ -71,6 +71,8 @@ bool DisplayServerAndroid::has_feature(Feature p_feature) const {
case FEATURE_MOUSE:
//case FEATURE_MOUSE_WARP:
//case FEATURE_NATIVE_DIALOG:
+ //case FEATURE_NATIVE_DIALOG_INPUT:
+ //case FEATURE_NATIVE_DIALOG_FILE:
//case FEATURE_NATIVE_ICON:
//case FEATURE_WINDOW_TRANSPARENCY:
case FEATURE_CLIPBOARD:
diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm
index e1c3dcd372..f84fb01ad0 100644
--- a/platform/ios/display_server_ios.mm
+++ b/platform/ios/display_server_ios.mm
@@ -328,6 +328,8 @@ bool DisplayServerIOS::has_feature(Feature p_feature) const {
// case FEATURE_MOUSE:
// case FEATURE_MOUSE_WARP:
// case FEATURE_NATIVE_DIALOG:
+ // case FEATURE_NATIVE_DIALOG_INPUT:
+ // case FEATURE_NATIVE_DIALOG_FILE:
// case FEATURE_NATIVE_ICON:
// case FEATURE_WINDOW_TRANSPARENCY:
case FEATURE_CLIPBOARD:
diff --git a/platform/linuxbsd/wayland/display_server_wayland.cpp b/platform/linuxbsd/wayland/display_server_wayland.cpp
index 80b6029c9d..d00d5deb2c 100644
--- a/platform/linuxbsd/wayland/display_server_wayland.cpp
+++ b/platform/linuxbsd/wayland/display_server_wayland.cpp
@@ -210,8 +210,10 @@ bool DisplayServerWayland::has_feature(Feature p_feature) const {
return true;
} break;
+ //case FEATURE_NATIVE_DIALOG:
+ //case FEATURE_NATIVE_DIALOG_INPUT:
#ifdef DBUS_ENABLED
- case FEATURE_NATIVE_DIALOG: {
+ case FEATURE_NATIVE_DIALOG_FILE: {
return true;
} break;
#endif
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index 226e123648..9069dd423f 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -128,8 +128,10 @@ bool DisplayServerX11::has_feature(Feature p_feature) const {
//case FEATURE_HIDPI:
case FEATURE_ICON:
#ifdef DBUS_ENABLED
- case FEATURE_NATIVE_DIALOG:
+ case FEATURE_NATIVE_DIALOG_FILE:
#endif
+ //case FEATURE_NATIVE_DIALOG:
+ //case FEATURE_NATIVE_DIALOG_INPUT:
//case FEATURE_NATIVE_ICON:
case FEATURE_SWAP_BUFFERS:
#ifdef DBUS_ENABLED
@@ -5210,7 +5212,7 @@ void DisplayServerX11::set_icon(const Ref<Image> &p_icon) {
if (g_set_icon_error) {
g_set_icon_error = false;
- WARN_PRINT("Icon too large, attempting to resize icon.");
+ WARN_PRINT(vformat("Icon too large (%dx%d), attempting to downscale icon.", w, h));
int new_width, new_height;
if (w > h) {
diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h
index 09073a8030..7c9d073afc 100644
--- a/platform/macos/display_server_macos.h
+++ b/platform/macos/display_server_macos.h
@@ -241,6 +241,8 @@ public:
NSImage *_convert_to_nsimg(Ref<Image> &p_image) const;
Point2i _get_screens_origin() const;
+ void set_menu_delegate(NSMenu *p_menu);
+
void send_event(NSEvent *p_event);
void send_window_event(const WindowData &p_wd, WindowEvent p_event);
void release_pressed_events();
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index 99e9d20f2b..d06eab8531 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -290,6 +290,10 @@ void DisplayServerMacOS::_update_displays_arrangement() {
displays_arrangement_dirty = false;
}
+void DisplayServerMacOS::set_menu_delegate(NSMenu *p_menu) {
+ [p_menu setDelegate:menu_delegate];
+}
+
Point2i DisplayServerMacOS::_get_screens_origin() const {
// Returns the native top-left screen coordinate of the smallest rectangle
// that encompasses all screens. Needed in get_screen_position(),
@@ -755,6 +759,8 @@ bool DisplayServerMacOS::has_feature(Feature p_feature) const {
case FEATURE_CURSOR_SHAPE:
case FEATURE_CUSTOM_CURSOR_SHAPE:
case FEATURE_NATIVE_DIALOG:
+ case FEATURE_NATIVE_DIALOG_INPUT:
+ case FEATURE_NATIVE_DIALOG_FILE:
case FEATURE_IME:
case FEATURE_WINDOW_TRANSPARENCY:
case FEATURE_HIDPI:
diff --git a/platform/macos/native_menu_macos.mm b/platform/macos/native_menu_macos.mm
index 250b64dc04..8c2dd98862 100644
--- a/platform/macos/native_menu_macos.mm
+++ b/platform/macos/native_menu_macos.mm
@@ -223,6 +223,11 @@ RID NativeMenuMacOS::get_system_menu(SystemMenus p_menu_id) const {
RID NativeMenuMacOS::create_menu() {
MenuData *md = memnew(MenuData);
md->menu = [[NSMenu alloc] initWithTitle:@""];
+ [md->menu setAutoenablesItems:NO];
+ DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
+ if (ds) {
+ ds->set_menu_delegate(md->menu);
+ }
RID rid = menus.make_rid(md);
menu_lookup[md->menu] = rid;
return rid;
diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp
index 281f312000..06f5eb82f7 100644
--- a/platform/web/display_server_web.cpp
+++ b/platform/web/display_server_web.cpp
@@ -1128,6 +1128,8 @@ bool DisplayServerWeb::has_feature(Feature p_feature) const {
return true;
//case FEATURE_MOUSE_WARP:
//case FEATURE_NATIVE_DIALOG:
+ //case FEATURE_NATIVE_DIALOG_INPUT:
+ //case FEATURE_NATIVE_DIALOG_FILE:
//case FEATURE_NATIVE_ICON:
//case FEATURE_WINDOW_TRANSPARENCY:
//case FEATURE_KEEP_SCREEN_ON:
diff --git a/platform/web/os_web.cpp b/platform/web/os_web.cpp
index 45671ca491..4b93ce9e75 100644
--- a/platform/web/os_web.cpp
+++ b/platform/web/os_web.cpp
@@ -105,6 +105,10 @@ Error OS_Web::execute(const String &p_path, const List<String> &p_arguments, Str
return create_process(p_path, p_arguments);
}
+Dictionary OS_Web::execute_with_pipe(const String &p_path, const List<String> &p_arguments) {
+ ERR_FAIL_V_MSG(Dictionary(), "OS::execute_with_pipe is not available on the Web platform.");
+}
+
Error OS_Web::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id, bool p_open_console) {
Array args;
for (const String &E : p_arguments) {
diff --git a/platform/web/os_web.h b/platform/web/os_web.h
index e578c93925..16f8ea8550 100644
--- a/platform/web/os_web.h
+++ b/platform/web/os_web.h
@@ -80,6 +80,7 @@ public:
bool main_loop_iterate();
Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) override;
+ Dictionary execute_with_pipe(const String &p_path, const List<String> &p_arguments) override;
Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
Error kill(const ProcessID &p_pid) override;
int get_process_id() const override;
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 79078456a5..e540b7617f 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -114,6 +114,8 @@ bool DisplayServerWindows::has_feature(Feature p_feature) const {
case FEATURE_ICON:
case FEATURE_NATIVE_ICON:
case FEATURE_NATIVE_DIALOG:
+ case FEATURE_NATIVE_DIALOG_INPUT:
+ case FEATURE_NATIVE_DIALOG_FILE:
case FEATURE_SWAP_BUFFERS:
case FEATURE_KEEP_SCREEN_ON:
case FEATURE_TEXT_TO_SPEECH:
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 152b9ac10f..c39b327953 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -42,6 +42,7 @@
#include "drivers/unix/net_socket_posix.h"
#include "drivers/windows/dir_access_windows.h"
#include "drivers/windows/file_access_windows.h"
+#include "drivers/windows/file_access_windows_pipe.h"
#include "main/main.h"
#include "servers/audio_server.h"
#include "servers/rendering/rendering_server_default.h"
@@ -178,6 +179,7 @@ void OS_Windows::initialize() {
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_RESOURCES);
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_USERDATA);
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_FILESYSTEM);
+ FileAccess::make_default<FileAccessWindowsPipe>(FileAccess::ACCESS_PIPE);
DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_RESOURCES);
DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_USERDATA);
DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_FILESYSTEM);
@@ -727,6 +729,105 @@ Dictionary OS_Windows::get_memory_info() const {
return meminfo;
}
+Dictionary OS_Windows::execute_with_pipe(const String &p_path, const List<String> &p_arguments) {
+#define CLEAN_PIPES \
+ if (pipe_in[0] != 0) { \
+ CloseHandle(pipe_in[0]); \
+ } \
+ if (pipe_in[1] != 0) { \
+ CloseHandle(pipe_in[1]); \
+ } \
+ if (pipe_out[0] != 0) { \
+ CloseHandle(pipe_out[0]); \
+ } \
+ if (pipe_out[1] != 0) { \
+ CloseHandle(pipe_out[1]); \
+ } \
+ if (pipe_err[0] != 0) { \
+ CloseHandle(pipe_err[0]); \
+ } \
+ if (pipe_err[1] != 0) { \
+ CloseHandle(pipe_err[1]); \
+ }
+
+ Dictionary ret;
+
+ String path = p_path.replace("/", "\\");
+ String command = _quote_command_line_argument(path);
+ for (const String &E : p_arguments) {
+ command += " " + _quote_command_line_argument(E);
+ }
+
+ // Create pipes.
+ HANDLE pipe_in[2] = { 0, 0 };
+ HANDLE pipe_out[2] = { 0, 0 };
+ HANDLE pipe_err[2] = { 0, 0 };
+
+ SECURITY_ATTRIBUTES sa;
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.bInheritHandle = true;
+ sa.lpSecurityDescriptor = nullptr;
+
+ ERR_FAIL_COND_V(!CreatePipe(&pipe_in[0], &pipe_in[1], &sa, 0), ret);
+ if (!SetHandleInformation(pipe_in[1], HANDLE_FLAG_INHERIT, 0)) {
+ CLEAN_PIPES
+ ERR_FAIL_V(ret);
+ }
+ if (!CreatePipe(&pipe_out[0], &pipe_out[1], &sa, 0)) {
+ CLEAN_PIPES
+ ERR_FAIL_V(ret);
+ }
+ if (!SetHandleInformation(pipe_out[0], HANDLE_FLAG_INHERIT, 0)) {
+ CLEAN_PIPES
+ ERR_FAIL_V(ret);
+ }
+ if (!CreatePipe(&pipe_err[0], &pipe_err[1], &sa, 0)) {
+ CLEAN_PIPES
+ ERR_FAIL_V(ret);
+ }
+ ERR_FAIL_COND_V(!SetHandleInformation(pipe_err[0], HANDLE_FLAG_INHERIT, 0), ret);
+
+ // Create process.
+ ProcessInfo pi;
+ ZeroMemory(&pi.si, sizeof(pi.si));
+ pi.si.cb = sizeof(pi.si);
+ ZeroMemory(&pi.pi, sizeof(pi.pi));
+ LPSTARTUPINFOW si_w = (LPSTARTUPINFOW)&pi.si;
+
+ pi.si.dwFlags |= STARTF_USESTDHANDLES;
+ pi.si.hStdInput = pipe_in[0];
+ pi.si.hStdOutput = pipe_out[1];
+ pi.si.hStdError = pipe_err[1];
+
+ DWORD creation_flags = NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW;
+
+ if (!CreateProcessW(nullptr, (LPWSTR)(command.utf16().ptrw()), nullptr, nullptr, true, creation_flags, nullptr, nullptr, si_w, &pi.pi)) {
+ CLEAN_PIPES
+ ERR_FAIL_V_MSG(ret, "Could not create child process: " + command);
+ }
+ CloseHandle(pipe_in[0]);
+ CloseHandle(pipe_out[1]);
+ CloseHandle(pipe_err[1]);
+
+ ProcessID pid = pi.pi.dwProcessId;
+ process_map->insert(pid, pi);
+
+ Ref<FileAccessWindowsPipe> main_pipe;
+ main_pipe.instantiate();
+ main_pipe->open_existing(pipe_out[0], pipe_in[1]);
+
+ Ref<FileAccessWindowsPipe> err_pipe;
+ err_pipe.instantiate();
+ err_pipe->open_existing(pipe_err[0], 0);
+
+ ret["stdio"] = main_pipe;
+ ret["stderr"] = err_pipe;
+ ret["pid"] = pid;
+
+#undef CLEAN_PIPES
+ return ret;
+}
+
Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex, bool p_open_console) {
String path = p_path.replace("/", "\\");
String command = _quote_command_line_argument(path);
diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h
index 351cb32c9b..c703a3ddca 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -181,6 +181,7 @@ public:
virtual Dictionary get_memory_info() const override;
virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) override;
+ virtual Dictionary execute_with_pipe(const String &p_path, const List<String> &p_arguments) override;
virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
virtual Error kill(const ProcessID &p_pid) override;
virtual int get_process_id() const override;
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index c344272f38..1163c0e390 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -68,7 +68,7 @@ void FileDialog::popup(const Rect2i &p_rect) {
}
#endif
- if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG) && (use_native_dialog || OS::get_singleton()->is_sandboxed())) {
+ if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE) && (use_native_dialog || OS::get_singleton()->is_sandboxed())) {
String root;
if (access == ACCESS_RESOURCES) {
root = ProjectSettings::get_singleton()->get_resource_path();
@@ -91,7 +91,7 @@ void FileDialog::set_visible(bool p_visible) {
}
#endif
- if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG) && (use_native_dialog || OS::get_singleton()->is_sandboxed())) {
+ if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE) && (use_native_dialog || OS::get_singleton()->is_sandboxed())) {
if (p_visible) {
String root;
if (access == ACCESS_RESOURCES) {
diff --git a/scene/main/node.compat.inc b/scene/main/node.compat.inc
new file mode 100644
index 0000000000..69ece1a40d
--- /dev/null
+++ b/scene/main/node.compat.inc
@@ -0,0 +1,41 @@
+/**************************************************************************/
+/* node.compat.inc */
+/**************************************************************************/
+/* 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 DISABLE_DEPRECATED
+
+void Node::_replace_by_bind_compat_89992(Node *p_node, bool p_keep_data) {
+ replace_by(p_node, p_keep_data, true);
+}
+
+void Node::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("replace_by", "node", "keep_groups"), &Node::_replace_by_bind_compat_89992, DEFVAL(false));
+}
+
+#endif
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index d4e2d6eb16..11c200064e 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -29,6 +29,7 @@
/**************************************************************************/
#include "node.h"
+#include "node.compat.inc"
#include "core/config/project_settings.h"
#include "core/core_string_names.h"
@@ -2804,9 +2805,7 @@ Node *Node::_duplicate(int p_flags, HashMap<const Node *, Node *> *r_duplimap) c
}
} else {
- if (value.get_type() != Variant::OBJECT && (value.get_type() != Variant::ARRAY || static_cast<Array>(value).get_typed_builtin() != Variant::OBJECT)) {
- current_node->set(name, value);
- }
+ current_node->set(name, value);
}
}
}
@@ -3007,7 +3006,7 @@ static void find_owned_by(Node *p_by, Node *p_node, List<Node *> *p_owned) {
}
}
-void Node::replace_by(Node *p_node, bool p_keep_groups) {
+void Node::replace_by(Node *p_node, bool p_keep_groups, bool p_keep_children) {
ERR_THREAD_GUARD
ERR_FAIL_NULL(p_node);
ERR_FAIL_COND(p_node->data.parent);
@@ -3028,13 +3027,13 @@ void Node::replace_by(Node *p_node, bool p_keep_groups) {
_replace_connections_target(p_node);
if (data.owner) {
- for (int i = 0; i < get_child_count(); i++) {
- find_owned_by(data.owner, get_child(i), &owned_by_owner);
+ if (p_keep_children) {
+ for (int i = 0; i < get_child_count(); i++) {
+ find_owned_by(data.owner, get_child(i), &owned_by_owner);
+ }
}
-
_clean_up_owner();
}
-
Node *parent = data.parent;
int index_in_parent = get_index(false);
@@ -3046,31 +3045,33 @@ void Node::replace_by(Node *p_node, bool p_keep_groups) {
emit_signal(SNAME("replacing_by"), p_node);
- while (get_child_count()) {
- Node *child = get_child(0);
- remove_child(child);
- if (!child->is_owned_by_parent()) {
- // add the custom children to the p_node
- Node *child_owner = child->get_owner() == this ? p_node : child->get_owner();
- child->set_owner(nullptr);
- p_node->add_child(child);
- child->set_owner(child_owner);
+ if (p_keep_children) {
+ while (get_child_count()) {
+ Node *child = get_child(0);
+ remove_child(child);
+ if (!child->is_owned_by_parent()) {
+ // add the custom children to the p_node
+ Node *child_owner = child->get_owner() == this ? p_node : child->get_owner();
+ child->set_owner(nullptr);
+ p_node->add_child(child);
+ child->set_owner(child_owner);
+ }
}
- }
- p_node->set_owner(owner);
- for (Node *E : owned) {
- if (E->data.owner != p_node) {
- E->set_owner(p_node);
+ for (Node *E : owned) {
+ if (E->data.owner != p_node) {
+ E->set_owner(p_node);
+ }
}
- }
- for (Node *E : owned_by_owner) {
- if (E->data.owner != owner) {
- E->set_owner(owner);
+ for (Node *E : owned_by_owner) {
+ if (E->data.owner != owner) {
+ E->set_owner(owner);
+ }
}
}
+ p_node->set_owner(owner);
p_node->set_scene_file_path(get_scene_file_path());
}
@@ -3597,7 +3598,7 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("create_tween"), &Node::create_tween);
ClassDB::bind_method(D_METHOD("duplicate", "flags"), &Node::duplicate, DEFVAL(DUPLICATE_USE_INSTANTIATION | DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS));
- ClassDB::bind_method(D_METHOD("replace_by", "node", "keep_groups"), &Node::replace_by, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("replace_by", "node", "keep_groups", "keep_children"), &Node::replace_by, DEFVAL(false), DEFVAL(true));
ClassDB::bind_method(D_METHOD("set_scene_instance_load_placeholder", "load_placeholder"), &Node::set_scene_instance_load_placeholder);
ClassDB::bind_method(D_METHOD("get_scene_instance_load_placeholder"), &Node::get_scene_instance_load_placeholder);
diff --git a/scene/main/node.h b/scene/main/node.h
index f49eeec9cd..99def10338 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -310,6 +310,11 @@ private:
Variant _call_thread_safe_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
protected:
+#ifndef DISABLE_DEPRECATED
+ void _replace_by_bind_compat_89992(Node *p_node, bool p_keep_data = false);
+ static void _bind_compatibility_methods();
+#endif // DISABLE_DEPRECATED
+
void _block() { data.blocked++; }
void _unblock() { data.blocked--; }
@@ -629,7 +634,7 @@ public:
return binds;
}
- void replace_by(Node *p_node, bool p_keep_data = false);
+ void replace_by(Node *p_node, bool p_keep_groups = false, bool p_keep_children = true);
void set_process_mode(ProcessMode p_mode);
ProcessMode get_process_mode() const;
diff --git a/scene/resources/2d/tile_set.cpp b/scene/resources/2d/tile_set.cpp
index 66d97398b9..6ae97944e0 100644
--- a/scene/resources/2d/tile_set.cpp
+++ b/scene/resources/2d/tile_set.cpp
@@ -6918,6 +6918,7 @@ void TileData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_terrain"), &TileData::get_terrain);
ClassDB::bind_method(D_METHOD("set_terrain_peering_bit", "peering_bit", "terrain"), &TileData::set_terrain_peering_bit);
ClassDB::bind_method(D_METHOD("get_terrain_peering_bit", "peering_bit"), &TileData::get_terrain_peering_bit);
+ ClassDB::bind_method(D_METHOD("is_valid_terrain_peering_bit", "peering_bit"), &TileData::is_valid_terrain_peering_bit);
// Navigation
ClassDB::bind_method(D_METHOD("set_navigation_polygon", "layer_id", "navigation_polygon"), &TileData::set_navigation_polygon);
diff --git a/scene/resources/3d/height_map_shape_3d.cpp b/scene/resources/3d/height_map_shape_3d.cpp
index 35c905bd43..5b55b66152 100644
--- a/scene/resources/3d/height_map_shape_3d.cpp
+++ b/scene/resources/3d/height_map_shape_3d.cpp
@@ -30,6 +30,7 @@
#include "height_map_shape_3d.h"
+#include "core/io/image.h"
#include "servers/physics_server_3d.h"
Vector<Vector3> HeightMapShape3D::get_debug_mesh_lines() const {
@@ -187,6 +188,104 @@ real_t HeightMapShape3D::get_max_height() const {
return max_height;
}
+void HeightMapShape3D::update_map_data_from_image(const Ref<Image> &p_image, real_t p_height_min, real_t p_height_max) {
+ ERR_FAIL_COND_MSG(p_image.is_null(), "Heightmap update image requires a valid Image reference.");
+ ERR_FAIL_COND_MSG(p_image->get_format() != Image::FORMAT_RF && p_image->get_format() != Image::FORMAT_RH && p_image->get_format() != Image::FORMAT_R8, "Heightmap update image requires Image in format FORMAT_RF (32 bit), FORMAT_RH (16 bit), or FORMAT_R8 (8 bit).");
+ ERR_FAIL_COND_MSG(p_image->get_width() < 2, "Heightmap update image requires a minimum Image width of 2.");
+ ERR_FAIL_COND_MSG(p_image->get_height() < 2, "Heightmap update image requires a minimum Image height of 2.");
+ ERR_FAIL_COND_MSG(p_height_min > p_height_max, "Heightmap update image requires height_max to be greater than height_min.");
+
+ map_width = p_image->get_width();
+ map_depth = p_image->get_height();
+ map_data.resize(map_width * map_depth);
+
+ real_t new_min_height = FLT_MAX;
+ real_t new_max_height = -FLT_MAX;
+
+ float remap_height_min = float(p_height_min);
+ float remap_height_max = float(p_height_max);
+
+ real_t *map_data_ptrw = map_data.ptrw();
+
+ switch (p_image->get_format()) {
+ case Image::FORMAT_RF: {
+ const float *image_data_ptr = (float *)p_image->get_data().ptr();
+
+ for (int i = 0; i < map_data.size(); i++) {
+ float pixel_value = image_data_ptr[i];
+
+ DEV_ASSERT(pixel_value >= 0.0 && pixel_value <= 1.0);
+
+ real_t height_value = Math::remap(pixel_value, 0.0f, 1.0f, remap_height_min, remap_height_max);
+
+ if (height_value < new_min_height) {
+ new_min_height = height_value;
+ }
+ if (height_value > new_max_height) {
+ new_max_height = height_value;
+ }
+
+ map_data_ptrw[i] = height_value;
+ }
+
+ } break;
+
+ case Image::FORMAT_RH: {
+ const uint16_t *image_data_ptr = (uint16_t *)p_image->get_data().ptr();
+
+ for (int i = 0; i < map_data.size(); i++) {
+ float pixel_value = Math::half_to_float(image_data_ptr[i]);
+
+ DEV_ASSERT(pixel_value >= 0.0 && pixel_value <= 1.0);
+
+ real_t height_value = Math::remap(pixel_value, 0.0f, 1.0f, remap_height_min, remap_height_max);
+
+ if (height_value < new_min_height) {
+ new_min_height = height_value;
+ }
+ if (height_value > new_max_height) {
+ new_max_height = height_value;
+ }
+
+ map_data_ptrw[i] = height_value;
+ }
+
+ } break;
+
+ case Image::FORMAT_R8: {
+ const uint8_t *image_data_ptr = (uint8_t *)p_image->get_data().ptr();
+
+ for (int i = 0; i < map_data.size(); i++) {
+ float pixel_value = float(image_data_ptr[i] / 255.0);
+
+ DEV_ASSERT(pixel_value >= 0.0 && pixel_value <= 1.0);
+
+ real_t height_value = Math::remap(pixel_value, 0.0f, 1.0f, remap_height_min, remap_height_max);
+
+ if (height_value < new_min_height) {
+ new_min_height = height_value;
+ }
+ if (height_value > new_max_height) {
+ new_max_height = height_value;
+ }
+
+ map_data_ptrw[i] = height_value;
+ }
+
+ } break;
+
+ default: {
+ return;
+ }
+ }
+
+ min_height = new_min_height;
+ max_height = new_max_height;
+
+ _update_shape();
+ emit_changed();
+}
+
void HeightMapShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_map_width", "width"), &HeightMapShape3D::set_map_width);
ClassDB::bind_method(D_METHOD("get_map_width"), &HeightMapShape3D::get_map_width);
@@ -197,6 +296,8 @@ void HeightMapShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_min_height"), &HeightMapShape3D::get_min_height);
ClassDB::bind_method(D_METHOD("get_max_height"), &HeightMapShape3D::get_max_height);
+ ClassDB::bind_method(D_METHOD("update_map_data_from_image", "image", "height_min", "height_max"), &HeightMapShape3D::update_map_data_from_image);
+
ADD_PROPERTY(PropertyInfo(Variant::INT, "map_width", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), "set_map_width", "get_map_width");
ADD_PROPERTY(PropertyInfo(Variant::INT, "map_depth", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater"), "set_map_depth", "get_map_depth");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "map_data"), "set_map_data", "get_map_data");
diff --git a/scene/resources/3d/height_map_shape_3d.h b/scene/resources/3d/height_map_shape_3d.h
index 363d9ec0e9..33ba9c4472 100644
--- a/scene/resources/3d/height_map_shape_3d.h
+++ b/scene/resources/3d/height_map_shape_3d.h
@@ -33,6 +33,8 @@
#include "scene/resources/3d/shape_3d.h"
+class Image;
+
class HeightMapShape3D : public Shape3D {
GDCLASS(HeightMapShape3D, Shape3D);
@@ -57,6 +59,8 @@ public:
real_t get_min_height() const;
real_t get_max_height() const;
+ void update_map_data_from_image(const Ref<Image> &p_image, real_t p_height_min, real_t p_height_max);
+
virtual Vector<Vector3> get_debug_mesh_lines() const override;
virtual real_t get_enclosing_radius() const override;
diff --git a/scene/theme/default_theme.cpp b/scene/theme/default_theme.cpp
index e4c469b752..c45f52ec9e 100644
--- a/scene/theme/default_theme.cpp
+++ b/scene/theme/default_theme.cpp
@@ -936,8 +936,6 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("separator", "VSeparator", separator_vertical);
theme->set_icon("close", "Icons", icons["close"]);
- theme->set_font("normal", "Fonts", Ref<Font>());
- theme->set_font("large", "Fonts", Ref<Font>());
theme->set_constant("separation", "HSeparator", Math::round(4 * scale));
theme->set_constant("separation", "VSeparator", Math::round(4 * scale));
diff --git a/servers/display_server.cpp b/servers/display_server.cpp
index 8e6c659565..9ceb6909fe 100644
--- a/servers/display_server.cpp
+++ b/servers/display_server.cpp
@@ -1014,6 +1014,8 @@ void DisplayServer::_bind_methods() {
BIND_ENUM_CONSTANT(FEATURE_SCREEN_CAPTURE);
BIND_ENUM_CONSTANT(FEATURE_STATUS_INDICATOR);
BIND_ENUM_CONSTANT(FEATURE_NATIVE_HELP);
+ BIND_ENUM_CONSTANT(FEATURE_NATIVE_DIALOG_INPUT);
+ BIND_ENUM_CONSTANT(FEATURE_NATIVE_DIALOG_FILE);
BIND_ENUM_CONSTANT(MOUSE_MODE_VISIBLE);
BIND_ENUM_CONSTANT(MOUSE_MODE_HIDDEN);
diff --git a/servers/display_server.h b/servers/display_server.h
index 39a80ae7c1..f1a98c2c17 100644
--- a/servers/display_server.h
+++ b/servers/display_server.h
@@ -140,6 +140,8 @@ public:
FEATURE_SCREEN_CAPTURE,
FEATURE_STATUS_INDICATOR,
FEATURE_NATIVE_HELP,
+ FEATURE_NATIVE_DIALOG_INPUT,
+ FEATURE_NATIVE_DIALOG_FILE,
};
virtual bool has_feature(Feature p_feature) const = 0;
diff --git a/servers/physics_server_3d.cpp b/servers/physics_server_3d.cpp
index 15d43ff5dd..f56ef11c14 100644
--- a/servers/physics_server_3d.cpp
+++ b/servers/physics_server_3d.cpp
@@ -985,6 +985,9 @@ void PhysicsServer3D::_bind_methods() {
BIND_ENUM_CONSTANT(G6DOF_JOINT_LINEAR_DAMPING);
BIND_ENUM_CONSTANT(G6DOF_JOINT_LINEAR_MOTOR_TARGET_VELOCITY);
BIND_ENUM_CONSTANT(G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT);
+ BIND_ENUM_CONSTANT(G6DOF_JOINT_LINEAR_SPRING_STIFFNESS);
+ BIND_ENUM_CONSTANT(G6DOF_JOINT_LINEAR_SPRING_DAMPING);
+ BIND_ENUM_CONSTANT(G6DOF_JOINT_LINEAR_SPRING_EQUILIBRIUM_POINT);
BIND_ENUM_CONSTANT(G6DOF_JOINT_ANGULAR_LOWER_LIMIT);
BIND_ENUM_CONSTANT(G6DOF_JOINT_ANGULAR_UPPER_LIMIT);
BIND_ENUM_CONSTANT(G6DOF_JOINT_ANGULAR_LIMIT_SOFTNESS);
@@ -994,11 +997,18 @@ void PhysicsServer3D::_bind_methods() {
BIND_ENUM_CONSTANT(G6DOF_JOINT_ANGULAR_ERP);
BIND_ENUM_CONSTANT(G6DOF_JOINT_ANGULAR_MOTOR_TARGET_VELOCITY);
BIND_ENUM_CONSTANT(G6DOF_JOINT_ANGULAR_MOTOR_FORCE_LIMIT);
+ BIND_ENUM_CONSTANT(G6DOF_JOINT_ANGULAR_SPRING_STIFFNESS);
+ BIND_ENUM_CONSTANT(G6DOF_JOINT_ANGULAR_SPRING_DAMPING);
+ BIND_ENUM_CONSTANT(G6DOF_JOINT_ANGULAR_SPRING_EQUILIBRIUM_POINT);
+ BIND_ENUM_CONSTANT(G6DOF_JOINT_MAX);
BIND_ENUM_CONSTANT(G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT);
BIND_ENUM_CONSTANT(G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT);
+ BIND_ENUM_CONSTANT(G6DOF_JOINT_FLAG_ENABLE_ANGULAR_SPRING);
+ BIND_ENUM_CONSTANT(G6DOF_JOINT_FLAG_ENABLE_LINEAR_SPRING);
BIND_ENUM_CONSTANT(G6DOF_JOINT_FLAG_ENABLE_MOTOR);
BIND_ENUM_CONSTANT(G6DOF_JOINT_FLAG_ENABLE_LINEAR_MOTOR);
+ BIND_ENUM_CONSTANT(G6DOF_JOINT_FLAG_MAX);
ClassDB::bind_method(D_METHOD("joint_get_type", "joint"), &PhysicsServer3D::joint_get_type);
diff --git a/servers/rendering/renderer_rd/effects/copy_effects.cpp b/servers/rendering/renderer_rd/effects/copy_effects.cpp
index abbe04b5b2..1568867663 100644
--- a/servers/rendering/renderer_rd/effects/copy_effects.cpp
+++ b/servers/rendering/renderer_rd/effects/copy_effects.cpp
@@ -583,7 +583,7 @@ void CopyEffects::copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffe
RID shader = copy_to_fb.shader.version_get_shader(copy_to_fb.shader_version, mode);
ERR_FAIL_COND(shader.is_null());
- RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, p_rect);
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 0.0, 0, p_rect);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, copy_to_fb.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
if (p_secondary.is_valid()) {
@@ -982,7 +982,7 @@ void CopyEffects::set_color_raster(RID p_dest_texture, const Color &p_color, con
RID shader = copy_to_fb.shader.version_get_shader(copy_to_fb.shader_version, mode);
ERR_FAIL_COND(shader.is_null());
- RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, p_region);
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 0.0, 0, p_region);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, copy_to_fb.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(dest_framebuffer)));
RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
RD::get_singleton()->draw_list_set_push_constant(draw_list, &copy_to_fb.push_constant, sizeof(CopyToFbPushConstant));
diff --git a/servers/rendering/renderer_rd/effects/debug_effects.cpp b/servers/rendering/renderer_rd/effects/debug_effects.cpp
index a57a65fd5a..017ad41fdc 100644
--- a/servers/rendering/renderer_rd/effects/debug_effects.cpp
+++ b/servers/rendering/renderer_rd/effects/debug_effects.cpp
@@ -282,7 +282,7 @@ void DebugEffects::draw_shadow_frustum(RID p_light, const Projection &p_cam_proj
// And draw our frustum.
RD::FramebufferFormatID fb_format_id = RD::get_singleton()->framebuffer_get_format(p_dest_fb);
- RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, rect);
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 0.0, 0, rect);
RID pipeline = shadow_frustum.pipelines[SFP_TRANSPARENT].get_render_pipeline(frustum.vertex_format, fb_format_id);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, pipeline);
@@ -326,7 +326,7 @@ void DebugEffects::draw_shadow_frustum(RID p_light, const Projection &p_cam_proj
rect.size.x *= atlas_rect_norm.size.x;
rect.size.y *= atlas_rect_norm.size.y;
- draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, rect);
+ draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 0.0, 0, rect);
pipeline = shadow_frustum.pipelines[SFP_TRANSPARENT].get_render_pipeline(frustum.vertex_format, fb_format_id);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, pipeline);
diff --git a/servers/rendering/renderer_rd/effects/fsr2.cpp b/servers/rendering/renderer_rd/effects/fsr2.cpp
index bebbf51d51..925352a7d1 100644
--- a/servers/rendering/renderer_rd/effects/fsr2.cpp
+++ b/servers/rendering/renderer_rd/effects/fsr2.cpp
@@ -527,6 +527,7 @@ FSR2Effect::FSR2Effect() {
"\n#define FFX_GLSL 1\n"
"\n#define FFX_FSR2_OPTION_LOW_RESOLUTION_MOTION_VECTORS 1\n"
"\n#define FFX_FSR2_OPTION_HDR_COLOR_INPUT 1\n"
+ "\n#define FFX_FSR2_OPTION_INVERTED_DEPTH 1\n"
"\n#define FFX_FSR2_OPTION_GODOT_REACTIVE_MASK_CLAMP 1\n"
"\n#define FFX_FSR2_OPTION_GODOT_DERIVE_INVALID_MOTION_VECTORS 1\n";
@@ -808,7 +809,7 @@ FSR2Effect::~FSR2Effect() {
FSR2Context *FSR2Effect::create_context(Size2i p_internal_size, Size2i p_target_size) {
FSR2Context *context = memnew(RendererRD::FSR2Context);
- context->fsr_desc.flags = FFX_FSR2_ENABLE_HIGH_DYNAMIC_RANGE;
+ context->fsr_desc.flags = FFX_FSR2_ENABLE_HIGH_DYNAMIC_RANGE | FFX_FSR2_ENABLE_DEPTH_INVERTED;
context->fsr_desc.maxRenderSize.width = p_internal_size.x;
context->fsr_desc.maxRenderSize.height = p_internal_size.y;
context->fsr_desc.displaySize.width = p_target_size.x;
diff --git a/servers/rendering/renderer_rd/effects/ss_effects.cpp b/servers/rendering/renderer_rd/effects/ss_effects.cpp
index bdd687d9f4..3db82c8fbd 100644
--- a/servers/rendering/renderer_rd/effects/ss_effects.cpp
+++ b/servers/rendering/renderer_rd/effects/ss_effects.cpp
@@ -483,8 +483,12 @@ void SSEffects::downsample_depth(Ref<RenderSceneBuffersRD> p_render_buffers, uin
downsample_uniform_set = uniform_set_cache->get_cache_vec(shader, 2, u_depths);
}
- float depth_linearize_mul = -p_projection.columns[3][2] * 0.5;
- float depth_linearize_add = p_projection.columns[2][2];
+ Projection correction;
+ correction.set_depth_correction(false);
+ Projection temp = correction * p_projection;
+
+ float depth_linearize_mul = -temp.columns[3][2];
+ float depth_linearize_add = temp.columns[2][2];
if (depth_linearize_mul * depth_linearize_add < 0) {
depth_linearize_add = -depth_linearize_add;
}
diff --git a/servers/rendering/renderer_rd/environment/gi.cpp b/servers/rendering/renderer_rd/environment/gi.cpp
index 78214ede0b..c7752f8a86 100644
--- a/servers/rendering/renderer_rd/environment/gi.cpp
+++ b/servers/rendering/renderer_rd/environment/gi.cpp
@@ -3407,7 +3407,7 @@ void GI::init(SkyRD *p_sky) {
RD::PipelineDepthStencilState ds;
ds.enable_depth_test = true;
ds.enable_depth_write = true;
- ds.depth_compare_operator = RD::COMPARE_OP_LESS_OR_EQUAL;
+ ds.depth_compare_operator = RD::COMPARE_OP_GREATER_OR_EQUAL;
voxel_gi_debug_shader_version_pipelines[i].setup(voxel_gi_debug_shader_version_shaders[i], RD::RENDER_PRIMITIVE_TRIANGLES, rs, RD::PipelineMultisampleState(), ds, RD::PipelineColorBlendState::create_disabled(), 0);
}
@@ -3575,7 +3575,7 @@ void GI::init(SkyRD *p_sky) {
RD::PipelineDepthStencilState ds;
ds.enable_depth_test = true;
ds.enable_depth_write = true;
- ds.depth_compare_operator = RD::COMPARE_OP_LESS_OR_EQUAL;
+ ds.depth_compare_operator = RD::COMPARE_OP_GREATER_OR_EQUAL;
for (int i = 0; i < SDFGIShader::PROBE_DEBUG_MAX; i++) {
// TODO check if version is enabled
@@ -3810,8 +3810,13 @@ void GI::process_gi(Ref<RenderSceneBuffersRD> p_render_buffers, const RID *p_nor
rbgi->scene_data_ubo = RD::get_singleton()->uniform_buffer_create(sizeof(SceneData));
}
+ Projection correction;
+ correction.set_depth_correction(false);
+
for (uint32_t v = 0; v < p_view_count; v++) {
- RendererRD::MaterialStorage::store_camera(p_projections[v].inverse(), scene_data.inv_projection[v]);
+ Projection temp = correction * p_projections[v];
+
+ RendererRD::MaterialStorage::store_camera(temp.inverse(), scene_data.inv_projection[v]);
scene_data.eye_offset[v][0] = p_eye_offsets[v].x;
scene_data.eye_offset[v][1] = p_eye_offsets[v].y;
scene_data.eye_offset[v][2] = p_eye_offsets[v].z;
diff --git a/servers/rendering/renderer_rd/environment/sky.cpp b/servers/rendering/renderer_rd/environment/sky.cpp
index 41609dc74d..27c07f23fa 100644
--- a/servers/rendering/renderer_rd/environment/sky.cpp
+++ b/servers/rendering/renderer_rd/environment/sky.cpp
@@ -141,7 +141,7 @@ void SkyRD::SkyShaderData::set_code(const String &p_code) {
for (int i = 0; i < SKY_VERSION_MAX; i++) {
RD::PipelineDepthStencilState depth_stencil_state;
depth_stencil_state.enable_depth_test = true;
- depth_stencil_state.depth_compare_operator = RD::COMPARE_OP_LESS_OR_EQUAL;
+ depth_stencil_state.depth_compare_operator = RD::COMPARE_OP_GREATER_OR_EQUAL;
if (scene_singleton->sky.sky_shader.shader.is_variant_enabled(i)) {
RID shader_variant = scene_singleton->sky.sky_shader.shader.version_get_shader(version, i);
@@ -1174,6 +1174,7 @@ void SkyRD::setup_sky(RID p_env, Ref<RenderSceneBuffersRD> p_render_buffers, con
}
Projection correction;
+ correction.set_depth_correction(false, true);
correction.add_jitter_offset(p_jitter);
sky_scene_state.view_count = p_view_count;
@@ -1184,10 +1185,12 @@ void SkyRD::setup_sky(RID p_env, Ref<RenderSceneBuffersRD> p_render_buffers, con
for (uint32_t i = 0; i < p_view_count; i++) {
Projection view_inv_projection = (correction * p_view_projections[i]).inverse();
if (p_view_count > 1) {
+ // Reprojection is used when we need to have things in combined space.
RendererRD::MaterialStorage::store_camera(p_cam_projection * view_inv_projection, sky_scene_state.ubo.combined_reprojection[i]);
} else {
+ // This is unused so just reset to identity.
Projection ident;
- RendererRD::MaterialStorage::store_camera(correction, sky_scene_state.ubo.combined_reprojection[i]);
+ RendererRD::MaterialStorage::store_camera(ident, sky_scene_state.ubo.combined_reprojection[i]);
}
RendererRD::MaterialStorage::store_camera(view_inv_projection, sky_scene_state.ubo.view_inv_projections[i]);
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
index d78f3ba05b..6d2a759c28 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
@@ -2014,7 +2014,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
uint32_t opaque_color_pass_flags = using_motion_pass ? (color_pass_flags & ~COLOR_PASS_FLAG_MOTION_VECTORS) : color_pass_flags;
RID opaque_framebuffer = using_motion_pass ? rb_data->get_color_pass_fb(opaque_color_pass_flags) : color_framebuffer;
RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, PASS_MODE_COLOR, opaque_color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count, 0, spec_constant_base_flags);
- _render_list_with_draw_list(&render_list_params, opaque_framebuffer, load_color ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, depth_pre_pass ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, c, 1.0, 0);
+ _render_list_with_draw_list(&render_list_params, opaque_framebuffer, load_color ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, depth_pre_pass ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, c, 0.0, 0);
}
RD::get_singleton()->draw_command_end_label();
@@ -2629,7 +2629,7 @@ void RenderForwardClustered::_render_shadow_end() {
for (SceneState::ShadowPass &shadow_pass : scene_state.shadow_passes) {
RenderListParameters render_list_parameters(render_list[RENDER_LIST_SECONDARY].elements.ptr() + shadow_pass.element_from, render_list[RENDER_LIST_SECONDARY].element_info.ptr() + shadow_pass.element_from, shadow_pass.element_count, shadow_pass.flip_cull, shadow_pass.pass_mode, 0, true, false, shadow_pass.rp_uniform_set, false, Vector2(), shadow_pass.lod_distance_multiplier, shadow_pass.screen_mesh_lod_threshold, 1, shadow_pass.element_from);
- _render_list_with_draw_list(&render_list_parameters, shadow_pass.framebuffer, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, shadow_pass.initial_depth_action, RD::FINAL_ACTION_STORE, Vector<Color>(), 1.0, 0, shadow_pass.rect);
+ _render_list_with_draw_list(&render_list_parameters, shadow_pass.framebuffer, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, shadow_pass.initial_depth_action, RD::FINAL_ACTION_STORE, Vector<Color>(), 0.0, 0, shadow_pass.rect);
}
RD::get_singleton()->draw_command_end_label();
@@ -2729,7 +2729,7 @@ void RenderForwardClustered::_render_material(const Transform3D &p_cam_transform
Color(0, 0, 0, 0)
};
- RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, clear, 1.0, 0, p_region);
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, clear, 0.0, 0, p_region);
_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), &render_list_params, 0, render_list_params.element_count);
RD::get_singleton()->draw_list_end();
}
@@ -2779,7 +2779,7 @@ void RenderForwardClustered::_render_uv2(const PagedArray<RenderGeometryInstance
Color(0, 0, 0, 0),
Color(0, 0, 0, 0)
};
- RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, clear, 1.0, 0, p_region);
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, clear, 0.0, 0, p_region);
const int uv_offset_count = 9;
static const Vector2 uv_offsets[uv_offset_count] = {
@@ -2885,7 +2885,7 @@ void RenderForwardClustered::_render_sdfgi(Ref<RenderSceneBuffersRD> p_render_bu
}
RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].element_info.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), true, pass_mode, 0, true, false, rp_uniform_set, false);
- _render_list_with_draw_list(&render_list_params, E->value, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, Rect2());
+ _render_list_with_draw_list(&render_list_params, E->value, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 0.0, 0, Rect2());
}
RD::get_singleton()->draw_command_end_label();
@@ -4265,7 +4265,7 @@ RenderForwardClustered::RenderForwardClustered() {
sampler.mag_filter = RD::SAMPLER_FILTER_NEAREST;
sampler.min_filter = RD::SAMPLER_FILTER_NEAREST;
sampler.enable_compare = true;
- sampler.compare_op = RD::COMPARE_OP_LESS;
+ sampler.compare_op = RD::COMPARE_OP_GREATER;
shadow_sampler = RD::get_singleton()->sampler_create(sampler);
}
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
index 12af8822b4..1f12d92754 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
@@ -379,7 +379,7 @@ class RenderForwardClustered : public RendererSceneRenderRD {
template <PassMode p_pass_mode, uint32_t p_color_pass_flags = 0>
_FORCE_INLINE_ void _render_list_template(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderListParameters *p_params, uint32_t p_from_element, uint32_t p_to_element);
void _render_list(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderListParameters *p_params, uint32_t p_from_element, uint32_t p_to_element);
- void _render_list_with_draw_list(RenderListParameters *p_params, RID p_framebuffer, RD::InitialAction p_initial_color_action, RD::FinalAction p_final_color_action, RD::InitialAction p_initial_depth_action, RD::FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2());
+ void _render_list_with_draw_list(RenderListParameters *p_params, RID p_framebuffer, RD::InitialAction p_initial_color_action, RD::FinalAction p_final_color_action, RD::InitialAction p_initial_depth_action, RD::FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 0.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2());
void _update_instance_data_buffer(RenderListType p_render_list);
void _fill_instance_data(RenderListType p_render_list, int *p_render_info = nullptr, uint32_t p_offset = 0, int32_t p_max_elements = -1, bool p_update_buffer = true);
diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
index 0b504eca0a..209fabeddf 100644
--- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
@@ -260,7 +260,7 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
if (depth_test != DEPTH_TEST_DISABLED) {
depth_stencil_state.enable_depth_test = true;
- depth_stencil_state.depth_compare_operator = RD::COMPARE_OP_LESS_OR_EQUAL;
+ depth_stencil_state.depth_compare_operator = RD::COMPARE_OP_GREATER_OR_EQUAL;
depth_stencil_state.enable_depth_write = depth_draw != DEPTH_DRAW_DISABLED ? true : false;
}
bool depth_pre_pass_enabled = bool(GLOBAL_GET("rendering/driver/depth_prepass/enable"));
@@ -827,7 +827,7 @@ void fragment() {
sampler.mag_filter = RD::SAMPLER_FILTER_LINEAR;
sampler.min_filter = RD::SAMPLER_FILTER_LINEAR;
sampler.enable_compare = true;
- sampler.compare_op = RD::COMPARE_OP_LESS;
+ sampler.compare_op = RD::COMPARE_OP_GREATER;
shadow_sampler = RD::get_singleton()->sampler_create(sampler);
}
}
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
index 2f307c62f3..b54f511d05 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
@@ -984,7 +984,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
}
}
- RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, load_color ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, c, 1.0, 0);
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, load_color ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, c, 0.0, 0);
RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer);
if (copy_canvas) {
@@ -1385,7 +1385,7 @@ void RenderForwardMobile::_render_shadow_end() {
for (SceneState::ShadowPass &shadow_pass : scene_state.shadow_passes) {
RenderListParameters render_list_parameters(render_list[RENDER_LIST_SECONDARY].elements.ptr() + shadow_pass.element_from, render_list[RENDER_LIST_SECONDARY].element_info.ptr() + shadow_pass.element_from, shadow_pass.element_count, shadow_pass.flip_cull, shadow_pass.pass_mode, shadow_pass.rp_uniform_set, 0, false, Vector2(), shadow_pass.lod_distance_multiplier, shadow_pass.screen_mesh_lod_threshold, 1, shadow_pass.element_from);
- _render_list_with_draw_list(&render_list_parameters, shadow_pass.framebuffer, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, shadow_pass.initial_depth_action, RD::FINAL_ACTION_STORE, Vector<Color>(), 1.0, 0, shadow_pass.rect);
+ _render_list_with_draw_list(&render_list_parameters, shadow_pass.framebuffer, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, shadow_pass.initial_depth_action, RD::FINAL_ACTION_STORE, Vector<Color>(), 0.0, 0, shadow_pass.rect);
}
RD::get_singleton()->draw_command_end_label();
@@ -1437,7 +1437,7 @@ void RenderForwardMobile::_render_material(const Transform3D &p_cam_transform, c
Color(0, 0, 0, 0),
Color(0, 0, 0, 0)
};
- RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, clear, 1.0, 0, p_region);
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, clear, 0.0, 0, p_region);
_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), &render_list_params, 0, render_list_params.element_count);
RD::get_singleton()->draw_list_end();
}
@@ -1483,7 +1483,7 @@ void RenderForwardMobile::_render_uv2(const PagedArray<RenderGeometryInstance *>
Color(0, 0, 0, 0)
};
- RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, clear, 1.0, 0, p_region);
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, clear, 0.0, 0, p_region);
const int uv_offset_count = 9;
static const Vector2 uv_offsets[uv_offset_count] = {
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
index 5c02204627..f29503e5ec 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
@@ -350,7 +350,7 @@ private:
template <PassMode p_pass_mode>
_FORCE_INLINE_ void _render_list_template(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderListParameters *p_params, uint32_t p_from_element, uint32_t p_to_element);
void _render_list(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderListParameters *p_params, uint32_t p_from_element, uint32_t p_to_element);
- void _render_list_with_draw_list(RenderListParameters *p_params, RID p_framebuffer, RD::InitialAction p_initial_color_action, RD::FinalAction p_final_color_action, RD::InitialAction p_initial_depth_action, RD::FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2());
+ void _render_list_with_draw_list(RenderListParameters *p_params, RID p_framebuffer, RD::InitialAction p_initial_color_action, RD::FinalAction p_final_color_action, RD::InitialAction p_initial_depth_action, RD::FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 0.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2());
RenderList render_list[RENDER_LIST_MAX];
diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
index 95ba76a707..a2f112669c 100644
--- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
@@ -271,7 +271,7 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
if (depth_test != DEPTH_TEST_DISABLED) {
depth_stencil_state.enable_depth_test = true;
- depth_stencil_state.depth_compare_operator = RD::COMPARE_OP_LESS_OR_EQUAL;
+ depth_stencil_state.depth_compare_operator = RD::COMPARE_OP_GREATER_OR_EQUAL;
depth_stencil_state.enable_depth_write = depth_draw != DEPTH_DRAW_DISABLED ? true : false;
}
@@ -731,7 +731,7 @@ void fragment() {
sampler.mag_filter = RD::SAMPLER_FILTER_LINEAR;
sampler.min_filter = RD::SAMPLER_FILTER_LINEAR;
sampler.enable_compare = true;
- sampler.compare_op = RD::COMPARE_OP_LESS;
+ sampler.compare_op = RD::COMPARE_OP_GREATER;
shadow_sampler = RD::get_singleton()->sampler_create(sampler);
}
}
diff --git a/servers/rendering/renderer_rd/shaders/effects/bokeh_dof.glsl b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof.glsl
index fe770ac065..48c1b0a3f6 100644
--- a/servers/rendering/renderer_rd/shaders/effects/bokeh_dof.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof.glsl
@@ -32,9 +32,9 @@ layout(set = 1, binding = 0) uniform sampler2D source_bokeh;
float get_depth_at_pos(vec2 uv) {
float depth = textureLod(source_depth, uv, 0.0).x * 2.0 - 1.0;
if (params.orthogonal) {
- depth = ((depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
+ depth = -(depth * (params.z_far - params.z_near) - (params.z_far + params.z_near)) / 2.0;
} else {
- depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - depth * (params.z_far - params.z_near));
+ depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near + depth * (params.z_far - params.z_near));
}
return depth;
}
diff --git a/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl
index 947aa793d9..2010b58474 100644
--- a/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl
@@ -53,9 +53,9 @@ layout(set = 2, binding = 0) uniform sampler2D original_weight;
float get_depth_at_pos(vec2 uv) {
float depth = textureLod(source_depth, uv, 0.0).x * 2.0 - 1.0;
if (params.orthogonal) {
- depth = ((depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
+ depth = -(depth * (params.z_far - params.z_near) - (params.z_far + params.z_near)) / 2.0;
} else {
- depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - depth * (params.z_far - params.z_near));
+ depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near + depth * (params.z_far - params.z_near));
}
return depth;
}
diff --git a/servers/rendering/renderer_rd/shaders/effects/cube_to_dp.glsl b/servers/rendering/renderer_rd/shaders/effects/cube_to_dp.glsl
index e77d0de719..3fb93dda35 100644
--- a/servers/rendering/renderer_rd/shaders/effects/cube_to_dp.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/cube_to_dp.glsl
@@ -77,8 +77,8 @@ void main() {
float depth_fix = 1.0 / dot(normal, unorm);
depth = 2.0 * depth - 1.0;
- float linear_depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - depth * (params.z_far - params.z_near));
- depth = (linear_depth * depth_fix) / params.z_far;
-
+ float linear_depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near + depth * (params.z_far - params.z_near));
+ // linear_depth equal to view space depth
+ depth = (params.z_far - linear_depth * depth_fix) / params.z_far;
gl_FragDepth = depth;
}
diff --git a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_scale.glsl b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_scale.glsl
index 51caa67d3c..d9e21b8cd1 100644
--- a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_scale.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_scale.glsl
@@ -98,9 +98,9 @@ void main() {
// unproject our Z value so we can use it directly.
depth = depth * 2.0 - 1.0;
if (params.orthogonal) {
- depth = ((depth + (params.camera_z_far + params.camera_z_near) / (params.camera_z_far - params.camera_z_near)) * (params.camera_z_far - params.camera_z_near)) / 2.0;
+ depth = -(depth * (params.camera_z_far - params.camera_z_near) - (params.camera_z_far + params.camera_z_near)) / 2.0;
} else {
- depth = 2.0 * params.camera_z_near * params.camera_z_far / (params.camera_z_far + params.camera_z_near - depth * (params.camera_z_far - params.camera_z_near));
+ depth = 2.0 * params.camera_z_near * params.camera_z_far / (params.camera_z_far + params.camera_z_near + depth * (params.camera_z_far - params.camera_z_near));
}
depth = -depth;
}
diff --git a/servers/rendering/renderer_rd/shaders/environment/gi.glsl b/servers/rendering/renderer_rd/shaders/environment/gi.glsl
index 80ed34cda1..480172f9dc 100644
--- a/servers/rendering/renderer_rd/shaders/environment/gi.glsl
+++ b/servers/rendering/renderer_rd/shaders/environment/gi.glsl
@@ -174,9 +174,9 @@ vec3 reconstruct_position(ivec2 screen_pos) {
pos.z = pos.z * 2.0 - 1.0;
if (params.orthogonal) {
- pos.z = ((pos.z + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
+ pos.z = -(pos.z * (params.z_far - params.z_near) - (params.z_far + params.z_near)) / 2.0;
} else {
- pos.z = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - pos.z * (params.z_far - params.z_near));
+ pos.z = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near + pos.z * (params.z_far - params.z_near));
}
pos.z = -pos.z;
diff --git a/servers/rendering/renderer_rd/shaders/environment/sky.glsl b/servers/rendering/renderer_rd/shaders/environment/sky.glsl
index 4e5b11aed8..35457a2482 100644
--- a/servers/rendering/renderer_rd/shaders/environment/sky.glsl
+++ b/servers/rendering/renderer_rd/shaders/environment/sky.glsl
@@ -25,7 +25,7 @@ params;
void main() {
vec2 base_arr[3] = vec2[](vec2(-1.0, -3.0), vec2(-1.0, 1.0), vec2(3.0, 1.0));
uv_interp = base_arr[gl_VertexIndex];
- gl_Position = vec4(uv_interp, 1.0, 1.0);
+ gl_Position = vec4(uv_interp, 0.0, 1.0);
}
#[fragment]
@@ -158,7 +158,7 @@ vec3 interleaved_gradient_noise(vec2 pos) {
vec4 volumetric_fog_process(vec2 screen_uv) {
#ifdef USE_MULTIVIEW
- vec4 reprojected = sky_scene_data.combined_reprojection[ViewIndex] * (vec4(screen_uv * 2.0 - 1.0, 1.0, 1.0) * sky_scene_data.z_far);
+ vec4 reprojected = sky_scene_data.combined_reprojection[ViewIndex] * vec4(screen_uv * 2.0 - 1.0, 0.0, 1.0); // Unproject at the far plane
vec3 fog_pos = vec3(reprojected.xy / reprojected.w, 1.0) * 0.5 + 0.5;
#else
vec3 fog_pos = vec3(screen_uv, 1.0);
@@ -187,9 +187,11 @@ void main() {
vec3 cube_normal;
#ifdef USE_MULTIVIEW
// In multiview our projection matrices will contain positional and rotational offsets that we need to properly unproject.
- vec4 unproject = vec4(uv_interp.x, -uv_interp.y, 1.0, 1.0);
+ vec4 unproject = vec4(uv_interp.x, -uv_interp.y, 0.0, 1.0); // unproject at the far plane
vec4 unprojected = sky_scene_data.view_inv_projections[ViewIndex] * unproject;
cube_normal = unprojected.xyz / unprojected.w;
+
+ // Unproject will give us the position between the eyes, need to re-offset
cube_normal += sky_scene_data.view_eye_offsets[ViewIndex].xyz;
#else
cube_normal.z = -1.0;
diff --git a/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl b/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl
index 57b9a4c320..d0cfe6a3b8 100644
--- a/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl
+++ b/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl
@@ -416,7 +416,7 @@ void main() {
}
float depth = texture(sampler2D(directional_shadow_atlas, linear_sampler), pssm_coord.xy).r;
- float shadow = exp(min(0.0, (depth - pssm_coord.z)) * z_range * INV_FOG_FADE);
+ float shadow = exp(min(0.0, (pssm_coord.z - depth)) * z_range * INV_FOG_FADE);
shadow = mix(shadow, 1.0, smoothstep(directional_lights.data[i].fade_from, directional_lights.data[i].fade_to, view_pos.z)); //done with negative values for performance
@@ -519,7 +519,7 @@ void main() {
float depth = texture(sampler2D(shadow_atlas, linear_sampler), pos.xy).r;
- shadow_attenuation = mix(1.0 - omni_lights.data[light_index].shadow_opacity, 1.0, exp(min(0.0, (depth - pos.z)) / omni_lights.data[light_index].inv_radius * INV_FOG_FADE));
+ shadow_attenuation = mix(1.0 - omni_lights.data[light_index].shadow_opacity, 1.0, exp(min(0.0, (pos.z - depth)) / omni_lights.data[light_index].inv_radius * INV_FOG_FADE));
}
total_light += light * attenuation * shadow_attenuation * henyey_greenstein(dot(normalize(light_pos - view_pos), normalize(view_pos)), params.phase_g) * omni_lights.data[light_index].volumetric_fog_energy;
}
@@ -597,7 +597,7 @@ void main() {
float depth = texture(sampler2D(shadow_atlas, linear_sampler), pos.xy).r;
- shadow_attenuation = mix(1.0 - spot_lights.data[light_index].shadow_opacity, 1.0, exp(min(0.0, (depth - pos.z)) / spot_lights.data[light_index].inv_radius * INV_FOG_FADE));
+ shadow_attenuation = mix(1.0 - spot_lights.data[light_index].shadow_opacity, 1.0, exp(min(0.0, (pos.z - depth)) / spot_lights.data[light_index].inv_radius * INV_FOG_FADE));
}
total_light += light * attenuation * shadow_attenuation * henyey_greenstein(dot(normalize(light_rel_vec), normalize(view_pos)), params.phase_g) * spot_lights.data[light_index].volumetric_fog_energy;
}
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 6eae64c04e..359d7799e5 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
@@ -487,8 +487,8 @@ void vertex_shader(vec3 vertex_input,
#ifdef MODE_RENDER_DEPTH
if (scene_data.pancake_shadows) {
- if (gl_Position.z <= 0.00001) {
- gl_Position.z = 0.00001;
+ if (gl_Position.z >= 0.9999) {
+ gl_Position.z = 0.9999;
}
}
#endif
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 259edc63a0..c26313092b 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
@@ -475,8 +475,8 @@ void main() {
#ifdef MODE_RENDER_DEPTH
if (scene_data.pancake_shadows) {
- if (gl_Position.z <= 0.00001) {
- gl_Position.z = 0.00001;
+ if (gl_Position.z >= 0.9999) {
+ gl_Position.z = 0.9999;
}
}
#endif // MODE_RENDER_DEPTH
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 e9722bad1f..47e6fe5873 100644
--- a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
@@ -454,7 +454,7 @@ float light_process_omni_shadow(uint idx, vec3 vertex, vec3 normal) {
vec3 v0 = abs(basis_normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);
vec3 tangent = normalize(cross(v0, basis_normal));
vec3 bitangent = normalize(cross(tangent, basis_normal));
- float z_norm = shadow_len * omni_lights.data[idx].inv_radius;
+ float z_norm = 1.0 - shadow_len * omni_lights.data[idx].inv_radius;
tangent *= omni_lights.data[idx].soft_shadow_size * omni_lights.data[idx].soft_shadow_scale;
bitangent *= omni_lights.data[idx].soft_shadow_size * omni_lights.data[idx].soft_shadow_scale;
@@ -479,7 +479,7 @@ float light_process_omni_shadow(uint idx, vec3 vertex, vec3 normal) {
pos.xy = uv_rect.xy + pos.xy * uv_rect.zw;
float d = textureLod(sampler2D(shadow_atlas, SAMPLER_LINEAR_CLAMP), pos.xy, 0.0).r;
- if (d < z_norm) {
+ if (d > z_norm) {
blocker_average += d;
blocker_count += 1.0;
}
@@ -488,11 +488,11 @@ float light_process_omni_shadow(uint idx, vec3 vertex, vec3 normal) {
if (blocker_count > 0.0) {
//blockers found, do soft shadow
blocker_average /= blocker_count;
- float penumbra = (z_norm - blocker_average) / blocker_average;
+ float penumbra = (z_norm + blocker_average) / blocker_average;
tangent *= penumbra;
bitangent *= penumbra;
- z_norm -= omni_lights.data[idx].inv_radius * omni_lights.data[idx].shadow_bias;
+ z_norm += omni_lights.data[idx].inv_radius * omni_lights.data[idx].shadow_bias;
shadow = 0.0;
for (uint i = 0; i < sc_penumbra_shadow_samples; i++) {
@@ -536,6 +536,7 @@ float light_process_omni_shadow(uint idx, vec3 vertex, vec3 normal) {
vec2 pos = shadow_sample.xy / shadow_sample.z;
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);
}
@@ -706,7 +707,7 @@ float light_process_spot_shadow(uint idx, vec3 vertex, vec3 normal) {
vec4 v = vec4(vertex + normal_bias, 1.0);
vec4 splane = (spot_lights.data[idx].shadow_matrix * v);
- splane.z -= spot_lights.data[idx].shadow_bias / (light_length * spot_lights.data[idx].inv_radius);
+ splane.z += spot_lights.data[idx].shadow_bias / (light_length * spot_lights.data[idx].inv_radius);
splane /= splane.w;
float shadow;
diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp
index cf8c29e624..d1ff9fc362 100644
--- a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp
@@ -668,7 +668,9 @@ void LightStorage::update_light_buffers(RenderDataRD *p_render_data, const Paged
light_data.blend_splits = (smode != RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL) && light->directional_blend_splits;
for (int j = 0; j < 4; j++) {
Rect2 atlas_rect = light_instance->shadow_transform[j].atlas_rect;
- Projection matrix = light_instance->shadow_transform[j].camera;
+ Projection correction;
+ correction.set_depth_correction(false, true, false);
+ Projection matrix = correction * light_instance->shadow_transform[j].camera;
float split = light_instance->shadow_transform[MIN(limit, j)].split;
Projection bias;
@@ -967,7 +969,9 @@ void LightStorage::update_light_buffers(RenderDataRD *p_render_data, const Paged
Projection bias;
bias.set_light_bias();
- Projection cm = light_instance->shadow_transform[0].camera;
+ Projection correction;
+ correction.set_depth_correction(false, true, false);
+ Projection cm = correction * light_instance->shadow_transform[0].camera;
Projection shadow_mtx = bias * cm * modelview;
RendererRD::MaterialStorage::store_camera(shadow_mtx, light_data.shadow_matrix);
diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp
index aa69cd8539..16bc211a12 100644
--- a/servers/rendering/renderer_scene_cull.cpp
+++ b/servers/rendering/renderer_scene_cull.cpp
@@ -1720,6 +1720,7 @@ void RendererSceneCull::_update_instance(Instance *p_instance) {
idata.base_rid = p_instance->base;
idata.parent_array_index = p_instance->visibility_parent ? p_instance->visibility_parent->array_index : -1;
idata.visibility_index = p_instance->visibility_index;
+ idata.occlusion_timeout = 0;
for (Instance *E : p_instance->visibility_dependencies) {
Instance *dep_instance = E;
@@ -2775,7 +2776,7 @@ void RendererSceneCull::_scene_cull(CullData &cull_data, InstanceCullResult &cul
#define VIS_RANGE_CHECK ((idata.visibility_index == -1) || _visibility_range_check<false>(cull_data.scenario->instance_visibility[idata.visibility_index], cull_data.cam_transform.origin, cull_data.visibility_viewport_mask) == 0)
#define VIS_PARENT_CHECK (_visibility_parent_check(cull_data, idata))
#define VIS_CHECK (visibility_check < 0 ? (visibility_check = (visibility_flags != InstanceData::FLAG_VISIBILITY_DEPENDENCY_NEEDS_CHECK || (VIS_RANGE_CHECK && VIS_PARENT_CHECK))) : visibility_check)
-#define OCCLUSION_CULLED (cull_data.occlusion_buffer != nullptr && (cull_data.scenario->instance_data[i].flags & InstanceData::FLAG_IGNORE_OCCLUSION_CULLING) == 0 && cull_data.occlusion_buffer->is_occluded(cull_data.scenario->instance_aabbs[i].bounds, cull_data.cam_transform.origin, inv_cam_transform, *cull_data.camera_matrix, z_near))
+#define OCCLUSION_CULLED (cull_data.occlusion_buffer != nullptr && (cull_data.scenario->instance_data[i].flags & InstanceData::FLAG_IGNORE_OCCLUSION_CULLING) == 0 && cull_data.occlusion_buffer->is_occluded(cull_data.scenario->instance_aabbs[i].bounds, cull_data.cam_transform.origin, inv_cam_transform, *cull_data.camera_matrix, z_near, cull_data.scenario->instance_data[i].occlusion_timeout))
if (!HIDDEN_BY_VISIBILITY_CHECKS) {
if ((LAYER_CHECK && IN_FRUSTUM(cull_data.cull->frustum) && VIS_CHECK && !OCCLUSION_CULLED) || (cull_data.scenario->instance_data[i].flags & InstanceData::FLAG_IGNORE_ALL_CULLING)) {
@@ -4252,6 +4253,7 @@ RendererSceneCull::RendererSceneCull() {
indexer_update_iterations = GLOBAL_GET("rendering/limits/spatial_indexer/update_iterations_per_frame");
thread_cull_threshold = GLOBAL_GET("rendering/limits/spatial_indexer/threaded_cull_minimum_instances");
thread_cull_threshold = MAX(thread_cull_threshold, (uint32_t)WorkerThreadPool::get_singleton()->get_thread_count()); //make sure there is at least one thread per CPU
+ RendererSceneOcclusionCull::HZBuffer::occlusion_jitter_enabled = GLOBAL_GET("rendering/occlusion_culling/jitter_projection");
dummy_occlusion_culling = memnew(RendererSceneOcclusionCull);
diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h
index 341ba0e3b0..0039d14475 100644
--- a/servers/rendering/renderer_scene_cull.h
+++ b/servers/rendering/renderer_scene_cull.h
@@ -286,6 +286,13 @@ public:
Instance *instance = nullptr;
int32_t parent_array_index = -1;
int32_t visibility_index = -1;
+
+ // Each time occlusion culling determines an instance is visible,
+ // set this to occlusion_frame plus some delay.
+ // Once the timeout is reached, allow the instance to be occlusion culled.
+ // This creates a delay for occlusion culling, which prevents flickering
+ // when jittering the raster occlusion projection.
+ uint64_t occlusion_timeout = 0;
};
struct InstanceVisibilityData {
diff --git a/servers/rendering/renderer_scene_occlusion_cull.cpp b/servers/rendering/renderer_scene_occlusion_cull.cpp
index c4f0177c73..1f0239411a 100644
--- a/servers/rendering/renderer_scene_occlusion_cull.cpp
+++ b/servers/rendering/renderer_scene_occlusion_cull.cpp
@@ -43,6 +43,8 @@ const Vector3 RendererSceneOcclusionCull::HZBuffer::corners[8] = {
Vector3(1, 1, 1)
};
+bool RendererSceneOcclusionCull::HZBuffer::occlusion_jitter_enabled = false;
+
bool RendererSceneOcclusionCull::HZBuffer::is_empty() const {
return sizes.is_empty();
}
@@ -66,6 +68,8 @@ void RendererSceneOcclusionCull::HZBuffer::clear() {
}
void RendererSceneOcclusionCull::HZBuffer::resize(const Size2i &p_size) {
+ occlusion_buffer_size = p_size;
+
if (p_size == Size2i()) {
clear();
return;
@@ -124,6 +128,9 @@ void RendererSceneOcclusionCull::HZBuffer::resize(const Size2i &p_size) {
}
void RendererSceneOcclusionCull::HZBuffer::update_mips() {
+ // Keep this up to date as a local to be used for occlusion timers.
+ occlusion_frame = Engine::get_singleton()->get_frames_drawn();
+
if (sizes.is_empty()) {
return;
}
diff --git a/servers/rendering/renderer_scene_occlusion_cull.h b/servers/rendering/renderer_scene_occlusion_cull.h
index 149d7b1cdb..5adba5dc6a 100644
--- a/servers/rendering/renderer_scene_occlusion_cull.h
+++ b/servers/rendering/renderer_scene_occlusion_cull.h
@@ -53,14 +53,10 @@ public:
PackedByteArray debug_data;
float debug_tex_range = 0.0f;
- public:
- bool is_empty() const;
- virtual void clear();
- virtual void resize(const Size2i &p_size);
-
- void update_mips();
+ uint64_t occlusion_frame = 0;
+ Size2i occlusion_buffer_size;
- _FORCE_INLINE_ bool is_occluded(const real_t p_bounds[6], const Vector3 &p_cam_position, const Transform3D &p_cam_inv_transform, const Projection &p_cam_projection, real_t p_near) const {
+ _FORCE_INLINE_ bool _is_occluded(const real_t p_bounds[6], const Vector3 &p_cam_position, const Transform3D &p_cam_inv_transform, const Projection &p_cam_projection, real_t p_near) const {
if (is_empty()) {
return false;
}
@@ -154,7 +150,47 @@ public:
return !visible;
}
+ public:
+ static bool occlusion_jitter_enabled;
+
+ bool is_empty() const;
+ virtual void clear();
+ virtual void resize(const Size2i &p_size);
+
+ void update_mips();
+
+ // Thin wrapper around _is_occluded(),
+ // allowing occlusion timers to delay the disappearance
+ // of objects to prevent flickering when using jittering.
+ _FORCE_INLINE_ bool is_occluded(const real_t p_bounds[6], const Vector3 &p_cam_position, const Transform3D &p_cam_inv_transform, const Projection &p_cam_projection, real_t p_near, uint64_t &r_occlusion_timeout) const {
+ bool occluded = _is_occluded(p_bounds, p_cam_position, p_cam_inv_transform, p_cam_projection, p_near);
+
+ // Special case, temporal jitter disabled,
+ // so we don't use occlusion timers.
+ if (!occlusion_jitter_enabled) {
+ return occluded;
+ }
+
+ if (!occluded) {
+//#define DEBUG_RASTER_OCCLUSION_JITTER
+#ifdef DEBUG_RASTER_OCCLUSION_JITTER
+ r_occlusion_timeout = occlusion_frame + 1;
+#else
+ r_occlusion_timeout = occlusion_frame + 9;
+#endif
+ } else if (r_occlusion_timeout) {
+ // Regular timeout, allow occlusion culling
+ // to proceed as normal after the delay.
+ if (occlusion_frame >= r_occlusion_timeout) {
+ r_occlusion_timeout = 0;
+ }
+ }
+
+ return occluded && !r_occlusion_timeout;
+ }
+
RID get_debug_texture();
+ const Size2i &get_occlusion_buffer_size() const { return occlusion_buffer_size; }
virtual ~HZBuffer(){};
};
diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h
index 9a898a2fca..c8277024cf 100644
--- a/servers/rendering/rendering_device.h
+++ b/servers/rendering/rendering_device.h
@@ -1103,7 +1103,7 @@ private:
public:
DrawListID draw_list_begin_for_screen(DisplayServer::WindowID p_screen = 0, const Color &p_clear_color = Color());
- DrawListID draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2());
+ DrawListID draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 0.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2());
void draw_list_set_blend_constants(DrawListID p_list, const Color &p_color);
void draw_list_bind_render_pipeline(DrawListID p_list, RID p_render_pipeline);
diff --git a/tests/core/io/test_marshalls.h b/tests/core/io/test_marshalls.h
index 3c0ba611c6..de8d6e1406 100644
--- a/tests/core/io/test_marshalls.h
+++ b/tests/core/io/test_marshalls.h
@@ -160,7 +160,7 @@ TEST_CASE("[Marshalls] NIL Variant encoding") {
uint8_t buffer[4];
CHECK(encode_variant(variant, buffer, r_len) == OK);
- CHECK_MESSAGE(r_len == 4, "Length == 4 bytes for Variant::Type");
+ CHECK_MESSAGE(r_len == 4, "Length == 4 bytes for header");
CHECK_MESSAGE(buffer[0] == 0x00, "Variant::NIL");
CHECK(buffer[1] == 0x00);
CHECK(buffer[2] == 0x00);
@@ -174,7 +174,7 @@ TEST_CASE("[Marshalls] INT 32 bit Variant encoding") {
uint8_t buffer[8];
CHECK(encode_variant(variant, buffer, r_len) == OK);
- CHECK_MESSAGE(r_len == 8, "Length == 4 bytes for Variant::Type + 4 bytes for int32_t");
+ CHECK_MESSAGE(r_len == 8, "Length == 4 bytes for header + 4 bytes for int32_t");
CHECK_MESSAGE(buffer[0] == 0x02, "Variant::INT");
CHECK(buffer[1] == 0x00);
CHECK(buffer[2] == 0x00);
@@ -192,10 +192,10 @@ TEST_CASE("[Marshalls] INT 64 bit Variant encoding") {
uint8_t buffer[12];
CHECK(encode_variant(variant, buffer, r_len) == OK);
- CHECK_MESSAGE(r_len == 12, "Length == 4 bytes for Variant::Type + 8 bytes for int64_t");
+ CHECK_MESSAGE(r_len == 12, "Length == 4 bytes for header + 8 bytes for int64_t");
CHECK_MESSAGE(buffer[0] == 0x02, "Variant::INT");
CHECK(buffer[1] == 0x00);
- CHECK_MESSAGE(buffer[2] == 0x01, "ENCODE_FLAG_64");
+ CHECK_MESSAGE(buffer[2] == 0x01, "HEADER_DATA_FLAG_64");
CHECK(buffer[3] == 0x00);
// Check value
CHECK(buffer[4] == 0xef);
@@ -214,7 +214,7 @@ TEST_CASE("[Marshalls] FLOAT single precision Variant encoding") {
uint8_t buffer[8];
CHECK(encode_variant(variant, buffer, r_len) == OK);
- CHECK_MESSAGE(r_len == 8, "Length == 4 bytes for Variant::Type + 4 bytes for float");
+ CHECK_MESSAGE(r_len == 8, "Length == 4 bytes for header + 4 bytes for float");
CHECK_MESSAGE(buffer[0] == 0x03, "Variant::FLOAT");
CHECK(buffer[1] == 0x00);
CHECK(buffer[2] == 0x00);
@@ -232,10 +232,10 @@ TEST_CASE("[Marshalls] FLOAT double precision Variant encoding") {
uint8_t buffer[12];
CHECK(encode_variant(variant, buffer, r_len) == OK);
- CHECK_MESSAGE(r_len == 12, "Length == 4 bytes for Variant::Type + 8 bytes for double");
+ CHECK_MESSAGE(r_len == 12, "Length == 4 bytes for header + 8 bytes for double");
CHECK_MESSAGE(buffer[0] == 0x03, "Variant::FLOAT");
CHECK(buffer[1] == 0x00);
- CHECK_MESSAGE(buffer[2] == 0x01, "ENCODE_FLAG_64");
+ CHECK_MESSAGE(buffer[2] == 0x01, "HEADER_DATA_FLAG_64");
CHECK(buffer[3] == 0x00);
// Check value
CHECK(buffer[4] == 0x55);
@@ -292,7 +292,7 @@ TEST_CASE("[Marshalls] INT 64 bit Variant decoding") {
Variant variant;
int r_len;
uint8_t buffer[] = {
- 0x02, 0x00, 0x01, 0x00, // Variant::INT & ENCODE_FLAG_64
+ 0x02, 0x00, 0x01, 0x00, // Variant::INT, HEADER_DATA_FLAG_64
0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0xf1 // value
};
@@ -318,7 +318,7 @@ TEST_CASE("[Marshalls] FLOAT double precision Variant decoding") {
Variant variant;
int r_len;
uint8_t buffer[] = {
- 0x03, 0x00, 0x01, 0x00, // Variant::FLOAT & ENCODE_FLAG_64
+ 0x03, 0x00, 0x01, 0x00, // Variant::FLOAT, HEADER_DATA_FLAG_64
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xd5, 0x3f // value
};
@@ -326,6 +326,66 @@ TEST_CASE("[Marshalls] FLOAT double precision Variant decoding") {
CHECK(r_len == 12);
CHECK(variant == Variant(0.33333333333333333));
}
+
+TEST_CASE("[Marshalls] Typed array encoding") {
+ int r_len;
+ Array array;
+ array.set_typed(Variant::INT, StringName(), Ref<Script>());
+ array.push_back(Variant(uint64_t(0x0f123456789abcdef)));
+ uint8_t buffer[24];
+
+ CHECK(encode_variant(array, buffer, r_len) == OK);
+ CHECK_MESSAGE(r_len == 24, "Length == 4 bytes for header + 4 bytes for array type + 4 bytes for array size + 12 bytes for element");
+ CHECK_MESSAGE(buffer[0] == 0x1c, "Variant::ARRAY");
+ CHECK(buffer[1] == 0x00);
+ CHECK_MESSAGE(buffer[2] == 0x01, "HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN");
+ CHECK(buffer[3] == 0x00);
+ // Check array type.
+ CHECK_MESSAGE(buffer[4] == 0x02, "Variant::INT");
+ CHECK(buffer[5] == 0x00);
+ CHECK(buffer[6] == 0x00);
+ CHECK(buffer[7] == 0x00);
+ // Check array size.
+ CHECK(buffer[8] == 0x01);
+ CHECK(buffer[9] == 0x00);
+ CHECK(buffer[10] == 0x00);
+ CHECK(buffer[11] == 0x00);
+ // Check element type.
+ CHECK_MESSAGE(buffer[12] == 0x02, "Variant::INT");
+ CHECK(buffer[13] == 0x00);
+ CHECK_MESSAGE(buffer[14] == 0x01, "HEADER_DATA_FLAG_64");
+ CHECK(buffer[15] == 0x00);
+ // Check element value.
+ CHECK(buffer[16] == 0xef);
+ CHECK(buffer[17] == 0xcd);
+ CHECK(buffer[18] == 0xab);
+ CHECK(buffer[19] == 0x89);
+ CHECK(buffer[20] == 0x67);
+ CHECK(buffer[21] == 0x45);
+ CHECK(buffer[22] == 0x23);
+ CHECK(buffer[23] == 0xf1);
+}
+
+TEST_CASE("[Marshalls] Typed array decoding") {
+ Variant variant;
+ int r_len;
+ uint8_t buffer[] = {
+ 0x1c, 0x00, 0x01, 0x00, // Variant::ARRAY, HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN
+ 0x02, 0x00, 0x00, 0x00, // Array type (Variant::INT).
+ 0x01, 0x00, 0x00, 0x00, // Array size.
+ 0x02, 0x00, 0x01, 0x00, // Element type (Variant::INT, HEADER_DATA_FLAG_64).
+ 0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0xf1, // Element value.
+ };
+
+ CHECK(decode_variant(variant, buffer, 24, &r_len) == OK);
+ CHECK(r_len == 24);
+ CHECK(variant.get_type() == Variant::ARRAY);
+ Array array = variant;
+ CHECK(array.get_typed_builtin() == Variant::INT);
+ CHECK(array.size() == 1);
+ CHECK(array[0] == Variant(uint64_t(0x0f123456789abcdef)));
+}
+
} // namespace TestMarshalls
#endif // TEST_MARSHALLS_H