diff options
46 files changed, 337 insertions, 89 deletions
diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp index 942ec95678..0cbcf58882 100644 --- a/core/extension/gdextension.cpp +++ b/core/extension/gdextension.cpp @@ -314,6 +314,7 @@ void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library parent_extension->gdextension.children.push_back(&extension->gdextension); } + extension->gdextension.library = self; extension->gdextension.parent_class_name = parent_class_name; extension->gdextension.class_name = class_name; extension->gdextension.editor_class = self->level_initialized == INITIALIZATION_LEVEL_EDITOR; @@ -524,7 +525,7 @@ void GDExtension::deinitialize_library(InitializationLevel p_level) { } void GDExtension::_bind_methods() { - ClassDB::bind_method(D_METHOD("open_library", "path", "entry_symbol", "use_legacy_interface"), &GDExtension::open_library); + ClassDB::bind_method(D_METHOD("open_library", "path", "entry_symbol", "use_legacy_interface"), &GDExtension::open_library, DEFVAL(false)); ClassDB::bind_compatibility_method(D_METHOD("open_library", "path", "entry_symbol"), &GDExtension::open_library_compat_76406); ClassDB::bind_method(D_METHOD("close_library"), &GDExtension::close_library); ClassDB::bind_method(D_METHOD("is_library_open"), &GDExtension::is_library_open); diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp index c661bfa8c7..12ef1772e3 100644 --- a/core/extension/gdextension_interface.cpp +++ b/core/extension/gdextension_interface.cpp @@ -992,6 +992,19 @@ static GDExtensionObjectPtr gdextension_object_get_instance_from_id(GDObjectInst return (GDExtensionObjectPtr)ObjectDB::get_instance(ObjectID(p_instance_id)); } +static GDExtensionBool gdextension_object_get_class_name(GDExtensionConstObjectPtr p_object, GDExtensionClassLibraryPtr p_library, GDExtensionUninitializedStringNamePtr r_class_name) { + if (!p_object) { + return false; + } + const Object *o = (const Object *)p_object; + + memnew_placement(r_class_name, StringName); + StringName *class_name = reinterpret_cast<StringName *>(r_class_name); + *class_name = o->get_class_name_for_extension((GDExtension *)p_library); + + return true; +} + static GDExtensionObjectPtr gdextension_object_cast_to(GDExtensionConstObjectPtr p_object, void *p_class_tag) { if (!p_object) { return nullptr; @@ -1176,6 +1189,7 @@ void gdextension_setup_interface() { REGISTER_INTERFACE_FUNC(object_get_instance_binding); REGISTER_INTERFACE_FUNC(object_set_instance_binding); REGISTER_INTERFACE_FUNC(object_set_instance); + REGISTER_INTERFACE_FUNC(object_get_class_name); REGISTER_INTERFACE_FUNC(object_cast_to); REGISTER_INTERFACE_FUNC(object_get_instance_from_id); REGISTER_INTERFACE_FUNC(object_get_instance_id); diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h index 4e4f300f5d..a5ea3918df 100644 --- a/core/extension/gdextension_interface.h +++ b/core/extension/gdextension_interface.h @@ -1902,6 +1902,19 @@ typedef void (*GDExtensionInterfaceObjectSetInstanceBinding)(GDExtensionObjectPt typedef void (*GDExtensionInterfaceObjectSetInstance)(GDExtensionObjectPtr p_o, GDExtensionConstStringNamePtr p_classname, GDExtensionClassInstancePtr p_instance); /* p_classname should be a registered extension class and should extend the p_o object's class. */ /** + * @name object_get_class_name + * + * Gets the class name of an Object. + * + * @param p_object A pointer to the Object. + * @param p_library A pointer the library received by the GDExtension's entry point function. + * @param r_class_name A pointer to a String to receive the class name. + * + * @return true if successful in getting the class name; otherwise false. + */ +typedef GDExtensionBool (*GDExtensionInterfaceObjectGetClassName)(GDExtensionConstObjectPtr p_object, GDExtensionClassLibraryPtr p_library, GDExtensionUninitializedStringNamePtr r_class_name); + +/** * @name object_cast_to * * Casts an Object to a different type. diff --git a/core/object/object.cpp b/core/object/object.cpp index f42d604832..f276547e7b 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -1739,7 +1739,7 @@ void *Object::get_instance_binding(void *p_token, const GDExtensionInstanceBindi break; } } - if (unlikely(!binding)) { + if (unlikely(!binding && p_callbacks)) { uint32_t current_size = next_power_of_2(_instance_binding_count); uint32_t new_size = next_power_of_2(_instance_binding_count + 1); diff --git a/core/object/object.h b/core/object/object.h index c4d287a71c..6f626b0ed0 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -304,8 +304,10 @@ struct MethodInfo { // API used to extend in GDExtension and other C compatible compiled languages. class MethodBind; +class GDExtension; struct ObjectGDExtension { + GDExtension *library = nullptr; ObjectGDExtension *parent = nullptr; List<ObjectGDExtension *> children; StringName parent_class_name; @@ -798,6 +800,18 @@ public: return *_class_name_ptr; } + _FORCE_INLINE_ const StringName &get_class_name_for_extension(const GDExtension *p_library) const { + // Only return the class name per the extension if it matches the given p_library. + if (_extension && _extension->library == p_library) { + return _extension->class_name; + } + // Otherwise, return the name of the built-in class. + if (unlikely(!_class_name_ptr)) { + return *_get_class_namev(); + } + return *_class_name_ptr; + } + /* IAPI */ void set(const StringName &p_name, const Variant &p_value, bool *r_valid = nullptr); diff --git a/doc/classes/GDExtension.xml b/doc/classes/GDExtension.xml index 4c2bcde608..762de88be6 100644 --- a/doc/classes/GDExtension.xml +++ b/doc/classes/GDExtension.xml @@ -32,7 +32,7 @@ <return type="int" enum="Error" /> <param index="0" name="path" type="String" /> <param index="1" name="entry_symbol" type="String" /> - <param index="2" name="use_legacy_interface" type="bool" /> + <param index="2" name="use_legacy_interface" type="bool" default="false" /> <description> </description> </method> diff --git a/doc/classes/OptionButton.xml b/doc/classes/OptionButton.xml index 787e20366e..17a164953d 100644 --- a/doc/classes/OptionButton.xml +++ b/doc/classes/OptionButton.xml @@ -7,6 +7,7 @@ OptionButton is a type button that provides a selectable list of items when pressed. The item selected becomes the "current" item and is displayed as the button text. See also [BaseButton] which contains common properties and methods associated with this node. [b]Note:[/b] Properties [member Button.text] and [member Button.icon] are automatically set based on the selected item. They shouldn't be changed manually. + [b]Note:[/b] The ID values used for items are limited to 32 bits, not full 64 bits of [int]. This has a range of [code]-2^32[/code] to [code]2^32 - 1[/code], i.e. [code]-2147483648[/code] to [code]2147483647[/code]. </description> <tutorials> </tutorials> diff --git a/doc/classes/Polygon2D.xml b/doc/classes/Polygon2D.xml index b657e49799..e40abd3312 100644 --- a/doc/classes/Polygon2D.xml +++ b/doc/classes/Polygon2D.xml @@ -47,7 +47,7 @@ <return type="PackedFloat32Array" /> <param index="0" name="index" type="int" /> <description> - Returns the height values of the specified bone. + Returns the weight values of the specified bone. </description> </method> <method name="set_bone_path"> diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml index c278f53a38..2f1d4a528a 100644 --- a/doc/classes/PopupMenu.xml +++ b/doc/classes/PopupMenu.xml @@ -9,6 +9,7 @@ If no maximum size is set, or if it is set to 0, the [PopupMenu] height will be limited by its parent rect. All [code]set_*[/code] methods allow negative item index, which makes the item accessed from the last one. [b]Incremental search:[/b] Like [ItemList] and [Tree], [PopupMenu] supports searching within the list while the control is focused. Press a key that matches the first letter of an item's name to select the first item starting with the given letter. After that point, there are two ways to perform incremental search: 1) Press the same key again before the timeout duration to select the next item starting with the same letter. 2) Press letter keys that match the rest of the word before the timeout duration to match to select the item in question directly. Both of these actions will be reset to the beginning of the list if the timeout duration has passed since the last keystroke was registered. You can adjust the timeout duration by changing [member ProjectSettings.gui/timers/incremental_search_max_interval_msec]. + [b]Note:[/b] The ID values used for items are limited to 32 bits, not full 64 bits of [int]. This has a range of [code]-2^32[/code] to [code]2^32 - 1[/code], i.e. [code]-2147483648[/code] to [code]2147483647[/code]. </description> <tutorials> </tutorials> diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml index c44f54749b..4a243a70ce 100644 --- a/doc/classes/RichTextLabel.xml +++ b/doc/classes/RichTextLabel.xml @@ -410,6 +410,7 @@ <param index="2" name="language" type="String" default="""" /> <param index="3" name="st_parser" type="int" enum="TextServer.StructuredTextParser" default="0" /> <param index="4" name="justification_flags" type="int" enum="TextServer.JustificationFlag" default="163" /> + <param index="5" name="tab_stops" type="PackedFloat32Array" default="PackedFloat32Array()" /> <description> Adds a [code][p][/code] tag to the tag stack. </description> diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml index 6b6851daf1..ffa79ed085 100644 --- a/doc/classes/TreeItem.xml +++ b/doc/classes/TreeItem.xml @@ -6,6 +6,7 @@ <description> Control for a single item inside a [Tree]. May have child [TreeItem]s and be styled as well as contain buttons. You can remove a [TreeItem] by using [method Object.free]. + [b]Note:[/b] The ID values used for buttons are limited to 32 bits, not full 64 bits of [int]. This has a range of [code]-2^32[/code] to [code]2^32 - 1[/code], i.e. [code]-2147483648[/code] to [code]2147483647[/code]. </description> <tutorials> </tutorials> diff --git a/doc/classes/Tween.xml b/doc/classes/Tween.xml index 7e313c3d4c..51efbd31d8 100644 --- a/doc/classes/Tween.xml +++ b/doc/classes/Tween.xml @@ -501,6 +501,9 @@ <constant name="TRANS_BACK" value="10" enum="TransitionType"> The animation is interpolated backing out at ends. </constant> + <constant name="TRANS_SPRING" value="11" enum="TransitionType"> + The animation is interpolated like a spring towards the end. + </constant> <constant name="EASE_IN" value="0" enum="EaseType"> The interpolation starts slowly and speeds up towards the end. </constant> diff --git a/drivers/gles3/storage/light_storage.h b/drivers/gles3/storage/light_storage.h index 8e6480869b..0f8120be15 100644 --- a/drivers/gles3/storage/light_storage.h +++ b/drivers/gles3/storage/light_storage.h @@ -287,6 +287,13 @@ public: return light->param[RS::LIGHT_PARAM_TRANSMITTANCE_BIAS]; } + virtual bool light_get_reverse_cull_face_mode(RID p_light) const override { + const Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND_V(!light, false); + + return light->reverse_cull; + } + virtual RS::LightBakeMode light_get_bake_mode(RID p_light) override; virtual uint32_t light_get_max_sdfgi_cascade(RID p_light) override { return 0; } virtual uint64_t light_get_version(RID p_light) const override; diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp index 44c9dc714c..585ed5ff96 100644 --- a/drivers/gles3/storage/mesh_storage.cpp +++ b/drivers/gles3/storage/mesh_storage.cpp @@ -534,7 +534,7 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) { if (skeleton->use_2d) { for (int j = 0; j < bs; j++) { - if (skbones[0].size == Vector3()) { + if (skbones[j].size == Vector3(-1, -1, -1)) { continue; //bone is unused } @@ -561,7 +561,7 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) { } } else { for (int j = 0; j < bs; j++) { - if (skbones[0].size == Vector3()) { + if (skbones[j].size == Vector3(-1, -1, -1)) { continue; //bone is unused } diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 74eb2777ed..5ed314c986 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -1506,6 +1506,16 @@ void EditorFileSystem::_save_late_updated_files() { } Vector<String> EditorFileSystem::_get_dependencies(const String &p_path) { + // Avoid error spam on first opening of a not yet imported project by treating the following situation + // as a benign one, not letting the file open error happen: the resource is of an importable type but + // it has not been imported yet. + if (ResourceFormatImporter::get_singleton()->recognize_path(p_path)) { + const String &internal_path = ResourceFormatImporter::get_singleton()->get_internal_resource_path(p_path); + if (!internal_path.is_empty() && !FileAccess::exists(internal_path)) { // If path is empty (error), keep the code flow to the error. + return Vector<String>(); + } + } + List<String> deps; ResourceLoader::get_dependencies(p_path, &deps); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 564cdf1192..c9f356b82c 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -5901,6 +5901,10 @@ void EditorNode::_add_dropped_files_recursive(const Vector<String> &p_files, Str } void EditorNode::_file_access_close_error_notify(const String &p_str) { + callable_mp_static(&EditorNode::_file_access_close_error_notify_impl).bind(p_str).call_deferred(); +} + +void EditorNode::_file_access_close_error_notify_impl(const String &p_str) { add_io_error(vformat(TTR("Unable to write to file '%s', file in use, locked or lacking permissions."), p_str)); } @@ -6502,13 +6506,16 @@ static Node *_resource_get_edited_scene() { } void EditorNode::_print_handler(void *p_this, const String &p_string, bool p_error, bool p_rich) { - EditorNode *en = static_cast<EditorNode *>(p_this); + callable_mp_static(&EditorNode::_print_handler_impl).bind(p_string, p_error, p_rich).call_deferred(); +} + +void EditorNode::_print_handler_impl(const String &p_string, bool p_error, bool p_rich) { if (p_error) { - en->log->add_message(p_string, EditorLog::MSG_TYPE_ERROR); + singleton->log->add_message(p_string, EditorLog::MSG_TYPE_ERROR); } else if (p_rich) { - en->log->add_message(p_string, EditorLog::MSG_TYPE_STD_RICH); + singleton->log->add_message(p_string, EditorLog::MSG_TYPE_STD_RICH); } else { - en->log->add_message(p_string, EditorLog::MSG_TYPE_STD); + singleton->log->add_message(p_string, EditorLog::MSG_TYPE_STD); } } diff --git a/editor/editor_node.h b/editor/editor_node.h index b5f84a3204..814899e169 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -518,8 +518,10 @@ private: static void _editor_file_dialog_unregister(EditorFileDialog *p_dialog); static void _file_access_close_error_notify(const String &p_str); + static void _file_access_close_error_notify_impl(const String &p_str); static void _print_handler(void *p_this, const String &p_string, bool p_error, bool p_rich); + static void _print_handler_impl(const String &p_string, bool p_error, bool p_rich); static void _resource_saved(Ref<Resource> p_resource, const String &p_path); static void _resource_loaded(Ref<Resource> p_resource, const String &p_path); diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index 6fa2ec23bb..5acb30b9d6 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -311,7 +311,7 @@ void EditorPropertyArray::update_property() { size_slider = memnew(EditorSpinSlider); size_slider->set_step(1); - size_slider->set_max(1000000); + size_slider->set_max(INT32_MAX); size_slider->set_h_size_flags(SIZE_EXPAND_FILL); size_slider->set_read_only(is_read_only()); size_slider->connect("value_changed", callable_mp(this, &EditorPropertyArray::_length_changed)); diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index 9ff9b521d6..563398e512 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -1866,6 +1866,7 @@ SpriteFramesEditor::SpriteFramesEditor() { frame_list = memnew(ItemList); frame_list->set_v_size_flags(SIZE_EXPAND_FILL); frame_list->set_icon_mode(ItemList::ICON_MODE_TOP); + frame_list->set_texture_filter(TEXTURE_FILTER_NEAREST_WITH_MIPMAPS); frame_list->set_max_columns(0); frame_list->set_icon_mode(ItemList::ICON_MODE_TOP); @@ -1990,6 +1991,7 @@ SpriteFramesEditor::SpriteFramesEditor() { split_sheet_preview = memnew(TextureRect); split_sheet_preview->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE); + split_sheet_preview->set_texture_filter(TEXTURE_FILTER_NEAREST_WITH_MIPMAPS); split_sheet_preview->set_mouse_filter(MOUSE_FILTER_PASS); split_sheet_preview->connect("draw", callable_mp(this, &SpriteFramesEditor::_sheet_preview_draw)); split_sheet_preview->connect("gui_input", callable_mp(this, &SpriteFramesEditor::_sheet_preview_input)); diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 3bce258072..fdbd505975 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -4192,7 +4192,7 @@ String GDScriptParser::DataType::to_string() const { return class_type->fqcn; case SCRIPT: { if (is_meta_type) { - return script_type->get_class_name().operator String(); + return script_type != nullptr ? script_type->get_class_name().operator String() : ""; } String name = script_type != nullptr ? script_type->get_name() : ""; if (!name.is_empty()) { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs index 44ea8fc83d..03744fc4e3 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs @@ -11,6 +11,7 @@ namespace Godot.Bridge public delegate* unmanaged<IntPtr, godot_variant**, int, godot_bool*, void> SignalAwaiter_SignalCallback; public delegate* unmanaged<IntPtr, void*, godot_variant**, int, godot_variant*, void> DelegateUtils_InvokeWithVariantArgs; public delegate* unmanaged<IntPtr, IntPtr, godot_bool> DelegateUtils_DelegateEquals; + public delegate* unmanaged<IntPtr, int> DelegateUtils_DelegateHash; public delegate* unmanaged<IntPtr, godot_array*, godot_bool> DelegateUtils_TrySerializeDelegateWithGCHandle; public delegate* unmanaged<godot_array*, IntPtr*, godot_bool> DelegateUtils_TryDeserializeDelegateWithGCHandle; public delegate* unmanaged<void> ScriptManagerBridge_FrameCallback; @@ -50,6 +51,7 @@ namespace Godot.Bridge SignalAwaiter_SignalCallback = &SignalAwaiter.SignalCallback, DelegateUtils_InvokeWithVariantArgs = &DelegateUtils.InvokeWithVariantArgs, DelegateUtils_DelegateEquals = &DelegateUtils.DelegateEquals, + DelegateUtils_DelegateHash = &DelegateUtils.DelegateHash, DelegateUtils_TrySerializeDelegateWithGCHandle = &DelegateUtils.TrySerializeDelegateWithGCHandle, DelegateUtils_TryDeserializeDelegateWithGCHandle = &DelegateUtils.TryDeserializeDelegateWithGCHandle, ScriptManagerBridge_FrameCallback = &ScriptManagerBridge.FrameCallback, diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs index 4a54f67cc9..279dadf425 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs @@ -30,6 +30,21 @@ namespace Godot } [UnmanagedCallersOnly] + internal static int DelegateHash(IntPtr delegateGCHandle) + { + try + { + var @delegate = (Delegate?)GCHandle.FromIntPtr(delegateGCHandle).Target; + return @delegate?.GetHashCode() ?? 0; + } + catch (Exception e) + { + ExceptionUtils.LogException(e); + return 0; + } + } + + [UnmanagedCallersOnly] internal static unsafe void InvokeWithVariantArgs(IntPtr delegateGCHandle, void* trampoline, godot_variant** args, int argc, godot_variant* outRet) { diff --git a/modules/mono/managed_callable.cpp b/modules/mono/managed_callable.cpp index 3e4eb5a966..faf3ae7b04 100644 --- a/modules/mono/managed_callable.cpp +++ b/modules/mono/managed_callable.cpp @@ -43,10 +43,10 @@ bool ManagedCallable::compare_equal(const CallableCustom *p_a, const CallableCus const ManagedCallable *a = static_cast<const ManagedCallable *>(p_a); const ManagedCallable *b = static_cast<const ManagedCallable *>(p_b); + if (a->delegate_handle.value == b->delegate_handle.value) { + return true; + } if (!a->delegate_handle.value || !b->delegate_handle.value) { - if (!a->delegate_handle.value && !b->delegate_handle.value) { - return true; - } return false; } @@ -63,7 +63,7 @@ bool ManagedCallable::compare_less(const CallableCustom *p_a, const CallableCust } uint32_t ManagedCallable::hash() const { - return hash_murmur3_one_64((uint64_t)delegate_handle.value); + return GDMonoCache::managed_callbacks.DelegateUtils_DelegateHash(delegate_handle); } String ManagedCallable::get_as_text() const { diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp index 3ef7554a3f..e254484df9 100644 --- a/modules/mono/mono_gd/gd_mono_cache.cpp +++ b/modules/mono/mono_gd/gd_mono_cache.cpp @@ -52,6 +52,7 @@ void update_godot_api_cache(const ManagedCallbacks &p_managed_callbacks) { CHECK_CALLBACK_NOT_NULL(SignalAwaiter, SignalCallback); CHECK_CALLBACK_NOT_NULL(DelegateUtils, InvokeWithVariantArgs); CHECK_CALLBACK_NOT_NULL(DelegateUtils, DelegateEquals); + CHECK_CALLBACK_NOT_NULL(DelegateUtils, DelegateHash); CHECK_CALLBACK_NOT_NULL(DelegateUtils, TrySerializeDelegateWithGCHandle); CHECK_CALLBACK_NOT_NULL(DelegateUtils, TryDeserializeDelegateWithGCHandle); CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, FrameCallback); diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h index 2554dfff97..b180d8133a 100644 --- a/modules/mono/mono_gd/gd_mono_cache.h +++ b/modules/mono/mono_gd/gd_mono_cache.h @@ -76,6 +76,7 @@ struct ManagedCallbacks { using FuncSignalAwaiter_SignalCallback = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const Variant **, int32_t, bool *); using FuncDelegateUtils_InvokeWithVariantArgs = void(GD_CLR_STDCALL *)(GCHandleIntPtr, void *, const Variant **, int32_t, const Variant *); using FuncDelegateUtils_DelegateEquals = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, GCHandleIntPtr); + using FuncDelegateUtils_DelegateHash = int32_t(GD_CLR_STDCALL *)(GCHandleIntPtr); using FuncDelegateUtils_TrySerializeDelegateWithGCHandle = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, const Array *); using FuncDelegateUtils_TryDeserializeDelegateWithGCHandle = bool(GD_CLR_STDCALL *)(const Array *, GCHandleIntPtr *); using FuncScriptManagerBridge_FrameCallback = void(GD_CLR_STDCALL *)(); @@ -109,6 +110,7 @@ struct ManagedCallbacks { FuncSignalAwaiter_SignalCallback SignalAwaiter_SignalCallback; FuncDelegateUtils_InvokeWithVariantArgs DelegateUtils_InvokeWithVariantArgs; FuncDelegateUtils_DelegateEquals DelegateUtils_DelegateEquals; + FuncDelegateUtils_DelegateHash DelegateUtils_DelegateHash; FuncDelegateUtils_TrySerializeDelegateWithGCHandle DelegateUtils_TrySerializeDelegateWithGCHandle; FuncDelegateUtils_TryDeserializeDelegateWithGCHandle DelegateUtils_TryDeserializeDelegateWithGCHandle; FuncScriptManagerBridge_FrameCallback ScriptManagerBridge_FrameCallback; diff --git a/platform/android/android_keys_utils.h b/platform/android/android_keys_utils.h index 3a587dd680..bdfaaa3215 100644 --- a/platform/android/android_keys_utils.h +++ b/platform/android/android_keys_utils.h @@ -60,6 +60,7 @@ static AndroidGodotCodePair android_godot_code_pairs[] = { { AKEYCODE_DPAD_DOWN, Key::DOWN }, // (20) Directional Pad Down key. { AKEYCODE_DPAD_LEFT, Key::LEFT }, // (21) Directional Pad Left key. { AKEYCODE_DPAD_RIGHT, Key::RIGHT }, // (22) Directional Pad Right key. + { AKEYCODE_DPAD_CENTER, Key::ENTER }, // (23) Directional Pad Center key. { AKEYCODE_VOLUME_UP, Key::VOLUMEUP }, // (24) Volume Up key. { AKEYCODE_VOLUME_DOWN, Key::VOLUMEDOWN }, // (25) Volume Down key. { AKEYCODE_POWER, Key::STANDBY }, // (26) Power key. diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 963f533d67..3a6ca66449 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -470,6 +470,7 @@ def configure_msvc(env, vcvars_msvc_config): env["BUILDERS"]["ProgramOriginal"] = env["BUILDERS"]["Program"] env["BUILDERS"]["Program"] = methods.precious_program + env.Append(LINKFLAGS=["/NATVIS:platform\windows\godot.natvis"]) env.AppendUnique(LINKFLAGS=["/STACK:" + str(STACK_SIZE)]) diff --git a/scene/animation/easing_equations.h b/scene/animation/easing_equations.h index 1b9c378b4f..9150191744 100644 --- a/scene/animation/easing_equations.h +++ b/scene/animation/easing_equations.h @@ -413,4 +413,33 @@ static real_t out_in(real_t t, real_t b, real_t c, real_t d) { } }; // namespace back +namespace spring { +static real_t out(real_t t, real_t b, real_t c, real_t d) { + t /= d; + real_t s = 1.0 - t; + t = (sin(t * Math_PI * (0.2 + 2.5 * t * t * t)) * pow(s, 2.2) + t) * (1.0 + (1.2 * s)); + return c * t + b; +} + +static real_t in(real_t t, real_t b, real_t c, real_t d) { + return c - out(d - t, 0, c, d) + b; +} + +static real_t in_out(real_t t, real_t b, real_t c, real_t d) { + if (t < d / 2) { + return in(t * 2, b, c / 2, d); + } + real_t h = c / 2; + return out(t * 2 - d, b + h, h, d); +} + +static real_t out_in(real_t t, real_t b, real_t c, real_t d) { + if (t < d / 2) { + return out(t * 2, b, c / 2, d); + } + real_t h = c / 2; + return in(t * 2 - d, b + h, h, d); +} +}; // namespace spring + #endif // EASING_EQUATIONS_H diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index 8266cfbd43..96e5da5a40 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -50,6 +50,7 @@ Tween::interpolater Tween::interpolaters[Tween::TRANS_MAX][Tween::EASE_MAX] = { { &circ::in, &circ::out, &circ::in_out, &circ::out_in }, { &bounce::in, &bounce::out, &bounce::in_out, &bounce::out_in }, { &back::in, &back::out, &back::in_out, &back::out_in }, + { &spring::in, &spring::out, &spring::in_out, &spring::out_in }, }; void Tweener::set_tween(const Ref<Tween> &p_tween) { @@ -483,6 +484,7 @@ void Tween::_bind_methods() { BIND_ENUM_CONSTANT(TRANS_CIRC); BIND_ENUM_CONSTANT(TRANS_BOUNCE); BIND_ENUM_CONSTANT(TRANS_BACK); + BIND_ENUM_CONSTANT(TRANS_SPRING); BIND_ENUM_CONSTANT(EASE_IN); BIND_ENUM_CONSTANT(EASE_OUT); diff --git a/scene/animation/tween.h b/scene/animation/tween.h index 67823da040..10c7a272ef 100644 --- a/scene/animation/tween.h +++ b/scene/animation/tween.h @@ -87,6 +87,7 @@ public: TRANS_CIRC, TRANS_BOUNCE, TRANS_BACK, + TRANS_SPRING, TRANS_MAX }; diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 5e861ba45d..2a03786471 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -109,6 +109,13 @@ void ColorPicker::_notification(int p_what) { picker_window->hide(); } } break; + + case NOTIFICATION_INTERNAL_PROCESS: { + if (!is_picking_color) { + return; + } + set_pick_color(DisplayServer::get_singleton()->screen_get_pixel(DisplayServer::get_singleton()->mouse_get_position())); + } } } @@ -608,13 +615,14 @@ void ColorPicker::_update_presets() { #ifdef TOOLS_ENABLED if (editor_settings) { - // Only load preset buttons when the only child is the add-preset button. - if (preset_container->get_child_count() == 1) { - for (int i = 0; i < preset_cache.size(); i++) { - _add_preset_button(preset_size, preset_cache[i]); - } - _notification(NOTIFICATION_VISIBILITY_CHANGED); + // Rebuild swatch color buttons, keeping the add-preset button in the first position. + for (int i = 1; i < preset_container->get_child_count(); i++) { + preset_container->get_child(i)->queue_free(); } + for (int i = 0; i < preset_cache.size(); i++) { + _add_preset_button(preset_size, preset_cache[i]); + } + _notification(NOTIFICATION_VISIBILITY_CHANGED); } #endif } @@ -1421,30 +1429,6 @@ void ColorPicker::_recent_preset_pressed(const bool p_pressed, ColorPresetButton emit_signal(SNAME("color_changed"), p_preset->get_preset_color()); } -void ColorPicker::_picker_texture_input(const Ref<InputEvent> &p_event) { - if (!is_inside_tree()) { - return; - } - - Ref<InputEventMouseButton> bev = p_event; - if (bev.is_valid() && bev->get_button_index() == MouseButton::LEFT && !bev->is_pressed()) { - set_pick_color(picker_color); - emit_signal(SNAME("color_changed"), color); - picker_window->hide(); - } - - Ref<InputEventMouseMotion> mev = p_event; - if (mev.is_valid()) { - Ref<Image> img = picker_texture_rect->get_texture()->get_image(); - if (img.is_valid() && !img->is_empty()) { - Vector2 ofs = mev->get_position(); - picker_color = img->get_pixel(ofs.x, ofs.y); - picker_preview_style_box->set_bg_color(picker_color); - picker_preview_label->set_self_modulate(picker_color.get_luminance() < 0.5 ? Color(1.0f, 1.0f, 1.0f) : Color(0.0f, 0.0f, 0.0f)); - } - } -} - void ColorPicker::_text_changed(const String &) { text_changed = true; } @@ -1455,6 +1439,34 @@ void ColorPicker::_add_preset_pressed() { } void ColorPicker::_pick_button_pressed() { + is_picking_color = true; + set_process_internal(true); + + if (!picker_window) { + picker_window = memnew(Popup); + picker_window->set_size(Vector2i(1, 1)); + picker_window->connect("visibility_changed", callable_mp(this, &ColorPicker::_pick_finished)); + add_child(picker_window); + } + picker_window->popup(); +} + +void ColorPicker::_pick_finished() { + if (picker_window->is_visible()) { + return; + } + + if (Input::get_singleton()->is_key_pressed(Key::ESCAPE)) { + set_pick_color(old_color); + } else { + emit_signal(SNAME("color_changed"), color); + } + is_picking_color = false; + set_process_internal(false); + picker_window->hide(); +} + +void ColorPicker::_pick_button_pressed_legacy() { if (!is_inside_tree()) { return; } @@ -1469,7 +1481,7 @@ void ColorPicker::_pick_button_pressed() { picker_texture_rect->set_anchors_preset(Control::PRESET_FULL_RECT); picker_window->add_child(picker_texture_rect); picker_texture_rect->set_default_cursor_shape(CURSOR_POINTING_HAND); - picker_texture_rect->connect(SNAME("gui_input"), callable_mp(this, &ColorPicker::_picker_texture_input)); + picker_texture_rect->connect("gui_input", callable_mp(this, &ColorPicker::_picker_texture_input)); picker_preview = memnew(Panel); picker_preview->set_anchors_preset(Control::PRESET_CENTER_TOP); @@ -1529,6 +1541,30 @@ void ColorPicker::_pick_button_pressed() { picker_window->popup(); } +void ColorPicker::_picker_texture_input(const Ref<InputEvent> &p_event) { + if (!is_inside_tree()) { + return; + } + + Ref<InputEventMouseButton> bev = p_event; + if (bev.is_valid() && bev->get_button_index() == MouseButton::LEFT && !bev->is_pressed()) { + set_pick_color(picker_color); + emit_signal(SNAME("color_changed"), color); + picker_window->hide(); + } + + Ref<InputEventMouseMotion> mev = p_event; + if (mev.is_valid()) { + Ref<Image> img = picker_texture_rect->get_texture()->get_image(); + if (img.is_valid() && !img->is_empty()) { + Vector2 ofs = mev->get_position(); + picker_color = img->get_pixel(ofs.x, ofs.y); + picker_preview_style_box->set_bg_color(picker_color); + picker_preview_label->set_self_modulate(picker_color.get_luminance() < 0.5 ? Color(1.0f, 1.0f, 1.0f) : Color(0.0f, 0.0f, 0.0f)); + } + } +} + void ColorPicker::_html_focus_exit() { if (c_text->is_menu_visible()) { return; @@ -1692,8 +1728,14 @@ ColorPicker::ColorPicker() { btn_pick = memnew(Button); sample_hbc->add_child(btn_pick); - btn_pick->set_tooltip_text(RTR("Pick a color from the application window.")); - btn_pick->connect(SNAME("pressed"), callable_mp(this, &ColorPicker::_pick_button_pressed)); + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_SCREEN_CAPTURE)) { + btn_pick->set_tooltip_text(RTR("Pick a color from the screen.")); + btn_pick->connect(SNAME("pressed"), callable_mp(this, &ColorPicker::_pick_button_pressed)); + } else { + // On unsupported platforms, use a legacy method for color picking. + btn_pick->set_tooltip_text(RTR("Pick a color from the application window.")); + btn_pick->connect(SNAME("pressed"), callable_mp(this, &ColorPicker::_pick_button_pressed_legacy)); + } sample = memnew(TextureRect); sample_hbc->add_child(sample); diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h index 018ae10955..711a371688 100644 --- a/scene/gui/color_picker.h +++ b/scene/gui/color_picker.h @@ -122,11 +122,13 @@ private: Vector<ColorMode *> modes; Popup *picker_window = nullptr; + // Legacy color picking. TextureRect *picker_texture_rect = nullptr; Panel *picker_preview = nullptr; Label *picker_preview_label = nullptr; Ref<StyleBoxFlat> picker_preview_style_box; Color picker_color; + Control *uv_edit = nullptr; Control *w_edit = nullptr; AspectRatioContainer *wheel_edit = nullptr; @@ -183,6 +185,7 @@ private: Color color; Color old_color; + bool is_picking_color = false; bool display_old_color = false; bool deferred_mode_enabled = false; @@ -259,11 +262,14 @@ private: void _line_edit_input(const Ref<InputEvent> &p_event); void _preset_input(const Ref<InputEvent> &p_event, const Color &p_color); void _recent_preset_pressed(const bool pressed, ColorPresetButton *p_preset); - void _picker_texture_input(const Ref<InputEvent> &p_event); void _text_changed(const String &p_new_text); void _add_preset_pressed(); - void _pick_button_pressed(); void _html_focus_exit(); + void _pick_button_pressed(); + void _pick_finished(); + // Legacy color picking. + void _pick_button_pressed_legacy(); + void _picker_texture_input(const Ref<InputEvent> &p_event); inline int _get_preset_size(); void _add_preset_button(int p_size, const Color &p_color); diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index 594276a428..aa9ecd4142 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -1260,12 +1260,13 @@ void ItemList::_notification(int p_what) { text_ofs += base_ofs; text_ofs += items[i].rect_cache.position; + float text_w = width - text_ofs.x; + items.write[i].text_buf->set_width(text_w); + if (rtl) { - text_ofs.x = size.width - text_ofs.x - max_len; + text_ofs.x = size.width - width; } - items.write[i].text_buf->set_width(width - text_ofs.x); - if (rtl) { items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_RIGHT); } else { diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 185db1b972..e12180f2f9 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -277,7 +277,10 @@ float RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font l.offset.x = _find_margin(l.from, p_base_font, p_base_font_size); l.text_buf->set_width(p_width - l.offset.x); - if (tab_size > 0) { // Align inline tabs. + PackedFloat32Array tab_stops = _find_tab_stops(l.from); + if (!tab_stops.is_empty()) { + l.text_buf->tab_align(tab_stops); + } else if (tab_size > 0) { // Align inline tabs. Vector<float> tabs; tabs.push_back(tab_size * p_base_font->get_char_size(' ', p_base_font_size).width); l.text_buf->tab_align(tabs); @@ -481,7 +484,10 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> l.text_buf->set_alignment(_find_alignment(l.from)); l.text_buf->set_direction(_find_direction(l.from)); - if (tab_size > 0) { // Align inline tabs. + PackedFloat32Array tab_stops = _find_tab_stops(l.from); + if (!tab_stops.is_empty()) { + l.text_buf->tab_align(tab_stops); + } else if (tab_size > 0) { // Align inline tabs. Vector<float> tabs; tabs.push_back(tab_size * p_base_font->get_char_size(' ', p_base_font_size).width); l.text_buf->tab_align(tabs); @@ -2468,6 +2474,21 @@ BitField<TextServer::JustificationFlag> RichTextLabel::_find_jst_flags(Item *p_i return default_jst_flags; } +PackedFloat32Array RichTextLabel::_find_tab_stops(Item *p_item) { + Item *item = p_item; + + while (item) { + if (item->type == ITEM_PARAGRAPH) { + ItemParagraph *p = static_cast<ItemParagraph *>(item); + return p->tab_stops; + } + + item = item->parent; + } + + return PackedFloat32Array(); +} + HorizontalAlignment RichTextLabel::_find_alignment(Item *p_item) { Item *item = p_item; @@ -3312,7 +3333,7 @@ void RichTextLabel::push_strikethrough() { _add_item(item, true); } -void RichTextLabel::push_paragraph(HorizontalAlignment p_alignment, Control::TextDirection p_direction, const String &p_language, TextServer::StructuredTextParser p_st_parser, BitField<TextServer::JustificationFlag> p_jst_flags) { +void RichTextLabel::push_paragraph(HorizontalAlignment p_alignment, Control::TextDirection p_direction, const String &p_language, TextServer::StructuredTextParser p_st_parser, BitField<TextServer::JustificationFlag> p_jst_flags, const PackedFloat32Array &p_tab_stops) { _stop_thread(); MutexLock data_lock(data_mutex); @@ -3324,6 +3345,7 @@ void RichTextLabel::push_paragraph(HorizontalAlignment p_alignment, Control::Tex item->language = p_language; item->st_parser = p_st_parser; item->jst_flags = p_jst_flags; + item->tab_stops = p_tab_stops; _add_item(item, true, true); } @@ -4094,6 +4116,7 @@ void RichTextLabel::append_text(const String &p_bbcode) { HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_LEFT; Control::TextDirection dir = Control::TEXT_DIRECTION_INHERITED; String lang; + PackedFloat32Array tab_stops; TextServer::StructuredTextParser st_parser_type = TextServer::STRUCTURED_TEXT_DEFAULT; BitField<TextServer::JustificationFlag> jst_flags = default_jst_flags; for (int i = 0; i < subtag.size(); i++) { @@ -4118,6 +4141,11 @@ void RichTextLabel::append_text(const String &p_bbcode) { jst_flags.set_flag(TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE); } } + } else if (subtag_a[0] == "tab_stops") { + Vector<String> splitters; + splitters.push_back(","); + splitters.push_back(";"); + tab_stops = subtag_a[1].split_floats_mk(splitters); } else if (subtag_a[0] == "align") { if (subtag_a[1] == "l" || subtag_a[1] == "left") { alignment = HORIZONTAL_ALIGNMENT_LEFT; @@ -4157,7 +4185,7 @@ void RichTextLabel::append_text(const String &p_bbcode) { } } } - push_paragraph(alignment, dir, lang, st_parser_type, jst_flags); + push_paragraph(alignment, dir, lang, st_parser_type, jst_flags, tab_stops); pos = brk_end + 1; tag_stack.push_front("p"); } else if (tag == "url") { @@ -5406,7 +5434,7 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("push_color", "color"), &RichTextLabel::push_color); ClassDB::bind_method(D_METHOD("push_outline_size", "outline_size"), &RichTextLabel::push_outline_size); ClassDB::bind_method(D_METHOD("push_outline_color", "color"), &RichTextLabel::push_outline_color); - ClassDB::bind_method(D_METHOD("push_paragraph", "alignment", "base_direction", "language", "st_parser", "justification_flags"), &RichTextLabel::push_paragraph, DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(""), DEFVAL(TextServer::STRUCTURED_TEXT_DEFAULT), DEFVAL(TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_SKIP_LAST_LINE | TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE)); + ClassDB::bind_method(D_METHOD("push_paragraph", "alignment", "base_direction", "language", "st_parser", "justification_flags", "tab_stops"), &RichTextLabel::push_paragraph, DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(""), DEFVAL(TextServer::STRUCTURED_TEXT_DEFAULT), DEFVAL(TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_SKIP_LAST_LINE | TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE), DEFVAL(PackedFloat32Array())); ClassDB::bind_method(D_METHOD("push_indent", "level"), &RichTextLabel::push_indent); ClassDB::bind_method(D_METHOD("push_list", "level", "type", "capitalize", "bullet"), &RichTextLabel::push_list, DEFVAL(String::utf8("•"))); ClassDB::bind_method(D_METHOD("push_meta", "data"), &RichTextLabel::push_meta); diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 3e3413d47a..f6cc9b2ac4 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -241,6 +241,7 @@ private: Control::TextDirection direction = Control::TEXT_DIRECTION_AUTO; TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT; BitField<TextServer::JustificationFlag> jst_flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_SKIP_LAST_LINE | TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE; + PackedFloat32Array tab_stops; ItemParagraph() { type = ITEM_PARAGRAPH; } }; @@ -493,6 +494,7 @@ private: ItemDropcap *_find_dc_item(Item *p_item); int _find_list(Item *p_item, Vector<int> &r_index, Vector<ItemList *> &r_list); int _find_margin(Item *p_item, const Ref<Font> &p_base_font, int p_base_font_size); + PackedFloat32Array _find_tab_stops(Item *p_item); HorizontalAlignment _find_alignment(Item *p_item); BitField<TextServer::JustificationFlag> _find_jst_flags(Item *p_item); TextServer::Direction _find_direction(Item *p_item); @@ -596,7 +598,7 @@ public: void push_outline_color(const Color &p_color); void push_underline(); void push_strikethrough(); - void push_paragraph(HorizontalAlignment p_alignment, Control::TextDirection p_direction = Control::TEXT_DIRECTION_INHERITED, const String &p_language = "", TextServer::StructuredTextParser p_st_parser = TextServer::STRUCTURED_TEXT_DEFAULT, BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_SKIP_LAST_LINE | TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE); + void push_paragraph(HorizontalAlignment p_alignment, Control::TextDirection p_direction = Control::TEXT_DIRECTION_INHERITED, const String &p_language = "", TextServer::StructuredTextParser p_st_parser = TextServer::STRUCTURED_TEXT_DEFAULT, BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_SKIP_LAST_LINE | TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE, const PackedFloat32Array &p_tab_stops = PackedFloat32Array()); void push_indent(int p_level); void push_list(int p_level, ListType p_list, bool p_capitalize, const String &p_bullet = String::utf8("•")); void push_meta(const Variant &p_meta); diff --git a/scene/main/node.h b/scene/main/node.h index 4d4e71ee56..4253928427 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -52,13 +52,17 @@ protected: // During group processing, these are thread-safe. // Outside group processing, these avoid the cost of sync by working as plain primitive types. union MTFlag { - SafeFlag mt{}; + SafeFlag mt; bool st; + MTFlag() : + mt{} {} }; template <class T> union MTNumeric { - SafeNumeric<T> mt{}; + SafeNumeric<T> mt; T st; + MTNumeric() : + mt{} {} }; public: diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index 97ab91e26d..a0bd22c79b 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -452,7 +452,7 @@ void Curve::bake() { _baked_cache.resize(_bake_resolution); for (int i = 1; i < _bake_resolution - 1; ++i) { - real_t x = i / static_cast<real_t>(_bake_resolution); + real_t x = i / static_cast<real_t>(_bake_resolution - 1); real_t y = sample(x); _baked_cache.write[i] = y; } @@ -489,7 +489,7 @@ real_t Curve::sample_baked(real_t p_offset) const { } // Get interpolation index - real_t fi = p_offset * _baked_cache.size(); + real_t fi = p_offset * (_baked_cache.size() - 1); int i = Math::floor(fi); if (i < 0) { i = 0; diff --git a/scene/resources/sky_material.cpp b/scene/resources/sky_material.cpp index 065132516b..670ce152bd 100644 --- a/scene/resources/sky_material.cpp +++ b/scene/resources/sky_material.cpp @@ -74,8 +74,11 @@ float ProceduralSkyMaterial::get_sky_energy_multiplier() const { void ProceduralSkyMaterial::set_sky_cover(const Ref<Texture2D> &p_sky_cover) { sky_cover = p_sky_cover; - RID tex_rid = p_sky_cover.is_valid() ? p_sky_cover->get_rid() : RID(); - RS::get_singleton()->material_set_param(_get_material(), "sky_cover", tex_rid); + if (p_sky_cover.is_valid()) { + RS::get_singleton()->material_set_param(_get_material(), "sky_cover", p_sky_cover->get_rid()); + } else { + RS::get_singleton()->material_set_param(_get_material(), "sky_cover", Variant()); + } } Ref<Texture2D> ProceduralSkyMaterial::get_sky_cover() const { @@ -365,8 +368,11 @@ ProceduralSkyMaterial::~ProceduralSkyMaterial() { void PanoramaSkyMaterial::set_panorama(const Ref<Texture2D> &p_panorama) { panorama = p_panorama; - RID tex_rid = p_panorama.is_valid() ? p_panorama->get_rid() : RID(); - RS::get_singleton()->material_set_param(_get_material(), "source_panorama", tex_rid); + if (p_panorama.is_valid()) { + RS::get_singleton()->material_set_param(_get_material(), "source_panorama", p_panorama->get_rid()); + } else { + RS::get_singleton()->material_set_param(_get_material(), "source_panorama", Variant()); + } } Ref<Texture2D> PanoramaSkyMaterial::get_panorama() const { @@ -558,8 +564,11 @@ bool PhysicalSkyMaterial::get_use_debanding() const { void PhysicalSkyMaterial::set_night_sky(const Ref<Texture2D> &p_night_sky) { night_sky = p_night_sky; - RID tex_rid = p_night_sky.is_valid() ? p_night_sky->get_rid() : RID(); - RS::get_singleton()->material_set_param(_get_material(), "night_sky", tex_rid); + if (p_night_sky.is_valid()) { + RS::get_singleton()->material_set_param(_get_material(), "night_sky", p_night_sky->get_rid()); + } else { + RS::get_singleton()->material_set_param(_get_material(), "night_sky", Variant()); + } } Ref<Texture2D> PhysicalSkyMaterial::get_night_sky() const { diff --git a/servers/rendering/dummy/storage/light_storage.h b/servers/rendering/dummy/storage/light_storage.h index b9e8bcc6f1..a9a7beb387 100644 --- a/servers/rendering/dummy/storage/light_storage.h +++ b/servers/rendering/dummy/storage/light_storage.h @@ -77,6 +77,7 @@ public: virtual AABB light_get_aabb(RID p_light) const override { return AABB(); } virtual float light_get_param(RID p_light, RS::LightParam p_param) override { return 0.0; } virtual Color light_get_color(RID p_light) override { return Color(); } + virtual bool light_get_reverse_cull_face_mode(RID p_light) const override { return false; } virtual RS::LightBakeMode light_get_bake_mode(RID p_light) override { return RS::LIGHT_BAKE_DISABLED; } virtual uint32_t light_get_max_sdfgi_cascade(RID p_light) override { return 0; } virtual uint64_t light_get_version(RID p_light) const override { return 0; } diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index 779249c2da..bac788d0e9 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -2149,6 +2149,7 @@ void RenderForwardClustered::_render_shadow_pass(RID p_light, RID p_shadow_atlas uint32_t atlas_size = 1; RID atlas_fb; + bool reverse_cull_face = light_storage->light_get_reverse_cull_face_mode(base); bool using_dual_paraboloid = false; bool using_dual_paraboloid_flip = false; Vector2i dual_paraboloid_offset; @@ -2292,7 +2293,7 @@ void RenderForwardClustered::_render_shadow_pass(RID p_light, RID p_shadow_atlas if (render_cubemap) { //rendering to cubemap - _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, false, false, use_pancake, p_camera_plane, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, Rect2(), false, true, true, true, p_render_info); + _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, reverse_cull_face, false, false, use_pancake, p_camera_plane, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, Rect2(), false, true, true, true, p_render_info); if (finalize_cubemap) { _render_shadow_process(); _render_shadow_end(); @@ -2310,7 +2311,7 @@ void RenderForwardClustered::_render_shadow_pass(RID p_light, RID p_shadow_atlas } else { //render shadow - _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, using_dual_paraboloid, using_dual_paraboloid_flip, use_pancake, p_camera_plane, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, atlas_rect, flip_y, p_clear_region, p_open_pass, p_close_pass, p_render_info); + _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, reverse_cull_face, using_dual_paraboloid, using_dual_paraboloid_flip, use_pancake, p_camera_plane, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, atlas_rect, flip_y, p_clear_region, p_open_pass, p_close_pass, p_render_info); } } @@ -2323,7 +2324,7 @@ void RenderForwardClustered::_render_shadow_begin() { scene_state.instance_data[RENDER_LIST_SECONDARY].clear(); } -void RenderForwardClustered::_render_shadow_append(RID p_framebuffer, const PagedArray<RenderGeometryInstance *> &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, const Plane &p_camera_plane, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, const Rect2i &p_rect, bool p_flip_y, bool p_clear_region, bool p_begin, bool p_end, RenderingMethod::RenderInfo *p_render_info) { +void RenderForwardClustered::_render_shadow_append(RID p_framebuffer, const PagedArray<RenderGeometryInstance *> &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_reverse_cull_face, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, const Plane &p_camera_plane, float p_lod_distance_multiplier, float p_screen_mesh_lod_threshold, const Rect2i &p_rect, bool p_flip_y, bool p_clear_region, bool p_begin, bool p_end, RenderingMethod::RenderInfo *p_render_info) { uint32_t shadow_pass_index = scene_state.shadow_passes.size(); SceneState::ShadowPass shadow_pass; @@ -2370,6 +2371,10 @@ void RenderForwardClustered::_render_shadow_append(RID p_framebuffer, const Page flip_cull = !flip_cull; } + if (p_reverse_cull_face) { + flip_cull = !flip_cull; + } + shadow_pass.element_from = render_list_from; shadow_pass.element_count = render_list_size; shadow_pass.flip_cull = flip_cull; diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h index e07d2f2258..7ab9e67bac 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h @@ -587,7 +587,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { void _render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0, float p_screen_mesh_lod_threshold = 0.0, bool p_open_pass = true, bool p_close_pass = true, bool p_clear_region = true, RenderingMethod::RenderInfo *p_render_info = nullptr); void _render_shadow_begin(); - void _render_shadow_append(RID p_framebuffer, const PagedArray<RenderGeometryInstance *> &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, const Rect2i &p_rect = Rect2i(), bool p_flip_y = false, bool p_clear_region = true, bool p_begin = true, bool p_end = true, RenderingMethod::RenderInfo *p_render_info = nullptr); + void _render_shadow_append(RID p_framebuffer, const PagedArray<RenderGeometryInstance *> &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_reverse_cull_face, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, const Rect2i &p_rect = Rect2i(), bool p_flip_y = false, bool p_clear_region = true, bool p_begin = true, bool p_end = true, RenderingMethod::RenderInfo *p_render_info = nullptr); void _render_shadow_process(); void _render_shadow_end(uint32_t p_barrier = RD::BARRIER_MASK_ALL_BARRIERS); diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.h b/servers/rendering/renderer_rd/storage_rd/light_storage.h index 3360358169..512c440ae8 100644 --- a/servers/rendering/renderer_rd/storage_rd/light_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/light_storage.h @@ -451,7 +451,7 @@ public: /* LIGHT */ - bool owns_light(RID p_rid) { return light_owner.owns(p_rid); }; + bool owns_light(RID p_rid) { return light_owner.owns(p_rid); } void _light_initialize(RID p_rid, RS::LightType p_type); @@ -565,6 +565,13 @@ public: return light->param[RS::LIGHT_PARAM_TRANSMITTANCE_BIAS]; } + virtual bool light_get_reverse_cull_face_mode(RID p_light) const override { + const Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND_V(!light, false); + + return light->reverse_cull; + } + virtual RS::LightBakeMode light_get_bake_mode(RID p_light) override; virtual uint32_t light_get_max_sdfgi_cascade(RID p_light) override; virtual uint64_t light_get_version(RID p_light) const override; diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp index b91e73c885..d3c22e4826 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp @@ -627,7 +627,7 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) { if (skeleton->use_2d) { for (int j = 0; j < bs; j++) { - if (skbones[0].size == Vector3()) { + if (skbones[j].size == Vector3(-1, -1, -1)) { continue; //bone is unused } @@ -654,7 +654,7 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) { } } else { for (int j = 0; j < bs; j++) { - if (skbones[0].size == Vector3()) { + if (skbones[j].size == Vector3(-1, -1, -1)) { continue; //bone is unused } diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp index 3d6092a9f6..8390782de3 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp @@ -1153,8 +1153,8 @@ void TextureStorage::texture_3d_update(RID p_texture, const Vector<Ref<Image>> & image = image->duplicate(); image->convert(tex->validated_format); } - all_data_size += images[i]->get_data().size(); - images.push_back(image); + all_data_size += image->get_data().size(); + images.write[i] = image; } all_data.resize(all_data_size); //consolidate all data here diff --git a/servers/rendering/storage/light_storage.h b/servers/rendering/storage/light_storage.h index 5bd4297179..c1f79cfc49 100644 --- a/servers/rendering/storage/light_storage.h +++ b/servers/rendering/storage/light_storage.h @@ -81,6 +81,7 @@ public: virtual AABB light_get_aabb(RID p_light) const = 0; virtual float light_get_param(RID p_light, RS::LightParam p_param) = 0; virtual Color light_get_color(RID p_light) = 0; + virtual bool light_get_reverse_cull_face_mode(RID p_light) const = 0; virtual RS::LightBakeMode light_get_bake_mode(RID p_light) = 0; virtual uint32_t light_get_max_sdfgi_cascade(RID p_light) = 0; virtual uint64_t light_get_version(RID p_light) const = 0; diff --git a/tests/scene/test_curve.h b/tests/scene/test_curve.h index bfa37e8f69..d67550f9f7 100644 --- a/tests/scene/test_curve.h +++ b/tests/scene/test_curve.h @@ -62,6 +62,7 @@ TEST_CASE("[Curve] Custom curve with free tangents") { curve->add_point(Vector2(0.25, 1)); curve->add_point(Vector2(0.5, 0)); curve->add_point(Vector2(0.75, 1)); + curve->set_bake_resolution(11); CHECK_MESSAGE( Math::is_zero_approx(curve->get_point_left_tangent(0)), @@ -82,41 +83,41 @@ TEST_CASE("[Curve] Custom curve with free tangents") { CHECK_MESSAGE( Math::is_zero_approx(curve->sample(-0.1)), - "Custom free curve should return the expected value at offset 0.1."); + "Custom free curve should return the expected value at offset -0.1."); CHECK_MESSAGE( curve->sample(0.1) == doctest::Approx((real_t)0.352), "Custom free curve should return the expected value at offset 0.1."); CHECK_MESSAGE( curve->sample(0.4) == doctest::Approx((real_t)0.352), - "Custom free curve should return the expected value at offset 0.1."); + "Custom free curve should return the expected value at offset 0.4."); CHECK_MESSAGE( curve->sample(0.7) == doctest::Approx((real_t)0.896), - "Custom free curve should return the expected value at offset 0.1."); + "Custom free curve should return the expected value at offset 0.7."); CHECK_MESSAGE( curve->sample(1) == doctest::Approx(1), - "Custom free curve should return the expected value at offset 0.1."); + "Custom free curve should return the expected value at offset 1."); CHECK_MESSAGE( curve->sample(2) == doctest::Approx(1), - "Custom free curve should return the expected value at offset 0.1."); + "Custom free curve should return the expected value at offset 2."); CHECK_MESSAGE( Math::is_zero_approx(curve->sample_baked(-0.1)), - "Custom free curve should return the expected baked value at offset 0.1."); + "Custom free curve should return the expected baked value at offset -0.1."); CHECK_MESSAGE( curve->sample_baked(0.1) == doctest::Approx((real_t)0.352), "Custom free curve should return the expected baked value at offset 0.1."); CHECK_MESSAGE( curve->sample_baked(0.4) == doctest::Approx((real_t)0.352), - "Custom free curve should return the expected baked value at offset 0.1."); + "Custom free curve should return the expected baked value at offset 0.4."); CHECK_MESSAGE( curve->sample_baked(0.7) == doctest::Approx((real_t)0.896), - "Custom free curve should return the expected baked value at offset 0.1."); + "Custom free curve should return the expected baked value at offset 0.7."); CHECK_MESSAGE( curve->sample_baked(1) == doctest::Approx(1), - "Custom free curve should return the expected baked value at offset 0.1."); + "Custom free curve should return the expected baked value at offset 1."); CHECK_MESSAGE( curve->sample_baked(2) == doctest::Approx(1), - "Custom free curve should return the expected baked value at offset 0.1."); + "Custom free curve should return the expected baked value at offset 2."); curve->remove_point(1); CHECK_MESSAGE( @@ -218,6 +219,16 @@ TEST_CASE("[Curve] Custom curve with linear tangents") { "Custom free curve should return the expected baked value at offset 0.7 after removing point at invalid index 10."); } +TEST_CASE("[Curve] Straight line offset test") { + Ref<Curve> curve = memnew(Curve); + curve->add_point(Vector2(0, 0)); + curve->add_point(Vector2(1, 1)); + + CHECK_MESSAGE( + curve->sample_baked(1.0 - (0.5 / curve->get_bake_resolution())) != curve->sample_baked(1), + "Straight line curve should return different baked values at offset 1 vs offset (1 - 0.5 / bake resolution) ."); +} + TEST_CASE("[Curve2D] Linear sampling should return exact value") { Ref<Curve2D> curve = memnew(Curve2D); real_t len = 2048.0; |
