summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.editorconfig10
-rw-r--r--core/config/project_settings.cpp2
-rw-r--r--core/extension/gdextension_interface.cpp164
-rw-r--r--core/extension/gdextension_interface.h69
-rw-r--r--core/variant/method_ptrcall.h44
-rw-r--r--core/variant/typed_array.h3
-rw-r--r--core/variant/variant_call.cpp6
-rw-r--r--doc/classes/AudioServer.xml10
-rw-r--r--doc/classes/CanvasTexture.xml1
-rw-r--r--doc/classes/ItemList.xml6
-rw-r--r--doc/classes/Joint2D.xml8
-rw-r--r--doc/classes/Joint3D.xml8
-rw-r--r--doc/classes/Line2D.xml13
-rw-r--r--doc/classes/ProjectSettings.xml3
-rw-r--r--doc/classes/Vector2i.xml6
-rw-r--r--doc/classes/Vector3i.xml6
-rw-r--r--doc/classes/Vector4i.xml6
-rw-r--r--drivers/gles3/shaders/canvas.glsl6
-rw-r--r--drivers/gles3/storage/material_storage.cpp1
-rw-r--r--drivers/vulkan/rendering_device_vulkan.cpp2
-rw-r--r--drivers/vulkan/vulkan_context.cpp24
-rw-r--r--drivers/vulkan/vulkan_context.h2
-rw-r--r--editor/animation_track_editor.cpp20
-rw-r--r--editor/connections_dialog.cpp48
-rw-r--r--editor/debugger/editor_debugger_node.cpp8
-rw-r--r--editor/debugger/editor_debugger_tree.cpp2
-rw-r--r--editor/debugger/editor_performance_profiler.cpp2
-rw-r--r--editor/editor_audio_buses.cpp47
-rw-r--r--editor/editor_audio_buses.h7
-rw-r--r--editor/editor_autoload_settings.cpp4
-rw-r--r--editor/editor_data.cpp4
-rw-r--r--editor/editor_inspector.cpp2
-rw-r--r--editor/editor_interface.cpp2
-rw-r--r--editor/editor_node.cpp24
-rw-r--r--editor/editor_paths.cpp2
-rw-r--r--editor/editor_plugin_settings.cpp2
-rw-r--r--editor/editor_properties.cpp4
-rw-r--r--editor/editor_properties_array_dict.cpp2
-rw-r--r--editor/editor_settings.cpp2
-rw-r--r--editor/editor_settings_dialog.cpp2
-rw-r--r--editor/export/editor_export_platform.cpp2
-rw-r--r--editor/filesystem_dock.cpp4
-rw-r--r--editor/find_in_files.cpp4
-rw-r--r--editor/gui/editor_run_bar.cpp2
-rw-r--r--editor/gui/editor_toaster.cpp2
-rw-r--r--editor/gui/scene_tree_editor.cpp20
-rw-r--r--editor/icons/CanvasTexture.svg1
-rw-r--r--editor/icons/CurveTexture.svg2
-rw-r--r--editor/icons/GizmoCPUParticles3D.svg2
-rw-r--r--editor/icons/GizmoDecal.svg1
-rw-r--r--editor/icons/GizmoFogVolume.svg1
-rw-r--r--editor/icons/GizmoLightmapProbe.svg1
-rw-r--r--editor/icons/KeyPosition.svg2
-rw-r--r--editor/icons/KeyRotation.svg2
-rw-r--r--editor/icons/KeyScale.svg2
-rw-r--r--editor/icons/Paint.svg1
-rw-r--r--editor/import/collada.cpp2
-rw-r--r--editor/import/editor_import_collada.cpp6
-rw-r--r--editor/import/resource_importer_scene.cpp10
-rw-r--r--editor/inspector_dock.cpp4
-rw-r--r--editor/localization_editor.cpp18
-rw-r--r--editor/plugins/animation_blend_space_1d_editor.cpp4
-rw-r--r--editor/plugins/animation_blend_space_2d_editor.cpp4
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.cpp10
-rw-r--r--editor/plugins/animation_library_editor.cpp2
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp2
-rw-r--r--editor/plugins/animation_state_machine_editor.cpp4
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp2
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp40
-rw-r--r--editor/plugins/control_editor_plugin.cpp2
-rw-r--r--editor/plugins/curve_editor_plugin.cpp2
-rw-r--r--editor/plugins/editor_debugger_plugin.cpp12
-rw-r--r--editor/plugins/font_config_plugin.cpp2
-rw-r--r--editor/plugins/gizmos/decal_gizmo_plugin.cpp6
-rw-r--r--editor/plugins/gizmos/fog_volume_gizmo_plugin.cpp6
-rw-r--r--editor/plugins/gizmos/lightmap_probe_gizmo_plugin.cpp7
-rw-r--r--editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp4
-rw-r--r--editor/plugins/lightmap_gi_editor_plugin.cpp2
-rw-r--r--editor/plugins/mesh_library_editor_plugin.cpp2
-rw-r--r--editor/plugins/navigation_obstacle_3d_editor_plugin.cpp6
-rw-r--r--editor/plugins/node_3d_editor_gizmos.cpp50
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp18
-rw-r--r--editor/plugins/polygon_3d_editor_plugin.cpp8
-rw-r--r--editor/plugins/resource_preloader_editor_plugin.cpp2
-rw-r--r--editor/plugins/script_editor_plugin.cpp4
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.cpp10
-rw-r--r--editor/plugins/sprite_2d_editor_plugin.cpp4
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.cpp2
-rw-r--r--editor/plugins/texture_editor_plugin.cpp2
-rw-r--r--editor/plugins/tiles/tile_data_editors.cpp44
-rw-r--r--editor/plugins/tiles/tile_map_editor.cpp10
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp12
-rw-r--r--editor/plugins/tiles/tile_set_editor.cpp4
-rw-r--r--editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp6
-rw-r--r--editor/plugins/tiles/tiles_editor_plugin.cpp6
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp12
-rw-r--r--editor/plugins/voxel_gi_editor_plugin.cpp4
-rw-r--r--editor/scene_tree_dock.cpp26
-rw-r--r--editor/window_wrapper.cpp2
-rw-r--r--modules/gdscript/.editorconfig8
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp12
-rw-r--r--modules/gdscript/gdscript_compiler.cpp40
-rw-r--r--modules/gdscript/gdscript_compiler.h2
-rw-r--r--modules/gdscript/gdscript_editor.cpp2
-rw-r--r--modules/gdscript/gdscript_lambda_callable.cpp74
-rw-r--r--modules/gdscript/gdscript_vm.cpp9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.gd21
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.out3
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/member_info.gd65
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/member_info.out6
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/metatypes.gd36
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/metatypes.notest.gd1
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/metatypes.out13
-rw-r--r--modules/gdscript/tests/scripts/utils.notest.gd137
-rw-r--r--modules/gltf/doc_classes/GLTFDocument.xml11
-rw-r--r--modules/gltf/doc_classes/GLTFDocumentExtension.xml55
-rw-r--r--modules/gltf/extensions/gltf_document_extension.cpp34
-rw-r--r--modules/gltf/extensions/gltf_document_extension.h10
-rw-r--r--modules/gltf/extensions/gltf_document_extension_texture_webp.cpp39
-rw-r--r--modules/gltf/extensions/gltf_document_extension_texture_webp.h5
-rw-r--r--modules/gltf/gltf_document.cpp120
-rw-r--r--modules/gltf/gltf_document.h11
-rw-r--r--modules/mono/csharp_script.cpp5
-rw-r--r--modules/mono/editor/bindings_generator.cpp15
-rw-r--r--modules/mono/glue/GodotSharp/.editorconfig4
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2I.cs14
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3I.cs14
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4I.cs14
-rw-r--r--modules/openxr/openxr_api.cpp4
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.cpp4
-rw-r--r--modules/webrtc/register_types.cpp8
-rw-r--r--modules/webrtc/webrtc_data_channel.cpp2
-rw-r--r--modules/webrtc/webrtc_data_channel.h2
-rw-r--r--platform/android/android_input_handler.cpp4
-rw-r--r--platform/android/android_input_handler.h2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotLib.java2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java24
-rw-r--r--platform/android/java_godot_lib_jni.cpp4
-rw-r--r--platform/android/java_godot_lib_jni.h2
-rw-r--r--scene/2d/audio_stream_player_2d.cpp15
-rw-r--r--scene/2d/audio_stream_player_2d.h4
-rw-r--r--scene/2d/joint_2d.cpp10
-rw-r--r--scene/2d/joint_2d.h2
-rw-r--r--scene/2d/line_2d.cpp63
-rw-r--r--scene/2d/line_2d.h4
-rw-r--r--scene/2d/line_builder.cpp344
-rw-r--r--scene/2d/line_builder.h2
-rw-r--r--scene/3d/audio_stream_player_3d.cpp15
-rw-r--r--scene/3d/audio_stream_player_3d.h3
-rw-r--r--scene/3d/joint_3d.cpp24
-rw-r--r--scene/3d/joint_3d.h2
-rw-r--r--scene/animation/animation_blend_tree.cpp2
-rw-r--r--scene/audio/audio_stream_player.cpp15
-rw-r--r--scene/audio/audio_stream_player.h3
-rw-r--r--scene/gui/item_list.cpp6
-rw-r--r--scene/gui/item_list.h3
-rw-r--r--scene/gui/text_edit.cpp8
-rw-r--r--servers/audio_server.cpp11
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp1
-rw-r--r--servers/rendering/renderer_rd/shaders/canvas.glsl6
-rw-r--r--servers/rendering/shader_types.cpp1
-rw-r--r--tests/scene/test_text_edit.h9
164 files changed, 1680 insertions, 736 deletions
diff --git a/.editorconfig b/.editorconfig
index 4bb7553b16..92ee947a82 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -21,13 +21,3 @@ indent_size = 4
[*.{yml,yaml}]
indent_style = space
indent_size = 2
-
-# GDScript unit test files
-[*.gd]
-indent_style = tab
-indent_size = 4
-insert_final_newline = true
-trim_trailing_whitespace = true
-
-[*.out]
-insert_final_newline = true
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 9ffbac3553..5ba800ebfe 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -1398,6 +1398,8 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "rendering/textures/canvas_textures/default_texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Linear Mipmap,Nearest Mipmap"), 1);
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "rendering/textures/canvas_textures/default_texture_repeat", PROPERTY_HINT_ENUM, "Disable,Enable,Mirror"), 0);
+ GLOBAL_DEF("collada/use_ambient", false);
+
// These properties will not show up in the dialog. If you want to exclude whole groups, use add_hidden_prefix().
GLOBAL_DEF_INTERNAL("application/config/features", PackedStringArray());
GLOBAL_DEF_INTERNAL("internationalization/locale/translation_remaps", PackedStringArray());
diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp
index f59ee291ed..f57a5c1873 100644
--- a/core/extension/gdextension_interface.cpp
+++ b/core/extension/gdextension_interface.cpp
@@ -41,6 +41,152 @@
#include "core/variant/variant.h"
#include "core/version.h"
+class CallableCustomExtension : public CallableCustom {
+ void *userdata;
+ void *token;
+
+ ObjectID object;
+
+ GDExtensionCallableCustomCall call_func;
+ GDExtensionCallableCustomIsValid is_valid_func;
+ GDExtensionCallableCustomFree free_func;
+
+ GDExtensionCallableCustomEqual equal_func;
+ GDExtensionCallableCustomLessThan less_than_func;
+
+ GDExtensionCallableCustomToString to_string_func;
+
+ uint32_t _hash;
+
+ static bool default_compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
+ const CallableCustomExtension *a = static_cast<const CallableCustomExtension *>(p_a);
+ const CallableCustomExtension *b = static_cast<const CallableCustomExtension *>(p_b);
+
+ if (a->call_func != b->call_func || a->userdata != b->userdata) {
+ return false;
+ }
+ return true;
+ }
+
+ static bool default_compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
+ const CallableCustomExtension *a = static_cast<const CallableCustomExtension *>(p_a);
+ const CallableCustomExtension *b = static_cast<const CallableCustomExtension *>(p_b);
+
+ if (a->call_func != b->call_func) {
+ return a->call_func < b->call_func;
+ }
+ return a->userdata < b->userdata;
+ }
+
+ static bool custom_compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
+ const CallableCustomExtension *a = static_cast<const CallableCustomExtension *>(p_a);
+ const CallableCustomExtension *b = static_cast<const CallableCustomExtension *>(p_b);
+
+ if (a->equal_func != b->equal_func) {
+ return false;
+ }
+ return a->equal_func(a->userdata, b->userdata);
+ }
+
+ static bool custom_compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
+ const CallableCustomExtension *a = static_cast<const CallableCustomExtension *>(p_a);
+ const CallableCustomExtension *b = static_cast<const CallableCustomExtension *>(p_b);
+
+ if (a->less_than_func != b->less_than_func) {
+ return default_compare_less(p_a, p_b);
+ }
+ return a->less_than_func(a->userdata, b->userdata);
+ }
+
+public:
+ uint32_t hash() const override {
+ return _hash;
+ }
+
+ String get_as_text() const override {
+ if (to_string_func != nullptr) {
+ String out;
+ GDExtensionBool is_valid = false;
+
+ to_string_func(userdata, &is_valid, (GDExtensionStringPtr)&out);
+
+ if (is_valid) {
+ return out;
+ }
+ }
+ return "<CallableCustom>";
+ }
+
+ CompareEqualFunc get_compare_equal_func() const override {
+ return (equal_func != nullptr) ? custom_compare_equal : default_compare_equal;
+ }
+
+ CompareLessFunc get_compare_less_func() const override {
+ return (less_than_func != nullptr) ? custom_compare_less : default_compare_less;
+ }
+
+ bool is_valid() const override {
+ if (is_valid_func != nullptr && !is_valid_func(userdata)) {
+ return false;
+ }
+ return call_func != nullptr;
+ }
+
+ StringName get_method() const override {
+ return StringName();
+ }
+
+ ObjectID get_object() const override {
+ return object;
+ }
+
+ void *get_userdata(void *p_token) const {
+ return (p_token == token) ? userdata : nullptr;
+ }
+
+ void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override {
+ GDExtensionCallError error;
+
+ call_func(userdata, (GDExtensionConstVariantPtr *)p_arguments, p_argcount, (GDExtensionVariantPtr)&r_return_value, &error);
+
+ r_call_error.error = (Callable::CallError::Error)error.error;
+ r_call_error.argument = error.argument;
+ r_call_error.expected = error.expected;
+ }
+
+ CallableCustomExtension(GDExtensionCallableCustomInfo *p_info) {
+ userdata = p_info->callable_userdata;
+ token = p_info->token;
+
+ if (p_info->object != nullptr) {
+ object = ((Object *)p_info->object)->get_instance_id();
+ }
+
+ call_func = p_info->call_func;
+ is_valid_func = p_info->is_valid_func;
+ free_func = p_info->free_func;
+
+ equal_func = p_info->equal_func;
+ less_than_func = p_info->less_than_func;
+
+ to_string_func = p_info->to_string_func;
+
+ // Pre-calculate the hash.
+ if (p_info->hash_func != nullptr) {
+ _hash = p_info->hash_func(userdata);
+ } else {
+ _hash = hash_murmur3_one_64((uint64_t)call_func);
+ _hash = hash_murmur3_one_64((uint64_t)userdata, _hash);
+ }
+ }
+
+ ~CallableCustomExtension() {
+ if (free_func != nullptr) {
+ free_func(userdata);
+ }
+ }
+};
+
// Core interface functions.
GDExtensionInterfaceFunctionPtr gdextension_get_proc_address(const char *p_name) {
return GDExtension::get_interface_function(p_name);
@@ -1139,6 +1285,22 @@ static GDExtensionScriptInstancePtr gdextension_object_get_script_instance(GDExt
return script_instance_extension->instance;
}
+static void gdextension_callable_custom_create(GDExtensionUninitializedTypePtr r_callable, GDExtensionCallableCustomInfo *p_custom_callable_info) {
+ memnew_placement(r_callable, Callable(memnew(CallableCustomExtension(p_custom_callable_info))));
+}
+
+static void *gdextension_callable_custom_get_userdata(GDExtensionTypePtr p_callable, void *p_token) {
+ const Callable &callable = *reinterpret_cast<const Callable *>(p_callable);
+ if (!callable.is_custom()) {
+ return nullptr;
+ }
+ const CallableCustomExtension *custom_callable = dynamic_cast<const CallableCustomExtension *>(callable.get_custom());
+ if (!custom_callable) {
+ return nullptr;
+ }
+ return custom_callable->get_userdata(p_token);
+}
+
static GDExtensionMethodBindPtr gdextension_classdb_get_method_bind(GDExtensionConstStringNamePtr p_classname, GDExtensionConstStringNamePtr p_methodname, GDExtensionInt p_hash) {
const StringName classname = *reinterpret_cast<const StringName *>(p_classname);
const StringName methodname = *reinterpret_cast<const StringName *>(p_methodname);
@@ -1313,6 +1475,8 @@ void gdextension_setup_interface() {
REGISTER_INTERFACE_FUNC(placeholder_script_instance_create);
REGISTER_INTERFACE_FUNC(placeholder_script_instance_update);
REGISTER_INTERFACE_FUNC(object_get_script_instance);
+ REGISTER_INTERFACE_FUNC(callable_custom_create);
+ REGISTER_INTERFACE_FUNC(callable_custom_get_userdata);
REGISTER_INTERFACE_FUNC(classdb_construct_object);
REGISTER_INTERFACE_FUNC(classdb_get_method_bind);
REGISTER_INTERFACE_FUNC(classdb_get_class_tag);
diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h
index 9d9ae20c51..4214fce5f9 100644
--- a/core/extension/gdextension_interface.h
+++ b/core/extension/gdextension_interface.h
@@ -367,6 +367,47 @@ typedef struct {
GDExtensionVariantPtr *default_arguments;
} GDExtensionClassMethodInfo;
+typedef void (*GDExtensionCallableCustomCall)(void *callable_userdata, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error);
+typedef GDExtensionBool (*GDExtensionCallableCustomIsValid)(void *callable_userdata);
+typedef void (*GDExtensionCallableCustomFree)(void *callable_userdata);
+
+typedef uint32_t (*GDExtensionCallableCustomHash)(void *callable_userdata);
+typedef GDExtensionBool (*GDExtensionCallableCustomEqual)(void *callable_userdata_a, void *callable_userdata_b);
+typedef GDExtensionBool (*GDExtensionCallableCustomLessThan)(void *callable_userdata_a, void *callable_userdata_b);
+
+typedef void (*GDExtensionCallableCustomToString)(void *callable_userdata, GDExtensionBool *r_is_valid, GDExtensionStringPtr r_out);
+
+typedef struct {
+ /* Only `call_func` and `token` are strictly required, however, `object` should be passed if its not a static method.
+ *
+ * `token` should point to an address that uniquely identifies the GDExtension (for example, the
+ * `GDExtensionClassLibraryPtr` passed to the entry symbol function.
+ *
+ * `hash_func`, `equal_func`, and `less_than_func` are optional. If not provided both `call_func` and
+ * `callable_userdata` together are used as the identity of the callable for hashing and comparison purposes.
+ *
+ * The hash returned by `hash_func` is cached, `hash_func` will not be called more than once per callable.
+ *
+ * `is_valid_func` is necessary if the validity of the callable can change before destruction.
+ *
+ * `free_func` is necessary if `callable_userdata` needs to be cleaned up when the callable is freed.
+ */
+ void *callable_userdata;
+ void *token;
+
+ GDExtensionObjectPtr object;
+
+ GDExtensionCallableCustomCall call_func;
+ GDExtensionCallableCustomIsValid is_valid_func;
+ GDExtensionCallableCustomFree free_func;
+
+ GDExtensionCallableCustomHash hash_func;
+ GDExtensionCallableCustomEqual equal_func;
+ GDExtensionCallableCustomLessThan less_than_func;
+
+ GDExtensionCallableCustomToString to_string_func;
+} GDExtensionCallableCustomInfo;
+
/* SCRIPT INSTANCE EXTENSION */
typedef void *GDExtensionScriptInstanceDataPtr; // Pointer to custom ScriptInstance native implementation.
@@ -2250,6 +2291,34 @@ typedef void (*GDExtensionInterfacePlaceHolderScriptInstanceUpdate)(GDExtensionS
*/
typedef GDExtensionScriptInstanceDataPtr (*GDExtensionInterfaceObjectGetScriptInstance)(GDExtensionConstObjectPtr p_object, GDExtensionObjectPtr p_language);
+/* INTERFACE: Callable */
+
+/**
+ * @name callable_custom_create
+ * @since 4.2
+ *
+ * Creates a custom Callable object from a function pointer.
+ *
+ * Provided struct can be safely freed once the function returns.
+ *
+ * @param r_callable A pointer that will receive the new Callable.
+ * @param p_callable_custom_info The info required to construct a Callable.
+ */
+typedef void (*GDExtensionInterfaceCallableCustomCreate)(GDExtensionUninitializedTypePtr r_callable, GDExtensionCallableCustomInfo *p_callable_custom_info);
+
+/**
+ * @name callable_custom_get_userdata
+ * @since 4.2
+ *
+ * Retrieves the userdata pointer from a custom Callable.
+ *
+ * If the Callable is not a custom Callable or the token does not match the one provided to callable_custom_create() via GDExtensionCallableCustomInfo then NULL will be returned.
+ *
+ * @param p_callable A pointer to a Callable.
+ * @param p_token A pointer to an address that uniquely identifies the GDExtension.
+ */
+typedef void *(*GDExtensionInterfaceCallableCustomGetUserData)(GDExtensionConstTypePtr p_callable, void *p_token);
+
/* INTERFACE: ClassDB */
/**
diff --git a/core/variant/method_ptrcall.h b/core/variant/method_ptrcall.h
index cbfb9cc257..79be85cae6 100644
--- a/core/variant/method_ptrcall.h
+++ b/core/variant/method_ptrcall.h
@@ -38,26 +38,26 @@
template <class T>
struct PtrToArg {};
-#define MAKE_PTRARG(m_type) \
- template <> \
- struct PtrToArg<m_type> { \
- _FORCE_INLINE_ static m_type convert(const void *p_ptr) { \
- return *reinterpret_cast<const m_type *>(p_ptr); \
- } \
- typedef m_type EncodeT; \
- _FORCE_INLINE_ static void encode(m_type p_val, void *p_ptr) { \
- *((m_type *)p_ptr) = p_val; \
- } \
- }; \
- template <> \
- struct PtrToArg<const m_type &> { \
- _FORCE_INLINE_ static m_type convert(const void *p_ptr) { \
- return *reinterpret_cast<const m_type *>(p_ptr); \
- } \
- typedef m_type EncodeT; \
- _FORCE_INLINE_ static void encode(m_type p_val, void *p_ptr) { \
- *((m_type *)p_ptr) = p_val; \
- } \
+#define MAKE_PTRARG(m_type) \
+ template <> \
+ struct PtrToArg<m_type> { \
+ _FORCE_INLINE_ static const m_type &convert(const void *p_ptr) { \
+ return *reinterpret_cast<const m_type *>(p_ptr); \
+ } \
+ typedef m_type EncodeT; \
+ _FORCE_INLINE_ static void encode(m_type p_val, void *p_ptr) { \
+ *((m_type *)p_ptr) = p_val; \
+ } \
+ }; \
+ template <> \
+ struct PtrToArg<const m_type &> { \
+ _FORCE_INLINE_ static const m_type &convert(const void *p_ptr) { \
+ return *reinterpret_cast<const m_type *>(p_ptr); \
+ } \
+ typedef m_type EncodeT; \
+ _FORCE_INLINE_ static void encode(m_type p_val, void *p_ptr) { \
+ *((m_type *)p_ptr) = p_val; \
+ } \
}
#define MAKE_PTRARGCONV(m_type, m_conv) \
@@ -85,7 +85,7 @@ struct PtrToArg {};
#define MAKE_PTRARG_BY_REFERENCE(m_type) \
template <> \
struct PtrToArg<m_type> { \
- _FORCE_INLINE_ static m_type convert(const void *p_ptr) { \
+ _FORCE_INLINE_ static const m_type &convert(const void *p_ptr) { \
return *reinterpret_cast<const m_type *>(p_ptr); \
} \
typedef m_type EncodeT; \
@@ -95,7 +95,7 @@ struct PtrToArg {};
}; \
template <> \
struct PtrToArg<const m_type &> { \
- _FORCE_INLINE_ static m_type convert(const void *p_ptr) { \
+ _FORCE_INLINE_ static const m_type &convert(const void *p_ptr) { \
return *reinterpret_cast<const m_type *>(p_ptr); \
} \
typedef m_type EncodeT; \
diff --git a/core/variant/typed_array.h b/core/variant/typed_array.h
index 055c52aa63..037c5c7c2e 100644
--- a/core/variant/typed_array.h
+++ b/core/variant/typed_array.h
@@ -145,8 +145,7 @@ struct PtrToArg<TypedArray<T>> {
template <class T>
struct PtrToArg<const TypedArray<T> &> {
typedef Array EncodeT;
- _FORCE_INLINE_ static TypedArray<T>
- convert(const void *p_ptr) {
+ _FORCE_INLINE_ static TypedArray<T> convert(const void *p_ptr) {
return TypedArray<T>(*reinterpret_cast<const Array *>(p_ptr));
}
};
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index 19ae1b0157..772a540d22 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -2569,9 +2569,13 @@ static void _register_variant_builtin_methods() {
_VariantCall::add_variant_constant(Variant::VECTOR4I, "ZERO", Vector4i(0, 0, 0, 0));
_VariantCall::add_variant_constant(Variant::VECTOR4I, "ONE", Vector4i(1, 1, 1, 1));
+ _VariantCall::add_variant_constant(Variant::VECTOR4I, "MIN", Vector4i(INT32_MIN, INT32_MIN, INT32_MIN, INT32_MIN));
+ _VariantCall::add_variant_constant(Variant::VECTOR4I, "MAX", Vector4i(INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX));
_VariantCall::add_variant_constant(Variant::VECTOR3I, "ZERO", Vector3i(0, 0, 0));
_VariantCall::add_variant_constant(Variant::VECTOR3I, "ONE", Vector3i(1, 1, 1));
+ _VariantCall::add_variant_constant(Variant::VECTOR3I, "MIN", Vector3i(INT32_MIN, INT32_MIN, INT32_MIN));
+ _VariantCall::add_variant_constant(Variant::VECTOR3I, "MAX", Vector3i(INT32_MAX, INT32_MAX, INT32_MAX));
_VariantCall::add_variant_constant(Variant::VECTOR3I, "LEFT", Vector3i(-1, 0, 0));
_VariantCall::add_variant_constant(Variant::VECTOR3I, "RIGHT", Vector3i(1, 0, 0));
_VariantCall::add_variant_constant(Variant::VECTOR3I, "UP", Vector3i(0, 1, 0));
@@ -2601,6 +2605,8 @@ static void _register_variant_builtin_methods() {
_VariantCall::add_variant_constant(Variant::VECTOR2I, "ZERO", Vector2i(0, 0));
_VariantCall::add_variant_constant(Variant::VECTOR2I, "ONE", Vector2i(1, 1));
+ _VariantCall::add_variant_constant(Variant::VECTOR2I, "MIN", Vector2i(INT32_MIN, INT32_MIN));
+ _VariantCall::add_variant_constant(Variant::VECTOR2I, "MAX", Vector2i(INT32_MAX, INT32_MAX));
_VariantCall::add_variant_constant(Variant::VECTOR2I, "LEFT", Vector2i(-1, 0));
_VariantCall::add_variant_constant(Variant::VECTOR2I, "RIGHT", Vector2i(1, 0));
_VariantCall::add_variant_constant(Variant::VECTOR2I, "UP", Vector2i(0, -1));
diff --git a/doc/classes/AudioServer.xml b/doc/classes/AudioServer.xml
index 5d3d7a0591..21ad817c6c 100644
--- a/doc/classes/AudioServer.xml
+++ b/doc/classes/AudioServer.xml
@@ -317,7 +317,15 @@
<signals>
<signal name="bus_layout_changed">
<description>
- Emitted when the [AudioBusLayout] changes.
+ Emitted when an audio bus is added, deleted, or moved.
+ </description>
+ </signal>
+ <signal name="bus_renamed">
+ <param index="0" name="bus_index" type="int" />
+ <param index="1" name="old_name" type="StringName" />
+ <param index="2" name="new_name" type="StringName" />
+ <description>
+ Emitted when the audio bus at [param bus_index] is renamed from [param old_name] to [param new_name].
</description>
</signal>
</signals>
diff --git a/doc/classes/CanvasTexture.xml b/doc/classes/CanvasTexture.xml
index df3f8e8125..1b22adb723 100644
--- a/doc/classes/CanvasTexture.xml
+++ b/doc/classes/CanvasTexture.xml
@@ -8,6 +8,7 @@
[b]Note:[/b] [CanvasTexture] cannot be used in 3D rendering. For physically-based materials in 3D, use [BaseMaterial3D] instead.
</description>
<tutorials>
+ <link title="2D Lights and Shadows">$DOCS_URL/tutorials/2d/2d_lights_and_shadows.html</link>
</tutorials>
<members>
<member name="diffuse_texture" type="Texture2D" setter="set_diffuse_texture" getter="get_diffuse_texture">
diff --git a/doc/classes/ItemList.xml b/doc/classes/ItemList.xml
index f6ad422234..593f41bc70 100644
--- a/doc/classes/ItemList.xml
+++ b/doc/classes/ItemList.xml
@@ -57,6 +57,12 @@
Ensure current selection is visible, adjusting the scroll position as necessary.
</description>
</method>
+ <method name="force_update_list_size">
+ <return type="void" />
+ <description>
+ Forces an update to the list size based on its items. This happens automatically whenever size of the items, or other relevant settings like [member auto_height], change. The method can be used to trigger the update ahead of next drawing pass.
+ </description>
+ </method>
<method name="get_item_at_position" qualifiers="const">
<return type="int" />
<param index="0" name="position" type="Vector2" />
diff --git a/doc/classes/Joint2D.xml b/doc/classes/Joint2D.xml
index 6a43180561..9fb9154827 100644
--- a/doc/classes/Joint2D.xml
+++ b/doc/classes/Joint2D.xml
@@ -8,6 +8,14 @@
</description>
<tutorials>
</tutorials>
+ <methods>
+ <method name="get_rid" qualifiers="const">
+ <return type="RID" />
+ <description>
+ Returns the joint's [RID].
+ </description>
+ </method>
+ </methods>
<members>
<member name="bias" type="float" setter="set_bias" getter="get_bias" default="0.0">
When [member node_a] and [member node_b] move in different directions the [code]bias[/code] controls how fast the joint pulls them back to their original position. The lower the [code]bias[/code] the more the two bodies can pull on the joint.
diff --git a/doc/classes/Joint3D.xml b/doc/classes/Joint3D.xml
index d848bdc0cd..9e0b753701 100644
--- a/doc/classes/Joint3D.xml
+++ b/doc/classes/Joint3D.xml
@@ -9,6 +9,14 @@
<tutorials>
<link title="3D Truck Town Demo">https://godotengine.org/asset-library/asset/524</link>
</tutorials>
+ <methods>
+ <method name="get_rid" qualifiers="const">
+ <return type="RID" />
+ <description>
+ Returns the joint's [RID].
+ </description>
+ </method>
+ </methods>
<members>
<member name="exclude_nodes_from_collision" type="bool" setter="set_exclude_nodes_from_collision" getter="get_exclude_nodes_from_collision" default="true">
If [code]true[/code], the two bodies of the nodes are not able to collide with each other.
diff --git a/doc/classes/Line2D.xml b/doc/classes/Line2D.xml
index 7e02a520a0..4c444721f4 100644
--- a/doc/classes/Line2D.xml
+++ b/doc/classes/Line2D.xml
@@ -63,16 +63,21 @@
[b]Note:[/b] [Line2D] is not accelerated by batching when being anti-aliased.
</member>
<member name="begin_cap_mode" type="int" setter="set_begin_cap_mode" getter="get_begin_cap_mode" enum="Line2D.LineCapMode" default="0">
- The style of the beginning of the polyline. Use [enum LineCapMode] constants.
+ The style of the beginning of the polyline, if [member closed] is [code]false[/code]. Use [enum LineCapMode] constants.
+ </member>
+ <member name="closed" type="bool" setter="set_closed" getter="is_closed" default="false">
+ If [code]true[/code] and the polyline has more than 2 points, the last point and the first one will be connected by a segment.
+ [b]Note:[/b] The shape of the closing segment is not guaranteed to be seamless if a [member width_curve] is provided.
+ [b]Note:[/b] The joint between the closing segment and the first segment is drawn first and it samples the [member gradient] and the [member width_curve] at the beginning. This is an implementation detail that might change in a future version.
</member>
<member name="default_color" type="Color" setter="set_default_color" getter="get_default_color" default="Color(1, 1, 1, 1)">
The color of the polyline. Will not be used if a gradient is set.
</member>
<member name="end_cap_mode" type="int" setter="set_end_cap_mode" getter="get_end_cap_mode" enum="Line2D.LineCapMode" default="0">
- The style of the end of the polyline. Use [enum LineCapMode] constants.
+ The style of the end of the polyline, if [member closed] is [code]false[/code]. Use [enum LineCapMode] constants.
</member>
<member name="gradient" type="Gradient" setter="set_gradient" getter="get_gradient">
- The gradient is drawn through the whole line from start to finish. The default color will not be used if a gradient is set.
+ The gradient is drawn through the whole line from start to finish. The [member default_color] will not be used if this property is set.
</member>
<member name="joint_mode" type="int" setter="set_joint_mode" getter="get_joint_mode" enum="Line2D.LineJointMode" default="0">
The style of the connections between segments of the polyline. Use [enum LineJointMode] constants.
@@ -93,7 +98,7 @@
The style to render the [member texture] of the polyline. Use [enum LineTextureMode] constants.
</member>
<member name="width" type="float" setter="set_width" getter="get_width" default="10.0">
- The polyline's width
+ The polyline's width.
</member>
<member name="width_curve" type="Curve" setter="set_curve" getter="get_curve">
The polyline's width curve. The width of the polyline over its length will be equivalent to the value of the width curve over its domain.
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 6f900e06dd..87c642c6d3 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -400,6 +400,9 @@
<member name="audio/video/video_delay_compensation_ms" type="int" setter="" getter="" default="0">
Setting to hardcode audio delay when playing video. Best to leave this untouched unless you know what you are doing.
</member>
+ <member name="collada/use_ambient" type="bool" setter="" getter="" default="false">
+ If [code]true[/code], ambient lights will be imported from COLLADA models as [DirectionalLight3D]. If [code]false[/code], ambient lights will be ignored.
+ </member>
<member name="compression/formats/gzip/compression_level" type="int" setter="" getter="" default="-1">
The default compression level for gzip. Affects compressed scenes and resources. Higher levels result in smaller files at the cost of compression speed. Decompression speed is mostly unaffected by the compression level. [code]-1[/code] uses the default gzip compression level, which is identical to [code]6[/code] but could change in the future due to underlying zlib updates.
</member>
diff --git a/doc/classes/Vector2i.xml b/doc/classes/Vector2i.xml
index ccb5bb7815..2100cd7612 100644
--- a/doc/classes/Vector2i.xml
+++ b/doc/classes/Vector2i.xml
@@ -124,6 +124,12 @@
<constant name="ONE" value="Vector2i(1, 1)">
One vector, a vector with all components set to [code]1[/code].
</constant>
+ <constant name="MIN" value="Vector2i(-2147483648, -2147483648)">
+ Min vector, a vector with all components equal to [code]INT32_MIN[/code]. Can be used as a negative integer equivalent of [constant Vector2.INF].
+ </constant>
+ <constant name="MAX" value="Vector2i(2147483647, 2147483647)">
+ Max vector, a vector with all components equal to [code]INT32_MAX[/code]. Can be used as an integer equivalent of [constant Vector2.INF].
+ </constant>
<constant name="LEFT" value="Vector2i(-1, 0)">
Left unit vector. Represents the direction of left.
</constant>
diff --git a/doc/classes/Vector3i.xml b/doc/classes/Vector3i.xml
index 90cb70f347..8906bf0aa7 100644
--- a/doc/classes/Vector3i.xml
+++ b/doc/classes/Vector3i.xml
@@ -125,6 +125,12 @@
<constant name="ONE" value="Vector3i(1, 1, 1)">
One vector, a vector with all components set to [code]1[/code].
</constant>
+ <constant name="MIN" value="Vector3i(-2147483648, -2147483648, -2147483648)">
+ Min vector, a vector with all components equal to [code]INT32_MIN[/code]. Can be used as a negative integer equivalent of [constant Vector3.INF].
+ </constant>
+ <constant name="MAX" value="Vector3i(2147483647, 2147483647, 2147483647)">
+ Max vector, a vector with all components equal to [code]INT32_MAX[/code]. Can be used as an integer equivalent of [constant Vector3.INF].
+ </constant>
<constant name="LEFT" value="Vector3i(-1, 0, 0)">
Left unit vector. Represents the local direction of left, and the global direction of west.
</constant>
diff --git a/doc/classes/Vector4i.xml b/doc/classes/Vector4i.xml
index f2eb353b5a..a612c135dd 100644
--- a/doc/classes/Vector4i.xml
+++ b/doc/classes/Vector4i.xml
@@ -129,6 +129,12 @@
<constant name="ONE" value="Vector4i(1, 1, 1, 1)">
One vector, a vector with all components set to [code]1[/code].
</constant>
+ <constant name="MIN" value="Vector4i(-2147483648, -2147483648, -2147483648, -2147483648)">
+ Min vector, a vector with all components equal to [code]INT32_MIN[/code]. Can be used as a negative integer equivalent of [constant Vector4.INF].
+ </constant>
+ <constant name="MAX" value="Vector4i(2147483647, 2147483647, 2147483647, 2147483647)">
+ Max vector, a vector with all components equal to [code]INT32_MAX[/code]. Can be used as an integer equivalent of [constant Vector4.INF].
+ </constant>
</constants>
<operators>
<operator name="operator !=">
diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl
index e47e3ae9a3..ce8fe25625 100644
--- a/drivers/gles3/shaders/canvas.glsl
+++ b/drivers/gles3/shaders/canvas.glsl
@@ -199,6 +199,10 @@ void main() {
#ifdef USE_POINT_SIZE
float point_size = 1.0;
#endif
+
+#ifdef USE_WORLD_VERTEX_COORDS
+ vertex = (model_matrix * vec4(vertex, 0.0, 1.0)).xy;
+#endif
{
#CODE : VERTEX
}
@@ -207,7 +211,7 @@ void main() {
pixel_size_interp = abs(read_draw_data_dst_rect.zw) * vertex_base;
#endif
-#if !defined(SKIP_TRANSFORM_USED)
+#if !defined(SKIP_TRANSFORM_USED) && !defined(USE_WORLD_VERTEX_COORDS)
vertex = (model_matrix * vec4(vertex, 0.0, 1.0)).xy;
#endif
diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp
index 600672b7ef..a594813ed0 100644
--- a/drivers/gles3/storage/material_storage.cpp
+++ b/drivers/gles3/storage/material_storage.cpp
@@ -1184,6 +1184,7 @@ MaterialStorage::MaterialStorage() {
actions.render_mode_defines["skip_vertex_transform"] = "#define SKIP_TRANSFORM_USED\n";
actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n";
actions.render_mode_defines["light_only"] = "#define MODE_LIGHT_ONLY\n";
+ actions.render_mode_defines["world_vertex_coords"] = "#define USE_WORLD_VERTEX_COORDS\n";
actions.global_buffer_array_variable = "global_shader_uniforms";
diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp
index 55c394b3bf..bd4d76a1b6 100644
--- a/drivers/vulkan/rendering_device_vulkan.cpp
+++ b/drivers/vulkan/rendering_device_vulkan.cpp
@@ -9158,7 +9158,7 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_de
VkPipelineCacheCreateInfo cache_info = {};
cache_info.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
cache_info.pNext = nullptr;
- if (context->is_device_extension_enabled(VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_EXTENSION_NAME)) {
+ if (context->get_pipeline_cache_control_support()) {
cache_info.flags = VK_PIPELINE_CACHE_CREATE_EXTERNALLY_SYNCHRONIZED_BIT;
}
cache_info.initialDataSize = pipelines_cache.buffer.size();
diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp
index 9f35230eaf..344ea0d324 100644
--- a/drivers/vulkan/vulkan_context.cpp
+++ b/drivers/vulkan/vulkan_context.cpp
@@ -769,6 +769,7 @@ Error VulkanContext::_check_capabilities() {
VkPhysicalDeviceFragmentShadingRateFeaturesKHR vrs_features = {};
VkPhysicalDevice16BitStorageFeaturesKHR storage_feature = {};
VkPhysicalDeviceMultiviewFeatures multiview_features = {};
+ VkPhysicalDevicePipelineCreationCacheControlFeatures pipeline_cache_control_features = {};
if (device_api_version >= VK_API_VERSION_1_2) {
device_features_vk12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
@@ -820,6 +821,15 @@ Error VulkanContext::_check_capabilities() {
next = &multiview_features;
}
+ if (is_device_extension_enabled(VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_EXTENSION_NAME)) {
+ pipeline_cache_control_features = {
+ /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES,
+ /*pNext*/ next,
+ /*pipelineCreationCacheControl*/ false,
+ };
+ next = &pipeline_cache_control_features;
+ }
+
VkPhysicalDeviceFeatures2 device_features;
device_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
device_features.pNext = next;
@@ -860,6 +870,10 @@ Error VulkanContext::_check_capabilities() {
storage_buffer_capabilities.storage_push_constant_16_is_supported = storage_feature.storagePushConstant16;
storage_buffer_capabilities.storage_input_output_16 = storage_feature.storageInputOutput16;
}
+
+ if (is_device_extension_enabled(VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_EXTENSION_NAME)) {
+ pipeline_cache_control_support = pipeline_cache_control_features.pipelineCreationCacheControl;
+ }
}
// Check extended properties.
@@ -1422,6 +1436,16 @@ Error VulkanContext::_create_device() {
nextptr = &vrs_features;
}
+ VkPhysicalDevicePipelineCreationCacheControlFeatures pipeline_cache_control_features = {};
+ if (pipeline_cache_control_support) {
+ pipeline_cache_control_features.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES;
+ pipeline_cache_control_features.pNext = nextptr;
+ pipeline_cache_control_features.pipelineCreationCacheControl = pipeline_cache_control_support;
+
+ nextptr = &pipeline_cache_control_features;
+ }
+
VkPhysicalDeviceVulkan11Features vulkan11features = {};
VkPhysicalDevice16BitStorageFeaturesKHR storage_feature = {};
VkPhysicalDeviceMultiviewFeatures multiview_features = {};
diff --git a/drivers/vulkan/vulkan_context.h b/drivers/vulkan/vulkan_context.h
index 9fd2c40a06..ef40aba9e1 100644
--- a/drivers/vulkan/vulkan_context.h
+++ b/drivers/vulkan/vulkan_context.h
@@ -116,6 +116,7 @@ private:
VRSCapabilities vrs_capabilities;
ShaderCapabilities shader_capabilities;
StorageBufferCapabilities storage_buffer_capabilities;
+ bool pipeline_cache_control_support = false;
String device_vendor;
String device_name;
@@ -281,6 +282,7 @@ public:
const ShaderCapabilities &get_shader_capabilities() const { return shader_capabilities; };
const StorageBufferCapabilities &get_storage_buffer_capabilities() const { return storage_buffer_capabilities; };
const VkPhysicalDeviceFeatures &get_physical_device_features() const { return physical_device_features; };
+ bool get_pipeline_cache_control_support() const { return pipeline_cache_control_support; };
VkDevice get_device();
VkPhysicalDevice get_physical_device();
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index 125d7c0217..920d94081e 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -77,10 +77,10 @@ void AnimationTrackKeyEdit::_fix_node_path(Variant &value) {
Node *root = EditorNode::get_singleton()->get_tree()->get_root();
Node *np_node = root->get_node(np);
- ERR_FAIL_COND(!np_node);
+ ERR_FAIL_NULL(np_node);
Node *edited_node = root->get_node(base);
- ERR_FAIL_COND(!edited_node);
+ ERR_FAIL_NULL(edited_node);
value = edited_node->get_path_to(np_node);
}
@@ -656,10 +656,10 @@ void AnimationMultiTrackKeyEdit::_fix_node_path(Variant &value, NodePath &base)
Node *root = EditorNode::get_singleton()->get_tree()->get_root();
Node *np_node = root->get_node(np);
- ERR_FAIL_COND(!np_node);
+ ERR_FAIL_NULL(np_node);
Node *edited_node = root->get_node(base);
- ERR_FAIL_COND(!edited_node);
+ ERR_FAIL_NULL(edited_node);
value = edited_node->get_path_to(np_node);
}
@@ -3703,7 +3703,7 @@ void AnimationTrackEditor::_insert_track(bool p_reset_wanted, bool p_create_bezi
}
void AnimationTrackEditor::insert_transform_key(Node3D *p_node, const String &p_sub, const Animation::TrackType p_type, const Variant p_value) {
- ERR_FAIL_COND(!root);
+ ERR_FAIL_NULL(root);
ERR_FAIL_COND_MSG(
(p_type != Animation::TYPE_POSITION_3D && p_type != Animation::TYPE_ROTATION_3D && p_type != Animation::TYPE_SCALE_3D),
"Track type must be Position/Rotation/Scale 3D.");
@@ -3746,7 +3746,7 @@ void AnimationTrackEditor::insert_transform_key(Node3D *p_node, const String &p_
}
bool AnimationTrackEditor::has_track(Node3D *p_node, const String &p_sub, const Animation::TrackType p_type) {
- ERR_FAIL_COND_V(!root, false);
+ ERR_FAIL_NULL_V(root, false);
if (!keying) {
return false;
}
@@ -3802,7 +3802,7 @@ void AnimationTrackEditor::_insert_animation_key(NodePath p_path, const Variant
}
void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_property, const Variant &p_value, bool p_only_if_exists) {
- ERR_FAIL_COND(!root);
+ ERR_FAIL_NULL(root);
// Let's build a node path.
Node *node = p_node;
@@ -3899,7 +3899,7 @@ void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_p
void AnimationTrackEditor::insert_value_key(const String &p_property, const Variant &p_value, bool p_advance) {
EditorSelectionHistory *history = EditorNode::get_singleton()->get_editor_selection_history();
- ERR_FAIL_COND(!root);
+ ERR_FAIL_NULL(root);
ERR_FAIL_COND(history->get_path_size() == 0);
Object *obj = ObjectDB::get_instance(history->get_path_object(0));
ERR_FAIL_COND(!Object::cast_to<Node>(obj));
@@ -4685,9 +4685,9 @@ void AnimationTrackEditor::_dropped_track(int p_from_track, int p_to_track) {
}
void AnimationTrackEditor::_new_track_node_selected(NodePath p_path) {
- ERR_FAIL_COND(!root);
+ ERR_FAIL_NULL(root);
Node *node = get_node(p_path);
- ERR_FAIL_COND(!node);
+ ERR_FAIL_NULL(node);
NodePath path_to = root->get_path_to(node, true);
if (adding_track_type == Animation::TYPE_BLEND_SHAPE && !node->is_class("MeshInstance3D")) {
diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp
index 11fc5efd62..208253d617 100644
--- a/editor/connections_dialog.cpp
+++ b/editor/connections_dialog.cpp
@@ -517,16 +517,41 @@ String ConnectDialog::get_signature(const MethodInfo &p_method, PackedStringArra
}
const PropertyInfo &pi = p_method.arguments[i];
- String tname = "var";
- if (pi.type == Variant::OBJECT && pi.class_name != StringName()) {
- tname = pi.class_name.operator String();
- } else if (pi.type != Variant::NIL) {
- tname = Variant::get_type_name(pi.type);
+ String type_name;
+ switch (pi.type) {
+ case Variant::NIL:
+ type_name = "Variant";
+ break;
+ case Variant::INT:
+ if ((pi.usage & PROPERTY_USAGE_CLASS_IS_ENUM) && pi.class_name != StringName() && !String(pi.class_name).begins_with("res://")) {
+ type_name = pi.class_name;
+ } else {
+ type_name = "int";
+ }
+ break;
+ case Variant::ARRAY:
+ if (pi.hint == PROPERTY_HINT_ARRAY_TYPE && !pi.hint_string.is_empty() && !pi.hint_string.begins_with("res://")) {
+ type_name = "Array[" + pi.hint_string + "]";
+ } else {
+ type_name = "Array";
+ }
+ break;
+ case Variant::OBJECT:
+ if (pi.class_name != StringName()) {
+ type_name = pi.class_name;
+ } else {
+ type_name = "Object";
+ }
+ break;
+ default:
+ type_name = Variant::get_type_name(pi.type);
+ break;
}
- signature.append((pi.name.is_empty() ? String("arg " + itos(i)) : pi.name) + ": " + tname);
+ String arg_name = pi.name.is_empty() ? "arg" + itos(i) : pi.name;
+ signature.append(arg_name + ": " + type_name);
if (r_arg_names) {
- r_arg_names->push_back(pi.name + ":" + tname);
+ r_arg_names->push_back(arg_name + ":" + type_name);
}
}
@@ -858,7 +883,7 @@ void ConnectionsDock::_filter_changed(const String &p_text) {
void ConnectionsDock::_make_or_edit_connection() {
NodePath dst_path = connect_dialog->get_dst_path();
Node *target = selected_node->get_node(dst_path);
- ERR_FAIL_COND(!target);
+ ERR_FAIL_NULL(target);
ConnectDialog::ConnectionData cd;
cd.source = connect_dialog->get_source();
@@ -1066,7 +1091,7 @@ void ConnectionsDock::_open_connection_dialog(TreeItem &p_item) {
*/
void ConnectionsDock::_open_edit_connection_dialog(TreeItem &p_item) {
TreeItem *signal_item = p_item.get_parent();
- ERR_FAIL_COND(!signal_item);
+ ERR_FAIL_NULL(signal_item);
Connection connection = p_item.get_metadata(0);
ConnectDialog::ConnectionData cd = connection;
@@ -1213,6 +1238,11 @@ void ConnectionsDock::_rmb_pressed(const Ref<InputEvent> &p_event) {
return;
}
+ if (item->is_selectable(0)) {
+ // Update selection now, before `about_to_popup` signal. Needed for SIGNAL and CONNECTION context menus.
+ tree->set_selected(item);
+ }
+
Vector2 screen_position = tree->get_screen_position() + mb_event->get_position();
switch (_get_item_type(*item)) {
diff --git a/editor/debugger/editor_debugger_node.cpp b/editor/debugger/editor_debugger_node.cpp
index 607f8f073e..a327fd778b 100644
--- a/editor/debugger/editor_debugger_node.cpp
+++ b/editor/debugger/editor_debugger_node.cpp
@@ -51,7 +51,7 @@ template <typename Func>
void _for_all(TabContainer *p_node, const Func &p_func) {
for (int i = 0; i < p_node->get_tab_count(); i++) {
ScriptEditorDebugger *dbg = Object::cast_to<ScriptEditorDebugger>(p_node->get_tab_control(i));
- ERR_FAIL_COND(!dbg);
+ ERR_FAIL_NULL(dbg);
p_func(dbg);
}
}
@@ -133,7 +133,7 @@ ScriptEditorDebugger *EditorDebuggerNode::_add_debugger() {
void EditorDebuggerNode::_stack_frame_selected(int p_debugger) {
const ScriptEditorDebugger *dbg = get_debugger(p_debugger);
- ERR_FAIL_COND(!dbg);
+ ERR_FAIL_NULL(dbg);
if (dbg != get_current_debugger()) {
return;
}
@@ -405,7 +405,7 @@ void EditorDebuggerNode::_update_errors() {
void EditorDebuggerNode::_debugger_stopped(int p_id) {
ScriptEditorDebugger *dbg = get_debugger(p_id);
- ERR_FAIL_COND(!dbg);
+ ERR_FAIL_NULL(dbg);
bool found = false;
_for_all(tabs, [&](ScriptEditorDebugger *p_debugger) {
@@ -603,7 +603,7 @@ void EditorDebuggerNode::_remote_tree_button_pressed(Object *p_item, int p_colum
}
TreeItem *item = Object::cast_to<TreeItem>(p_item);
- ERR_FAIL_COND(!item);
+ ERR_FAIL_NULL(item);
if (p_id == EditorDebuggerTree::BUTTON_SUBSCENE) {
remote_scene_tree->emit_signal(SNAME("open"), item->get_meta("scene_file_path"));
diff --git a/editor/debugger/editor_debugger_tree.cpp b/editor/debugger/editor_debugger_tree.cpp
index 2e24a233a1..6305b7435a 100644
--- a/editor/debugger/editor_debugger_tree.cpp
+++ b/editor/debugger/editor_debugger_tree.cpp
@@ -300,7 +300,7 @@ String EditorDebuggerTree::get_selected_path() {
}
String EditorDebuggerTree::_get_path(TreeItem *p_item) {
- ERR_FAIL_COND_V(!p_item, "");
+ ERR_FAIL_NULL_V(p_item, "");
if (p_item->get_parent() == nullptr) {
return "/root";
diff --git a/editor/debugger/editor_performance_profiler.cpp b/editor/debugger/editor_performance_profiler.cpp
index e6e9d4e33d..e93369179c 100644
--- a/editor/debugger/editor_performance_profiler.cpp
+++ b/editor/debugger/editor_performance_profiler.cpp
@@ -47,7 +47,7 @@ EditorPerformanceProfiler::Monitor::Monitor(String p_name, String p_base, int p_
}
void EditorPerformanceProfiler::Monitor::update_value(float p_value) {
- ERR_FAIL_COND(!item);
+ ERR_FAIL_NULL(item);
String label = EditorPerformanceProfiler::_create_label(p_value, type);
String tooltip = label;
switch (type) {
diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp
index 8b60ac405a..6116ad8e4c 100644
--- a/editor/editor_audio_buses.cpp
+++ b/editor/editor_audio_buses.cpp
@@ -298,8 +298,6 @@ void EditorAudioBus::_name_changed(const String &p_new_name) {
StringName current = AudioServer::get_singleton()->get_bus_name(get_index());
ur->create_action(TTR("Rename Audio Bus"));
- ur->add_do_method(buses, "_set_renaming_buses", true);
- ur->add_undo_method(buses, "_set_renaming_buses", true);
ur->add_do_method(AudioServer::get_singleton(), "set_bus_name", get_index(), attempt);
ur->add_undo_method(AudioServer::get_singleton(), "set_bus_name", get_index(), current);
@@ -317,8 +315,6 @@ void EditorAudioBus::_name_changed(const String &p_new_name) {
ur->add_do_method(buses, "_update_sends");
ur->add_undo_method(buses, "_update_sends");
- ur->add_do_method(buses, "_set_renaming_buses", false);
- ur->add_undo_method(buses, "_set_renaming_buses", false);
ur->commit_action();
updating_bus = false;
@@ -545,9 +541,9 @@ void EditorAudioBus::_effect_add(int p_which) {
StringName name = effect_options->get_item_metadata(p_which);
Object *fx = ClassDB::instantiate(name);
- ERR_FAIL_COND(!fx);
+ ERR_FAIL_NULL(fx);
AudioEffect *afx = Object::cast_to<AudioEffect>(fx);
- ERR_FAIL_COND(!afx);
+ ERR_FAIL_NULL(afx);
Ref<AudioEffect> afxr = Ref<AudioEffect>(afx);
afxr->set_name(effect_options->get_item_text(p_which));
@@ -1011,18 +1007,7 @@ void EditorAudioBusDrop::_bind_methods() {
EditorAudioBusDrop::EditorAudioBusDrop() {
}
-void EditorAudioBuses::_set_renaming_buses(bool p_renaming) {
- renaming_buses = p_renaming;
-}
-
-void EditorAudioBuses::_update_buses() {
- if (renaming_buses) {
- // This case will be handled more gracefully, no need to trigger a full rebuild.
- // This is possibly a mistake in the AudioServer, which fires bus_layout_changed
- // on a rename. This may not be intended, but no way to tell at the moment.
- return;
- }
-
+void EditorAudioBuses::_rebuild_buses() {
for (int i = bus_hb->get_child_count() - 1; i >= 0; i--) {
EditorAudioBus *audio_bus = Object::cast_to<EditorAudioBus>(bus_hb->get_child(i));
if (audio_bus) {
@@ -1063,7 +1048,7 @@ void EditorAudioBuses::_notification(int p_what) {
} break;
case NOTIFICATION_READY: {
- _update_buses();
+ _rebuild_buses();
} break;
case NOTIFICATION_DRAG_END: {
@@ -1102,8 +1087,6 @@ void EditorAudioBuses::_add_bus() {
ur->create_action(TTR("Add Audio Bus"));
ur->add_do_method(AudioServer::get_singleton(), "set_bus_count", AudioServer::get_singleton()->get_bus_count() + 1);
ur->add_undo_method(AudioServer::get_singleton(), "set_bus_count", AudioServer::get_singleton()->get_bus_count());
- ur->add_do_method(this, "_update_buses");
- ur->add_undo_method(this, "_update_buses");
ur->commit_action();
}
@@ -1144,8 +1127,6 @@ void EditorAudioBuses::_delete_bus(Object *p_which) {
ur->add_undo_method(AudioServer::get_singleton(), "add_bus_effect", index, AudioServer::get_singleton()->get_bus_effect(index, i));
ur->add_undo_method(AudioServer::get_singleton(), "set_bus_effect_enabled", index, i, AudioServer::get_singleton()->is_bus_effect_enabled(index, i));
}
- ur->add_do_method(this, "_update_buses");
- ur->add_undo_method(this, "_update_buses");
ur->commit_action();
}
@@ -1165,8 +1146,6 @@ void EditorAudioBuses::_duplicate_bus(int p_which) {
ur->add_do_method(AudioServer::get_singleton(), "set_bus_effect_enabled", add_at_pos, i, AudioServer::get_singleton()->is_bus_effect_enabled(p_which, i));
}
ur->add_undo_method(AudioServer::get_singleton(), "remove_bus", add_at_pos);
- ur->add_do_method(this, "_update_buses");
- ur->add_undo_method(this, "_update_buses");
ur->commit_action();
}
@@ -1178,8 +1157,8 @@ void EditorAudioBuses::_reset_bus_volume(Object *p_which) {
ur->create_action(TTR("Reset Bus Volume"));
ur->add_do_method(AudioServer::get_singleton(), "set_bus_volume_db", index, 0.f);
ur->add_undo_method(AudioServer::get_singleton(), "set_bus_volume_db", index, AudioServer::get_singleton()->get_bus_volume_db(index));
- ur->add_do_method(this, "_update_buses");
- ur->add_undo_method(this, "_update_buses");
+ ur->add_do_method(this, "_update_bus", index);
+ ur->add_undo_method(this, "_update_bus", index);
ur->commit_action();
}
@@ -1202,8 +1181,6 @@ void EditorAudioBuses::_drop_at_index(int p_bus, int p_index) {
int real_index = p_index > p_bus ? p_index - 1 : p_index;
ur->add_undo_method(AudioServer::get_singleton(), "move_bus", real_index, real_bus);
- ur->add_do_method(this, "_update_buses");
- ur->add_undo_method(this, "_update_buses");
ur->commit_action();
}
@@ -1252,7 +1229,7 @@ void EditorAudioBuses::_load_default_layout() {
edited_path = layout_path;
file->set_text(String(TTR("Layout:")) + " " + layout_path.get_file());
AudioServer::get_singleton()->set_bus_layout(state);
- _update_buses();
+ _rebuild_buses();
EditorUndoRedoManager::get_singleton()->clear_history(true, EditorUndoRedoManager::GLOBAL_HISTORY);
call_deferred(SNAME("_select_layout"));
}
@@ -1268,7 +1245,7 @@ void EditorAudioBuses::_file_dialog_callback(const String &p_string) {
edited_path = p_string;
file->set_text(String(TTR("Layout:")) + " " + p_string.get_file());
AudioServer::get_singleton()->set_bus_layout(state);
- _update_buses();
+ _rebuild_buses();
EditorUndoRedoManager::get_singleton()->clear_history(true, EditorUndoRedoManager::GLOBAL_HISTORY);
call_deferred(SNAME("_select_layout"));
@@ -1288,15 +1265,13 @@ void EditorAudioBuses::_file_dialog_callback(const String &p_string) {
edited_path = p_string;
file->set_text(String(TTR("Layout:")) + " " + p_string.get_file());
- _update_buses();
+ _rebuild_buses();
EditorUndoRedoManager::get_singleton()->clear_history(true, EditorUndoRedoManager::GLOBAL_HISTORY);
call_deferred(SNAME("_select_layout"));
}
}
void EditorAudioBuses::_bind_methods() {
- ClassDB::bind_method("_set_renaming_buses", &EditorAudioBuses::_set_renaming_buses);
- ClassDB::bind_method("_update_buses", &EditorAudioBuses::_update_buses);
ClassDB::bind_method("_update_bus", &EditorAudioBuses::_update_bus);
ClassDB::bind_method("_update_sends", &EditorAudioBuses::_update_sends);
ClassDB::bind_method("_select_layout", &EditorAudioBuses::_select_layout);
@@ -1373,7 +1348,7 @@ EditorAudioBuses::EditorAudioBuses() {
add_child(file_dialog);
file_dialog->connect("file_selected", callable_mp(this, &EditorAudioBuses::_file_dialog_callback));
- AudioServer::get_singleton()->connect("bus_layout_changed", callable_mp(this, &EditorAudioBuses::_update_buses));
+ AudioServer::get_singleton()->connect("bus_layout_changed", callable_mp(this, &EditorAudioBuses::_rebuild_buses));
set_process(true);
}
@@ -1390,7 +1365,7 @@ void EditorAudioBuses::open_layout(const String &p_path) {
edited_path = p_path;
file->set_text(p_path.get_file());
AudioServer::get_singleton()->set_bus_layout(state);
- _update_buses();
+ _rebuild_buses();
EditorUndoRedoManager::get_singleton()->clear_history(true, EditorUndoRedoManager::GLOBAL_HISTORY);
call_deferred(SNAME("_select_layout"));
}
diff --git a/editor/editor_audio_buses.h b/editor/editor_audio_buses.h
index 51f858dd11..99e3214781 100644
--- a/editor/editor_audio_buses.h
+++ b/editor/editor_audio_buses.h
@@ -172,14 +172,11 @@ class EditorAudioBuses : public VBoxContainer {
Timer *save_timer = nullptr;
String edited_path;
- bool renaming_buses = false;
- void _set_renaming_buses(bool p_renaming);
-
- void _add_bus();
- void _update_buses();
+ void _rebuild_buses();
void _update_bus(int p_index);
void _update_sends();
+ void _add_bus();
void _delete_bus(Object *p_which);
void _duplicate_bus(int p_which);
void _reset_bus_volume(Object *p_which);
diff --git a/editor/editor_autoload_settings.cpp b/editor/editor_autoload_settings.cpp
index 24daab3c12..6658669d66 100644
--- a/editor/editor_autoload_settings.cpp
+++ b/editor/editor_autoload_settings.cpp
@@ -425,14 +425,14 @@ Node *EditorAutoloadSettings::_create_autoload(const String &p_path) {
Object *obj = ClassDB::instantiate(ibt);
- ERR_FAIL_COND_V_MSG(!obj, nullptr, vformat("Cannot instance script for Autoload, expected 'Node' inheritance, got: %s.", ibt));
+ ERR_FAIL_NULL_V_MSG(obj, nullptr, vformat("Cannot instance script for Autoload, expected 'Node' inheritance, got: %s.", ibt));
n = Object::cast_to<Node>(obj);
n->set_script(scr);
}
}
- ERR_FAIL_COND_V_MSG(!n, nullptr, vformat("Path in Autoload not a node or script: %s.", p_path));
+ ERR_FAIL_NULL_V_MSG(n, nullptr, vformat("Path in Autoload not a node or script: %s.", p_path));
return n;
}
diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp
index d8749aa290..e4f198a529 100644
--- a/editor/editor_data.cpp
+++ b/editor/editor_data.cpp
@@ -106,7 +106,7 @@ void EditorSelectionHistory::cleanup_history() {
void EditorSelectionHistory::add_object(ObjectID p_object, const String &p_property, bool p_inspector_only) {
Object *obj = ObjectDB::get_instance(p_object);
- ERR_FAIL_COND(!obj);
+ ERR_FAIL_NULL(obj);
RefCounted *r = Object::cast_to<RefCounted>(obj);
_Object o;
if (r) {
@@ -700,7 +700,7 @@ bool EditorData::check_and_update_scene(int p_idx) {
ERR_FAIL_COND_V(err != OK, false);
ep.step(TTR("Updating scene..."), 1);
Node *new_scene = pscene->instantiate(PackedScene::GEN_EDIT_STATE_MAIN);
- ERR_FAIL_COND_V(!new_scene, false);
+ ERR_FAIL_NULL_V(new_scene, false);
// Transfer selection.
List<Node *> new_selection;
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 199866e1f7..3fa3768d4b 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -3955,7 +3955,7 @@ void EditorInspector::_property_pinned(const String &p_path, bool p_pinned) {
}
Node *node = Object::cast_to<Node>(object);
- ERR_FAIL_COND(!node);
+ ERR_FAIL_NULL(node);
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(vformat(p_pinned ? TTR("Pinned %s") : TTR("Unpinned %s"), p_path));
diff --git a/editor/editor_interface.cpp b/editor/editor_interface.cpp
index f8b1e1d2fb..cb8f910074 100644
--- a/editor/editor_interface.cpp
+++ b/editor/editor_interface.cpp
@@ -480,7 +480,7 @@ void EditorInterface::create() {
}
void EditorInterface::free() {
- ERR_FAIL_COND(singleton == nullptr);
+ ERR_FAIL_NULL(singleton);
memdelete(singleton);
}
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index df58f93bf4..5341d73fe4 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -2003,7 +2003,7 @@ void EditorNode::_dialog_action(String p_file) {
saving_resource = Ref<Resource>();
ObjectID current_id = editor_history.get_current();
Object *current_obj = current_id.is_valid() ? ObjectDB::get_instance(current_id) : nullptr;
- ERR_FAIL_COND(!current_obj);
+ ERR_FAIL_NULL(current_obj);
current_obj->notify_property_list_changed();
} break;
case SETTINGS_LAYOUT_SAVE: {
@@ -2281,7 +2281,7 @@ void EditorNode::_edit_current(bool p_skip_foreign) {
if (is_resource) {
Resource *current_res = Object::cast_to<Resource>(current_obj);
- ERR_FAIL_COND(!current_res);
+ ERR_FAIL_NULL(current_res);
InspectorDock::get_inspector_singleton()->edit(current_res);
SceneTreeDock::get_singleton()->set_selected(nullptr);
@@ -2315,7 +2315,7 @@ void EditorNode::_edit_current(bool p_skip_foreign) {
}
} else if (is_node) {
Node *current_node = Object::cast_to<Node>(current_obj);
- ERR_FAIL_COND(!current_node);
+ ERR_FAIL_NULL(current_node);
InspectorDock::get_inspector_singleton()->edit(current_node);
if (current_node->is_inside_tree()) {
@@ -2950,9 +2950,9 @@ void EditorNode::_screenshot(bool p_use_utc) {
void EditorNode::_save_screenshot(NodePath p_path) {
Control *editor_main_screen = EditorInterface::get_singleton()->get_editor_main_screen();
- ERR_FAIL_COND_MSG(!editor_main_screen, "Cannot get the editor main screen control.");
+ ERR_FAIL_NULL_MSG(editor_main_screen, "Cannot get the editor main screen control.");
Viewport *viewport = editor_main_screen->get_viewport();
- ERR_FAIL_COND_MSG(!viewport, "Cannot get a viewport from the editor main screen.");
+ ERR_FAIL_NULL_MSG(viewport, "Cannot get a viewport from the editor main screen.");
Ref<ViewportTexture> texture = viewport->get_texture();
ERR_FAIL_COND_MSG(texture.is_null(), "Cannot get a viewport texture from the editor main screen.");
Ref<Image> img = texture->get_image();
@@ -3136,7 +3136,7 @@ void EditorNode::editor_select(int p_which) {
selecting = false;
EditorPlugin *new_editor = editor_table[p_which];
- ERR_FAIL_COND(!new_editor);
+ ERR_FAIL_NULL(new_editor);
if (editor_plugin_screen == new_editor) {
return;
@@ -4169,7 +4169,7 @@ void EditorNode::stop_child_process(OS::ProcessID p_pid) {
}
Ref<Script> EditorNode::get_object_custom_type_base(const Object *p_object) const {
- ERR_FAIL_COND_V(!p_object, nullptr);
+ ERR_FAIL_NULL_V(p_object, nullptr);
Ref<Script> scr = p_object->get_script();
@@ -4201,7 +4201,7 @@ Ref<Script> EditorNode::get_object_custom_type_base(const Object *p_object) cons
}
StringName EditorNode::get_object_custom_type_name(const Object *p_object) const {
- ERR_FAIL_COND_V(!p_object, StringName());
+ ERR_FAIL_NULL_V(p_object, StringName());
Ref<Script> scr = p_object->get_script();
if (scr.is_null() && Object::cast_to<Script>(p_object)) {
@@ -4341,7 +4341,7 @@ Ref<Texture2D> EditorNode::get_class_icon(const String &p_class, const String &p
}
bool EditorNode::is_object_of_custom_type(const Object *p_object, const StringName &p_class) {
- ERR_FAIL_COND_V(!p_object, false);
+ ERR_FAIL_NULL_V(p_object, false);
Ref<Script> scr = p_object->get_script();
if (scr.is_null() && Object::cast_to<Script>(p_object)) {
@@ -4631,7 +4631,7 @@ void EditorNode::_dock_make_selected_float() {
}
void EditorNode::_dock_make_float(Control *p_dock, int p_slot_index, bool p_show_window) {
- ERR_FAIL_COND(!p_dock);
+ ERR_FAIL_NULL(p_dock);
Size2 borders = Size2(4, 4) * EDSCALE;
// Remember size and position before removing it from the main window.
@@ -5810,7 +5810,7 @@ void EditorNode::remove_control_from_dock(Control *p_control) {
}
}
- ERR_FAIL_COND_MSG(!dock, "Control was not in dock.");
+ ERR_FAIL_NULL_MSG(dock, "Control was not in dock.");
dock->remove_child(p_control);
_update_dock_slots_visibility();
@@ -6207,7 +6207,7 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins
instantiated_node = current_packed_scene->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE);
}
- ERR_FAIL_COND(!instantiated_node);
+ ERR_FAIL_NULL(instantiated_node);
bool original_node_is_displayed_folded = original_node->is_displayed_folded();
bool original_node_scene_instance_load_placeholder = original_node->get_scene_instance_load_placeholder();
diff --git a/editor/editor_paths.cpp b/editor/editor_paths.cpp
index 610f66cff9..801c81efa8 100644
--- a/editor/editor_paths.cpp
+++ b/editor/editor_paths.cpp
@@ -95,7 +95,7 @@ void EditorPaths::create() {
}
void EditorPaths::free() {
- ERR_FAIL_COND(singleton == nullptr);
+ ERR_FAIL_NULL(singleton);
memdelete(singleton);
}
diff --git a/editor/editor_plugin_settings.cpp b/editor/editor_plugin_settings.cpp
index 167a151419..ef8730cabf 100644
--- a/editor/editor_plugin_settings.cpp
+++ b/editor/editor_plugin_settings.cpp
@@ -137,7 +137,7 @@ void EditorPluginSettings::_plugin_activity_changed() {
}
TreeItem *ti = plugin_list->get_edited();
- ERR_FAIL_COND(!ti);
+ ERR_FAIL_NULL(ti);
bool active = ti->is_checked(3);
String name = ti->get_metadata(0);
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index 2a82b2cde4..7a6cae7248 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -2786,7 +2786,7 @@ void EditorPropertyNodePath::_node_selected(const NodePath &p_path) {
if (!base_node && Object::cast_to<RefCounted>(get_edited_object())) {
Node *to_node = get_node(p_path);
- ERR_FAIL_COND(!to_node);
+ ERR_FAIL_NULL(to_node);
path = get_tree()->get_edited_scene_root()->get_path_to(to_node);
}
@@ -2899,7 +2899,7 @@ void EditorPropertyNodePath::update_property() {
}
Node *target_node = base_node->get_node(p);
- ERR_FAIL_COND(!target_node);
+ ERR_FAIL_NULL(target_node);
if (String(target_node->get_name()).contains("@")) {
assign->set_icon(Ref<Texture2D>());
diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp
index 61c6c0bcdb..950a1e1c4d 100644
--- a/editor/editor_properties_array_dict.cpp
+++ b/editor/editor_properties_array_dict.cpp
@@ -1111,7 +1111,7 @@ void EditorPropertyDictionary::update_property() {
}
}
- ERR_FAIL_COND(!prop);
+ ERR_FAIL_NULL(prop);
prop->set_read_only(is_read_only());
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index a1504d1ac0..2c93cc68b0 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -528,7 +528,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
// FileSystem
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "docks/filesystem/thumbnail_size", 64, "32,128,16")
_initial_set("docks/filesystem/always_show_folders", true);
- _initial_set("docks/filesystem/textfile_extensions", "txt,md,cfg,ini,log,json,yml,yaml,toml");
+ _initial_set("docks/filesystem/textfile_extensions", "txt,md,cfg,ini,log,json,yml,yaml,toml,xml");
// Property editor
EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "docks/property_editor/auto_refresh_interval", 0.2, "0.01,1,0.001"); // Update 5 times per second by default.
diff --git a/editor/editor_settings_dialog.cpp b/editor/editor_settings_dialog.cpp
index 23b319f456..a5e70c5b6c 100644
--- a/editor/editor_settings_dialog.cpp
+++ b/editor/editor_settings_dialog.cpp
@@ -500,7 +500,7 @@ void EditorSettingsDialog::_shortcut_button_pressed(Object *p_item, int p_column
return;
}
TreeItem *ti = Object::cast_to<TreeItem>(p_item);
- ERR_FAIL_COND_MSG(!ti, "Object passed is not a TreeItem");
+ ERR_FAIL_NULL_MSG(ti, "Object passed is not a TreeItem.");
ShortcutButton button_idx = (ShortcutButton)p_idx;
diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp
index c07f17b46f..b3fefaafc6 100644
--- a/editor/export/editor_export_platform.cpp
+++ b/editor/export/editor_export_platform.cpp
@@ -756,7 +756,7 @@ String EditorExportPlatform::_export_customize(const String &p_path, LocalVector
Ref<PackedScene> ps = ResourceLoader::load(p_path, "PackedScene", ResourceFormatLoader::CACHE_MODE_IGNORE);
ERR_FAIL_COND_V(ps.is_null(), p_path);
Node *node = ps->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE); // Make sure the child scene root gets the correct inheritance chain.
- ERR_FAIL_COND_V(node == nullptr, p_path);
+ ERR_FAIL_NULL_V(node, p_path);
if (!customize_scenes_plugins.is_empty()) {
for (Ref<EditorExportPlugin> &plugin : customize_scenes_plugins) {
Node *customized = plugin->_customize_scene(node, p_path);
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index b947f5cefd..1a518f7d03 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -2349,7 +2349,7 @@ void FileSystemDock::_resource_created() {
ERR_FAIL_COND(!c);
Resource *r = Object::cast_to<Resource>(c);
- ERR_FAIL_COND(!r);
+ ERR_FAIL_NULL(r);
PackedScene *scene = Object::cast_to<PackedScene>(r);
if (scene) {
@@ -2913,7 +2913,7 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, Vector<Str
p_popup->add_child(folder_colors_menu);
p_popup->add_submenu_item(TTR("Set Folder Color..."), "FolderColor");
- p_popup->set_item_icon(-1, get_editor_theme_icon(SNAME("CanvasItem")));
+ p_popup->set_item_icon(-1, get_editor_theme_icon(SNAME("Paint")));
folder_colors_menu->add_icon_item(get_editor_theme_icon(SNAME("Folder")), TTR("Default (Reset)"));
folder_colors_menu->set_item_icon_modulate(0, get_theme_color(SNAME("folder_icon_color"), SNAME("FileDialog")));
diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp
index 3e580e5f08..5506f4a6bc 100644
--- a/editor/find_in_files.cpp
+++ b/editor/find_in_files.cpp
@@ -501,8 +501,8 @@ void FindInFilesDialog::custom_action(const String &p_action) {
}
void FindInFilesDialog::_on_search_text_modified(String text) {
- ERR_FAIL_COND(!_find_button);
- ERR_FAIL_COND(!_replace_button);
+ ERR_FAIL_NULL(_find_button);
+ ERR_FAIL_NULL(_replace_button);
_find_button->set_disabled(get_search_text().is_empty());
_replace_button->set_disabled(get_search_text().is_empty());
diff --git a/editor/gui/editor_run_bar.cpp b/editor/gui/editor_run_bar.cpp
index 4dfe40f0ad..497c92d951 100644
--- a/editor/gui/editor_run_bar.cpp
+++ b/editor/gui/editor_run_bar.cpp
@@ -220,7 +220,7 @@ void EditorRunBar::_run_scene(const String &p_scene_path) {
return;
}
- run_filename = GLOBAL_DEF_BASIC("application/run/main_scene", "");
+ run_filename = GLOBAL_GET("application/run/main_scene");
} break;
}
diff --git a/editor/gui/editor_toaster.cpp b/editor/gui/editor_toaster.cpp
index 6a415c18da..0d468bc0a3 100644
--- a/editor/gui/editor_toaster.cpp
+++ b/editor/gui/editor_toaster.cpp
@@ -456,7 +456,7 @@ void EditorToaster::_popup_str(String p_message, Severity p_severity, String p_t
// Retrieve the label back, then update the text.
Label *message_label = toasts[control].message_label;
- ERR_FAIL_COND(!message_label);
+ ERR_FAIL_NULL(message_label);
message_label->set_text(p_message);
message_label->set_text_overrun_behavior(TextServer::OVERRUN_NO_TRIMMING);
message_label->set_custom_minimum_size(Size2());
diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp
index 8e08528f19..f483539ba2 100644
--- a/editor/gui/scene_tree_editor.cpp
+++ b/editor/gui/scene_tree_editor.cpp
@@ -66,12 +66,12 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i
}
TreeItem *item = Object::cast_to<TreeItem>(p_item);
- ERR_FAIL_COND(!item);
+ ERR_FAIL_NULL(item);
NodePath np = item->get_metadata(0);
Node *n = get_node(np);
- ERR_FAIL_COND(!n);
+ ERR_FAIL_NULL(n);
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (p_id == BUTTON_SUBSCENE) {
@@ -94,7 +94,7 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i
List<Node *> selection = editor_selection->get_selected_node_list();
if (selection.size() > 1 && selection.find(n) != nullptr) {
for (Node *nv : selection) {
- ERR_FAIL_COND(!nv);
+ ERR_FAIL_NULL(nv);
if (nv == n) {
continue;
}
@@ -826,7 +826,7 @@ void SceneTreeEditor::_tree_changed() {
void SceneTreeEditor::_selected_changed() {
TreeItem *s = tree->get_selected();
- ERR_FAIL_COND(!s);
+ ERR_FAIL_NULL(s);
NodePath np = s->get_metadata(0);
Node *n = get_node(np);
@@ -852,7 +852,7 @@ void SceneTreeEditor::_deselect_items() {
void SceneTreeEditor::_cell_multi_selected(Object *p_object, int p_cell, bool p_selected) {
TreeItem *item = Object::cast_to<TreeItem>(p_object);
- ERR_FAIL_COND(!item);
+ ERR_FAIL_NULL(item);
if (!item->is_visible()) {
return;
@@ -980,7 +980,7 @@ void SceneTreeEditor::set_selected(Node *p_node, bool p_emit_selected) {
void SceneTreeEditor::_rename_node(Node *p_node, const String &p_name) {
TreeItem *item = _find(tree->get_root(), p_node->get_path());
- ERR_FAIL_COND(!item);
+ ERR_FAIL_NULL(item);
String new_name = p_name.validate_node_name();
if (new_name != p_name) {
@@ -1060,10 +1060,10 @@ void SceneTreeEditor::_rename_node(Node *p_node, const String &p_name) {
void SceneTreeEditor::_renamed() {
TreeItem *which = tree->get_edited();
- ERR_FAIL_COND(!which);
+ ERR_FAIL_NULL(which);
NodePath np = which->get_metadata(0);
Node *n = get_node(np);
- ERR_FAIL_COND(!n);
+ ERR_FAIL_NULL(n);
String new_name = which->get_text(0);
@@ -1131,7 +1131,7 @@ void SceneTreeEditor::set_editor_selection(EditorSelection *p_selection) {
}
void SceneTreeEditor::_update_selection(TreeItem *item) {
- ERR_FAIL_COND(!item);
+ ERR_FAIL_NULL(item);
NodePath np = item->get_metadata(0);
@@ -1196,7 +1196,7 @@ void SceneTreeEditor::_cell_collapsed(Object *p_obj) {
NodePath np = ti->get_metadata(0);
Node *n = get_node(np);
- ERR_FAIL_COND(!n);
+ ERR_FAIL_NULL(n);
n->set_display_folded(collapsed);
}
diff --git a/editor/icons/CanvasTexture.svg b/editor/icons/CanvasTexture.svg
new file mode 100644
index 0000000000..8734da3ee8
--- /dev/null
+++ b/editor/icons/CanvasTexture.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v8.5c1.5.5 1-1 2-1V3h10v2.2a2.415 2.415 0 0 1 2 .5V2a1 1 0 0 0-1-1zm1.36 10.18c-.283.169-.516.466-.645.865-.416 1.277-2.417-.94-.946 2.009.465.931 1.912 1.202 2.835.723a1.922 1.922 0 0 0 .83-2.555c-.578-1.158-1.45-1.411-2.074-1.041zm2.222-.7 1.272 2.495 7.069-3.602a1.415 1.415 0 0 0-1.259-2.534zM9 5v1H8v1H6v1H5v1H4v1h.25L11 6.527V6h-1V5z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/CurveTexture.svg b/editor/icons/CurveTexture.svg
index a5524f145c..b9838ebc36 100644
--- a/editor/icons/CurveTexture.svg
+++ b/editor/icons/CurveTexture.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v9.16A3 3 0 0 1 2 11h1V3h10v1.135a3 3 0 0 1 2 0V2a1 1 0 0 0-1-1zm7 4v1H8v1H6v1H5v1H4v1h4.39c1.113-.567 1.968-1.454 2.61-3.473V6h-1V5H9zm4.039 1.727c-.927 3.246-2.636 4.682-4.652 5.466C6.37 12.978 4 13 2 13a1 1 0 1 0 0 2c2 0 4.63.024 7.113-.941 2.484-.966 4.775-3.03 5.848-6.784a1 1 0 0 0-1.922-.548z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v9.16A3 3 0 0 1 2 11h1V3h10v1.135a3 3 0 0 1 2 0V2a1 1 0 0 0-1-1zm7 4v1H8v1H6v1H5v1H4v1h4.39c1.113-.567 1.968-1.454 2.61-3.473V6h-1V5zm4.039 1.727c-.927 3.246-2.636 4.682-4.652 5.466C6.37 12.978 4 13 2 13a1 1 0 1 0 0 2c2 0 4.63.024 7.113-.941 2.484-.966 4.775-3.03 5.848-6.784a1 1 0 0 0-1.922-.548z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/GizmoCPUParticles3D.svg b/editor/icons/GizmoCPUParticles3D.svg
index 3dae7ade80..b67aa0eaed 100644
--- a/editor/icons/GizmoCPUParticles3D.svg
+++ b/editor/icons/GizmoCPUParticles3D.svg
@@ -1 +1 @@
-<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="M36.688 4a6.112 6.112 0 0 0-6.112 6.112v4.8h-9.6a6.112 6.112 0 0 0-6.112 6.112v9.6h-4.8a6.112 6.112 0 0 0-6.112 6.112v3.2a6.112 6.112 0 0 0 6.112 6.112h4.8v36h-4.8a6.112 6.112 0 0 0-6.112 6.112v3.2a6.112 6.112 0 0 0 6.112 6.112h4.8v9.6a6.112 6.112 0 0 0 6.112 6.112h9.6v4.8a6.112 6.112 0 0 0 6.112 6.112h3.2A6.112 6.112 0 0 0 46 117.984v-4.8h36v4.8a6.112 6.112 0 0 0 6.112 6.112h3.2a6.112 6.112 0 0 0 6.112-6.112v-4.8h9.6a6.112 6.112 0 0 0 6.112-6.112v-9.6h4.8a6.112 6.112 0 0 0 6.112-6.112v-3.2a6.112 6.112 0 0 0-6.112-6.112h-4.8v-36h4.8a6.112 6.112 0 0 0 6.112-6.112v-3.2a6.112 6.112 0 0 0-6.112-6.112h-4.8v-9.6a6.112 6.112 0 0 0-6.112-6.104h-9.6v-4.8a6.112 6.112 0 0 0-6.112-6.112h-3.2A6.112 6.112 0 0 0 82 10.12v4.8H46v-4.8a6.112 6.112 0 0 0-6.112-6.112z" fill="#f7f5cf" stroke="#b3b3b3" stroke-width="3"/><path d="M88 82a18 18 0 0 0 2.484-35.814 27 30 0 0 0-52.944 0 18 18 0 0 0 2.484 35.802zm-48 6a6 6 0 0 0 0 12 6 6 0 0 0 0-12zm48 0a6 6 0 0 0 0 12 6 6 0 0 0 0-12zm-24 6a6 6 0 0 0 0 12 6 6 0 0 0 0-12z" fill="#b3b3b3"/></svg>
+<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="M36.688 4a6.112 6.112 0 0 0-6.112 6.112v4.8h-9.6a6.112 6.112 0 0 0-6.112 6.112v9.6h-4.8a6.112 6.112 0 0 0-6.112 6.112v3.2a6.112 6.112 0 0 0 6.112 6.112h4.8v36h-4.8a6.112 6.112 0 0 0-6.112 6.112v3.2a6.112 6.112 0 0 0 6.112 6.112h4.8v9.6a6.112 6.112 0 0 0 6.112 6.112h9.6v4.8a6.112 6.112 0 0 0 6.112 6.112h3.2A6.112 6.112 0 0 0 46 117.984v-4.8h36v4.8a6.112 6.112 0 0 0 6.112 6.112h3.2a6.112 6.112 0 0 0 6.112-6.112v-4.8h9.6a6.112 6.112 0 0 0 6.112-6.112v-9.6h4.8a6.112 6.112 0 0 0 6.112-6.112v-3.2a6.112 6.112 0 0 0-6.112-6.112h-4.8v-36h4.8a6.112 6.112 0 0 0 6.112-6.112v-3.2a6.112 6.112 0 0 0-6.112-6.112h-4.8v-9.6a6.112 6.112 0 0 0-6.112-6.104h-9.6v-4.8a6.112 6.112 0 0 0-6.112-6.112h-3.2A6.112 6.112 0 0 0 82 10.12v4.8H46v-4.8a6.112 6.112 0 0 0-6.112-6.112z" stroke="#000" stroke-width="8" stroke-opacity=".3"/><path d="M36.688 4a6.112 6.112 0 0 0-6.112 6.112v4.8h-9.6a6.112 6.112 0 0 0-6.112 6.112v9.6h-4.8a6.112 6.112 0 0 0-6.112 6.112v3.2a6.112 6.112 0 0 0 6.112 6.112h4.8v36h-4.8a6.112 6.112 0 0 0-6.112 6.112v3.2a6.112 6.112 0 0 0 6.112 6.112h4.8v9.6a6.112 6.112 0 0 0 6.112 6.112h9.6v4.8a6.112 6.112 0 0 0 6.112 6.112h3.2A6.112 6.112 0 0 0 46 117.984v-4.8h36v4.8a6.112 6.112 0 0 0 6.112 6.112h3.2a6.112 6.112 0 0 0 6.112-6.112v-4.8h9.6a6.112 6.112 0 0 0 6.112-6.112v-9.6h4.8a6.112 6.112 0 0 0 6.112-6.112v-3.2a6.112 6.112 0 0 0-6.112-6.112h-4.8v-36h4.8a6.112 6.112 0 0 0 6.112-6.112v-3.2a6.112 6.112 0 0 0-6.112-6.112h-4.8v-9.6a6.112 6.112 0 0 0-6.112-6.104h-9.6v-4.8a6.112 6.112 0 0 0-6.112-6.112h-3.2A6.112 6.112 0 0 0 82 10.12v4.8H46v-4.8a6.112 6.112 0 0 0-6.112-6.112z" fill="#f7f5cf"/><path d="M88 82a18 18 0 0 0 2.484-35.814 27 30 0 0 0-52.944 0 18 18 0 0 0 2.484 35.802zm-48 6a6 6 0 0 0 0 12 6 6 0 0 0 0-12zm48 0a6 6 0 0 0 0 12 6 6 0 0 0 0-12zm-24 6a6 6 0 0 0 0 12 6 6 0 0 0 0-12z" fill="#e1b44c"/></svg>
diff --git a/editor/icons/GizmoDecal.svg b/editor/icons/GizmoDecal.svg
new file mode 100644
index 0000000000..bd3b3f608f
--- /dev/null
+++ b/editor/icons/GizmoDecal.svg
@@ -0,0 +1 @@
+<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><circle cx="64" cy="64" fill="none" stroke="#000" stroke-width="8" stroke-opacity=".3" r="48"/><path d="M111.313 55.934a48 48 0 1 0-71.28 49.528z" fill="#f7f5cf"/><path d="M111.313 55.934a48 48 0 0 0-71.28 49.528z" fill="#e1b44c"/><path d="M40.033 105.462a48 48 0 0 0 71.28-49.528z" fill="#f7f5cf" fill-opacity=".4"/></svg>
diff --git a/editor/icons/GizmoFogVolume.svg b/editor/icons/GizmoFogVolume.svg
new file mode 100644
index 0000000000..6a3423b1a2
--- /dev/null
+++ b/editor/icons/GizmoFogVolume.svg
@@ -0,0 +1 @@
+<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="M20 68S8 68 8 56s16-12 16-12 0-32 28-32 28 24 28 24 10.08-16 24-8 4 24 4 24 12 0 12 8-8 8-8 8zm16 8a4 4 0 0 0 0 8h64a4 4 0 0 0 0-8zm-8 16a4 4 0 0 0 0 8h40a4 4 0 0 0 0-8zm56 0a4 4 0 0 0 0 8h20a4 4 0 0 0 0-8zm-40 16a4 4 0 0 0 0 8h40a4 4 0 0 0 0-8z" stroke="#000" stroke-width="8" stroke-opacity=".3"/><path d="M20 68S8 68 8 56s16-12 16-12 0-32 28-32 28 24 28 24 10.08-16 24-8 4 24 4 24 12 0 12 8-8 8-8 8zm16 8a4 4 0 0 0 0 8h64a4 4 0 0 0 0-8zm-8 16a4 4 0 0 0 0 8h40a4 4 0 0 0 0-8zm56 0a4 4 0 0 0 0 8h20a4 4 0 0 0 0-8zm-40 16a4 4 0 0 0 0 8h40a4 4 0 0 0 0-8z" fill="#f7f5cf"/></svg>
diff --git a/editor/icons/GizmoLightmapProbe.svg b/editor/icons/GizmoLightmapProbe.svg
new file mode 100644
index 0000000000..7259a7c184
--- /dev/null
+++ b/editor/icons/GizmoLightmapProbe.svg
@@ -0,0 +1 @@
+<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="M8 72h24V56H8zm16.4 20.28 11.32 11.312L47.032 92.28 35.72 80.968zm0-56.56 11.32 11.312L47.032 35.72 35.72 24.4zM40 64a24 24 0 0 0 48 0 24 24 0 0 0-48 0zm24 56A56 56 0 0 0 64 8v18.672a37.328 37.328 0 0 1 0 74.656z" stroke="#000" stroke-width="8" stroke-opacity=".3"/><path d="M8 72h24V56H8zm16.4 20.28 11.32 11.312L47.032 92.28 35.72 80.968zm0-56.56 11.32 11.312L47.032 35.72 35.72 24.4zM40 64a24 24 0 0 0 48 0 24 24 0 0 0-48 0zm24 56A56 56 0 0 0 64 8v18.672a37.328 37.328 0 0 1 0 74.656z" fill="#f7f5cf"/></svg>
diff --git a/editor/icons/KeyPosition.svg b/editor/icons/KeyPosition.svg
index 37ba41f996..c96df83523 100644
--- a/editor/icons/KeyPosition.svg
+++ b/editor/icons/KeyPosition.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1a.76.76 0 0 0-.54.225L4.226 4.46a.76.76 0 0 0 0 1.078L7.46 8.775a.76.76 0 0 0 1.078 0l3.236-3.236a.76.76 0 0 0 0-1.078L8.54 1.225A.76.76 0 0 0 8 1zM1 8v5a3 3 0 0 0 3 3h1v-2H4a1 1 0 0 1-1-1V8zm7 2a3 3 0 0 0 0 6 3 3 0 0 0 0-6zm6 0a3 3 0 0 0 0 6h1v-2h-1a1 1 0 0 1 0-2h1v-2h-1zm-6 2a1 1 0 0 1 0 2 1 1 0 0 1 0-2z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M4.226 3.46a.76.76 0 0 0 0 1.078L7.46 7.775a.76.76 0 0 0 1.078 0l3.236-3.236a.76.76 0 0 0 0-1.078L8.54.225a.76.76 0 0 0-1.08 0zM8 9a3 3 0 0 0 0 6 3 3 0 0 0 0-6zm0 2a1 1 0 0 1 0 2 1 1 0 0 1 0-2zm5-2a1 1 0 0 0 0 4h-2v2h2a1 1 0 0 0 0-4h2V9zM0 9h2a2.7 2.7 0 0 1 0 5.4V16H0zm2 3.7a1 1 0 0 0 0-2z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/KeyRotation.svg b/editor/icons/KeyRotation.svg
index 4d46f8a0f0..88fc22bafd 100644
--- a/editor/icons/KeyRotation.svg
+++ b/editor/icons/KeyRotation.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1a.76.76 0 0 0-.54.225L4.226 4.46a.76.76 0 0 0 0 1.078L7.46 8.775a.76.76 0 0 0 1.078 0l3.236-3.236a.76.76 0 0 0 0-1.078L8.54 1.225A.76.76 0 0 0 8 1zm3 7v5a3 3 0 0 0 3 3h1v-2h-1a1 1 0 0 1-1-1v-1h2v-2h-2V8zm0 5a3 3 0 0 0-6 0 3 3 0 0 0 6 0zm-7-3a3 3 0 0 0-3 3v3h2v-3a1 1 0 0 1 1-1h1v-2zm4 2a1 1 0 0 1 0 2 1 1 0 0 1 0-2z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M4.226 3.46a.76.76 0 0 0 0 1.078L7.46 7.775a.76.76 0 0 0 1.078 0l3.236-3.236a.76.76 0 0 0 0-1.078L8.54.225a.76.76 0 0 0-1.08 0zM11 7v5a3 3 0 0 0 3 3h1v-2h-1a1 1 0 0 1-1-1v-1h2V9h-2V7zm0 5a3 3 0 0 0-6 0 3 3 0 0 0 6 0zM4 9a3 3 0 0 0-3 3v3h2v-3a1 1 0 0 1 1-1h1V9zm4 2a1 1 0 0 1 0 2 1 1 0 0 1 0-2z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/KeyScale.svg b/editor/icons/KeyScale.svg
index c7b55028ab..bd36c83b30 100644
--- a/editor/icons/KeyScale.svg
+++ b/editor/icons/KeyScale.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1a.76.76 0 0 0-.54.225L4.226 4.46a.76.76 0 0 0 0 1.078L7.46 8.775a.76.76 0 0 0 1.078 0l3.236-3.236a.76.76 0 0 0 0-1.078L8.54 1.225A.76.76 0 0 0 8 1zm3 7v5a3 3 0 0 0 3 3h1v-2h-1a1 1 0 0 1-1-1V8zm-8 2a2 2 0 1 0 0 4H1v2h2a2 2 0 1 0 0-4h2v-2zm6 0a3 3 0 1 0 0 6h1v-2H9a1 1 0 0 1 0-2h1v-2z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M4.226 3.46a.76.76 0 0 0 0 1.078L7.46 7.775a.76.76 0 0 0 1.078 0l3.236-3.236a.76.76 0 0 0 0-1.078L8.54.225a.76.76 0 0 0-1.08 0zM11 7v5a3 3 0 0 0 3 3h1v-2h-1a1 1 0 0 1-1-1V7zM3 9a2 2 0 1 0 0 4H1v2h2a2 2 0 1 0 0-4h2V9zm6 0a3 3 0 1 0 0 6h1v-2H9a1 1 0 0 1 0-2h1V9z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/Paint.svg b/editor/icons/Paint.svg
new file mode 100644
index 0000000000..eb0c621296
--- /dev/null
+++ b/editor/icons/Paint.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2.92 10c-.263.3-.42.73-.42 1.238 0 1.628-3.138-.178-.337 2.67.884.9 2.654.67 3.538-.228a2.33 2.33 0 0 0 0-3.256c-1.1-1.119-2.2-1.084-2.78-.424zm2.3-1.64 2.4 2.4 6.8-6.8a1.7 1.7 0 0 0-2.4-2.45z" fill="#e0e0e0"/></svg>
diff --git a/editor/import/collada.cpp b/editor/import/collada.cpp
index a499fc4feb..d27b0aea40 100644
--- a/editor/import/collada.cpp
+++ b/editor/import/collada.cpp
@@ -2196,7 +2196,7 @@ bool Collada::_move_geometry_to_skeletons(VisualScene *p_vscene, Node *p_node, L
ERR_FAIL_COND_V(!state.scene_map.has(nodeid), false); //weird, it should have it...
NodeJoint *nj = dynamic_cast<NodeJoint *>(state.scene_map[nodeid]);
- ERR_FAIL_COND_V(!nj, false);
+ ERR_FAIL_NULL_V(nj, false);
ERR_FAIL_COND_V(!nj->owner, false); //weird, node should have a skeleton owner
NodeSkeleton *sk = nj->owner;
diff --git a/editor/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp
index 5f714e4488..1e410b7272 100644
--- a/editor/import/editor_import_collada.cpp
+++ b/editor/import/editor_import_collada.cpp
@@ -208,7 +208,7 @@ Error ColladaImport::_create_scene(Collada::Node *p_node, Node3D *p_parent) {
return OK; //do nothing not needed
}
- if (!bool(GLOBAL_DEF("collada/use_ambient", false))) {
+ if (!bool(GLOBAL_GET("collada/use_ambient"))) {
return OK;
}
//well, it's an ambient light..
@@ -1107,7 +1107,7 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres
ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(node);
- ERR_FAIL_COND_V(!mi, ERR_BUG);
+ ERR_FAIL_NULL_V(mi, ERR_BUG);
Collada::SkinControllerData *skin = nullptr;
Collada::MorphControllerData *morph = nullptr;
@@ -1131,7 +1131,7 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres
ERR_FAIL_COND_V(!node_map.has(skname), ERR_INVALID_DATA);
NodeMap nmsk = node_map[skname];
Skeleton3D *sk = Object::cast_to<Skeleton3D>(nmsk.node);
- ERR_FAIL_COND_V(!sk, ERR_INVALID_DATA);
+ ERR_FAIL_NULL_V(sk, ERR_INVALID_DATA);
ERR_FAIL_COND_V(!skeleton_bone_map.has(sk), ERR_INVALID_DATA);
HashMap<String, int> &bone_remap_map = skeleton_bone_map[sk];
diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp
index c70918e55e..6996280477 100644
--- a/editor/import/resource_importer_scene.cpp
+++ b/editor/import/resource_importer_scene.cpp
@@ -155,11 +155,11 @@ Variant EditorScenePostImportPlugin::get_option_value(const StringName &p_name)
return Variant();
}
void EditorScenePostImportPlugin::add_import_option(const String &p_name, Variant p_default_value) {
- ERR_FAIL_COND_MSG(current_option_list == nullptr, "add_import_option() can only be called from get_import_options()");
+ ERR_FAIL_NULL_MSG(current_option_list, "add_import_option() can only be called from get_import_options().");
add_import_option_advanced(p_default_value.get_type(), p_name, p_default_value);
}
void EditorScenePostImportPlugin::add_import_option_advanced(Variant::Type p_type, const String &p_name, Variant p_default_value, PropertyHint p_hint, const String &p_hint_string, int p_usage_flags) {
- ERR_FAIL_COND_MSG(current_option_list == nullptr, "add_import_option_advanced() can only be called from get_import_options()");
+ ERR_FAIL_NULL_MSG(current_option_list, "add_import_option_advanced() can only be called from get_import_options().");
current_option_list->push_back(ResourceImporter::ImportOption(PropertyInfo(p_type, p_name, p_hint, p_hint_string, p_usage_flags), p_default_value));
}
@@ -356,7 +356,7 @@ static String _fixstr(const String &p_what, const String &p_str) {
}
static void _pre_gen_shape_list(Ref<ImporterMesh> &mesh, Vector<Ref<Shape3D>> &r_shape_list, bool p_convex) {
- ERR_FAIL_NULL_MSG(mesh, "Cannot generate shape list with null mesh value");
+ ERR_FAIL_NULL_MSG(mesh, "Cannot generate shape list with null mesh value.");
if (!p_convex) {
Ref<ConcavePolygonShape3D> shape = mesh->create_trimesh_shape();
r_shape_list.push_back(shape);
@@ -2143,7 +2143,7 @@ void ResourceImporterScene::_optimize_track_usage(AnimationPlayer *p_player, Ani
List<StringName> anims;
p_player->get_animation_list(&anims);
Node *parent = p_player->get_parent();
- ERR_FAIL_COND(parent == nullptr);
+ ERR_FAIL_NULL(parent);
HashMap<NodePath, uint32_t> used_tracks[TRACK_CHANNEL_MAX];
bool tracks_to_add = false;
static const Animation::TrackType track_types[TRACK_CHANNEL_MAX] = { Animation::TYPE_POSITION_3D, Animation::TYPE_ROTATION_3D, Animation::TYPE_SCALE_3D, Animation::TYPE_BLEND_SHAPE };
@@ -2727,7 +2727,7 @@ Node *EditorSceneFormatImporterESCN::import_scene(const String &p_path, uint32_t
}
}
- ERR_FAIL_COND_V(!scene, nullptr);
+ ERR_FAIL_NULL_V(scene, nullptr);
return scene;
}
diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp
index ac06841b30..044ac52147 100644
--- a/editor/inspector_dock.cpp
+++ b/editor/inspector_dock.cpp
@@ -205,7 +205,7 @@ void InspectorDock::_menu_option_confirm(int p_option, bool p_confirmed) {
default: {
if (p_option >= OBJECT_METHOD_BASE) {
- ERR_FAIL_COND(!current);
+ ERR_FAIL_NULL(current);
int idx = p_option - OBJECT_METHOD_BASE;
@@ -373,7 +373,7 @@ void InspectorDock::_resource_created() {
ERR_FAIL_COND(!c);
Resource *r = Object::cast_to<Resource>(c);
- ERR_FAIL_COND(!r);
+ ERR_FAIL_NULL(r);
EditorNode::get_singleton()->push_item(r);
}
diff --git a/editor/localization_editor.cpp b/editor/localization_editor.cpp
index d4154d371b..fb34740318 100644
--- a/editor/localization_editor.cpp
+++ b/editor/localization_editor.cpp
@@ -101,7 +101,7 @@ void LocalizationEditor::_translation_delete(Object *p_item, int p_column, int p
}
TreeItem *ti = Object::cast_to<TreeItem>(p_item);
- ERR_FAIL_COND(!ti);
+ ERR_FAIL_NULL(ti);
int idx = ti->get_metadata(0);
@@ -163,7 +163,7 @@ void LocalizationEditor::_translation_res_option_add(const PackedStringArray &p_
Dictionary remaps = GLOBAL_GET("internationalization/locale/translation_remaps");
TreeItem *k = translation_remap->get_selected();
- ERR_FAIL_COND(!k);
+ ERR_FAIL_NULL(k);
String key = k->get_metadata(0);
@@ -194,7 +194,7 @@ void LocalizationEditor::_translation_res_select() {
void LocalizationEditor::_translation_res_option_popup(bool p_arrow_clicked) {
TreeItem *ed = translation_remap_options->get_edited();
- ERR_FAIL_COND(!ed);
+ ERR_FAIL_NULL(ed);
locale_select->set_locale(ed->get_tooltip_text(1));
locale_select->popup_locale_dialog();
@@ -202,7 +202,7 @@ void LocalizationEditor::_translation_res_option_popup(bool p_arrow_clicked) {
void LocalizationEditor::_translation_res_option_selected(const String &p_locale) {
TreeItem *ed = translation_remap_options->get_edited();
- ERR_FAIL_COND(!ed);
+ ERR_FAIL_NULL(ed);
ed->set_text(1, TranslationServer::get_singleton()->get_locale_name(p_locale));
ed->set_tooltip_text(1, p_locale);
@@ -222,9 +222,9 @@ void LocalizationEditor::_translation_res_option_changed() {
Dictionary remaps = GLOBAL_GET("internationalization/locale/translation_remaps");
TreeItem *k = translation_remap->get_selected();
- ERR_FAIL_COND(!k);
+ ERR_FAIL_NULL(k);
TreeItem *ed = translation_remap_options->get_edited();
- ERR_FAIL_COND(!ed);
+ ERR_FAIL_NULL(ed);
String key = k->get_metadata(0);
int idx = ed->get_metadata(0);
@@ -299,9 +299,9 @@ void LocalizationEditor::_translation_res_option_delete(Object *p_item, int p_co
Dictionary remaps = GLOBAL_GET("internationalization/locale/translation_remaps");
TreeItem *k = translation_remap->get_selected();
- ERR_FAIL_COND(!k);
+ ERR_FAIL_NULL(k);
TreeItem *ed = Object::cast_to<TreeItem>(p_item);
- ERR_FAIL_COND(!ed);
+ ERR_FAIL_NULL(ed);
String key = k->get_metadata(0);
int idx = ed->get_metadata(0);
@@ -348,7 +348,7 @@ void LocalizationEditor::_pot_delete(Object *p_item, int p_column, int p_button,
}
TreeItem *ti = Object::cast_to<TreeItem>(p_item);
- ERR_FAIL_COND(!ti);
+ ERR_FAIL_NULL(ti);
int idx = ti->get_metadata(0);
diff --git a/editor/plugins/animation_blend_space_1d_editor.cpp b/editor/plugins/animation_blend_space_1d_editor.cpp
index 373a927ef9..58eec1fa1e 100644
--- a/editor/plugins/animation_blend_space_1d_editor.cpp
+++ b/editor/plugins/animation_blend_space_1d_editor.cpp
@@ -423,9 +423,9 @@ void AnimationNodeBlendSpace1DEditor::_add_menu_type(int p_index) {
String type = menu->get_item_metadata(p_index);
Object *obj = ClassDB::instantiate(type);
- ERR_FAIL_COND(!obj);
+ ERR_FAIL_NULL(obj);
AnimationNode *an = Object::cast_to<AnimationNode>(obj);
- ERR_FAIL_COND(!an);
+ ERR_FAIL_NULL(an);
node = Ref<AnimationNode>(an);
}
diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp
index cd69e90660..7971fbf6d6 100644
--- a/editor/plugins/animation_blend_space_2d_editor.cpp
+++ b/editor/plugins/animation_blend_space_2d_editor.cpp
@@ -349,9 +349,9 @@ void AnimationNodeBlendSpace2DEditor::_add_menu_type(int p_index) {
String type = menu->get_item_metadata(p_index);
Object *obj = ClassDB::instantiate(type);
- ERR_FAIL_COND(!obj);
+ ERR_FAIL_NULL(obj);
AnimationNode *an = Object::cast_to<AnimationNode>(obj);
- ERR_FAIL_COND(!an);
+ ERR_FAIL_NULL(an);
node = Ref<AnimationNode>(an);
}
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp
index 8fffa5efab..f28c989ac1 100644
--- a/editor/plugins/animation_blend_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp
@@ -325,14 +325,14 @@ void AnimationNodeBlendTreeEditor::_add_node(int p_idx) {
base_name = anode->get_class();
} else if (!add_options[p_idx].type.is_empty()) {
AnimationNode *an = Object::cast_to<AnimationNode>(ClassDB::instantiate(add_options[p_idx].type));
- ERR_FAIL_COND(!an);
+ ERR_FAIL_NULL(an);
anode = Ref<AnimationNode>(an);
base_name = add_options[p_idx].name;
} else {
ERR_FAIL_COND(add_options[p_idx].script.is_null());
StringName base_type = add_options[p_idx].script->get_instance_base_type();
AnimationNode *an = Object::cast_to<AnimationNode>(ClassDB::instantiate(base_type));
- ERR_FAIL_COND(!an);
+ ERR_FAIL_NULL(an);
anode = Ref<AnimationNode>(an);
anode->set_script(add_options[p_idx].script);
base_name = add_options[p_idx].name;
@@ -568,7 +568,7 @@ void AnimationNodeBlendTreeEditor::_node_selected(Object *p_node) {
}
GraphNode *gn = Object::cast_to<GraphNode>(p_node);
- ERR_FAIL_COND(!gn);
+ ERR_FAIL_NULL(gn);
String name = gn->get_name();
@@ -598,7 +598,7 @@ void AnimationNodeBlendTreeEditor::_filter_toggled() {
void AnimationNodeBlendTreeEditor::_filter_edited() {
TreeItem *edited = filters->get_edited();
- ERR_FAIL_COND(!edited);
+ ERR_FAIL_NULL(edited);
NodePath edited_path = edited->get_metadata(0);
bool filtered = edited->is_checked(0);
@@ -966,7 +966,7 @@ void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<Anima
String prev_name = blend_tree->get_node_name(p_node);
ERR_FAIL_COND(prev_name.is_empty());
GraphNode *gn = Object::cast_to<GraphNode>(graph->get_node(prev_name));
- ERR_FAIL_COND(!gn);
+ ERR_FAIL_NULL(gn);
const String &new_name = p_text;
diff --git a/editor/plugins/animation_library_editor.cpp b/editor/plugins/animation_library_editor.cpp
index f658b2d5e6..6a54bc654f 100644
--- a/editor/plugins/animation_library_editor.cpp
+++ b/editor/plugins/animation_library_editor.cpp
@@ -621,7 +621,7 @@ void AnimationLibraryEditor::update_tree() {
}
tree->clear();
- ERR_FAIL_COND(!player);
+ ERR_FAIL_NULL(player);
Color ss_color = get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor));
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index a9c3eebc3f..05e189290d 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -2013,7 +2013,7 @@ bool EditorInspectorPluginAnimationTrackKeyEdit::can_handle(Object *p_object) {
void EditorInspectorPluginAnimationTrackKeyEdit::parse_begin(Object *p_object) {
AnimationTrackKeyEdit *atk = Object::cast_to<AnimationTrackKeyEdit>(p_object);
- ERR_FAIL_COND(!atk);
+ ERR_FAIL_NULL(atk);
atk_editor = memnew(AnimationTrackKeyEditEditor(atk->animation, atk->track, atk->key_ofs, atk->use_fps));
add_custom_control(atk_editor);
diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp
index 5e40a2f986..638838bcca 100644
--- a/editor/plugins/animation_state_machine_editor.cpp
+++ b/editor/plugins/animation_state_machine_editor.cpp
@@ -708,9 +708,9 @@ void AnimationNodeStateMachineEditor::_add_menu_type(int p_index) {
String type = menu->get_item_metadata(p_index);
Object *obj = ClassDB::instantiate(type);
- ERR_FAIL_COND(!obj);
+ ERR_FAIL_NULL(obj);
AnimationNode *an = Object::cast_to<AnimationNode>(obj);
- ERR_FAIL_COND(!an);
+ ERR_FAIL_NULL(an);
node = Ref<AnimationNode>(an);
base_name = type.replace_first("AnimationNode", "");
diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp
index ae9497116d..5c08263099 100644
--- a/editor/plugins/asset_library_editor_plugin.cpp
+++ b/editor/plugins/asset_library_editor_plugin.cpp
@@ -661,7 +661,7 @@ void EditorAssetLibrary::shortcut_input(const Ref<InputEvent> &p_event) {
}
void EditorAssetLibrary::_install_asset() {
- ERR_FAIL_COND(!description);
+ ERR_FAIL_NULL(description);
EditorAssetLibraryItemDownload *d = _get_asset_in_progress(description->get_asset_id());
if (d) {
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index a6ea76943f..91f403bc49 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -808,7 +808,7 @@ List<CanvasItem *> CanvasItemEditor::_get_edited_canvas_items(bool retrieve_lock
}
Vector2 CanvasItemEditor::_anchor_to_position(const Control *p_control, Vector2 anchor) {
- ERR_FAIL_COND_V(!p_control, Vector2());
+ ERR_FAIL_NULL_V(p_control, Vector2());
Transform2D parent_transform = p_control->get_transform().affine_inverse();
Rect2 parent_rect = p_control->get_parent_anchorable_rect();
@@ -821,7 +821,7 @@ Vector2 CanvasItemEditor::_anchor_to_position(const Control *p_control, Vector2
}
Vector2 CanvasItemEditor::_position_to_anchor(const Control *p_control, Vector2 position) {
- ERR_FAIL_COND_V(!p_control, Vector2());
+ ERR_FAIL_NULL_V(p_control, Vector2());
Rect2 parent_rect = p_control->get_parent_anchorable_rect();
@@ -2056,27 +2056,31 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) {
if (drag_type == DRAG_MOVE || drag_type == DRAG_MOVE_X || drag_type == DRAG_MOVE_Y) {
// Move the nodes
- if (m.is_valid()) {
+ if (m.is_valid() && !drag_selection.is_empty()) {
_restore_canvas_item_state(drag_selection, true);
drag_to = transform.affine_inverse().xform(m->get_position());
Point2 previous_pos;
- if (!drag_selection.is_empty()) {
- if (drag_selection.size() == 1) {
- Transform2D xform = drag_selection[0]->get_global_transform_with_canvas() * drag_selection[0]->get_transform().affine_inverse();
- previous_pos = xform.xform(drag_selection[0]->_edit_get_position());
- } else {
- previous_pos = _get_encompassing_rect_from_list(drag_selection).position;
- }
+ if (drag_selection.size() == 1) {
+ Transform2D xform = drag_selection[0]->get_global_transform_with_canvas() * drag_selection[0]->get_transform().affine_inverse();
+ previous_pos = xform.xform(drag_selection[0]->_edit_get_position());
+ } else {
+ previous_pos = _get_encompassing_rect_from_list(drag_selection).position;
}
- Point2 new_pos = snap_point(previous_pos + (drag_to - drag_from), SNAP_GRID | SNAP_GUIDES | SNAP_PIXEL | SNAP_NODE_PARENT | SNAP_NODE_ANCHORS | SNAP_OTHER_NODES, 0, nullptr, drag_selection);
+ Point2 drag_delta = drag_to - drag_from;
+ if (drag_selection.size() == 1 && (drag_type == DRAG_MOVE_X || drag_type == DRAG_MOVE_Y)) {
+ const CanvasItem *selected = drag_selection.front()->get();
+ drag_delta = selected->get_transform().affine_inverse().basis_xform(drag_delta);
- if (drag_type == DRAG_MOVE_X) {
- new_pos.y = previous_pos.y;
- } else if (drag_type == DRAG_MOVE_Y) {
- new_pos.x = previous_pos.x;
+ if (drag_type == DRAG_MOVE_X) {
+ drag_delta.y = 0;
+ } else {
+ drag_delta.x = 0;
+ }
+ drag_delta = selected->get_transform().basis_xform(drag_delta);
}
+ Point2 new_pos = snap_point(previous_pos + drag_delta, SNAP_GRID | SNAP_GUIDES | SNAP_PIXEL | SNAP_NODE_PARENT | SNAP_NODE_ANCHORS | SNAP_OTHER_NODES, 0, nullptr, drag_selection);
bool single_axis = m->is_shift_pressed();
if (single_axis) {
@@ -3608,7 +3612,7 @@ void CanvasItemEditor::_draw_axis() {
}
void CanvasItemEditor::_draw_invisible_nodes_positions(Node *p_node, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) {
- ERR_FAIL_COND(!p_node);
+ ERR_FAIL_NULL(p_node);
Node *scene = EditorNode::get_singleton()->get_edited_scene();
if (p_node != scene && p_node->get_owner() != scene && !scene->is_editable_instance(p_node->get_owner())) {
@@ -3739,7 +3743,7 @@ void CanvasItemEditor::_draw_transform_message() {
}
void CanvasItemEditor::_draw_locks_and_groups(Node *p_node, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) {
- ERR_FAIL_COND(!p_node);
+ ERR_FAIL_NULL(p_node);
Node *scene = EditorNode::get_singleton()->get_edited_scene();
if (p_node != scene && p_node->get_owner() != scene && !scene->is_editable_instance(p_node->get_owner())) {
@@ -5050,7 +5054,7 @@ CanvasItemEditor::CanvasItemEditor() {
EditorRunBar::get_singleton()->call_deferred(SNAME("connect"), "play_pressed", callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(true));
EditorRunBar::get_singleton()->call_deferred(SNAME("connect"), "stop_pressed", callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(false));
- // Add some margin to the sides for better aesthetics.
+ // Add some margin to the sides for better esthetics.
// This prevents the first button's hover/pressed effect from "touching" the panel's border,
// which looks ugly.
MarginContainer *toolbar_margin = memnew(MarginContainer);
diff --git a/editor/plugins/control_editor_plugin.cpp b/editor/plugins/control_editor_plugin.cpp
index 2477d121c0..8401f6c0b6 100644
--- a/editor/plugins/control_editor_plugin.cpp
+++ b/editor/plugins/control_editor_plugin.cpp
@@ -817,7 +817,7 @@ void ControlEditorToolbar::_container_flags_selected(int p_flags, bool p_vertica
}
Vector2 ControlEditorToolbar::_position_to_anchor(const Control *p_control, Vector2 position) {
- ERR_FAIL_COND_V(!p_control, Vector2());
+ ERR_FAIL_NULL_V(p_control, Vector2());
Rect2 parent_rect = p_control->get_parent_anchorable_rect();
diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp
index 468278bd27..1d3ffc0e3a 100644
--- a/editor/plugins/curve_editor_plugin.cpp
+++ b/editor/plugins/curve_editor_plugin.cpp
@@ -1041,7 +1041,7 @@ bool EditorInspectorPluginCurve::can_handle(Object *p_object) {
void EditorInspectorPluginCurve::parse_begin(Object *p_object) {
Curve *curve = Object::cast_to<Curve>(p_object);
- ERR_FAIL_COND(!curve);
+ ERR_FAIL_NULL(curve);
Ref<Curve> c(curve);
CurveEditor *editor = memnew(CurveEditor);
diff --git a/editor/plugins/editor_debugger_plugin.cpp b/editor/plugins/editor_debugger_plugin.cpp
index eb1b2dcca7..9f495d5cd2 100644
--- a/editor/plugins/editor_debugger_plugin.cpp
+++ b/editor/plugins/editor_debugger_plugin.cpp
@@ -76,27 +76,27 @@ void EditorDebuggerSession::remove_session_tab(Control *p_tab) {
}
void EditorDebuggerSession::send_message(const String &p_message, const Array &p_args) {
- ERR_FAIL_COND_MSG(!debugger, "Plugin is not attached to debugger");
+ ERR_FAIL_NULL_MSG(debugger, "Plugin is not attached to debugger.");
debugger->send_message(p_message, p_args);
}
void EditorDebuggerSession::toggle_profiler(const String &p_profiler, bool p_enable, const Array &p_data) {
- ERR_FAIL_COND_MSG(!debugger, "Plugin is not attached to debugger");
+ ERR_FAIL_NULL_MSG(debugger, "Plugin is not attached to debugger.");
debugger->toggle_profiler(p_profiler, p_enable, p_data);
}
bool EditorDebuggerSession::is_breaked() {
- ERR_FAIL_COND_V_MSG(!debugger, false, "Plugin is not attached to debugger");
+ ERR_FAIL_NULL_V_MSG(debugger, false, "Plugin is not attached to debugger.");
return debugger->is_breaked();
}
bool EditorDebuggerSession::is_debuggable() {
- ERR_FAIL_COND_V_MSG(!debugger, false, "Plugin is not attached to debugger");
+ ERR_FAIL_NULL_V_MSG(debugger, false, "Plugin is not attached to debugger.");
return debugger->is_debuggable();
}
bool EditorDebuggerSession::is_active() {
- ERR_FAIL_COND_V_MSG(!debugger, false, "Plugin is not attached to debugger");
+ ERR_FAIL_NULL_V_MSG(debugger, false, "Plugin is not attached to debugger.");
return debugger->is_session_active();
}
@@ -121,7 +121,7 @@ void EditorDebuggerSession::_debugger_gone_away() {
}
EditorDebuggerSession::EditorDebuggerSession(ScriptEditorDebugger *p_debugger) {
- ERR_FAIL_COND(!p_debugger);
+ ERR_FAIL_NULL(p_debugger);
debugger = p_debugger;
debugger->connect("started", callable_mp(this, &EditorDebuggerSession::_started));
debugger->connect("stopped", callable_mp(this, &EditorDebuggerSession::_stopped));
diff --git a/editor/plugins/font_config_plugin.cpp b/editor/plugins/font_config_plugin.cpp
index 5dd5de224f..6de2549059 100644
--- a/editor/plugins/font_config_plugin.cpp
+++ b/editor/plugins/font_config_plugin.cpp
@@ -970,7 +970,7 @@ bool EditorInspectorPluginFontPreview::can_handle(Object *p_object) {
void EditorInspectorPluginFontPreview::parse_begin(Object *p_object) {
Font *fd = Object::cast_to<Font>(p_object);
- ERR_FAIL_COND(!fd);
+ ERR_FAIL_NULL(fd);
FontPreview *editor = memnew(FontPreview);
editor->set_data(fd);
diff --git a/editor/plugins/gizmos/decal_gizmo_plugin.cpp b/editor/plugins/gizmos/decal_gizmo_plugin.cpp
index 68206a7ee5..7572e1dcd5 100644
--- a/editor/plugins/gizmos/decal_gizmo_plugin.cpp
+++ b/editor/plugins/gizmos/decal_gizmo_plugin.cpp
@@ -30,7 +30,9 @@
#include "decal_gizmo_plugin.h"
+#include "editor/editor_node.h"
#include "editor/editor_settings.h"
+#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/plugins/gizmos/gizmo_3d_helper.h"
#include "editor/plugins/node_3d_editor_plugin.h"
@@ -40,6 +42,8 @@ DecalGizmoPlugin::DecalGizmoPlugin() {
helper.instantiate();
Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/decal", Color(0.6, 0.5, 1.0));
+ create_icon_material("decal_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoDecal"), EditorStringName(EditorIcons)));
+
create_material("decal_material", gizmo_color);
create_handle_material("handles");
@@ -124,7 +128,9 @@ void DecalGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
Vector<Vector3> handles = helper->box_get_handles(decal->get_size());
Ref<Material> material = get_material("decal_material", p_gizmo);
+ const Ref<Material> icon = get_material("decal_icon", p_gizmo);
p_gizmo->add_lines(lines, material);
+ p_gizmo->add_unscaled_billboard(icon, 0.05);
p_gizmo->add_handles(handles, get_material("handles"));
}
diff --git a/editor/plugins/gizmos/fog_volume_gizmo_plugin.cpp b/editor/plugins/gizmos/fog_volume_gizmo_plugin.cpp
index 8f7cbee405..931a738aa2 100644
--- a/editor/plugins/gizmos/fog_volume_gizmo_plugin.cpp
+++ b/editor/plugins/gizmos/fog_volume_gizmo_plugin.cpp
@@ -30,7 +30,9 @@
#include "fog_volume_gizmo_plugin.h"
+#include "editor/editor_node.h"
#include "editor/editor_settings.h"
+#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/plugins/node_3d_editor_plugin.h"
#include "scene/3d/fog_volume.h"
@@ -41,6 +43,8 @@ FogVolumeGizmoPlugin::FogVolumeGizmoPlugin() {
gizmo_color.a = 0.15;
create_material("shape_material_internal", gizmo_color);
+ create_icon_material("fog_volume_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoFogVolume"), EditorStringName(EditorIcons)));
+
create_handle_material("handles");
}
@@ -143,6 +147,8 @@ void FogVolumeGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
p_gizmo->add_lines(lines, material);
p_gizmo->add_collision_segments(lines);
+ const Ref<Material> icon = get_material("fog_volume_icon", p_gizmo);
+ p_gizmo->add_unscaled_billboard(icon, 0.05);
p_gizmo->add_handles(handles, handles_material);
}
}
diff --git a/editor/plugins/gizmos/lightmap_probe_gizmo_plugin.cpp b/editor/plugins/gizmos/lightmap_probe_gizmo_plugin.cpp
index 093beac6a2..420829515f 100644
--- a/editor/plugins/gizmos/lightmap_probe_gizmo_plugin.cpp
+++ b/editor/plugins/gizmos/lightmap_probe_gizmo_plugin.cpp
@@ -30,11 +30,15 @@
#include "lightmap_probe_gizmo_plugin.h"
+#include "editor/editor_node.h"
#include "editor/editor_settings.h"
+#include "editor/editor_string_names.h"
#include "editor/plugins/node_3d_editor_plugin.h"
#include "scene/3d/lightmap_probe.h"
LightmapProbeGizmoPlugin::LightmapProbeGizmoPlugin() {
+ create_icon_material("lightmap_probe_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoLightmapProbe"), EditorStringName(EditorIcons)));
+
Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/lightprobe_lines", Color(0.5, 0.6, 1));
gizmo_color.a = 0.3;
@@ -111,5 +115,8 @@ void LightmapProbeGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
}
}
+ const Ref<Material> icon = get_material("lightmap_probe_icon", p_gizmo);
+
p_gizmo->add_lines(lines, material_lines);
+ p_gizmo->add_unscaled_billboard(icon, 0.05);
}
diff --git a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp
index e86ce440ee..c7d7cc7918 100644
--- a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp
@@ -127,12 +127,12 @@ void GPUParticlesCollisionSDF3DEditorPlugin::bake_func_begin(int p_steps) {
}
void GPUParticlesCollisionSDF3DEditorPlugin::bake_func_step(int p_step, const String &p_description) {
- ERR_FAIL_COND(tmp_progress == nullptr);
+ ERR_FAIL_NULL(tmp_progress);
tmp_progress->step(p_description, p_step, false);
}
void GPUParticlesCollisionSDF3DEditorPlugin::bake_func_end() {
- ERR_FAIL_COND(tmp_progress == nullptr);
+ ERR_FAIL_NULL(tmp_progress);
memdelete(tmp_progress);
tmp_progress = nullptr;
}
diff --git a/editor/plugins/lightmap_gi_editor_plugin.cpp b/editor/plugins/lightmap_gi_editor_plugin.cpp
index c09dbeb1cc..7664739480 100644
--- a/editor/plugins/lightmap_gi_editor_plugin.cpp
+++ b/editor/plugins/lightmap_gi_editor_plugin.cpp
@@ -143,7 +143,7 @@ EditorProgress *LightmapGIEditorPlugin::tmp_progress = nullptr;
bool LightmapGIEditorPlugin::bake_func_step(float p_progress, const String &p_description, void *, bool p_refresh) {
if (!tmp_progress) {
tmp_progress = memnew(EditorProgress("bake_lightmaps", TTR("Bake Lightmaps"), 1000, false));
- ERR_FAIL_COND_V(tmp_progress == nullptr, false);
+ ERR_FAIL_NULL_V(tmp_progress, false);
}
return tmp_progress->step(p_description, p_progress * 1000, p_refresh);
}
diff --git a/editor/plugins/mesh_library_editor_plugin.cpp b/editor/plugins/mesh_library_editor_plugin.cpp
index 79331098fb..e6f0e65e40 100644
--- a/editor/plugins/mesh_library_editor_plugin.cpp
+++ b/editor/plugins/mesh_library_editor_plugin.cpp
@@ -211,7 +211,7 @@ void MeshLibraryEditor::_import_scene_cbk(const String &p_str) {
ERR_FAIL_COND(ps.is_null());
Node *scene = ps->instantiate();
- ERR_FAIL_COND_MSG(!scene, "Cannot create an instance from PackedScene '" + p_str + "'.");
+ ERR_FAIL_NULL_MSG(scene, "Cannot create an instance from PackedScene '" + p_str + "'.");
_import_scene(scene, mesh_library, option == MENU_OPTION_UPDATE_FROM_SCENE, apply_xforms);
diff --git a/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp b/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp
index 4f90061797..bbbde52935 100644
--- a/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp
+++ b/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp
@@ -82,7 +82,7 @@ void NavigationObstacle3DEditor::_menu_option(int p_option) {
}
void NavigationObstacle3DEditor::_wip_close() {
- ERR_FAIL_COND_MSG(!obstacle_node, "Edited NavigationObstacle3D is not valid.");
+ ERR_FAIL_NULL_MSG(obstacle_node, "Edited NavigationObstacle3D is not valid.");
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Set NavigationObstacle3D Vertices"));
undo_redo->add_undo_method(obstacle_node, "set_vertices", obstacle_node->get_vertices());
@@ -344,12 +344,12 @@ EditorPlugin::AfterGUIInput NavigationObstacle3DEditor::forward_3d_gui_input(Cam
}
PackedVector2Array NavigationObstacle3DEditor::_get_polygon() {
- ERR_FAIL_COND_V_MSG(!obstacle_node, PackedVector2Array(), "Edited object is not valid.");
+ ERR_FAIL_NULL_V_MSG(obstacle_node, PackedVector2Array(), "Edited object is not valid.");
return PackedVector2Array(obstacle_node->call("get_polygon"));
}
void NavigationObstacle3DEditor::_set_polygon(PackedVector2Array p_poly) {
- ERR_FAIL_COND_MSG(!obstacle_node, "Edited object is not valid.");
+ ERR_FAIL_NULL_MSG(obstacle_node, "Edited object is not valid.");
obstacle_node->call("set_polygon", p_poly);
}
diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp
index d28a93d9e5..0d3000a318 100644
--- a/editor/plugins/node_3d_editor_gizmos.cpp
+++ b/editor/plugins/node_3d_editor_gizmos.cpp
@@ -41,7 +41,7 @@
#define HANDLE_HALF_SIZE 9.5
bool EditorNode3DGizmo::is_editable() const {
- ERR_FAIL_COND_V(!spatial_node, false);
+ ERR_FAIL_NULL_V(spatial_node, false);
Node *edited_root = spatial_node->get_tree()->get_edited_scene_root();
if (spatial_node == edited_root) {
return true;
@@ -77,7 +77,7 @@ void EditorNode3DGizmo::clear() {
void EditorNode3DGizmo::redraw() {
if (!GDVIRTUAL_CALL(_redraw)) {
- ERR_FAIL_COND(!gizmo_plugin);
+ ERR_FAIL_NULL(gizmo_plugin);
gizmo_plugin->redraw(this);
}
@@ -92,7 +92,7 @@ String EditorNode3DGizmo::get_handle_name(int p_id, bool p_secondary) const {
return ret;
}
- ERR_FAIL_COND_V(!gizmo_plugin, "");
+ ERR_FAIL_NULL_V(gizmo_plugin, "");
return gizmo_plugin->get_handle_name(this, p_id, p_secondary);
}
@@ -102,7 +102,7 @@ bool EditorNode3DGizmo::is_handle_highlighted(int p_id, bool p_secondary) const
return success;
}
- ERR_FAIL_COND_V(!gizmo_plugin, false);
+ ERR_FAIL_NULL_V(gizmo_plugin, false);
return gizmo_plugin->is_handle_highlighted(this, p_id, p_secondary);
}
@@ -112,7 +112,7 @@ Variant EditorNode3DGizmo::get_handle_value(int p_id, bool p_secondary) const {
return value;
}
- ERR_FAIL_COND_V(!gizmo_plugin, Variant());
+ ERR_FAIL_NULL_V(gizmo_plugin, Variant());
return gizmo_plugin->get_handle_value(this, p_id, p_secondary);
}
@@ -121,7 +121,7 @@ void EditorNode3DGizmo::begin_handle_action(int p_id, bool p_secondary) {
return;
}
- ERR_FAIL_COND(!gizmo_plugin);
+ ERR_FAIL_NULL(gizmo_plugin);
gizmo_plugin->begin_handle_action(this, p_id, p_secondary);
}
@@ -130,7 +130,7 @@ void EditorNode3DGizmo::set_handle(int p_id, bool p_secondary, Camera3D *p_camer
return;
}
- ERR_FAIL_COND(!gizmo_plugin);
+ ERR_FAIL_NULL(gizmo_plugin);
gizmo_plugin->set_handle(this, p_id, p_secondary, p_camera, p_point);
}
@@ -139,7 +139,7 @@ void EditorNode3DGizmo::commit_handle(int p_id, bool p_secondary, const Variant
return;
}
- ERR_FAIL_COND(!gizmo_plugin);
+ ERR_FAIL_NULL(gizmo_plugin);
gizmo_plugin->commit_handle(this, p_id, p_secondary, p_restore, p_cancel);
}
@@ -149,7 +149,7 @@ int EditorNode3DGizmo::subgizmos_intersect_ray(Camera3D *p_camera, const Vector2
return id;
}
- ERR_FAIL_COND_V(!gizmo_plugin, -1);
+ ERR_FAIL_NULL_V(gizmo_plugin, -1);
return gizmo_plugin->subgizmos_intersect_ray(this, p_camera, p_point);
}
@@ -164,7 +164,7 @@ Vector<int> EditorNode3DGizmo::subgizmos_intersect_frustum(const Camera3D *p_cam
return ret;
}
- ERR_FAIL_COND_V(!gizmo_plugin, Vector<int>());
+ ERR_FAIL_NULL_V(gizmo_plugin, Vector<int>());
return gizmo_plugin->subgizmos_intersect_frustum(this, p_camera, p_frustum);
}
@@ -174,7 +174,7 @@ Transform3D EditorNode3DGizmo::get_subgizmo_transform(int p_id) const {
return ret;
}
- ERR_FAIL_COND_V(!gizmo_plugin, Transform3D());
+ ERR_FAIL_NULL_V(gizmo_plugin, Transform3D());
return gizmo_plugin->get_subgizmo_transform(this, p_id);
}
@@ -183,7 +183,7 @@ void EditorNode3DGizmo::set_subgizmo_transform(int p_id, Transform3D p_transform
return;
}
- ERR_FAIL_COND(!gizmo_plugin);
+ ERR_FAIL_NULL(gizmo_plugin);
gizmo_plugin->set_subgizmo_transform(this, p_id, p_transform);
}
@@ -198,7 +198,7 @@ void EditorNode3DGizmo::commit_subgizmos(const Vector<int> &p_ids, const Vector<
return;
}
- ERR_FAIL_COND(!gizmo_plugin);
+ ERR_FAIL_NULL(gizmo_plugin);
gizmo_plugin->commit_subgizmos(this, p_ids, p_restore, p_cancel);
}
@@ -224,7 +224,7 @@ void EditorNode3DGizmo::Instance::create_instance(Node3D *p_base, bool p_hidden)
}
void EditorNode3DGizmo::add_mesh(const Ref<Mesh> &p_mesh, const Ref<Material> &p_material, const Transform3D &p_xform, const Ref<SkinReference> &p_skin_reference) {
- ERR_FAIL_COND(!spatial_node);
+ ERR_FAIL_NULL(spatial_node);
ERR_FAIL_COND_MSG(!p_mesh.is_valid(), "EditorNode3DGizmo.add_mesh() requires a valid Mesh resource.");
Instance ins;
@@ -253,7 +253,7 @@ void EditorNode3DGizmo::add_vertices(const Vector<Vector3> &p_vertices, const Re
return;
}
- ERR_FAIL_COND(!spatial_node);
+ ERR_FAIL_NULL(spatial_node);
Instance ins;
Ref<ArrayMesh> mesh = memnew(ArrayMesh);
@@ -300,7 +300,7 @@ void EditorNode3DGizmo::add_vertices(const Vector<Vector3> &p_vertices, const Re
}
void EditorNode3DGizmo::add_unscaled_billboard(const Ref<Material> &p_material, real_t p_scale, const Color &p_modulate) {
- ERR_FAIL_COND(!spatial_node);
+ ERR_FAIL_NULL(spatial_node);
Instance ins;
Vector<Vector3> vs = {
@@ -454,7 +454,7 @@ void EditorNode3DGizmo::add_handles(const Vector<Vector3> &p_handles, const Ref<
}
void EditorNode3DGizmo::add_solid_box(const Ref<Material> &p_material, Vector3 p_size, Vector3 p_position, const Transform3D &p_xform) {
- ERR_FAIL_COND(!spatial_node);
+ ERR_FAIL_NULL(spatial_node);
BoxMesh box_mesh;
box_mesh.set_size(p_size);
@@ -475,7 +475,7 @@ void EditorNode3DGizmo::add_solid_box(const Ref<Material> &p_material, Vector3 p
}
bool EditorNode3DGizmo::intersect_frustum(const Camera3D *p_camera, const Vector<Plane> &p_frustum) {
- ERR_FAIL_COND_V(!spatial_node, false);
+ ERR_FAIL_NULL_V(spatial_node, false);
ERR_FAIL_COND_V(!valid, false);
if (hidden && !gizmo_plugin->is_selectable_when_hidden()) {
@@ -556,7 +556,7 @@ void EditorNode3DGizmo::handles_intersect_ray(Camera3D *p_camera, const Vector2
r_id = -1;
r_secondary = false;
- ERR_FAIL_COND(!spatial_node);
+ ERR_FAIL_NULL(spatial_node);
ERR_FAIL_COND(!valid);
if (hidden) {
@@ -615,7 +615,7 @@ void EditorNode3DGizmo::handles_intersect_ray(Camera3D *p_camera, const Vector2
}
bool EditorNode3DGizmo::intersect_ray(Camera3D *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal) {
- ERR_FAIL_COND_V(!spatial_node, false);
+ ERR_FAIL_NULL_V(spatial_node, false);
ERR_FAIL_COND_V(!valid, false);
if (hidden && !gizmo_plugin->is_selectable_when_hidden()) {
@@ -739,7 +739,7 @@ bool EditorNode3DGizmo::intersect_ray(Camera3D *p_camera, const Point2 &p_point,
bool EditorNode3DGizmo::is_subgizmo_selected(int p_id) const {
Node3DEditor *ed = Node3DEditor::get_singleton();
- ERR_FAIL_COND_V(!ed, false);
+ ERR_FAIL_NULL_V(ed, false);
return ed->is_current_selected_gizmo(this) && ed->is_subgizmo_selected(p_id);
}
@@ -747,7 +747,7 @@ Vector<int> EditorNode3DGizmo::get_subgizmo_selection() const {
Vector<int> ret;
Node3DEditor *ed = Node3DEditor::get_singleton();
- ERR_FAIL_COND_V(!ed, ret);
+ ERR_FAIL_NULL_V(ed, ret);
if (ed->is_current_selected_gizmo(this)) {
ret = ed->get_subgizmo_selection();
@@ -757,7 +757,7 @@ Vector<int> EditorNode3DGizmo::get_subgizmo_selection() const {
}
void EditorNode3DGizmo::create() {
- ERR_FAIL_COND(!spatial_node);
+ ERR_FAIL_NULL(spatial_node);
ERR_FAIL_COND(valid);
valid = true;
@@ -769,7 +769,7 @@ void EditorNode3DGizmo::create() {
}
void EditorNode3DGizmo::transform() {
- ERR_FAIL_COND(!spatial_node);
+ ERR_FAIL_NULL(spatial_node);
ERR_FAIL_COND(!valid);
for (int i = 0; i < instances.size(); i++) {
RS::get_singleton()->instance_set_transform(instances[i].instance, spatial_node->get_global_transform() * instances[i].xform);
@@ -778,7 +778,7 @@ void EditorNode3DGizmo::transform() {
void EditorNode3DGizmo::free() {
ERR_FAIL_NULL(RenderingServer::get_singleton());
- ERR_FAIL_COND(!spatial_node);
+ ERR_FAIL_NULL(spatial_node);
ERR_FAIL_COND(!valid);
for (int i = 0; i < instances.size(); i++) {
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index b2229442e3..c05a6d1392 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -1460,14 +1460,10 @@ Transform3D Node3DEditorViewport::_compute_transform(TransformMode p_mode, const
}
if (p_local) {
- p_motion = p_original.basis.xform(p_motion);
+ return p_original_local.translated_local(p_motion);
}
- // Apply translation
- Transform3D t = p_original;
- t.origin += p_motion;
-
- return t;
+ return p_original.translated(p_motion);
}
case TRANSFORM_ROTATE: {
Transform3D r;
@@ -3161,7 +3157,7 @@ void Node3DEditorViewport::_draw() {
get_editor_theme_icon(SNAME("ViewportSpeed")),
get_theme_font(SNAME("font"), SNAME("Label")),
get_theme_font_size(SNAME("font_size"), SNAME("Label")),
- vformat("%s u/s", String::num(freelook_speed).pad_decimals(precision)),
+ vformat("%s m/s", String::num(freelook_speed).pad_decimals(precision)),
Color(1.0, 0.95, 0.7));
}
@@ -3184,7 +3180,7 @@ void Node3DEditorViewport::_draw() {
get_editor_theme_icon(SNAME("ViewportZoom")),
get_theme_font(SNAME("font"), SNAME("Label")),
get_theme_font_size(SNAME("font_size"), SNAME("Label")),
- vformat("%s u", String::num(cursor.distance).pad_decimals(precision)),
+ vformat("%s m", String::num(cursor.distance).pad_decimals(precision)),
Color(0.7, 0.95, 1.0));
}
}
@@ -7499,7 +7495,7 @@ void Node3DEditor::_add_sun_to_scene(bool p_already_added_environment) {
SceneTreeDock::get_singleton()->add_root_node(memnew(Node3D));
base = get_tree()->get_edited_scene_root();
}
- ERR_FAIL_COND(!base);
+ ERR_FAIL_NULL(base);
Node *new_sun = preview_sun->duplicate();
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
@@ -7528,7 +7524,7 @@ void Node3DEditor::_add_environment_to_scene(bool p_already_added_sun) {
SceneTreeDock::get_singleton()->add_root_node(memnew(Node3D));
base = get_tree()->get_edited_scene_root();
}
- ERR_FAIL_COND(!base);
+ ERR_FAIL_NULL(base);
WorldEnvironment *new_env = memnew(WorldEnvironment);
new_env->set_environment(preview_environment->get_environment()->duplicate(true));
@@ -8201,7 +8197,7 @@ Node3DEditor::Node3DEditor() {
camera_override_viewport_id = 0;
- // Add some margin to the sides for better aesthetics.
+ // Add some margin to the sides for better esthetics.
// This prevents the first button's hover/pressed effect from "touching" the panel's border,
// which looks ugly.
MarginContainer *toolbar_margin = memnew(MarginContainer);
diff --git a/editor/plugins/polygon_3d_editor_plugin.cpp b/editor/plugins/polygon_3d_editor_plugin.cpp
index 1fe1e1089e..41672822b2 100644
--- a/editor/plugins/polygon_3d_editor_plugin.cpp
+++ b/editor/plugins/polygon_3d_editor_plugin.cpp
@@ -95,7 +95,7 @@ void Polygon3DEditor::_menu_option(int p_option) {
void Polygon3DEditor::_wip_close() {
Object *obj = node_resource.is_valid() ? (Object *)node_resource.ptr() : node;
- ERR_FAIL_COND_MSG(!obj, "Edited object is not valid.");
+ ERR_FAIL_NULL_MSG(obj, "Edited object is not valid.");
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Create Polygon3D"));
undo_redo->add_undo_method(obj, "set_polygon", obj->call("get_polygon"));
@@ -349,7 +349,7 @@ EditorPlugin::AfterGUIInput Polygon3DEditor::forward_3d_gui_input(Camera3D *p_ca
float Polygon3DEditor::_get_depth() {
Object *obj = node_resource.is_valid() ? (Object *)node_resource.ptr() : node;
- ERR_FAIL_COND_V_MSG(!obj, 0.0f, "Edited object is not valid.");
+ ERR_FAIL_NULL_V_MSG(obj, 0.0f, "Edited object is not valid.");
if (bool(obj->call("_has_editable_3d_polygon_no_depth"))) {
return 0.0f;
@@ -360,13 +360,13 @@ float Polygon3DEditor::_get_depth() {
PackedVector2Array Polygon3DEditor::_get_polygon() {
Object *obj = node_resource.is_valid() ? (Object *)node_resource.ptr() : node;
- ERR_FAIL_COND_V_MSG(!obj, PackedVector2Array(), "Edited object is not valid.");
+ ERR_FAIL_NULL_V_MSG(obj, PackedVector2Array(), "Edited object is not valid.");
return PackedVector2Array(obj->call("get_polygon"));
}
void Polygon3DEditor::_set_polygon(PackedVector2Array p_poly) {
Object *obj = node_resource.is_valid() ? (Object *)node_resource.ptr() : node;
- ERR_FAIL_COND_MSG(!obj, "Edited object is not valid.");
+ ERR_FAIL_NULL_MSG(obj, "Edited object is not valid.");
obj->call("set_polygon", p_poly);
}
diff --git a/editor/plugins/resource_preloader_editor_plugin.cpp b/editor/plugins/resource_preloader_editor_plugin.cpp
index e352fd27ad..ee7ad739b8 100644
--- a/editor/plugins/resource_preloader_editor_plugin.cpp
+++ b/editor/plugins/resource_preloader_editor_plugin.cpp
@@ -225,7 +225,7 @@ void ResourcePreloaderEditor::_cell_button_pressed(Object *p_item, int p_column,
}
TreeItem *item = Object::cast_to<TreeItem>(p_item);
- ERR_FAIL_COND(!item);
+ ERR_FAIL_NULL(item);
if (p_id == BUTTON_OPEN_SCENE) {
String rpath = item->get_text(p_column);
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 89d9b0d023..901ac91a46 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -2376,7 +2376,7 @@ bool ScriptEditor::edit(const Ref<Resource> &p_resource, int p_line, int p_col,
break;
}
}
- ERR_FAIL_COND_V(!se, false);
+ ERR_FAIL_NULL_V(se, false);
se->set_edited_resource(p_resource);
@@ -2695,7 +2695,7 @@ void ScriptEditor::_editor_stop() {
}
void ScriptEditor::_add_callback(Object *p_obj, const String &p_function, const PackedStringArray &p_args) {
- ERR_FAIL_COND(!p_obj);
+ ERR_FAIL_NULL(p_obj);
Ref<Script> scr = p_obj->get_script();
ERR_FAIL_COND(!scr.is_valid());
diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp
index 764c1a94c8..4a44cea5ba 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_3d_editor_plugin.cpp
@@ -1122,7 +1122,7 @@ bool EditorInspectorPluginSkeleton::can_handle(Object *p_object) {
void EditorInspectorPluginSkeleton::parse_begin(Object *p_object) {
Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_object);
- ERR_FAIL_COND(!skeleton);
+ ERR_FAIL_NULL(skeleton);
skel_editor = memnew(Skeleton3DEditor(this, skeleton));
add_custom_control(skel_editor);
@@ -1244,7 +1244,7 @@ int Skeleton3DGizmoPlugin::get_priority() const {
int Skeleton3DGizmoPlugin::subgizmos_intersect_ray(const EditorNode3DGizmo *p_gizmo, Camera3D *p_camera, const Vector2 &p_point) const {
Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_gizmo->get_node_3d());
- ERR_FAIL_COND_V(!skeleton, -1);
+ ERR_FAIL_NULL_V(skeleton, -1);
Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();
@@ -1285,14 +1285,14 @@ int Skeleton3DGizmoPlugin::subgizmos_intersect_ray(const EditorNode3DGizmo *p_gi
Transform3D Skeleton3DGizmoPlugin::get_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id) const {
Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_gizmo->get_node_3d());
- ERR_FAIL_COND_V(!skeleton, Transform3D());
+ ERR_FAIL_NULL_V(skeleton, Transform3D());
return skeleton->get_bone_global_pose(p_id);
}
void Skeleton3DGizmoPlugin::set_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id, Transform3D p_transform) {
Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_gizmo->get_node_3d());
- ERR_FAIL_COND(!skeleton);
+ ERR_FAIL_NULL(skeleton);
// Prepare for global to local.
Transform3D original_to_local;
@@ -1321,7 +1321,7 @@ void Skeleton3DGizmoPlugin::set_subgizmo_transform(const EditorNode3DGizmo *p_gi
void Skeleton3DGizmoPlugin::commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel) {
Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_gizmo->get_node_3d());
- ERR_FAIL_COND(!skeleton);
+ ERR_FAIL_NULL(skeleton);
Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();
Node3DEditor *ne = Node3DEditor::get_singleton();
diff --git a/editor/plugins/sprite_2d_editor_plugin.cpp b/editor/plugins/sprite_2d_editor_plugin.cpp
index 1363669928..e90609cd2f 100644
--- a/editor/plugins/sprite_2d_editor_plugin.cpp
+++ b/editor/plugins/sprite_2d_editor_plugin.cpp
@@ -74,7 +74,7 @@ Vector<Vector2> expand(const Vector<Vector2> &points, const Rect2i &rect, float
ClipperLib::PolyNode *p = solution.GetFirst();
- ERR_FAIL_COND_V(!p, points);
+ ERR_FAIL_NULL_V(p, points);
while (p->IsHole()) {
p = p->GetNext();
@@ -97,7 +97,7 @@ Vector<Vector2> expand(const Vector<Vector2> &points, const Rect2i &rect, float
Vector<Vector2> outPoints;
ClipperLib::PolyNode *p2 = out.GetFirst();
- ERR_FAIL_COND_V(!p2, points);
+ ERR_FAIL_NULL_V(p2, points);
while (p2->IsHole()) {
p2 = p2->GetNext();
diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp
index 54b72b6b3e..3134c0b951 100644
--- a/editor/plugins/sprite_frames_editor_plugin.cpp
+++ b/editor/plugins/sprite_frames_editor_plugin.cpp
@@ -855,7 +855,7 @@ void SpriteFramesEditor::_animation_selected() {
}
TreeItem *selected = animations->get_selected();
- ERR_FAIL_COND(!selected);
+ ERR_FAIL_NULL(selected);
edited_anim = selected->get_text(0);
if (animated_sprite) {
diff --git a/editor/plugins/texture_editor_plugin.cpp b/editor/plugins/texture_editor_plugin.cpp
index 74a03c1e97..ec5785e605 100644
--- a/editor/plugins/texture_editor_plugin.cpp
+++ b/editor/plugins/texture_editor_plugin.cpp
@@ -167,7 +167,7 @@ void EditorInspectorPluginTexture::parse_begin(Object *p_object) {
Ref<Image> image(Object::cast_to<Image>(p_object));
texture = ImageTexture::create_from_image(image);
- ERR_FAIL_COND_MSG(texture == nullptr, "Failed to create the texture from an invalid image.");
+ ERR_FAIL_NULL_MSG(texture, "Failed to create the texture from an invalid image.");
}
add_custom_control(memnew(TexturePreview(texture, true)));
diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp
index a63c2f5742..19dd2d51c0 100644
--- a/editor/plugins/tiles/tile_data_editors.cpp
+++ b/editor/plugins/tiles/tile_data_editors.cpp
@@ -948,19 +948,19 @@ GenericTilePolygonEditor::GenericTilePolygonEditor() {
}
void TileDataDefaultEditor::_property_value_changed(StringName p_property, Variant p_value, StringName p_field) {
- ERR_FAIL_COND(!dummy_object);
+ ERR_FAIL_NULL(dummy_object);
dummy_object->set(p_property, p_value);
emit_signal(SNAME("needs_redraw"));
}
Variant TileDataDefaultEditor::_get_painted_value() {
- ERR_FAIL_COND_V(!dummy_object, Variant());
+ ERR_FAIL_NULL_V(dummy_object, Variant());
return dummy_object->get(property);
}
void TileDataDefaultEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) {
TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile);
- ERR_FAIL_COND(!tile_data);
+ ERR_FAIL_NULL(tile_data);
Variant value = tile_data->get(property);
dummy_object->set(property, value);
if (property_editor) {
@@ -970,13 +970,13 @@ void TileDataDefaultEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_at
void TileDataDefaultEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) {
TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile);
- ERR_FAIL_COND(!tile_data);
+ ERR_FAIL_NULL(tile_data);
tile_data->set(property, p_value);
}
Variant TileDataDefaultEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) {
TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile);
- ERR_FAIL_COND_V(!tile_data, Variant());
+ ERR_FAIL_NULL_V(tile_data, Variant());
return tile_data->get(property);
}
@@ -1186,7 +1186,7 @@ void TileDataDefaultEditor::forward_painting_alternatives_gui_input(TileAtlasVie
void TileDataDefaultEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) {
TileData *tile_data = _get_tile_data(p_cell);
- ERR_FAIL_COND(!tile_data);
+ ERR_FAIL_NULL(tile_data);
bool valid;
Variant value = tile_data->get(property, &valid);
@@ -1315,7 +1315,7 @@ TileDataDefaultEditor::~TileDataDefaultEditor() {
void TileDataTextureOriginEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) {
TileData *tile_data = _get_tile_data(p_cell);
- ERR_FAIL_COND(!tile_data);
+ ERR_FAIL_NULL(tile_data);
Vector2i tile_set_tile_size = tile_set->get_tile_size();
Color color = Color(1.0, 1.0, 1.0);
@@ -1349,7 +1349,7 @@ void TileDataTextureOriginEditor::draw_over_tile(CanvasItem *p_canvas_item, Tran
void TileDataPositionEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) {
TileData *tile_data = _get_tile_data(p_cell);
- ERR_FAIL_COND(!tile_data);
+ ERR_FAIL_NULL(tile_data);
bool valid;
Variant value = tile_data->get(property, &valid);
@@ -1370,7 +1370,7 @@ void TileDataPositionEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform
void TileDataYSortEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) {
TileData *tile_data = _get_tile_data(p_cell);
- ERR_FAIL_COND(!tile_data);
+ ERR_FAIL_NULL(tile_data);
Color color = Color(1.0, 1.0, 1.0);
if (p_selected) {
@@ -1397,7 +1397,7 @@ void TileDataYSortEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D
void TileDataOcclusionShapeEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) {
TileData *tile_data = _get_tile_data(p_cell);
- ERR_FAIL_COND(!tile_data);
+ ERR_FAIL_NULL(tile_data);
Color grid_color = EDITOR_GET("editors/tiles_editor/grid_color");
Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0);
@@ -1429,7 +1429,7 @@ Variant TileDataOcclusionShapeEditor::_get_painted_value() {
void TileDataOcclusionShapeEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) {
TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile);
- ERR_FAIL_COND(!tile_data);
+ ERR_FAIL_NULL(tile_data);
Ref<OccluderPolygon2D> occluder_polygon = tile_data->get_occluder(occlusion_layer);
polygon_editor->clear_polygons();
@@ -1441,7 +1441,7 @@ void TileDataOcclusionShapeEditor::_set_painted_value(TileSetAtlasSource *p_tile
void TileDataOcclusionShapeEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) {
TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile);
- ERR_FAIL_COND(!tile_data);
+ ERR_FAIL_NULL(tile_data);
Ref<OccluderPolygon2D> occluder_polygon = p_value;
tile_data->set_occluder(occlusion_layer, occluder_polygon);
@@ -1450,7 +1450,7 @@ void TileDataOcclusionShapeEditor::_set_value(TileSetAtlasSource *p_tile_set_atl
Variant TileDataOcclusionShapeEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) {
TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile);
- ERR_FAIL_COND_V(!tile_data, Variant());
+ ERR_FAIL_NULL_V(tile_data, Variant());
return tile_data->get_occluder(occlusion_layer);
}
@@ -1571,7 +1571,7 @@ Variant TileDataCollisionEditor::_get_painted_value() {
void TileDataCollisionEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) {
TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile);
- ERR_FAIL_COND(!tile_data);
+ ERR_FAIL_NULL(tile_data);
polygon_editor->clear_polygons();
for (int i = 0; i < tile_data->get_collision_polygons_count(physics_layer); i++) {
@@ -1597,7 +1597,7 @@ void TileDataCollisionEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_
void TileDataCollisionEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) {
TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile);
- ERR_FAIL_COND(!tile_data);
+ ERR_FAIL_NULL(tile_data);
Dictionary dict = p_value;
tile_data->set_constant_linear_velocity(physics_layer, dict["linear_velocity"]);
@@ -1616,7 +1616,7 @@ void TileDataCollisionEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_so
Variant TileDataCollisionEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) {
TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile);
- ERR_FAIL_COND_V(!tile_data, Variant());
+ ERR_FAIL_NULL_V(tile_data, Variant());
Dictionary dict;
dict["linear_velocity"] = tile_data->get_constant_linear_velocity(physics_layer);
@@ -1717,7 +1717,7 @@ TileDataCollisionEditor::~TileDataCollisionEditor() {
void TileDataCollisionEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) {
TileData *tile_data = _get_tile_data(p_cell);
- ERR_FAIL_COND(!tile_data);
+ ERR_FAIL_NULL(tile_data);
// Draw all shapes.
Vector<Color> color;
@@ -2767,7 +2767,7 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
void TileDataTerrainsEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) {
TileData *tile_data = _get_tile_data(p_cell);
- ERR_FAIL_COND(!tile_data);
+ ERR_FAIL_NULL(tile_data);
tile_set->draw_terrains(p_canvas_item, p_transform, tile_data);
}
@@ -2835,7 +2835,7 @@ Variant TileDataNavigationEditor::_get_painted_value() {
void TileDataNavigationEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) {
TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile);
- ERR_FAIL_COND(!tile_data);
+ ERR_FAIL_NULL(tile_data);
Ref<NavigationPolygon> nav_polygon = tile_data->get_navigation_polygon(navigation_layer);
polygon_editor->clear_polygons();
@@ -2849,7 +2849,7 @@ void TileDataNavigationEditor::_set_painted_value(TileSetAtlasSource *p_tile_set
void TileDataNavigationEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) {
TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile);
- ERR_FAIL_COND(!tile_data);
+ ERR_FAIL_NULL(tile_data);
Ref<NavigationPolygon> nav_polygon = p_value;
tile_data->set_navigation_polygon(navigation_layer, nav_polygon);
@@ -2858,7 +2858,7 @@ void TileDataNavigationEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_s
Variant TileDataNavigationEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) {
TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile);
- ERR_FAIL_COND_V(!tile_data, Variant());
+ ERR_FAIL_NULL_V(tile_data, Variant());
return tile_data->get_navigation_polygon(navigation_layer);
}
@@ -2893,7 +2893,7 @@ TileDataNavigationEditor::TileDataNavigationEditor() {
void TileDataNavigationEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) {
TileData *tile_data = _get_tile_data(p_cell);
- ERR_FAIL_COND(!tile_data);
+ ERR_FAIL_NULL(tile_data);
// Draw all shapes.
RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform);
diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp
index 21e532b22a..d5011380d3 100644
--- a/editor/plugins/tiles/tile_map_editor.cpp
+++ b/editor/plugins/tiles/tile_map_editor.cpp
@@ -367,7 +367,7 @@ void TileMapEditorTilesPlugin::_update_atlas_view() {
int source_id = sources_list->get_item_metadata(sources_list->get_current());
TileSetSource *source = *tile_set->get_source(source_id);
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
- ERR_FAIL_COND(!atlas_source);
+ ERR_FAIL_NULL(atlas_source);
tile_atlas_view->set_atlas_source(*tile_map->get_tileset(), atlas_source, source_id);
TilesEditorUtils::get_singleton()->synchronize_atlas_view(tile_atlas_view);
@@ -388,7 +388,7 @@ void TileMapEditorTilesPlugin::_update_scenes_collection_view() {
int source_id = sources_list->get_item_metadata(sources_list->get_current());
TileSetSource *source = *tile_set->get_source(source_id);
TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source);
- ERR_FAIL_COND(!scenes_collection_source);
+ ERR_FAIL_NULL(scenes_collection_source);
// Clear the list.
scene_tiles_list->clear();
@@ -448,7 +448,7 @@ void TileMapEditorTilesPlugin::_scenes_list_multi_selected(int p_index, bool p_s
int source_id = sources_list->get_item_metadata(sources_list->get_current());
TileSetSource *source = *tile_set->get_source(source_id);
TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source);
- ERR_FAIL_COND(!scenes_collection_source);
+ ERR_FAIL_NULL(scenes_collection_source);
TileMapCell selected = TileMapCell(source_id, Vector2i(), scene_id);
@@ -1028,7 +1028,7 @@ TileMapCell TileMapEditorTilesPlugin::_pick_random_tile(Ref<TileMapPattern> p_pa
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
if (atlas_source) {
TileData *tile_data = atlas_source->get_tile_data(atlas_coords, alternative_tile);
- ERR_FAIL_COND_V(!tile_data, TileMapCell());
+ ERR_FAIL_NULL_V(tile_data, TileMapCell());
sum += tile_data->get_probability();
} else {
sum += 1.0;
@@ -3735,7 +3735,7 @@ void TileMapEditor::_update_bottom_panel() {
}
Vector<Vector2i> TileMapEditor::get_line(TileMap *p_tile_map, Vector2i p_from_cell, Vector2i p_to_cell) {
- ERR_FAIL_COND_V(!p_tile_map, Vector<Vector2i>());
+ ERR_FAIL_NULL_V(p_tile_map, Vector<Vector2i>());
Ref<TileSet> tile_set = p_tile_map->get_tileset();
ERR_FAIL_COND_V(!tile_set.is_valid(), Vector<Vector2i>());
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index a2e4c4a784..1485ee9115 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -295,7 +295,7 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_set(const StringName &p_na
bool valid = false;
TileData *tile_data = tile_set_atlas_source->get_tile_data(coords, alternative);
- ERR_FAIL_COND_V(!tile_data, false);
+ ERR_FAIL_NULL_V(tile_data, false);
tile_data->set(p_name, p_value, &valid);
any_valid |= valid;
@@ -383,7 +383,7 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_get(const StringName &p_na
const int &alternative = E.alternative;
TileData *tile_data = tile_set_atlas_source->get_tile_data(coords, alternative);
- ERR_FAIL_COND_V(!tile_data, false);
+ ERR_FAIL_NULL_V(tile_data, false);
bool valid = false;
r_ret = tile_data->get(p_name, &valid);
@@ -461,7 +461,7 @@ void TileSetAtlasSourceEditor::AtlasTileProxyObject::_get_property_list(List<Pro
const int &alternative = E.alternative;
TileData *tile_data = tile_set_atlas_source->get_tile_data(coords, alternative);
- ERR_FAIL_COND(!tile_data);
+ ERR_FAIL_NULL(tile_data);
List<PropertyInfo> list;
tile_data->get_property_list(&list);
@@ -2157,7 +2157,7 @@ Vector2i TileSetAtlasSourceEditor::_get_drag_offset_tile_coords(const Vector2i &
void TileSetAtlasSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id) {
ERR_FAIL_COND(!p_tile_set.is_valid());
- ERR_FAIL_COND(!p_tile_set_atlas_source);
+ ERR_FAIL_NULL(p_tile_set_atlas_source);
ERR_FAIL_COND(p_source_id < 0);
ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_atlas_source);
@@ -2406,7 +2406,7 @@ void TileSetAtlasSourceEditor::_notification(int p_what) {
case NOTIFICATION_THEME_CHANGED: {
tool_setup_atlas_source_button->set_icon(get_editor_theme_icon(SNAME("Tools")));
tool_select_button->set_icon(get_editor_theme_icon(SNAME("ToolSelect")));
- tool_paint_button->set_icon(get_editor_theme_icon(SNAME("CanvasItem")));
+ tool_paint_button->set_icon(get_editor_theme_icon(SNAME("Paint")));
tools_settings_erase_button->set_icon(get_editor_theme_icon(SNAME("Eraser")));
tool_advanced_menu_button->set_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl")));
@@ -2771,7 +2771,7 @@ void EditorPropertyTilePolygon::_polygons_changed() {
void EditorPropertyTilePolygon::update_property() {
TileSetAtlasSourceEditor::AtlasTileProxyObject *atlas_tile_proxy_object = Object::cast_to<TileSetAtlasSourceEditor::AtlasTileProxyObject>(get_edited_object());
- ERR_FAIL_COND(!atlas_tile_proxy_object);
+ ERR_FAIL_NULL(atlas_tile_proxy_object);
ERR_FAIL_COND(atlas_tile_proxy_object->get_edited_tiles().is_empty());
Ref<TileSetAtlasSource> tile_set_atlas_source = atlas_tile_proxy_object->get_edited_tile_set_atlas_source();
diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp
index 53cc59b718..0209e6a6d6 100644
--- a/editor/plugins/tiles/tile_set_editor.cpp
+++ b/editor/plugins/tiles/tile_set_editor.cpp
@@ -568,7 +568,7 @@ void TileSetEditor::_move_tile_set_array_element(Object *p_undo_redo, Object *p_
for (int k = 0; k < tas->get_alternative_tiles_count(tile_id); k++) {
int alternative_id = tas->get_alternative_tile_id(tile_id, k);
TileData *tile_data = tas->get_tile_data(tile_id, alternative_id);
- ERR_FAIL_COND(!tile_data);
+ ERR_FAIL_NULL(tile_data);
// Actually saving stuff.
if (p_array_prefix == "occlusion_layer_") {
@@ -687,7 +687,7 @@ void TileSetEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p
for (int k = 0; k < tas->get_alternative_tiles_count(tile_id); k++) {
int alternative_id = tas->get_alternative_tile_id(tile_id, k);
TileData *tile_data = tas->get_tile_data(tile_id, alternative_id);
- ERR_FAIL_COND(!tile_data);
+ ERR_FAIL_NULL(tile_data);
if (components.size() == 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "mode") {
ADD_UNDO(tile_data, "terrain_set");
diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
index 6b9250010e..939ee5f056 100644
--- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
@@ -107,7 +107,7 @@ void TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::_b
void TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::edit(Ref<TileSet> p_tile_set, TileSetScenesCollectionSource *p_tile_set_scenes_collection_source, int p_source_id) {
ERR_FAIL_COND(!p_tile_set.is_valid());
- ERR_FAIL_COND(!p_tile_set_scenes_collection_source);
+ ERR_FAIL_NULL(p_tile_set_scenes_collection_source);
ERR_FAIL_COND(p_source_id < 0);
ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_scenes_collection_source);
@@ -197,7 +197,7 @@ void TileSetScenesCollectionSourceEditor::SceneTileProxyObject::_get_property_li
}
void TileSetScenesCollectionSourceEditor::SceneTileProxyObject::edit(TileSetScenesCollectionSource *p_tile_set_scenes_collection_source, int p_scene_id) {
- ERR_FAIL_COND(!p_tile_set_scenes_collection_source);
+ ERR_FAIL_NULL(p_tile_set_scenes_collection_source);
ERR_FAIL_COND(!p_tile_set_scenes_collection_source->has_scene_tile_id(p_scene_id));
if (tile_set_scenes_collection_source == p_tile_set_scenes_collection_source && scene_id == p_scene_id) {
@@ -390,7 +390,7 @@ void TileSetScenesCollectionSourceEditor::_notification(int p_what) {
void TileSetScenesCollectionSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetScenesCollectionSource *p_tile_set_scenes_collection_source, int p_source_id) {
ERR_FAIL_COND(!p_tile_set.is_valid());
- ERR_FAIL_COND(!p_tile_set_scenes_collection_source);
+ ERR_FAIL_NULL(p_tile_set_scenes_collection_source);
ERR_FAIL_COND(p_source_id < 0);
ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_scenes_collection_source);
diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp
index e8498501c6..4d4b1fa734 100644
--- a/editor/plugins/tiles/tiles_editor_plugin.cpp
+++ b/editor/plugins/tiles/tiles_editor_plugin.cpp
@@ -164,8 +164,8 @@ void TilesEditorUtils::set_sources_lists_current(int p_current) {
void TilesEditorUtils::synchronize_sources_list(Object *p_current_list, Object *p_current_sort_button) {
ItemList *item_list = Object::cast_to<ItemList>(p_current_list);
MenuButton *sorting_button = Object::cast_to<MenuButton>(p_current_sort_button);
- ERR_FAIL_COND(!item_list);
- ERR_FAIL_COND(!sorting_button);
+ ERR_FAIL_NULL(item_list);
+ ERR_FAIL_NULL(sorting_button);
if (sorting_button->is_visible_in_tree()) {
for (int i = 0; i != SOURCE_SORT_MAX; i++) {
@@ -196,7 +196,7 @@ void TilesEditorUtils::set_atlas_view_transform(float p_zoom, Vector2 p_scroll)
void TilesEditorUtils::synchronize_atlas_view(Object *p_current) {
TileAtlasView *tile_atlas_view = Object::cast_to<TileAtlasView>(p_current);
- ERR_FAIL_COND(!tile_atlas_view);
+ ERR_FAIL_NULL(tile_atlas_view);
if (tile_atlas_view->is_visible_in_tree()) {
tile_atlas_view->set_transform(atlas_view_zoom, atlas_view_scroll);
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 13d16297c8..b9b47c475b 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -2152,7 +2152,7 @@ void VisualShaderEditor::_change_input_port_name(const String &p_text, Object *p
}
LineEdit *line_edit = Object::cast_to<LineEdit>(p_line_edit);
- ERR_FAIL_COND(!line_edit);
+ ERR_FAIL_NULL(line_edit);
String validated_name = visual_shader->validate_port_name(p_text, node.ptr(), p_port_id, false);
if (validated_name.is_empty() || prev_name == validated_name) {
@@ -2179,7 +2179,7 @@ void VisualShaderEditor::_change_output_port_name(const String &p_text, Object *
}
LineEdit *line_edit = Object::cast_to<LineEdit>(p_line_edit);
- ERR_FAIL_COND(!line_edit);
+ ERR_FAIL_NULL(line_edit);
String validated_name = visual_shader->validate_port_name(p_text, node.ptr(), p_port_id, true);
if (validated_name.is_empty() || prev_name == validated_name) {
@@ -3036,7 +3036,7 @@ void VisualShaderEditor::_add_node(int p_idx, const Vector<Variant> &p_ops, Stri
if (!is_custom && !add_options[p_idx].type.is_empty()) {
VisualShaderNode *vsn = Object::cast_to<VisualShaderNode>(ClassDB::instantiate(add_options[p_idx].type));
- ERR_FAIL_COND(!vsn);
+ ERR_FAIL_NULL(vsn);
if (!p_ops.is_empty()) {
_setup_node(vsn, p_ops);
}
@@ -3074,13 +3074,13 @@ void VisualShaderEditor::_add_node(int p_idx, const Vector<Variant> &p_ops, Stri
base_type = add_options[p_idx].script->get_instance_base_type();
}
VisualShaderNode *vsn = Object::cast_to<VisualShaderNode>(ClassDB::instantiate(base_type));
- ERR_FAIL_COND(!vsn);
+ ERR_FAIL_NULL(vsn);
vsnode = Ref<VisualShaderNode>(vsn);
if (!is_native) {
vsnode->set_script(add_options[p_idx].script);
}
VisualShaderNodeCustom *custom_node = Object::cast_to<VisualShaderNodeCustom>(vsn);
- ERR_FAIL_COND(!custom_node);
+ ERR_FAIL_NULL(custom_node);
custom_node->update_ports();
}
@@ -3846,7 +3846,7 @@ void VisualShaderEditor::_node_selected(Object *p_node) {
VisualShader::Type type = get_current_shader_type();
GraphElement *graph_element = Object::cast_to<GraphElement>(p_node);
- ERR_FAIL_COND(!graph_element);
+ ERR_FAIL_NULL(graph_element);
int id = String(graph_element->get_name()).to_int();
diff --git a/editor/plugins/voxel_gi_editor_plugin.cpp b/editor/plugins/voxel_gi_editor_plugin.cpp
index 91d90aaf80..1c6dcc8b86 100644
--- a/editor/plugins/voxel_gi_editor_plugin.cpp
+++ b/editor/plugins/voxel_gi_editor_plugin.cpp
@@ -153,12 +153,12 @@ void VoxelGIEditorPlugin::bake_func_begin(int p_steps) {
}
void VoxelGIEditorPlugin::bake_func_step(int p_step, const String &p_description) {
- ERR_FAIL_COND(tmp_progress == nullptr);
+ ERR_FAIL_NULL(tmp_progress);
tmp_progress->step(p_description, p_step, false);
}
void VoxelGIEditorPlugin::bake_func_end() {
- ERR_FAIL_COND(tmp_progress == nullptr);
+ ERR_FAIL_NULL(tmp_progress);
memdelete(tmp_progress);
tmp_progress = nullptr;
}
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 118d512905..966cba6348 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -189,7 +189,7 @@ void SceneTreeDock::instantiate_scenes(const Vector<String> &p_files, Node *p_pa
}
void SceneTreeDock::_perform_instantiate_scenes(const Vector<String> &p_files, Node *parent, int p_pos) {
- ERR_FAIL_COND(!parent);
+ ERR_FAIL_NULL(parent);
Vector<Node *> instances;
@@ -1255,7 +1255,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
ERR_FAIL_INDEX(idx, subresources.size());
Object *obj = ObjectDB::get_instance(subresources[idx]);
- ERR_FAIL_COND(!obj);
+ ERR_FAIL_NULL(obj);
_push_item(obj);
}
@@ -1881,7 +1881,7 @@ bool SceneTreeDock::_validate_no_instance() {
void SceneTreeDock::_node_reparent(NodePath p_path, bool p_keep_global_xform) {
Node *new_parent = scene_root->get_node(p_path);
- ERR_FAIL_COND(!new_parent);
+ ERR_FAIL_NULL(new_parent);
List<Node *> selection = editor_selection->get_selected_node_list();
@@ -1900,7 +1900,7 @@ void SceneTreeDock::_node_reparent(NodePath p_path, bool p_keep_global_xform) {
void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, Vector<Node *> p_nodes, bool p_keep_global_xform) {
Node *new_parent = p_new_parent;
- ERR_FAIL_COND(!new_parent);
+ ERR_FAIL_NULL(new_parent);
if (p_nodes.size() == 0) {
return; // Nothing to reparent.
@@ -2297,7 +2297,7 @@ void SceneTreeDock::_selection_changed() {
void SceneTreeDock::_do_create(Node *p_parent) {
Variant c = create_dialog->instantiate_selected();
Node *child = Object::cast_to<Node>(c);
- ERR_FAIL_COND(!child);
+ ERR_FAIL_NULL(child);
String new_name = p_parent->validate_child_name(child);
if (GLOBAL_GET("editor/naming/node_name_casing").operator int() != NAME_CASING_PASCAL_CASE) {
@@ -2365,7 +2365,7 @@ void SceneTreeDock::_create() {
} else {
// If no root exist in edited scene
parent = scene_root;
- ERR_FAIL_COND(!parent);
+ ERR_FAIL_NULL(parent);
}
_do_create(parent);
@@ -2378,13 +2378,13 @@ void SceneTreeDock::_create() {
ur->create_action(TTR("Change type of node(s)"), UndoRedo::MERGE_DISABLE, selection.front()->get());
for (Node *n : selection) {
- ERR_FAIL_COND(!n);
+ ERR_FAIL_NULL(n);
Variant c = create_dialog->instantiate_selected();
ERR_FAIL_COND(!c);
Node *new_node = Object::cast_to<Node>(c);
- ERR_FAIL_COND(!new_node);
+ ERR_FAIL_NULL(new_node);
replace_node(n, new_node);
}
@@ -2397,13 +2397,13 @@ void SceneTreeDock::_create() {
bool only_one_top_node = true;
Node *first = selection.front()->get();
- ERR_FAIL_COND(!first);
+ ERR_FAIL_NULL(first);
int smaller_path_to_top = first->get_path_to(scene_root).get_name_count();
Node *top_node = first;
for (List<Node *>::Element *E = selection.front()->next(); E; E = E->next()) {
Node *n = E->get();
- ERR_FAIL_COND(!n);
+ ERR_FAIL_NULL(n);
int path_length = n->get_path_to(scene_root).get_name_count();
@@ -2759,7 +2759,7 @@ void SceneTreeDock::_normalize_drop(Node *&to_node, int &to_pos, int p_type) {
void SceneTreeDock::_files_dropped(Vector<String> p_files, NodePath p_to, int p_type) {
Node *node = get_node(p_to);
- ERR_FAIL_COND(!node);
+ ERR_FAIL_NULL(node);
if (scene_tree->get_scene_tree()->get_drop_mode_flags() & Tree::DROP_MODE_INBETWEEN) {
// Dropped PackedScene, instance it.
@@ -3230,7 +3230,7 @@ void SceneTreeDock::save_branch_to_file(String p_directory) {
void SceneTreeDock::_focus_node() {
Node *node = scene_tree->get_selected();
- ERR_FAIL_COND(!node);
+ ERR_FAIL_NULL(node);
if (node->is_class("CanvasItem")) {
CanvasItemEditorPlugin *editor = Object::cast_to<CanvasItemEditorPlugin>(editor_data->get_editor_by_name("2D"));
@@ -3754,7 +3754,7 @@ void SceneTreeDock::_edit_subresource(int p_idx, const PopupMenu *p_from_menu) {
const ObjectID &id = p_from_menu->get_item_metadata(p_idx);
Object *obj = ObjectDB::get_instance(id);
- ERR_FAIL_COND(!obj);
+ ERR_FAIL_NULL(obj);
_push_item(obj);
}
diff --git a/editor/window_wrapper.cpp b/editor/window_wrapper.cpp
index f0eba7b215..a5c862ffd6 100644
--- a/editor/window_wrapper.cpp
+++ b/editor/window_wrapper.cpp
@@ -59,7 +59,7 @@ class ShortcutBin : public Node {
return;
}
Window *grandparent_window = get_window()->get_parent_visible_window();
- ERR_FAIL_COND(!grandparent_window);
+ ERR_FAIL_NULL(grandparent_window);
if (Object::cast_to<InputEventKey>(p_event.ptr()) || Object::cast_to<InputEventShortcut>(p_event.ptr())) {
// HACK: Propagate the window input to the editor main window to handle global shortcuts.
diff --git a/modules/gdscript/.editorconfig b/modules/gdscript/.editorconfig
new file mode 100644
index 0000000000..640c205093
--- /dev/null
+++ b/modules/gdscript/.editorconfig
@@ -0,0 +1,8 @@
+[*.gd]
+indent_style = tab
+indent_size = 4
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.out]
+insert_final_newline = true
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 42be231424..077b30a7fe 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -3204,7 +3204,13 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
bool is_constructor = (base_type.is_meta_type || (p_call->callee && p_call->callee->type == GDScriptParser::Node::IDENTIFIER)) && p_call->function_name == SNAME("new");
if (get_function_signature(p_call, is_constructor, base_type, p_call->function_name, return_type, par_types, default_arg_count, method_flags)) {
- // If the function require typed arrays we must make literals be typed.
+ // If the method is implemented in the class hierarchy, the virtual flag will not be set for that MethodInfo and the search stops there.
+ // Virtual check only possible for super() calls because class hierarchy is known. Node/Objects may have scripts attached we don't know of at compile-time.
+ if (p_call->is_super && method_flags.has_flag(METHOD_FLAG_VIRTUAL)) {
+ push_error(vformat(R"*(Cannot call the parent class' virtual function "%s()" because it hasn't been defined.)*", p_call->function_name), p_call);
+ }
+
+ // If the function requires typed arrays we must make literals be typed.
for (const KeyValue<int, GDScriptParser::ArrayNode *> &E : arrays) {
int index = E.key;
if (index < par_types.size() && par_types[index].has_container_element_type()) {
@@ -3457,7 +3463,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base_set_class(GDScriptParser::Ide
p_identifier->set_datatype(p_identifier_datatype);
Error err = OK;
- Ref<GDScript> scr = GDScriptCache::get_shallow_script(p_identifier_datatype.script_path, err);
+ Ref<GDScript> scr = GDScriptCache::get_shallow_script(p_identifier_datatype.script_path, err, parser->script_path);
if (err) {
push_error(vformat(R"(Error while getting cache for script "%s".)", p_identifier_datatype.script_path), p_identifier);
return;
@@ -4581,7 +4587,7 @@ Array GDScriptAnalyzer::make_array_from_element_datatype(const GDScriptParser::D
Ref<Script> script_type = p_element_datatype.script_type;
if (p_element_datatype.kind == GDScriptParser::DataType::CLASS && script_type.is_null()) {
Error err = OK;
- Ref<GDScript> scr = GDScriptCache::get_shallow_script(p_element_datatype.script_path, err);
+ Ref<GDScript> scr = GDScriptCache::get_shallow_script(p_element_datatype.script_path, err, parser->script_path);
if (err) {
push_error(vformat(R"(Error while getting cache for script "%s".)", p_element_datatype.script_path), p_source_node);
return array;
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index fae7861539..7f2c401afc 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -84,7 +84,7 @@ void GDScriptCompiler::_set_error(const String &p_error, const GDScriptParser::N
}
}
-GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner) {
+GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner, bool p_handle_metatype) {
if (!p_datatype.is_set() || !p_datatype.is_hard_type() || p_datatype.is_coroutine) {
return GDScriptDataType();
}
@@ -101,11 +101,25 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
result.builtin_type = p_datatype.builtin_type;
} break;
case GDScriptParser::DataType::NATIVE: {
+ if (p_handle_metatype && p_datatype.is_meta_type) {
+ result.kind = GDScriptDataType::NATIVE;
+ result.builtin_type = Variant::OBJECT;
+ result.native_type = GDScriptNativeClass::get_class_static();
+ break;
+ }
+
result.kind = GDScriptDataType::NATIVE;
result.native_type = p_datatype.native_type;
result.builtin_type = p_datatype.builtin_type;
} break;
case GDScriptParser::DataType::SCRIPT: {
+ if (p_handle_metatype && p_datatype.is_meta_type) {
+ result.kind = GDScriptDataType::NATIVE;
+ result.builtin_type = Variant::OBJECT;
+ result.native_type = p_datatype.script_type.is_valid() ? p_datatype.script_type->get_class() : Script::get_class_static();
+ break;
+ }
+
result.kind = GDScriptDataType::SCRIPT;
result.builtin_type = p_datatype.builtin_type;
result.script_type_ref = p_datatype.script_type;
@@ -113,6 +127,13 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
result.native_type = p_datatype.native_type;
} break;
case GDScriptParser::DataType::CLASS: {
+ if (p_handle_metatype && p_datatype.is_meta_type) {
+ result.kind = GDScriptDataType::NATIVE;
+ result.builtin_type = Variant::OBJECT;
+ result.native_type = GDScript::get_class_static();
+ break;
+ }
+
result.kind = GDScriptDataType::GDSCRIPT;
result.builtin_type = p_datatype.builtin_type;
result.native_type = p_datatype.native_type;
@@ -148,6 +169,12 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
}
} break;
case GDScriptParser::DataType::ENUM:
+ if (p_handle_metatype && p_datatype.is_meta_type) {
+ result.kind = GDScriptDataType::BUILTIN;
+ result.builtin_type = Variant::DICTIONARY;
+ break;
+ }
+
result.kind = GDScriptDataType::BUILTIN;
result.builtin_type = p_datatype.builtin_type;
break;
@@ -159,7 +186,7 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
}
if (p_datatype.has_container_element_type()) {
- result.set_container_element_type(_gdtype_from_datatype(p_datatype.get_container_element_type(), p_owner));
+ result.set_container_element_type(_gdtype_from_datatype(p_datatype.get_container_element_type(), p_owner, false));
}
return result;
@@ -402,7 +429,8 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
String global_class_path = ScriptServer::get_global_class_path(identifier);
if (ResourceLoader::get_resource_type(global_class_path) == "GDScript") {
Error err = OK;
- res = GDScriptCache::get_full_script(global_class_path, err);
+ // Should not need to pass p_owner since analyzer will already have done it.
+ res = GDScriptCache::get_shallow_script(global_class_path, err);
if (err != OK) {
_set_error("Can't load global class " + String(identifier), p_expression);
r_error = ERR_COMPILATION_FAILED;
@@ -533,7 +561,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
} break;
case GDScriptParser::Node::CAST: {
const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression);
- GDScriptDataType cast_type = _gdtype_from_datatype(cn->get_datatype(), codegen.script);
+ GDScriptDataType cast_type = _gdtype_from_datatype(cn->get_datatype(), codegen.script, false);
GDScriptCodeGenerator::Address result;
if (cast_type.has_type) {
@@ -911,7 +939,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(type_test->get_datatype(), codegen.script));
GDScriptCodeGenerator::Address operand = _parse_expression(codegen, r_error, type_test->operand);
- GDScriptDataType test_type = _gdtype_from_datatype(type_test->test_datatype, codegen.script);
+ GDScriptDataType test_type = _gdtype_from_datatype(type_test->test_datatype, codegen.script, false);
if (r_error) {
return GDScriptCodeGenerator::Address();
}
@@ -2587,7 +2615,7 @@ Error GDScriptCompiler::_prepare_compilation(GDScript *p_script, const GDScriptP
}
}
- GDScriptDataType base_type = _gdtype_from_datatype(p_class->base_type, p_script);
+ GDScriptDataType base_type = _gdtype_from_datatype(p_class->base_type, p_script, false);
int native_idx = GDScriptLanguage::get_singleton()->get_global_map()[base_type.native_type];
p_script->native = GDScriptLanguage::get_singleton()->get_global_array()[native_idx];
diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h
index 1882471331..099bd00a2e 100644
--- a/modules/gdscript/gdscript_compiler.h
+++ b/modules/gdscript/gdscript_compiler.h
@@ -124,7 +124,7 @@ class GDScriptCompiler {
Error _create_binary_operator(CodeGen &codegen, const GDScriptParser::BinaryOpNode *on, Variant::Operator op, bool p_initializer = false, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
Error _create_binary_operator(CodeGen &codegen, const GDScriptParser::ExpressionNode *p_left_operand, const GDScriptParser::ExpressionNode *p_right_operand, Variant::Operator op, bool p_initializer = false, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
- GDScriptDataType _gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner);
+ GDScriptDataType _gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner, bool p_handle_metatype = true);
GDScriptCodeGenerator::Address _parse_assign_right_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::AssignmentNode *p_assignmentint, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
GDScriptCodeGenerator::Address _parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root = false, bool p_initializer = false, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 5e626f0520..aec8f56516 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -500,7 +500,7 @@ String GDScriptLanguage::make_function(const String &p_class, const String &p_na
s += p_args[i].get_slice(":", 0);
if (th) {
String type = p_args[i].get_slice(":", 1);
- if (!type.is_empty() && type != "var") {
+ if (!type.is_empty()) {
s += ": " + type;
}
}
diff --git a/modules/gdscript/gdscript_lambda_callable.cpp b/modules/gdscript/gdscript_lambda_callable.cpp
index 3b89f077bd..9d0fce0928 100644
--- a/modules/gdscript/gdscript_lambda_callable.cpp
+++ b/modules/gdscript/gdscript_lambda_callable.cpp
@@ -79,13 +79,48 @@ void GDScriptLambdaCallable::call(const Variant **p_arguments, int p_argcount, V
args.resize(p_argcount + captures_amount);
for (int i = 0; i < captures_amount; i++) {
args.write[i] = &captures[i];
+ if (captures[i].get_type() == Variant::OBJECT) {
+ bool was_freed = false;
+ captures[i].get_validated_object_with_check(was_freed);
+ if (was_freed) {
+ ERR_PRINT(vformat(R"(Lambda capture at index %d was freed. Passed "null" instead.)", i));
+ static Variant nil;
+ args.write[i] = &nil;
+ }
+ }
}
for (int i = 0; i < p_argcount; i++) {
args.write[i + captures_amount] = p_arguments[i];
}
r_return_value = function->call(nullptr, args.ptrw(), args.size(), r_call_error);
- r_call_error.argument -= captures_amount;
+ switch (r_call_error.error) {
+ case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
+ r_call_error.argument -= captures_amount;
+#ifdef DEBUG_ENABLED
+ if (r_call_error.argument < 0) {
+ ERR_PRINT(vformat("GDScript bug (please report): Invalid value of lambda capture at index %d.", captures_amount + r_call_error.argument));
+ r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // TODO: Add a more suitable error code.
+ r_call_error.argument = 0;
+ r_call_error.expected = 0;
+ }
+#endif
+ break;
+ case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
+ case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
+ r_call_error.expected -= captures_amount;
+#ifdef DEBUG_ENABLED
+ if (r_call_error.expected < 0) {
+ ERR_PRINT("GDScript bug (please report): Invalid lambda captures count.");
+ r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // TODO: Add a more suitable error code.
+ r_call_error.argument = 0;
+ r_call_error.expected = 0;
+ }
+#endif
+ break;
+ default:
+ break;
+ }
} else {
r_return_value = function->call(nullptr, p_arguments, p_argcount, r_call_error);
}
@@ -148,13 +183,48 @@ void GDScriptLambdaSelfCallable::call(const Variant **p_arguments, int p_argcoun
args.resize(p_argcount + captures_amount);
for (int i = 0; i < captures_amount; i++) {
args.write[i] = &captures[i];
+ if (captures[i].get_type() == Variant::OBJECT) {
+ bool was_freed = false;
+ captures[i].get_validated_object_with_check(was_freed);
+ if (was_freed) {
+ ERR_PRINT(vformat(R"(Lambda capture at index %d was freed. Passed "null" instead.)", i));
+ static Variant nil;
+ args.write[i] = &nil;
+ }
+ }
}
for (int i = 0; i < p_argcount; i++) {
args.write[i + captures_amount] = p_arguments[i];
}
r_return_value = function->call(static_cast<GDScriptInstance *>(object->get_script_instance()), args.ptrw(), args.size(), r_call_error);
- r_call_error.argument -= captures_amount;
+ switch (r_call_error.error) {
+ case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
+ r_call_error.argument -= captures_amount;
+#ifdef DEBUG_ENABLED
+ if (r_call_error.argument < 0) {
+ ERR_PRINT(vformat("GDScript bug (please report): Invalid value of lambda capture at index %d.", captures_amount + r_call_error.argument));
+ r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // TODO: Add a more suitable error code.
+ r_call_error.argument = 0;
+ r_call_error.expected = 0;
+ }
+#endif
+ break;
+ case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
+ case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
+ r_call_error.expected -= captures_amount;
+#ifdef DEBUG_ENABLED
+ if (r_call_error.expected < 0) {
+ ERR_PRINT("GDScript bug (please report): Invalid lambda captures count.");
+ r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // TODO: Add a more suitable error code.
+ r_call_error.argument = 0;
+ r_call_error.expected = 0;
+ }
+#endif
+ break;
+ default:
+ break;
+ }
} else {
r_return_value = function->call(static_cast<GDScriptInstance *>(object->get_script_instance()), p_arguments, p_argcount, r_call_error);
}
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
index a7dc0b6d59..c0644e089c 100644
--- a/modules/gdscript/gdscript_vm.cpp
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -116,6 +116,7 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
if (p_err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
int errorarg = p_err.argument;
+ ERR_FAIL_COND_V_MSG(errorarg < 0 || argptrs[errorarg] == nullptr, "GDScript bug (please report): Invalid CallError argument index or null pointer.", "Invalid CallError argument index or null pointer.");
// Handle the Object to Object case separately as we don't have further class details.
#ifdef DEBUG_ENABLED
if (p_err.expected == Variant::OBJECT && argptrs[errorarg]->get_type() == p_err.expected) {
@@ -128,9 +129,9 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
err_text = "Invalid type in " + p_where + ". Cannot convert argument " + itos(errorarg + 1) + " from " + Variant::get_type_name(argptrs[errorarg]->get_type()) + " to " + Variant::get_type_name(Variant::Type(p_err.expected)) + ".";
}
} else if (p_err.error == Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) {
- err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments.";
+ err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.expected) + " arguments.";
} else if (p_err.error == Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) {
- err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments.";
+ err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.expected) + " arguments.";
} else if (p_err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
err_text = "Invalid call. Nonexistent " + p_where + ".";
} else if (p_err.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
@@ -511,13 +512,13 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
if (p_argcount != _argument_count) {
if (p_argcount > _argument_count) {
r_err.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_err.argument = _argument_count;
+ r_err.expected = _argument_count;
call_depth--;
return _get_default_variant_for_data_type(return_type);
} else if (p_argcount < _argument_count - _default_arg_count) {
r_err.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_err.argument = _argument_count - _default_arg_count;
+ r_err.expected = _argument_count - _default_arg_count;
call_depth--;
return _get_default_variant_for_data_type(return_type);
} else {
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.gd b/modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.gd
new file mode 100644
index 0000000000..57dfffdbee
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.gd
@@ -0,0 +1,5 @@
+func _init():
+ super()
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.out b/modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.out
new file mode 100644
index 0000000000..e68759223c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot call the parent class' virtual function "_init()" because it hasn't been defined.
diff --git a/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.gd b/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.gd
new file mode 100644
index 0000000000..a8641e4f3b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.gd
@@ -0,0 +1,21 @@
+class BaseClass:
+ func _get_property_list():
+ return {"property" : "definition"}
+
+class SuperClassMethodsRecognized extends BaseClass:
+ func _init():
+ # Recognizes super class methods.
+ var _x = _get_property_list()
+
+class SuperMethodsRecognized extends BaseClass:
+ func _get_property_list():
+ # Recognizes super method.
+ var result = super()
+ result["new"] = "new"
+ return result
+
+func test():
+ var test1 = SuperClassMethodsRecognized.new()
+ print(test1._get_property_list()) # Calls base class's method.
+ var test2 = SuperMethodsRecognized.new()
+ print(test2._get_property_list())
diff --git a/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.out b/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.out
new file mode 100644
index 0000000000..ccff660117
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+{ "property": "definition" }
+{ "property": "definition", "new": "new" }
diff --git a/modules/gdscript/tests/scripts/runtime/features/member_info.gd b/modules/gdscript/tests/scripts/runtime/features/member_info.gd
index 50f840cef3..805ea42455 100644
--- a/modules/gdscript/tests/scripts/runtime/features/member_info.gd
+++ b/modules/gdscript/tests/scripts/runtime/features/member_info.gd
@@ -5,6 +5,8 @@ class MyClass:
enum MyEnum {}
+const Utils = preload("../../utils.notest.gd")
+
static var test_static_var_untyped
static var test_static_var_weak_null = null
static var test_static_var_weak_int = 1
@@ -58,68 +60,13 @@ func test():
var script: Script = get_script()
for property in script.get_property_list():
if str(property.name).begins_with("test_"):
- if not (property.usage & PROPERTY_USAGE_SCRIPT_VARIABLE):
- print("Error: Missing `PROPERTY_USAGE_SCRIPT_VARIABLE` flag.")
- print("static var ", property.name, ": ", get_type(property))
+ print(Utils.get_property_signature(property, true))
for property in get_property_list():
if str(property.name).begins_with("test_"):
- if not (property.usage & PROPERTY_USAGE_SCRIPT_VARIABLE):
- print("Error: Missing `PROPERTY_USAGE_SCRIPT_VARIABLE` flag.")
- print("var ", property.name, ": ", get_type(property))
+ print(Utils.get_property_signature(property))
for method in get_method_list():
if str(method.name).begins_with("test_"):
- print(get_signature(method))
+ print(Utils.get_method_signature(method))
for method in get_signal_list():
if str(method.name).begins_with("test_"):
- print(get_signature(method, true))
-
-func get_type(property: Dictionary, is_return: bool = false) -> String:
- match property.type:
- TYPE_NIL:
- if property.usage & PROPERTY_USAGE_NIL_IS_VARIANT:
- return "Variant"
- return "void" if is_return else "null"
- TYPE_BOOL:
- return "bool"
- TYPE_INT:
- if property.usage & PROPERTY_USAGE_CLASS_IS_ENUM:
- return property.class_name
- return "int"
- TYPE_STRING:
- return "String"
- TYPE_DICTIONARY:
- return "Dictionary"
- TYPE_ARRAY:
- if property.hint == PROPERTY_HINT_ARRAY_TYPE:
- return "Array[%s]" % property.hint_string
- return "Array"
- TYPE_OBJECT:
- if not str(property.class_name).is_empty():
- return property.class_name
- return "Object"
- return "<error>"
-
-func get_signature(method: Dictionary, is_signal: bool = false) -> String:
- var result: String = ""
- if method.flags & METHOD_FLAG_STATIC:
- result += "static "
- result += ("signal " if is_signal else "func ") + method.name + "("
-
- var args: Array[Dictionary] = method.args
- var default_args: Array = method.default_args
- var mandatory_argc: int = args.size() - default_args.size()
- for i in args.size():
- if i > 0:
- result += ", "
- var arg: Dictionary = args[i]
- result += arg.name + ": " + get_type(arg)
- if i >= mandatory_argc:
- result += " = " + var_to_str(default_args[i - mandatory_argc])
-
- result += ")"
- if is_signal:
- if get_type(method.return, true) != "void":
- print("Error: Signal return type must be `void`.")
- else:
- result += " -> " + get_type(method.return, true)
- return result
+ print(Utils.get_method_signature(method, true))
diff --git a/modules/gdscript/tests/scripts/runtime/features/member_info.out b/modules/gdscript/tests/scripts/runtime/features/member_info.out
index 7c826ac05a..3a91507da9 100644
--- a/modules/gdscript/tests/scripts/runtime/features/member_info.out
+++ b/modules/gdscript/tests/scripts/runtime/features/member_info.out
@@ -6,13 +6,13 @@ static var test_static_var_hard_int: int
var test_var_untyped: Variant
var test_var_weak_null: Variant
var test_var_weak_int: Variant
-var test_var_weak_int_exported: int
+@export var test_var_weak_int_exported: int
var test_var_weak_variant_type: Variant
-var test_var_weak_variant_type_exported: Variant.Type
+@export var test_var_weak_variant_type_exported: Variant.Type
var test_var_hard_variant: Variant
var test_var_hard_int: int
var test_var_hard_variant_type: Variant.Type
-var test_var_hard_variant_type_exported: Variant.Type
+@export var test_var_hard_variant_type_exported: Variant.Type
var test_var_hard_node_process_mode: Node.ProcessMode
var test_var_hard_my_enum: TestMemberInfo.MyEnum
var test_var_hard_array: Array
diff --git a/modules/gdscript/tests/scripts/runtime/features/metatypes.gd b/modules/gdscript/tests/scripts/runtime/features/metatypes.gd
new file mode 100644
index 0000000000..6c5df32ffe
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/metatypes.gd
@@ -0,0 +1,36 @@
+class MyClass:
+ const TEST = 10
+
+enum MyEnum {A, B, C}
+
+const Utils = preload("../../utils.notest.gd")
+const Other = preload("./metatypes.notest.gd")
+
+var test_native := JSON
+var test_script := Other
+var test_class := MyClass
+var test_enum := MyEnum
+
+func check_gdscript_native_class(value: Variant) -> void:
+ print(var_to_str(value).get_slice(",", 0).trim_prefix("Object("))
+
+func check_gdscript(value: GDScript) -> void:
+ print(value.get_class())
+
+func check_enum(value: Dictionary) -> void:
+ print(value)
+
+func test():
+ for property in get_property_list():
+ if str(property.name).begins_with("test_"):
+ print(Utils.get_property_signature(property))
+
+ check_gdscript_native_class(test_native)
+ check_gdscript(test_script)
+ check_gdscript(test_class)
+ check_enum(test_enum)
+
+ print(test_native.stringify([]))
+ print(test_script.TEST)
+ print(test_class.TEST)
+ print(test_enum.keys())
diff --git a/modules/gdscript/tests/scripts/runtime/features/metatypes.notest.gd b/modules/gdscript/tests/scripts/runtime/features/metatypes.notest.gd
new file mode 100644
index 0000000000..e6a591b927
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/metatypes.notest.gd
@@ -0,0 +1 @@
+const TEST = 100
diff --git a/modules/gdscript/tests/scripts/runtime/features/metatypes.out b/modules/gdscript/tests/scripts/runtime/features/metatypes.out
new file mode 100644
index 0000000000..352d1caa59
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/metatypes.out
@@ -0,0 +1,13 @@
+GDTEST_OK
+var test_native: GDScriptNativeClass
+var test_script: GDScript
+var test_class: GDScript
+var test_enum: Dictionary
+GDScriptNativeClass
+GDScript
+GDScript
+{ "A": 0, "B": 1, "C": 2 }
+[]
+100
+10
+["A", "B", "C"]
diff --git a/modules/gdscript/tests/scripts/utils.notest.gd b/modules/gdscript/tests/scripts/utils.notest.gd
new file mode 100644
index 0000000000..50444e62a1
--- /dev/null
+++ b/modules/gdscript/tests/scripts/utils.notest.gd
@@ -0,0 +1,137 @@
+static func get_type(property: Dictionary, is_return: bool = false) -> String:
+ match property.type:
+ TYPE_NIL:
+ if property.usage & PROPERTY_USAGE_NIL_IS_VARIANT:
+ return "Variant"
+ return "void" if is_return else "null"
+ TYPE_INT:
+ if property.usage & PROPERTY_USAGE_CLASS_IS_ENUM:
+ if property.class_name == &"":
+ return "<unknown enum>"
+ return property.class_name
+ TYPE_ARRAY:
+ if property.hint == PROPERTY_HINT_ARRAY_TYPE:
+ if str(property.hint_string).is_empty():
+ return "Array[<unknown type>]"
+ return "Array[%s]" % property.hint_string
+ TYPE_OBJECT:
+ if not str(property.class_name).is_empty():
+ return property.class_name
+ return variant_get_type_name(property.type)
+
+static func get_property_signature(property: Dictionary, is_static: bool = false) -> String:
+ var result: String = ""
+ if not (property.usage & PROPERTY_USAGE_SCRIPT_VARIABLE):
+ printerr("Missing `PROPERTY_USAGE_SCRIPT_VARIABLE` flag.")
+ if property.usage & PROPERTY_USAGE_DEFAULT:
+ result += "@export "
+ if is_static:
+ result += "static "
+ result += "var " + property.name + ": " + get_type(property)
+ return result
+
+static func get_method_signature(method: Dictionary, is_signal: bool = false) -> String:
+ var result: String = ""
+ if method.flags & METHOD_FLAG_STATIC:
+ result += "static "
+ result += ("signal " if is_signal else "func ") + method.name + "("
+
+ var args: Array[Dictionary] = method.args
+ var default_args: Array = method.default_args
+ var mandatory_argc: int = args.size() - default_args.size()
+ for i in args.size():
+ if i > 0:
+ result += ", "
+ var arg: Dictionary = args[i]
+ result += arg.name + ": " + get_type(arg)
+ if i >= mandatory_argc:
+ result += " = " + var_to_str(default_args[i - mandatory_argc])
+
+ result += ")"
+ if is_signal:
+ if get_type(method.return, true) != "void":
+ printerr("Signal return type must be `void`.")
+ else:
+ result += " -> " + get_type(method.return, true)
+ return result
+
+static func variant_get_type_name(type: Variant.Type) -> String:
+ match type:
+ TYPE_NIL:
+ return "Nil" # `Nil` in core, `null` in GDScript.
+ TYPE_BOOL:
+ return "bool"
+ TYPE_INT:
+ return "int"
+ TYPE_FLOAT:
+ return "float"
+ TYPE_STRING:
+ return "String"
+ TYPE_VECTOR2:
+ return "Vector2"
+ TYPE_VECTOR2I:
+ return "Vector2i"
+ TYPE_RECT2:
+ return "Rect2"
+ TYPE_RECT2I:
+ return "Rect2i"
+ TYPE_VECTOR3:
+ return "Vector3"
+ TYPE_VECTOR3I:
+ return "Vector3i"
+ TYPE_TRANSFORM2D:
+ return "Transform2D"
+ TYPE_VECTOR4:
+ return "Vector4"
+ TYPE_VECTOR4I:
+ return "Vector4i"
+ TYPE_PLANE:
+ return "Plane"
+ TYPE_QUATERNION:
+ return "Quaternion"
+ TYPE_AABB:
+ return "AABB"
+ TYPE_BASIS:
+ return "Basis"
+ TYPE_TRANSFORM3D:
+ return "Transform3D"
+ TYPE_PROJECTION:
+ return "Projection"
+ TYPE_COLOR:
+ return "Color"
+ TYPE_STRING_NAME:
+ return "StringName"
+ TYPE_NODE_PATH:
+ return "NodePath"
+ TYPE_RID:
+ return "RID"
+ TYPE_OBJECT:
+ return "Object"
+ TYPE_CALLABLE:
+ return "Callable"
+ TYPE_SIGNAL:
+ return "Signal"
+ TYPE_DICTIONARY:
+ return "Dictionary"
+ TYPE_ARRAY:
+ return "Array"
+ TYPE_PACKED_BYTE_ARRAY:
+ return "PackedByteArray"
+ TYPE_PACKED_INT32_ARRAY:
+ return "PackedInt32Array"
+ TYPE_PACKED_INT64_ARRAY:
+ return "PackedInt64Array"
+ TYPE_PACKED_FLOAT32_ARRAY:
+ return "PackedFloat32Array"
+ TYPE_PACKED_FLOAT64_ARRAY:
+ return "PackedFloat64Array"
+ TYPE_PACKED_STRING_ARRAY:
+ return "PackedStringArray"
+ TYPE_PACKED_VECTOR2_ARRAY:
+ return "PackedVector2Array"
+ TYPE_PACKED_VECTOR3_ARRAY:
+ return "PackedVector3Array"
+ TYPE_PACKED_COLOR_ARRAY:
+ return "PackedColorArray"
+ push_error("Argument `type` is invalid. Use `TYPE_*` constants.")
+ return "<invalid type>"
diff --git a/modules/gltf/doc_classes/GLTFDocument.xml b/modules/gltf/doc_classes/GLTFDocument.xml
index 9b760a997a..9b84397c7e 100644
--- a/modules/gltf/doc_classes/GLTFDocument.xml
+++ b/modules/gltf/doc_classes/GLTFDocument.xml
@@ -5,7 +5,7 @@
</brief_description>
<description>
GLTFDocument supports reading data from a glTF file, buffer, or Godot scene. This data can then be written to the filesystem, buffer, or used to create a Godot scene.
- All of the data in a GLTF scene is stored in the [GLTFState] class. GLTFDocument processes state objects, but does not contain any scene data itself.
+ All of the data in a GLTF scene is stored in the [GLTFState] class. GLTFDocument processes state objects, but does not contain any scene data itself. GLTFDocument has member variables to store export configuration settings such as the image format, but is otherwise stateless. Multiple scenes can be processed with the same settings using the same GLTFDocument object and different [GLTFState] objects.
GLTFDocument can be extended with arbitrary functionality by extending the [GLTFDocumentExtension] class and registering it with GLTFDocument via [method register_gltf_document_extension]. This allows for custom data to be imported and exported.
</description>
<tutorials>
@@ -87,4 +87,13 @@
</description>
</method>
</methods>
+ <members>
+ <member name="image_format" type="String" setter="set_image_format" getter="get_image_format" default="&quot;PNG&quot;">
+ The user-friendly name of the export image format. This is used when exporting the GLTF file, including writing to a file and writing to a byte array.
+ By default, Godot allows the following options: "None", "PNG", "JPEG", "Lossless WebP", and "Lossy WebP". Support for more image formats can be added in [GLTFDocumentExtension] classes.
+ </member>
+ <member name="lossy_quality" type="float" setter="set_lossy_quality" getter="get_lossy_quality" default="0.75">
+ If [member image_format] is a lossy image format, this determines the lossy quality of the image. On a range of [code]0.0[/code] to [code]1.0[/code], where [code]0.0[/code] is the lowest quality and [code]1.0[/code] is the highest quality. A lossy quality of [code]1.0[/code] is not the same as lossless.
+ </member>
+ </members>
</class>
diff --git a/modules/gltf/doc_classes/GLTFDocumentExtension.xml b/modules/gltf/doc_classes/GLTFDocumentExtension.xml
index bae980fb56..dbfbf63da6 100644
--- a/modules/gltf/doc_classes/GLTFDocumentExtension.xml
+++ b/modules/gltf/doc_classes/GLTFDocumentExtension.xml
@@ -28,7 +28,7 @@
<param index="2" name="json" type="Dictionary" />
<param index="3" name="node" type="Node" />
<description>
- Part of the export process. This method is run after [method _export_preserialize] and before [method _export_post].
+ Part of the export process. This method is run after [method _get_saveable_image_formats] and before [method _export_post]. If this [GLTFDocumentExtension] is used for exporting images, this runs after [method _serialize_texture_json].
This method can be used to modify the final JSON of each node.
</description>
</method>
@@ -53,7 +53,7 @@
<return type="int" enum="Error" />
<param index="0" name="state" type="GLTFState" />
<description>
- Part of the export process. This method is run after [method _convert_scene_node] and before [method _export_node].
+ Part of the export process. This method is run after [method _convert_scene_node] and before [method _get_saveable_image_formats].
This method can be used to alter the state before performing serialization. It runs every time when generating a buffer with [method GLTFDocument.generate_buffer] or writing to the file system with [method GLTFDocument.write_to_filesystem].
</description>
</method>
@@ -63,7 +63,7 @@
<param index="1" name="gltf_node" type="GLTFNode" />
<param index="2" name="scene_parent" type="Node" />
<description>
- Part of the import process. This method is run after [method _parse_node_extensions] and before [method _import_post_parse].
+ Part of the import process. This method is run after [method _import_post_parse] and before [method _import_node].
Runs when generating a Godot scene node from a GLTFNode. The returned node will be added to the scene tree. Multiple nodes can be generated in this step if they are added as a child of the returned node.
</description>
</method>
@@ -73,6 +73,13 @@
Returns the file extension to use for saving image data into, for example, [code]".png"[/code]. If defined, when this extension is used to handle images, and the images are saved to a separate file, the image bytes will be copied to a file with this extension. If this is set, there should be a [ResourceImporter] class able to import the file. If not defined or empty, Godot will save the image into a PNG file.
</description>
</method>
+ <method name="_get_saveable_image_formats" qualifiers="virtual">
+ <return type="PackedStringArray" />
+ <description>
+ Part of the export process. This method is run after [method _convert_scene_node] and before [method _export_node].
+ Returns an array of the image formats that can be saved/exported by this extension. This extension will only be selected as the image exporter if the [GLTFDocument]'s [member GLTFDocument.image_format] is in this array. If this [GLTFDocumentExtension] is selected as the image exporter, one of the [method _save_image_at_path] or [method _serialize_image_to_bytes] methods will run next, otherwise [method _export_node] will run next. If the format name contains [code]"Lossy"[/code], the lossy quality slider will be displayed.
+ </description>
+ </method>
<method name="_get_supported_extensions" qualifiers="virtual">
<return type="PackedStringArray" />
<description>
@@ -87,7 +94,7 @@
<param index="2" name="json" type="Dictionary" />
<param index="3" name="node" type="Node" />
<description>
- Part of the import process. This method is run after [method _import_post_parse] and before [method _import_post].
+ Part of the import process. This method is run after [method _generate_scene_node] and before [method _import_post].
This method can be used to make modifications to each of the generated Godot scene nodes.
</description>
</method>
@@ -104,7 +111,7 @@
<return type="int" enum="Error" />
<param index="0" name="state" type="GLTFState" />
<description>
- Part of the import process. This method is run after [method _generate_scene_node] and before [method _import_node].
+ Part of the import process. This method is run after [method _parse_node_extensions] and before [method _generate_scene_node].
This method can be used to modify any of the data imported so far, including any scene nodes, before running the final per-node import step.
</description>
</method>
@@ -134,7 +141,7 @@
<param index="1" name="gltf_node" type="GLTFNode" />
<param index="2" name="extensions" type="Dictionary" />
<description>
- Part of the import process. This method is run after [method _get_supported_extensions] and before [method _generate_scene_node].
+ Part of the import process. This method is run after [method _get_supported_extensions] and before [method _import_post_parse].
Runs when parsing the node extensions of a GLTFNode. This method can be used to process the extension JSON data into a format that can be used by [method _generate_scene_node]. The return value should be a member of the [enum Error] enum.
</description>
</method>
@@ -148,5 +155,41 @@
Runs when parsing the texture JSON from the GLTF textures array. This can be used to set the source image index to use as the texture.
</description>
</method>
+ <method name="_save_image_at_path" qualifiers="virtual">
+ <return type="int" enum="Error" />
+ <param index="0" name="state" type="GLTFState" />
+ <param index="1" name="image" type="Image" />
+ <param index="2" name="file_path" type="String" />
+ <param index="3" name="image_format" type="String" />
+ <param index="4" name="lossy_quality" type="float" />
+ <description>
+ Part of the export process. This method is run after [method _get_saveable_image_formats] and before [method _serialize_texture_json].
+ This method is run when saving images separately from the GLTF file. When images are embedded, [method _serialize_image_to_bytes] runs instead. Note that these methods only run when this [GLTFDocumentExtension] is selected as the image exporter.
+ </description>
+ </method>
+ <method name="_serialize_image_to_bytes" qualifiers="virtual">
+ <return type="PackedByteArray" />
+ <param index="0" name="state" type="GLTFState" />
+ <param index="1" name="image" type="Image" />
+ <param index="2" name="image_dict" type="Dictionary" />
+ <param index="3" name="image_format" type="String" />
+ <param index="4" name="lossy_quality" type="float" />
+ <description>
+ Part of the export process. This method is run after [method _get_saveable_image_formats] and before [method _serialize_texture_json].
+ This method is run when embedding images in the GLTF file. When images are saved separately, [method _save_image_at_path] runs instead. Note that these methods only run when this [GLTFDocumentExtension] is selected as the image exporter.
+ This method must set the image MIME type in the [param image_dict] with the [code]"mimeType"[/code] key. For example, for a PNG image, it would be set to [code]"image/png"[/code]. The return value must be a [PackedByteArray] containing the image data.
+ </description>
+ </method>
+ <method name="_serialize_texture_json" qualifiers="virtual">
+ <return type="int" enum="Error" />
+ <param index="0" name="state" type="GLTFState" />
+ <param index="1" name="texture_json" type="Dictionary" />
+ <param index="2" name="gltf_texture" type="GLTFTexture" />
+ <param index="3" name="image_format" type="String" />
+ <description>
+ Part of the export process. This method is run after [method _save_image_at_path] or [method _serialize_image_to_bytes], and before [method _export_node]. Note that this method only runs when this [GLTFDocumentExtension] is selected as the image exporter.
+ This method can be used to set up the extensions for the texture JSON by editing [param texture_json]. The extension must also be added as used extension with [method GLTFState.add_used_extension], be sure to set [code]required[/code] to [code]true[/code] if you are not providing a fallback.
+ </description>
+ </method>
</methods>
</class>
diff --git a/modules/gltf/extensions/gltf_document_extension.cpp b/modules/gltf/extensions/gltf_document_extension.cpp
index 11718ba78a..e16e366692 100644
--- a/modules/gltf/extensions/gltf_document_extension.cpp
+++ b/modules/gltf/extensions/gltf_document_extension.cpp
@@ -46,6 +46,10 @@ void GLTFDocumentExtension::_bind_methods() {
GDVIRTUAL_BIND(_export_preflight, "state", "root");
GDVIRTUAL_BIND(_convert_scene_node, "state", "gltf_node", "scene_node");
GDVIRTUAL_BIND(_export_preserialize, "state");
+ GDVIRTUAL_BIND(_get_saveable_image_formats);
+ GDVIRTUAL_BIND(_serialize_image_to_bytes, "state", "image", "image_dict", "image_format", "lossy_quality");
+ GDVIRTUAL_BIND(_save_image_at_path, "state", "image", "file_path", "image_format", "lossy_quality");
+ GDVIRTUAL_BIND(_serialize_texture_json, "state", "texture_json", "gltf_texture", "image_format");
GDVIRTUAL_BIND(_export_node, "state", "gltf_node", "json", "node");
GDVIRTUAL_BIND(_export_post, "state");
}
@@ -149,6 +153,36 @@ Error GLTFDocumentExtension::export_preserialize(Ref<GLTFState> p_state) {
return err;
}
+Vector<String> GLTFDocumentExtension::get_saveable_image_formats() {
+ Vector<String> ret;
+ GDVIRTUAL_CALL(_get_saveable_image_formats, ret);
+ return ret;
+}
+
+PackedByteArray GLTFDocumentExtension::serialize_image_to_bytes(Ref<GLTFState> p_state, Ref<Image> p_image, Dictionary p_image_dict, const String &p_image_format, float p_lossy_quality) {
+ PackedByteArray ret;
+ ERR_FAIL_NULL_V(p_state, ret);
+ ERR_FAIL_NULL_V(p_image, ret);
+ GDVIRTUAL_CALL(_serialize_image_to_bytes, p_state, p_image, p_image_dict, p_image_format, p_lossy_quality, ret);
+ return ret;
+}
+
+Error GLTFDocumentExtension::save_image_at_path(Ref<GLTFState> p_state, Ref<Image> p_image, const String &p_file_path, const String &p_image_format, float p_lossy_quality) {
+ ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER);
+ ERR_FAIL_NULL_V(p_image, ERR_INVALID_PARAMETER);
+ Error ret = OK;
+ GDVIRTUAL_CALL(_save_image_at_path, p_state, p_image, p_file_path, p_image_format, p_lossy_quality, ret);
+ return ret;
+}
+
+Error GLTFDocumentExtension::serialize_texture_json(Ref<GLTFState> p_state, Dictionary p_texture_json, Ref<GLTFTexture> p_gltf_texture, const String &p_image_format) {
+ ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER);
+ ERR_FAIL_NULL_V(p_gltf_texture, ERR_INVALID_PARAMETER);
+ Error err = OK;
+ GDVIRTUAL_CALL(_serialize_texture_json, p_state, p_texture_json, p_gltf_texture, p_image_format, err);
+ return err;
+}
+
Error GLTFDocumentExtension::export_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_dict, Node *p_node) {
ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER);
ERR_FAIL_NULL_V(p_gltf_node, ERR_INVALID_PARAMETER);
diff --git a/modules/gltf/extensions/gltf_document_extension.h b/modules/gltf/extensions/gltf_document_extension.h
index 0a631bb6c5..512b7aba91 100644
--- a/modules/gltf/extensions/gltf_document_extension.h
+++ b/modules/gltf/extensions/gltf_document_extension.h
@@ -47,14 +47,18 @@ public:
virtual Error parse_image_data(Ref<GLTFState> p_state, const PackedByteArray &p_image_data, const String &p_mime_type, Ref<Image> r_image);
virtual String get_image_file_extension();
virtual Error parse_texture_json(Ref<GLTFState> p_state, const Dictionary &p_texture_json, Ref<GLTFTexture> r_gltf_texture);
- virtual Node3D *generate_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_parent);
virtual Error import_post_parse(Ref<GLTFState> p_state);
+ virtual Node3D *generate_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_parent);
virtual Error import_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_json, Node *p_node);
virtual Error import_post(Ref<GLTFState> p_state, Node *p_node);
// Export process.
virtual Error export_preflight(Ref<GLTFState> p_state, Node *p_root);
virtual void convert_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_node);
virtual Error export_preserialize(Ref<GLTFState> p_state);
+ virtual Vector<String> get_saveable_image_formats();
+ virtual PackedByteArray serialize_image_to_bytes(Ref<GLTFState> p_state, Ref<Image> p_image, Dictionary p_image_dict, const String &p_image_format, float p_lossy_quality);
+ virtual Error save_image_at_path(Ref<GLTFState> p_state, Ref<Image> p_image, const String &p_file_path, const String &p_image_format, float p_lossy_quality);
+ virtual Error serialize_texture_json(Ref<GLTFState> p_state, Dictionary p_texture_json, Ref<GLTFTexture> p_gltf_texture, const String &p_image_format);
virtual Error export_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_json, Node *p_node);
virtual Error export_post(Ref<GLTFState> p_state);
@@ -73,6 +77,10 @@ public:
GDVIRTUAL2R(Error, _export_preflight, Ref<GLTFState>, Node *);
GDVIRTUAL3(_convert_scene_node, Ref<GLTFState>, Ref<GLTFNode>, Node *);
GDVIRTUAL1R(Error, _export_preserialize, Ref<GLTFState>);
+ GDVIRTUAL0R(Vector<String>, _get_saveable_image_formats);
+ GDVIRTUAL5R(PackedByteArray, _serialize_image_to_bytes, Ref<GLTFState>, Ref<Image>, Dictionary, String, float);
+ GDVIRTUAL5R(Error, _save_image_at_path, Ref<GLTFState>, Ref<Image>, String, String, float);
+ GDVIRTUAL4R(Error, _serialize_texture_json, Ref<GLTFState>, Dictionary, Ref<GLTFTexture>, String);
GDVIRTUAL4R(Error, _export_node, Ref<GLTFState>, Ref<GLTFNode>, Dictionary, Node *);
GDVIRTUAL1R(Error, _export_post, Ref<GLTFState>);
};
diff --git a/modules/gltf/extensions/gltf_document_extension_texture_webp.cpp b/modules/gltf/extensions/gltf_document_extension_texture_webp.cpp
index 73c869be3b..f8bd6d57cf 100644
--- a/modules/gltf/extensions/gltf_document_extension_texture_webp.cpp
+++ b/modules/gltf/extensions/gltf_document_extension_texture_webp.cpp
@@ -68,3 +68,42 @@ Error GLTFDocumentExtensionTextureWebP::parse_texture_json(Ref<GLTFState> p_stat
r_gltf_texture->set_src_image(texture_webp["source"]);
return OK;
}
+
+Vector<String> GLTFDocumentExtensionTextureWebP::get_saveable_image_formats() {
+ Vector<String> ret;
+ ret.push_back("Lossless WebP");
+ ret.push_back("Lossy WebP");
+ return ret;
+}
+
+PackedByteArray GLTFDocumentExtensionTextureWebP::serialize_image_to_bytes(Ref<GLTFState> p_state, Ref<Image> p_image, Dictionary p_image_dict, const String &p_image_format, float p_lossy_quality) {
+ if (p_image_format == "Lossless WebP") {
+ p_image_dict["mimeType"] = "image/webp";
+ return p_image->save_webp_to_buffer(false);
+ } else if (p_image_format == "Lossy WebP") {
+ p_image_dict["mimeType"] = "image/webp";
+ return p_image->save_webp_to_buffer(true, p_lossy_quality);
+ }
+ ERR_FAIL_V(PackedByteArray());
+}
+
+Error GLTFDocumentExtensionTextureWebP::save_image_at_path(Ref<GLTFState> p_state, Ref<Image> p_image, const String &p_file_path, const String &p_image_format, float p_lossy_quality) {
+ if (p_image_format == "Lossless WebP") {
+ p_image->save_webp(p_file_path, false);
+ return OK;
+ } else if (p_image_format == "Lossy WebP") {
+ p_image->save_webp(p_file_path, true, p_lossy_quality);
+ return OK;
+ }
+ return ERR_INVALID_PARAMETER;
+}
+
+Error GLTFDocumentExtensionTextureWebP::serialize_texture_json(Ref<GLTFState> p_state, Dictionary p_texture_json, Ref<GLTFTexture> p_gltf_texture, const String &p_image_format) {
+ Dictionary ext_texture_webp;
+ ext_texture_webp["source"] = p_gltf_texture->get_src_image();
+ Dictionary texture_extensions;
+ texture_extensions["EXT_texture_webp"] = ext_texture_webp;
+ p_texture_json["extensions"] = texture_extensions;
+ p_state->add_used_extension("EXT_texture_webp", true);
+ return OK;
+}
diff --git a/modules/gltf/extensions/gltf_document_extension_texture_webp.h b/modules/gltf/extensions/gltf_document_extension_texture_webp.h
index d2654aae8c..2113bd4768 100644
--- a/modules/gltf/extensions/gltf_document_extension_texture_webp.h
+++ b/modules/gltf/extensions/gltf_document_extension_texture_webp.h
@@ -43,6 +43,11 @@ public:
Error parse_image_data(Ref<GLTFState> p_state, const PackedByteArray &p_image_data, const String &p_mime_type, Ref<Image> r_image) override;
String get_image_file_extension() override;
Error parse_texture_json(Ref<GLTFState> p_state, const Dictionary &p_texture_json, Ref<GLTFTexture> r_gltf_texture) override;
+ // Export process.
+ Vector<String> get_saveable_image_formats() override;
+ PackedByteArray serialize_image_to_bytes(Ref<GLTFState> p_state, Ref<Image> p_image, Dictionary p_image_dict, const String &p_image_format, float p_lossy_quality) override;
+ Error save_image_at_path(Ref<GLTFState> p_state, Ref<Image> p_image, const String &p_full_path, const String &p_image_format, float p_lossy_quality) override;
+ Error serialize_texture_json(Ref<GLTFState> p_state, Dictionary p_texture_json, Ref<GLTFTexture> p_gltf_texture, const String &p_image_format) override;
};
#endif // GLTF_DOCUMENT_EXTENSION_TEXTURE_WEBP_H
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index 984052d2ca..e98b83359d 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -42,7 +42,6 @@
#include "core/io/stream_peer.h"
#include "core/math/disjoint_set.h"
#include "core/version.h"
-#include "drivers/png/png_driver_common.h"
#include "scene/3d/bone_attachment_3d.h"
#include "scene/3d/camera_3d.h"
#include "scene/3d/importer_mesh_instance_3d.h"
@@ -3001,8 +3000,35 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
return OK;
}
+void GLTFDocument::set_image_format(const String &p_image_format) {
+ _image_format = p_image_format;
+}
+
+String GLTFDocument::get_image_format() const {
+ return _image_format;
+}
+
+void GLTFDocument::set_lossy_quality(float p_lossy_quality) {
+ _lossy_quality = p_lossy_quality;
+}
+
+float GLTFDocument::get_lossy_quality() const {
+ return _lossy_quality;
+}
+
Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state) {
Array images;
+ // Check if any extension wants to be the image saver.
+ _image_save_extension = Ref<GLTFDocumentExtension>();
+ for (Ref<GLTFDocumentExtension> ext : document_extensions) {
+ ERR_CONTINUE(ext.is_null());
+ Vector<String> image_formats = ext->get_saveable_image_formats();
+ if (image_formats.has(_image_format)) {
+ _image_save_extension = ext;
+ break;
+ }
+ }
+ // Serialize every image in the state's images array.
for (int i = 0; i < p_state->images.size(); i++) {
Dictionary image_dict;
@@ -3010,6 +3036,10 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state) {
Ref<Image> image = p_state->images[i]->get_image();
ERR_CONTINUE(image.is_null());
+ if (image->is_compressed()) {
+ image->decompress();
+ ERR_FAIL_COND_V_MSG(image->is_compressed(), ERR_INVALID_DATA, "GLTF: Image was compressed, but could not be decompressed.");
+ }
if (p_state->filename.to_lower().ends_with("gltf")) {
String img_name = p_state->images[i]->get_name();
@@ -3017,14 +3047,26 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state) {
img_name = itos(i);
}
img_name = _gen_unique_name(p_state, img_name);
- img_name = img_name.pad_zeros(3) + ".png";
+ img_name = img_name.pad_zeros(3);
String relative_texture_dir = "textures";
String full_texture_dir = p_state->base_path.path_join(relative_texture_dir);
Ref<DirAccess> da = DirAccess::open(p_state->base_path);
if (!da->dir_exists(full_texture_dir)) {
da->make_dir(full_texture_dir);
}
- image->save_png(full_texture_dir.path_join(img_name));
+ if (_image_save_extension.is_valid()) {
+ img_name = img_name + _image_save_extension->get_image_file_extension();
+ Error err = _image_save_extension->save_image_at_path(p_state, image, full_texture_dir.path_join(img_name), _image_format, _lossy_quality);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "GLTF: Failed to save image in '" + _image_format + "' format as a separate file.");
+ } else if (_image_format == "PNG") {
+ img_name = img_name + ".png";
+ image->save_png(full_texture_dir.path_join(img_name));
+ } else if (_image_format == "JPEG") {
+ img_name = img_name + ".jpg";
+ image->save_jpg(full_texture_dir.path_join(img_name), _lossy_quality);
+ } else {
+ ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "GLTF: Unknown image format '" + _image_format + "'.");
+ }
image_dict["uri"] = relative_texture_dir.path_join(img_name).uri_encode();
} else {
GLTFBufferViewIndex bvi;
@@ -3042,8 +3084,20 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state) {
if (img_tex.is_valid()) {
image = img_tex->get_image();
}
- Error err = PNGDriverCommon::image_to_png(image, buffer);
- ERR_FAIL_COND_V_MSG(err, err, "Can't convert image to PNG.");
+ // Save in various image formats. Note that if the format is "None",
+ // the state's images will be empty, so this code will not be reached.
+ if (_image_save_extension.is_valid()) {
+ buffer = _image_save_extension->serialize_image_to_bytes(p_state, image, image_dict, _image_format, _lossy_quality);
+ } else if (_image_format == "PNG") {
+ buffer = image->save_png_to_buffer();
+ image_dict["mimeType"] = "image/png";
+ } else if (_image_format == "JPEG") {
+ buffer = image->save_jpg_to_buffer(_lossy_quality);
+ image_dict["mimeType"] = "image/jpeg";
+ } else {
+ ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "GLTF: Unknown image format '" + _image_format + "'.");
+ }
+ ERR_FAIL_COND_V_MSG(buffer.is_empty(), ERR_INVALID_DATA, "GLTF: Failed to save image in '" + _image_format + "' format.");
bv->byte_length = buffer.size();
p_state->buffers.write[bi].resize(p_state->buffers[bi].size() + bv->byte_length);
@@ -3053,7 +3107,6 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state) {
p_state->buffer_views.push_back(bv);
bvi = p_state->buffer_views.size() - 1;
image_dict["bufferView"] = bvi;
- image_dict["mimeType"] = "image/png";
}
images.push_back(image_dict);
}
@@ -3332,9 +3385,13 @@ Error GLTFDocument::_serialize_textures(Ref<GLTFState> p_state) {
for (int32_t i = 0; i < p_state->textures.size(); i++) {
Dictionary texture_dict;
Ref<GLTFTexture> gltf_texture = p_state->textures[i];
- ERR_CONTINUE(gltf_texture->get_src_image() == -1);
- texture_dict["source"] = gltf_texture->get_src_image();
-
+ if (_image_save_extension.is_valid()) {
+ Error err = _image_save_extension->serialize_texture_json(p_state, texture_dict, gltf_texture, _image_format);
+ ERR_FAIL_COND_V(err != OK, err);
+ } else {
+ ERR_CONTINUE(gltf_texture->get_src_image() == -1);
+ texture_dict["source"] = gltf_texture->get_src_image();
+ }
GLTFTextureSamplerIndex sampler_index = gltf_texture->get_sampler();
if (sampler_index != -1) {
texture_dict["sampler"] = sampler_index;
@@ -3543,7 +3600,7 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> p_state) {
arr.push_back(c.a);
mr["baseColorFactor"] = arr;
}
- {
+ if (_image_format != "None") {
Dictionary bct;
Ref<Texture2D> albedo_texture = base_material->get_texture(BaseMaterial3D::TEXTURE_ALBEDO);
GLTFTextureIndex gltf_texture_index = -1;
@@ -5280,7 +5337,7 @@ void GLTFDocument::_assign_node_names(Ref<GLTFState> p_state) {
if (gltf_node->mesh >= 0) {
gltf_node_name = "Mesh";
} else if (gltf_node->camera >= 0) {
- gltf_node_name = "Camera3D";
+ gltf_node_name = "Camera";
} else {
gltf_node_name = "Node";
}
@@ -5821,6 +5878,10 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> p_state, const GLTFNodeIn
current_node = _generate_spatial(p_state, p_node_index);
}
}
+ String gltf_node_name = gltf_node->get_name();
+ if (!gltf_node_name.is_empty()) {
+ current_node->set_name(gltf_node_name);
+ }
// Add the node we generated and set the owner to the scene root.
p_scene_parent->add_child(current_node, true);
if (current_node != p_scene_root) {
@@ -5829,7 +5890,6 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> p_state, const GLTFNodeIn
current_node->propagate_call(StringName("set_owner"), args);
}
current_node->set_transform(gltf_node->xform);
- current_node->set_name(gltf_node->get_name());
p_state->scene_nodes.insert(p_node_index, current_node);
for (int i = 0; i < gltf_node->children.size(); ++i) {
@@ -6443,7 +6503,7 @@ float GLTFDocument::get_max_component(const Color &p_color) {
return MAX(MAX(r, g), b);
}
-void GLTFDocument::_process_mesh_instances(Ref<GLTFState> p_state, Node *p_scene_root) {
+void GLTFDocument::_process_mesh_instances(Ref<GLTFState> p_state) {
for (GLTFNodeIndex node_i = 0; node_i < p_state->nodes.size(); ++node_i) {
Ref<GLTFNode> node = p_state->nodes[node_i];
@@ -7158,6 +7218,14 @@ void GLTFDocument::_bind_methods() {
ClassDB::bind_method(D_METHOD("write_to_filesystem", "state", "path"),
&GLTFDocument::write_to_filesystem);
+ ClassDB::bind_method(D_METHOD("set_image_format", "image_format"), &GLTFDocument::set_image_format);
+ ClassDB::bind_method(D_METHOD("get_image_format"), &GLTFDocument::get_image_format);
+ ClassDB::bind_method(D_METHOD("set_lossy_quality", "lossy_quality"), &GLTFDocument::set_lossy_quality);
+ ClassDB::bind_method(D_METHOD("get_lossy_quality"), &GLTFDocument::get_lossy_quality);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "image_format"), "set_image_format", "get_image_format");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lossy_quality"), "set_lossy_quality", "get_lossy_quality");
+
ClassDB::bind_static_method("GLTFDocument", D_METHOD("register_gltf_document_extension", "extension", "first_priority"),
&GLTFDocument::register_gltf_document_extension, DEFVAL(false));
ClassDB::bind_static_method("GLTFDocument", D_METHOD("unregister_gltf_document_extension", "extension"),
@@ -7266,15 +7334,28 @@ Error GLTFDocument::write_to_filesystem(Ref<GLTFState> p_state, const String &p_
return OK;
}
+Node *GLTFDocument::_generate_scene_node_tree(Ref<GLTFState> p_state) {
+ Node *single_root = memnew(Node3D);
+ for (int32_t root_i = 0; root_i < p_state->root_nodes.size(); root_i++) {
+ _generate_scene_node(p_state, p_state->root_nodes[root_i], single_root, single_root);
+ }
+ // Assign the scene name and single root name to each other
+ // if one is missing, or do nothing if both are already set.
+ if (unlikely(p_state->scene_name.is_empty())) {
+ p_state->scene_name = single_root->get_name();
+ } else if (single_root->get_name() == StringName()) {
+ single_root->set_name(_gen_unique_name(p_state, p_state->scene_name));
+ }
+ return single_root;
+}
+
Node *GLTFDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, bool p_trimming, bool p_remove_immutable_tracks) {
ERR_FAIL_NULL_V(p_state, nullptr);
ERR_FAIL_INDEX_V(0, p_state->root_nodes.size(), nullptr);
Error err = OK;
- GLTFNodeIndex gltf_root = p_state->root_nodes.write[0];
- Node *gltf_root_node = p_state->get_scene_node(gltf_root);
- Node *root = gltf_root_node->get_parent();
+ Node *root = _generate_scene_node_tree(p_state);
ERR_FAIL_NULL_V(root, nullptr);
- _process_mesh_instances(p_state, root);
+ _process_mesh_instances(p_state);
if (p_state->get_create_animations() && p_state->animations.size()) {
AnimationPlayer *ap = memnew(AnimationPlayer);
root->add_child(ap, true);
@@ -7454,11 +7535,6 @@ Error GLTFDocument::_parse_gltf_state(Ref<GLTFState> p_state, const String &p_se
/* ASSIGN SCENE NAMES */
_assign_node_names(p_state);
- Node3D *root = memnew(Node3D);
- for (int32_t root_i = 0; root_i < p_state->root_nodes.size(); root_i++) {
- _generate_scene_node(p_state, p_state->root_nodes[root_i], root, root);
- }
-
return OK;
}
diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h
index f2e36a0457..febe04b55f 100644
--- a/modules/gltf/gltf_document.h
+++ b/modules/gltf/gltf_document.h
@@ -42,6 +42,9 @@ class GLTFDocument : public Resource {
private:
const float BAKE_FPS = 30.0f;
+ String _image_format = "PNG";
+ float _lossy_quality = 0.75f;
+ Ref<GLTFDocumentExtension> _image_save_extension;
public:
const int32_t JOINT_GROUP_SIZE = 4;
@@ -77,6 +80,11 @@ public:
static void unregister_gltf_document_extension(Ref<GLTFDocumentExtension> p_extension);
static void unregister_all_gltf_document_extensions();
+ void set_image_format(const String &p_image_format);
+ String get_image_format() const;
+ void set_lossy_quality(float p_lossy_quality);
+ float get_lossy_quality() const;
+
private:
void _build_parent_hierachy(Ref<GLTFState> p_state);
double _filter_number(double p_float);
@@ -306,7 +314,8 @@ public:
Error _parse_gltf_state(Ref<GLTFState> p_state, const String &p_search_path);
Error _parse_asset_header(Ref<GLTFState> p_state);
Error _parse_gltf_extensions(Ref<GLTFState> p_state);
- void _process_mesh_instances(Ref<GLTFState> p_state, Node *p_scene_root);
+ void _process_mesh_instances(Ref<GLTFState> p_state);
+ Node *_generate_scene_node_tree(Ref<GLTFState> p_state);
void _generate_scene_node(Ref<GLTFState> p_state, const GLTFNodeIndex p_node_index, Node *p_scene_parent, Node *p_scene_root);
void _generate_skeleton_bone_node(Ref<GLTFState> p_state, const GLTFNodeIndex p_node_index, Node *p_scene_parent, Node *p_scene_root);
void _import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_animation_player,
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 9e23a27093..b41f2155f8 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -435,6 +435,11 @@ static String variant_type_to_managed_name(const String &p_var_type_name) {
return "Collections.Dictionary";
}
+ if (p_var_type_name.begins_with(Variant::get_type_name(Variant::ARRAY) + "[")) {
+ String element_type = p_var_type_name.trim_prefix(Variant::get_type_name(Variant::ARRAY) + "[").trim_suffix("]");
+ return "Collections.Array<" + variant_type_to_managed_name(element_type) + ">";
+ }
+
if (p_var_type_name == Variant::get_type_name(Variant::ARRAY)) {
return "Collections.Array";
}
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 9b41f9cd1b..839846f963 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -989,9 +989,6 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
p_output.append("namespace " BINDINGS_NAMESPACE ";\n\n");
- p_output.append("\n#pragma warning disable CS1591 // Disable warning: "
- "'Missing XML comment for publicly visible type or member'\n");
-
p_output.append("public static partial class " BINDINGS_GLOBAL_SCOPE_CLASS "\n{");
for (const ConstantInterface &iconstant : global_constants) {
@@ -1089,8 +1086,6 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
p_output.append(CLOSE_BLOCK);
}
}
-
- p_output.append("\n#pragma warning restore CS1591\n");
}
Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
@@ -1406,12 +1401,6 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
output.append("using System.Diagnostics;\n"); // DebuggerBrowsable
output.append("using Godot.NativeInterop;\n");
- output.append("\n"
- "#pragma warning disable CS1591 // Disable warning: "
- "'Missing XML comment for publicly visible type or member'\n"
- "#pragma warning disable CS1573 // Disable warning: "
- "'Parameter has no matching param tag in the XML comment'\n");
-
output.append("\n#nullable disable\n");
const DocData::ClassDoc *class_doc = itype.class_doc;
@@ -1904,10 +1893,6 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
output.append(CLOSE_BLOCK /* class */);
- output.append("\n"
- "#pragma warning restore CS1591\n"
- "#pragma warning restore CS1573\n");
-
return _save_file(p_output_file, output);
}
diff --git a/modules/mono/glue/GodotSharp/.editorconfig b/modules/mono/glue/GodotSharp/.editorconfig
index d4e71b1bd9..d0c660de4c 100644
--- a/modules/mono/glue/GodotSharp/.editorconfig
+++ b/modules/mono/glue/GodotSharp/.editorconfig
@@ -6,3 +6,7 @@ dotnet_diagnostic.CA1062.severity = error
dotnet_diagnostic.CA1069.severity = none
# CA1708: Identifiers should differ by more than case
dotnet_diagnostic.CA1708.severity = none
+# CS1591: Missing XML comment for publicly visible type or member
+dotnet_diagnostic.CS1591.severity = none
+# CS1573: Parameter has no matching param tag in the XML comment
+dotnet_diagnostic.CS1573.severity = none
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2I.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2I.cs
index 231e791904..b5ff744c55 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2I.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2I.cs
@@ -182,6 +182,9 @@ namespace Godot
}
// Constants
+ private static readonly Vector2I _min = new Vector2I(int.MinValue, int.MinValue);
+ private static readonly Vector2I _max = new Vector2I(int.MaxValue, int.MaxValue);
+
private static readonly Vector2I _zero = new Vector2I(0, 0);
private static readonly Vector2I _one = new Vector2I(1, 1);
@@ -191,6 +194,17 @@ namespace Godot
private static readonly Vector2I _left = new Vector2I(-1, 0);
/// <summary>
+ /// Min vector, a vector with all components equal to <see cref="int.MinValue"/>. Can be used as a negative integer equivalent of <see cref="Vector2.Inf"/>.
+ /// </summary>
+ /// <value>Equivalent to <c>new Vector2I(int.MinValue, int.MinValue)</c>.</value>
+ public static Vector2I Min { get { return _min; } }
+ /// <summary>
+ /// Max vector, a vector with all components equal to <see cref="int.MaxValue"/>. Can be used as an integer equivalent of <see cref="Vector2.Inf"/>.
+ /// </summary>
+ /// <value>Equivalent to <c>new Vector2I(int.MaxValue, int.MaxValue)</c>.</value>
+ public static Vector2I Max { get { return _max; } }
+
+ /// <summary>
/// Zero vector, a vector with all components set to <c>0</c>.
/// </summary>
/// <value>Equivalent to <c>new Vector2I(0, 0)</c>.</value>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3I.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3I.cs
index 8543052f56..62aa02e512 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3I.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3I.cs
@@ -193,6 +193,9 @@ namespace Godot
}
// Constants
+ private static readonly Vector3I _min = new Vector3I(int.MinValue, int.MinValue, int.MinValue);
+ private static readonly Vector3I _max = new Vector3I(int.MaxValue, int.MaxValue, int.MaxValue);
+
private static readonly Vector3I _zero = new Vector3I(0, 0, 0);
private static readonly Vector3I _one = new Vector3I(1, 1, 1);
@@ -204,6 +207,17 @@ namespace Godot
private static readonly Vector3I _back = new Vector3I(0, 0, 1);
/// <summary>
+ /// Min vector, a vector with all components equal to <see cref="int.MinValue"/>. Can be used as a negative integer equivalent of <see cref="Vector3.Inf"/>.
+ /// </summary>
+ /// <value>Equivalent to <c>new Vector3I(int.MinValue, int.MinValue, int.MinValue)</c>.</value>
+ public static Vector3I Min { get { return _min; } }
+ /// <summary>
+ /// Max vector, a vector with all components equal to <see cref="int.MaxValue"/>. Can be used as an integer equivalent of <see cref="Vector3.Inf"/>.
+ /// </summary>
+ /// <value>Equivalent to <c>new Vector3I(int.MaxValue, int.MaxValue, int.MaxValue)</c>.</value>
+ public static Vector3I Max { get { return _max; } }
+
+ /// <summary>
/// Zero vector, a vector with all components set to <c>0</c>.
/// </summary>
/// <value>Equivalent to <c>new Vector3I(0, 0, 0)</c>.</value>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4I.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4I.cs
index f813903177..56c1df4c64 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4I.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4I.cs
@@ -228,10 +228,24 @@ namespace Godot
}
// Constants
+ private static readonly Vector4I _min = new Vector4I(int.MinValue, int.MinValue, int.MinValue, int.MinValue);
+ private static readonly Vector4I _max = new Vector4I(int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue);
+
private static readonly Vector4I _zero = new Vector4I(0, 0, 0, 0);
private static readonly Vector4I _one = new Vector4I(1, 1, 1, 1);
/// <summary>
+ /// Min vector, a vector with all components equal to <see cref="int.MinValue"/>. Can be used as a negative integer equivalent of <see cref="Vector4.Inf"/>.
+ /// </summary>
+ /// <value>Equivalent to <c>new Vector4I(int.MinValue, int.MinValue, int.MinValue, int.MinValue)</c>.</value>
+ public static Vector4I Min { get { return _min; } }
+ /// <summary>
+ /// Max vector, a vector with all components equal to <see cref="int.MaxValue"/>. Can be used as an integer equivalent of <see cref="Vector4.Inf"/>.
+ /// </summary>
+ /// <value>Equivalent to <c>new Vector4I(int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue)</c>.</value>
+ public static Vector4I Max { get { return _max; } }
+
+ /// <summary>
/// Zero vector, a vector with all components set to <c>0</c>.
/// </summary>
/// <value>Equivalent to <c>new Vector4I(0, 0, 0, 0)</c>.</value>
diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp
index 32abc0a1c7..1f1c7e4ee0 100644
--- a/modules/openxr/openxr_api.cpp
+++ b/modules/openxr/openxr_api.cpp
@@ -2921,8 +2921,8 @@ bool OpenXRAPI::is_environment_blend_mode_supported(XrEnvironmentBlendMode p_ble
}
bool OpenXRAPI::set_environment_blend_mode(XrEnvironmentBlendMode p_blend_mode) {
- // We allow setting this when not initialised and will check if it is supported when initialising.
- // After OpenXR is initialised we verify we're setting a supported blend mode.
+ // We allow setting this when not initialized and will check if it is supported when initializing.
+ // After OpenXR is initialized we verify we're setting a supported blend mode.
if (!is_initialized() || is_environment_blend_mode_supported(p_blend_mode)) {
environment_blend_mode = p_blend_mode;
return true;
diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp
index b54335b724..b155dfc5d9 100644
--- a/modules/vorbis/audio_stream_ogg_vorbis.cpp
+++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp
@@ -328,9 +328,9 @@ void AudioStreamPlaybackOggVorbis::seek(double p_time) {
int64_t samples_to_burn = samples_in_page - (granule_pos - desired_sample);
if (samples_to_burn > samples_in_page) {
- WARN_PRINT("Burning more samples than we have in this page. Check seek algorithm.");
+ WARN_PRINT_ONCE("Burning more samples than we have in this page. Check seek algorithm.");
} else if (samples_to_burn < 0) {
- WARN_PRINT("Burning negative samples doesn't make sense. Check seek algorithm.");
+ WARN_PRINT_ONCE("Burning negative samples doesn't make sense. Check seek algorithm.");
}
// Seek again, this time we'll burn a specific number of samples instead of all of them.
diff --git a/modules/webrtc/register_types.cpp b/modules/webrtc/register_types.cpp
index 687c7b711e..28ce36f1e8 100644
--- a/modules/webrtc/register_types.cpp
+++ b/modules/webrtc/register_types.cpp
@@ -42,11 +42,7 @@ void initialize_webrtc_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
-
-#define SET_HINT(NAME, _VAL_, _MAX_) \
- GLOBAL_DEF(PropertyInfo(Variant::INT, NAME, PROPERTY_HINT_RANGE, "2," #_MAX_ ",1,or_greater"), _VAL_);
-
- SET_HINT(WRTC_IN_BUF, 64, 4096);
+ GLOBAL_DEF(PropertyInfo(Variant::INT, "network/limits/webrtc/max_channel_in_buffer_kb", PROPERTY_HINT_RANGE, "2,4096,1,or_greater"), 64);
ClassDB::register_custom_instance_class<WebRTCPeerConnection>();
GDREGISTER_CLASS(WebRTCPeerConnectionExtension);
@@ -55,8 +51,6 @@ void initialize_webrtc_module(ModuleInitializationLevel p_level) {
GDREGISTER_CLASS(WebRTCDataChannelExtension);
GDREGISTER_CLASS(WebRTCMultiplayerPeer);
-
-#undef SET_HINT
}
void uninitialize_webrtc_module(ModuleInitializationLevel p_level) {
diff --git a/modules/webrtc/webrtc_data_channel.cpp b/modules/webrtc/webrtc_data_channel.cpp
index bebf5c2741..6c0d0bea37 100644
--- a/modules/webrtc/webrtc_data_channel.cpp
+++ b/modules/webrtc/webrtc_data_channel.cpp
@@ -61,7 +61,7 @@ void WebRTCDataChannel::_bind_methods() {
}
WebRTCDataChannel::WebRTCDataChannel() {
- _in_buffer_shift = nearest_shift((int)GLOBAL_GET(WRTC_IN_BUF) - 1) + 10;
+ _in_buffer_shift = nearest_shift((int)GLOBAL_GET("network/limits/webrtc/max_channel_in_buffer_kb") - 1) + 10;
}
WebRTCDataChannel::~WebRTCDataChannel() {
diff --git a/modules/webrtc/webrtc_data_channel.h b/modules/webrtc/webrtc_data_channel.h
index e884c8425d..f35461a5a0 100644
--- a/modules/webrtc/webrtc_data_channel.h
+++ b/modules/webrtc/webrtc_data_channel.h
@@ -33,8 +33,6 @@
#include "core/io/packet_peer.h"
-#define WRTC_IN_BUF PNAME("network/limits/webrtc/max_channel_in_buffer_kb")
-
class WebRTCDataChannel : public PacketPeer {
GDCLASS(WebRTCDataChannel, PacketPeer);
diff --git a/platform/android/android_input_handler.cpp b/platform/android/android_input_handler.cpp
index f6a0776017..bd194478d9 100644
--- a/platform/android/android_input_handler.cpp
+++ b/platform/android/android_input_handler.cpp
@@ -292,7 +292,7 @@ void AndroidInputHandler::_release_mouse_event_info(bool p_source_mouse_relative
mouse_event_info.valid = false;
}
-void AndroidInputHandler::process_mouse_event(int p_event_action, int p_event_android_buttons_mask, Point2 p_event_pos, Vector2 p_delta, bool p_double_click, bool p_source_mouse_relative) {
+void AndroidInputHandler::process_mouse_event(int p_event_action, int p_event_android_buttons_mask, Point2 p_event_pos, Vector2 p_delta, bool p_double_click, bool p_source_mouse_relative, float p_pressure, Vector2 p_tilt) {
BitField<MouseButtonMask> event_buttons_mask = _android_button_mask_to_godot_button_mask(p_event_android_buttons_mask);
switch (p_event_action) {
case AMOTION_EVENT_ACTION_HOVER_MOVE: // hover move
@@ -349,6 +349,8 @@ void AndroidInputHandler::process_mouse_event(int p_event_action, int p_event_an
hover_prev_pos = p_event_pos;
}
ev->set_button_mask(event_buttons_mask);
+ ev->set_pressure(p_pressure);
+ ev->set_tilt(p_tilt);
Input::get_singleton()->parse_input_event(ev);
} break;
diff --git a/platform/android/android_input_handler.h b/platform/android/android_input_handler.h
index c74c5020e3..78a484cf05 100644
--- a/platform/android/android_input_handler.h
+++ b/platform/android/android_input_handler.h
@@ -96,7 +96,7 @@ private:
void _cancel_all_touch();
public:
- void process_mouse_event(int p_event_action, int p_event_android_buttons_mask, Point2 p_event_pos, Vector2 p_delta, bool p_double_click, bool p_source_mouse_relative);
+ void process_mouse_event(int p_event_action, int p_event_android_buttons_mask, Point2 p_event_pos, Vector2 p_delta, bool p_double_click, bool p_source_mouse_relative, float p_pressure, Vector2 p_tilt);
void process_touch_event(int p_event, int p_pointer, const Vector<TouchPos> &p_points, bool p_double_tap);
void process_magnify(Point2 p_pos, float p_factor);
void process_pan(Point2 p_pos, Vector2 p_delta);
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
index b9ecd6971d..fee50e93c2 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
@@ -114,7 +114,7 @@ public class GodotLib {
/**
* Dispatch mouse events
*/
- public static native void dispatchMouseEvent(int event, int buttonMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative);
+ public static native void dispatchMouseEvent(int event, int buttonMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative, float pressure, float tiltX, float tiltY);
public static native void magnify(float x, float y, float factor);
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
index 1a25be0460..c8b222254e 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
@@ -433,7 +433,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
}
private static boolean isMouseEvent(int eventSource) {
- boolean mouseSource = ((eventSource & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) || ((eventSource & (InputDevice.SOURCE_TOUCHSCREEN | InputDevice.SOURCE_STYLUS)) == InputDevice.SOURCE_STYLUS);
+ boolean mouseSource = ((eventSource & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) || ((eventSource & InputDevice.SOURCE_STYLUS) == InputDevice.SOURCE_STYLUS);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mouseSource = mouseSource || ((eventSource & InputDevice.SOURCE_MOUSE_RELATIVE) == InputDevice.SOURCE_MOUSE_RELATIVE);
}
@@ -470,13 +470,27 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
final float y = event.getY();
final int buttonsMask = event.getButtonState();
+ final float pressure = event.getPressure();
+
+ // Orientation is returned as a radian value between 0 to pi clockwise or 0 to -pi counterclockwise.
+ final float orientation = event.getOrientation();
+
+ // Tilt is zero is perpendicular to the screen and pi/2 is flat on the surface.
+ final float tilt = event.getAxisValue(MotionEvent.AXIS_TILT);
+
+ float tiltMult = (float)Math.sin(tilt);
+
+ // To be consistent with expected tilt.
+ final float tiltX = (float)-Math.sin(orientation) * tiltMult;
+ final float tiltY = (float)Math.cos(orientation) * tiltMult;
+
final float verticalFactor = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
final float horizontalFactor = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
boolean sourceMouseRelative = false;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
sourceMouseRelative = event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE);
}
- return handleMouseEvent(eventAction, buttonsMask, x, y, horizontalFactor, verticalFactor, false, sourceMouseRelative);
+ return handleMouseEvent(eventAction, buttonsMask, x, y, horizontalFactor, verticalFactor, false, sourceMouseRelative, pressure, tiltX, tiltY);
}
static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y) {
@@ -484,6 +498,10 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
}
static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative) {
+ return handleMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative, 1, 0, 0);
+ }
+
+ static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative, float pressure, float tiltX, float tiltY) {
// Fix the buttonsMask
switch (eventAction) {
case MotionEvent.ACTION_CANCEL:
@@ -511,7 +529,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
case MotionEvent.ACTION_HOVER_MOVE:
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_SCROLL: {
- GodotLib.dispatchMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative);
+ GodotLib.dispatchMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative, pressure, tiltX, tiltY);
return true;
}
}
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index 74605e3377..50075ed3f5 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -274,12 +274,12 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env,
}
// Called on the UI thread
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchMouseEvent(JNIEnv *env, jclass clazz, jint p_event_type, jint p_button_mask, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y, jboolean p_double_click, jboolean p_source_mouse_relative) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchMouseEvent(JNIEnv *env, jclass clazz, jint p_event_type, jint p_button_mask, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y, jboolean p_double_click, jboolean p_source_mouse_relative, jfloat p_pressure, jfloat p_tilt_x, jfloat p_tilt_y) {
if (step.get() <= 0) {
return;
}
- input_handler->process_mouse_event(p_event_type, p_button_mask, Point2(p_x, p_y), Vector2(p_delta_x, p_delta_y), p_double_click, p_source_mouse_relative);
+ input_handler->process_mouse_event(p_event_type, p_button_mask, Point2(p_x, p_y), Vector2(p_delta_x, p_delta_y), p_double_click, p_source_mouse_relative, p_pressure, Vector2(p_tilt_x, p_tilt_y));
}
// Called on the UI thread
diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h
index ee6a19034c..1ddda6c1c8 100644
--- a/platform/android/java_godot_lib_jni.h
+++ b/platform/android/java_godot_lib_jni.h
@@ -45,7 +45,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *en
JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ttsCallback(JNIEnv *env, jclass clazz, jint event, jint id, jint pos);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jclass clazz);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchMouseEvent(JNIEnv *env, jclass clazz, jint p_event_type, jint p_button_mask, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y, jboolean p_double_click, jboolean p_source_mouse_relative);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchMouseEvent(JNIEnv *env, jclass clazz, jint p_event_type, jint p_button_mask, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y, jboolean p_double_click, jboolean p_source_mouse_relative, jfloat p_pressure, jfloat p_tilt_x, jfloat p_tilt_y);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchTouchEvent(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jboolean p_double_tap);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnify(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_factor);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_pan(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y);
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index 6a9dd6137d..0a80467b6b 100644
--- a/scene/2d/audio_stream_player_2d.cpp
+++ b/scene/2d/audio_stream_player_2d.cpp
@@ -354,10 +354,6 @@ void AudioStreamPlayer2D::_validate_property(PropertyInfo &p_property) const {
}
}
-void AudioStreamPlayer2D::_bus_layout_changed() {
- notify_property_list_changed();
-}
-
void AudioStreamPlayer2D::set_max_distance(float p_pixels) {
ERR_FAIL_COND(p_pixels <= 0.0);
max_distance = p_pixels;
@@ -426,6 +422,14 @@ float AudioStreamPlayer2D::get_panning_strength() const {
return panning_strength;
}
+void AudioStreamPlayer2D::_on_bus_layout_changed() {
+ notify_property_list_changed();
+}
+
+void AudioStreamPlayer2D::_on_bus_renamed(int p_bus_index, const StringName &p_old_name, const StringName &p_new_name) {
+ notify_property_list_changed();
+}
+
void AudioStreamPlayer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer2D::set_stream);
ClassDB::bind_method(D_METHOD("get_stream"), &AudioStreamPlayer2D::get_stream);
@@ -490,7 +494,8 @@ void AudioStreamPlayer2D::_bind_methods() {
}
AudioStreamPlayer2D::AudioStreamPlayer2D() {
- AudioServer::get_singleton()->connect("bus_layout_changed", callable_mp(this, &AudioStreamPlayer2D::_bus_layout_changed));
+ AudioServer::get_singleton()->connect("bus_layout_changed", callable_mp(this, &AudioStreamPlayer2D::_on_bus_layout_changed));
+ AudioServer::get_singleton()->connect("bus_renamed", callable_mp(this, &AudioStreamPlayer2D::_on_bus_renamed));
cached_global_panning_strength = GLOBAL_GET("audio/general/2d_panning_strength");
set_hide_clip_children(true);
}
diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h
index a4677bef36..0766f2f77d 100644
--- a/scene/2d/audio_stream_player_2d.h
+++ b/scene/2d/audio_stream_player_2d.h
@@ -75,7 +75,9 @@ private:
StringName _get_actual_bus();
void _update_panning();
- void _bus_layout_changed();
+
+ void _on_bus_layout_changed();
+ void _on_bus_renamed(int p_bus_index, const StringName &p_old_name, const StringName &p_new_name);
static void _listener_changed_cb(void *self) { reinterpret_cast<AudioStreamPlayer2D *>(self)->force_update_panning = true; }
diff --git a/scene/2d/joint_2d.cpp b/scene/2d/joint_2d.cpp
index ce427d47aa..62b44655c1 100644
--- a/scene/2d/joint_2d.cpp
+++ b/scene/2d/joint_2d.cpp
@@ -235,6 +235,8 @@ void Joint2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_exclude_nodes_from_collision", "enable"), &Joint2D::set_exclude_nodes_from_collision);
ClassDB::bind_method(D_METHOD("get_exclude_nodes_from_collision"), &Joint2D::get_exclude_nodes_from_collision);
+ ClassDB::bind_method(D_METHOD("get_rid"), &Joint2D::get_rid);
+
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_a", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "PhysicsBody2D"), "set_node_a", "get_node_a");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_b", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "PhysicsBody2D"), "set_node_b", "get_node_b");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bias", PROPERTY_HINT_RANGE, "0,0.9,0.001"), "set_bias", "get_bias");
@@ -281,7 +283,7 @@ void PinJoint2D::set_softness(real_t p_softness) {
softness = p_softness;
queue_redraw();
if (is_configured()) {
- PhysicsServer2D::get_singleton()->pin_joint_set_param(get_joint(), PhysicsServer2D::PIN_JOINT_SOFTNESS, p_softness);
+ PhysicsServer2D::get_singleton()->pin_joint_set_param(get_rid(), PhysicsServer2D::PIN_JOINT_SOFTNESS, p_softness);
}
}
@@ -410,7 +412,7 @@ void DampedSpringJoint2D::set_rest_length(real_t p_rest_length) {
rest_length = p_rest_length;
queue_redraw();
if (is_configured()) {
- PhysicsServer2D::get_singleton()->damped_spring_joint_set_param(get_joint(), PhysicsServer2D::DAMPED_SPRING_REST_LENGTH, p_rest_length ? p_rest_length : length);
+ PhysicsServer2D::get_singleton()->damped_spring_joint_set_param(get_rid(), PhysicsServer2D::DAMPED_SPRING_REST_LENGTH, p_rest_length ? p_rest_length : length);
}
}
@@ -422,7 +424,7 @@ void DampedSpringJoint2D::set_stiffness(real_t p_stiffness) {
stiffness = p_stiffness;
queue_redraw();
if (is_configured()) {
- PhysicsServer2D::get_singleton()->damped_spring_joint_set_param(get_joint(), PhysicsServer2D::DAMPED_SPRING_STIFFNESS, p_stiffness);
+ PhysicsServer2D::get_singleton()->damped_spring_joint_set_param(get_rid(), PhysicsServer2D::DAMPED_SPRING_STIFFNESS, p_stiffness);
}
}
@@ -434,7 +436,7 @@ void DampedSpringJoint2D::set_damping(real_t p_damping) {
damping = p_damping;
queue_redraw();
if (is_configured()) {
- PhysicsServer2D::get_singleton()->damped_spring_joint_set_param(get_joint(), PhysicsServer2D::DAMPED_SPRING_DAMPING, p_damping);
+ PhysicsServer2D::get_singleton()->damped_spring_joint_set_param(get_rid(), PhysicsServer2D::DAMPED_SPRING_DAMPING, p_damping);
}
}
diff --git a/scene/2d/joint_2d.h b/scene/2d/joint_2d.h
index b23fd8028d..581a3b6757 100644
--- a/scene/2d/joint_2d.h
+++ b/scene/2d/joint_2d.h
@@ -76,7 +76,7 @@ public:
void set_exclude_nodes_from_collision(bool p_enable);
bool get_exclude_nodes_from_collision() const;
- RID get_joint() const { return joint; }
+ RID get_rid() const { return joint; }
Joint2D();
~Joint2D();
};
diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp
index 3a9473d76c..be58875e0e 100644
--- a/scene/2d/line_2d.cpp
+++ b/scene/2d/line_2d.cpp
@@ -42,12 +42,12 @@ Rect2 Line2D::_edit_get_rect() const {
return Rect2(0, 0, 0, 0);
}
Vector2 d = Vector2(_width, _width);
- Rect2 aabb = Rect2(_points[0] - d, 2 * d);
+ Rect2 bounding_rect = Rect2(_points[0] - d, 2 * d);
for (int i = 1; i < _points.size(); i++) {
- aabb.expand_to(_points[i] - d);
- aabb.expand_to(_points[i] + d);
+ bounding_rect.expand_to(_points[i] - d);
+ bounding_rect.expand_to(_points[i] + d);
}
- return aabb;
+ return bounding_rect;
}
bool Line2D::_edit_use_rect() const {
@@ -59,7 +59,14 @@ bool Line2D::_edit_is_selected_on_click(const Point2 &p_point, double p_toleranc
const Vector2 *points = _points.ptr();
for (int i = 0; i < _points.size() - 1; i++) {
Vector2 p = Geometry2D::get_closest_point_to_segment(p_point, &points[i]);
- if (p.distance_to(p_point) <= d) {
+ if (p_point.distance_to(p) <= d) {
+ return true;
+ }
+ }
+ if (_closed && _points.size() > 2) {
+ const Vector2 closing_segment[2] = { points[0], points[_points.size() - 1] };
+ Vector2 p = Geometry2D::get_closest_point_to_segment(p_point, closing_segment);
+ if (p_point.distance_to(p) <= d) {
return true;
}
}
@@ -73,6 +80,15 @@ void Line2D::set_points(const Vector<Vector2> &p_points) {
queue_redraw();
}
+void Line2D::set_closed(bool p_closed) {
+ _closed = p_closed;
+ queue_redraw();
+}
+
+bool Line2D::is_closed() const {
+ return _closed;
+}
+
void Line2D::set_width(float p_width) {
if (p_width < 0.0) {
p_width = 0.0;
@@ -86,14 +102,12 @@ float Line2D::get_width() const {
}
void Line2D::set_curve(const Ref<Curve> &p_curve) {
- // Cleanup previous connection if any
if (_curve.is_valid()) {
_curve->disconnect_changed(callable_mp(this, &Line2D::_curve_changed));
}
_curve = p_curve;
- // Connect to the curve so the line will update when it is changed
if (_curve.is_valid()) {
_curve->connect_changed(callable_mp(this, &Line2D::_curve_changed));
}
@@ -156,14 +170,12 @@ Color Line2D::get_default_color() const {
}
void Line2D::set_gradient(const Ref<Gradient> &p_gradient) {
- // Cleanup previous connection if any
if (_gradient.is_valid()) {
_gradient->disconnect_changed(callable_mp(this, &Line2D::_gradient_changed));
}
_gradient = p_gradient;
- // Connect to the gradient so the line will update when the Gradient is changed
if (_gradient.is_valid()) {
_gradient->connect_changed(callable_mp(this, &Line2D::_gradient_changed));
}
@@ -264,20 +276,10 @@ void Line2D::_draw() {
return;
}
- // TODO Is this really needed?
- // Copy points for faster access
- Vector<Vector2> points;
- points.resize(len);
- {
- const Vector2 *points_read = _points.ptr();
- for (int i = 0; i < len; ++i) {
- points.write[i] = points_read[i];
- }
- }
-
// TODO Maybe have it as member rather than copying parameters and allocating memory?
LineBuilder lb;
- lb.points = points;
+ lb.points = _points;
+ lb.closed = _closed;
lb.default_color = _default_color;
lb.gradient = *_gradient;
lb.texture_mode = _texture_mode;
@@ -306,13 +308,10 @@ void Line2D::_draw() {
lb.uvs, Vector<int>(), Vector<float>(),
texture_rid);
- // DEBUG
- // Draw wireframe
- // if(lb.indices.size() % 3 == 0) {
- // Color col(0,0,0);
- // for(int i = 0; i < lb.indices.size(); i += 3) {
- // int vi = lb.indices[i];
- // int lbvsize = lb.vertices.size();
+ // DEBUG: Draw wireframe
+ // if (lb.indices.size() % 3 == 0) {
+ // Color col(0, 0, 0);
+ // for (int i = 0; i < lb.indices.size(); i += 3) {
// Vector2 a = lb.vertices[lb.indices[i]];
// Vector2 b = lb.vertices[lb.indices[i+1]];
// Vector2 c = lb.vertices[lb.indices[i+2]];
@@ -320,9 +319,9 @@ void Line2D::_draw() {
// draw_line(b, c, col);
// draw_line(c, a, col);
// }
- // for(int i = 0; i < lb.vertices.size(); ++i) {
+ // for (int i = 0; i < lb.vertices.size(); ++i) {
// Vector2 p = lb.vertices[i];
- // draw_rect(Rect2(p.x-1, p.y-1, 2, 2), Color(0,0,0,0.5));
+ // draw_rect(Rect2(p.x - 1, p.y - 1, 2, 2), Color(0, 0, 0, 0.5));
// }
// }
}
@@ -350,6 +349,9 @@ void Line2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear_points"), &Line2D::clear_points);
+ ClassDB::bind_method(D_METHOD("set_closed", "closed"), &Line2D::set_closed);
+ ClassDB::bind_method(D_METHOD("is_closed"), &Line2D::is_closed);
+
ClassDB::bind_method(D_METHOD("set_width", "width"), &Line2D::set_width);
ClassDB::bind_method(D_METHOD("get_width"), &Line2D::get_width);
@@ -387,6 +389,7 @@ void Line2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_antialiased"), &Line2D::get_antialiased);
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "points"), "set_points", "get_points");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "closed"), "set_closed", "is_closed");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width", PROPERTY_HINT_NONE, "suffix:px"), "set_width", "get_width");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "width_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "default_color"), "set_default_color", "get_default_color");
diff --git a/scene/2d/line_2d.h b/scene/2d/line_2d.h
index aa873ca642..1d750ca456 100644
--- a/scene/2d/line_2d.h
+++ b/scene/2d/line_2d.h
@@ -76,6 +76,9 @@ public:
void add_point(Vector2 pos, int atpos = -1);
void remove_point(int i);
+ void set_closed(bool p_closed);
+ bool is_closed() const;
+
void set_width(float width);
float get_width() const;
@@ -127,6 +130,7 @@ private:
LineJointMode _joint_mode = LINE_JOINT_SHARP;
LineCapMode _begin_cap_mode = LINE_CAP_NONE;
LineCapMode _end_cap_mode = LINE_CAP_NONE;
+ bool _closed = false;
float _width = 10.0;
Ref<Curve> _curve;
Color _default_color = Color(1, 1, 1);
diff --git a/scene/2d/line_builder.cpp b/scene/2d/line_builder.cpp
index 0970325502..e898dc7fab 100644
--- a/scene/2d/line_builder.cpp
+++ b/scene/2d/line_builder.cpp
@@ -30,75 +30,25 @@
#include "line_builder.h"
-//----------------------------------------------------------------------------
-// Util
-//----------------------------------------------------------------------------
-
-enum SegmentIntersectionResult {
- SEGMENT_PARALLEL = 0,
- SEGMENT_NO_INTERSECT = 1,
- SEGMENT_INTERSECT = 2
-};
-
-static SegmentIntersectionResult segment_intersection(
- Vector2 a, Vector2 b, Vector2 c, Vector2 d,
- Vector2 *out_intersection) {
- // http://paulbourke.net/geometry/pointlineplane/ <-- Good stuff
- Vector2 cd = d - c;
- Vector2 ab = b - a;
- float div = cd.y * ab.x - cd.x * ab.y;
-
- if (Math::abs(div) > 0.001f) {
- float ua = (cd.x * (a.y - c.y) - cd.y * (a.x - c.x)) / div;
- float ub = (ab.x * (a.y - c.y) - ab.y * (a.x - c.x)) / div;
- *out_intersection = a + ua * ab;
- if (ua >= 0.f && ua <= 1.f &&
- ub >= 0.f && ub <= 1.f) {
- return SEGMENT_INTERSECT;
- }
- return SEGMENT_NO_INTERSECT;
- }
-
- return SEGMENT_PARALLEL;
-}
-
-static float calculate_total_distance(const Vector<Vector2> &points) {
- float d = 0.f;
- for (int i = 1; i < points.size(); ++i) {
- d += points[i].distance_to(points[i - 1]);
- }
- return d;
-}
-
-static inline Vector2 rotate90(const Vector2 &v) {
- // Note: the 2D referential is X-right, Y-down
- return Vector2(v.y, -v.x);
-}
+#include "core/math/geometry_2d.h"
+// Utility method.
static inline Vector2 interpolate(const Rect2 &r, const Vector2 &v) {
return Vector2(
Math::lerp(r.position.x, r.position.x + r.get_size().x, v.x),
Math::lerp(r.position.y, r.position.y + r.get_size().y, v.y));
}
-//----------------------------------------------------------------------------
-// LineBuilder
-//----------------------------------------------------------------------------
-
LineBuilder::LineBuilder() {
}
-void LineBuilder::clear_output() {
- vertices.clear();
- colors.clear();
- indices.clear();
- uvs.clear();
-}
-
void LineBuilder::build() {
- // Need at least 2 points to draw a line
+ // Need at least 2 points to draw a line, so clear the output and return.
if (points.size() < 2) {
- clear_output();
+ vertices.clear();
+ colors.clear();
+ indices.clear();
+ uvs.clear();
return;
}
@@ -107,14 +57,21 @@ void LineBuilder::build() {
const float hw = width / 2.f;
const float hw_sq = hw * hw;
const float sharp_limit_sq = sharp_limit * sharp_limit;
- const int len = points.size();
+ const int point_count = points.size();
+ const bool wrap_around = closed && point_count > 2;
+
+ _interpolate_color = gradient != nullptr;
+ const bool retrieve_curve = curve != nullptr;
+ const bool distance_required = _interpolate_color || retrieve_curve ||
+ texture_mode == Line2D::LINE_TEXTURE_TILE ||
+ texture_mode == Line2D::LINE_TEXTURE_STRETCH;
// Initial values
Vector2 pos0 = points[0];
Vector2 pos1 = points[1];
Vector2 f0 = (pos1 - pos0).normalized();
- Vector2 u0 = rotate90(f0);
+ Vector2 u0 = f0.orthogonal();
Vector2 pos_up0 = pos0;
Vector2 pos_down0 = pos0;
@@ -124,32 +81,37 @@ void LineBuilder::build() {
float current_distance0 = 0.f;
float current_distance1 = 0.f;
float total_distance = 0.f;
+
float width_factor = 1.f;
- _interpolate_color = gradient != nullptr;
- bool retrieve_curve = curve != nullptr;
- bool distance_required = _interpolate_color ||
- retrieve_curve ||
- texture_mode == Line2D::LINE_TEXTURE_TILE ||
- texture_mode == Line2D::LINE_TEXTURE_STRETCH;
+ float modified_hw = hw;
+ if (retrieve_curve) {
+ width_factor = curve->sample_baked(0.f);
+ modified_hw = hw * width_factor;
+ }
+
if (distance_required) {
- total_distance = calculate_total_distance(points);
- //Adjust totalDistance.
- // The line's outer length will be a little higher due to begin and end caps
- if (begin_cap_mode == Line2D::LINE_CAP_BOX || begin_cap_mode == Line2D::LINE_CAP_ROUND) {
- if (retrieve_curve) {
- total_distance += width * curve->sample_baked(0.f) * 0.5f;
- } else {
- total_distance += width * 0.5f;
- }
+ // Calculate the total distance.
+ for (int i = 1; i < point_count; ++i) {
+ total_distance += points[i].distance_to(points[i - 1]);
}
- if (end_cap_mode == Line2D::LINE_CAP_BOX || end_cap_mode == Line2D::LINE_CAP_ROUND) {
- if (retrieve_curve) {
- total_distance += width * curve->sample_baked(1.f) * 0.5f;
- } else {
- total_distance += width * 0.5f;
+ if (wrap_around) {
+ total_distance += points[point_count - 1].distance_to(pos0);
+ } else {
+ // Adjust the total distance.
+ // The line's outer length may be a little higher due to the end caps.
+ if (begin_cap_mode == Line2D::LINE_CAP_BOX || begin_cap_mode == Line2D::LINE_CAP_ROUND) {
+ total_distance += modified_hw;
+ }
+ if (end_cap_mode == Line2D::LINE_CAP_BOX || end_cap_mode == Line2D::LINE_CAP_ROUND) {
+ if (retrieve_curve) {
+ total_distance += hw * curve->sample_baked(1.f);
+ } else {
+ total_distance += hw;
+ }
}
}
}
+
if (_interpolate_color) {
color0 = gradient->get_color(0);
} else {
@@ -159,34 +121,31 @@ void LineBuilder::build() {
float uvx0 = 0.f;
float uvx1 = 0.f;
- if (retrieve_curve) {
- width_factor = curve->sample_baked(0.f);
- }
-
- pos_up0 += u0 * hw * width_factor;
- pos_down0 -= u0 * hw * width_factor;
+ pos_up0 += u0 * modified_hw;
+ pos_down0 -= u0 * modified_hw;
// Begin cap
- if (begin_cap_mode == Line2D::LINE_CAP_BOX) {
- // Push back first vertices a little bit
- pos_up0 -= f0 * hw * width_factor;
- pos_down0 -= f0 * hw * width_factor;
-
- current_distance0 += hw * width_factor;
- current_distance1 = current_distance0;
- } else if (begin_cap_mode == Line2D::LINE_CAP_ROUND) {
- if (texture_mode == Line2D::LINE_TEXTURE_TILE) {
- uvx0 = width_factor * 0.5f / tile_aspect;
- } else if (texture_mode == Line2D::LINE_TEXTURE_STRETCH) {
- uvx0 = width * width_factor / total_distance;
+ if (!wrap_around) {
+ if (begin_cap_mode == Line2D::LINE_CAP_BOX) {
+ // Push back first vertices a little bit.
+ pos_up0 -= f0 * modified_hw;
+ pos_down0 -= f0 * modified_hw;
+
+ current_distance0 += modified_hw;
+ current_distance1 = current_distance0;
+ } else if (begin_cap_mode == Line2D::LINE_CAP_ROUND) {
+ if (texture_mode == Line2D::LINE_TEXTURE_TILE) {
+ uvx0 = width_factor * 0.5f / tile_aspect;
+ } else if (texture_mode == Line2D::LINE_TEXTURE_STRETCH) {
+ uvx0 = width * width_factor / total_distance;
+ }
+ new_arc(pos0, pos_up0 - pos0, -Math_PI, color0, Rect2(0.f, 0.f, uvx0 * 2, 1.f));
+ current_distance0 += modified_hw;
+ current_distance1 = current_distance0;
}
- new_arc(pos0, pos_up0 - pos0, -Math_PI, color0, Rect2(0.f, 0.f, uvx0 * 2, 1.f));
- current_distance0 += hw * width_factor;
- current_distance1 = current_distance0;
+ strip_begin(pos_up0, pos_down0, color0, uvx0);
}
- strip_begin(pos_up0, pos_down0, color0, uvx0);
-
/*
* pos_up0 ------------- pos_up1 --------------------
* | |
@@ -200,19 +159,30 @@ void LineBuilder::build() {
// http://labs.hyperandroid.com/tag/opengl-lines
// (not the same implementation but visuals help a lot)
+ // If the polyline wraps around, then draw two more segments with joints:
+ // The last one, which should normally end with an end cap, and the one that matches the end and the beginning.
+ int segments_count = wrap_around ? point_count : (point_count - 2);
+ // The wraparound case starts with a "fake walk" from the end of the polyline
+ // to its beginning, so that its first joint is correct, without drawing anything.
+ int first_point = wrap_around ? -1 : 1;
+
+ // If the line wraps around, these variables will be used for the final segment.
+ Vector2 first_pos_up, first_pos_down;
+ bool is_first_joint_sharp = false;
+
// For each additional segment
- for (int i = 1; i < len - 1; ++i) {
- pos1 = points[i];
- Vector2 pos2 = points[i + 1];
+ for (int i = first_point; i <= segments_count; ++i) {
+ pos1 = points[(i == -1) ? point_count - 1 : i % point_count]; // First point.
+ Vector2 pos2 = points[(i + 1) % point_count]; // Second point.
Vector2 f1 = (pos2 - pos1).normalized();
- Vector2 u1 = rotate90(f1);
+ Vector2 u1 = f1.orthogonal();
- // Determine joint orientation
- const float dp = u0.dot(f1);
+ // Determine joint orientation.
+ float dp = u0.dot(f1);
const Orientation orientation = (dp > 0.f ? UP : DOWN);
- if (distance_required) {
+ if (distance_required && i >= 1) {
current_distance1 += pos0.distance_to(pos1);
}
if (_interpolate_color) {
@@ -220,15 +190,14 @@ void LineBuilder::build() {
}
if (retrieve_curve) {
width_factor = curve->sample_baked(current_distance1 / total_distance);
+ modified_hw = hw * width_factor;
}
- Vector2 inner_normal0, inner_normal1;
- if (orientation == UP) {
- inner_normal0 = u0 * hw * width_factor;
- inner_normal1 = u1 * hw * width_factor;
- } else {
- inner_normal0 = -u0 * hw * width_factor;
- inner_normal1 = -u1 * hw * width_factor;
+ Vector2 inner_normal0 = u0 * modified_hw;
+ Vector2 inner_normal1 = u1 * modified_hw;
+ if (orientation == DOWN) {
+ inner_normal0 = -inner_normal0;
+ inner_normal1 = -inner_normal1;
}
/*
@@ -245,18 +214,18 @@ void LineBuilder::build() {
* /
*/
- // Find inner intersection at the joint
+ // Find inner intersection at the joint.
Vector2 corner_pos_in, corner_pos_out;
- SegmentIntersectionResult intersection_result = segment_intersection(
+ bool is_intersecting = Geometry2D::segment_intersects_segment(
pos0 + inner_normal0, pos1 + inner_normal0,
pos1 + inner_normal1, pos2 + inner_normal1,
&corner_pos_in);
- if (intersection_result == SEGMENT_INTERSECT) {
- // Inner parts of the segments intersect
+ if (is_intersecting) {
+ // Inner parts of the segments intersect.
corner_pos_out = 2.f * pos1 - corner_pos_in;
} else {
- // No intersection, segments are either parallel or too sharp
+ // No intersection, segments are too sharp or they overlap.
corner_pos_in = pos1 + inner_normal0;
corner_pos_out = pos1 - inner_normal0;
}
@@ -273,8 +242,8 @@ void LineBuilder::build() {
Line2D::LineJointMode current_joint_mode = joint_mode;
Vector2 pos_up1, pos_down1;
- if (intersection_result == SEGMENT_INTERSECT) {
- // Fallback on bevel if sharp angle is too high (because it would produce very long miters)
+ if (is_intersecting) {
+ // Fallback on bevel if sharp angle is too high (because it would produce very long miters).
float width_factor_sq = width_factor * width_factor;
if (current_joint_mode == Line2D::LINE_JOINT_SHARP && corner_pos_out.distance_squared_to(pos1) / (hw_sq * width_factor_sq) > sharp_limit_sq) {
current_joint_mode = Line2D::LINE_JOINT_BEVEL;
@@ -288,57 +257,78 @@ void LineBuilder::build() {
// Bevel or round
if (orientation == UP) {
pos_up1 = corner_pos_up;
- pos_down1 = pos1 - u0 * hw * width_factor;
+ pos_down1 = pos1 - u0 * modified_hw;
} else {
- pos_up1 = pos1 + u0 * hw * width_factor;
+ pos_up1 = pos1 + u0 * modified_hw;
pos_down1 = corner_pos_down;
}
}
} else {
// No intersection: fallback
if (current_joint_mode == Line2D::LINE_JOINT_SHARP) {
- // There is no fallback implementation for LINE_JOINT_SHARP so switch to the LINE_JOINT_BEVEL
+ // There is no fallback implementation for LINE_JOINT_SHARP so switch to the LINE_JOINT_BEVEL.
current_joint_mode = Line2D::LINE_JOINT_BEVEL;
}
pos_up1 = corner_pos_up;
pos_down1 = corner_pos_down;
}
- // Add current line body quad
- // Triangles are clockwise
+ // Triangles are clockwise.
if (texture_mode == Line2D::LINE_TEXTURE_TILE) {
uvx1 = current_distance1 / (width * tile_aspect);
} else if (texture_mode == Line2D::LINE_TEXTURE_STRETCH) {
uvx1 = current_distance1 / total_distance;
}
- strip_add_quad(pos_up1, pos_down1, color1, uvx1);
-
- // Swap vars for use in the next line
+ // Swap vars for use in the next line.
color0 = color1;
u0 = u1;
f0 = f1;
pos0 = pos1;
- if (intersection_result == SEGMENT_INTERSECT) {
+ if (is_intersecting) {
if (current_joint_mode == Line2D::LINE_JOINT_SHARP) {
pos_up0 = pos_up1;
pos_down0 = pos_down1;
} else {
if (orientation == UP) {
pos_up0 = corner_pos_up;
- pos_down0 = pos1 - u1 * hw * width_factor;
+ pos_down0 = pos1 - u1 * modified_hw;
} else {
- pos_up0 = pos1 + u1 * hw * width_factor;
+ pos_up0 = pos1 + u1 * modified_hw;
pos_down0 = corner_pos_down;
}
}
} else {
- pos_up0 = pos1 + u1 * hw * width_factor;
- pos_down0 = pos1 - u1 * hw * width_factor;
+ pos_up0 = pos1 + u1 * modified_hw;
+ pos_down0 = pos1 - u1 * modified_hw;
+ }
+
+ // End the "fake pass" in the closed line case before the drawing subroutine.
+ if (i == -1) {
+ continue;
+ }
+
+ // For wrap-around polylines, store some kind of start positions of the first joint for the final connection.
+ if (wrap_around && i == 0) {
+ Vector2 first_pos_center = (pos_up1 + pos_down1) / 2;
+ float lerp_factor = 1.0 / width_factor;
+ first_pos_up = first_pos_center.lerp(pos_up1, lerp_factor);
+ first_pos_down = first_pos_center.lerp(pos_down1, lerp_factor);
+ is_first_joint_sharp = current_joint_mode == Line2D::LINE_JOINT_SHARP;
+ }
+
+ // Add current line body quad.
+ if (wrap_around && retrieve_curve && !is_first_joint_sharp && i == segments_count) {
+ // If the width curve is not seamless, we might need to fetch the line's start points to use them for the final connection.
+ Vector2 first_pos_center = (first_pos_up + first_pos_down) / 2;
+ strip_add_quad(first_pos_center.lerp(first_pos_up, width_factor), first_pos_center.lerp(first_pos_down, width_factor), color1, uvx1);
+ return;
+ } else {
+ strip_add_quad(pos_up1, pos_down1, color1, uvx1);
}
- // From this point, bu0 and bd0 concern the next segment
- // Add joint geometry
+ // From this point, bu0 and bd0 concern the next segment.
+ // Add joint geometry.
if (current_joint_mode != Line2D::LINE_JOINT_SHARP) {
/* ________________ cbegin
* / \
@@ -358,64 +348,68 @@ void LineBuilder::build() {
cend = pos_up0;
}
- if (current_joint_mode == Line2D::LINE_JOINT_BEVEL) {
+ if (current_joint_mode == Line2D::LINE_JOINT_BEVEL && !(wrap_around && i == segments_count)) {
strip_add_tri(cend, orientation);
- } else if (current_joint_mode == Line2D::LINE_JOINT_ROUND) {
+ } else if (current_joint_mode == Line2D::LINE_JOINT_ROUND && !(wrap_around && i == segments_count)) {
Vector2 vbegin = cbegin - pos1;
Vector2 vend = cend - pos1;
strip_add_arc(pos1, vbegin.angle_to(vend), orientation);
}
- if (intersection_result != SEGMENT_INTERSECT) {
+ if (!is_intersecting) {
// In this case the joint is too corrupted to be re-used,
// start again the strip with fallback points
strip_begin(pos_up0, pos_down0, color1, uvx1);
}
}
}
- // Last (or only) segment
- pos1 = points[points.size() - 1];
- if (distance_required) {
- current_distance1 += pos0.distance_to(pos1);
- }
- if (_interpolate_color) {
- color1 = gradient->get_color(gradient->get_point_count() - 1);
- }
- if (retrieve_curve) {
- width_factor = curve->sample_baked(1.f);
- }
+ // Draw the last (or only) segment, with its end cap logic.
+ if (!wrap_around) {
+ pos1 = points[point_count - 1];
- Vector2 pos_up1 = pos1 + u0 * hw * width_factor;
- Vector2 pos_down1 = pos1 - u0 * hw * width_factor;
+ if (distance_required) {
+ current_distance1 += pos0.distance_to(pos1);
+ }
+ if (_interpolate_color) {
+ color1 = gradient->get_color(gradient->get_point_count() - 1);
+ }
+ if (retrieve_curve) {
+ width_factor = curve->sample_baked(1.f);
+ modified_hw = hw * width_factor;
+ }
- // End cap (box)
- if (end_cap_mode == Line2D::LINE_CAP_BOX) {
- pos_up1 += f0 * hw * width_factor;
- pos_down1 += f0 * hw * width_factor;
+ Vector2 pos_up1 = pos1 + u0 * modified_hw;
+ Vector2 pos_down1 = pos1 - u0 * modified_hw;
- current_distance1 += hw * width_factor;
- }
+ // Add extra distance for a box end cap.
+ if (end_cap_mode == Line2D::LINE_CAP_BOX) {
+ pos_up1 += f0 * modified_hw;
+ pos_down1 += f0 * modified_hw;
- if (texture_mode == Line2D::LINE_TEXTURE_TILE) {
- uvx1 = current_distance1 / (width * tile_aspect);
- } else if (texture_mode == Line2D::LINE_TEXTURE_STRETCH) {
- uvx1 = current_distance1 / total_distance;
- }
-
- strip_add_quad(pos_up1, pos_down1, color1, uvx1);
+ current_distance1 += modified_hw;
+ }
- // End cap (round)
- if (end_cap_mode == Line2D::LINE_CAP_ROUND) {
- // Note: color is not used in case we don't interpolate...
- Color color = _interpolate_color ? gradient->get_color(gradient->get_point_count() - 1) : Color(0, 0, 0);
- float dist = 0;
if (texture_mode == Line2D::LINE_TEXTURE_TILE) {
- dist = width_factor / tile_aspect;
+ uvx1 = current_distance1 / (width * tile_aspect);
} else if (texture_mode == Line2D::LINE_TEXTURE_STRETCH) {
- dist = width * width_factor / total_distance;
+ uvx1 = current_distance1 / total_distance;
+ }
+
+ strip_add_quad(pos_up1, pos_down1, color1, uvx1);
+
+ // Custom drawing for a round end cap.
+ if (end_cap_mode == Line2D::LINE_CAP_ROUND) {
+ // Note: color is not used in case we don't interpolate.
+ Color color = _interpolate_color ? gradient->get_color(gradient->get_point_count() - 1) : Color(0, 0, 0);
+ float dist = 0;
+ if (texture_mode == Line2D::LINE_TEXTURE_TILE) {
+ dist = width_factor / tile_aspect;
+ } else if (texture_mode == Line2D::LINE_TEXTURE_STRETCH) {
+ dist = width * width_factor / total_distance;
+ }
+ new_arc(pos1, pos_up1 - pos1, Math_PI, color, Rect2(uvx1 - 0.5f * dist, 0.f, dist, 1.f));
}
- new_arc(pos1, pos_up1 - pos1, Math_PI, color, Rect2(uvx1 - 0.5f * dist, 0.f, dist, 1.f));
}
}
diff --git a/scene/2d/line_builder.h b/scene/2d/line_builder.h
index 044ff93b42..3706352d5c 100644
--- a/scene/2d/line_builder.h
+++ b/scene/2d/line_builder.h
@@ -41,6 +41,7 @@ public:
Line2D::LineJointMode joint_mode = Line2D::LINE_JOINT_SHARP;
Line2D::LineCapMode begin_cap_mode = Line2D::LINE_CAP_NONE;
Line2D::LineCapMode end_cap_mode = Line2D::LINE_CAP_NONE;
+ bool closed = false;
float width = 10.0;
Curve *curve = nullptr;
Color default_color = Color(0.4, 0.5, 1);
@@ -61,7 +62,6 @@ public:
LineBuilder();
void build();
- void clear_output();
private:
enum Orientation {
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index 8147521a01..9f99b5ecbd 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -674,10 +674,6 @@ void AudioStreamPlayer3D::_validate_property(PropertyInfo &p_property) const {
}
}
-void AudioStreamPlayer3D::_bus_layout_changed() {
- notify_property_list_changed();
-}
-
void AudioStreamPlayer3D::set_max_distance(float p_metres) {
ERR_FAIL_COND(p_metres < 0.0);
max_distance = p_metres;
@@ -814,6 +810,14 @@ float AudioStreamPlayer3D::get_panning_strength() const {
return panning_strength;
}
+void AudioStreamPlayer3D::_on_bus_layout_changed() {
+ notify_property_list_changed();
+}
+
+void AudioStreamPlayer3D::_on_bus_renamed(int p_bus_index, const StringName &p_old_name, const StringName &p_new_name) {
+ notify_property_list_changed();
+}
+
void AudioStreamPlayer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer3D::set_stream);
ClassDB::bind_method(D_METHOD("get_stream"), &AudioStreamPlayer3D::get_stream);
@@ -923,7 +927,8 @@ void AudioStreamPlayer3D::_bind_methods() {
AudioStreamPlayer3D::AudioStreamPlayer3D() {
velocity_tracker.instantiate();
- AudioServer::get_singleton()->connect("bus_layout_changed", callable_mp(this, &AudioStreamPlayer3D::_bus_layout_changed));
+ AudioServer::get_singleton()->connect("bus_layout_changed", callable_mp(this, &AudioStreamPlayer3D::_on_bus_layout_changed));
+ AudioServer::get_singleton()->connect("bus_renamed", callable_mp(this, &AudioStreamPlayer3D::_on_bus_renamed));
set_disable_scale(true);
cached_global_panning_strength = GLOBAL_GET("audio/general/3d_panning_strength");
}
diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h
index bba8f10761..f20170e63b 100644
--- a/scene/3d/audio_stream_player_3d.h
+++ b/scene/3d/audio_stream_player_3d.h
@@ -97,7 +97,8 @@ private:
Area3D *_get_overriding_area();
Vector<AudioFrame> _update_panning();
- void _bus_layout_changed();
+ void _on_bus_layout_changed();
+ void _on_bus_renamed(int p_bus_index, const StringName &p_old_name, const StringName &p_new_name);
uint32_t area_mask = 1;
diff --git a/scene/3d/joint_3d.cpp b/scene/3d/joint_3d.cpp
index 61f89648ee..f1361ab1bb 100644
--- a/scene/3d/joint_3d.cpp
+++ b/scene/3d/joint_3d.cpp
@@ -221,6 +221,8 @@ void Joint3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_exclude_nodes_from_collision", "enable"), &Joint3D::set_exclude_nodes_from_collision);
ClassDB::bind_method(D_METHOD("get_exclude_nodes_from_collision"), &Joint3D::get_exclude_nodes_from_collision);
+ ClassDB::bind_method(D_METHOD("get_rid"), &Joint3D::get_rid);
+
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_a", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "PhysicsBody3D"), "set_node_a", "get_node_a");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_b", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "PhysicsBody3D"), "set_node_b", "get_node_b");
ADD_PROPERTY(PropertyInfo(Variant::INT, "solver_priority", PROPERTY_HINT_RANGE, "1,8,1"), "set_solver_priority", "get_solver_priority");
@@ -257,7 +259,7 @@ void PinJoint3D::set_param(Param p_param, real_t p_value) {
ERR_FAIL_INDEX(p_param, 3);
params[p_param] = p_value;
if (is_configured()) {
- PhysicsServer3D::get_singleton()->pin_joint_set_param(get_joint(), PhysicsServer3D::PinJointParam(p_param), p_value);
+ PhysicsServer3D::get_singleton()->pin_joint_set_param(get_rid(), PhysicsServer3D::PinJointParam(p_param), p_value);
}
}
@@ -332,7 +334,7 @@ void HingeJoint3D::set_param(Param p_param, real_t p_value) {
ERR_FAIL_INDEX(p_param, PARAM_MAX);
params[p_param] = p_value;
if (is_configured()) {
- PhysicsServer3D::get_singleton()->hinge_joint_set_param(get_joint(), PhysicsServer3D::HingeJointParam(p_param), p_value);
+ PhysicsServer3D::get_singleton()->hinge_joint_set_param(get_rid(), PhysicsServer3D::HingeJointParam(p_param), p_value);
}
update_gizmos();
@@ -347,7 +349,7 @@ void HingeJoint3D::set_flag(Flag p_flag, bool p_value) {
ERR_FAIL_INDEX(p_flag, FLAG_MAX);
flags[p_flag] = p_value;
if (is_configured()) {
- PhysicsServer3D::get_singleton()->hinge_joint_set_flag(get_joint(), PhysicsServer3D::HingeJointFlag(p_flag), p_value);
+ PhysicsServer3D::get_singleton()->hinge_joint_set_flag(get_rid(), PhysicsServer3D::HingeJointFlag(p_flag), p_value);
}
update_gizmos();
@@ -458,7 +460,7 @@ void SliderJoint3D::set_param(Param p_param, real_t p_value) {
ERR_FAIL_INDEX(p_param, PARAM_MAX);
params[p_param] = p_value;
if (is_configured()) {
- PhysicsServer3D::get_singleton()->slider_joint_set_param(get_joint(), PhysicsServer3D::SliderJointParam(p_param), p_value);
+ PhysicsServer3D::get_singleton()->slider_joint_set_param(get_rid(), PhysicsServer3D::SliderJointParam(p_param), p_value);
}
update_gizmos();
}
@@ -540,7 +542,7 @@ void ConeTwistJoint3D::set_param(Param p_param, real_t p_value) {
ERR_FAIL_INDEX(p_param, PARAM_MAX);
params[p_param] = p_value;
if (is_configured()) {
- PhysicsServer3D::get_singleton()->cone_twist_joint_set_param(get_joint(), PhysicsServer3D::ConeTwistJointParam(p_param), p_value);
+ PhysicsServer3D::get_singleton()->cone_twist_joint_set_param(get_rid(), PhysicsServer3D::ConeTwistJointParam(p_param), p_value);
}
update_gizmos();
@@ -753,7 +755,7 @@ void Generic6DOFJoint3D::set_param_x(Param p_param, real_t p_value) {
ERR_FAIL_INDEX(p_param, PARAM_MAX);
params_x[p_param] = p_value;
if (is_configured()) {
- PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(get_joint(), Vector3::AXIS_X, PhysicsServer3D::G6DOFJointAxisParam(p_param), p_value);
+ PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(get_rid(), Vector3::AXIS_X, PhysicsServer3D::G6DOFJointAxisParam(p_param), p_value);
}
update_gizmos();
@@ -768,7 +770,7 @@ void Generic6DOFJoint3D::set_param_y(Param p_param, real_t p_value) {
ERR_FAIL_INDEX(p_param, PARAM_MAX);
params_y[p_param] = p_value;
if (is_configured()) {
- PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(get_joint(), Vector3::AXIS_Y, PhysicsServer3D::G6DOFJointAxisParam(p_param), p_value);
+ PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(get_rid(), Vector3::AXIS_Y, PhysicsServer3D::G6DOFJointAxisParam(p_param), p_value);
}
update_gizmos();
}
@@ -782,7 +784,7 @@ void Generic6DOFJoint3D::set_param_z(Param p_param, real_t p_value) {
ERR_FAIL_INDEX(p_param, PARAM_MAX);
params_z[p_param] = p_value;
if (is_configured()) {
- PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(get_joint(), Vector3::AXIS_Z, PhysicsServer3D::G6DOFJointAxisParam(p_param), p_value);
+ PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(get_rid(), Vector3::AXIS_Z, PhysicsServer3D::G6DOFJointAxisParam(p_param), p_value);
}
update_gizmos();
}
@@ -796,7 +798,7 @@ void Generic6DOFJoint3D::set_flag_x(Flag p_flag, bool p_enabled) {
ERR_FAIL_INDEX(p_flag, FLAG_MAX);
flags_x[p_flag] = p_enabled;
if (is_configured()) {
- PhysicsServer3D::get_singleton()->generic_6dof_joint_set_flag(get_joint(), Vector3::AXIS_X, PhysicsServer3D::G6DOFJointAxisFlag(p_flag), p_enabled);
+ PhysicsServer3D::get_singleton()->generic_6dof_joint_set_flag(get_rid(), Vector3::AXIS_X, PhysicsServer3D::G6DOFJointAxisFlag(p_flag), p_enabled);
}
update_gizmos();
}
@@ -810,7 +812,7 @@ void Generic6DOFJoint3D::set_flag_y(Flag p_flag, bool p_enabled) {
ERR_FAIL_INDEX(p_flag, FLAG_MAX);
flags_y[p_flag] = p_enabled;
if (is_configured()) {
- PhysicsServer3D::get_singleton()->generic_6dof_joint_set_flag(get_joint(), Vector3::AXIS_Y, PhysicsServer3D::G6DOFJointAxisFlag(p_flag), p_enabled);
+ PhysicsServer3D::get_singleton()->generic_6dof_joint_set_flag(get_rid(), Vector3::AXIS_Y, PhysicsServer3D::G6DOFJointAxisFlag(p_flag), p_enabled);
}
update_gizmos();
}
@@ -824,7 +826,7 @@ void Generic6DOFJoint3D::set_flag_z(Flag p_flag, bool p_enabled) {
ERR_FAIL_INDEX(p_flag, FLAG_MAX);
flags_z[p_flag] = p_enabled;
if (is_configured()) {
- PhysicsServer3D::get_singleton()->generic_6dof_joint_set_flag(get_joint(), Vector3::AXIS_Z, PhysicsServer3D::G6DOFJointAxisFlag(p_flag), p_enabled);
+ PhysicsServer3D::get_singleton()->generic_6dof_joint_set_flag(get_rid(), Vector3::AXIS_Z, PhysicsServer3D::G6DOFJointAxisFlag(p_flag), p_enabled);
}
update_gizmos();
}
diff --git a/scene/3d/joint_3d.h b/scene/3d/joint_3d.h
index cadd617153..527aed4079 100644
--- a/scene/3d/joint_3d.h
+++ b/scene/3d/joint_3d.h
@@ -77,7 +77,7 @@ public:
void set_exclude_nodes_from_collision(bool p_enable);
bool get_exclude_nodes_from_collision() const;
- RID get_joint() const { return joint; }
+ RID get_rid() const { return joint; }
Joint3D();
~Joint3D();
};
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
index 8da1ef8e1d..b9cbd3cf94 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -149,7 +149,7 @@ double AnimationNodeAnimation::_process(double p_time, bool p_seek, bool p_is_ex
}
// Emit start & finish signal. Internally, the detections are the same for backward.
- // We should use call_deferred since the track keys are still being prosessed.
+ // We should use call_deferred since the track keys are still being processed.
if (state->tree && !p_test_only) {
// AnimationTree uses seek to 0 "internally" to process the first key of the animation, which is used as the start detection.
if (p_seek && !p_is_external_seeking && cur_time == 0) {
diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp
index ae68bd719b..7f6dfd0ab8 100644
--- a/scene/audio/audio_stream_player.cpp
+++ b/scene/audio/audio_stream_player.cpp
@@ -236,6 +236,14 @@ bool AudioStreamPlayer::_is_active() const {
return false;
}
+void AudioStreamPlayer::_on_bus_layout_changed() {
+ notify_property_list_changed();
+}
+
+void AudioStreamPlayer::_on_bus_renamed(int p_bus_index, const StringName &p_old_name, const StringName &p_new_name) {
+ notify_property_list_changed();
+}
+
void AudioStreamPlayer::set_stream_paused(bool p_pause) {
// TODO this does not have perfect recall, fix that maybe? If there are zero playbacks registered with the AudioServer, this bool isn't persisted.
for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
@@ -303,10 +311,6 @@ void AudioStreamPlayer::_validate_property(PropertyInfo &p_property) const {
}
}
-void AudioStreamPlayer::_bus_layout_changed() {
- notify_property_list_changed();
-}
-
bool AudioStreamPlayer::has_stream_playback() {
return !stream_playbacks.is_empty();
}
@@ -372,7 +376,8 @@ void AudioStreamPlayer::_bind_methods() {
}
AudioStreamPlayer::AudioStreamPlayer() {
- AudioServer::get_singleton()->connect("bus_layout_changed", callable_mp(this, &AudioStreamPlayer::_bus_layout_changed));
+ AudioServer::get_singleton()->connect("bus_layout_changed", callable_mp(this, &AudioStreamPlayer::_on_bus_layout_changed));
+ AudioServer::get_singleton()->connect("bus_renamed", callable_mp(this, &AudioStreamPlayer::_on_bus_renamed));
}
AudioStreamPlayer::~AudioStreamPlayer() {
diff --git a/scene/audio/audio_stream_player.h b/scene/audio/audio_stream_player.h
index b97387ebb1..9dbdccdc69 100644
--- a/scene/audio/audio_stream_player.h
+++ b/scene/audio/audio_stream_player.h
@@ -63,7 +63,8 @@ private:
void _set_playing(bool p_enable);
bool _is_active() const;
- void _bus_layout_changed();
+ void _on_bus_layout_changed();
+ void _on_bus_renamed(int p_bus_index, const StringName &p_old_name, const StringName &p_new_name);
Vector<AudioFrame> _get_volume_vector();
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index 6b1b172ec3..e1fc7d7cd4 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -1011,7 +1011,7 @@ void ItemList::_notification(int p_what) {
} break;
case NOTIFICATION_DRAW: {
- _check_shape_changed();
+ force_update_list_size();
int scroll_bar_minwidth = scroll_bar->get_minimum_size().x;
scroll_bar->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -scroll_bar_minwidth);
@@ -1314,7 +1314,7 @@ void ItemList::_notification(int p_what) {
}
}
-void ItemList::_check_shape_changed() {
+void ItemList::force_update_list_size() {
if (!shape_changed) {
return;
}
@@ -1855,6 +1855,8 @@ void ItemList::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "overrun_behavior"), &ItemList::set_text_overrun_behavior);
ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &ItemList::get_text_overrun_behavior);
+ ClassDB::bind_method(D_METHOD("force_update_list_size"), &ItemList::force_update_list_size);
+
ADD_PROPERTY(PropertyInfo(Variant::INT, "select_mode", PROPERTY_HINT_ENUM, "Single,Multi"), "set_select_mode", "get_select_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_reselect"), "set_allow_reselect", "get_allow_reselect");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_rmb_select"), "set_allow_rmb_select", "get_allow_rmb_select");
diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h
index e52d57bb62..796e0eb687 100644
--- a/scene/gui/item_list.h
+++ b/scene/gui/item_list.h
@@ -148,7 +148,6 @@ private:
} theme_cache;
void _scroll_changed(double);
- void _check_shape_changed();
void _shape_text(int p_idx);
void _mouse_exited();
@@ -281,6 +280,8 @@ public:
void set_autoscroll_to_bottom(const bool p_enable);
+ void force_update_list_size();
+
VScrollBar *get_v_scroll_bar() { return scroll_bar; }
ItemList();
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index f28f20b584..98bedd4493 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -1298,7 +1298,8 @@ void TextEdit::_notification(int p_what) {
if (had_glyphs_drawn) {
if (first_visible_char > glyphs[j].start) {
first_visible_char = glyphs[j].start;
- } else if (last_visible_char < glyphs[j].end) {
+ }
+ if (last_visible_char < glyphs[j].end) {
last_visible_char = glyphs[j].end;
}
}
@@ -4328,6 +4329,11 @@ Rect2i TextEdit::get_rect_at_line_column(int p_line, int p_column) const {
ERR_FAIL_COND_V(p_column < 0, Rect2i(-1, -1, 0, 0));
ERR_FAIL_COND_V(p_column > text[p_line].length(), Rect2i(-1, -1, 0, 0));
+ if (text.size() == 1 && text[0].length() == 0) {
+ // The TextEdit is empty.
+ return Rect2i();
+ }
+
if (line_drawing_cache.size() == 0 || !line_drawing_cache.has(p_line)) {
// Line is not in the cache, which means it's outside of the viewing area.
return Rect2i(-1, -1, 0, 0);
diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp
index 6a03a67fcd..ce7f6ee6f0 100644
--- a/servers/audio_server.cpp
+++ b/servers/audio_server.cpp
@@ -857,14 +857,16 @@ int AudioServer::get_bus_count() const {
void AudioServer::set_bus_name(int p_bus, const String &p_name) {
ERR_FAIL_INDEX(p_bus, buses.size());
if (p_bus == 0 && p_name != "Master") {
- return; //bus 0 is always master
+ return; // Bus 0 is always "Master".
}
MARK_EDITED
lock();
- if (buses[p_bus]->name == p_name) {
+ StringName old_name = buses[p_bus]->name;
+
+ if (old_name == p_name) {
unlock();
return;
}
@@ -888,12 +890,12 @@ void AudioServer::set_bus_name(int p_bus, const String &p_name) {
attempts++;
attempt = p_name + " " + itos(attempts);
}
- bus_map.erase(buses[p_bus]->name);
+ bus_map.erase(old_name);
buses[p_bus]->name = attempt;
bus_map[attempt] = buses[p_bus];
unlock();
- emit_signal(SNAME("bus_layout_changed"));
+ emit_signal(SNAME("bus_renamed"), p_bus, old_name, attempt);
}
String AudioServer::get_bus_name(int p_bus) const {
@@ -1751,6 +1753,7 @@ void AudioServer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "playback_speed_scale"), "set_playback_speed_scale", "get_playback_speed_scale");
ADD_SIGNAL(MethodInfo("bus_layout_changed"));
+ ADD_SIGNAL(MethodInfo("bus_renamed", PropertyInfo(Variant::INT, "bus_index"), PropertyInfo(Variant::STRING_NAME, "old_name"), PropertyInfo(Variant::STRING_NAME, "new_name")));
BIND_ENUM_CONSTANT(SPEAKER_MODE_STEREO);
BIND_ENUM_CONSTANT(SPEAKER_SURROUND_31);
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
index b1ad7e16ed..4fede0defc 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
@@ -2480,6 +2480,7 @@ RendererCanvasRenderRD::RendererCanvasRenderRD() {
actions.render_mode_defines["skip_vertex_transform"] = "#define SKIP_TRANSFORM_USED\n";
actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n";
actions.render_mode_defines["light_only"] = "#define MODE_LIGHT_ONLY\n";
+ actions.render_mode_defines["world_vertex_coords"] = "#define USE_WORLD_VERTEX_COORDS\n";
actions.custom_samplers["TEXTURE"] = "texture_sampler";
actions.custom_samplers["NORMAL_TEXTURE"] = "texture_sampler";
diff --git a/servers/rendering/renderer_rd/shaders/canvas.glsl b/servers/rendering/renderer_rd/shaders/canvas.glsl
index 31c5aadc88..7a13ac7207 100644
--- a/servers/rendering/renderer_rd/shaders/canvas.glsl
+++ b/servers/rendering/renderer_rd/shaders/canvas.glsl
@@ -180,6 +180,10 @@ void main() {
#ifdef USE_POINT_SIZE
float point_size = 1.0;
#endif
+
+#ifdef USE_WORLD_VERTEX_COORDS
+ vertex = (model_matrix * vec4(vertex, 0.0, 1.0)).xy;
+#endif
{
#CODE : VERTEX
}
@@ -188,7 +192,7 @@ void main() {
pixel_size_interp = abs(draw_data.dst_rect.zw) * vertex_base;
#endif
-#if !defined(SKIP_TRANSFORM_USED)
+#if !defined(SKIP_TRANSFORM_USED) && !defined(USE_WORLD_VERTEX_COORDS)
vertex = (model_matrix * vec4(vertex, 0.0, 1.0)).xy;
#endif
diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp
index 728089f516..38574b0597 100644
--- a/servers/rendering/shader_types.cpp
+++ b/servers/rendering/shader_types.cpp
@@ -321,6 +321,7 @@ ShaderTypes::ShaderTypes() {
shader_modes[RS::SHADER_CANVAS_ITEM].modes.push_back({ PNAME("blend"), "mix", "add", "sub", "mul", "premul_alpha", "disabled" });
shader_modes[RS::SHADER_CANVAS_ITEM].modes.push_back({ PNAME("unshaded") });
shader_modes[RS::SHADER_CANVAS_ITEM].modes.push_back({ PNAME("light_only") });
+ shader_modes[RS::SHADER_CANVAS_ITEM].modes.push_back({ PNAME("world_vertex_coords") });
}
/************ PARTICLES **************************/
diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h
index 79766cd919..7e9b472af1 100644
--- a/tests/scene/test_text_edit.h
+++ b/tests/scene/test_text_edit.h
@@ -3241,6 +3241,15 @@ TEST_CASE("[SceneTree][TextEdit] mouse") {
SceneTree::get_singleton()->get_root()->add_child(text_edit);
text_edit->set_size(Size2(800, 200));
+
+ CHECK(text_edit->get_rect_at_line_column(0, 0).get_position() == Point2i(0, 0));
+
+ text_edit->set_line(0, "A");
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_rect_at_line_column(0, 1).get_position().x > 0);
+
+ text_edit->clear(); // Necessary, otherwise the following test cases fail.
+
text_edit->set_line(0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vasius mattis leo, sed porta ex lacinia bibendum. Nunc bibendum pellentesque.");
MessageQueue::get_singleton()->flush();