summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/io/marshalls.cpp21
-rw-r--r--core/io/marshalls.h2
-rw-r--r--core/math/math_funcs.h10
-rw-r--r--core/variant/variant_call.cpp2
-rw-r--r--doc/classes/CharacterBody3D.xml8
-rw-r--r--doc/classes/OptionButton.xml4
-rw-r--r--doc/classes/PhysicsServer2D.xml17
-rw-r--r--doc/classes/ProjectSettings.xml3
-rw-r--r--editor/debugger/editor_debugger_tree.cpp3
-rw-r--r--editor/editor_file_dialog.cpp2
-rw-r--r--editor/editor_inspector.cpp5
-rw-r--r--editor/editor_properties.cpp2
-rw-r--r--editor/plugin_config_dialog.cpp8
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp6
-rw-r--r--editor/plugins/editor_preview_plugins.cpp3
-rw-r--r--editor/plugins/node_3d_editor_gizmos.cpp2
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp2
-rw-r--r--editor/plugins/script_editor_plugin.cpp4
-rw-r--r--editor/plugins/tiles/tile_map_editor.cpp4
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp4
-rw-r--r--editor/rename_dialog.cpp4
-rw-r--r--editor/scene_tree_dock.cpp3
-rw-r--r--modules/gdscript/gdscript.cpp4
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp70
-rw-r--r--modules/gdscript/gdscript_compiler.cpp56
-rw-r--r--modules/gdscript/gdscript_parser.h1
-rw-r--r--modules/gdscript/gdscript_vm.cpp20
-rw-r--r--modules/gdscript/gdscript_warning.cpp5
-rw-r--r--modules/gdscript/gdscript_warning.h1
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/constructor_call_type.gd10
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/constructor_call_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_builtin_method.gd3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_builtin_method.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_custom_method.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_custom_method.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_gd_utility.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_gd_utility.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_native_method.gd3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_native_method.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_utility.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_utility.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_as_const.gd29
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_as_const.out17
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/void_assignment.gd6
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/void_assignment.out5
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.gd4
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.out6
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.gd4
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.out6
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.gd4
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.out6
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd8
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/typed_assignment.gd9
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/typed_assignment.out12
-rw-r--r--modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp1
-rw-r--r--modules/gridmap/editor/grid_map_editor_plugin.cpp1
-rw-r--r--modules/gridmap/grid_map.cpp9
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs9
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs10
-rw-r--r--modules/noise/noise_texture_2d.cpp1
-rw-r--r--platform/android/export/export_plugin.cpp10
-rw-r--r--platform/android/export/gradle_export_util.cpp58
-rw-r--r--platform/android/export/gradle_export_util.h16
-rw-r--r--platform/android/java/app/AndroidManifest.xml1
-rw-r--r--platform/linuxbsd/detect.py3
-rw-r--r--platform/windows/display_server_windows.cpp6
-rw-r--r--platform/windows/display_server_windows.h2
-rw-r--r--scene/2d/collision_object_2d.cpp1
-rw-r--r--scene/2d/cpu_particles_2d.cpp1
-rw-r--r--scene/2d/gpu_particles_2d.cpp1
-rw-r--r--scene/2d/joint_2d.cpp1
-rw-r--r--scene/2d/light_2d.cpp1
-rw-r--r--scene/2d/light_occluder_2d.cpp3
-rw-r--r--scene/2d/navigation_agent_2d.cpp1
-rw-r--r--scene/2d/navigation_link_2d.cpp1
-rw-r--r--scene/2d/navigation_obstacle_2d.cpp1
-rw-r--r--scene/2d/navigation_region_2d.cpp1
-rw-r--r--scene/2d/polygon_2d.cpp1
-rw-r--r--scene/2d/skeleton_2d.cpp2
-rw-r--r--scene/2d/tile_map.cpp4
-rw-r--r--scene/3d/camera_3d.cpp2
-rw-r--r--scene/3d/collision_object_3d.cpp6
-rw-r--r--scene/3d/cpu_particles_3d.cpp1
-rw-r--r--scene/3d/decal.cpp1
-rw-r--r--scene/3d/fog_volume.cpp1
-rw-r--r--scene/3d/gpu_particles_3d.cpp1
-rw-r--r--scene/3d/gpu_particles_collision_3d.cpp2
-rw-r--r--scene/3d/joint_3d.cpp1
-rw-r--r--scene/3d/label_3d.cpp1
-rw-r--r--scene/3d/light_3d.cpp1
-rw-r--r--scene/3d/lightmap_gi.cpp1
-rw-r--r--scene/3d/navigation_agent_3d.cpp1
-rw-r--r--scene/3d/navigation_link_3d.cpp2
-rw-r--r--scene/3d/navigation_obstacle_3d.cpp1
-rw-r--r--scene/3d/navigation_region_3d.cpp3
-rw-r--r--scene/3d/occluder_instance_3d.cpp15
-rw-r--r--scene/3d/path_3d.cpp1
-rw-r--r--scene/3d/physics_body_3d.cpp10
-rw-r--r--scene/3d/physics_body_3d.h2
-rw-r--r--scene/3d/reflection_probe.cpp1
-rw-r--r--scene/3d/skeleton_3d.cpp2
-rw-r--r--scene/3d/soft_body_3d.cpp1
-rw-r--r--scene/3d/sprite_3d.cpp1
-rw-r--r--scene/3d/visible_on_screen_notifier_3d.cpp1
-rw-r--r--scene/3d/visual_instance_3d.cpp1
-rw-r--r--scene/3d/voxel_gi.cpp2
-rw-r--r--scene/gui/file_dialog.cpp2
-rw-r--r--scene/gui/menu_bar.cpp1
-rw-r--r--scene/gui/tab_bar.cpp31
-rw-r--r--scene/gui/tree.cpp12
-rw-r--r--scene/main/canvas_item.cpp2
-rw-r--r--scene/main/canvas_layer.cpp1
-rw-r--r--scene/main/viewport.cpp6
-rw-r--r--scene/resources/camera_attributes.cpp1
-rw-r--r--scene/resources/canvas_item_material.cpp2
-rw-r--r--scene/resources/environment.cpp1
-rw-r--r--scene/resources/fog_material.cpp1
-rw-r--r--scene/resources/font.cpp90
-rw-r--r--scene/resources/font.h39
-rw-r--r--scene/resources/immediate_mesh.cpp1
-rw-r--r--scene/resources/importer_mesh.cpp22
-rw-r--r--scene/resources/material.cpp2
-rw-r--r--scene/resources/mesh.cpp2
-rw-r--r--scene/resources/multimesh.cpp1
-rw-r--r--scene/resources/particle_process_material.cpp1
-rw-r--r--scene/resources/primitive_meshes.cpp1
-rw-r--r--scene/resources/shader.cpp1
-rw-r--r--scene/resources/sky.cpp1
-rw-r--r--scene/resources/surface_tool.cpp2
-rw-r--r--scene/resources/texture.cpp20
-rw-r--r--scene/resources/visual_shader.cpp4
-rw-r--r--scene/resources/world_2d.cpp3
-rw-r--r--scene/resources/world_3d.cpp3
-rw-r--r--servers/camera/camera_feed.cpp1
-rw-r--r--servers/physics_3d/godot_collision_solver_3d.cpp4
-rw-r--r--servers/physics_3d/godot_space_3d.cpp1
-rw-r--r--servers/physics_server_2d.cpp3
-rw-r--r--servers/physics_server_3d.h1
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp61
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp4
-rw-r--r--servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp13
-rw-r--r--servers/rendering/renderer_scene_occlusion_cull.cpp2
-rw-r--r--servers/xr/xr_interface.cpp1
-rw-r--r--tests/core/object/test_object.h78
-rw-r--r--tests/scene/test_node.h415
-rw-r--r--tests/scene/test_visual_shader.h151
-rw-r--r--tests/test_main.cpp2
153 files changed, 1399 insertions, 245 deletions
diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp
index 9ba653e1a9..9f89f5d8c9 100644
--- a/core/io/marshalls.cpp
+++ b/core/io/marshalls.cpp
@@ -1813,3 +1813,24 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
return OK;
}
+
+Vector<float> vector3_to_float32_array(const Vector3 *vecs, size_t count) {
+ // We always allocate a new array, and we don't memcpy.
+ // We also don't consider returning a pointer to the passed vectors when sizeof(real_t) == 4.
+ // One reason is that we could decide to put a 4th component in Vector3 for SIMD/mobile performance,
+ // which would cause trouble with these optimizations.
+ Vector<float> floats;
+ if (count == 0) {
+ return floats;
+ }
+ floats.resize(count * 3);
+ float *floats_w = floats.ptrw();
+ for (size_t i = 0; i < count; ++i) {
+ const Vector3 v = vecs[i];
+ floats_w[0] = v.x;
+ floats_w[1] = v.y;
+ floats_w[2] = v.z;
+ floats_w += 3;
+ }
+ return floats;
+}
diff --git a/core/io/marshalls.h b/core/io/marshalls.h
index fef3a1c2c1..66e2571066 100644
--- a/core/io/marshalls.h
+++ b/core/io/marshalls.h
@@ -215,4 +215,6 @@ public:
Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len = nullptr, bool p_allow_objects = false, int p_depth = 0);
Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_full_objects = false, int p_depth = 0);
+Vector<float> vector3_to_float32_array(const Vector3 *vecs, size_t count);
+
#endif // MARSHALLS_H
diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h
index 8dff8e6e7e..a998dece2a 100644
--- a/core/math/math_funcs.h
+++ b/core/math/math_funcs.h
@@ -453,7 +453,10 @@ public:
}
static _ALWAYS_INLINE_ double wrapf(double value, double min, double max) {
double range = max - min;
- double result = is_zero_approx(range) ? min : value - (range * Math::floor((value - min) / range));
+ if (is_zero_approx(range)) {
+ return min;
+ }
+ double result = value - (range * Math::floor((value - min) / range));
if (is_equal_approx(result, max)) {
return min;
}
@@ -461,7 +464,10 @@ public:
}
static _ALWAYS_INLINE_ float wrapf(float value, float min, float max) {
float range = max - min;
- float result = is_zero_approx(range) ? min : value - (range * Math::floor((value - min) / range));
+ if (is_zero_approx(range)) {
+ return min;
+ }
+ float result = value - (range * Math::floor((value - min) / range));
if (is_equal_approx(result, max)) {
return min;
}
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index ac569941bf..57d62dac91 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -65,11 +65,13 @@ static _FORCE_INLINE_ void vc_method_call(R (T::*method)(P...) const, Variant *b
template <class T, class... P>
static _FORCE_INLINE_ void vc_method_call(void (T::*method)(P...), Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) {
+ VariantInternal::clear(&r_ret);
call_with_variant_args_dv(VariantGetInternalPtr<T>::get_ptr(base), method, p_args, p_argcount, r_error, p_defvals);
}
template <class T, class... P>
static _FORCE_INLINE_ void vc_method_call(void (T::*method)(P...) const, Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) {
+ VariantInternal::clear(&r_ret);
call_with_variant_argsc_dv(VariantGetInternalPtr<T>::get_ptr(base), method, p_args, p_argcount, r_error, p_defvals);
}
diff --git a/doc/classes/CharacterBody3D.xml b/doc/classes/CharacterBody3D.xml
index 309e2231a4..821117122c 100644
--- a/doc/classes/CharacterBody3D.xml
+++ b/doc/classes/CharacterBody3D.xml
@@ -41,10 +41,16 @@
Returns a [KinematicCollision3D], which contains information about the latest collision that occurred during the last call to [method move_and_slide].
</description>
</method>
+ <method name="get_platform_angular_velocity" qualifiers="const">
+ <return type="Vector3" />
+ <description>
+ Returns the angular velocity of the platform at the last collision point. Only valid after calling [method move_and_slide].
+ </description>
+ </method>
<method name="get_platform_velocity" qualifiers="const">
<return type="Vector3" />
<description>
- Returns the linear velocity of the floor at the last collision point. Only valid after calling [method move_and_slide] and when [method is_on_floor] returns [code]true[/code].
+ Returns the linear velocity of the platform at the last collision point. Only valid after calling [method move_and_slide].
</description>
</method>
<method name="get_position_delta" qualifiers="const">
diff --git a/doc/classes/OptionButton.xml b/doc/classes/OptionButton.xml
index a58a6249ae..fdf0fff0fb 100644
--- a/doc/classes/OptionButton.xml
+++ b/doc/classes/OptionButton.xml
@@ -94,6 +94,8 @@
<return type="int" />
<param index="0" name="from_last" type="bool" default="false" />
<description>
+ Returns the index of the first item which is not disabled, or marked as a separator. If [param from_last] is [code]true[/code], the items will be searched in reverse order.
+ Returns [code]-1[/code] if no item is found.
</description>
</method>
<method name="get_selected_id" qualifiers="const">
@@ -111,6 +113,7 @@
<method name="has_selectable_items" qualifiers="const">
<return type="bool" />
<description>
+ Returns [code]true[/code] if this button contains at least one item which is not disabled, or marked as a separator.
</description>
</method>
<method name="is_item_disabled" qualifiers="const">
@@ -124,6 +127,7 @@
<return type="bool" />
<param index="0" name="idx" type="int" />
<description>
+ Returns [code]true[/code] if the item at index [param idx] is marked as a separator.
</description>
</method>
<method name="remove_item">
diff --git a/doc/classes/PhysicsServer2D.xml b/doc/classes/PhysicsServer2D.xml
index 2d25965180..f1316fa991 100644
--- a/doc/classes/PhysicsServer2D.xml
+++ b/doc/classes/PhysicsServer2D.xml
@@ -805,6 +805,23 @@
Sets a joint parameter. See [enum JointParam] for a list of available parameters.
</description>
</method>
+ <method name="pin_joint_get_param" qualifiers="const">
+ <return type="float" />
+ <param index="0" name="joint" type="RID" />
+ <param index="1" name="param" type="int" enum="PhysicsServer2D.PinJointParam" />
+ <description>
+ Returns the value of a pin joint parameter. See [enum PinJointParam] for a list of available parameters.
+ </description>
+ </method>
+ <method name="pin_joint_set_param">
+ <return type="void" />
+ <param index="0" name="joint" type="RID" />
+ <param index="1" name="param" type="int" enum="PhysicsServer2D.PinJointParam" />
+ <param index="2" name="value" type="float" />
+ <description>
+ Sets a pin joint parameter. See [enum PinJointParam] for a list of available parameters.
+ </description>
+ </method>
<method name="rectangle_shape_create">
<return type="RID" />
<description>
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 67b692a7a0..d4c42e36eb 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -464,9 +464,6 @@
<member name="debug/gdscript/warnings/unused_variable" type="int" setter="" getter="" default="1">
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a local variable is unused.
</member>
- <member name="debug/gdscript/warnings/void_assignment" type="int" setter="" getter="" default="1">
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when assigning the result of a function that returns [code]void[/code] to a variable.
- </member>
<member name="debug/settings/crash_handler/message" type="String" setter="" getter="" default="&quot;Please include this when reporting the bug to the project developer.&quot;">
Message to be displayed before the backtrace when the engine crashes. By default, this message is only used in exported projects due to the editor-only override applied to this setting.
</member>
diff --git a/editor/debugger/editor_debugger_tree.cpp b/editor/debugger/editor_debugger_tree.cpp
index 8b31781c5e..351af4508f 100644
--- a/editor/debugger/editor_debugger_tree.cpp
+++ b/editor/debugger/editor_debugger_tree.cpp
@@ -118,6 +118,7 @@ void EditorDebuggerTree::_scene_tree_rmb_selected(const Vector2 &p_position, Mou
item_menu->add_icon_item(get_theme_icon(SNAME("CreateNewSceneFrom"), SNAME("EditorIcons")), TTR("Save Branch as Scene"), ITEM_MENU_SAVE_REMOTE_NODE);
item_menu->add_icon_item(get_theme_icon(SNAME("CopyNodePath"), SNAME("EditorIcons")), TTR("Copy Node Path"), ITEM_MENU_COPY_NODE_PATH);
item_menu->set_position(get_screen_position() + get_local_mouse_position());
+ item_menu->reset_size();
item_menu->popup();
}
@@ -323,6 +324,8 @@ void EditorDebuggerTree::_item_menu_id_pressed(int p_option) {
file_dialog->add_filter("*." + extensions[i], extensions[i].to_upper());
}
+ String filename = get_selected_path().get_file() + "." + extensions.front()->get().to_lower();
+ file_dialog->set_current_path(filename);
file_dialog->popup_file_dialog();
} break;
case ITEM_MENU_COPY_NODE_PATH: {
diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp
index 55c512f77d..a9c46d4d94 100644
--- a/editor/editor_file_dialog.cpp
+++ b/editor/editor_file_dialog.cpp
@@ -1500,7 +1500,7 @@ void EditorFileDialog::_go_back() {
}
void EditorFileDialog::_go_forward() {
- if (local_history_pos == local_history.size() - 1) {
+ if (local_history_pos >= local_history.size() - 1) {
return;
}
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 88a1bbb4a9..9a3eb75416 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -3365,7 +3365,10 @@ void EditorInspector::set_keying(bool p_active) {
return;
}
keying = p_active;
- update_tree();
+ // Propagate the keying state to its editor properties.
+ Array args;
+ args.append(keying);
+ main_vbox->propagate_call(SNAME("set_keying"), args, true);
}
void EditorInspector::set_read_only(bool p_read_only) {
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index 4c9b18efe7..8c5db444ab 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -4121,6 +4121,8 @@ void EditorPropertyResource::update_property() {
if (use_editor) {
// Open editor directly and hide other such editors which are currently open.
+ // The opened editor is the one that edits the sub-resource, so keying state will be toggled to false.
+ sub_inspector->set_keying(false);
_open_editor_pressed();
if (is_inside_tree()) {
get_tree()->call_deferred(SNAME("call_group"), "_editor_resource_properties", "_fold_other_editors", this);
diff --git a/editor/plugin_config_dialog.cpp b/editor/plugin_config_dialog.cpp
index affb31945d..1f350f1199 100644
--- a/editor/plugin_config_dialog.cpp
+++ b/editor/plugin_config_dialog.cpp
@@ -219,6 +219,7 @@ PluginConfigDialog::PluginConfigDialog() {
GridContainer *grid = memnew(GridContainer);
grid->set_columns(3);
+ grid->set_v_size_flags(Control::SIZE_EXPAND_FILL);
vbox->add_child(grid);
// Plugin Name
@@ -234,6 +235,7 @@ PluginConfigDialog::PluginConfigDialog() {
name_edit = memnew(LineEdit);
name_edit->connect("text_changed", callable_mp(this, &PluginConfigDialog::_on_required_text_changed));
name_edit->set_placeholder("MyPlugin");
+ name_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
grid->add_child(name_edit);
// Subfolder
@@ -248,6 +250,7 @@ PluginConfigDialog::PluginConfigDialog() {
subfolder_edit = memnew(LineEdit);
subfolder_edit->set_placeholder("\"my_plugin\" -> res://addons/my_plugin");
+ subfolder_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
subfolder_edit->connect("text_changed", callable_mp(this, &PluginConfigDialog::_on_required_text_changed));
grid->add_child(subfolder_edit);
@@ -263,6 +266,8 @@ PluginConfigDialog::PluginConfigDialog() {
desc_edit = memnew(TextEdit);
desc_edit->set_custom_minimum_size(Size2(400, 80) * EDSCALE);
desc_edit->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY);
+ desc_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ desc_edit->set_v_size_flags(Control::SIZE_EXPAND_FILL);
grid->add_child(desc_edit);
// Author
@@ -276,6 +281,7 @@ PluginConfigDialog::PluginConfigDialog() {
author_edit = memnew(LineEdit);
author_edit->set_placeholder("Godette");
+ author_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
grid->add_child(author_edit);
// Version
@@ -289,6 +295,7 @@ PluginConfigDialog::PluginConfigDialog() {
version_edit = memnew(LineEdit);
version_edit->set_placeholder("1.0");
+ version_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
grid->add_child(version_edit);
// Language dropdown
@@ -326,6 +333,7 @@ PluginConfigDialog::PluginConfigDialog() {
script_edit = memnew(LineEdit);
script_edit->connect("text_changed", callable_mp(this, &PluginConfigDialog::_on_required_text_changed));
script_edit->set_placeholder("\"plugin.gd\" -> res://addons/my_plugin/plugin.gd");
+ script_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
grid->add_child(script_edit);
// Activate now checkbox
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index aab64587ce..30322a94f1 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -135,7 +135,7 @@ public:
label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
grid_step_x = memnew(SpinBox);
- grid_step_x->set_min(0.01);
+ grid_step_x->set_min(1);
grid_step_x->set_max(SPIN_BOX_GRID_RANGE);
grid_step_x->set_allow_greater(true);
grid_step_x->set_suffix("px");
@@ -144,7 +144,7 @@ public:
child_container->add_child(grid_step_x);
grid_step_y = memnew(SpinBox);
- grid_step_y->set_min(0.01);
+ grid_step_y->set_min(1);
grid_step_y->set_max(SPIN_BOX_GRID_RANGE);
grid_step_y->set_allow_greater(true);
grid_step_y->set_suffix("px");
@@ -4977,7 +4977,7 @@ CanvasItemEditor::CanvasItemEditor() {
SceneTreeDock::get_singleton()->connect("node_created", callable_mp(this, &CanvasItemEditor::_node_created));
SceneTreeDock::get_singleton()->connect("add_node_used", callable_mp(this, &CanvasItemEditor::_reset_create_position));
- EditorNode::get_singleton()->call_deferred(SNAME("connect"), callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(true));
+ EditorNode::get_singleton()->call_deferred(SNAME("connect"), "play_pressed", callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(true));
EditorNode::get_singleton()->call_deferred(SNAME("connect"), "stop_pressed", callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(false));
// A fluid container for all toolbars.
diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp
index 5f9446c3b1..cbe30f7e0f 100644
--- a/editor/plugins/editor_preview_plugins.cpp
+++ b/editor/plugins/editor_preview_plugins.cpp
@@ -436,6 +436,7 @@ EditorMaterialPreviewPlugin::EditorMaterialPreviewPlugin() {
}
EditorMaterialPreviewPlugin::~EditorMaterialPreviewPlugin() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(sphere);
RS::get_singleton()->free(sphere_instance);
RS::get_singleton()->free(viewport);
@@ -767,6 +768,7 @@ EditorMeshPreviewPlugin::EditorMeshPreviewPlugin() {
}
EditorMeshPreviewPlugin::~EditorMeshPreviewPlugin() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
//RS::get_singleton()->free(sphere);
RS::get_singleton()->free(mesh_instance);
RS::get_singleton()->free(viewport);
@@ -867,6 +869,7 @@ EditorFontPreviewPlugin::EditorFontPreviewPlugin() {
}
EditorFontPreviewPlugin::~EditorFontPreviewPlugin() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(canvas_item);
RS::get_singleton()->free(canvas);
RS::get_singleton()->free(viewport);
diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp
index 91c7fbc06a..0df53bddd1 100644
--- a/editor/plugins/node_3d_editor_gizmos.cpp
+++ b/editor/plugins/node_3d_editor_gizmos.cpp
@@ -100,6 +100,7 @@ bool EditorNode3DGizmo::is_editable() const {
}
void EditorNode3DGizmo::clear() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
for (int i = 0; i < instances.size(); i++) {
if (instances[i].instance.is_valid()) {
RS::get_singleton()->free(instances[i].instance);
@@ -809,6 +810,7 @@ void EditorNode3DGizmo::transform() {
}
void EditorNode3DGizmo::free() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
ERR_FAIL_COND(!spatial_node);
ERR_FAIL_COND(!valid);
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 4976c8c750..8e2b9b4bce 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -3557,6 +3557,7 @@ void Node3DEditorViewport::_init_gizmo_instance(int p_idx) {
}
void Node3DEditorViewport::_finish_gizmo_instances() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
for (int i = 0; i < 3; i++) {
RS::get_singleton()->free(move_gizmo_instance[i]);
RS::get_singleton()->free(move_plane_gizmo_instance[i]);
@@ -5536,6 +5537,7 @@ Node3DEditorViewportContainer::Node3DEditorViewportContainer() {
Node3DEditor *Node3DEditor::singleton = nullptr;
Node3DEditorSelectedItem::~Node3DEditorSelectedItem() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
if (sbox_instance.is_valid()) {
RenderingServer::get_singleton()->free(sbox_instance);
}
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index bb5491fcb5..562eddbc64 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -3003,6 +3003,7 @@ void ScriptEditor::shortcut_input(const Ref<InputEvent> &p_event) {
_go_to_tab(script_list->get_item_metadata(next_tab));
_update_script_names();
}
+ accept_event();
}
if (ED_IS_SHORTCUT("script_editor/prev_script", p_event)) {
if (script_list->get_item_count() > 1) {
@@ -3011,12 +3012,15 @@ void ScriptEditor::shortcut_input(const Ref<InputEvent> &p_event) {
_go_to_tab(script_list->get_item_metadata(next_tab));
_update_script_names();
}
+ accept_event();
}
if (ED_IS_SHORTCUT("script_editor/window_move_up", p_event)) {
_menu_option(WINDOW_MOVE_UP);
+ accept_event();
}
if (ED_IS_SHORTCUT("script_editor/window_move_down", p_event)) {
_menu_option(WINDOW_MOVE_DOWN);
+ accept_event();
}
}
diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp
index 5a1f214933..414c3a2759 100644
--- a/editor/plugins/tiles/tile_map_editor.cpp
+++ b/editor/plugins/tiles/tile_map_editor.cpp
@@ -1488,13 +1488,15 @@ void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() {
}
// Selection if needed.
- for (RBSet<TileMapCell>::Element *E = tile_set_selection.front(); E; E = E->next()) {
+ for (RBSet<TileMapCell>::Element *E = tile_set_selection.front(); E;) {
+ RBSet<TileMapCell>::Element *N = E->next();
const TileMapCell *selected = &(E->get());
if (!tile_set->has_source(selected->source_id) ||
!tile_set->get_source(selected->source_id)->has_tile(selected->get_atlas_coords()) ||
!tile_set->get_source(selected->source_id)->has_alternative_tile(selected->get_atlas_coords(), selected->alternative_tile)) {
tile_set_selection.erase(E);
}
+ E = N;
}
if (!tile_map_selection.is_empty()) {
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index c93b0019dc..3d0c1acff8 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -5383,6 +5383,10 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("Binormal", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "binormal", "BINORMAL"), { "binormal" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("Color", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "color", "COLOR"), { "color" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Custom0", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "custom0", "CUSTOM0"), { "custom0" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Custom1", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "custom1", "CUSTOM1"), { "custom1" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Custom2", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "custom2", "CUSTOM2"), { "custom2" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Custom3", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "custom3", "CUSTOM3"), { "custom3" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("InstanceId", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_id", "INSTANCE_ID"), { "instance_id" }, VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("InstanceCustom", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_custom", "INSTANCE_CUSTOM"), { "instance_custom" }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("ModelViewMatrix", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "modelview_matrix", "MODELVIEW_MATRIX"), { "modelview_matrix" }, VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
diff --git a/editor/rename_dialog.cpp b/editor/rename_dialog.cpp
index 40683e2938..5243916bd2 100644
--- a/editor/rename_dialog.cpp
+++ b/editor/rename_dialog.cpp
@@ -338,7 +338,7 @@ void RenameDialog::_bind_methods() {
}
void RenameDialog::_update_substitute() {
- LineEdit *focus_owner_line_edit = Object::cast_to<LineEdit>(scene_tree_editor->get_viewport()->gui_get_focus_owner());
+ LineEdit *focus_owner_line_edit = Object::cast_to<LineEdit>(get_viewport()->gui_get_focus_owner());
bool is_main_field = _is_main_field(focus_owner_line_edit);
but_insert_name->set_disabled(!is_main_field);
@@ -636,7 +636,7 @@ bool RenameDialog::_is_main_field(LineEdit *line_edit) {
}
void RenameDialog::_insert_text(String text) {
- LineEdit *focus_owner = Object::cast_to<LineEdit>(scene_tree_editor->get_viewport()->gui_get_focus_owner());
+ LineEdit *focus_owner = Object::cast_to<LineEdit>(get_viewport()->gui_get_focus_owner());
if (_is_main_field(focus_owner)) {
focus_owner->selection_delete();
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index f91b571118..4b04fd1589 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -134,6 +134,8 @@ void SceneTreeDock::shortcut_input(const Ref<InputEvent> &p_event) {
_tool_selected(TOOL_MOVE_DOWN);
} else if (ED_IS_SHORTCUT("scene_tree/reparent", p_event)) {
_tool_selected(TOOL_REPARENT);
+ } else if (ED_IS_SHORTCUT("scene_tree/reparent_to_new_node", p_event)) {
+ _tool_selected(TOOL_REPARENT_TO_NEW_NODE);
} else if (ED_IS_SHORTCUT("scene_tree/save_branch_as_scene", p_event)) {
_tool_selected(TOOL_NEW_SCENE_FROM);
} else if (ED_IS_SHORTCUT("scene_tree/delete_no_confirm", p_event)) {
@@ -3217,6 +3219,7 @@ List<Node *> SceneTreeDock::paste_nodes() {
if (!paste_parent) {
paste_parent = dup;
owner = dup;
+ dup->set_scene_file_path(String()); // Make sure the scene path is empty, to avoid accidental references.
ur->add_do_method(EditorNode::get_singleton(), "set_edited_scene", dup);
} else {
ur->add_do_method(paste_parent, "add_child", dup, true);
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 1fe1561559..40bc2beb23 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -629,6 +629,10 @@ void GDScript::_update_doc() {
}
}
+ for (KeyValue<StringName, Ref<GDScript>> &E : subclasses) {
+ E.value->_update_doc();
+ }
+
_add_doc(doc);
}
#endif
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index fc2e6e94f3..757e602ebe 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -937,6 +937,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
const GDScriptParser::EnumNode *prev_enum = current_enum;
current_enum = member.m_enum;
+ Dictionary dictionary;
for (int j = 0; j < member.m_enum->values.size(); j++) {
GDScriptParser::EnumNode::Value &element = member.m_enum->values.write[j];
@@ -960,11 +961,13 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
}
enum_type.enum_values[element.identifier->name] = element.value;
+ dictionary[String(element.identifier->name)] = element.value;
}
current_enum = prev_enum;
member.m_enum->set_datatype(enum_type);
+ member.m_enum->dictionary = dictionary;
// Apply annotations.
for (GDScriptParser::AnnotationNode *&E : member.m_enum->annotations) {
@@ -1748,12 +1751,6 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
type = p_variable->initializer->get_datatype();
-#ifdef DEBUG_ENABLED
- if (p_variable->initializer->type == GDScriptParser::Node::CALL && type.is_hard_type() && type.kind == GDScriptParser::DataType::BUILTIN && type.builtin_type == Variant::NIL) {
- parser->push_warning(p_variable->initializer, GDScriptWarning::VOID_ASSIGNMENT, static_cast<GDScriptParser::CallNode *>(p_variable->initializer)->function_name);
- }
-#endif
-
if (p_variable->infer_datatype) {
type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
@@ -1843,12 +1840,6 @@ void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant
}
type = p_constant->initializer->get_datatype();
-
-#ifdef DEBUG_ENABLED
- if (p_constant->initializer->type == GDScriptParser::Node::CALL && type.is_hard_type() && type.kind == GDScriptParser::DataType::BUILTIN && type.builtin_type == Variant::NIL) {
- parser->push_warning(p_constant->initializer, GDScriptWarning::VOID_ASSIGNMENT, static_cast<GDScriptParser::CallNode *>(p_constant->initializer)->function_name);
- }
-#endif
}
if (p_constant->datatype_specifier != nullptr) {
@@ -2044,6 +2035,9 @@ void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) {
update_array_literal_element_type(expected_type, static_cast<GDScriptParser::ArrayNode *>(p_return->return_value));
}
}
+ if (has_expected_type && expected_type.is_hard_type() && expected_type.kind == GDScriptParser::DataType::BUILTIN && expected_type.builtin_type == Variant::NIL) {
+ push_error("A void function cannot return a value.", p_return);
+ }
result = p_return->return_value->get_datatype();
} else {
// Return type is null by default.
@@ -2259,30 +2253,26 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
}
p_assignment->set_datatype(op_type);
- if (!assignee_type.is_variant() && assigned_value_type.is_hard_type()) {
+ if (assignee_type.is_hard_type() && !assignee_type.is_variant() && op_type.is_hard_type()) {
if (compatible) {
compatible = is_type_compatible(assignee_type, op_type, true, p_assignment->assigned_value);
if (!compatible) {
- if (assignee_type.is_hard_type()) {
- // Try reverse test since it can be a masked subtype.
- if (!is_type_compatible(op_type, assignee_type, true, p_assignment->assigned_value)) {
- push_error(vformat(R"(Cannot assign a value of type "%s" to a target of type "%s".)", assigned_value_type.to_string(), assignee_type.to_string()), p_assignment->assigned_value);
- } else {
- // TODO: Add warning.
- mark_node_unsafe(p_assignment);
- p_assignment->use_conversion_assign = true;
- }
+ // Try reverse test since it can be a masked subtype.
+ if (!is_type_compatible(op_type, assignee_type, true)) {
+ push_error(vformat(R"(Cannot assign a value of type "%s" to a target of type "%s".)", assigned_value_type.to_string(), assignee_type.to_string()), p_assignment->assigned_value);
} else {
- // TODO: Warning in this case.
+ // TODO: Add warning.
mark_node_unsafe(p_assignment);
+ p_assignment->use_conversion_assign = true;
}
}
} else {
push_error(vformat(R"(Invalid operands "%s" and "%s" for assignment operator.)", assignee_type.to_string(), assigned_value_type.to_string()), p_assignment);
}
- }
-
- if (assignee_type.has_no_type() || assigned_value_type.is_variant()) {
+ } else if (assignee_type.is_hard_type() && !assignee_type.is_variant()) {
+ mark_node_unsafe(p_assignment);
+ p_assignment->use_conversion_assign = true;
+ } else {
mark_node_unsafe(p_assignment);
if (assignee_type.is_hard_type() && !assignee_type.is_variant()) {
p_assignment->use_conversion_assign = true;
@@ -2333,9 +2323,7 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
}
#ifdef DEBUG_ENABLED
- if (p_assignment->assigned_value->type == GDScriptParser::Node::CALL && assigned_value_type.is_hard_type() && assigned_value_type.kind == GDScriptParser::DataType::BUILTIN && assigned_value_type.builtin_type == Variant::NIL) {
- parser->push_warning(p_assignment->assigned_value, GDScriptWarning::VOID_ASSIGNMENT, static_cast<GDScriptParser::CallNode *>(p_assignment->assigned_value)->function_name);
- } else if (assignee_type.is_hard_type() && assignee_type.builtin_type == Variant::INT && assigned_value_type.builtin_type == Variant::FLOAT) {
+ if (assignee_type.is_hard_type() && assignee_type.builtin_type == Variant::INT && assigned_value_type.builtin_type == Variant::FLOAT) {
parser->push_warning(p_assignment->assigned_value, GDScriptWarning::NARROWING_CONVERSION);
}
#endif
@@ -2650,6 +2638,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
} else if (GDScriptUtilityFunctions::function_exists(function_name)) {
MethodInfo function_info = GDScriptUtilityFunctions::get_function_info(function_name);
+ if (!p_is_root && function_info.return_val.type == Variant::NIL && ((function_info.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT) == 0)) {
+ push_error(vformat(R"*(Cannot get return value of call to "%s()" because it returns "void".)*", function_name), p_call);
+ }
+
if (all_is_constant && GDScriptUtilityFunctions::is_function_constant(function_name)) {
// Can call on compilation.
Vector<const Variant *> args;
@@ -2693,6 +2685,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
} else if (Variant::has_utility_function(function_name)) {
MethodInfo function_info = info_from_utility_func(function_name);
+ if (!p_is_root && function_info.return_val.type == Variant::NIL && ((function_info.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT) == 0)) {
+ push_error(vformat(R"*(Cannot get return value of call to "%s()" because it returns "void".)*", function_name), p_call);
+ }
+
if (all_is_constant && Variant::get_utility_function_type(function_name) == Variant::UTILITY_FUNC_TYPE_MATH) {
// Can call on compilation.
Vector<const Variant *> args;
@@ -2835,6 +2831,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
mark_lambda_use_self();
}
+ if (!p_is_root && return_type.is_hard_type() && return_type.kind == GDScriptParser::DataType::BUILTIN && return_type.builtin_type == Variant::NIL) {
+ push_error(vformat(R"*(Cannot get return value of call to "%s()" because it returns "void".)*", p_call->function_name), p_call);
+ }
+
#ifdef DEBUG_ENABLED
if (p_is_root && return_type.kind != GDScriptParser::DataType::UNRESOLVED && return_type.builtin_type != Variant::NIL) {
parser->push_warning(p_call, GDScriptWarning::RETURN_VALUE_DISCARDED, p_call->function_name);
@@ -3140,10 +3140,9 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
break;
case GDScriptParser::ClassNode::Member::ENUM:
- if (p_base != nullptr && p_base->is_constant) {
- p_identifier->is_constant = true;
- p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
- }
+ p_identifier->is_constant = true;
+ p_identifier->reduced_value = member.m_enum->dictionary;
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
break;
case GDScriptParser::ClassNode::Member::VARIABLE:
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_VARIABLE;
@@ -3197,7 +3196,8 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
return;
case GDScriptParser::ClassNode::Member::ENUM:
p_identifier->set_datatype(member.get_datatype());
- p_identifier->is_constant = false;
+ p_identifier->is_constant = true;
+ p_identifier->reduced_value = member.m_enum->dictionary;
return;
case GDScriptParser::ClassNode::Member::CLASS:
p_identifier->set_datatype(member.get_datatype());
@@ -4174,7 +4174,7 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
r_default_arg_count++;
}
}
- r_return_type = found_function->get_datatype();
+ r_return_type = p_is_constructor ? p_base_type : found_function->get_datatype();
r_return_type.is_meta_type = false;
r_return_type.is_coroutine = found_function->is_coroutine;
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index cf9e734b05..3c9387a837 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -538,20 +538,20 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
// Construct a built-in type.
Variant::Type vtype = GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name);
- gen->write_construct(result, vtype, arguments);
+ gen->write_construct(return_addr, vtype, arguments);
} else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && Variant::has_utility_function(call->function_name)) {
// Variant utility function.
- gen->write_call_utility(result, call->function_name, arguments);
+ gen->write_call_utility(return_addr, call->function_name, arguments);
} else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptUtilityFunctions::function_exists(call->function_name)) {
// GDScript utility function.
- gen->write_call_gdscript_utility(result, GDScriptUtilityFunctions::get_function(call->function_name), arguments);
+ gen->write_call_gdscript_utility(return_addr, GDScriptUtilityFunctions::get_function(call->function_name), arguments);
} else {
// Regular function.
const GDScriptParser::ExpressionNode *callee = call->callee;
if (call->is_super) {
// Super call.
- gen->write_super_call(result, call->function_name, arguments);
+ gen->write_super_call(return_addr, call->function_name, arguments);
} else {
if (callee->type == GDScriptParser::Node::IDENTIFIER) {
// Self function call.
@@ -563,22 +563,22 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
if (_have_exact_arguments(method, arguments)) {
// Exact arguments, use ptrcall.
- gen->write_call_ptrcall(result, self, method, arguments);
+ gen->write_call_ptrcall(return_addr, self, method, arguments);
} else {
// Not exact arguments, but still can use method bind call.
- gen->write_call_method_bind(result, self, method, arguments);
+ gen->write_call_method_bind(return_addr, self, method, arguments);
}
} else if ((codegen.function_node && codegen.function_node->is_static) || call->function_name == "new") {
GDScriptCodeGenerator::Address self;
self.mode = GDScriptCodeGenerator::Address::CLASS;
if (within_await) {
- gen->write_call_async(result, self, call->function_name, arguments);
+ gen->write_call_async(return_addr, self, call->function_name, arguments);
} else {
gen->write_call(return_addr, self, call->function_name, arguments);
}
} else {
if (within_await) {
- gen->write_call_self_async(result, call->function_name, arguments);
+ gen->write_call_self_async(return_addr, call->function_name, arguments);
} else {
gen->write_call_self(return_addr, call->function_name, arguments);
}
@@ -589,18 +589,18 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
if (subscript->is_attribute) {
// May be static built-in method call.
if (!call->is_super && subscript->base->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name) < Variant::VARIANT_MAX) {
- gen->write_call_builtin_type_static(result, GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name), subscript->attribute->name, arguments);
+ gen->write_call_builtin_type_static(return_addr, GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name), subscript->attribute->name, arguments);
} else if (!call->is_super && subscript->base->type == GDScriptParser::Node::IDENTIFIER && call->function_name != SNAME("new") &&
ClassDB::class_exists(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name) && !Engine::get_singleton()->has_singleton(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name)) {
// It's a static native method call.
- gen->write_call_native_static(result, static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name, subscript->attribute->name, arguments);
+ gen->write_call_native_static(return_addr, static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name, subscript->attribute->name, arguments);
} else {
GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, subscript->base);
if (r_error) {
return GDScriptCodeGenerator::Address();
}
if (within_await) {
- gen->write_call_async(result, base, call->function_name, arguments);
+ gen->write_call_async(return_addr, base, call->function_name, arguments);
} else if (base.type.has_type && base.type.kind != GDScriptDataType::BUILTIN) {
// Native method, use faster path.
StringName class_name;
@@ -613,16 +613,16 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
MethodBind *method = ClassDB::get_method(class_name, call->function_name);
if (_have_exact_arguments(method, arguments)) {
// Exact arguments, use ptrcall.
- gen->write_call_ptrcall(result, base, method, arguments);
+ gen->write_call_ptrcall(return_addr, base, method, arguments);
} else {
// Not exact arguments, but still can use method bind call.
- gen->write_call_method_bind(result, base, method, arguments);
+ gen->write_call_method_bind(return_addr, base, method, arguments);
}
} else {
gen->write_call(return_addr, base, call->function_name, arguments);
}
} else if (base.type.has_type && base.type.kind == GDScriptDataType::BUILTIN) {
- gen->write_call_builtin_type(result, base, base.type.builtin_type, call->function_name, arguments);
+ gen->write_call_builtin_type(return_addr, base, base.type.builtin_type, call->function_name, arguments);
} else {
gen->write_call(return_addr, base, call->function_name, arguments);
}
@@ -2453,26 +2453,20 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
case GDScriptParser::ClassNode::Member::ENUM: {
const GDScriptParser::EnumNode *enum_n = member.m_enum;
+ StringName name = enum_n->identifier->name;
- // TODO: Make enums not be just a dictionary?
- Dictionary new_enum;
- for (int j = 0; j < enum_n->values.size(); j++) {
- // Needs to be string because Variant::get will convert to String.
- new_enum[String(enum_n->values[j].identifier->name)] = enum_n->values[j].value;
- }
-
- p_script->constants.insert(enum_n->identifier->name, new_enum);
+ p_script->constants.insert(name, enum_n->dictionary);
#ifdef TOOLS_ENABLED
- p_script->member_lines[enum_n->identifier->name] = enum_n->start_line;
- p_script->doc_enums[enum_n->identifier->name] = DocData::EnumDoc();
- p_script->doc_enums[enum_n->identifier->name].name = enum_n->identifier->name;
- p_script->doc_enums[enum_n->identifier->name].description = enum_n->doc_description;
+ p_script->member_lines[name] = enum_n->start_line;
+ p_script->doc_enums[name] = DocData::EnumDoc();
+ p_script->doc_enums[name].name = name;
+ p_script->doc_enums[name].description = enum_n->doc_description;
for (int j = 0; j < enum_n->values.size(); j++) {
DocData::ConstantDoc const_doc;
const_doc.name = enum_n->values[j].identifier->name;
const_doc.value = Variant(enum_n->values[j].value).operator String();
const_doc.description = enum_n->values[j].doc_description;
- p_script->doc_enums[enum_n->identifier->name].values.push_back(const_doc);
+ p_script->doc_enums[name].values.push_back(const_doc);
}
#endif
} break;
@@ -2642,10 +2636,6 @@ Error GDScriptCompiler::_compile_class(GDScript *p_script, const GDScriptParser:
}
}
-#ifdef TOOLS_ENABLED
- p_script->_update_doc();
-#endif
-
p_script->_init_rpc_methods_properties();
p_script->valid = true;
@@ -2730,6 +2720,10 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri
return err;
}
+#ifdef TOOLS_ENABLED
+ p_script->_update_doc();
+#endif
+
return GDScriptCache::finish_compiling(main_script->get_path());
}
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 540ef1c561..fd2f7b3288 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -483,6 +483,7 @@ public:
IdentifierNode *identifier = nullptr;
Vector<Value> values;
+ Variant dictionary;
#ifdef TOOLS_ENABLED
String doc_description;
#endif // TOOLS_ENABLED
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
index fdcc0625d7..bc8e33b01a 100644
--- a/modules/gdscript/gdscript_vm.cpp
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -1533,8 +1533,28 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
Callable::CallError err;
if (call_ret) {
GET_INSTRUCTION_ARG(ret, argc + 1);
+#ifdef DEBUG_ENABLED
+ Variant::Type base_type = base->get_type();
+ Object *base_obj = base->get_validated_object();
+ StringName base_class = base_obj ? base_obj->get_class_name() : StringName();
+#endif
base->callp(*methodname, (const Variant **)argptrs, argc, *ret, err);
#ifdef DEBUG_ENABLED
+ if (ret->get_type() == Variant::NIL) {
+ if (base_type == Variant::OBJECT) {
+ if (base_obj) {
+ MethodBind *method = ClassDB::get_method(base_class, *methodname);
+ if (*methodname == CoreStringNames::get_singleton()->_free || (method && !method->has_return())) {
+ err_text = R"(Trying to get a return value of a method that returns "void")";
+ OPCODE_BREAK;
+ }
+ }
+ } else if (Variant::has_builtin_method(base_type, *methodname) && !Variant::has_builtin_method_return_value(base_type, *methodname)) {
+ err_text = R"(Trying to get a return value of a method that returns "void")";
+ OPCODE_BREAK;
+ }
+ }
+
if (!call_async && ret->get_type() == Variant::OBJECT) {
// Check if getting a function state without await.
bool was_freed = false;
diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp
index 36bc051643..9ae4bdae56 100644
--- a/modules/gdscript/gdscript_warning.cpp
+++ b/modules/gdscript/gdscript_warning.cpp
@@ -80,10 +80,6 @@ String GDScriptWarning::get_message() const {
case STANDALONE_EXPRESSION: {
return "Standalone expression (the line has no effect).";
} break;
- case VOID_ASSIGNMENT: {
- CHECK_SYMBOLS(1);
- return "Assignment operation, but the function '" + symbols[0] + "()' returns void.";
- } break;
case NARROWING_CONVERSION: {
return "Narrowing conversion (float is converted to int and loses precision).";
} break;
@@ -202,7 +198,6 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
"UNREACHABLE_CODE",
"UNREACHABLE_PATTERN",
"STANDALONE_EXPRESSION",
- "VOID_ASSIGNMENT",
"NARROWING_CONVERSION",
"INCOMPATIBLE_TERNARY",
"UNUSED_SIGNAL",
diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h
index 7e4e975510..df6ee71425 100644
--- a/modules/gdscript/gdscript_warning.h
+++ b/modules/gdscript/gdscript_warning.h
@@ -57,7 +57,6 @@ public:
UNREACHABLE_CODE, // Code after a return statement.
UNREACHABLE_PATTERN, // Pattern in a match statement after a catch all pattern (wildcard or bind).
STANDALONE_EXPRESSION, // Expression not assigned to a variable.
- VOID_ASSIGNMENT, // Function returns void but it's assigned to a variable.
NARROWING_CONVERSION, // Float value into an integer slot, precision is lost.
INCOMPATIBLE_TERNARY, // Possible values of a ternary if are not mutually compatible.
UNUSED_SIGNAL, // Signal is defined but never emitted.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constructor_call_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/constructor_call_type.gd
new file mode 100644
index 0000000000..251be70088
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/constructor_call_type.gd
@@ -0,0 +1,10 @@
+class A:
+ func _init():
+ pass
+
+class B extends A: pass
+class C extends A: pass
+
+func test():
+ var x := B.new()
+ print(x is C)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constructor_call_type.out b/modules/gdscript/tests/scripts/analyzer/errors/constructor_call_type.out
new file mode 100644
index 0000000000..91d5125ec0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/constructor_call_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "B" so it can't be of type "C".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.gd b/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.gd
new file mode 100644
index 0000000000..63587942f7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.gd
@@ -0,0 +1,2 @@
+func test() -> void:
+ return null
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.out b/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.out
new file mode 100644
index 0000000000..3c09f44ba9
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+A void function cannot return a value.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.gd b/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.gd
new file mode 100644
index 0000000000..0ee4e7ea36
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.gd
@@ -0,0 +1,4 @@
+func test() -> void:
+ var a
+ a = 1
+ return a
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.out b/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.out
new file mode 100644
index 0000000000..3c09f44ba9
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+A void function cannot return a value.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_builtin_method.gd b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_builtin_method.gd
new file mode 100644
index 0000000000..a3450966cc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_builtin_method.gd
@@ -0,0 +1,3 @@
+func test():
+ var builtin := []
+ print(builtin.reverse()) # Built-in type method.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_builtin_method.out b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_builtin_method.out
new file mode 100644
index 0000000000..225c85e9c7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_builtin_method.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot get return value of call to "reverse()" because it returns "void".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_custom_method.gd b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_custom_method.gd
new file mode 100644
index 0000000000..2162a181ac
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_custom_method.gd
@@ -0,0 +1,5 @@
+func foo() -> void:
+ pass
+
+func test():
+ print(foo()) # Custom method.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_custom_method.out b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_custom_method.out
new file mode 100644
index 0000000000..2b1a607883
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_custom_method.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot get return value of call to "foo()" because it returns "void".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_gd_utility.gd b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_gd_utility.gd
new file mode 100644
index 0000000000..f3443d985e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_gd_utility.gd
@@ -0,0 +1,2 @@
+func test():
+ print(print_debug()) # GDScript utility function.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_gd_utility.out b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_gd_utility.out
new file mode 100644
index 0000000000..502c18ab9d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_gd_utility.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot get return value of call to "print_debug()" because it returns "void".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_native_method.gd b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_native_method.gd
new file mode 100644
index 0000000000..b8e81b160a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_native_method.gd
@@ -0,0 +1,3 @@
+func test():
+ var obj := Node.new()
+ print(obj.free()) # Native type method.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_native_method.out b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_native_method.out
new file mode 100644
index 0000000000..88be39345b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_native_method.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot get return value of call to "free()" because it returns "void".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_utility.gd b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_utility.gd
new file mode 100644
index 0000000000..8eabed4271
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_utility.gd
@@ -0,0 +1,2 @@
+func test():
+ print(print()) # Built-in utility function.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_utility.out b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_utility.out
new file mode 100644
index 0000000000..ebf43186be
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_utility.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot get return value of call to "print()" because it returns "void".
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_as_const.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_as_const.gd
new file mode 100644
index 0000000000..2ce588373b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_as_const.gd
@@ -0,0 +1,29 @@
+class Outer:
+ enum OuterEnum { OuterValue = 3 }
+ const OuterConst := OuterEnum
+
+ class Inner:
+ enum InnerEnum { InnerValue = 7 }
+ const InnerConst := InnerEnum
+
+ static func test() -> void:
+ print(OuterEnum.size());
+ print(OuterEnum.OuterValue);
+ print(OuterConst.size());
+ print(OuterConst.OuterValue);
+ print(Outer.OuterEnum.size());
+ print(Outer.OuterEnum.OuterValue);
+ print(Outer.OuterConst.size());
+ print(Outer.OuterConst.OuterValue);
+
+ print(InnerEnum.size());
+ print(InnerEnum.InnerValue);
+ print(InnerConst.size());
+ print(InnerConst.InnerValue);
+ print(Inner.InnerEnum.size());
+ print(Inner.InnerEnum.InnerValue);
+ print(Inner.InnerConst.size());
+ print(Inner.InnerConst.InnerValue);
+
+func test():
+ Outer.Inner.test()
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_as_const.out b/modules/gdscript/tests/scripts/analyzer/features/enum_as_const.out
new file mode 100644
index 0000000000..e049f85b6e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_as_const.out
@@ -0,0 +1,17 @@
+GDTEST_OK
+1
+3
+1
+3
+1
+3
+1
+3
+1
+7
+1
+7
+1
+7
+1
+7
diff --git a/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.gd b/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.gd
new file mode 100644
index 0000000000..c9caef7d7c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.gd
@@ -0,0 +1,5 @@
+func variant() -> Variant:
+ return 'variant'
+
+func test():
+ print(variant())
diff --git a/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.out b/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.out
new file mode 100644
index 0000000000..57fe608cc5
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+variant
diff --git a/modules/gdscript/tests/scripts/parser/warnings/void_assignment.gd b/modules/gdscript/tests/scripts/parser/warnings/void_assignment.gd
deleted file mode 100644
index b4a42b3e3d..0000000000
--- a/modules/gdscript/tests/scripts/parser/warnings/void_assignment.gd
+++ /dev/null
@@ -1,6 +0,0 @@
-func i_return_void() -> void:
- return
-
-
-func test():
- var __ = i_return_void()
diff --git a/modules/gdscript/tests/scripts/parser/warnings/void_assignment.out b/modules/gdscript/tests/scripts/parser/warnings/void_assignment.out
deleted file mode 100644
index 84c9598f9a..0000000000
--- a/modules/gdscript/tests/scripts/parser/warnings/void_assignment.out
+++ /dev/null
@@ -1,5 +0,0 @@
-GDTEST_OK
->> WARNING
->> Line: 6
->> VOID_ASSIGNMENT
->> Assignment operation, but the function 'i_return_void()' returns void.
diff --git a/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.gd b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.gd
new file mode 100644
index 0000000000..a3daf70627
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.gd
@@ -0,0 +1,4 @@
+func test():
+ var obj
+ obj = Node.new()
+ print(obj.free())
diff --git a/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.out b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.out
new file mode 100644
index 0000000000..5edaf19442
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> runtime/errors/use_return_value_of_free_call.gd
+>> 4
+>> Trying to get a return value of a method that returns "void"
diff --git a/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.gd b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.gd
new file mode 100644
index 0000000000..49fb76ad1f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.gd
@@ -0,0 +1,4 @@
+func test():
+ var value
+ value = []
+ print(value.reverse())
diff --git a/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.out b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.out
new file mode 100644
index 0000000000..128356ff8a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> runtime/errors/use_return_value_of_void_builtin_method_call.gd
+>> 4
+>> Trying to get a return value of a method that returns "void"
diff --git a/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.gd b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.gd
new file mode 100644
index 0000000000..44f9aa467a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.gd
@@ -0,0 +1,4 @@
+func test():
+ var obj
+ obj = RefCounted.new()
+ print(obj.notify_property_list_changed())
diff --git a/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.out b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.out
new file mode 100644
index 0000000000..e02c206778
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> runtime/errors/use_return_value_of_void_native_method_call.gd
+>> 4
+>> Trying to get a return value of a method that returns "void"
diff --git a/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd b/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd
index 5303fb04e2..9b64084fa6 100644
--- a/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd
+++ b/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd
@@ -15,10 +15,10 @@ func test():
var string_array: Array[String] = []
var stringname_array: Array[StringName] = []
- assert(!string_array.push_back(&"abc"))
+ string_array.push_back(&"abc")
print("Array[String] insert converted: ", typeof(string_array[0]) == TYPE_STRING)
- assert(!stringname_array.push_back("abc"))
+ stringname_array.push_back("abc")
print("Array[StringName] insert converted: ", typeof(stringname_array[0]) == TYPE_STRING_NAME)
print("StringName in Array[String]: ", &"abc" in string_array)
@@ -28,8 +28,8 @@ func test():
assert(!packed_string_array.push_back("abc"))
print("StringName in PackedStringArray: ", &"abc" in packed_string_array)
- assert(!string_array.push_back("abc"))
+ string_array.push_back("abc")
print("StringName finds String in Array: ", string_array.find(&"abc"))
- assert(!stringname_array.push_back(&"abc"))
+ stringname_array.push_back(&"abc")
print("String finds StringName in Array: ", stringname_array.find("abc"))
diff --git a/modules/gdscript/tests/scripts/runtime/features/typed_assignment.gd b/modules/gdscript/tests/scripts/runtime/features/typed_assignment.gd
new file mode 100644
index 0000000000..22e54cf91c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/typed_assignment.gd
@@ -0,0 +1,9 @@
+func test():
+ var x: int = 2
+ var y = 3.14
+ var z := 2.72
+ print(typeof(x))
+ x = y
+ print(typeof(x))
+ x = z
+ print(typeof(x))
diff --git a/modules/gdscript/tests/scripts/runtime/features/typed_assignment.out b/modules/gdscript/tests/scripts/runtime/features/typed_assignment.out
new file mode 100644
index 0000000000..4a268dd8e0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/typed_assignment.out
@@ -0,0 +1,12 @@
+GDTEST_OK
+>> WARNING
+>> Line: 6
+>> NARROWING_CONVERSION
+>> Narrowing conversion (float is converted to int and loses precision).
+>> WARNING
+>> Line: 8
+>> NARROWING_CONVERSION
+>> Narrowing conversion (float is converted to int and loses precision).
+2
+2
+2
diff --git a/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp b/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp
index cfa498af65..4b71511582 100644
--- a/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp
+++ b/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp
@@ -59,6 +59,7 @@ Error GLTFDocumentExtensionConvertImporterMesh::import_post(Ref<GLTFState> p_sta
mesh_instance_node_3d->set_skeleton_path(mesh_3d->get_skeleton_path());
node->replace_by(mesh_instance_node_3d);
delete_queue.push_back(node);
+ node = mesh_instance_node_3d;
} else {
memdelete(mesh_instance_node_3d);
}
diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp
index 89ef9ddd90..186e6a504f 100644
--- a/modules/gridmap/editor/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp
@@ -1405,6 +1405,7 @@ GridMapEditor::GridMapEditor() {
}
GridMapEditor::~GridMapEditor() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
_clear_clipboard_data();
for (int i = 0; i < 3; i++) {
diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp
index e396d77d86..d23d68ffba 100644
--- a/modules/gridmap/grid_map.cpp
+++ b/modules/gridmap/grid_map.cpp
@@ -796,6 +796,10 @@ void GridMap::_octant_enter_world(const OctantKey &p_key) {
}
void GridMap::_octant_exit_world(const OctantKey &p_key) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ ERR_FAIL_NULL(PhysicsServer3D::get_singleton());
+ ERR_FAIL_NULL(NavigationServer3D::get_singleton());
+
ERR_FAIL_COND(!octant_map.has(p_key));
Octant &g = *octant_map[p_key];
PhysicsServer3D::get_singleton()->body_set_state(g.static_body, PhysicsServer3D::BODY_STATE_TRANSFORM, get_global_transform());
@@ -834,6 +838,10 @@ void GridMap::_octant_exit_world(const OctantKey &p_key) {
}
void GridMap::_octant_clean_up(const OctantKey &p_key) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ ERR_FAIL_NULL(PhysicsServer3D::get_singleton());
+ ERR_FAIL_NULL(NavigationServer3D::get_singleton());
+
ERR_FAIL_COND(!octant_map.has(p_key));
Octant &g = *octant_map[p_key];
@@ -1186,6 +1194,7 @@ Vector3 GridMap::_get_offset() const {
}
void GridMap::clear_baked_meshes() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
for (int i = 0; i < baked_meshes.size(); i++) {
RS::get_singleton()->free(baked_meshes[i].instance);
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs
index fb32f6192f..eae7e41da8 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs
@@ -45,8 +45,11 @@ namespace Godot.SourceGenerators
return false;
})
)
- // Ignore classes whose name is not the same as the file name
- .Where(x => Path.GetFileNameWithoutExtension(x.cds.SyntaxTree.FilePath) == x.symbol.Name)
+ .Where(x =>
+ // Ignore classes whose name is not the same as the file name
+ Path.GetFileNameWithoutExtension(x.cds.SyntaxTree.FilePath) == x.symbol.Name &&
+ // Ignore generic classes
+ !x.symbol.IsGenericType)
.GroupBy(x => x.symbol)
.ToDictionary(g => g.Key, g => g.Select(x => x.cds));
@@ -150,8 +153,6 @@ namespace Godot.SourceGenerators
first = false;
sourceBuilder.Append("typeof(");
sourceBuilder.Append(qualifiedName);
- if (godotClass.Key.IsGenericType)
- sourceBuilder.Append($"<{new string(',', godotClass.Key.TypeParameters.Count() - 1)}>");
sourceBuilder.Append(")");
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
index e6a8054ae2..dafa83431b 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
@@ -297,7 +297,7 @@ namespace Godot.Bridge
foreach (var type in assembly.GetTypes())
{
- if (type.IsNested)
+ if (type.IsNested || type.IsGenericType)
continue;
if (!typeOfGodotObject.IsAssignableFrom(type))
@@ -314,9 +314,12 @@ namespace Godot.Bridge
if (scriptTypes != null)
{
- for (int i = 0; i < scriptTypes.Length; i++)
+ foreach (var type in scriptTypes)
{
- LookupScriptForClass(scriptTypes[i]);
+ if (type.IsGenericType)
+ continue;
+
+ LookupScriptForClass(type);
}
}
}
@@ -729,6 +732,7 @@ namespace Godot.Bridge
{
ExceptionUtils.LogException(e);
*outTool = godot_bool.False;
+ *outMethodsDest = NativeFuncs.godotsharp_array_new();
*outRpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new();
*outEventSignalsDest = NativeFuncs.godotsharp_dictionary_new();
*outBaseScript = default;
diff --git a/modules/noise/noise_texture_2d.cpp b/modules/noise/noise_texture_2d.cpp
index 8c785d7591..1b6f1d7897 100644
--- a/modules/noise/noise_texture_2d.cpp
+++ b/modules/noise/noise_texture_2d.cpp
@@ -40,6 +40,7 @@ NoiseTexture2D::NoiseTexture2D() {
}
NoiseTexture2D::~NoiseTexture2D() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
if (texture.is_valid()) {
RS::get_singleton()->free(texture);
}
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index 77cfa99aee..41d2579ac0 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -873,7 +873,7 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
int hand_tracking_frequency_index = p_preset->get("xr_features/hand_tracking_frequency");
bool backup_allowed = p_preset->get("user_data_backup/allow");
- bool classify_as_game = p_preset->get("package/classify_as_game");
+ int app_category = p_preset->get("package/app_category");
bool retain_data_on_uninstall = p_preset->get("package/retain_data_on_uninstall");
bool exclude_from_recents = p_preset->get("package/exclude_from_recents");
bool is_resizeable = bool(GLOBAL_GET("display/window/size/resizable"));
@@ -972,8 +972,12 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
encode_uint32(backup_allowed, &p_manifest.write[iofs + 16]);
}
+ if (tname == "application" && attrname == "appCategory") {
+ encode_uint32(_get_app_category_value(app_category), &p_manifest.write[iofs + 16]);
+ }
+
if (tname == "application" && attrname == "isGame") {
- encode_uint32(classify_as_game, &p_manifest.write[iofs + 16]);
+ encode_uint32(app_category == APP_CATEGORY_GAME, &p_manifest.write[iofs + 16]);
}
if (tname == "application" && attrname == "hasFragileUserData") {
@@ -1731,7 +1735,7 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "ext.domain.name"), "org.godotengine.$genname"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name [default if blank]"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/signed"), true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/classify_as_game"), true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "package/app_category", PROPERTY_HINT_ENUM, "Accessibility,Audio,Game,Image,Maps,News,Productivity,Social,Video"), APP_CATEGORY_GAME));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/retain_data_on_uninstall"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/exclude_from_recents"), false));
diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp
index 8d016d3fac..1cbed4b3bb 100644
--- a/platform/android/export/gradle_export_util.cpp
+++ b/platform/android/export/gradle_export_util.cpp
@@ -72,6 +72,54 @@ String _get_android_orientation_label(DisplayServer::ScreenOrientation screen_or
}
}
+int _get_app_category_value(int category_index) {
+ switch (category_index) {
+ case APP_CATEGORY_ACCESSIBILITY:
+ return 8;
+ case APP_CATEGORY_AUDIO:
+ return 1;
+ case APP_CATEGORY_IMAGE:
+ return 3;
+ case APP_CATEGORY_MAPS:
+ return 6;
+ case APP_CATEGORY_NEWS:
+ return 5;
+ case APP_CATEGORY_PRODUCTIVITY:
+ return 7;
+ case APP_CATEGORY_SOCIAL:
+ return 4;
+ case APP_CATEGORY_VIDEO:
+ return 2;
+ case APP_CATEGORY_GAME:
+ default:
+ return 0;
+ }
+}
+
+String _get_app_category_label(int category_index) {
+ switch (category_index) {
+ case APP_CATEGORY_ACCESSIBILITY:
+ return "accessibility";
+ case APP_CATEGORY_AUDIO:
+ return "audio";
+ case APP_CATEGORY_IMAGE:
+ return "image";
+ case APP_CATEGORY_MAPS:
+ return "maps";
+ case APP_CATEGORY_NEWS:
+ return "news";
+ case APP_CATEGORY_PRODUCTIVITY:
+ return "productivity";
+ case APP_CATEGORY_SOCIAL:
+ return "social";
+ case APP_CATEGORY_VIDEO:
+ return "video";
+ case APP_CATEGORY_GAME:
+ default:
+ return "game";
+ }
+}
+
// Utility method used to create a directory.
Error create_directory(const String &p_dir) {
if (!DirAccess::exists(p_dir)) {
@@ -253,21 +301,27 @@ String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) {
}
String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_read_write_storage_permission) {
+ int app_category_index = (int)(p_preset->get("package/app_category"));
+ bool is_game = app_category_index == APP_CATEGORY_GAME;
+
int xr_mode_index = (int)(p_preset->get("xr_features/xr_mode"));
bool uses_xr = xr_mode_index == XR_MODE_OPENXR;
+
String manifest_application_text = vformat(
" <application android:label=\"@string/godot_project_name_string\"\n"
" android:allowBackup=\"%s\"\n"
" android:icon=\"@mipmap/icon\"\n"
+ " android:appCategory=\"%s\"\n"
" android:isGame=\"%s\"\n"
" android:hasFragileUserData=\"%s\"\n"
" android:requestLegacyExternalStorage=\"%s\"\n"
- " tools:replace=\"android:allowBackup,android:isGame,android:hasFragileUserData,android:requestLegacyExternalStorage\"\n"
+ " tools:replace=\"android:allowBackup,android:appCategory,android:isGame,android:hasFragileUserData,android:requestLegacyExternalStorage\"\n"
" tools:ignore=\"GoogleAppIndexingWarning\">\n\n"
" <meta-data tools:node=\"remove\" android:name=\"xr_hand_tracking_version_name\" />\n"
" <meta-data tools:node=\"remove\" android:name=\"xr_hand_tracking_metadata_name\" />\n",
bool_to_string(p_preset->get("user_data_backup/allow")),
- bool_to_string(p_preset->get("package/classify_as_game")),
+ _get_app_category_label(app_category_index),
+ bool_to_string(is_game),
bool_to_string(p_preset->get("package/retain_data_on_uninstall")),
bool_to_string(p_has_read_write_storage_permission));
diff --git a/platform/android/export/gradle_export_util.h b/platform/android/export/gradle_export_util.h
index 232b4458c6..9c9c5923f7 100644
--- a/platform/android/export/gradle_export_util.h
+++ b/platform/android/export/gradle_export_util.h
@@ -44,6 +44,18 @@ const String godot_project_name_xml_string = R"(<?xml version="1.0" encoding="ut
</resources>
)";
+// Application category.
+// See https://developer.android.com/guide/topics/manifest/application-element#appCategory for standards
+static const int APP_CATEGORY_ACCESSIBILITY = 0;
+static const int APP_CATEGORY_AUDIO = 1;
+static const int APP_CATEGORY_GAME = 2;
+static const int APP_CATEGORY_IMAGE = 3;
+static const int APP_CATEGORY_MAPS = 4;
+static const int APP_CATEGORY_NEWS = 5;
+static const int APP_CATEGORY_PRODUCTIVITY = 6;
+static const int APP_CATEGORY_SOCIAL = 7;
+static const int APP_CATEGORY_VIDEO = 8;
+
// Supported XR modes.
// This should match the entries in 'platform/android/java/lib/src/org/godotengine/godot/xr/XRMode.java'
static const int XR_MODE_REGULAR = 0;
@@ -73,6 +85,10 @@ int _get_android_orientation_value(DisplayServer::ScreenOrientation screen_orien
String _get_android_orientation_label(DisplayServer::ScreenOrientation screen_orientation);
+int _get_app_category_value(int category_index);
+
+String _get_app_category_label(int category_index);
+
// Utility method used to create a directory.
Error create_directory(const String &p_dir);
diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml
index 1db135826a..8c8608cbbb 100644
--- a/platform/android/java/app/AndroidManifest.xml
+++ b/platform/android/java/app/AndroidManifest.xml
@@ -20,6 +20,7 @@
android:label="@string/godot_project_name_string"
android:allowBackup="false"
android:icon="@mipmap/icon"
+ android:appCategory="game"
android:isGame="true"
android:hasFragileUserData="false"
android:requestLegacyExternalStorage="false"
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index 844b15e9fb..747dcbd76c 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -339,9 +339,6 @@ def configure(env: "Environment"):
env.Prepend(CPPPATH=["#platform/linuxbsd"])
if env["x11"]:
- if not env["vulkan"]:
- print("Error: X11 support requires vulkan=yes")
- env.Exit(255)
env.Append(CPPDEFINES=["X11_ENABLED"])
env.Append(CPPDEFINES=["UNIX_ENABLED"])
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index e7864ebac0..f462893112 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -3709,7 +3709,6 @@ WTEnablePtr DisplayServerWindows::wintab_WTEnable = nullptr;
// UXTheme API.
bool DisplayServerWindows::dark_title_available = false;
bool DisplayServerWindows::ux_theme_available = false;
-IsDarkModeAllowedForAppPtr DisplayServerWindows::IsDarkModeAllowedForApp = nullptr;
ShouldAppsUseDarkModePtr DisplayServerWindows::ShouldAppsUseDarkMode = nullptr;
GetImmersiveColorFromColorSetExPtr DisplayServerWindows::GetImmersiveColorFromColorSetEx = nullptr;
GetImmersiveColorTypeFromNamePtr DisplayServerWindows::GetImmersiveColorTypeFromName = nullptr;
@@ -3727,7 +3726,7 @@ typedef enum _SHC_PROCESS_DPI_AWARENESS {
} SHC_PROCESS_DPI_AWARENESS;
bool DisplayServerWindows::is_dark_mode_supported() const {
- return ux_theme_available && IsDarkModeAllowedForApp();
+ return ux_theme_available;
}
bool DisplayServerWindows::is_dark_mode() const {
@@ -3817,13 +3816,12 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
// Load UXTheme.
HMODULE ux_theme_lib = LoadLibraryW(L"uxtheme.dll");
if (ux_theme_lib) {
- IsDarkModeAllowedForApp = (IsDarkModeAllowedForAppPtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(136));
ShouldAppsUseDarkMode = (ShouldAppsUseDarkModePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(132));
GetImmersiveColorFromColorSetEx = (GetImmersiveColorFromColorSetExPtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(95));
GetImmersiveColorTypeFromName = (GetImmersiveColorTypeFromNamePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(96));
GetImmersiveUserColorSetPreference = (GetImmersiveUserColorSetPreferencePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(98));
- ux_theme_available = IsDarkModeAllowedForApp && ShouldAppsUseDarkMode && GetImmersiveColorFromColorSetEx && GetImmersiveColorTypeFromName && GetImmersiveUserColorSetPreference;
+ ux_theme_available = ShouldAppsUseDarkMode && GetImmersiveColorFromColorSetEx && GetImmersiveColorTypeFromName && GetImmersiveUserColorSetPreference;
if (os_ver.dwBuildNumber >= 22000) {
dark_title_available = true;
}
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index 4702bb7765..82894d300f 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -152,7 +152,6 @@ typedef UINT(WINAPI *WTInfoPtr)(UINT p_category, UINT p_index, LPVOID p_output);
typedef BOOL(WINAPI *WTPacketPtr)(HANDLE p_ctx, UINT p_param, LPVOID p_packets);
typedef BOOL(WINAPI *WTEnablePtr)(HANDLE p_ctx, BOOL p_enable);
-typedef bool(WINAPI *IsDarkModeAllowedForAppPtr)();
typedef bool(WINAPI *ShouldAppsUseDarkModePtr)();
typedef DWORD(WINAPI *GetImmersiveColorFromColorSetExPtr)(UINT dwImmersiveColorSet, UINT dwImmersiveColorType, bool bIgnoreHighContrast, UINT dwHighContrastCacheMode);
typedef int(WINAPI *GetImmersiveColorTypeFromNamePtr)(const WCHAR *name);
@@ -288,7 +287,6 @@ class DisplayServerWindows : public DisplayServer {
// UXTheme API
static bool dark_title_available;
static bool ux_theme_available;
- static IsDarkModeAllowedForAppPtr IsDarkModeAllowedForApp;
static ShouldAppsUseDarkModePtr ShouldAppsUseDarkMode;
static GetImmersiveColorFromColorSetExPtr GetImmersiveColorFromColorSetEx;
static GetImmersiveColorTypeFromNamePtr GetImmersiveColorTypeFromName;
diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp
index df75d34e0f..fccaf63319 100644
--- a/scene/2d/collision_object_2d.cpp
+++ b/scene/2d/collision_object_2d.cpp
@@ -661,5 +661,6 @@ CollisionObject2D::CollisionObject2D() {
}
CollisionObject2D::~CollisionObject2D() {
+ ERR_FAIL_NULL(PhysicsServer2D::get_singleton());
PhysicsServer2D::get_singleton()->free(rid);
}
diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp
index c6296f4732..68dc85486d 100644
--- a/scene/2d/cpu_particles_2d.cpp
+++ b/scene/2d/cpu_particles_2d.cpp
@@ -1507,6 +1507,7 @@ CPUParticles2D::CPUParticles2D() {
}
CPUParticles2D::~CPUParticles2D() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(multimesh);
RS::get_singleton()->free(mesh);
}
diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp
index ccbc080768..95e24028a6 100644
--- a/scene/2d/gpu_particles_2d.cpp
+++ b/scene/2d/gpu_particles_2d.cpp
@@ -688,6 +688,7 @@ GPUParticles2D::GPUParticles2D() {
}
GPUParticles2D::~GPUParticles2D() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(particles);
RS::get_singleton()->free(mesh);
}
diff --git a/scene/2d/joint_2d.cpp b/scene/2d/joint_2d.cpp
index 3ec744ff8e..9e12c7aa3b 100644
--- a/scene/2d/joint_2d.cpp
+++ b/scene/2d/joint_2d.cpp
@@ -246,6 +246,7 @@ Joint2D::Joint2D() {
}
Joint2D::~Joint2D() {
+ ERR_FAIL_NULL(PhysicsServer2D::get_singleton());
PhysicsServer2D::get_singleton()->free(joint);
}
diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp
index 78b5199358..e07393a9c7 100644
--- a/scene/2d/light_2d.cpp
+++ b/scene/2d/light_2d.cpp
@@ -321,6 +321,7 @@ Light2D::Light2D() {
}
Light2D::~Light2D() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RenderingServer::get_singleton()->free(canvas_light);
}
diff --git a/scene/2d/light_occluder_2d.cpp b/scene/2d/light_occluder_2d.cpp
index 67e82140e4..e7997c6eff 100644
--- a/scene/2d/light_occluder_2d.cpp
+++ b/scene/2d/light_occluder_2d.cpp
@@ -148,6 +148,7 @@ OccluderPolygon2D::OccluderPolygon2D() {
}
OccluderPolygon2D::~OccluderPolygon2D() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(occ_polygon);
}
@@ -291,5 +292,7 @@ LightOccluder2D::LightOccluder2D() {
}
LightOccluder2D::~LightOccluder2D() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+
RS::get_singleton()->free(occluder);
}
diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp
index 62afe0d89b..4455142a6c 100644
--- a/scene/2d/navigation_agent_2d.cpp
+++ b/scene/2d/navigation_agent_2d.cpp
@@ -197,6 +197,7 @@ NavigationAgent2D::NavigationAgent2D() {
}
NavigationAgent2D::~NavigationAgent2D() {
+ ERR_FAIL_NULL(NavigationServer2D::get_singleton());
NavigationServer2D::get_singleton()->free(agent);
agent = RID(); // Pointless
}
diff --git a/scene/2d/navigation_link_2d.cpp b/scene/2d/navigation_link_2d.cpp
index d639e1cc89..34005dbd9e 100644
--- a/scene/2d/navigation_link_2d.cpp
+++ b/scene/2d/navigation_link_2d.cpp
@@ -285,6 +285,7 @@ NavigationLink2D::NavigationLink2D() {
}
NavigationLink2D::~NavigationLink2D() {
+ ERR_FAIL_NULL(NavigationServer2D::get_singleton());
NavigationServer2D::get_singleton()->free(link);
link = RID();
}
diff --git a/scene/2d/navigation_obstacle_2d.cpp b/scene/2d/navigation_obstacle_2d.cpp
index cbec4db4c5..4d9d46fb06 100644
--- a/scene/2d/navigation_obstacle_2d.cpp
+++ b/scene/2d/navigation_obstacle_2d.cpp
@@ -116,6 +116,7 @@ NavigationObstacle2D::NavigationObstacle2D() {
}
NavigationObstacle2D::~NavigationObstacle2D() {
+ ERR_FAIL_NULL(NavigationServer2D::get_singleton());
NavigationServer2D::get_singleton()->free(agent);
agent = RID(); // Pointless
}
diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp
index 675ef7c780..78c65256e8 100644
--- a/scene/2d/navigation_region_2d.cpp
+++ b/scene/2d/navigation_region_2d.cpp
@@ -343,6 +343,7 @@ NavigationRegion2D::NavigationRegion2D() {
}
NavigationRegion2D::~NavigationRegion2D() {
+ ERR_FAIL_NULL(NavigationServer2D::get_singleton());
NavigationServer2D::get_singleton()->free(region);
#ifdef DEBUG_ENABLED
diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp
index e41664b006..5b00a3a2dc 100644
--- a/scene/2d/polygon_2d.cpp
+++ b/scene/2d/polygon_2d.cpp
@@ -664,6 +664,7 @@ Polygon2D::Polygon2D() {
Polygon2D::~Polygon2D() {
// This will free the internally-allocated mesh instance, if allocated.
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->canvas_item_attach_skeleton(get_canvas_item(), RID());
RS::get_singleton()->free(mesh);
}
diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp
index 62787d4488..fa0358fabc 100644
--- a/scene/2d/skeleton_2d.cpp
+++ b/scene/2d/skeleton_2d.cpp
@@ -529,6 +529,7 @@ Bone2D::Bone2D() {
Bone2D::~Bone2D() {
#ifdef TOOLS_ENABLED
if (!editor_gizmo_rid.is_null()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RenderingServer::get_singleton()->free(editor_gizmo_rid);
}
#endif // TOOLS_ENABLED
@@ -803,5 +804,6 @@ Skeleton2D::Skeleton2D() {
}
Skeleton2D::~Skeleton2D() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(skeleton);
}
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 3136752a2e..f52dbd1c53 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -1108,6 +1108,7 @@ void TileMap::_rendering_update_layer(int p_layer) {
void TileMap::_rendering_cleanup_layer(int p_layer) {
ERR_FAIL_INDEX(p_layer, (int)layers.size());
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RenderingServer *rs = RenderingServer::get_singleton();
if (layers[p_layer].canvas_item.is_valid()) {
rs->free(layers[p_layer].canvas_item);
@@ -1280,6 +1281,7 @@ void TileMap::_rendering_create_quadrant(TileMapQuadrant *p_quadrant) {
}
void TileMap::_rendering_cleanup_quadrant(TileMapQuadrant *p_quadrant) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
// Free the canvas items.
for (const RID &ci : p_quadrant->canvas_items) {
RenderingServer::get_singleton()->free(ci);
@@ -1596,6 +1598,7 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r
void TileMap::_physics_cleanup_quadrant(TileMapQuadrant *p_quadrant) {
// Remove a quadrant.
+ ERR_FAIL_NULL(PhysicsServer2D::get_singleton());
for (RID body : p_quadrant->bodies) {
bodies_coords.erase(body);
PhysicsServer2D::get_singleton()->free(body);
@@ -1754,6 +1757,7 @@ void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
void TileMap::_navigation_cleanup_quadrant(TileMapQuadrant *p_quadrant) {
// Clear navigation shapes in the quadrant.
+ ERR_FAIL_NULL(NavigationServer2D::get_singleton());
for (const KeyValue<Vector2i, Vector<RID>> &E : p_quadrant->navigation_regions) {
for (int i = 0; i < E.value.size(); i++) {
RID region = E.value[i];
diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp
index 2a50575749..03c24e63ef 100644
--- a/scene/3d/camera_3d.cpp
+++ b/scene/3d/camera_3d.cpp
@@ -744,8 +744,10 @@ Camera3D::Camera3D() {
}
Camera3D::~Camera3D() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RenderingServer::get_singleton()->free(camera);
if (pyramid_shape.is_valid()) {
+ ERR_FAIL_NULL(PhysicsServer3D::get_singleton());
PhysicsServer3D::get_singleton()->free(pyramid_shape);
}
}
diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp
index 66546092f2..1b94ff822c 100644
--- a/scene/3d/collision_object_3d.cpp
+++ b/scene/3d/collision_object_3d.cpp
@@ -352,6 +352,8 @@ void CollisionObject3D::_shape_changed(const Ref<Shape3D> &p_shape) {
}
void CollisionObject3D::_update_debug_shapes() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+
if (!is_inside_tree()) {
debug_shapes_to_update.clear();
return;
@@ -393,6 +395,8 @@ void CollisionObject3D::_update_debug_shapes() {
}
void CollisionObject3D::_clear_debug_shapes() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+
for (KeyValue<uint32_t, ShapeData> &E : shapes) {
ShapeData &shapedata = E.value;
ShapeData::ShapeBase *shape_bases = shapedata.shapes.ptrw();
@@ -627,6 +631,7 @@ int CollisionObject3D::shape_owner_get_shape_index(uint32_t p_owner, int p_shape
}
void CollisionObject3D::shape_owner_remove_shape(uint32_t p_owner, int p_shape) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
ERR_FAIL_COND(!shapes.has(p_owner));
ERR_FAIL_INDEX(p_shape, shapes[p_owner].shapes.size());
@@ -722,5 +727,6 @@ CollisionObject3D::CollisionObject3D() {
}
CollisionObject3D::~CollisionObject3D() {
+ ERR_FAIL_NULL(PhysicsServer3D::get_singleton());
PhysicsServer3D::get_singleton()->free(rid);
}
diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp
index 5ac8535bb6..6d47375589 100644
--- a/scene/3d/cpu_particles_3d.cpp
+++ b/scene/3d/cpu_particles_3d.cpp
@@ -1701,5 +1701,6 @@ CPUParticles3D::CPUParticles3D() {
}
CPUParticles3D::~CPUParticles3D() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(multimesh);
}
diff --git a/scene/3d/decal.cpp b/scene/3d/decal.cpp
index fc442986a8..ee1086fb88 100644
--- a/scene/3d/decal.cpp
+++ b/scene/3d/decal.cpp
@@ -254,5 +254,6 @@ Decal::Decal() {
}
Decal::~Decal() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(decal);
}
diff --git a/scene/3d/fog_volume.cpp b/scene/3d/fog_volume.cpp
index 4606e70310..d9912c47b1 100644
--- a/scene/3d/fog_volume.cpp
+++ b/scene/3d/fog_volume.cpp
@@ -118,5 +118,6 @@ FogVolume::FogVolume() {
}
FogVolume::~FogVolume() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(volume);
}
diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp
index 17dfe2610e..6302a9e1b8 100644
--- a/scene/3d/gpu_particles_3d.cpp
+++ b/scene/3d/gpu_particles_3d.cpp
@@ -634,5 +634,6 @@ GPUParticles3D::GPUParticles3D() {
}
GPUParticles3D::~GPUParticles3D() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(particles);
}
diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp
index 476820b1c4..f6546b2d1f 100644
--- a/scene/3d/gpu_particles_collision_3d.cpp
+++ b/scene/3d/gpu_particles_collision_3d.cpp
@@ -58,6 +58,7 @@ GPUParticlesCollision3D::GPUParticlesCollision3D(RS::ParticlesCollisionType p_ty
}
GPUParticlesCollision3D::~GPUParticlesCollision3D() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(collision);
}
@@ -819,6 +820,7 @@ GPUParticlesAttractor3D::GPUParticlesAttractor3D(RS::ParticlesCollisionType p_ty
set_base(collision);
}
GPUParticlesAttractor3D::~GPUParticlesAttractor3D() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(collision);
}
diff --git a/scene/3d/joint_3d.cpp b/scene/3d/joint_3d.cpp
index 1a18f43e7b..d86c156d99 100644
--- a/scene/3d/joint_3d.cpp
+++ b/scene/3d/joint_3d.cpp
@@ -234,6 +234,7 @@ Joint3D::Joint3D() {
}
Joint3D::~Joint3D() {
+ ERR_FAIL_NULL(PhysicsServer3D::get_singleton());
PhysicsServer3D::get_singleton()->free(joint);
}
diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp
index 262dc0db37..d3020fc5b7 100644
--- a/scene/3d/label_3d.cpp
+++ b/scene/3d/label_3d.cpp
@@ -966,6 +966,7 @@ Label3D::~Label3D() {
TS->free_rid(text_rid);
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RenderingServer::get_singleton()->free(mesh);
for (KeyValue<SurfaceKey, SurfaceData> E : surfaces) {
RenderingServer::get_singleton()->free(E.value.material);
diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp
index 3f43e718b8..23a08f0292 100644
--- a/scene/3d/light_3d.cpp
+++ b/scene/3d/light_3d.cpp
@@ -476,6 +476,7 @@ Light3D::Light3D() {
}
Light3D::~Light3D() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->instance_set_base(get_instance(), RID());
if (light.is_valid()) {
diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp
index 1b5427c80d..108bbe072e 100644
--- a/scene/3d/lightmap_gi.cpp
+++ b/scene/3d/lightmap_gi.cpp
@@ -313,6 +313,7 @@ LightmapGIData::LightmapGIData() {
}
LightmapGIData::~LightmapGIData() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(lightmap);
}
diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp
index 741f7397ad..3acb33f42a 100644
--- a/scene/3d/navigation_agent_3d.cpp
+++ b/scene/3d/navigation_agent_3d.cpp
@@ -204,6 +204,7 @@ NavigationAgent3D::NavigationAgent3D() {
}
NavigationAgent3D::~NavigationAgent3D() {
+ ERR_FAIL_NULL(NavigationServer3D::get_singleton());
NavigationServer3D::get_singleton()->free(agent);
agent = RID(); // Pointless
}
diff --git a/scene/3d/navigation_link_3d.cpp b/scene/3d/navigation_link_3d.cpp
index bee7c7f39b..009abfc8fd 100644
--- a/scene/3d/navigation_link_3d.cpp
+++ b/scene/3d/navigation_link_3d.cpp
@@ -227,10 +227,12 @@ NavigationLink3D::NavigationLink3D() {
}
NavigationLink3D::~NavigationLink3D() {
+ ERR_FAIL_NULL(NavigationServer3D::get_singleton());
NavigationServer3D::get_singleton()->free(link);
link = RID();
#ifdef DEBUG_ENABLED
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
if (debug_instance.is_valid()) {
RenderingServer::get_singleton()->free(debug_instance);
}
diff --git a/scene/3d/navigation_obstacle_3d.cpp b/scene/3d/navigation_obstacle_3d.cpp
index f241d65649..6431bd2b77 100644
--- a/scene/3d/navigation_obstacle_3d.cpp
+++ b/scene/3d/navigation_obstacle_3d.cpp
@@ -122,6 +122,7 @@ NavigationObstacle3D::NavigationObstacle3D() {
}
NavigationObstacle3D::~NavigationObstacle3D() {
+ ERR_FAIL_NULL(NavigationServer3D::get_singleton());
NavigationServer3D::get_singleton()->free(agent);
agent = RID(); // Pointless
}
diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp
index 86b78f847e..0e0233c0cd 100644
--- a/scene/3d/navigation_region_3d.cpp
+++ b/scene/3d/navigation_region_3d.cpp
@@ -375,12 +375,15 @@ NavigationRegion3D::~NavigationRegion3D() {
if (navigation_mesh.is_valid()) {
navigation_mesh->disconnect("changed", callable_mp(this, &NavigationRegion3D::_navigation_changed));
}
+ ERR_FAIL_NULL(NavigationServer3D::get_singleton());
NavigationServer3D::get_singleton()->free(region);
#ifdef DEBUG_ENABLED
NavigationServer3D::get_singleton_mut()->disconnect("map_changed", callable_mp(this, &NavigationRegion3D::_navigation_map_changed));
NavigationServer3D::get_singleton_mut()->disconnect("navigation_debug_changed", callable_mp(this, &NavigationRegion3D::_update_debug_mesh));
NavigationServer3D::get_singleton_mut()->disconnect("navigation_debug_changed", callable_mp(this, &NavigationRegion3D::_update_debug_edge_connections_mesh));
+
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
if (debug_instance.is_valid()) {
RenderingServer::get_singleton()->free(debug_instance);
}
diff --git a/scene/3d/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp
index 4e1ed5654a..7166d99b92 100644
--- a/scene/3d/occluder_instance_3d.cpp
+++ b/scene/3d/occluder_instance_3d.cpp
@@ -32,6 +32,7 @@
#include "core/config/project_settings.h"
#include "core/core_string_names.h"
+#include "core/io/marshalls.h"
#include "core/math/geometry_2d.h"
#include "core/math/triangulate.h"
#include "scene/3d/importer_mesh_instance_3d.h"
@@ -138,6 +139,7 @@ Occluder3D::Occluder3D() {
Occluder3D::~Occluder3D() {
if (occluder.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(occluder);
}
}
@@ -533,11 +535,20 @@ void OccluderInstance3D::_bake_surface(const Transform3D &p_transform, Array p_s
}
if (!Math::is_zero_approx(p_simplification_dist) && SurfaceTool::simplify_func) {
- float error_scale = SurfaceTool::simplify_scale_func((float *)vertices.ptr(), vertices.size(), sizeof(Vector3));
+ Vector<float> vertices_f32 = vector3_to_float32_array(vertices.ptr(), vertices.size());
+
+ float error_scale = SurfaceTool::simplify_scale_func(vertices_f32.ptr(), vertices.size(), sizeof(float) * 3);
float target_error = p_simplification_dist / error_scale;
float error = -1.0f;
int target_index_count = MIN(indices.size(), 36);
- uint32_t index_count = SurfaceTool::simplify_func((unsigned int *)indices.ptrw(), (unsigned int *)indices.ptr(), indices.size(), (float *)vertices.ptr(), vertices.size(), sizeof(Vector3), target_index_count, target_error, &error);
+
+ uint32_t index_count = SurfaceTool::simplify_func(
+ (unsigned int *)indices.ptrw(),
+ (unsigned int *)indices.ptr(),
+ indices.size(),
+ vertices_f32.ptr(), vertices.size(), sizeof(float) * 3,
+ target_index_count, target_error, &error);
+
indices.resize(index_count);
}
diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp
index 02ab297d8e..ea0cfce987 100644
--- a/scene/3d/path_3d.cpp
+++ b/scene/3d/path_3d.cpp
@@ -40,6 +40,7 @@ Path3D::Path3D() {
}
Path3D::~Path3D() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
if (debug_instance.is_valid()) {
RS::get_singleton()->free(debug_instance);
}
diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp
index d29f337fc8..fd0b7912ae 100644
--- a/scene/3d/physics_body_3d.cpp
+++ b/scene/3d/physics_body_3d.cpp
@@ -1246,6 +1246,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
platform_rid = RID();
platform_object_id = ObjectID();
platform_velocity = Vector3();
+ platform_angular_velocity = Vector3();
platform_ceiling_velocity = Vector3();
floor_normal = Vector3();
wall_normal = Vector3();
@@ -1506,6 +1507,7 @@ void CharacterBody3D::_move_and_slide_floating(double p_delta) {
platform_object_id = ObjectID();
floor_normal = Vector3();
platform_velocity = Vector3();
+ platform_angular_velocity = Vector3();
bool first_slide = true;
for (int iteration = 0; iteration < max_slides; ++iteration) {
@@ -1708,6 +1710,7 @@ void CharacterBody3D::_set_platform_data(const PhysicsServer3D::MotionCollision
platform_rid = p_collision.collider;
platform_object_id = p_collision.collider_id;
platform_velocity = p_collision.collider_velocity;
+ platform_angular_velocity = p_collision.collider_angular_velocity;
platform_layer = PhysicsServer3D::get_singleton()->body_get_collision_layer(platform_rid);
}
@@ -1780,6 +1783,10 @@ const Vector3 &CharacterBody3D::get_platform_velocity() const {
return platform_velocity;
}
+const Vector3 &CharacterBody3D::get_platform_angular_velocity() const {
+ return platform_angular_velocity;
+}
+
Vector3 CharacterBody3D::get_linear_velocity() const {
return get_real_velocity();
}
@@ -1932,6 +1939,7 @@ void CharacterBody3D::_notification(int p_what) {
platform_object_id = ObjectID();
motion_results.clear();
platform_velocity = Vector3();
+ platform_angular_velocity = Vector3();
} break;
}
}
@@ -1986,6 +1994,7 @@ void CharacterBody3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_real_velocity"), &CharacterBody3D::get_real_velocity);
ClassDB::bind_method(D_METHOD("get_floor_angle", "up_direction"), &CharacterBody3D::get_floor_angle, DEFVAL(Vector3(0.0, 1.0, 0.0)));
ClassDB::bind_method(D_METHOD("get_platform_velocity"), &CharacterBody3D::get_platform_velocity);
+ ClassDB::bind_method(D_METHOD("get_platform_angular_velocity"), &CharacterBody3D::get_platform_angular_velocity);
ClassDB::bind_method(D_METHOD("get_slide_collision_count"), &CharacterBody3D::get_slide_collision_count);
ClassDB::bind_method(D_METHOD("get_slide_collision", "slide_idx"), &CharacterBody3D::_get_slide_collision);
ClassDB::bind_method(D_METHOD("get_last_slide_collision"), &CharacterBody3D::_get_last_slide_collision);
@@ -3358,6 +3367,7 @@ PhysicalBone3D::~PhysicalBone3D() {
if (joint_data) {
memdelete(joint_data);
}
+ ERR_FAIL_NULL(PhysicsServer3D::get_singleton());
PhysicsServer3D::get_singleton()->free(joint);
}
diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h
index 36b7ce774c..9c5cb48762 100644
--- a/scene/3d/physics_body_3d.h
+++ b/scene/3d/physics_body_3d.h
@@ -371,6 +371,7 @@ public:
const Vector3 &get_real_velocity() const;
real_t get_floor_angle(const Vector3 &p_up_direction = Vector3(0.0, 1.0, 0.0)) const;
const Vector3 &get_platform_velocity() const;
+ const Vector3 &get_platform_angular_velocity() const;
virtual Vector3 get_linear_velocity() const override;
@@ -423,6 +424,7 @@ private:
Vector3 ceiling_normal;
Vector3 last_motion;
Vector3 platform_velocity;
+ Vector3 platform_angular_velocity;
Vector3 platform_ceiling_velocity;
Vector3 previous_position;
Vector3 real_velocity;
diff --git a/scene/3d/reflection_probe.cpp b/scene/3d/reflection_probe.cpp
index bc3cc31963..f8164ee1aa 100644
--- a/scene/3d/reflection_probe.cpp
+++ b/scene/3d/reflection_probe.cpp
@@ -257,5 +257,6 @@ ReflectionProbe::ReflectionProbe() {
}
ReflectionProbe::~ReflectionProbe() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(probe);
}
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index b205c2cde0..27310e7cee 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -58,10 +58,10 @@ Ref<Skin> SkinReference::get_skin() const {
}
SkinReference::~SkinReference() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
if (skeleton_node) {
skeleton_node->skin_bindings.erase(this);
}
-
RS::get_singleton()->free(skeleton);
}
diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_body_3d.cpp
index 1814baa9e9..31d3f1aeb7 100644
--- a/scene/3d/soft_body_3d.cpp
+++ b/scene/3d/soft_body_3d.cpp
@@ -704,6 +704,7 @@ SoftBody3D::SoftBody3D() :
SoftBody3D::~SoftBody3D() {
memdelete(rendering_server_handler);
+ ERR_FAIL_NULL(PhysicsServer3D::get_singleton());
PhysicsServer3D::get_singleton()->free(physics_rid);
}
diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp
index e488ff3059..3629464cd1 100644
--- a/scene/3d/sprite_3d.cpp
+++ b/scene/3d/sprite_3d.cpp
@@ -611,6 +611,7 @@ SpriteBase3D::SpriteBase3D() {
}
SpriteBase3D::~SpriteBase3D() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RenderingServer::get_singleton()->free(mesh);
RenderingServer::get_singleton()->free(material);
}
diff --git a/scene/3d/visible_on_screen_notifier_3d.cpp b/scene/3d/visible_on_screen_notifier_3d.cpp
index 6d4c18a69e..70c9321338 100644
--- a/scene/3d/visible_on_screen_notifier_3d.cpp
+++ b/scene/3d/visible_on_screen_notifier_3d.cpp
@@ -99,6 +99,7 @@ VisibleOnScreenNotifier3D::VisibleOnScreenNotifier3D() {
VisibleOnScreenNotifier3D::~VisibleOnScreenNotifier3D() {
RID base_old = get_base();
set_base(RID());
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(base_old);
}
diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp
index 3b21f5c03a..07ca63dcd6 100644
--- a/scene/3d/visual_instance_3d.cpp
+++ b/scene/3d/visual_instance_3d.cpp
@@ -157,6 +157,7 @@ VisualInstance3D::VisualInstance3D() {
}
VisualInstance3D::~VisualInstance3D() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RenderingServer::get_singleton()->free(instance);
}
diff --git a/scene/3d/voxel_gi.cpp b/scene/3d/voxel_gi.cpp
index 7caf2f4874..2cb15715c6 100644
--- a/scene/3d/voxel_gi.cpp
+++ b/scene/3d/voxel_gi.cpp
@@ -242,6 +242,7 @@ VoxelGIData::VoxelGIData() {
}
VoxelGIData::~VoxelGIData() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(probe);
}
@@ -516,5 +517,6 @@ VoxelGI::VoxelGI() {
}
VoxelGI::~VoxelGI() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(voxel_gi);
}
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 11a3803b35..a846ab9485 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -411,7 +411,7 @@ void FileDialog::_go_back() {
}
void FileDialog::_go_forward() {
- if (local_history_pos == local_history.size() - 1) {
+ if (local_history_pos >= local_history.size() - 1) {
return;
}
diff --git a/scene/gui/menu_bar.cpp b/scene/gui/menu_bar.cpp
index 82ef53e317..3c404c65b5 100644
--- a/scene/gui/menu_bar.cpp
+++ b/scene/gui/menu_bar.cpp
@@ -345,6 +345,7 @@ void MenuBar::_notification(int p_what) {
} break;
case NOTIFICATION_MOUSE_EXIT: {
focused_menu = -1;
+ selected_menu = -1;
queue_redraw();
} break;
case NOTIFICATION_TRANSLATION_CHANGED:
diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp
index f87829cf71..beaffc14b0 100644
--- a/scene/gui/tab_bar.cpp
+++ b/scene/gui/tab_bar.cpp
@@ -154,7 +154,9 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
queue_redraw();
}
- _update_hover();
+ if (!tabs.is_empty()) {
+ _update_hover();
+ }
return;
}
@@ -362,12 +364,19 @@ void TabBar::_notification(int p_what) {
} break;
case NOTIFICATION_DRAW: {
+ bool rtl = is_layout_rtl();
+ Vector2 size = get_size();
+
if (tabs.is_empty()) {
+ // Draw the drop indicator where the first tab would be if there are no tabs.
+ if (dragging_valid_tab) {
+ int x = rtl ? size.x : 0;
+ theme_cache.drop_mark_icon->draw(get_canvas_item(), Point2(x - (theme_cache.drop_mark_icon->get_width() / 2), (size.height - theme_cache.drop_mark_icon->get_height()) / 2), theme_cache.drop_mark_color);
+ }
+
return;
}
- bool rtl = is_layout_rtl();
- Vector2 size = get_size();
int limit_minus_buttons = size.width - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width();
int ofs = tabs[offset].ofs_cache;
@@ -1092,7 +1101,8 @@ void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) {
hover_now += 1;
}
} else {
- hover_now = is_layout_rtl() ^ (p_point.x < get_tab_rect(0).position.x) ? 0 : get_tab_count() - 1;
+ int x = tabs.is_empty() ? 0 : get_tab_rect(0).position.x;
+ hover_now = is_layout_rtl() ^ (p_point.x < x) ? 0 : get_tab_count() - 1;
}
move_tab(tab_from_id, hover_now);
@@ -1118,7 +1128,7 @@ void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) {
hover_now += 1;
}
} else {
- hover_now = is_layout_rtl() ^ (p_point.x < get_tab_rect(0).position.x) ? 0 : get_tab_count();
+ hover_now = tabs.is_empty() || (is_layout_rtl() ^ (p_point.x < get_tab_rect(0).position.x)) ? 0 : get_tab_count();
}
Tab moving_tab = from_tabs->tabs[tab_from_id];
@@ -1154,10 +1164,13 @@ void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) {
int TabBar::get_tab_idx_at_point(const Point2 &p_point) const {
int hover_now = -1;
- for (int i = offset; i <= max_drawn_tab; i++) {
- Rect2 rect = get_tab_rect(i);
- if (rect.has_point(p_point)) {
- hover_now = i;
+
+ if (!tabs.is_empty()) {
+ for (int i = offset; i <= max_drawn_tab; i++) {
+ Rect2 rect = get_tab_rect(i);
+ if (rect.has_point(p_point)) {
+ hover_now = i;
+ }
}
}
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 79bad44e15..4560643998 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -2580,8 +2580,8 @@ void Tree::_range_click_timeout() {
mb.instantiate();
int x_limit = get_size().width - theme_cache.panel_style->get_minimum_size().width;
- if (h_scroll->is_visible()) {
- x_limit -= h_scroll->get_minimum_size().width;
+ if (v_scroll->is_visible()) {
+ x_limit -= v_scroll->get_minimum_size().width;
}
cache.rtl = is_layout_rtl();
@@ -3640,8 +3640,8 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) {
propagate_mouse_activated = false;
int x_limit = get_size().width - theme_cache.panel_style->get_minimum_size().width;
- if (h_scroll->is_visible()) {
- x_limit -= h_scroll->get_minimum_size().width;
+ if (v_scroll->is_visible()) {
+ x_limit -= v_scroll->get_minimum_size().width;
}
cache.rtl = is_layout_rtl();
@@ -4015,8 +4015,8 @@ void Tree::_notification(int p_what) {
Point2 draw_ofs;
draw_ofs += bg->get_offset();
Size2 draw_size = get_size() - bg->get_minimum_size();
- if (h_scroll->is_visible()) {
- draw_size.width -= h_scroll->get_minimum_size().width;
+ if (v_scroll->is_visible()) {
+ draw_size.width -= v_scroll->get_minimum_size().width;
}
bg->draw(ci, Rect2(Point2(), get_size()));
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index f3812eb497..404c289a6a 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -1305,6 +1305,7 @@ CanvasItem::CanvasItem() :
}
CanvasItem::~CanvasItem() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RenderingServer::get_singleton()->free(canvas_item);
}
@@ -1459,5 +1460,6 @@ CanvasTexture::CanvasTexture() {
canvas_texture = RS::get_singleton()->canvas_texture_create();
}
CanvasTexture::~CanvasTexture() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(canvas_texture);
}
diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp
index 97b784e9d0..2aa8df10ab 100644
--- a/scene/main/canvas_layer.cpp
+++ b/scene/main/canvas_layer.cpp
@@ -360,5 +360,6 @@ CanvasLayer::CanvasLayer() {
}
CanvasLayer::~CanvasLayer() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(canvas);
}
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index afc31ae480..c0b81c9b9a 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -80,6 +80,7 @@ void ViewportTexture::setup_local_to_scene() {
vp->viewport_textures.insert(this);
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
if (proxy_ph.is_valid()) {
RS::get_singleton()->texture_proxy_update(proxy, vp->texture_rid);
RS::get_singleton()->free(proxy_ph);
@@ -153,6 +154,8 @@ ViewportTexture::~ViewportTexture() {
vp->viewport_textures.erase(this);
}
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+
if (proxy_ph.is_valid()) {
RS::get_singleton()->free(proxy_ph);
}
@@ -301,6 +304,8 @@ void Viewport::_sub_window_remove(Window *p_window) {
int index = _sub_window_find(p_window);
ERR_FAIL_COND(index == -1);
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+
RS::get_singleton()->free(gui.sub_windows[index].canvas_item);
gui.sub_windows.remove_at(index);
@@ -4117,6 +4122,7 @@ Viewport::~Viewport() {
for (ViewportTexture *E : viewport_textures) {
E->vp = nullptr;
}
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RenderingServer::get_singleton()->free(viewport);
}
diff --git a/scene/resources/camera_attributes.cpp b/scene/resources/camera_attributes.cpp
index 8e4876e01f..292accddc1 100644
--- a/scene/resources/camera_attributes.cpp
+++ b/scene/resources/camera_attributes.cpp
@@ -135,6 +135,7 @@ CameraAttributes::CameraAttributes() {
}
CameraAttributes::~CameraAttributes() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(camera_attributes);
}
diff --git a/scene/resources/canvas_item_material.cpp b/scene/resources/canvas_item_material.cpp
index b16059c218..0cc5faffb1 100644
--- a/scene/resources/canvas_item_material.cpp
+++ b/scene/resources/canvas_item_material.cpp
@@ -294,6 +294,8 @@ CanvasItemMaterial::CanvasItemMaterial() :
CanvasItemMaterial::~CanvasItemMaterial() {
MutexLock lock(material_mutex);
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+
if (shader_map.has(current_key)) {
shader_map[current_key].users--;
if (shader_map[current_key].users == 0) {
diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp
index 23bd8a4be4..f97cffc3fc 100644
--- a/scene/resources/environment.cpp
+++ b/scene/resources/environment.cpp
@@ -1550,5 +1550,6 @@ Environment::Environment() {
}
Environment::~Environment() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(environment);
}
diff --git a/scene/resources/fog_material.cpp b/scene/resources/fog_material.cpp
index 46b44d681f..2aca552716 100644
--- a/scene/resources/fog_material.cpp
+++ b/scene/resources/fog_material.cpp
@@ -132,6 +132,7 @@ void FogMaterial::_bind_methods() {
void FogMaterial::cleanup_shader() {
if (shader.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(shader);
}
}
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index 584a7e7eac..78b4edfcf7 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -270,24 +270,18 @@ void Font::set_cache_capacity(int p_single_line, int p_multi_line) {
}
Size2 Font::get_string_size(const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const {
- uint64_t hash = p_text.hash64();
- hash = hash_djb2_one_64(p_font_size, hash);
- if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) {
- hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash);
- hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash);
- }
- hash = hash_djb2_one_64(p_direction, hash);
- hash = hash_djb2_one_64(p_orientation, hash);
+ bool fill = (p_alignment == HORIZONTAL_ALIGNMENT_FILL);
+ ShapedTextKey key = ShapedTextKey(p_text, p_font_size, fill ? p_width : 0.0, fill ? p_jst_flags : TextServer::JUSTIFICATION_NONE, TextServer::BREAK_NONE, p_direction, p_orientation);
Ref<TextLine> buffer;
- if (cache.has(hash)) {
- buffer = cache.get(hash);
+ if (cache.has(key)) {
+ buffer = cache.get(key);
} else {
buffer.instantiate();
buffer->set_direction(p_direction);
buffer->set_orientation(p_orientation);
buffer->add_string(p_text, Ref<Font>(this), p_font_size);
- cache.insert(hash, buffer);
+ cache.insert(key, buffer);
}
buffer->set_width(p_width);
@@ -300,17 +294,11 @@ Size2 Font::get_string_size(const String &p_text, HorizontalAlignment p_alignmen
}
Size2 Font::get_multiline_string_size(const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, BitField<TextServer::LineBreakFlag> p_brk_flags, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const {
- uint64_t hash = p_text.hash64();
- hash = hash_djb2_one_64(p_font_size, hash);
- hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash);
- hash = hash_djb2_one_64(p_brk_flags.operator int64_t(), hash);
- hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash);
- hash = hash_djb2_one_64(p_direction, hash);
- hash = hash_djb2_one_64(p_orientation, hash);
+ ShapedTextKey key = ShapedTextKey(p_text, p_font_size, p_width, p_jst_flags, p_brk_flags, p_direction, p_orientation);
Ref<TextParagraph> lines_buffer;
- if (cache_wrap.has(hash)) {
- lines_buffer = cache_wrap.get(hash);
+ if (cache_wrap.has(key)) {
+ lines_buffer = cache_wrap.get(key);
} else {
lines_buffer.instantiate();
lines_buffer->set_direction(p_direction);
@@ -319,7 +307,7 @@ Size2 Font::get_multiline_string_size(const String &p_text, HorizontalAlignment
lines_buffer->set_width(p_width);
lines_buffer->set_break_flags(p_brk_flags);
lines_buffer->set_justification_flags(p_jst_flags);
- cache_wrap.insert(hash, lines_buffer);
+ cache_wrap.insert(key, lines_buffer);
}
lines_buffer->set_alignment(p_alignment);
@@ -329,24 +317,18 @@ Size2 Font::get_multiline_string_size(const String &p_text, HorizontalAlignment
}
void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, const Color &p_modulate, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const {
- uint64_t hash = p_text.hash64();
- hash = hash_djb2_one_64(p_font_size, hash);
- if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) {
- hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash);
- hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash);
- }
- hash = hash_djb2_one_64(p_direction, hash);
- hash = hash_djb2_one_64(p_orientation, hash);
+ bool fill = (p_alignment == HORIZONTAL_ALIGNMENT_FILL);
+ ShapedTextKey key = ShapedTextKey(p_text, p_font_size, fill ? p_width : 0.0, fill ? p_jst_flags : TextServer::JUSTIFICATION_NONE, TextServer::BREAK_NONE, p_direction, p_orientation);
Ref<TextLine> buffer;
- if (cache.has(hash)) {
- buffer = cache.get(hash);
+ if (cache.has(key)) {
+ buffer = cache.get(key);
} else {
buffer.instantiate();
buffer->set_direction(p_direction);
buffer->set_orientation(p_orientation);
buffer->add_string(p_text, Ref<Font>(this), p_font_size);
- cache.insert(hash, buffer);
+ cache.insert(key, buffer);
}
Vector2 ofs = p_pos;
@@ -366,17 +348,11 @@ void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_t
}
void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, const Color &p_modulate, BitField<TextServer::LineBreakFlag> p_brk_flags, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const {
- uint64_t hash = p_text.hash64();
- hash = hash_djb2_one_64(p_font_size, hash);
- hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash);
- hash = hash_djb2_one_64(p_brk_flags.operator int64_t(), hash);
- hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash);
- hash = hash_djb2_one_64(p_direction, hash);
- hash = hash_djb2_one_64(p_orientation, hash);
+ ShapedTextKey key = ShapedTextKey(p_text, p_font_size, p_width, p_jst_flags, p_brk_flags, p_direction, p_orientation);
Ref<TextParagraph> lines_buffer;
- if (cache_wrap.has(hash)) {
- lines_buffer = cache_wrap.get(hash);
+ if (cache_wrap.has(key)) {
+ lines_buffer = cache_wrap.get(key);
} else {
lines_buffer.instantiate();
lines_buffer->set_direction(p_direction);
@@ -385,7 +361,7 @@ void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const S
lines_buffer->set_width(p_width);
lines_buffer->set_break_flags(p_brk_flags);
lines_buffer->set_justification_flags(p_jst_flags);
- cache_wrap.insert(hash, lines_buffer);
+ cache_wrap.insert(key, lines_buffer);
}
Vector2 ofs = p_pos;
@@ -402,24 +378,18 @@ void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const S
}
void Font::draw_string_outline(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_size, const Color &p_modulate, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const {
- uint64_t hash = p_text.hash64();
- hash = hash_djb2_one_64(p_font_size, hash);
- if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) {
- hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash);
- hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash);
- }
- hash = hash_djb2_one_64(p_direction, hash);
- hash = hash_djb2_one_64(p_orientation, hash);
+ bool fill = (p_alignment == HORIZONTAL_ALIGNMENT_FILL);
+ ShapedTextKey key = ShapedTextKey(p_text, p_font_size, fill ? p_width : 0.0, fill ? p_jst_flags : TextServer::JUSTIFICATION_NONE, TextServer::BREAK_NONE, p_direction, p_orientation);
Ref<TextLine> buffer;
- if (cache.has(hash)) {
- buffer = cache.get(hash);
+ if (cache.has(key)) {
+ buffer = cache.get(key);
} else {
buffer.instantiate();
buffer->set_direction(p_direction);
buffer->set_orientation(p_orientation);
buffer->add_string(p_text, Ref<Font>(this), p_font_size);
- cache.insert(hash, buffer);
+ cache.insert(key, buffer);
}
Vector2 ofs = p_pos;
@@ -439,17 +409,11 @@ void Font::draw_string_outline(RID p_canvas_item, const Point2 &p_pos, const Str
}
void Font::draw_multiline_string_outline(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, int p_size, const Color &p_modulate, BitField<TextServer::LineBreakFlag> p_brk_flags, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const {
- uint64_t hash = p_text.hash64();
- hash = hash_djb2_one_64(p_font_size, hash);
- hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash);
- hash = hash_djb2_one_64(p_brk_flags.operator int64_t(), hash);
- hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash);
- hash = hash_djb2_one_64(p_direction, hash);
- hash = hash_djb2_one_64(p_orientation, hash);
+ ShapedTextKey key = ShapedTextKey(p_text, p_font_size, p_width, p_jst_flags, p_brk_flags, p_direction, p_orientation);
Ref<TextParagraph> lines_buffer;
- if (cache_wrap.has(hash)) {
- lines_buffer = cache_wrap.get(hash);
+ if (cache_wrap.has(key)) {
+ lines_buffer = cache_wrap.get(key);
} else {
lines_buffer.instantiate();
lines_buffer->set_direction(p_direction);
@@ -458,7 +422,7 @@ void Font::draw_multiline_string_outline(RID p_canvas_item, const Point2 &p_pos,
lines_buffer->set_width(p_width);
lines_buffer->set_break_flags(p_brk_flags);
lines_buffer->set_justification_flags(p_jst_flags);
- cache_wrap.insert(hash, lines_buffer);
+ cache_wrap.insert(key, lines_buffer);
}
Vector2 ofs = p_pos;
diff --git a/scene/resources/font.h b/scene/resources/font.h
index e9f7507652..44198a3111 100644
--- a/scene/resources/font.h
+++ b/scene/resources/font.h
@@ -47,9 +47,44 @@ class TextParagraph;
class Font : public Resource {
GDCLASS(Font, Resource);
+ struct ShapedTextKey {
+ String text;
+ int font_size = 14;
+ float width = 0.f;
+ BitField<TextServer::JustificationFlag> jst_flags = TextServer::JUSTIFICATION_NONE;
+ BitField<TextServer::LineBreakFlag> brk_flags = TextServer::BREAK_MANDATORY;
+ TextServer::Direction direction = TextServer::DIRECTION_AUTO;
+ TextServer::Orientation orientation = TextServer::ORIENTATION_HORIZONTAL;
+
+ bool operator==(const ShapedTextKey &p_b) const {
+ return (font_size == p_b.font_size) && (width == p_b.width) && (jst_flags == p_b.jst_flags) && (brk_flags == p_b.brk_flags) && (direction == p_b.direction) && (orientation == p_b.orientation) && (text == p_b.text);
+ }
+
+ ShapedTextKey() {}
+ ShapedTextKey(const String &p_text, int p_font_size, float p_width, BitField<TextServer::JustificationFlag> p_jst_flags, BitField<TextServer::LineBreakFlag> p_brk_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
+ text = p_text;
+ font_size = p_font_size;
+ width = p_width;
+ jst_flags = p_jst_flags;
+ brk_flags = p_brk_flags;
+ direction = p_direction;
+ orientation = p_orientation;
+ }
+ };
+
+ struct ShapedTextKeyHasher {
+ _FORCE_INLINE_ static uint32_t hash(const ShapedTextKey &p_a) {
+ uint32_t hash = p_a.text.hash();
+ hash = hash_murmur3_one_32(p_a.font_size, hash);
+ hash = hash_murmur3_one_float(p_a.width, hash);
+ hash = hash_murmur3_one_32(p_a.brk_flags | (p_a.jst_flags << 6) | (p_a.direction << 12) | (p_a.orientation << 15), hash);
+ return hash_fmix32(hash);
+ }
+ };
+
// Shaped string cache.
- mutable LRUCache<uint64_t, Ref<TextLine>> cache;
- mutable LRUCache<uint64_t, Ref<TextParagraph>> cache_wrap;
+ mutable LRUCache<ShapedTextKey, Ref<TextLine>, ShapedTextKeyHasher> cache;
+ mutable LRUCache<ShapedTextKey, Ref<TextParagraph>, ShapedTextKeyHasher> cache_wrap;
protected:
// Output.
diff --git a/scene/resources/immediate_mesh.cpp b/scene/resources/immediate_mesh.cpp
index 90cc3ea5f4..2defe729cc 100644
--- a/scene/resources/immediate_mesh.cpp
+++ b/scene/resources/immediate_mesh.cpp
@@ -410,5 +410,6 @@ ImmediateMesh::ImmediateMesh() {
mesh = RS::get_singleton()->mesh_create();
}
ImmediateMesh::~ImmediateMesh() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(mesh);
}
diff --git a/scene/resources/importer_mesh.cpp b/scene/resources/importer_mesh.cpp
index d1278f9340..b5e02b2f76 100644
--- a/scene/resources/importer_mesh.cpp
+++ b/scene/resources/importer_mesh.cpp
@@ -30,6 +30,7 @@
#include "importer_mesh.h"
+#include "core/io/marshalls.h"
#include "core/math/random_pcg.h"
#include "core/math/static_raycaster.h"
#include "scene/resources/surface_tool.h"
@@ -424,9 +425,8 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli
normal_weights[j] = 2.0; // Give some weight to normal preservation, may be worth exposing as an import setting
}
- const float max_mesh_error = FLT_MAX; // We don't want to limit by error, just by index target
- float scale = SurfaceTool::simplify_scale_func((const float *)merged_vertices_ptr, merged_vertex_count, sizeof(Vector3));
- float mesh_error = 0.0f;
+ Vector<float> merged_vertices_f32 = vector3_to_float32_array(merged_vertices_ptr, merged_vertex_count);
+ float scale = SurfaceTool::simplify_scale_func(merged_vertices_f32.ptr(), merged_vertex_count, sizeof(float) * 3);
unsigned int index_target = 12; // Start with the smallest target, 4 triangles
unsigned int last_index_count = 0;
@@ -446,11 +446,25 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli
raycaster->commit();
}
+ const float max_mesh_error = FLT_MAX; // We don't want to limit by error, just by index target
+ float mesh_error = 0.0f;
+
while (index_target < index_count) {
PackedInt32Array new_indices;
new_indices.resize(index_count);
- size_t new_index_count = SurfaceTool::simplify_with_attrib_func((unsigned int *)new_indices.ptrw(), (const uint32_t *)merged_indices_ptr, index_count, (const float *)merged_vertices_ptr, merged_vertex_count, sizeof(Vector3), index_target, max_mesh_error, &mesh_error, (float *)merged_normals.ptr(), normal_weights.ptr(), 3);
+ Vector<float> merged_normals_f32 = vector3_to_float32_array(merged_normals.ptr(), merged_normals.size());
+
+ size_t new_index_count = SurfaceTool::simplify_with_attrib_func(
+ (unsigned int *)new_indices.ptrw(),
+ (const uint32_t *)merged_indices_ptr, index_count,
+ merged_vertices_f32.ptr(), merged_vertex_count,
+ sizeof(float) * 3, // Vertex stride
+ index_target,
+ max_mesh_error,
+ &mesh_error,
+ merged_normals_f32.ptr(),
+ normal_weights.ptr(), 3);
if (new_index_count < last_index_count * 1.5f) {
index_target = index_target * 1.5f;
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index a16d2c2072..44ce90cc4a 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -141,6 +141,7 @@ Material::Material() {
}
Material::~Material() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RenderingServer::get_singleton()->free(material);
}
@@ -3005,6 +3006,7 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) :
}
BaseMaterial3D::~BaseMaterial3D() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
MutexLock lock(material_mutex);
if (shader_map.has(current_key)) {
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index af770ddede..a610290a11 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -2117,6 +2117,7 @@ ArrayMesh::ArrayMesh() {
ArrayMesh::~ArrayMesh() {
if (mesh.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RenderingServer::get_singleton()->free(mesh);
}
}
@@ -2132,5 +2133,6 @@ PlaceholderMesh::PlaceholderMesh() {
}
PlaceholderMesh::~PlaceholderMesh() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(rid);
}
diff --git a/scene/resources/multimesh.cpp b/scene/resources/multimesh.cpp
index 8afb0563b2..2ea357d814 100644
--- a/scene/resources/multimesh.cpp
+++ b/scene/resources/multimesh.cpp
@@ -365,5 +365,6 @@ MultiMesh::MultiMesh() {
}
MultiMesh::~MultiMesh() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RenderingServer::get_singleton()->free(multimesh);
}
diff --git a/scene/resources/particle_process_material.cpp b/scene/resources/particle_process_material.cpp
index b77430c154..a07a8ac7cd 100644
--- a/scene/resources/particle_process_material.cpp
+++ b/scene/resources/particle_process_material.cpp
@@ -1894,6 +1894,7 @@ ParticleProcessMaterial::ParticleProcessMaterial() :
}
ParticleProcessMaterial::~ParticleProcessMaterial() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
MutexLock lock(material_mutex);
if (shader_map.has(current_key)) {
diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp
index 54d3676c15..aeb62d6709 100644
--- a/scene/resources/primitive_meshes.cpp
+++ b/scene/resources/primitive_meshes.cpp
@@ -340,6 +340,7 @@ PrimitiveMesh::PrimitiveMesh() {
}
PrimitiveMesh::~PrimitiveMesh() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RenderingServer::get_singleton()->free(mesh);
}
diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp
index 48ec084b02..c6621ce482 100644
--- a/scene/resources/shader.cpp
+++ b/scene/resources/shader.cpp
@@ -208,6 +208,7 @@ Shader::Shader() {
}
Shader::~Shader() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RenderingServer::get_singleton()->free(shader);
}
diff --git a/scene/resources/sky.cpp b/scene/resources/sky.cpp
index 735134e27b..32ce0abf7a 100644
--- a/scene/resources/sky.cpp
+++ b/scene/resources/sky.cpp
@@ -106,5 +106,6 @@ Sky::Sky() {
}
Sky::~Sky() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(sky);
}
diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp
index 94967352c8..185cbdc6bf 100644
--- a/scene/resources/surface_tool.cpp
+++ b/scene/resources/surface_tool.cpp
@@ -46,7 +46,7 @@ void SurfaceTool::strip_mesh_arrays(PackedVector3Array &r_vertices, PackedInt32A
Vector<uint32_t> remap;
remap.resize(r_vertices.size());
- uint32_t new_vertex_count = generate_remap_func(remap.ptrw(), (unsigned int *)r_indices.ptr(), r_indices.size(), (float *)r_vertices.ptr(), r_vertices.size(), sizeof(Vector3));
+ uint32_t new_vertex_count = generate_remap_func(remap.ptrw(), (unsigned int *)r_indices.ptr(), r_indices.size(), r_vertices.ptr(), r_vertices.size(), sizeof(Vector3));
remap_vertex_func(r_vertices.ptrw(), r_vertices.ptr(), r_vertices.size(), sizeof(Vector3), remap.ptr());
r_vertices.resize(new_vertex_count);
remap_index_func((unsigned int *)r_indices.ptrw(), (unsigned int *)r_indices.ptr(), r_indices.size(), remap.ptr());
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 18915e294e..0685ff0992 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -324,6 +324,7 @@ ImageTexture::ImageTexture() {}
ImageTexture::~ImageTexture() {
if (texture.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RenderingServer::get_singleton()->free(texture);
}
}
@@ -630,6 +631,7 @@ PortableCompressedTexture2D::PortableCompressedTexture2D() {}
PortableCompressedTexture2D::~PortableCompressedTexture2D() {
if (texture.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RenderingServer::get_singleton()->free(texture);
}
}
@@ -1041,6 +1043,7 @@ CompressedTexture2D::CompressedTexture2D() {}
CompressedTexture2D::~CompressedTexture2D() {
if (texture.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(texture);
}
}
@@ -1225,6 +1228,7 @@ ImageTexture3D::ImageTexture3D() {
ImageTexture3D::~ImageTexture3D() {
if (texture.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(texture);
}
}
@@ -1386,6 +1390,7 @@ CompressedTexture3D::CompressedTexture3D() {}
CompressedTexture3D::~CompressedTexture3D() {
if (texture.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(texture);
}
}
@@ -1911,6 +1916,7 @@ CurveTexture::CurveTexture() {}
CurveTexture::~CurveTexture() {
if (_texture.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(_texture);
}
}
@@ -2109,6 +2115,7 @@ CurveXYZTexture::CurveXYZTexture() {}
CurveXYZTexture::~CurveXYZTexture() {
if (_texture.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(_texture);
}
}
@@ -2121,6 +2128,7 @@ GradientTexture1D::GradientTexture1D() {
GradientTexture1D::~GradientTexture1D() {
if (texture.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(texture);
}
}
@@ -2263,6 +2271,7 @@ GradientTexture2D::GradientTexture2D() {
GradientTexture2D::~GradientTexture2D() {
if (texture.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(texture);
}
}
@@ -2521,6 +2530,7 @@ void ProxyTexture::set_base(const Ref<Texture2D> &p_texture) {
base = p_texture;
if (base.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
if (proxy_ph.is_valid()) {
RS::get_singleton()->texture_proxy_update(proxy, base->get_rid());
RS::get_singleton()->free(proxy_ph);
@@ -2571,6 +2581,7 @@ ProxyTexture::ProxyTexture() {
}
ProxyTexture::~ProxyTexture() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
if (proxy_ph.is_valid()) {
RS::get_singleton()->free(proxy_ph);
}
@@ -2828,6 +2839,7 @@ AnimatedTexture::AnimatedTexture() {
}
AnimatedTexture::~AnimatedTexture() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(proxy);
RS::get_singleton()->free(proxy_ph);
}
@@ -3031,6 +3043,7 @@ ImageTextureLayered::ImageTextureLayered(LayeredType p_layered_type) {
ImageTextureLayered::~ImageTextureLayered() {
if (texture.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(texture);
}
}
@@ -3198,6 +3211,7 @@ CompressedTextureLayered::CompressedTextureLayered(LayeredType p_type) {
CompressedTextureLayered::~CompressedTextureLayered() {
if (texture.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(texture);
}
}
@@ -3353,6 +3367,7 @@ CameraTexture::CameraTexture() {}
CameraTexture::~CameraTexture() {
if (_texture.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RenderingServer::get_singleton()->free(_texture);
}
}
@@ -3386,7 +3401,7 @@ RID PlaceholderTexture2D::get_rid() const {
void PlaceholderTexture2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_size", "size"), &PlaceholderTexture2D::set_size);
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size", PROPERTY_HINT_NONE, "suffix:px"), "set_size", "get_size");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size", PROPERTY_HINT_NONE, "suffix:px"), "set_size", "get_size");
}
PlaceholderTexture2D::PlaceholderTexture2D() {
@@ -3394,6 +3409,7 @@ PlaceholderTexture2D::PlaceholderTexture2D() {
}
PlaceholderTexture2D::~PlaceholderTexture2D() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(rid);
}
@@ -3441,6 +3457,7 @@ PlaceholderTexture3D::PlaceholderTexture3D() {
rid = RS::get_singleton()->texture_3d_placeholder_create();
}
PlaceholderTexture3D::~PlaceholderTexture3D() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(rid);
}
@@ -3499,5 +3516,6 @@ PlaceholderTextureLayered::PlaceholderTextureLayered(LayeredType p_type) {
rid = RS::get_singleton()->texture_2d_layered_placeholder_create(RS::TextureLayeredType(layered_type));
}
PlaceholderTextureLayered::~PlaceholderTextureLayered() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(rid);
}
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index 6b8f8097a8..fc2366a78c 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -2650,6 +2650,10 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "camera_direction_world", "CAMERA_DIRECTION_WORLD" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "camera_visible_layers", "CAMERA_VISIBLE_LAYERS" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "node_position_view", "NODE_POSITION_VIEW" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "custom0", "CUSTOM0" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "custom1", "CUSTOM1" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "custom2", "CUSTOM2" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "custom3", "CUSTOM3" },
// Node3D, Fragment
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" },
diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp
index 75deb1e60b..85ce47f06c 100644
--- a/scene/resources/world_2d.cpp
+++ b/scene/resources/world_2d.cpp
@@ -89,6 +89,9 @@ World2D::World2D() {
}
World2D::~World2D() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ ERR_FAIL_NULL(PhysicsServer2D::get_singleton());
+ ERR_FAIL_NULL(NavigationServer2D::get_singleton());
RenderingServer::get_singleton()->free(canvas);
PhysicsServer2D::get_singleton()->free(space);
NavigationServer2D::get_singleton()->free(navigation_map);
diff --git a/scene/resources/world_3d.cpp b/scene/resources/world_3d.cpp
index ae8c9a182f..8808da95d7 100644
--- a/scene/resources/world_3d.cpp
+++ b/scene/resources/world_3d.cpp
@@ -157,6 +157,9 @@ World3D::World3D() {
}
World3D::~World3D() {
+ ERR_FAIL_NULL(PhysicsServer3D::get_singleton());
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ ERR_FAIL_NULL(NavigationServer3D::get_singleton());
PhysicsServer3D::get_singleton()->free(space);
RenderingServer::get_singleton()->free(scenario);
NavigationServer3D::get_singleton()->free(navigation_map);
diff --git a/servers/camera/camera_feed.cpp b/servers/camera/camera_feed.cpp
index 624c06ecf1..e5b160ae27 100644
--- a/servers/camera/camera_feed.cpp
+++ b/servers/camera/camera_feed.cpp
@@ -165,6 +165,7 @@ CameraFeed::CameraFeed(String p_name, FeedPosition p_position) {
CameraFeed::~CameraFeed() {
// Free our textures
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RenderingServer::get_singleton()->free(texture[CameraServer::FEED_Y_IMAGE]);
RenderingServer::get_singleton()->free(texture[CameraServer::FEED_CBCR_IMAGE]);
}
diff --git a/servers/physics_3d/godot_collision_solver_3d.cpp b/servers/physics_3d/godot_collision_solver_3d.cpp
index ca76a819ec..47f767425b 100644
--- a/servers/physics_3d/godot_collision_solver_3d.cpp
+++ b/servers/physics_3d/godot_collision_solver_3d.cpp
@@ -513,10 +513,6 @@ bool GodotCollisionSolver3D::solve_distance_world_boundary(const GodotShape3D *p
}
bool GodotCollisionSolver3D::solve_distance(const GodotShape3D *p_shape_A, const Transform3D &p_transform_A, const GodotShape3D *p_shape_B, const Transform3D &p_transform_B, Vector3 &r_point_A, Vector3 &r_point_B, const AABB &p_concave_hint, Vector3 *r_sep_axis) {
- if (p_shape_A->is_concave()) {
- return false;
- }
-
if (p_shape_B->get_type() == PhysicsServer3D::SHAPE_WORLD_BOUNDARY) {
Vector3 a, b;
bool col = solve_distance_world_boundary(p_shape_B, p_transform_B, p_shape_A, p_transform_A, a, b);
diff --git a/servers/physics_3d/godot_space_3d.cpp b/servers/physics_3d/godot_space_3d.cpp
index c98409e2c4..813970848a 100644
--- a/servers/physics_3d/godot_space_3d.cpp
+++ b/servers/physics_3d/godot_space_3d.cpp
@@ -992,6 +992,7 @@ bool GodotSpace3D::test_body_motion(GodotBody3D *p_body, const PhysicsServer3D::
Vector3 rel_vec = result.contact - (body->get_transform().origin + body->get_center_of_mass());
collision.collider_velocity = body->get_linear_velocity() + (body->get_angular_velocity()).cross(rel_vec);
+ collision.collider_angular_velocity = body->get_angular_velocity();
}
r_result->travel = safe * p_parameters.motion;
diff --git a/servers/physics_server_2d.cpp b/servers/physics_server_2d.cpp
index 20f23ae031..bd4512a3d5 100644
--- a/servers/physics_server_2d.cpp
+++ b/servers/physics_server_2d.cpp
@@ -772,6 +772,9 @@ void PhysicsServer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("joint_make_groove", "joint", "groove1_a", "groove2_a", "anchor_b", "body_a", "body_b"), &PhysicsServer2D::joint_make_groove, DEFVAL(RID()), DEFVAL(RID()));
ClassDB::bind_method(D_METHOD("joint_make_damped_spring", "joint", "anchor_a", "anchor_b", "body_a", "body_b"), &PhysicsServer2D::joint_make_damped_spring, DEFVAL(RID()));
+ ClassDB::bind_method(D_METHOD("pin_joint_set_param", "joint", "param", "value"), &PhysicsServer2D::pin_joint_set_param);
+ ClassDB::bind_method(D_METHOD("pin_joint_get_param", "joint", "param"), &PhysicsServer2D::pin_joint_get_param);
+
ClassDB::bind_method(D_METHOD("damped_spring_joint_set_param", "joint", "param", "value"), &PhysicsServer2D::damped_spring_joint_set_param);
ClassDB::bind_method(D_METHOD("damped_spring_joint_get_param", "joint", "param"), &PhysicsServer2D::damped_spring_joint_get_param);
diff --git a/servers/physics_server_3d.h b/servers/physics_server_3d.h
index ca1ff57c99..922baad663 100644
--- a/servers/physics_server_3d.h
+++ b/servers/physics_server_3d.h
@@ -541,6 +541,7 @@ public:
Vector3 position;
Vector3 normal;
Vector3 collider_velocity;
+ Vector3 collider_angular_velocity;
real_t depth = 0.0;
int local_shape = 0;
ObjectID collider_id;
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 a741fb4c6a..5b465fb45c 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
@@ -1162,6 +1162,7 @@ void RenderForwardClustered::_update_volumetric_fog(Ref<RenderSceneBuffersRD> p_
ERR_FAIL_COND(p_render_buffers.is_null());
Ref<RenderBufferDataForwardClustered> rb_data = p_render_buffers->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED);
+ ERR_FAIL_COND(rb_data.is_null());
ERR_FAIL_COND(!p_render_buffers->has_custom_data(RB_SCOPE_GI));
Ref<RendererRD::GI::RenderBuffersGI> rbgi = p_render_buffers->get_custom_data(RB_SCOPE_GI);
@@ -1332,7 +1333,9 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo
Ref<RenderSceneBuffersRD> rb = p_render_data->render_buffers;
Ref<RenderBufferDataForwardClustered> rb_data;
- if (rb.is_valid()) {
+ if (rb.is_valid() && rb->has_custom_data(RB_SCOPE_FORWARD_CLUSTERED)) {
+ // Our forward clustered custom data buffer will only be available when we're rendering our normal view.
+ // This will not be available when rendering reflection probes.
rb_data = rb->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED);
}
@@ -1485,7 +1488,7 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo
current_cluster_builder->bake_cluster();
}
- if (rb.is_valid()) {
+ if (rb_data.is_valid()) {
bool directional_shadows = RendererRD::LightStorage::get_singleton()->has_directional_shadows(directional_light_count);
_update_volumetric_fog(rb, p_render_data->environment, p_render_data->scene_data->cam_projection, p_render_data->scene_data->cam_transform, p_render_data->scene_data->prev_cam_transform.affine_inverse(), p_render_data->shadow_atlas, directional_light_count, directional_shadows, positional_light_count, p_render_data->voxel_gi_count, *p_render_data->fog_volumes);
}
@@ -1551,7 +1554,11 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
Ref<RenderBufferDataForwardClustered> rb_data;
if (p_render_data && p_render_data->render_buffers.is_valid()) {
rb = p_render_data->render_buffers;
- rb_data = rb->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED);
+ if (rb->has_custom_data(RB_SCOPE_FORWARD_CLUSTERED)) {
+ // Our forward clustered custom data buffer will only be available when we're rendering our normal view.
+ // This will not be available when rendering reflection probes.
+ rb_data = rb->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED);
+ }
}
static const int texture_multisamples[RS::VIEWPORT_MSAA_MAX] = { 1, 2, 4, 8 };
@@ -1570,32 +1577,32 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
}
// obtain cluster builder
- if (rb_data.is_valid()) {
- current_cluster_builder = rb_data->cluster_builder;
- } else if (light_storage->owns_reflection_probe_instance(p_render_data->reflection_probe)) {
+ if (light_storage->owns_reflection_probe_instance(p_render_data->reflection_probe)) {
current_cluster_builder = light_storage->reflection_probe_instance_get_cluster_builder(p_render_data->reflection_probe, &cluster_builder_shared);
if (p_render_data->camera_attributes.is_valid()) {
light_storage->reflection_probe_set_baked_exposure(light_storage->reflection_probe_instance_get_probe(p_render_data->reflection_probe), RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes));
}
- } else {
- ERR_PRINT("No render buffer nor reflection atlas, bug"); //should never happen, will crash
- current_cluster_builder = nullptr;
- }
+ } else if (rb_data.is_valid()) {
+ current_cluster_builder = rb_data->cluster_builder;
- p_render_data->voxel_gi_count = 0;
+ p_render_data->voxel_gi_count = 0;
- if (rb.is_valid()) {
- if (rb->has_custom_data(RB_SCOPE_SDFGI)) {
- Ref<RendererRD::GI::SDFGI> sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI);
- if (sdfgi.is_valid()) {
- sdfgi->update_cascades();
- sdfgi->pre_process_gi(p_render_data->scene_data->cam_transform, p_render_data);
- sdfgi->update_light();
+ if (rb.is_valid()) {
+ if (rb->has_custom_data(RB_SCOPE_SDFGI)) {
+ Ref<RendererRD::GI::SDFGI> sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI);
+ if (sdfgi.is_valid()) {
+ sdfgi->update_cascades();
+ sdfgi->pre_process_gi(p_render_data->scene_data->cam_transform, p_render_data);
+ sdfgi->update_light();
+ }
}
- }
- gi.setup_voxel_gi_instances(p_render_data, p_render_data->render_buffers, p_render_data->scene_data->cam_transform, *p_render_data->voxel_gi_instances, p_render_data->voxel_gi_count);
+ gi.setup_voxel_gi_instances(p_render_data, p_render_data->render_buffers, p_render_data->scene_data->cam_transform, *p_render_data->voxel_gi_instances, p_render_data->voxel_gi_count);
+ }
+ } else {
+ ERR_PRINT("No render buffer nor reflection atlas, bug"); //should never happen, will crash
+ current_cluster_builder = nullptr;
}
if (current_cluster_builder != nullptr) {
@@ -2054,7 +2061,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
RD::get_singleton()->draw_command_begin_label("Resolve");
- if (rb.is_valid() && rb->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) {
+ if (rb_data.is_valid() && rb->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) {
for (uint32_t v = 0; v < rb->get_view_count(); v++) {
RD::get_singleton()->texture_resolve_multisample(rb_data->get_color_msaa(v), rb->get_internal_texture(v));
resolve_effects->resolve_depth(rb_data->get_depth_msaa(v), rb->get_depth_texture(v), rb->get_internal_size(), texture_multisamples[rb->get_msaa_3d()]);
@@ -2073,12 +2080,12 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
}
RD::get_singleton()->draw_command_end_label();
- if (rb.is_valid() && taa && rb->get_use_taa()) {
+ if (rb_data.is_valid() && taa && rb->get_use_taa()) {
RENDER_TIMESTAMP("TAA")
taa->process(rb, _render_buffers_get_color_format(), p_render_data->scene_data->z_near, p_render_data->scene_data->z_far);
}
- if (rb.is_valid()) {
+ if (rb_data.is_valid()) {
_debug_draw_cluster(rb);
RENDER_TIMESTAMP("Tonemap");
@@ -2086,7 +2093,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
_render_buffers_post_process_and_tonemap(p_render_data);
}
- if (rb.is_valid()) {
+ if (rb_data.is_valid()) {
_render_buffers_debug_draw(rb, p_render_data->shadow_atlas, p_render_data->occluder_debug_tex);
if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_SDFGI && rb->has_custom_data(RB_SCOPE_SDFGI)) {
@@ -2876,7 +2883,11 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend
Ref<RenderBufferDataForwardClustered> rb_data;
if (p_render_data && p_render_data->render_buffers.is_valid()) {
rb = p_render_data->render_buffers;
- rb_data = rb->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED);
+ if (rb->has_custom_data(RB_SCOPE_FORWARD_CLUSTERED)) {
+ // Our forward clustered custom data buffer will only be available when we're rendering our normal view.
+ // This will not be available when rendering reflection probes.
+ rb_data = rb->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED);
+ }
}
//default render buffer and scene state uniform set
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 7f567bcc2e..53bcb1c038 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
@@ -362,6 +362,8 @@ RID RenderForwardMobile::_setup_render_pass_uniform_set(RenderListType p_render_
if (p_render_data && p_render_data->render_buffers.is_valid()) {
rb = p_render_data->render_buffers;
if (rb->has_custom_data(RB_SCOPE_MOBILE)) {
+ // Our forward mobile custom data buffer will only be available when we're rendering our normal view.
+ // This will not be available when rendering reflection probes.
rb_data = rb->get_custom_data(RB_SCOPE_MOBILE);
}
}
@@ -643,6 +645,8 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
if (p_render_data->render_buffers.is_valid()) {
rb = p_render_data->render_buffers;
if (rb->has_custom_data(RB_SCOPE_MOBILE)) {
+ // Our forward mobile custom data buffer will only be available when we're rendering our normal view.
+ // This will not be available when rendering reflection probes.
rb_data = rb->get_custom_data(RB_SCOPE_MOBILE);
}
}
diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp
index fd7b62cd6c..12ba29a0b8 100644
--- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp
@@ -202,6 +202,7 @@ void RenderSceneBuffersRD::configure(RID p_render_target, const Size2i p_interna
vrs_texture = create_texture(RB_SCOPE_VRS, RB_TEXTURE, RD::DATA_FORMAT_R8_UINT, usage_bits, RD::TEXTURE_SAMPLES_1, vrs->get_vrs_texture_size(internal_size));
}
+ // (re-)configure any named buffers
for (KeyValue<StringName, Ref<RenderBufferCustomDataRD>> &E : data_buffers) {
E.value->configure(this);
}
@@ -220,6 +221,14 @@ void RenderSceneBuffersRD::configure_for_reflections(const Size2i p_reflection_s
use_taa = false;
use_debanding = false;
view_count = 1;
+
+ // cleanout any old buffers we had.
+ cleanup();
+
+ // (re-)configure any named buffers
+ for (KeyValue<StringName, Ref<RenderBufferCustomDataRD>> &E : data_buffers) {
+ E.value->configure(this);
+ }
}
void RenderSceneBuffersRD::set_fsr_sharpness(float p_fsr_sharpness) {
@@ -498,10 +507,6 @@ void RenderSceneBuffersRD::set_custom_data(const StringName &p_name, Ref<RenderB
}
Ref<RenderBufferCustomDataRD> RenderSceneBuffersRD::get_custom_data(const StringName &p_name) const {
- if (!data_buffers.has(p_name)) {
- print_line("test");
- }
-
ERR_FAIL_COND_V(!data_buffers.has(p_name), Ref<RenderBufferCustomDataRD>());
Ref<RenderBufferCustomDataRD> ret = data_buffers[p_name];
diff --git a/servers/rendering/renderer_scene_occlusion_cull.cpp b/servers/rendering/renderer_scene_occlusion_cull.cpp
index e1ca5a7103..627e6941cf 100644
--- a/servers/rendering/renderer_scene_occlusion_cull.cpp
+++ b/servers/rendering/renderer_scene_occlusion_cull.cpp
@@ -60,6 +60,8 @@ void RendererSceneOcclusionCull::HZBuffer::clear() {
if (debug_image.is_valid()) {
debug_image.unref();
}
+
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(debug_texture);
}
diff --git a/servers/xr/xr_interface.cpp b/servers/xr/xr_interface.cpp
index ec4ae98397..b437ed1512 100644
--- a/servers/xr/xr_interface.cpp
+++ b/servers/xr/xr_interface.cpp
@@ -123,6 +123,7 @@ XRInterface::XRInterface() {}
XRInterface::~XRInterface() {
if (vrs.vrs_texture.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free(vrs.vrs_texture);
vrs.vrs_texture = RID();
}
diff --git a/tests/core/object/test_object.h b/tests/core/object/test_object.h
index f5c5de7fdf..5feb3a4054 100644
--- a/tests/core/object/test_object.h
+++ b/tests/core/object/test_object.h
@@ -281,6 +281,84 @@ TEST_CASE("[Object] Absent name getter") {
actual_value == Variant(),
"The returned value should equal nil variant.");
}
+
+TEST_CASE("[Object] Signals") {
+ Object object;
+
+ CHECK_FALSE(object.has_signal("my_custom_signal"));
+
+ List<MethodInfo> signals_before;
+ object.get_signal_list(&signals_before);
+
+ object.add_user_signal(MethodInfo("my_custom_signal"));
+
+ CHECK(object.has_signal("my_custom_signal"));
+
+ List<MethodInfo> signals_after;
+ object.get_signal_list(&signals_after);
+
+ // Should be one more signal.
+ CHECK_EQ(signals_before.size() + 1, signals_after.size());
+
+ SUBCASE("Adding the same user signal again should not have any effect") {
+ CHECK(object.has_signal("my_custom_signal"));
+ ERR_PRINT_OFF;
+ object.add_user_signal(MethodInfo("my_custom_signal"));
+ ERR_PRINT_ON;
+ CHECK(object.has_signal("my_custom_signal"));
+
+ List<MethodInfo> signals_after_existing_added;
+ object.get_signal_list(&signals_after_existing_added);
+
+ CHECK_EQ(signals_after.size(), signals_after_existing_added.size());
+ }
+
+ SUBCASE("Trying to add a preexisting signal should not have any effect") {
+ CHECK(object.has_signal("script_changed"));
+ ERR_PRINT_OFF;
+ object.add_user_signal(MethodInfo("script_changed"));
+ ERR_PRINT_ON;
+ CHECK(object.has_signal("script_changed"));
+
+ List<MethodInfo> signals_after_existing_added;
+ object.get_signal_list(&signals_after_existing_added);
+
+ CHECK_EQ(signals_after.size(), signals_after_existing_added.size());
+ }
+
+ SUBCASE("Adding an empty signal should not have any effect") {
+ CHECK_FALSE(object.has_signal(""));
+ ERR_PRINT_OFF;
+ object.add_user_signal(MethodInfo(""));
+ ERR_PRINT_ON;
+ CHECK_FALSE(object.has_signal(""));
+
+ List<MethodInfo> signals_after_empty_added;
+ object.get_signal_list(&signals_after_empty_added);
+
+ CHECK_EQ(signals_after.size(), signals_after_empty_added.size());
+ }
+
+ SUBCASE("Emitting a non existing signal will return an error") {
+ Error err = object.emit_signal("some_signal");
+ CHECK(err == ERR_UNAVAILABLE);
+ }
+
+ SUBCASE("Emitting an existing signal should call the connected method") {
+ Array empty_signal_args;
+ empty_signal_args.push_back(Array());
+
+ SIGNAL_WATCH(&object, "my_custom_signal");
+ SIGNAL_CHECK_FALSE("my_custom_signal");
+
+ Error err = object.emit_signal("my_custom_signal");
+ CHECK(err == OK);
+
+ SIGNAL_CHECK("my_custom_signal", empty_signal_args);
+ SIGNAL_UNWATCH(&object, "my_custom_signal");
+ }
+}
+
} // namespace TestObject
#endif // TEST_OBJECT_H
diff --git a/tests/scene/test_node.h b/tests/scene/test_node.h
new file mode 100644
index 0000000000..68ec41f07a
--- /dev/null
+++ b/tests/scene/test_node.h
@@ -0,0 +1,415 @@
+/*************************************************************************/
+/* test_node.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TEST_NODE_H
+#define TEST_NODE_H
+
+#include "scene/main/node.h"
+
+#include "tests/test_macros.h"
+
+namespace TestNode {
+
+TEST_CASE("[SceneTree][Node] Simple Add/Remove/Move/Find") {
+ Node *node = memnew(Node);
+
+ // Check initial scene tree setup.
+ CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 0);
+ CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 1);
+
+ // Check initial node setup.
+ CHECK(node->get_name() == StringName());
+ CHECK_FALSE(node->is_inside_tree());
+ CHECK_EQ(node->get_parent(), nullptr);
+ ERR_PRINT_OFF;
+ CHECK(node->get_path().is_empty());
+ ERR_PRINT_ON;
+ CHECK_EQ(node->get_child_count(), 0);
+
+ SceneTree::get_singleton()->get_root()->add_child(node);
+
+ CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 1);
+ CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 2);
+
+ CHECK(node->get_name() != StringName());
+ CHECK(node->is_inside_tree());
+ CHECK_EQ(SceneTree::get_singleton()->get_root(), node->get_parent());
+ CHECK_FALSE(node->get_path().is_empty());
+ CHECK_EQ(node->get_child_count(), 0);
+
+ SUBCASE("Node should be accessible as first child") {
+ Node *child = SceneTree::get_singleton()->get_root()->get_child(0);
+ CHECK_EQ(child, node);
+ }
+
+ SUBCASE("Node should be accessible via the node path") {
+ Node *child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(node->get_path());
+ CHECK_EQ(child_by_path, node);
+
+ child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(NodePath("Node"));
+ CHECK_EQ(child_by_path, nullptr);
+
+ child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(NodePath("/root/Node"));
+ CHECK_EQ(child_by_path, nullptr);
+
+ node->set_name("Node");
+
+ child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(node->get_path());
+ CHECK_EQ(child_by_path, node);
+
+ child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(NodePath("Node"));
+ CHECK_EQ(child_by_path, node);
+
+ child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(NodePath("/root/Node"));
+ CHECK_EQ(child_by_path, node);
+ }
+
+ SUBCASE("Node should be accessible via group") {
+ List<Node *> nodes;
+ SceneTree::get_singleton()->get_nodes_in_group("nodes", &nodes);
+ CHECK(nodes.is_empty());
+
+ node->add_to_group("nodes");
+
+ SceneTree::get_singleton()->get_nodes_in_group("nodes", &nodes);
+ CHECK_EQ(nodes.size(), 1);
+ List<Node *>::Element *E = nodes.front();
+ CHECK_EQ(E->get(), node);
+ }
+
+ SUBCASE("Node should be possible to find") {
+ Node *child = SceneTree::get_singleton()->get_root()->find_child("Node", true, false);
+ CHECK_EQ(child, nullptr);
+
+ node->set_name("Node");
+
+ child = SceneTree::get_singleton()->get_root()->find_child("Node", true, false);
+ CHECK_EQ(child, node);
+ }
+
+ SUBCASE("Node should be possible to remove") {
+ SceneTree::get_singleton()->get_root()->remove_child(node);
+
+ CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 0);
+ CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 1);
+
+ CHECK_FALSE(node->is_inside_tree());
+ CHECK_EQ(node->get_parent(), nullptr);
+ ERR_PRINT_OFF;
+ CHECK(node->get_path().is_empty());
+ ERR_PRINT_ON;
+ }
+
+ SUBCASE("Node should be possible to move") {
+ SceneTree::get_singleton()->get_root()->move_child(node, 0);
+
+ Node *child = SceneTree::get_singleton()->get_root()->get_child(0);
+ CHECK_EQ(child, node);
+ CHECK(node->is_inside_tree());
+ }
+
+ memdelete(node);
+}
+
+TEST_CASE("[SceneTree][Node] Nested Add/Remove/Move/Find") {
+ Node *node1 = memnew(Node);
+ Node *node2 = memnew(Node);
+ Node *node1_1 = memnew(Node);
+
+ SceneTree::get_singleton()->get_root()->add_child(node1);
+ SceneTree::get_singleton()->get_root()->add_child(node2);
+
+ node1->add_child(node1_1);
+
+ CHECK(node1_1->is_inside_tree());
+ CHECK_EQ(node1_1->get_parent(), node1);
+ CHECK_EQ(node1->get_child_count(), 1);
+
+ CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 2);
+ CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 4);
+
+ SUBCASE("Nodes should be accessible via get_child(..)") {
+ Node *child1 = SceneTree::get_singleton()->get_root()->get_child(0);
+ CHECK_EQ(child1, node1);
+
+ Node *child2 = SceneTree::get_singleton()->get_root()->get_child(1);
+ CHECK_EQ(child2, node2);
+
+ Node *child1_1 = node1->get_child(0);
+ CHECK_EQ(child1_1, node1_1);
+ }
+
+ SUBCASE("Removed nodes should also remove their children from the scene tree") {
+ // Should also remove node1_1 from the scene tree.
+ SceneTree::get_singleton()->get_root()->remove_child(node1);
+
+ CHECK_EQ(node1->get_child_count(), 1);
+
+ CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 1);
+ CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 2);
+
+ // First child should now be the second node.
+ Node *child1 = SceneTree::get_singleton()->get_root()->get_child(0);
+ CHECK_EQ(child1, node2);
+ }
+
+ SUBCASE("Removed children nodes should not affect their parent in the scene tree") {
+ node1->remove_child(node1_1);
+
+ CHECK_EQ(node1_1->get_parent(), nullptr);
+ CHECK_EQ(node1->get_child_count(), 0);
+
+ CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 3);
+ }
+
+ SUBCASE("Nodes should be in the expected order when a node is moved to the back") {
+ SceneTree::get_singleton()->get_root()->move_child(node1, 1);
+
+ Node *child1 = SceneTree::get_singleton()->get_root()->get_child(0);
+ CHECK_EQ(child1, node2);
+
+ Node *child2 = SceneTree::get_singleton()->get_root()->get_child(1);
+ CHECK_EQ(child2, node1);
+ }
+
+ SUBCASE("Nodes should be in the expected order when a node is moved to the front") {
+ SceneTree::get_singleton()->get_root()->move_child(node2, 0);
+
+ Node *child1 = SceneTree::get_singleton()->get_root()->get_child(0);
+ CHECK_EQ(child1, node2);
+
+ Node *child2 = SceneTree::get_singleton()->get_root()->get_child(1);
+ CHECK_EQ(child2, node1);
+ }
+
+ SUBCASE("Nodes should be in the expected order when reparented") {
+ CHECK_EQ(node2->get_child_count(), 0);
+
+ node1->remove_child(node1_1);
+ CHECK_EQ(node1->get_child_count(), 0);
+ CHECK_EQ(node1_1->get_parent(), nullptr);
+
+ node2->add_child(node1_1);
+ CHECK_EQ(node2->get_child_count(), 1);
+ CHECK_EQ(node1_1->get_parent(), node2);
+
+ Node *child = node2->get_child(0);
+ CHECK_EQ(child, node1_1);
+
+ CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 2);
+ CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 4);
+ }
+
+ SUBCASE("Nodes should be possible to find") {
+ Node *child = SceneTree::get_singleton()->get_root()->find_child("NestedNode", true, false);
+ CHECK_EQ(child, nullptr);
+
+ node1->set_name("Node1");
+ node2->set_name("Node2");
+ node1_1->set_name("NestedNode");
+
+ child = SceneTree::get_singleton()->get_root()->find_child("NestedNode", true, false);
+ CHECK_EQ(child, node1_1);
+
+ // First node that matches with the name is node1.
+ child = SceneTree::get_singleton()->get_root()->find_child("Node?", true, false);
+ CHECK_EQ(child, node1);
+
+ SceneTree::get_singleton()->get_root()->move_child(node2, 0);
+
+ // It should be node2, as it is now the first one in the tree.
+ child = SceneTree::get_singleton()->get_root()->find_child("Node?", true, false);
+ CHECK_EQ(child, node2);
+ }
+
+ SUBCASE("Nodes should be accessible via their node path") {
+ Node *child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(node1->get_path());
+ CHECK_EQ(child_by_path, node1);
+
+ child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(node2->get_path());
+ CHECK_EQ(child_by_path, node2);
+
+ child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(node1_1->get_path());
+ CHECK_EQ(child_by_path, node1_1);
+
+ node1->set_name("Node1");
+ node1_1->set_name("NestedNode");
+
+ child_by_path = node1->get_node_or_null(NodePath("NestedNode"));
+ CHECK_EQ(child_by_path, node1_1);
+
+ child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(NodePath("/root/Node1/NestedNode"));
+ CHECK_EQ(child_by_path, node1_1);
+
+ child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(NodePath("Node1/NestedNode"));
+ CHECK_EQ(child_by_path, node1_1);
+ }
+
+ SUBCASE("Nodes should be accessible via their groups") {
+ List<Node *> nodes;
+ SceneTree::get_singleton()->get_nodes_in_group("nodes", &nodes);
+ CHECK(nodes.is_empty());
+
+ SceneTree::get_singleton()->get_nodes_in_group("other_nodes", &nodes);
+ CHECK(nodes.is_empty());
+
+ node1->add_to_group("nodes");
+ node2->add_to_group("other_nodes");
+ node1_1->add_to_group("nodes");
+ node1_1->add_to_group("other_nodes");
+
+ SceneTree::get_singleton()->get_nodes_in_group("nodes", &nodes);
+ CHECK_EQ(nodes.size(), 2);
+
+ List<Node *>::Element *E = nodes.front();
+ CHECK_EQ(E->get(), node1);
+ E = E->next();
+ CHECK_EQ(E->get(), node1_1);
+
+ // Clear and try again with the other group.
+ nodes.clear();
+
+ SceneTree::get_singleton()->get_nodes_in_group("other_nodes", &nodes);
+ CHECK_EQ(nodes.size(), 2);
+
+ E = nodes.front();
+ CHECK_EQ(E->get(), node1_1);
+ E = E->next();
+ CHECK_EQ(E->get(), node2);
+
+ // Clear and try again with the other group and one node removed.
+ nodes.clear();
+
+ node1->remove_from_group("nodes");
+ SceneTree::get_singleton()->get_nodes_in_group("nodes", &nodes);
+ CHECK_EQ(nodes.size(), 1);
+
+ E = nodes.front();
+ CHECK_EQ(E->get(), node1_1);
+ }
+
+ memdelete(node1_1);
+ memdelete(node1);
+ memdelete(node2);
+}
+
+TEST_CASE("[Node] Processing") {
+ Node *node = memnew(Node);
+
+ SUBCASE("Processing") {
+ CHECK_FALSE(node->is_processing());
+
+ node->set_process(true);
+
+ CHECK(node->is_processing());
+
+ node->set_process(false);
+
+ CHECK_FALSE(node->is_processing());
+ }
+
+ SUBCASE("Physics processing") {
+ CHECK_FALSE(node->is_physics_processing());
+
+ node->set_physics_process(true);
+
+ CHECK(node->is_physics_processing());
+
+ node->set_physics_process(false);
+
+ CHECK_FALSE(node->is_physics_processing());
+ }
+
+ SUBCASE("Unhandled input processing") {
+ CHECK_FALSE(node->is_processing_unhandled_input());
+
+ node->set_process_unhandled_input(true);
+
+ CHECK(node->is_processing_unhandled_input());
+
+ node->set_process_unhandled_input(false);
+
+ CHECK_FALSE(node->is_processing_unhandled_input());
+ }
+
+ SUBCASE("Input processing") {
+ CHECK_FALSE(node->is_processing_input());
+
+ node->set_process_input(true);
+
+ CHECK(node->is_processing_input());
+
+ node->set_process_input(false);
+
+ CHECK_FALSE(node->is_processing_input());
+ }
+
+ SUBCASE("Unhandled key input processing") {
+ CHECK_FALSE(node->is_processing_unhandled_key_input());
+
+ node->set_process_unhandled_key_input(true);
+
+ CHECK(node->is_processing_unhandled_key_input());
+
+ node->set_process_unhandled_key_input(false);
+
+ CHECK_FALSE(node->is_processing_unhandled_key_input());
+ }
+
+ SUBCASE("Shortcut input processing") {
+ CHECK_FALSE(node->is_processing_shortcut_input());
+
+ node->set_process_shortcut_input(true);
+
+ CHECK(node->is_processing_shortcut_input());
+
+ node->set_process_shortcut_input(false);
+
+ CHECK_FALSE(node->is_processing_shortcut_input());
+ }
+
+ SUBCASE("Internal processing") {
+ CHECK_FALSE(node->is_processing_internal());
+
+ node->set_process_internal(true);
+
+ CHECK(node->is_processing_internal());
+
+ node->set_process_internal(false);
+
+ CHECK_FALSE(node->is_processing_internal());
+ }
+
+ memdelete(node);
+}
+
+} // namespace TestNode
+
+#endif // TEST_NODE_H
diff --git a/tests/scene/test_visual_shader.h b/tests/scene/test_visual_shader.h
new file mode 100644
index 0000000000..457f9ad6ac
--- /dev/null
+++ b/tests/scene/test_visual_shader.h
@@ -0,0 +1,151 @@
+/*************************************************************************/
+/* test_visual_shader.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TEST_VISUAL_SHADER_H
+#define TEST_VISUAL_SHADER_H
+
+#include "scene/resources/visual_shader.h"
+
+#include "tests/test_macros.h"
+
+namespace TestVisualArray {
+
+TEST_CASE("[SceneTree][VisualShader] Object creation and parameter") {
+ Ref<VisualShader> vs = memnew(VisualShader);
+ CHECK(vs.is_valid());
+
+ CHECK(vs->get_mode() == Shader::MODE_SPATIAL);
+
+ for (int i = 1; i < Shader::MODE_MAX; i++) {
+ vs->set_mode((Shader::Mode)i);
+ CHECK(vs->get_mode() == i);
+ }
+}
+
+TEST_CASE("[SceneTree][VisualShader] Testing VisualShaderNodes") {
+ SUBCASE("Testing Node Creation") {
+ Ref<VisualShader> vs = memnew(VisualShader);
+ CHECK(vs.is_valid());
+
+ for (int i = 0; i < VisualShader::TYPE_MAX; i++) {
+ Ref<VisualShaderNode> vsn = memnew(VisualShaderNodeInput);
+ CHECK(vsn.is_valid());
+ vs->add_node(VisualShader::Type(i), vsn, Vector2(1, 10), i + 2);
+ CHECK(vs->get_node(VisualShader::Type(i), i + 2) == vsn);
+ }
+
+ ERR_PRINT_OFF;
+
+ // Testing for Invalid entries.
+ Ref<VisualShaderNode> vsn5 = memnew(VisualShaderNodeInput);
+ Ref<VisualShaderNode> vsn6 = memnew(VisualShaderNodeInput);
+ CHECK(vsn6.is_valid());
+ CHECK(vsn5.is_valid());
+
+ vs->add_node(VisualShader::TYPE_SKY, vsn5, Vector2(1, 10), 0);
+ CHECK_FALSE(vs->get_node(VisualShader::TYPE_SKY, 0) == vsn6);
+ vs->add_node(VisualShader::TYPE_MAX, vsn6, Vector2(1, 10), 7);
+ CHECK_FALSE(vs->get_node(VisualShader::TYPE_SKY, 7) == vsn6);
+
+ ERR_PRINT_ON;
+ }
+
+ SUBCASE("Testing VisualShaderNode position getter and setter") {
+ Ref<VisualShader> vs = memnew(VisualShader);
+ CHECK(vs.is_valid());
+
+ Ref<VisualShaderNode> vsn1 = memnew(VisualShaderNodeInput);
+ CHECK(vsn1.is_valid());
+ vs->add_node(VisualShader::TYPE_COLLIDE, vsn1, Vector2(0, 0), 3);
+ CHECK(vs->get_node_position(VisualShader::TYPE_COLLIDE, 3) == Vector2(0, 0));
+ vs->set_node_position(VisualShader::TYPE_COLLIDE, 3, Vector2(1, 1));
+ CHECK(vs->get_node_position(VisualShader::TYPE_COLLIDE, 3) == Vector2(1, 1));
+
+ Ref<VisualShaderNode> vsn2 = memnew(VisualShaderNodeInput);
+ CHECK(vsn2.is_valid());
+ vs->add_node(VisualShader::TYPE_FOG, vsn2, Vector2(1, 2), 4);
+ CHECK(vs->get_node_position(VisualShader::TYPE_FOG, 4) == Vector2(1, 2));
+ vs->set_node_position(VisualShader::TYPE_FOG, 4, Vector2(2, 2));
+ CHECK(vs->get_node_position(VisualShader::TYPE_FOG, 4) == Vector2(2, 2));
+ }
+
+ SUBCASE("Testing VisualShaderNode ID") {
+ Ref<VisualShader> vs = memnew(VisualShader);
+ CHECK(vs.is_valid());
+
+ for (int i = 0; i < VisualShader::TYPE_MAX; i++) {
+ Ref<VisualShaderNode> vsn = memnew(VisualShaderNodeInput);
+ CHECK(vsn.is_valid());
+ vs->add_node(VisualShader::Type(i), vsn, Vector2(1, 10), i + 2);
+ CHECK(vs->get_valid_node_id(VisualShader::Type(i)) - 1 == i + 2);
+ }
+ }
+
+ SUBCASE("Testing remove and replace VisualShaderNode") {
+ Ref<VisualShader> vs = memnew(VisualShader);
+ CHECK(vs.is_valid());
+
+ ERR_PRINT_OFF;
+
+ for (int i = 0; i < VisualShader::TYPE_MAX; i++) {
+ Ref<VisualShaderNode> vsn = memnew(VisualShaderNodeInput);
+ CHECK(vsn.is_valid());
+ vs->add_node(VisualShader::Type(i), vsn, Vector2(1, 10), i + 2);
+ CHECK(vs->get_node(VisualShader::Type(i), i + 2) == vsn);
+ vs->remove_node(VisualShader::Type(i), i + 2);
+ CHECK_FALSE(vs->get_node(VisualShader::Type(i), i + 2) == vsn);
+ }
+
+ ERR_PRINT_ON;
+ }
+}
+
+TEST_CASE("[SceneTree][VisualShader] Testing Varyings") {
+ Ref<VisualShader> vs = memnew(VisualShader);
+
+ vs->add_varying("Test1", VisualShader::VARYING_MODE_FRAG_TO_LIGHT, VisualShader::VARYING_TYPE_TRANSFORM);
+ CHECK(vs->has_varying("Test1") == true);
+
+ vs->add_varying("Test2", VisualShader::VARYING_MODE_VERTEX_TO_FRAG_LIGHT, VisualShader::VARYING_TYPE_VECTOR_2D);
+ CHECK(vs->has_varying("Test2"));
+
+ CHECK_FALSE(vs->has_varying("Does_not_exits"));
+ ERR_PRINT_OFF;
+ vs->add_varying("Test3", VisualShader::VARYING_MODE_MAX, VisualShader::VARYING_TYPE_INT);
+ CHECK_FALSE(vs->has_varying("Test3"));
+
+ vs->add_varying("Test4", VisualShader::VARYING_MODE_FRAG_TO_LIGHT, VisualShader::VARYING_TYPE_MAX);
+ CHECK_FALSE(vs->has_varying("Test4"));
+ ERR_PRINT_ON;
+}
+
+} //namespace TestVisualArray
+
+#endif // TEST_VISUAL_SHADER_H
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index 4c4d47a7ae..3ef8e2de6f 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -92,12 +92,14 @@
#include "tests/scene/test_code_edit.h"
#include "tests/scene/test_curve.h"
#include "tests/scene/test_gradient.h"
+#include "tests/scene/test_node.h"
#include "tests/scene/test_path_2d.h"
#include "tests/scene/test_path_3d.h"
#include "tests/scene/test_primitives.h"
#include "tests/scene/test_sprite_frames.h"
#include "tests/scene/test_text_edit.h"
#include "tests/scene/test_theme.h"
+#include "tests/scene/test_visual_shader.h"
#include "tests/servers/test_text_server.h"
#include "tests/test_validate_testing.h"