diff options
33 files changed, 583 insertions, 111 deletions
diff --git a/core/math/basis.cpp b/core/math/basis.cpp index 234a4ddb79..95a4187062 100644 --- a/core/math/basis.cpp +++ b/core/math/basis.cpp @@ -239,13 +239,18 @@ void Basis::scale_orthogonal(const Vector3 &p_scale) { Basis Basis::scaled_orthogonal(const Vector3 &p_scale) const { Basis m = *this; Vector3 s = Vector3(-1, -1, -1) + p_scale; + bool sign = signbit(s.x + s.y + s.z); + Basis b = m.orthonormalized(); + s = b.xform_inv(s); Vector3 dots; - Basis b; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { dots[j] += s[i] * abs(m.get_column(i).normalized().dot(b.get_column(j))); } } + if (sign != signbit(dots.x + dots.y + dots.z)) { + dots = -dots; + } m.scale_local(Vector3(1, 1, 1) + dots); return m; } diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp index 61f5cfc22a..1d53cf66d4 100644 --- a/core/object/script_language.cpp +++ b/core/object/script_language.cpp @@ -369,10 +369,6 @@ void ScriptServer::save_global_classes() { ProjectSettings::get_singleton()->store_global_class_list(gcarr); } -bool ScriptServer::has_global_classes() { - return !global_classes.is_empty(); -} - String ScriptServer::get_global_class_cache_file_path() { return ProjectSettings::get_singleton()->get_global_class_list_path(); } diff --git a/core/object/script_language.h b/core/object/script_language.h index b1393c7196..14cc30e029 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -91,7 +91,6 @@ public: static void get_global_class_list(List<StringName> *r_global_classes); static void get_inheriters_list(const StringName &p_base_type, List<StringName> *r_classes); static void save_global_classes(); - static bool has_global_classes(); static String get_global_class_cache_file_path(); static void init_languages(); diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml index e30ff6be19..ab7ae82875 100644 --- a/doc/classes/Object.xml +++ b/doc/classes/Object.xml @@ -639,7 +639,7 @@ <description> Returns an [Array] of connections for the given [param signal] name. Each connection is represented as a [Dictionary] that contains three entries: - [code]signal[/code] is a reference to the [Signal]; - - [code]callable[/code] is a reference to the [Callable]; + - [code]callable[/code] is a reference to the connected [Callable]; - [code]flags[/code] is a combination of [enum ConnectFlags]. </description> </method> diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml index 1ecc8a1d4e..49bb65b64d 100644 --- a/doc/classes/RichTextLabel.xml +++ b/doc/classes/RichTextLabel.xml @@ -106,6 +106,45 @@ <return type="PopupMenu" /> <description> Returns the [PopupMenu] of this [RichTextLabel]. By default, this menu is displayed when right-clicking on the [RichTextLabel]. + You can add custom menu items or remove standard ones. Make sure your IDs don't conflict with the standard ones (see [enum MenuItems]). For example: + [codeblocks] + [gdscript] + func _ready(): + var menu = get_menu() + # Remove "Select All" item. + menu.remove_item(MENU_SELECT_ALL) + # Add custom items. + menu.add_separator() + menu.add_item("Duplicate Text", MENU_MAX + 1) + # Connect callback. + menu.id_pressed.connect(_on_item_pressed) + + func _on_item_pressed(id): + if id == MENU_MAX + 1: + add_text("\n" + get_parsed_text()) + [/gdscript] + [csharp] + public override void _Ready() + { + var menu = GetMenu(); + // Remove "Select All" item. + menu.RemoveItem(RichTextLabel.MenuItems.SelectAll); + // Add custom items. + menu.AddSeparator(); + menu.AddItem("Duplicate Text", RichTextLabel.MenuItems.Max + 1); + // Add event handler. + menu.IdPressed += OnItemPressed; + } + + public void OnItemPressed(int id) + { + if (id == TextEdit.MenuItems.Max + 1) + { + AddText("\n" + GetParsedText()); + } + } + [/csharp] + [/codeblocks] [b]Warning:[/b] This is a required internal node, removing and freeing it may cause a crash. If you wish to hide it or any of its children, use their [member Window.visible] property. </description> </method> @@ -193,6 +232,13 @@ If [member threaded] is enabled, returns [code]true[/code] if the background thread has finished text processing, otherwise always return [code]true[/code]. </description> </method> + <method name="menu_option"> + <return type="void" /> + <param index="0" name="option" type="int" /> + <description> + Executes a given action as defined in the [enum MenuItems] enum. + </description> + </method> <method name="newline"> <return type="void" /> <description> @@ -633,6 +679,15 @@ </constant> <constant name="ITEM_CUSTOMFX" value="26" enum="ItemType"> </constant> + <constant name="MENU_COPY" value="0" enum="MenuItems"> + Copies the selected text. + </constant> + <constant name="MENU_SELECT_ALL" value="1" enum="MenuItems"> + Selects the whole [RichTextLabel] text. + </constant> + <constant name="MENU_MAX" value="2" enum="MenuItems"> + Represents the size of the [enum MenuItems] enum. + </constant> </constants> <theme_items> <theme_item name="default_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)"> diff --git a/doc/classes/Signal.xml b/doc/classes/Signal.xml index 71905e8b2e..cdbeba0220 100644 --- a/doc/classes/Signal.xml +++ b/doc/classes/Signal.xml @@ -85,7 +85,10 @@ <method name="get_connections" qualifiers="const"> <return type="Array" /> <description> - Returns the list of [Callable]s connected to this signal. + Returns an [Array] of connections for this signal. Each connection is represented as a [Dictionary] that contains three entries: + - [code]signal[/code] is a reference to this signal; + - [code]callable[/code] is a reference to the connected [Callable]; + - [code]flags[/code] is a combination of [enum Object.ConnectFlags]. </description> </method> <method name="get_name" qualifiers="const"> diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index 672dcacd88..96278c3b92 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -179,7 +179,7 @@ typedef void (*DEBUGPROCARB)(GLenum source, typedef void (*DebugMessageCallbackARB)(DEBUGPROCARB callback, const void *userParam); void RasterizerGLES3::initialize() { - print_line("OpenGL Renderer: " + RS::get_singleton()->get_video_adapter_name()); + print_line(vformat("OpenGL API %s - Compatibility - Using Device: %s - %s", RS::get_singleton()->get_video_adapter_api_version(), RS::get_singleton()->get_video_adapter_vendor(), RS::get_singleton()->get_video_adapter_name())); } void RasterizerGLES3::finalize() { diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp index d1391cb53e..864ad5931a 100644 --- a/drivers/vulkan/vulkan_context.cpp +++ b/drivers/vulkan/vulkan_context.cpp @@ -1299,8 +1299,15 @@ Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) { // Get device version device_api_version = gpu_props.apiVersion; + String rendering_method; + if (OS::get_singleton()->get_current_rendering_method() == "mobile") { + rendering_method = "Forward Mobile"; + } else { + rendering_method = "Forward+"; + } + // Output our device version - print_line("Vulkan API " + get_device_api_version() + " - " + "Using Vulkan Device #" + itos(device_index) + ": " + device_vendor + " - " + device_name); + print_line(vformat("Vulkan API %s - %s - Using Vulkan Device #%d: %s - %s", get_device_api_version(), rendering_method, device_index, device_vendor, device_name)); { Error _err = _initialize_device_extensions(); diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index d2c82ad013..6410821dcf 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -1423,9 +1423,11 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_icon("grabber", "VSplitContainer", theme->get_icon(SNAME("GuiVsplitter"), SNAME("EditorIcons"))); theme->set_icon("grabber", "HSplitContainer", theme->get_icon(SNAME("GuiHsplitter"), SNAME("EditorIcons"))); + theme->set_constant("separation", "SplitContainer", default_margin_size * 2 * EDSCALE); theme->set_constant("separation", "HSplitContainer", default_margin_size * 2 * EDSCALE); theme->set_constant("separation", "VSplitContainer", default_margin_size * 2 * EDSCALE); + theme->set_constant("minimum_grab_thickness", "SplitContainer", 6 * EDSCALE); theme->set_constant("minimum_grab_thickness", "HSplitContainer", 6 * EDSCALE); theme->set_constant("minimum_grab_thickness", "VSplitContainer", 6 * EDSCALE); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 36d1e54246..b001b4f766 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -1415,9 +1415,6 @@ Transform3D Node3DEditorViewport::_compute_transform(TransformMode p_mode, const // Recalculate orthogonalized scale without moving origin. if (p_orthogonal) { s.basis = p_original.basis.scaled_orthogonal(p_motion + Vector3(1, 1, 1)); - // The scaled_orthogonal() does not require orthogonal Basis, - // but it may make a bit skew by precision problems. - s.basis.orthogonalize(); } } @@ -4634,7 +4631,7 @@ void Node3DEditorViewport::update_transform(Point2 p_mousepos, bool p_shift) { if (se->gizmo.is_valid()) { for (KeyValue<int, Transform3D> &GE : se->subgizmos) { Transform3D xform = GE.value; - Transform3D new_xform = _compute_transform(TRANSFORM_SCALE, se->original * xform, xform, motion, snap, local_coords, true); // Force orthogonal with subgizmo. + Transform3D new_xform = _compute_transform(TRANSFORM_SCALE, se->original * xform, xform, motion, snap, local_coords, _edit.plane != TRANSFORM_VIEW); // Force orthogonal with subgizmo. if (!local_coords) { new_xform = se->original.affine_inverse() * new_xform; } diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index 782e365138..7f43de3240 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -1301,7 +1301,7 @@ void Skeleton3DGizmoPlugin::set_subgizmo_transform(const EditorNode3DGizmo *p_gi Transform3D original_to_local; int parent_idx = skeleton->get_bone_parent(p_id); if (parent_idx >= 0) { - original_to_local = original_to_local * skeleton->get_bone_global_pose(parent_idx); + original_to_local = skeleton->get_bone_global_pose(parent_idx); } Basis to_local = original_to_local.get_basis().inverse(); diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 59b5795ae3..af761a2cea 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -128,7 +128,7 @@ void VisualShaderGraphPlugin::set_connections(const List<VisualShader::Connectio connections = p_connections; } -void VisualShaderGraphPlugin::show_port_preview(VisualShader::Type p_type, int p_node_id, int p_port_id) { +void VisualShaderGraphPlugin::show_port_preview(VisualShader::Type p_type, int p_node_id, int p_port_id, bool p_is_valid) { if (visual_shader->get_shader_type() == p_type && links.has(p_node_id) && links[p_node_id].output_ports.has(p_port_id)) { Link &link = links[p_node_id]; @@ -162,7 +162,7 @@ void VisualShaderGraphPlugin::show_port_preview(VisualShader::Type p_type, int p vbox->add_child(offset); VisualShaderNodePortPreview *port_preview = memnew(VisualShaderNodePortPreview); - port_preview->setup(visual_shader, visual_shader->get_shader_type(), p_node_id, p_port_id); + port_preview->setup(visual_shader, visual_shader->get_shader_type(), p_node_id, p_port_id, p_is_valid); port_preview->set_h_size_flags(Control::SIZE_SHRINK_CENTER); vbox->add_child(port_preview); link.preview_visible = true; @@ -351,6 +351,29 @@ void VisualShaderGraphPlugin::update_theme() { vector_expanded_color[3] = editor->get_theme_color(SNAME("axis_w_color"), SNAME("Editor")); // alpha } +bool VisualShaderGraphPlugin::is_node_has_parameter_instances_relatively(VisualShader::Type p_type, int p_node) const { + bool result = false; + + Ref<VisualShaderNodeParameter> parameter_node = Object::cast_to<VisualShaderNodeParameter>(visual_shader->get_node_unchecked(p_type, p_node).ptr()); + if (parameter_node.is_valid()) { + if (parameter_node->get_qualifier() == VisualShaderNodeParameter::QUAL_INSTANCE) { + return true; + } + } + + LocalVector<int> prev_connected_nodes; + visual_shader->get_prev_connected_nodes(p_type, p_node, prev_connected_nodes); + + for (const int &E : prev_connected_nodes) { + result = is_node_has_parameter_instances_relatively(p_type, E); + if (result) { + break; + } + } + + return result; +} + void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool p_just_update) { if (!visual_shader.is_valid() || p_type != visual_shader->get_shader_type()) { return; @@ -969,8 +992,10 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool } } + bool has_relative_parameter_instances = false; if (vsnode->get_output_port_for_preview() >= 0) { - show_port_preview(p_type, p_id, vsnode->get_output_port_for_preview()); + has_relative_parameter_instances = is_node_has_parameter_instances_relatively(p_type, p_id); + show_port_preview(p_type, p_id, vsnode->get_output_port_for_preview(), !has_relative_parameter_instances); } else { offset = memnew(Control); offset->set_custom_minimum_size(Size2(0, 4 * EDSCALE)); @@ -978,6 +1003,9 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool } String error = vsnode->get_warning(mode, p_type); + if (has_relative_parameter_instances) { + error += "\n" + TTR("The 2D preview cannot correctly show the result retrieved from instance parameter."); + } if (!error.is_empty()) { Label *error_label = memnew(Label); error_label->add_theme_color_override("font_color", editor->get_theme_color(SNAME("error_color"), SNAME("Editor"))); @@ -4970,6 +4998,29 @@ void VisualShaderEditor::_update_preview() { } } +void VisualShaderEditor::_update_next_previews(int p_node_id) { + VisualShader::Type type = get_current_shader_type(); + + LocalVector<int> nodes; + _get_next_nodes_recursively(type, p_node_id, nodes); + + for (int node_id : nodes) { + if (graph_plugin->is_preview_visible(node_id)) { + graph_plugin->update_node_deferred(type, node_id); + } + } +} + +void VisualShaderEditor::_get_next_nodes_recursively(VisualShader::Type p_type, int p_node_id, LocalVector<int> &r_nodes) const { + LocalVector<int> next_connections; + visual_shader->get_next_connected_nodes(p_type, p_node_id, next_connections); + + for (int node_id : next_connections) { + r_nodes.push_back(node_id); + _get_next_nodes_recursively(p_type, node_id, r_nodes); + } +} + void VisualShaderEditor::_visibility_changed() { if (!is_visible()) { if (preview_window->is_visible()) { @@ -5002,6 +5053,7 @@ void VisualShaderEditor::_bind_methods() { ClassDB::bind_method("_update_options_menu_deferred", &VisualShaderEditor::_update_options_menu_deferred); ClassDB::bind_method("_rebuild_shader_deferred", &VisualShaderEditor::_rebuild_shader_deferred); ClassDB::bind_method("_resources_removed", &VisualShaderEditor::_resources_removed); + ClassDB::bind_method("_update_next_previews", &VisualShaderEditor::_update_next_previews); ClassDB::bind_method("_is_available", &VisualShaderEditor::_is_available); } @@ -6292,6 +6344,8 @@ public: if (p_property != "constant") { VisualShaderGraphPlugin *graph_plugin = editor->get_graph_plugin(); if (graph_plugin) { + undo_redo->add_do_method(editor, "_update_next_previews", node_id); + undo_redo->add_undo_method(editor, "_update_next_previews", node_id); undo_redo->add_do_method(graph_plugin, "update_node_deferred", shader_type, node_id); undo_redo->add_undo_method(graph_plugin, "update_node_deferred", shader_type, node_id); } @@ -6586,7 +6640,7 @@ bool EditorInspectorVisualShaderModePlugin::parse_property(Object *p_object, con ////////////////////////////////// void VisualShaderNodePortPreview::_shader_changed() { - if (shader.is_null()) { + if (!is_valid || shader.is_null()) { return; } @@ -6633,12 +6687,13 @@ void VisualShaderNodePortPreview::_shader_changed() { set_material(mat); } -void VisualShaderNodePortPreview::setup(const Ref<VisualShader> &p_shader, VisualShader::Type p_type, int p_node, int p_port) { +void VisualShaderNodePortPreview::setup(const Ref<VisualShader> &p_shader, VisualShader::Type p_type, int p_node, int p_port, bool p_is_valid) { shader = p_shader; - shader->connect("changed", callable_mp(this, &VisualShaderNodePortPreview::_shader_changed)); + shader->connect("changed", callable_mp(this, &VisualShaderNodePortPreview::_shader_changed), CONNECT_DEFERRED); type = p_type; port = p_port; node = p_node; + is_valid = p_is_valid; queue_redraw(); _shader_changed(); } @@ -6665,14 +6720,24 @@ void VisualShaderNodePortPreview::_notification(int p_what) { Vector2(0, 1) }; - Vector<Color> colors = { - Color(1, 1, 1, 1), - Color(1, 1, 1, 1), - Color(1, 1, 1, 1), - Color(1, 1, 1, 1) - }; + if (is_valid) { + Vector<Color> colors = { + Color(1, 1, 1, 1), + Color(1, 1, 1, 1), + Color(1, 1, 1, 1), + Color(1, 1, 1, 1) + }; + draw_primitive(points, colors, uvs); + } else { + Vector<Color> colors = { + Color(0, 0, 0, 1), + Color(0, 0, 0, 1), + Color(0, 0, 0, 1), + Color(0, 0, 0, 1) + }; + draw_primitive(points, colors, uvs); + } - draw_primitive(points, colors, uvs); } break; } } diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h index 142c8167a8..21139fbddd 100644 --- a/editor/plugins/visual_shader_editor_plugin.h +++ b/editor/plugins/visual_shader_editor_plugin.h @@ -120,7 +120,7 @@ public: void remove_node(VisualShader::Type p_type, int p_id, bool p_just_update); void connect_nodes(VisualShader::Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port); void disconnect_nodes(VisualShader::Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port); - void show_port_preview(VisualShader::Type p_type, int p_node_id, int p_port_id); + void show_port_preview(VisualShader::Type p_type, int p_node_id, int p_port_id, bool p_is_valid); void set_node_position(VisualShader::Type p_type, int p_id, const Vector2 &p_position); void refresh_node_ports(VisualShader::Type p_type, int p_node); void set_input_port_default_value(VisualShader::Type p_type, int p_node_id, int p_port_id, Variant p_value); @@ -133,6 +133,7 @@ public: Ref<Script> get_node_script(int p_node_id) const; void update_node_size(int p_node_id); void update_theme(); + bool is_node_has_parameter_instances_relatively(VisualShader::Type p_type, int p_node) const; VisualShader::Type get_shader_type() const; VisualShaderGraphPlugin(); @@ -354,6 +355,8 @@ class VisualShaderEditor : public VBoxContainer { void _preview_close_requested(); void _preview_size_changed(); void _update_preview(); + void _update_next_previews(int p_node_id); + void _get_next_nodes_recursively(VisualShader::Type p_type, int p_node_id, LocalVector<int> &r_nodes) const; String _get_description(int p_idx); struct DragOp { @@ -570,6 +573,7 @@ class VisualShaderNodePortPreview : public Control { VisualShader::Type type = VisualShader::Type::TYPE_MAX; int node = 0; int port = 0; + bool is_valid = false; void _shader_changed(); //must regen protected: void _notification(int p_what); @@ -577,7 +581,7 @@ protected: public: virtual Size2 get_minimum_size() const override; - void setup(const Ref<VisualShader> &p_shader, VisualShader::Type p_type, int p_node, int p_port); + void setup(const Ref<VisualShader> &p_shader, VisualShader::Type p_type, int p_node, int p_port, bool p_is_valid); }; class VisualShaderConversionPlugin : public EditorResourceConversionPlugin { diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 8324cb0fe0..a876229276 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -1496,7 +1496,12 @@ GDScript::~GDScript() { // Order matters since clearing the stack may already cause // the GDScriptFunctionState to be destroyed and thus removed from the list. pending_func_states.remove(E); - E->self()->_clear_stack(); + GDScriptFunctionState *state = E->self(); + ObjectID state_id = state->get_instance_id(); + state->_clear_connections(); + if (ObjectDB::get_instance(state_id)) { + state->_clear_stack(); + } } } @@ -1920,7 +1925,12 @@ GDScriptInstance::~GDScriptInstance() { // Order matters since clearing the stack may already cause // the GDSCriptFunctionState to be destroyed and thus removed from the list. pending_func_states.remove(E); - E->self()->_clear_stack(); + GDScriptFunctionState *state = E->self(); + ObjectID state_id = state->get_instance_id(); + state->_clear_connections(); + if (ObjectDB::get_instance(state_id)) { + state->_clear_stack(); + } } if (script.is_valid() && owner) { diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index 71831a3a97..a6b4dc7981 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -296,6 +296,15 @@ void GDScriptFunctionState::_clear_stack() { } } +void GDScriptFunctionState::_clear_connections() { + List<Object::Connection> conns; + get_signals_connected_to_this(&conns); + + for (Object::Connection &c : conns) { + c.signal.disconnect(c.callable); + } +} + void GDScriptFunctionState::_bind_methods() { ClassDB::bind_method(D_METHOD("resume", "arg"), &GDScriptFunctionState::resume, DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("is_valid", "extended_check"), &GDScriptFunctionState::is_valid, DEFVAL(false)); diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index 37416a734d..f45c1f9577 100644 --- a/modules/gdscript/gdscript_function.h +++ b/modules/gdscript/gdscript_function.h @@ -628,6 +628,7 @@ public: Variant resume(const Variant &p_arg = Variant()); void _clear_stack(); + void _clear_connections(); GDScriptFunctionState(); ~GDScriptFunctionState(); diff --git a/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs b/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs index 344b76a202..02d0226e90 100644 --- a/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs +++ b/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs @@ -21,6 +21,26 @@ namespace GodotPlugins _resolver = new AssemblyDependencyResolver(pluginPath); _sharedAssemblies = sharedAssemblies; _mainLoadContext = mainLoadContext; + + if (string.IsNullOrEmpty(AppContext.BaseDirectory)) + { + // See https://github.com/dotnet/runtime/blob/v6.0.0/src/libraries/System.Private.CoreLib/src/System/AppContext.AnyOS.cs#L17-L35 + // but Assembly.Location is unavailable, because we load assemblies from memory. + string? baseDirectory = Path.GetDirectoryName(pluginPath); + if (baseDirectory != null) + { + if (!Path.EndsInDirectorySeparator(baseDirectory)) + baseDirectory += Path.PathSeparator; + // This SetData call effectively sets AppContext.BaseDirectory + // See https://github.com/dotnet/runtime/blob/v6.0.0/src/libraries/System.Private.CoreLib/src/System/AppContext.cs#L21-L25 + AppDomain.CurrentDomain.SetData("APP_CONTEXT_BASE_DIRECTORY", baseDirectory); + } + else + { + // TODO: How to log from GodotPlugins? (delegate pointer?) + Console.Error.WriteLine("Failed to set AppContext.BaseDirectory. Dynamic loading of libraries may fail."); + } + } } protected override Assembly? Load(AssemblyName assemblyName) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index 1e23689c95..8ac18c4314 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -459,6 +459,10 @@ namespace Godot.NativeInterop public static partial godot_bool godotsharp_node_path_is_absolute(in godot_node_path p_self); + public static partial godot_bool godotsharp_node_path_equals(in godot_node_path p_self, in godot_node_path p_other); + + public static partial int godotsharp_node_path_hash(in godot_node_path p_self); + // GD, etc internal static partial void godotsharp_bytes_to_var(in godot_packed_byte_array p_bytes, diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs index b02bd167a1..f216fb7ea3 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs @@ -39,7 +39,7 @@ namespace Godot /// new NodePath("/root/MyAutoload"); // If you have an autoloaded node or scene. /// </code> /// </example> - public sealed class NodePath : IDisposable + public sealed class NodePath : IDisposable, IEquatable<NodePath> { internal godot_node_path.movable NativeValue; @@ -288,5 +288,37 @@ namespace Godot /// </summary> /// <returns>If the <see cref="NodePath"/> is empty.</returns> public bool IsEmpty => NativeValue.DangerousSelfRef.IsEmpty; + + public static bool operator ==(NodePath left, NodePath right) + { + if (left is null) + return right is null; + return left.Equals(right); + } + + public static bool operator !=(NodePath left, NodePath right) + { + return !(left == right); + } + + public bool Equals(NodePath other) + { + if (other is null) + return false; + var self = (godot_node_path)NativeValue; + var otherNative = (godot_node_path)other.NativeValue; + return NativeFuncs.godotsharp_node_path_equals(self, otherNative).ToBool(); + } + + public override bool Equals(object obj) + { + return ReferenceEquals(this, obj) || (obj is NodePath other && Equals(other)); + } + + public override int GetHashCode() + { + var self = (godot_node_path)NativeValue; + return NativeFuncs.godotsharp_node_path_hash(self); + } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs index b9ee0bc278..97d28f9ee9 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs @@ -10,7 +10,7 @@ namespace Godot /// Comparing them is much faster than with regular strings, because only the pointers are compared, /// not the whole strings. /// </summary> - public sealed class StringName : IDisposable + public sealed class StringName : IDisposable, IEquatable<StringName> { internal godot_string_name.movable NativeValue; diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp index d17fe3e75f..a90b07620c 100644 --- a/modules/mono/glue/runtime_interop.cpp +++ b/modules/mono/glue/runtime_interop.cpp @@ -1141,6 +1141,14 @@ bool godotsharp_node_path_is_absolute(const NodePath *p_self) { return p_self->is_absolute(); } +bool godotsharp_node_path_equals(const NodePath *p_self, const NodePath *p_other) { + return *p_self == *p_other; +} + +int godotsharp_node_path_hash(const NodePath *p_self) { + return p_self->hash(); +} + void godotsharp_randomize() { Math::randomize(); } @@ -1477,6 +1485,8 @@ static const void *unmanaged_callbacks[]{ (void *)godotsharp_node_path_get_subname, (void *)godotsharp_node_path_get_subname_count, (void *)godotsharp_node_path_is_absolute, + (void *)godotsharp_node_path_equals, + (void *)godotsharp_node_path_hash, (void *)godotsharp_bytes_to_var, (void *)godotsharp_convert, (void *)godotsharp_hash, diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp index ed6f3b8020..85f6840fde 100644 --- a/scene/2d/navigation_agent_2d.cpp +++ b/scene/2d/navigation_agent_2d.cpp @@ -168,6 +168,17 @@ void NavigationAgent2D::_notification(int p_what) { set_physics_process_internal(false); } break; + case NOTIFICATION_EXIT_TREE: { + set_agent_parent(nullptr); + set_physics_process_internal(false); + +#ifdef DEBUG_ENABLED + if (debug_path_instance.is_valid()) { + RenderingServer::get_singleton()->canvas_item_set_visible(debug_path_instance, false); + } +#endif // DEBUG_ENABLED + } break; + case NOTIFICATION_PAUSED: { if (agent_parent && !agent_parent->can_process()) { map_before_pause = NavigationServer2D::get_singleton()->agent_get_map(get_rid()); @@ -188,17 +199,6 @@ void NavigationAgent2D::_notification(int p_what) { } } break; - case NOTIFICATION_EXIT_TREE: { - agent_parent = nullptr; - set_physics_process_internal(false); - -#ifdef DEBUG_ENABLED - if (debug_path_instance.is_valid()) { - RenderingServer::get_singleton()->canvas_item_set_visible(debug_path_instance, false); - } -#endif // DEBUG_ENABLED - } break; - case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { if (agent_parent && target_position_submitted) { if (avoidance_enabled) { @@ -208,7 +208,6 @@ void NavigationAgent2D::_notification(int p_what) { } _check_distance_to_target(); } - #ifdef DEBUG_ENABLED if (debug_path_dirty) { _update_debug_path(); @@ -220,11 +219,11 @@ void NavigationAgent2D::_notification(int p_what) { NavigationAgent2D::NavigationAgent2D() { agent = NavigationServer2D::get_singleton()->agent_create(); - set_neighbor_distance(neighbor_distance); - set_max_neighbors(max_neighbors); - set_time_horizon(time_horizon); - set_radius(radius); - set_max_speed(max_speed); + NavigationServer2D::get_singleton()->agent_set_neighbor_distance(agent, neighbor_distance); + NavigationServer2D::get_singleton()->agent_set_max_neighbors(agent, max_neighbors); + NavigationServer2D::get_singleton()->agent_set_time_horizon(agent, time_horizon); + NavigationServer2D::get_singleton()->agent_set_radius(agent, radius); + NavigationServer2D::get_singleton()->agent_set_max_speed(agent, max_speed); // Preallocate query and result objects to improve performance. navigation_query = Ref<NavigationPathQueryParameters2D>(); @@ -254,7 +253,12 @@ NavigationAgent2D::~NavigationAgent2D() { } void NavigationAgent2D::set_avoidance_enabled(bool p_enabled) { + if (avoidance_enabled == p_enabled) { + return; + } + avoidance_enabled = p_enabled; + if (avoidance_enabled) { NavigationServer2D::get_singleton()->agent_set_callback(agent, callable_mp(this, &NavigationAgent2D::_avoidance_done)); } else { @@ -267,6 +271,10 @@ bool NavigationAgent2D::get_avoidance_enabled() const { } void NavigationAgent2D::set_agent_parent(Node *p_agent_parent) { + if (agent_parent == p_agent_parent) { + return; + } + // remove agent from any avoidance map before changing parent or there will be leftovers on the RVO map NavigationServer2D::get_singleton()->agent_set_callback(agent, Callable()); @@ -280,7 +288,9 @@ void NavigationAgent2D::set_agent_parent(Node *p_agent_parent) { } // create new avoidance callback if enabled - set_avoidance_enabled(avoidance_enabled); + if (avoidance_enabled) { + NavigationServer2D::get_singleton()->agent_set_callback(agent, callable_mp(this, &NavigationAgent2D::_avoidance_done)); + } } else { agent_parent = nullptr; NavigationServer2D::get_singleton()->agent_set_map(get_rid(), RID()); @@ -288,11 +298,13 @@ void NavigationAgent2D::set_agent_parent(Node *p_agent_parent) { } void NavigationAgent2D::set_navigation_layers(uint32_t p_navigation_layers) { - bool navigation_layers_changed = navigation_layers != p_navigation_layers; - navigation_layers = p_navigation_layers; - if (navigation_layers_changed) { - _request_repath(); + if (navigation_layers == p_navigation_layers) { + return; } + + navigation_layers = p_navigation_layers; + + _request_repath(); } uint32_t NavigationAgent2D::get_navigation_layers() const { @@ -326,7 +338,12 @@ void NavigationAgent2D::set_path_metadata_flags(BitField<NavigationPathQueryPara } void NavigationAgent2D::set_navigation_map(RID p_navigation_map) { + if (map_override == p_navigation_map) { + return; + } + map_override = p_navigation_map; + NavigationServer2D::get_singleton()->agent_set_map(agent, map_override); _request_repath(); } @@ -340,41 +357,78 @@ RID NavigationAgent2D::get_navigation_map() const { return RID(); } -void NavigationAgent2D::set_path_desired_distance(real_t p_dd) { - path_desired_distance = p_dd; +void NavigationAgent2D::set_path_desired_distance(real_t p_path_desired_distance) { + if (Math::is_equal_approx(path_desired_distance, p_path_desired_distance)) { + return; + } + + path_desired_distance = p_path_desired_distance; } -void NavigationAgent2D::set_target_desired_distance(real_t p_dd) { - target_desired_distance = p_dd; +void NavigationAgent2D::set_target_desired_distance(real_t p_target_desired_distance) { + if (Math::is_equal_approx(target_desired_distance, p_target_desired_distance)) { + return; + } + + target_desired_distance = p_target_desired_distance; } void NavigationAgent2D::set_radius(real_t p_radius) { + if (Math::is_equal_approx(radius, p_radius)) { + return; + } + radius = p_radius; + NavigationServer2D::get_singleton()->agent_set_radius(agent, radius); } void NavigationAgent2D::set_neighbor_distance(real_t p_distance) { + if (Math::is_equal_approx(neighbor_distance, p_distance)) { + return; + } + neighbor_distance = p_distance; + NavigationServer2D::get_singleton()->agent_set_neighbor_distance(agent, neighbor_distance); } void NavigationAgent2D::set_max_neighbors(int p_count) { + if (max_neighbors == p_count) { + return; + } + max_neighbors = p_count; + NavigationServer2D::get_singleton()->agent_set_max_neighbors(agent, max_neighbors); } void NavigationAgent2D::set_time_horizon(real_t p_time) { + if (Math::is_equal_approx(time_horizon, p_time)) { + return; + } + time_horizon = p_time; + NavigationServer2D::get_singleton()->agent_set_time_horizon(agent, time_horizon); } void NavigationAgent2D::set_max_speed(real_t p_max_speed) { + if (Math::is_equal_approx(max_speed, p_max_speed)) { + return; + } + max_speed = p_max_speed; + NavigationServer2D::get_singleton()->agent_set_max_speed(agent, max_speed); } -void NavigationAgent2D::set_path_max_distance(real_t p_pmd) { - path_max_distance = p_pmd; +void NavigationAgent2D::set_path_max_distance(real_t p_path_max_distance) { + if (Math::is_equal_approx(path_max_distance, p_path_max_distance)) { + return; + } + + path_max_distance = p_path_max_distance; } real_t NavigationAgent2D::get_path_max_distance() { @@ -382,8 +436,13 @@ real_t NavigationAgent2D::get_path_max_distance() { } void NavigationAgent2D::set_target_position(Vector2 p_position) { + if (target_position.is_equal_approx(p_position)) { + return; + } + target_position = p_position; target_position_submitted = true; + _request_repath(); } @@ -432,10 +491,15 @@ Vector2 NavigationAgent2D::get_final_position() { } void NavigationAgent2D::set_velocity(Vector2 p_velocity) { + if (target_velocity.is_equal_approx(p_velocity)) { + return; + } + target_velocity = p_velocity; + velocity_submitted = true; + NavigationServer2D::get_singleton()->agent_set_target_velocity(agent, target_velocity); NavigationServer2D::get_singleton()->agent_set_velocity(agent, prev_safe_velocity); - velocity_submitted = true; } void NavigationAgent2D::_avoidance_done(Vector3 p_new_velocity) { @@ -608,6 +672,10 @@ void NavigationAgent2D::_check_distance_to_target() { #ifdef DEBUG_ENABLED void NavigationAgent2D::set_debug_enabled(bool p_enabled) { + if (debug_enabled == p_enabled) { + return; + } + debug_enabled = p_enabled; debug_path_dirty = true; } @@ -617,6 +685,10 @@ bool NavigationAgent2D::get_debug_enabled() const { } void NavigationAgent2D::set_debug_use_custom(bool p_enabled) { + if (debug_use_custom == p_enabled) { + return; + } + debug_use_custom = p_enabled; debug_path_dirty = true; } @@ -626,6 +698,10 @@ bool NavigationAgent2D::get_debug_use_custom() const { } void NavigationAgent2D::set_debug_path_custom_color(Color p_color) { + if (debug_path_custom_color == p_color) { + return; + } + debug_path_custom_color = p_color; debug_path_dirty = true; } @@ -635,6 +711,10 @@ Color NavigationAgent2D::get_debug_path_custom_color() const { } void NavigationAgent2D::set_debug_path_custom_point_size(float p_point_size) { + if (Math::is_equal_approx(debug_path_custom_point_size, p_point_size)) { + return; + } + debug_path_custom_point_size = MAX(0.1, p_point_size); debug_path_dirty = true; } @@ -644,6 +724,10 @@ float NavigationAgent2D::get_debug_path_custom_point_size() const { } void NavigationAgent2D::set_debug_path_custom_line_width(float p_line_width) { + if (Math::is_equal_approx(debug_path_custom_line_width, p_line_width)) { + return; + } + debug_path_custom_line_width = p_line_width; debug_path_dirty = true; } diff --git a/scene/2d/navigation_agent_2d.h b/scene/2d/navigation_agent_2d.h index 8f4a373327..5278c81f66 100644 --- a/scene/2d/navigation_agent_2d.h +++ b/scene/2d/navigation_agent_2d.h @@ -57,7 +57,6 @@ class NavigationAgent2D : public Node { int max_neighbors = 10; real_t time_horizon = 1.0; real_t max_speed = 100.0; - real_t path_max_distance = 100.0; Vector2 target_position; diff --git a/scene/2d/navigation_obstacle_2d.cpp b/scene/2d/navigation_obstacle_2d.cpp index 4bd170301a..d7ef77e25b 100644 --- a/scene/2d/navigation_obstacle_2d.cpp +++ b/scene/2d/navigation_obstacle_2d.cpp @@ -185,6 +185,10 @@ real_t NavigationObstacle2D::estimate_agent_radius() const { } void NavigationObstacle2D::set_agent_parent(Node *p_agent_parent) { + if (parent_node2d == p_agent_parent) { + return; + } + if (Object::cast_to<Node2D>(p_agent_parent) != nullptr) { parent_node2d = Object::cast_to<Node2D>(p_agent_parent); if (map_override.is_valid()) { @@ -200,7 +204,12 @@ void NavigationObstacle2D::set_agent_parent(Node *p_agent_parent) { } void NavigationObstacle2D::set_navigation_map(RID p_navigation_map) { + if (map_override == p_navigation_map) { + return; + } + map_override = p_navigation_map; + NavigationServer2D::get_singleton()->agent_set_map(agent, map_override); } @@ -214,13 +223,23 @@ RID NavigationObstacle2D::get_navigation_map() const { } void NavigationObstacle2D::set_estimate_radius(bool p_estimate_radius) { + if (estimate_radius == p_estimate_radius) { + return; + } + estimate_radius = p_estimate_radius; + notify_property_list_changed(); reevaluate_agent_radius(); } void NavigationObstacle2D::set_radius(real_t p_radius) { ERR_FAIL_COND_MSG(p_radius <= 0.0, "Radius must be greater than 0."); + if (Math::is_equal_approx(radius, p_radius)) { + return; + } + radius = p_radius; + reevaluate_agent_radius(); } diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp index 1ffabbb6da..524304425c 100644 --- a/scene/3d/navigation_agent_3d.cpp +++ b/scene/3d/navigation_agent_3d.cpp @@ -207,7 +207,7 @@ void NavigationAgent3D::_notification(int p_what) { if (avoidance_enabled) { // agent_position on NavigationServer is avoidance only and has nothing to do with pathfinding // no point in flooding NavigationServer queue with agent position updates that get send to the void if avoidance is not used - NavigationServer3D::get_singleton()->agent_set_position(agent, agent_parent->get_global_transform().origin); + NavigationServer3D::get_singleton()->agent_set_position(agent, agent_parent->get_global_position()); } _check_distance_to_target(); } @@ -222,12 +222,12 @@ void NavigationAgent3D::_notification(int p_what) { NavigationAgent3D::NavigationAgent3D() { agent = NavigationServer3D::get_singleton()->agent_create(); - set_neighbor_distance(50.0); - set_max_neighbors(10); - set_time_horizon(5.0); - set_radius(1.0); - set_max_speed(10.0); - set_ignore_y(true); + NavigationServer3D::get_singleton()->agent_set_neighbor_distance(agent, neighbor_distance); + NavigationServer3D::get_singleton()->agent_set_max_neighbors(agent, max_neighbors); + NavigationServer3D::get_singleton()->agent_set_time_horizon(agent, time_horizon); + NavigationServer3D::get_singleton()->agent_set_radius(agent, radius); + NavigationServer3D::get_singleton()->agent_set_max_speed(agent, max_speed); + NavigationServer3D::get_singleton()->agent_set_ignore_y(agent, ignore_y); // Preallocate query and result objects to improve performance. navigation_query = Ref<NavigationPathQueryParameters3D>(); @@ -260,7 +260,12 @@ NavigationAgent3D::~NavigationAgent3D() { } void NavigationAgent3D::set_avoidance_enabled(bool p_enabled) { + if (avoidance_enabled == p_enabled) { + return; + } + avoidance_enabled = p_enabled; + if (avoidance_enabled) { NavigationServer3D::get_singleton()->agent_set_callback(agent, callable_mp(this, &NavigationAgent3D::_avoidance_done)); } else { @@ -273,6 +278,10 @@ bool NavigationAgent3D::get_avoidance_enabled() const { } void NavigationAgent3D::set_agent_parent(Node *p_agent_parent) { + if (agent_parent == p_agent_parent) { + return; + } + // remove agent from any avoidance map before changing parent or there will be leftovers on the RVO map NavigationServer3D::get_singleton()->agent_set_callback(agent, Callable()); @@ -286,7 +295,9 @@ void NavigationAgent3D::set_agent_parent(Node *p_agent_parent) { } // create new avoidance callback if enabled - set_avoidance_enabled(avoidance_enabled); + if (avoidance_enabled) { + NavigationServer3D::get_singleton()->agent_set_callback(agent, callable_mp(this, &NavigationAgent3D::_avoidance_done)); + } } else { agent_parent = nullptr; NavigationServer3D::get_singleton()->agent_set_map(get_rid(), RID()); @@ -294,11 +305,13 @@ void NavigationAgent3D::set_agent_parent(Node *p_agent_parent) { } void NavigationAgent3D::set_navigation_layers(uint32_t p_navigation_layers) { - bool navigation_layers_changed = navigation_layers != p_navigation_layers; - navigation_layers = p_navigation_layers; - if (navigation_layers_changed) { - _request_repath(); + if (navigation_layers == p_navigation_layers) { + return; } + + navigation_layers = p_navigation_layers; + + _request_repath(); } uint32_t NavigationAgent3D::get_navigation_layers() const { @@ -332,7 +345,12 @@ void NavigationAgent3D::set_path_metadata_flags(BitField<NavigationPathQueryPara } void NavigationAgent3D::set_navigation_map(RID p_navigation_map) { + if (map_override == p_navigation_map) { + return; + } + map_override = p_navigation_map; + NavigationServer3D::get_singleton()->agent_set_map(agent, map_override); _request_repath(); } @@ -346,50 +364,96 @@ RID NavigationAgent3D::get_navigation_map() const { return RID(); } -void NavigationAgent3D::set_path_desired_distance(real_t p_dd) { - path_desired_distance = p_dd; +void NavigationAgent3D::set_path_desired_distance(real_t p_path_desired_distance) { + if (Math::is_equal_approx(path_desired_distance, p_path_desired_distance)) { + return; + } + + path_desired_distance = p_path_desired_distance; } -void NavigationAgent3D::set_target_desired_distance(real_t p_dd) { - target_desired_distance = p_dd; +void NavigationAgent3D::set_target_desired_distance(real_t p_target_desired_distance) { + if (Math::is_equal_approx(target_desired_distance, p_target_desired_distance)) { + return; + } + + target_desired_distance = p_target_desired_distance; } void NavigationAgent3D::set_radius(real_t p_radius) { + if (Math::is_equal_approx(radius, p_radius)) { + return; + } + radius = p_radius; + NavigationServer3D::get_singleton()->agent_set_radius(agent, radius); } -void NavigationAgent3D::set_agent_height_offset(real_t p_hh) { - navigation_height_offset = p_hh; +void NavigationAgent3D::set_agent_height_offset(real_t p_agent_height_offset) { + if (Math::is_equal_approx(navigation_height_offset, p_agent_height_offset)) { + return; + } + + navigation_height_offset = p_agent_height_offset; } void NavigationAgent3D::set_ignore_y(bool p_ignore_y) { + if (ignore_y == p_ignore_y) { + return; + } + ignore_y = p_ignore_y; + NavigationServer3D::get_singleton()->agent_set_ignore_y(agent, ignore_y); } void NavigationAgent3D::set_neighbor_distance(real_t p_distance) { + if (Math::is_equal_approx(neighbor_distance, p_distance)) { + return; + } + neighbor_distance = p_distance; + NavigationServer3D::get_singleton()->agent_set_neighbor_distance(agent, neighbor_distance); } void NavigationAgent3D::set_max_neighbors(int p_count) { + if (max_neighbors == p_count) { + return; + } + max_neighbors = p_count; + NavigationServer3D::get_singleton()->agent_set_max_neighbors(agent, max_neighbors); } void NavigationAgent3D::set_time_horizon(real_t p_time) { + if (Math::is_equal_approx(time_horizon, p_time)) { + return; + } + time_horizon = p_time; + NavigationServer3D::get_singleton()->agent_set_time_horizon(agent, time_horizon); } void NavigationAgent3D::set_max_speed(real_t p_max_speed) { + if (Math::is_equal_approx(max_speed, p_max_speed)) { + return; + } + max_speed = p_max_speed; + NavigationServer3D::get_singleton()->agent_set_max_speed(agent, max_speed); } -void NavigationAgent3D::set_path_max_distance(real_t p_pmd) { - path_max_distance = p_pmd; +void NavigationAgent3D::set_path_max_distance(real_t p_path_max_distance) { + if (Math::is_equal_approx(path_max_distance, p_path_max_distance)) { + return; + } + + path_max_distance = p_path_max_distance; } real_t NavigationAgent3D::get_path_max_distance() { @@ -397,8 +461,13 @@ real_t NavigationAgent3D::get_path_max_distance() { } void NavigationAgent3D::set_target_position(Vector3 p_position) { + if (target_position.is_equal_approx(p_position)) { + return; + } + target_position = p_position; target_position_submitted = true; + _request_repath(); } @@ -412,7 +481,7 @@ Vector3 NavigationAgent3D::get_next_path_position() { const Vector<Vector3> &navigation_path = navigation_result->get_path(); if (navigation_path.size() == 0) { ERR_FAIL_COND_V_MSG(agent_parent == nullptr, Vector3(), "The agent has no parent."); - return agent_parent->get_global_transform().origin; + return agent_parent->get_global_position(); } else { return navigation_path[navigation_path_index] - Vector3(0, navigation_height_offset, 0); } @@ -420,7 +489,7 @@ Vector3 NavigationAgent3D::get_next_path_position() { real_t NavigationAgent3D::distance_to_target() const { ERR_FAIL_COND_V_MSG(agent_parent == nullptr, 0.0, "The agent has no parent."); - return agent_parent->get_global_transform().origin.distance_to(target_position); + return agent_parent->get_global_position().distance_to(target_position); } bool NavigationAgent3D::is_target_reached() const { @@ -447,10 +516,15 @@ Vector3 NavigationAgent3D::get_final_position() { } void NavigationAgent3D::set_velocity(Vector3 p_velocity) { + if (target_velocity.is_equal_approx(p_velocity)) { + return; + } + target_velocity = p_velocity; + velocity_submitted = true; + NavigationServer3D::get_singleton()->agent_set_target_velocity(agent, target_velocity); NavigationServer3D::get_singleton()->agent_set_velocity(agent, prev_safe_velocity); - velocity_submitted = true; } void NavigationAgent3D::_avoidance_done(Vector3 p_new_velocity) { @@ -491,7 +565,7 @@ void NavigationAgent3D::update_navigation() { update_frame_id = Engine::get_singleton()->get_physics_frames(); - Vector3 origin = agent_parent->get_global_transform().origin; + Vector3 origin = agent_parent->get_global_position(); bool reload_path = false; @@ -624,6 +698,10 @@ void NavigationAgent3D::_check_distance_to_target() { #ifdef DEBUG_ENABLED void NavigationAgent3D::set_debug_enabled(bool p_enabled) { + if (debug_enabled == p_enabled) { + return; + } + debug_enabled = p_enabled; debug_path_dirty = true; } @@ -633,6 +711,10 @@ bool NavigationAgent3D::get_debug_enabled() const { } void NavigationAgent3D::set_debug_use_custom(bool p_enabled) { + if (debug_use_custom == p_enabled) { + return; + } + debug_use_custom = p_enabled; debug_path_dirty = true; } @@ -642,6 +724,10 @@ bool NavigationAgent3D::get_debug_use_custom() const { } void NavigationAgent3D::set_debug_path_custom_color(Color p_color) { + if (debug_path_custom_color == p_color) { + return; + } + debug_path_custom_color = p_color; debug_path_dirty = true; } @@ -651,6 +737,10 @@ Color NavigationAgent3D::get_debug_path_custom_color() const { } void NavigationAgent3D::set_debug_path_custom_point_size(float p_point_size) { + if (Math::is_equal_approx(debug_path_custom_point_size, p_point_size)) { + return; + } + debug_path_custom_point_size = p_point_size; debug_path_dirty = true; } diff --git a/scene/3d/navigation_agent_3d.h b/scene/3d/navigation_agent_3d.h index 98bf395d7c..209b2a0989 100644 --- a/scene/3d/navigation_agent_3d.h +++ b/scene/3d/navigation_agent_3d.h @@ -52,14 +52,13 @@ class NavigationAgent3D : public Node { real_t path_desired_distance = 1.0; real_t target_desired_distance = 1.0; - real_t radius = 0.0; + real_t radius = 1.0; real_t navigation_height_offset = 0.0; - bool ignore_y = false; - real_t neighbor_distance = 0.0; - int max_neighbors = 0; - real_t time_horizon = 0.0; - real_t max_speed = 0.0; - + bool ignore_y = true; + real_t neighbor_distance = 50.0; + int max_neighbors = 10; + real_t time_horizon = 5.0; + real_t max_speed = 10.0; real_t path_max_distance = 3.0; Vector3 target_position; diff --git a/scene/3d/navigation_obstacle_3d.cpp b/scene/3d/navigation_obstacle_3d.cpp index c706f55566..85b3c164cc 100644 --- a/scene/3d/navigation_obstacle_3d.cpp +++ b/scene/3d/navigation_obstacle_3d.cpp @@ -192,6 +192,10 @@ real_t NavigationObstacle3D::estimate_agent_radius() const { } void NavigationObstacle3D::set_agent_parent(Node *p_agent_parent) { + if (parent_node3d == p_agent_parent) { + return; + } + if (Object::cast_to<Node3D>(p_agent_parent) != nullptr) { parent_node3d = Object::cast_to<Node3D>(p_agent_parent); if (map_override.is_valid()) { @@ -207,7 +211,12 @@ void NavigationObstacle3D::set_agent_parent(Node *p_agent_parent) { } void NavigationObstacle3D::set_navigation_map(RID p_navigation_map) { + if (map_override == p_navigation_map) { + return; + } + map_override = p_navigation_map; + NavigationServer3D::get_singleton()->agent_set_map(agent, map_override); } @@ -221,13 +230,23 @@ RID NavigationObstacle3D::get_navigation_map() const { } void NavigationObstacle3D::set_estimate_radius(bool p_estimate_radius) { + if (estimate_radius == p_estimate_radius) { + return; + } + estimate_radius = p_estimate_radius; + notify_property_list_changed(); reevaluate_agent_radius(); } void NavigationObstacle3D::set_radius(real_t p_radius) { ERR_FAIL_COND_MSG(p_radius <= 0.0, "Radius must be greater than 0."); + if (Math::is_equal_approx(radius, p_radius)) { + return; + } + radius = p_radius; + reevaluate_agent_radius(); } diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 3051502dd0..f9c9906efa 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -2031,7 +2031,7 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { } } if (b->get_button_index() == MouseButton::RIGHT && context_menu_enabled) { - _generate_context_menu(); + _update_context_menu(); menu->set_position(get_screen_position() + b->get_position()); menu->reset_size(); menu->popup(); @@ -2090,7 +2090,7 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { } if (k->is_action("ui_menu", true)) { if (context_menu_enabled) { - _generate_context_menu(); + _update_context_menu(); menu->set_position(get_screen_position()); menu->reset_size(); menu->popup(); @@ -4992,7 +4992,9 @@ bool RichTextLabel::is_shortcut_keys_enabled() const { // Context menu. PopupMenu *RichTextLabel::get_menu() const { - const_cast<RichTextLabel *>(this)->_generate_context_menu(); + if (!menu) { + const_cast<RichTextLabel *>(this)->_generate_context_menu(); + } return menu; } @@ -5466,6 +5468,7 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("get_menu"), &RichTextLabel::get_menu); ClassDB::bind_method(D_METHOD("is_menu_visible"), &RichTextLabel::is_menu_visible); + ClassDB::bind_method(D_METHOD("menu_option", "option"), &RichTextLabel::menu_option); ClassDB::bind_method(D_METHOD("_thread_end"), &RichTextLabel::_thread_end); @@ -5544,6 +5547,10 @@ void RichTextLabel::_bind_methods() { BIND_ENUM_CONSTANT(ITEM_HINT); BIND_ENUM_CONSTANT(ITEM_DROPCAP); BIND_ENUM_CONSTANT(ITEM_CUSTOMFX); + + BIND_ENUM_CONSTANT(MENU_COPY); + BIND_ENUM_CONSTANT(MENU_SELECT_ALL); + BIND_ENUM_CONSTANT(MENU_MAX); } TextServer::VisibleCharactersBehavior RichTextLabel::get_visible_characters_behavior() const { @@ -5675,19 +5682,32 @@ Size2 RichTextLabel::get_minimum_size() const { // Context menu. void RichTextLabel::_generate_context_menu() { - if (!menu) { - menu = memnew(PopupMenu); - add_child(menu, false, INTERNAL_MODE_FRONT); + menu = memnew(PopupMenu); + add_child(menu, false, INTERNAL_MODE_FRONT); + menu->connect("id_pressed", callable_mp(this, &RichTextLabel::menu_option)); + + menu->add_item(RTR("Copy"), MENU_COPY); + menu->add_item(RTR("Select All"), MENU_SELECT_ALL); +} - menu->connect("id_pressed", callable_mp(this, &RichTextLabel::_menu_option)); +void RichTextLabel::_update_context_menu() { + if (!menu) { + _generate_context_menu(); } - // Reorganize context menu. - menu->clear(); - if (selection.enabled) { - menu->add_item(RTR("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_copy") : Key::NONE); - menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_text_select_all") : Key::NONE); + int idx = -1; + +#define MENU_ITEM_ACTION_DISABLED(m_menu, m_id, m_action, m_disabled) \ + idx = m_menu->get_item_index(m_id); \ + if (idx >= 0) { \ + m_menu->set_item_accelerator(idx, shortcut_keys_enabled ? _get_menu_action_accelerator(m_action) : Key::NONE); \ + m_menu->set_item_disabled(idx, m_disabled); \ } + + MENU_ITEM_ACTION_DISABLED(menu, MENU_COPY, "ui_copy", !selection.enabled) + MENU_ITEM_ACTION_DISABLED(menu, MENU_SELECT_ALL, "ui_text_select_all", !selection.enabled) + +#undef MENU_ITEM_ACTION_DISABLED } Key RichTextLabel::_get_menu_action_accelerator(const String &p_action) { @@ -5715,7 +5735,7 @@ Key RichTextLabel::_get_menu_action_accelerator(const String &p_action) { } } -void RichTextLabel::_menu_option(int p_option) { +void RichTextLabel::menu_option(int p_option) { switch (p_option) { case MENU_COPY: { selection_copy(); diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index fcbb91f67e..b01fccf14c 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -81,6 +81,7 @@ public: enum MenuItems { MENU_COPY, MENU_SELECT_ALL, + MENU_MAX }; enum DefaultFont { @@ -454,8 +455,8 @@ private: // Context menu. PopupMenu *menu = nullptr; void _generate_context_menu(); + void _update_context_menu(); Key _get_menu_action_accelerator(const String &p_action); - void _menu_option(int p_option); int visible_characters = -1; float visible_ratio = 1.0; @@ -688,6 +689,7 @@ public: // Context menu. PopupMenu *get_menu() const; bool is_menu_visible() const; + void menu_option(int p_option); void parse_bbcode(const String &p_bbcode); void append_text(const String &p_bbcode); @@ -739,5 +741,6 @@ public: VARIANT_ENUM_CAST(RichTextLabel::ListType); VARIANT_ENUM_CAST(RichTextLabel::ItemType); +VARIANT_ENUM_CAST(RichTextLabel::MenuItems); #endif // RICH_TEXT_LABEL_H diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp index ead9550b93..0c0125df76 100644 --- a/scene/gui/split_container.cpp +++ b/scene/gui/split_container.cpp @@ -115,7 +115,7 @@ void SplitContainerDragger::_notification(int p_what) { return; } - Ref<Texture2D> tex = sc->get_theme_icon(SNAME("grabber")); + Ref<Texture2D> tex = sc->_get_grabber_icon(); draw_texture(tex, (get_size() - tex->get_size()) / 2); } break; } diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 19d16ae27e..28521c5bbe 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1062,6 +1062,10 @@ void Viewport::assign_next_enabled_camera_2d(const StringName &p_camera_group) { Camera2D *new_camera = nullptr; for (Node *E : camera_list) { Camera2D *cam = Object::cast_to<Camera2D>(E); + if (!cam) { + continue; // Non-camera node (e.g. ParallaxBackground). + } + if (cam->is_enabled()) { new_camera = cam; break; diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 4132972cb3..3a6d40d22c 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -815,6 +815,8 @@ void VisualShader::remove_node(Type p_type, int p_id) { if (E->get().from_node == p_id) { g->nodes[E->get().to_node].prev_connected_nodes.erase(p_id); g->nodes[E->get().to_node].node->set_input_port_connected(E->get().to_port, false); + } else if (E->get().to_node == p_id) { + g->nodes[E->get().from_node].next_connected_nodes.erase(p_id); } } E = N; @@ -981,6 +983,7 @@ void VisualShader::connect_nodes_forced(Type p_type, int p_from_node, int p_from c.to_node = p_to_node; c.to_port = p_to_port; g->connections.push_back(c); + g->nodes[p_from_node].next_connected_nodes.push_back(p_to_node); g->nodes[p_to_node].prev_connected_nodes.push_back(p_from_node); g->nodes[p_from_node].node->set_output_port_connected(p_from_port, true); g->nodes[p_to_node].node->set_input_port_connected(p_to_port, true); @@ -1014,6 +1017,7 @@ Error VisualShader::connect_nodes(Type p_type, int p_from_node, int p_from_port, c.to_node = p_to_node; c.to_port = p_to_port; g->connections.push_back(c); + g->nodes[p_from_node].next_connected_nodes.push_back(p_to_node); g->nodes[p_to_node].prev_connected_nodes.push_back(p_from_node); g->nodes[p_from_node].node->set_output_port_connected(p_from_port, true); g->nodes[p_to_node].node->set_input_port_connected(p_to_port, true); @@ -1029,6 +1033,7 @@ void VisualShader::disconnect_nodes(Type p_type, int p_from_node, int p_from_por for (const List<Connection>::Element *E = g->connections.front(); E; E = E->next()) { if (E->get().from_node == p_from_node && E->get().from_port == p_from_port && E->get().to_node == p_to_node && E->get().to_port == p_to_port) { g->connections.erase(E); + g->nodes[p_from_node].next_connected_nodes.erase(p_to_node); g->nodes[p_to_node].prev_connected_nodes.erase(p_from_node); g->nodes[p_from_node].node->set_output_port_connected(p_from_port, false); g->nodes[p_to_node].node->set_input_port_connected(p_to_port, false); diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index fc5e48410b..2838a49209 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -122,7 +122,8 @@ private: struct Node { Ref<VisualShaderNode> node; Vector2 position; - List<int> prev_connected_nodes; + LocalVector<int> prev_connected_nodes; + LocalVector<int> next_connected_nodes; }; struct Graph { @@ -199,6 +200,16 @@ public: // internal methods Vector2 get_node_position(Type p_type, int p_id) const; Ref<VisualShaderNode> get_node(Type p_type, int p_id) const; + _FORCE_INLINE_ Ref<VisualShaderNode> get_node_unchecked(Type p_type, int p_id) const { + return graph[p_type].nodes[p_id].node; + } + _FORCE_INLINE_ void get_next_connected_nodes(Type p_type, int p_id, LocalVector<int> &r_list) const { + r_list = graph[p_type].nodes[p_id].next_connected_nodes; + } + _FORCE_INLINE_ void get_prev_connected_nodes(Type p_type, int p_id, LocalVector<int> &r_list) const { + r_list = graph[p_type].nodes[p_id].prev_connected_nodes; + } + Vector<int> get_node_list(Type p_type) const; int get_valid_node_id(Type p_type) const; |