diff options
102 files changed, 2889 insertions, 231 deletions
diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml index 341d70d0e7..8c716242e4 100644 --- a/.github/workflows/linux_builds.yml +++ b/.github/workflows/linux_builds.yml @@ -19,30 +19,15 @@ jobs: fail-fast: false matrix: include: -# Temporarily disabled until Mono is fixed -# -# - name: Editor w Mono (target=release_debug, tools=yes, tests=yes) -# cache-name: linux-editor-mono -# target: release_debug -# tools: true -# tests: false # Disabled due freeze caused by mix Mono build and CI -# sconsflags: module_mono_enabled=yes mono_static=yes mono_glue=no -# doc-test: true -# bin: "./bin/godot.linuxbsd.opt.tools.64.mono" -# build-mono: true -# proj-conv: true -# artifact: true - -# Temporary replacement: - - - name: Editor w/o Mono (target=release_debug, tools=yes, tests=yes) + - name: Editor w Mono (target=release_debug, tools=yes, tests=yes) cache-name: linux-editor-mono target: release_debug tools: true tests: false # Disabled due freeze caused by mix Mono build and CI + sconsflags: module_mono_enabled=yes mono_static=yes mono_glue=no doc-test: true - bin: "./bin/godot.linuxbsd.opt.tools.64" - build-mono: false + bin: "./bin/godot.linuxbsd.opt.tools.64.mono" + build-mono: true proj-conv: true artifact: true @@ -72,24 +57,12 @@ jobs: # Skip 2GiB artifact speeding up action. artifact: false -# Temporarily disabled: -# -# - name: Template w/ Mono (target=release, tools=no) -# cache-name: linux-template-mono -# target: release -# tools: false -# tests: false -# sconsflags: module_mono_enabled=yes mono_static=yes mono_glue=no debug_symbols=no -# build-mono: false -# artifact: true - -# Temporary replacement: - - - name: Template w/o Mono (target=release, tools=no) + - name: Template w/ Mono (target=release, tools=no) cache-name: linux-template-mono target: release tools: false tests: false + sconsflags: module_mono_enabled=yes mono_static=yes mono_glue=no debug_symbols=no build-mono: false artifact: true diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 3852f77bfc..3f94ff8329 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -46,8 +46,8 @@ namespace core_bind { ResourceLoader *ResourceLoader::singleton = nullptr; -Error ResourceLoader::load_threaded_request(const String &p_path, const String &p_type_hint, bool p_use_sub_threads) { - return ::ResourceLoader::load_threaded_request(p_path, p_type_hint, p_use_sub_threads); +Error ResourceLoader::load_threaded_request(const String &p_path, const String &p_type_hint, bool p_use_sub_threads, CacheMode p_cache_mode) { + return ::ResourceLoader::load_threaded_request(p_path, p_type_hint, p_use_sub_threads, ResourceFormatLoader::CacheMode(p_cache_mode)); } ResourceLoader::ThreadLoadStatus ResourceLoader::load_threaded_get_status(const String &p_path, Array r_progress) { @@ -121,7 +121,7 @@ ResourceUID::ID ResourceLoader::get_resource_uid(const String &p_path) { } void ResourceLoader::_bind_methods() { - ClassDB::bind_method(D_METHOD("load_threaded_request", "path", "type_hint", "use_sub_threads"), &ResourceLoader::load_threaded_request, DEFVAL(""), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("load_threaded_request", "path", "type_hint", "use_sub_threads", "cache_mode"), &ResourceLoader::load_threaded_request, DEFVAL(""), DEFVAL(false), DEFVAL(CACHE_MODE_REUSE)); ClassDB::bind_method(D_METHOD("load_threaded_get_status", "path", "progress"), &ResourceLoader::load_threaded_get_status, DEFVAL(Array())); ClassDB::bind_method(D_METHOD("load_threaded_get", "path"), &ResourceLoader::load_threaded_get); diff --git a/core/core_bind.h b/core/core_bind.h index 068d2a6dc3..3a4faa3422 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -71,7 +71,7 @@ public: static ResourceLoader *get_singleton() { return singleton; } - Error load_threaded_request(const String &p_path, const String &p_type_hint = "", bool p_use_sub_threads = false); + Error load_threaded_request(const String &p_path, const String &p_type_hint = "", bool p_use_sub_threads = false, CacheMode p_cache_mode = CACHE_MODE_REUSE); ThreadLoadStatus load_threaded_get_status(const String &p_path, Array r_progress = Array()); Ref<Resource> load_threaded_get(const String &p_path); diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml index a92b237624..f40f11944d 100644 --- a/doc/classes/Animation.xml +++ b/doc/classes/Animation.xml @@ -391,13 +391,13 @@ </description> </method> <method name="track_insert_key"> - <return type="void" /> + <return type="int" /> <argument index="0" name="track_idx" type="int" /> <argument index="1" name="time" type="float" /> <argument index="2" name="key" type="Variant" /> <argument index="3" name="transition" type="float" default="1" /> <description> - Insert a generic key in a given track. + Inserts a generic key in a given track. Returns the key index. </description> </method> <method name="track_is_compressed" qualifiers="const"> diff --git a/doc/classes/EditorProperty.xml b/doc/classes/EditorProperty.xml index 84f8523da3..586458bd28 100644 --- a/doc/classes/EditorProperty.xml +++ b/doc/classes/EditorProperty.xml @@ -101,6 +101,13 @@ Used by sub-inspectors. Emit it if what was selected was an Object ID. </description> </signal> + <signal name="property_can_revert_changed"> + <argument index="0" name="property" type="StringName" /> + <argument index="1" name="can_revert" type="bool" /> + <description> + Emitted when the revertability (i.e., whether it has a non-default value and thus is displayed with a revert icon) of a property has changed. + </description> + </signal> <signal name="property_changed"> <argument index="0" name="property" type="StringName" /> <argument index="1" name="value" type="Variant" /> diff --git a/doc/classes/GraphEdit.xml b/doc/classes/GraphEdit.xml index 965dbe7449..33145dccd0 100644 --- a/doc/classes/GraphEdit.xml +++ b/doc/classes/GraphEdit.xml @@ -55,6 +55,29 @@ [/codeblock] </description> </method> + <method name="_is_node_hover_valid" qualifiers="virtual"> + <return type="bool" /> + <argument index="0" name="from" type="StringName" /> + <argument index="1" name="from_slot" type="int" /> + <argument index="2" name="to" type="StringName" /> + <argument index="3" name="to_slot" type="int" /> + <description> + This virtual method can be used to insert additional error detection while the user is dragging a connection over a valid port. + Return [code]true[/code] if the connection is indeed valid or return [code]false[/code] if the connection is impossible. If the connection is impossible, no snapping to the port and thus no connection request to that port will happen. + In this example a connection to same node is suppressed: + [codeblocks] + [gdscript] + func _is_node_hover_valid(from, from_slot, to, to_slot): + return from != to + [/gdscript] + [csharp] + public override bool _IsNodeHoverValid(String from, int fromSlot, String to, int toSlot) { + return from != to; + } + [/csharp] + [/codeblocks] + </description> + </method> <method name="add_valid_connection_type"> <return type="void" /> <argument index="0" name="from_type" type="int" /> diff --git a/doc/classes/ResourceLoader.xml b/doc/classes/ResourceLoader.xml index dd52d09750..729058c9b3 100644 --- a/doc/classes/ResourceLoader.xml +++ b/doc/classes/ResourceLoader.xml @@ -95,8 +95,10 @@ <argument index="0" name="path" type="String" /> <argument index="1" name="type_hint" type="String" default="""" /> <argument index="2" name="use_sub_threads" type="bool" default="false" /> + <argument index="3" name="cache_mode" type="int" enum="ResourceLoader.CacheMode" default="1" /> <description> Loads the resource using threads. If [code]use_sub_threads[/code] is [code]true[/code], multiple threads will be used to load the resource, which makes loading faster, but may affect the main thread (and thus cause game slowdowns). + The [code]cache_mode[/code] property defines whether and how the cache should be used or updated when loading the resource. See [enum CacheMode] for details. </description> </method> <method name="remove_resource_format_loader"> diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index 18ce2ded63..9e693eaffd 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -33,7 +33,6 @@ #include "editor/editor_data.h" #include "editor/editor_spin_slider.h" -#include "editor/property_editor.h" #include "editor/property_selector.h" #include "scene/gui/control.h" diff --git a/editor/debugger/debug_adapter/debug_adapter_protocol.cpp b/editor/debugger/debug_adapter/debug_adapter_protocol.cpp index 92ea0f15e9..4c445eb766 100644 --- a/editor/debugger/debug_adapter/debug_adapter_protocol.cpp +++ b/editor/debugger/debug_adapter/debug_adapter_protocol.cpp @@ -37,6 +37,7 @@ #include "editor/doc_tools.h" #include "editor/editor_log.h" #include "editor/editor_node.h" +#include "editor/editor_settings.h" DebugAdapterProtocol *DebugAdapterProtocol::singleton = nullptr; diff --git a/editor/debugger/debug_adapter/debug_adapter_server.cpp b/editor/debugger/debug_adapter/debug_adapter_server.cpp index e9fc7ec913..41e6b1f308 100644 --- a/editor/debugger/debug_adapter/debug_adapter_server.cpp +++ b/editor/debugger/debug_adapter/debug_adapter_server.cpp @@ -33,6 +33,7 @@ #include "core/os/os.h" #include "editor/editor_log.h" #include "editor/editor_node.h" +#include "editor/editor_settings.h" DebugAdapterServer::DebugAdapterServer() { _EDITOR_DEF("network/debug_adapter/remote_port", remote_port); diff --git a/editor/debugger/editor_debugger_tree.cpp b/editor/debugger/editor_debugger_tree.cpp index bdab1cfecb..dbd2c61d44 100644 --- a/editor/debugger/editor_debugger_tree.cpp +++ b/editor/debugger/editor_debugger_tree.cpp @@ -225,6 +225,39 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int updating_scene_tree = false; } +Variant EditorDebuggerTree::get_drag_data(const Point2 &p_point) { + if (get_button_id_at_position(p_point) != -1) { + return Variant(); + } + + TreeItem *selected = get_selected(); + if (!selected) { + return Variant(); + } + + String path = selected->get_text(0); + + HBoxContainer *hb = memnew(HBoxContainer); + TextureRect *tf = memnew(TextureRect); + tf->set_texture(selected->get_icon(0)); + tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED); + hb->add_child(tf); + Label *label = memnew(Label(path)); + hb->add_child(label); + set_drag_preview(hb); + + if (!selected->get_parent() || !selected->get_parent()->get_parent()) { + path = "."; + } else { + while (selected->get_parent()->get_parent() != get_root()) { + selected = selected->get_parent(); + path = selected->get_text(0) + "/" + path; + } + } + + return vformat("\"%s\"", path); +} + String EditorDebuggerTree::get_selected_path() { if (!get_selected()) { return ""; diff --git a/editor/debugger/editor_debugger_tree.h b/editor/debugger/editor_debugger_tree.h index 9d163fd548..5b2df8abd5 100644 --- a/editor/debugger/editor_debugger_tree.h +++ b/editor/debugger/editor_debugger_tree.h @@ -65,6 +65,8 @@ protected: void _notification(int p_what); public: + virtual Variant get_drag_data(const Point2 &p_point) override; + String get_selected_path(); ObjectID get_selected_object(); int get_current_debugger(); // Would love to have one tree for every debugger. diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index 41095a7267..ac2e958c5b 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -50,7 +50,6 @@ #include "editor/plugins/canvas_item_editor_plugin.h" #include "editor/plugins/editor_debugger_plugin.h" #include "editor/plugins/node_3d_editor_plugin.h" -#include "editor/property_editor.h" #include "main/performance.h" #include "scene/3d/camera_3d.h" #include "scene/debugger/scene_debugger.h" diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp index 4f89e1b2d1..5b4da0e32b 100644 --- a/editor/dependency_editor.cpp +++ b/editor/dependency_editor.cpp @@ -37,6 +37,7 @@ #include "editor/editor_file_system.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_settings.h" #include "scene/gui/margin_container.h" void DependencyEditor::_searched(const String &p_path) { diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp index 556a0176cb..b6d7bbc45f 100644 --- a/editor/editor_audio_buses.cpp +++ b/editor/editor_audio_buses.cpp @@ -37,6 +37,7 @@ #include "editor/editor_file_dialog.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_settings.h" #include "filesystem_dock.h" #include "scene/resources/font.h" #include "servers/audio_server.h" diff --git a/editor/editor_command_palette.cpp b/editor/editor_command_palette.cpp index 174a97d471..ba1f2fd6af 100644 --- a/editor/editor_command_palette.cpp +++ b/editor/editor_command_palette.cpp @@ -32,6 +32,7 @@ #include "core/os/keyboard.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_settings.h" #include "scene/gui/control.h" #include "scene/gui/tree.h" diff --git a/editor/editor_help_search.cpp b/editor/editor_help_search.cpp index 424195e4dd..297d1226e3 100644 --- a/editor/editor_help_search.cpp +++ b/editor/editor_help_search.cpp @@ -34,6 +34,7 @@ #include "editor/editor_feature_profile.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_settings.h" void EditorHelpSearch::_update_icons() { search_box->set_right_icon(results_tree->get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 133a4a3398..e06e3cbc5f 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -471,6 +471,9 @@ void EditorProperty::update_revert_and_pin_status() { bool new_can_revert = EditorPropertyRevert::can_property_revert(object, property, ¤t) && !is_read_only(); if (new_can_revert != can_revert || new_pinned != pinned) { + if (new_can_revert != can_revert) { + emit_signal(SNAME("property_can_revert_changed"), property, new_can_revert); + } can_revert = new_can_revert; pinned = new_pinned; update(); @@ -784,6 +787,9 @@ void EditorProperty::expand_all_folding() { void EditorProperty::collapse_all_folding() { } +void EditorProperty::expand_revertable() { +} + void EditorProperty::set_selectable(bool p_selectable) { selectable = p_selectable; } @@ -966,6 +972,7 @@ void EditorProperty::_bind_methods() { ADD_SIGNAL(MethodInfo("property_keyed_with_value", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT))); ADD_SIGNAL(MethodInfo("property_checked", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::BOOL, "checked"))); ADD_SIGNAL(MethodInfo("property_pinned", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::BOOL, "pinned"))); + ADD_SIGNAL(MethodInfo("property_can_revert_changed", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::BOOL, "can_revert"))); ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"))); ADD_SIGNAL(MethodInfo("object_id_selected", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::INT, "id"))); ADD_SIGNAL(MethodInfo("selected", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::INT, "focusable_idx"))); @@ -1225,12 +1232,15 @@ void EditorInspectorSection::_notification(int p_what) { // Get the section header font. Ref<Font> font = get_theme_font(SNAME("bold"), SNAME("EditorFonts")); int font_size = get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts")); + Color font_color = get_theme_color(SNAME("font_color"), SNAME("Editor")); // Get the right direction arrow texture, if the section is foldable. Ref<Texture2D> arrow; + bool folded = foldable; if (foldable) { if (object->editor_is_section_unfolded(section)) { arrow = get_theme_icon(SNAME("arrow"), SNAME("Tree")); + folded = false; } else { if (is_layout_rtl()) { arrow = get_theme_icon(SNAME("arrow_collapsed_mirrored"), SNAME("Tree")); @@ -1274,28 +1284,71 @@ void EditorInspectorSection::_notification(int p_what) { } draw_rect(header_rect, c); - // Draw header title and folding arrow. - const int arrow_margin = 2; - const int arrow_width = arrow.is_valid() ? arrow->get_width() : 0; - Color color = get_theme_color(SNAME("font_color")); - float text_width = get_size().width - Math::round(arrow_width + arrow_margin * EDSCALE) - section_indent; - Point2 text_offset = Point2(0, font->get_ascent(font_size) + (header_height - font->get_height(font_size)) / 2); - HorizontalAlignment text_align = HORIZONTAL_ALIGNMENT_LEFT; - if (rtl) { - text_align = HORIZONTAL_ALIGNMENT_RIGHT; - } else { - text_offset.x = section_indent + Math::round(arrow_width + arrow_margin * EDSCALE); - } - draw_string(font, text_offset.floor(), label, text_align, text_width, font_size, color); + // Draw header title, folding arrow and coutn of revertable properties. + { + int separation = Math::round(2 * EDSCALE); - if (arrow.is_valid()) { - Point2 arrow_position = Point2(0, (header_height - arrow->get_height()) / 2); + int margin_start = section_indent + separation; + int margin_end = separation; + + // - Arrow. + if (arrow.is_valid()) { + Point2 arrow_position; + if (rtl) { + arrow_position.x = get_size().width - (margin_start + arrow->get_width()); + } else { + arrow_position.x = margin_start; + } + arrow_position.y = (header_height - arrow->get_height()) / 2; + draw_texture(arrow, arrow_position); + margin_start += arrow->get_width(); + } + + int available = get_size().width - (margin_start + margin_end); + + // - Count of revertable properties. + String num_revertable_str; + int num_revertable_width = 0; + if (folded && revertable_properties.size()) { + int label_width = font->get_string_size(label, HORIZONTAL_ALIGNMENT_LEFT, available, font_size, TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_CONSTRAIN_ELLIPSIS).x; + + Ref<Font> light_font = get_theme_font(SNAME("main"), SNAME("EditorFonts")); + int light_font_size = get_theme_font_size(SNAME("main_size"), SNAME("EditorFonts")); + Color light_font_color = get_theme_color(SNAME("disabled_font_color"), SNAME("Editor")); + + // Can we fit the long version of the revertable count text? + if (revertable_properties.size() == 1) { + num_revertable_str = "(1 change)"; + } else { + num_revertable_str = vformat("(%d changes)", revertable_properties.size()); + } + num_revertable_width = light_font->get_string_size(num_revertable_str, HORIZONTAL_ALIGNMENT_LEFT, -1.0f, light_font_size, TextServer::JUSTIFICATION_NONE).x; + if (label_width + separation + num_revertable_width > available) { + // We'll have to use the short version. + num_revertable_str = vformat("(%d)", revertable_properties.size()); + num_revertable_width = light_font->get_string_size(num_revertable_str, HORIZONTAL_ALIGNMENT_LEFT, -1.0f, light_font_size, TextServer::JUSTIFICATION_NONE).x; + } + + Point2 text_offset = Point2( + margin_end, + light_font->get_ascent(light_font_size) + (header_height - light_font->get_height(light_font_size)) / 2); + if (!rtl) { + text_offset.x = get_size().width - (text_offset.x + num_revertable_width); + } + draw_string(light_font, text_offset, num_revertable_str, HORIZONTAL_ALIGNMENT_LEFT, -1.0f, light_font_size, light_font_color, TextServer::JUSTIFICATION_NONE); + margin_end += num_revertable_width + separation; + available -= num_revertable_width + separation; + } + + // - Label. + Point2 text_offset = Point2( + margin_start, + font->get_ascent(font_size) + (header_height - font->get_height(font_size)) / 2); if (rtl) { - arrow_position.x = get_size().width - section_indent - arrow->get_width() - Math::round(arrow_margin * EDSCALE); - } else { - arrow_position.x = section_indent + Math::round(arrow_margin * EDSCALE); + text_offset.x = margin_end; } - draw_texture(arrow, arrow_position.floor()); + HorizontalAlignment text_align = rtl ? HORIZONTAL_ALIGNMENT_RIGHT : HORIZONTAL_ALIGNMENT_LEFT; + draw_string(font, text_offset, label, text_align, available, font_size, font_color, TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_CONSTRAIN_ELLIPSIS); } // Draw dropping highlight. @@ -1471,6 +1524,22 @@ void EditorInspectorSection::fold() { update(); } +bool EditorInspectorSection::has_revertable_properties() const { + return !revertable_properties.is_empty(); +} + +void EditorInspectorSection::property_can_revert_changed(const String &p_path, bool p_can_revert) { + bool had_revertable_properties = has_revertable_properties(); + if (p_can_revert) { + revertable_properties.insert(p_path); + } else { + revertable_properties.erase(p_path); + } + if (has_revertable_properties() != had_revertable_properties) { + update(); + } +} + void EditorInspectorSection::_bind_methods() { ClassDB::bind_method(D_METHOD("setup", "section", "label", "object", "bg_color", "foldable"), &EditorInspectorSection::setup); ClassDB::bind_method(D_METHOD("get_vbox"), &EditorInspectorSection::get_vbox); @@ -2330,7 +2399,7 @@ String EditorInspector::get_selected_path() const { return property_selected; } -void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, Ref<EditorInspectorPlugin> ped) { +void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, EditorInspectorSection *p_section, Ref<EditorInspectorPlugin> ped) { for (const EditorInspectorPlugin::AddedEditor &F : ped->added_editors) { EditorProperty *ep = Object::cast_to<EditorProperty>(F.property_editor); current_vbox->add_child(F.property_editor); @@ -2370,6 +2439,10 @@ void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, Ref<Edit } } + if (p_section) { + ep->connect("property_can_revert_changed", callable_mp(p_section, &EditorInspectorSection::property_can_revert_changed)); + } + ep->set_read_only(read_only); ep->update_property(); ep->_update_pin_flags(); @@ -2476,7 +2549,7 @@ void EditorInspector::update_tree() { // Get the lists of editors to add the beginning. for (Ref<EditorInspectorPlugin> &ped : valid_plugins) { ped->parse_begin(object); - _parse_added_editors(main_vbox, ped); + _parse_added_editors(main_vbox, nullptr, ped); } StringName type_name; @@ -2601,7 +2674,7 @@ void EditorInspector::update_tree() { // Add editors at the start of a category. for (Ref<EditorInspectorPlugin> &ped : valid_plugins) { ped->parse_category(object, p.name); - _parse_added_editors(main_vbox, ped); + _parse_added_editors(main_vbox, nullptr, ped); } continue; @@ -2793,7 +2866,7 @@ void EditorInspector::update_tree() { // Add editors at the start of a group. for (Ref<EditorInspectorPlugin> &ped : valid_plugins) { ped->parse_group(object, path); - _parse_added_editors(section->get_vbox(), ped); + _parse_added_editors(section->get_vbox(), section, ped); } vbox_per_path[root_vbox][acc_path] = section->get_vbox(); @@ -2975,6 +3048,12 @@ void EditorInspector::update_tree() { editor_property_map[prop].push_back(ep); } } + + EditorInspectorSection *section = Object::cast_to<EditorInspectorSection>(current_vbox->get_parent()); + if (section) { + ep->connect("property_can_revert_changed", callable_mp(section, &EditorInspectorSection::property_can_revert_changed)); + } + ep->set_draw_warning(draw_warning); ep->set_use_folding(use_folding); ep->set_checkable(checkable); @@ -3027,7 +3106,7 @@ void EditorInspector::update_tree() { // Get the lists of to add at the end. for (Ref<EditorInspectorPlugin> &ped : valid_plugins) { ped->parse_end(object); - _parse_added_editors(main_vbox, ped); + _parse_added_editors(main_vbox, nullptr, ped); } } @@ -3180,6 +3259,44 @@ void EditorInspector::expand_all_folding() { } } +void EditorInspector::expand_revertable() { + HashSet<EditorInspectorSection *> sections_to_unfold[2]; + for (EditorInspectorSection *E : sections) { + if (E->has_revertable_properties()) { + sections_to_unfold[0].insert(E); + } + } + + // Climb up the hierachy doing double buffering with the sets. + int a = 0; + int b = 1; + while (sections_to_unfold[a].size()) { + for (EditorInspectorSection *E : sections_to_unfold[a]) { + E->unfold(); + + Node *n = E->get_parent(); + while (n) { + if (Object::cast_to<EditorInspector>(n)) { + break; + } + if (Object::cast_to<EditorInspectorSection>(n) && !sections_to_unfold[a].has((EditorInspectorSection *)n)) { + sections_to_unfold[b].insert((EditorInspectorSection *)n); + } + n = n->get_parent(); + } + } + + sections_to_unfold[a].clear(); + SWAP(a, b); + } + + for (const KeyValue<StringName, List<EditorProperty *>> &F : editor_property_map) { + for (EditorProperty *E : F.value) { + E->expand_revertable(); + } + } +} + void EditorInspector::set_scroll_offset(int p_offset) { set_v_scroll(p_offset); } diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index 4ec3513da5..54533de960 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -184,6 +184,7 @@ public: virtual void expand_all_folding(); virtual void collapse_all_folding(); + virtual void expand_revertable(); virtual Variant get_drag_data(const Point2 &p_point) override; virtual void update_cache(); @@ -281,6 +282,8 @@ class EditorInspectorSection : public Container { Timer *dropping_unfold_timer = nullptr; bool dropping = false; + HashSet<StringName> revertable_properties; + void _test_unfold(); protected: @@ -299,6 +302,9 @@ public: void unfold(); void fold(); + bool has_revertable_properties() const; + void property_can_revert_changed(const String &p_path, bool p_can_revert); + EditorInspectorSection(); ~EditorInspectorSection(); }; @@ -517,7 +523,7 @@ class EditorInspector : public ScrollContainer { void _edit_request_change(Object *p_object, const String &p_prop); void _filter_changed(const String &p_text); - void _parse_added_editors(VBoxContainer *current_vbox, Ref<EditorInspectorPlugin> ped); + void _parse_added_editors(VBoxContainer *current_vbox, EditorInspectorSection *p_section, Ref<EditorInspectorPlugin> ped); void _vscroll_changed(double); @@ -579,6 +585,7 @@ public: void collapse_all_folding(); void expand_all_folding(); + void expand_revertable(); void set_scroll_offset(int p_offset); int get_scroll_offset() const; diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp index 012666d625..38cc85bb4e 100644 --- a/editor/editor_log.cpp +++ b/editor/editor_log.cpp @@ -35,7 +35,9 @@ #include "editor/editor_node.h" #include "editor/editor_paths.h" #include "editor/editor_scale.h" +#include "editor/editor_settings.h" #include "scene/gui/center_container.h" +#include "scene/gui/separator.h" #include "scene/resources/font.h" void EditorLog::_error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, bool p_editor_notify, ErrorHandlerType p_type) { diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 8b540f9787..e9aceb684f 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -49,6 +49,7 @@ #include "main/main.h" #include "scene/3d/importer_mesh_instance_3d.h" #include "scene/gui/center_container.h" +#include "scene/gui/color_picker.h" #include "scene/gui/control.h" #include "scene/gui/dialogs.h" #include "scene/gui/file_dialog.h" @@ -149,6 +150,7 @@ #include "editor/plugins/debugger_editor_plugin.h" #include "editor/plugins/editor_debugger_plugin.h" #include "editor/plugins/editor_preview_plugins.h" +#include "editor/plugins/editor_resource_conversion_plugin.h" #include "editor/plugins/font_config_plugin.h" #include "editor/plugins/gdextension_export_plugin.h" #include "editor/plugins/gpu_particles_2d_editor_plugin.h" diff --git a/editor/editor_node.h b/editor/editor_node.h index c521c0fb04..0201e84eaf 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -37,7 +37,6 @@ #include "editor/editor_run.h" #include "editor/export/editor_export.h" #include "editor/inspector_dock.h" -#include "editor/property_editor.h" typedef void (*EditorNodeInitCallback)(); typedef void (*EditorPluginInitializeCallback)(); @@ -48,6 +47,7 @@ class AudioStreamPreviewGenerator; class BackgroundProgress; class Button; class CenterContainer; +class ColorPicker; class ConfirmationDialog; class Control; class DependencyEditor; @@ -67,6 +67,7 @@ class EditorPlugin; class EditorPluginList; class EditorQuickOpen; class EditorResourcePreview; +class EditorResourceConversionPlugin; class EditorRun; class EditorRunNative; class EditorSettingsDialog; diff --git a/editor/editor_plugin_settings.cpp b/editor/editor_plugin_settings.cpp index 3a0b875b8c..5a010a66c1 100644 --- a/editor/editor_plugin_settings.cpp +++ b/editor/editor_plugin_settings.cpp @@ -37,6 +37,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "scene/gui/margin_container.h" +#include "scene/gui/tree.h" void EditorPluginSettings::_notification(int p_what) { switch (p_what) { diff --git a/editor/editor_plugin_settings.h b/editor/editor_plugin_settings.h index 7c6d93e6f4..9c619066f2 100644 --- a/editor/editor_plugin_settings.h +++ b/editor/editor_plugin_settings.h @@ -34,8 +34,8 @@ #include "core/object/undo_redo.h" #include "editor/editor_data.h" #include "editor/plugin_config_dialog.h" -#include "property_editor.h" -#include "scene/gui/dialogs.h" + +class Tree; class EditorPluginSettings : public VBoxContainer { GDCLASS(EditorPluginSettings, VBoxContainer); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 919443eeb8..09c55d5d24 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -3918,6 +3918,12 @@ void EditorPropertyResource::expand_all_folding() { } } +void EditorPropertyResource::expand_revertable() { + if (sub_inspector) { + sub_inspector->expand_revertable(); + } +} + void EditorPropertyResource::set_use_sub_inspector(bool p_enable) { use_sub_inspector = p_enable; } diff --git a/editor/editor_properties.h b/editor/editor_properties.h index b3aac6e8ca..cfe0c2f38c 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -856,6 +856,7 @@ public: void collapse_all_folding() override; void expand_all_folding() override; + void expand_revertable() override; void set_use_sub_inspector(bool p_enable); diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp index 60c091a4b0..d850773a6d 100644 --- a/editor/editor_resource_picker.cpp +++ b/editor/editor_resource_picker.cpp @@ -38,6 +38,7 @@ #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "editor/filesystem_dock.h" +#include "editor/plugins/editor_resource_conversion_plugin.h" #include "editor/plugins/script_editor_plugin.h" #include "editor/scene_tree_dock.h" diff --git a/editor/editor_run_native.cpp b/editor/editor_run_native.cpp index ec5edab860..d8b738be17 100644 --- a/editor/editor_run_native.cpp +++ b/editor/editor_run_native.cpp @@ -32,6 +32,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_settings.h" #include "editor/export/editor_export_platform.h" void EditorRunNative::_notification(int p_what) { diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 5d60baf202..da7105c94c 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -1669,17 +1669,14 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { const Color alpha2 = Color(mono_value, mono_value, mono_value, 0.14); const Color alpha3 = Color(mono_value, mono_value, mono_value, 0.27); - // editor main color - const Color main_color = dark_theme ? Color(0.34, 0.7, 1.0) : Color(0.02, 0.5, 1.0); - - const Color symbol_color = Color(0.34, 0.57, 1.0).lerp(mono_color, dark_theme ? 0.5 : 0.3); - const Color keyword_color = Color(1.0, 0.44, 0.52); - const Color control_flow_keyword_color = dark_theme ? Color(1.0, 0.55, 0.8) : Color(0.8, 0.4, 0.6); - const Color basetype_color = dark_theme ? Color(0.26, 1.0, 0.76) : Color(0.0, 0.76, 0.38); - const Color type_color = basetype_color.lerp(mono_color, dark_theme ? 0.4 : 0.3); - const Color usertype_color = basetype_color.lerp(mono_color, dark_theme ? 0.7 : 0.5); - const Color comment_color = dim_color; - const Color string_color = (dark_theme ? Color(1.0, 0.85, 0.26) : Color(1.0, 0.82, 0.09)).lerp(mono_color, dark_theme ? 0.5 : 0.3); + const Color symbol_color = dark_theme ? Color(0.67, 0.79, 1) : Color(0, 0, 0.61); + const Color keyword_color = dark_theme ? Color(1.0, 0.44, 0.52) : Color(0.9, 0.135, 0.51); + const Color control_flow_keyword_color = dark_theme ? Color(1.0, 0.55, 0.8) : Color(0.743, 0.12, 0.8); + const Color base_type_color = dark_theme ? Color(0.26, 1.0, 0.76) : Color(0, 0.6, 0.2); + const Color engine_type_color = dark_theme ? Color(0.56, 1, 0.86) : Color(0.11, 0.55, 0.4); + const Color user_type_color = dark_theme ? Color(0.78, 1, 0.93) : Color(0.18, 0.45, 0.4); + const Color comment_color = dark_theme ? dim_color : Color(0.08, 0.08, 0.08, 0.5); + const Color string_color = dark_theme ? Color(1, 0.93, 0.63) : Color(0.6, 0.42, 0); // Use the brightest background color on a light theme (which generally uses a negative contrast rate). const Color te_background_color = dark_theme ? background_color : dark_color_3; @@ -1692,24 +1689,24 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { const Color completion_font_color = font_color; const Color text_color = font_color; const Color line_number_color = dim_color; - const Color safe_line_number_color = dim_color * Color(1, 1.2, 1, 1.5); + const Color safe_line_number_color = dark_theme ? (dim_color * Color(1, 1.2, 1, 1.5)) : Color(0, 0.4, 0, 0.75); const Color caret_color = mono_color; const Color caret_background_color = mono_color.inverted(); const Color text_selected_color = dark_color_3; - const Color brace_mismatch_color = error_color; + const Color brace_mismatch_color = dark_theme ? error_color : Color(1, 0.08, 0, 1); const Color current_line_color = alpha1; const Color line_length_guideline_color = dark_theme ? base_color : background_color; const Color word_highlighted_color = alpha1; - const Color number_color = basetype_color.lerp(mono_color, dark_theme ? 0.5 : 0.3); - const Color function_color = main_color; - const Color member_variable_color = main_color.lerp(mono_color, 0.6); + const Color number_color = dark_theme ? Color(0.63, 1, 0.88) : Color(0, 0.55, 0.28, 1); + const Color function_color = dark_theme ? Color(0.34, 0.7, 1.0) : Color(0, 0.225, 0.9, 1); + const Color member_variable_color = dark_theme ? Color(0.34, 0.7, 1.0).lerp(mono_color, 0.6) : Color(0, 0.4, 0.68, 1); const Color mark_color = Color(error_color.r, error_color.g, error_color.b, 0.3); const Color bookmark_color = Color(0.08, 0.49, 0.98); - const Color breakpoint_color = error_color; + const Color breakpoint_color = dark_theme ? error_color : Color(1, 0.27, 0.2, 1); const Color executing_line_color = Color(0.98, 0.89, 0.27); const Color code_folding_color = alpha3; const Color search_result_color = alpha1; - const Color search_result_border_color = Color(0.41, 0.61, 0.91, 0.38); + const Color search_result_border_color = dark_theme ? Color(0.41, 0.61, 0.91, 0.38) : Color(0, 0.4, 1, 0.38); EditorSettings *setting = EditorSettings::get_singleton(); String text_editor_color_theme = setting->get("text_editor/theme/color_theme"); @@ -1717,9 +1714,9 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { setting->set_initial_value("text_editor/theme/highlighting/symbol_color", symbol_color, true); setting->set_initial_value("text_editor/theme/highlighting/keyword_color", keyword_color, true); setting->set_initial_value("text_editor/theme/highlighting/control_flow_keyword_color", control_flow_keyword_color, true); - setting->set_initial_value("text_editor/theme/highlighting/base_type_color", basetype_color, true); - setting->set_initial_value("text_editor/theme/highlighting/engine_type_color", type_color, true); - setting->set_initial_value("text_editor/theme/highlighting/user_type_color", usertype_color, true); + setting->set_initial_value("text_editor/theme/highlighting/base_type_color", base_type_color, true); + setting->set_initial_value("text_editor/theme/highlighting/engine_type_color", engine_type_color, true); + setting->set_initial_value("text_editor/theme/highlighting/user_type_color", user_type_color, true); setting->set_initial_value("text_editor/theme/highlighting/comment_color", comment_color, true); setting->set_initial_value("text_editor/theme/highlighting/string_color", string_color, true); setting->set_initial_value("text_editor/theme/highlighting/background_color", te_background_color, true); diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp index 57ae83aeee..34b407779e 100644 --- a/editor/export/editor_export_platform.cpp +++ b/editor/export/editor_export_platform.cpp @@ -41,6 +41,7 @@ #include "editor/editor_node.h" #include "editor/editor_paths.h" #include "editor/editor_scale.h" +#include "editor/editor_settings.h" #include "editor/plugins/script_editor_plugin.h" #include "editor_export_plugin.h" diff --git a/editor/export/export_template_manager.cpp b/editor/export/export_template_manager.cpp index 7d20081217..a7d9d7f068 100644 --- a/editor/export/export_template_manager.cpp +++ b/editor/export/export_template_manager.cpp @@ -37,8 +37,10 @@ #include "editor/editor_node.h" #include "editor/editor_paths.h" #include "editor/editor_scale.h" +#include "editor/editor_settings.h" #include "editor/progress_dialog.h" #include "scene/gui/file_dialog.h" +#include "scene/gui/separator.h" #include "scene/gui/tree.h" #include "scene/main/http_request.h" diff --git a/editor/import/scene_import_settings.cpp b/editor/import/scene_import_settings.cpp index 410f01a9c6..6c12464b5a 100644 --- a/editor/import/scene_import_settings.cpp +++ b/editor/import/scene_import_settings.cpp @@ -35,6 +35,7 @@ #include "editor/editor_inspector.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_settings.h" #include "scene/3d/importer_mesh_instance_3d.h" #include "scene/animation/animation_player.h" #include "scene/resources/importer_mesh.h" diff --git a/editor/import_defaults_editor.cpp b/editor/import_defaults_editor.cpp index 9d96822aef..a70f5225e9 100644 --- a/editor/import_defaults_editor.cpp +++ b/editor/import_defaults_editor.cpp @@ -36,6 +36,7 @@ #include "editor/editor_autoload_settings.h" #include "editor/editor_plugin_settings.h" #include "editor/editor_sectioned_inspector.h" +#include "editor/editor_settings.h" #include "editor/localization_editor.h" #include "editor/shader_globals_editor.h" #include "scene/gui/center_container.h" diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp index f9e5885f9d..087ef48b56 100644 --- a/editor/import_dock.cpp +++ b/editor/import_dock.cpp @@ -34,6 +34,7 @@ #include "editor/editor_node.h" #include "editor/editor_resource_preview.h" #include "editor/editor_scale.h" +#include "editor/editor_settings.h" class ImportDockParameters : public Object { GDCLASS(ImportDockParameters, Object); diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp index e0dd5610ee..79d94246ad 100644 --- a/editor/inspector_dock.cpp +++ b/editor/inspector_dock.cpp @@ -33,6 +33,7 @@ #include "editor/editor_file_dialog.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_settings.h" #include "editor/plugins/script_editor_plugin.h" InspectorDock *InspectorDock::singleton = nullptr; @@ -64,6 +65,9 @@ void InspectorDock::_menu_option_confirm(int p_option, bool p_confirmed) { case COLLAPSE_ALL: { _menu_collapseall(); } break; + case EXPAND_REVERTABLE: { + _menu_expand_revertable(); + } break; case RESOURCE_SAVE: { _save_resource(false); @@ -400,6 +404,10 @@ void InspectorDock::_menu_expandall() { inspector->expand_all_folding(); } +void InspectorDock::_menu_expand_revertable() { + inspector->expand_revertable(); +} + void InspectorDock::_warning_pressed() { warning_dialog->popup_centered(); } @@ -518,6 +526,8 @@ void InspectorDock::update(Object *p_object) { p->clear(); p->add_icon_shortcut(get_theme_icon(SNAME("GuiTreeArrowDown"), SNAME("EditorIcons")), ED_SHORTCUT("property_editor/expand_all", TTR("Expand All")), EXPAND_ALL); p->add_icon_shortcut(get_theme_icon(SNAME("GuiTreeArrowRight"), SNAME("EditorIcons")), ED_SHORTCUT("property_editor/collapse_all", TTR("Collapse All")), COLLAPSE_ALL); + // Calling it 'revertable' internally, because that's what the implementation is based on, but labeling it as 'non-default' because that's more user friendly, even if not 100% accurate. + p->add_shortcut(ED_SHORTCUT("property_editor/expand_revertable", TTR("Expand Non-Default")), EXPAND_REVERTABLE); p->add_separator(TTR("Property Name Style")); p->add_radio_check_item(TTR("Raw"), PROPERTY_NAME_STYLE_RAW); diff --git a/editor/inspector_dock.h b/editor/inspector_dock.h index e3d35c2157..e32410151f 100644 --- a/editor/inspector_dock.h +++ b/editor/inspector_dock.h @@ -61,6 +61,7 @@ class InspectorDock : public VBoxContainer { COLLAPSE_ALL, EXPAND_ALL, + EXPAND_REVERTABLE, // Matches `EditorPropertyNameProcessor::Style`. PROPERTY_NAME_STYLE_RAW, @@ -124,6 +125,7 @@ class InspectorDock : public VBoxContainer { void _edit_back(); void _menu_collapseall(); void _menu_expandall(); + void _menu_expand_revertable(); void _select_history(int p_idx); void _prepare_history(); diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp index 9d80e7a193..cb77cd78bf 100644 --- a/editor/plugins/abstract_polygon_2d_editor.cpp +++ b/editor/plugins/abstract_polygon_2d_editor.cpp @@ -35,6 +35,8 @@ #include "core/os/keyboard.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_settings.h" +#include "scene/gui/separator.h" bool AbstractPolygon2DEditor::Vertex::operator==(const AbstractPolygon2DEditor::Vertex &p_vertex) const { return polygon == p_vertex.polygon && vertex == p_vertex.vertex; diff --git a/editor/plugins/animation_blend_space_1d_editor.cpp b/editor/plugins/animation_blend_space_1d_editor.cpp index 7481882cbe..32d97c65a9 100644 --- a/editor/plugins/animation_blend_space_1d_editor.cpp +++ b/editor/plugins/animation_blend_space_1d_editor.cpp @@ -34,6 +34,7 @@ #include "editor/editor_file_dialog.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_settings.h" #include "scene/animation/animation_blend_tree.h" StringName AnimationNodeBlendSpace1DEditor::get_blend_position_path() const { diff --git a/editor/plugins/animation_blend_space_1d_editor.h b/editor/plugins/animation_blend_space_1d_editor.h index 3488b4bf30..9b06f3248f 100644 --- a/editor/plugins/animation_blend_space_1d_editor.h +++ b/editor/plugins/animation_blend_space_1d_editor.h @@ -33,11 +33,11 @@ #include "editor/editor_plugin.h" #include "editor/plugins/animation_tree_editor_plugin.h" -#include "editor/property_editor.h" #include "scene/animation/animation_blend_space_1d.h" #include "scene/gui/button.h" #include "scene/gui/graph_edit.h" #include "scene/gui/popup.h" +#include "scene/gui/separator.h" #include "scene/gui/tree.h" class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin { diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp index 14d009f03b..dc764725dd 100644 --- a/editor/plugins/animation_blend_space_2d_editor.cpp +++ b/editor/plugins/animation_blend_space_2d_editor.cpp @@ -38,6 +38,7 @@ #include "editor/editor_file_dialog.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_settings.h" #include "scene/animation/animation_blend_tree.h" #include "scene/animation/animation_player.h" #include "scene/gui/menu_button.h" diff --git a/editor/plugins/animation_blend_space_2d_editor.h b/editor/plugins/animation_blend_space_2d_editor.h index 88b9072599..26471df051 100644 --- a/editor/plugins/animation_blend_space_2d_editor.h +++ b/editor/plugins/animation_blend_space_2d_editor.h @@ -33,11 +33,11 @@ #include "editor/editor_plugin.h" #include "editor/plugins/animation_tree_editor_plugin.h" -#include "editor/property_editor.h" #include "scene/animation/animation_blend_space_2d.h" #include "scene/gui/button.h" #include "scene/gui/graph_edit.h" #include "scene/gui/popup.h" +#include "scene/gui/separator.h" #include "scene/gui/tree.h" class AnimationNodeBlendSpace2DEditor : public AnimationTreeNodeEditorPlugin { diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index 561651e5f5..79be2d04b3 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -38,6 +38,7 @@ #include "editor/editor_inspector.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_settings.h" #include "scene/animation/animation_player.h" #include "scene/gui/menu_button.h" #include "scene/gui/panel.h" diff --git a/editor/plugins/animation_blend_tree_editor_plugin.h b/editor/plugins/animation_blend_tree_editor_plugin.h index b5bf91a1da..18199676b8 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.h +++ b/editor/plugins/animation_blend_tree_editor_plugin.h @@ -33,7 +33,6 @@ #include "editor/editor_plugin.h" #include "editor/plugins/animation_tree_editor_plugin.h" -#include "editor/property_editor.h" #include "scene/animation/animation_blend_tree.h" #include "scene/gui/button.h" #include "scene/gui/graph_edit.h" diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp index fe5a772b0d..473450b292 100644 --- a/editor/plugins/animation_state_machine_editor.cpp +++ b/editor/plugins/animation_state_machine_editor.cpp @@ -38,6 +38,7 @@ #include "editor/editor_file_dialog.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_settings.h" #include "scene/animation/animation_blend_tree.h" #include "scene/animation/animation_player.h" #include "scene/gui/menu_button.h" diff --git a/editor/plugins/animation_state_machine_editor.h b/editor/plugins/animation_state_machine_editor.h index ea16abd64c..165940e639 100644 --- a/editor/plugins/animation_state_machine_editor.h +++ b/editor/plugins/animation_state_machine_editor.h @@ -33,7 +33,6 @@ #include "editor/editor_plugin.h" #include "editor/plugins/animation_tree_editor_plugin.h" -#include "editor/property_editor.h" #include "scene/animation/animation_node_state_machine.h" #include "scene/gui/button.h" #include "scene/gui/graph_edit.h" diff --git a/editor/plugins/animation_tree_editor_plugin.h b/editor/plugins/animation_tree_editor_plugin.h index ab4ef5a001..a33d97f62f 100644 --- a/editor/plugins/animation_tree_editor_plugin.h +++ b/editor/plugins/animation_tree_editor_plugin.h @@ -32,7 +32,6 @@ #define ANIMATION_TREE_EDITOR_PLUGIN_H #include "editor/editor_plugin.h" -#include "editor/property_editor.h" #include "scene/animation/animation_tree.h" #include "scene/gui/button.h" #include "scene/gui/graph_edit.h" diff --git a/editor/plugins/control_editor_plugin.cpp b/editor/plugins/control_editor_plugin.cpp index ec038174fc..2756e45cf4 100644 --- a/editor/plugins/control_editor_plugin.cpp +++ b/editor/plugins/control_editor_plugin.cpp @@ -31,7 +31,9 @@ #include "control_editor_plugin.h" #include "editor/editor_node.h" +#include "editor/editor_settings.h" #include "editor/plugins/canvas_item_editor_plugin.h" +#include "scene/gui/separator.h" void ControlPositioningWarning::_update_warning() { if (!control_node) { diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp index 2954071054..8aeab684e3 100644 --- a/editor/plugins/curve_editor_plugin.cpp +++ b/editor/plugins/curve_editor_plugin.cpp @@ -36,6 +36,7 @@ #include "core/os/keyboard.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_settings.h" CurveEditor::CurveEditor() { _selected_point = -1; diff --git a/editor/plugins/debugger_editor_plugin.cpp b/editor/plugins/debugger_editor_plugin.cpp index 5c90d70982..c572b5b7fe 100644 --- a/editor/plugins/debugger_editor_plugin.cpp +++ b/editor/plugins/debugger_editor_plugin.cpp @@ -35,6 +35,7 @@ #include "editor/debugger/editor_debugger_server.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_settings.h" #include "editor/fileserver/editor_file_server.h" #include "editor/plugins/script_editor_plugin.h" #include "scene/gui/menu_button.h" diff --git a/editor/plugins/editor_resource_conversion_plugin.cpp b/editor/plugins/editor_resource_conversion_plugin.cpp new file mode 100644 index 0000000000..91394dbac7 --- /dev/null +++ b/editor/plugins/editor_resource_conversion_plugin.cpp @@ -0,0 +1,64 @@ +/*************************************************************************/ +/* editor_resource_conversion_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "editor_resource_conversion_plugin.h" + +void EditorResourceConversionPlugin::_bind_methods() { + GDVIRTUAL_BIND(_converts_to); + GDVIRTUAL_BIND(_handles, "resource"); + GDVIRTUAL_BIND(_convert, "resource"); +} + +String EditorResourceConversionPlugin::converts_to() const { + String ret; + if (GDVIRTUAL_CALL(_converts_to, ret)) { + return ret; + } + + return ""; +} + +bool EditorResourceConversionPlugin::handles(const Ref<Resource> &p_resource) const { + bool ret; + if (GDVIRTUAL_CALL(_handles, p_resource, ret)) { + return ret; + } + + return false; +} + +Ref<Resource> EditorResourceConversionPlugin::convert(const Ref<Resource> &p_resource) const { + Ref<Resource> ret; + if (GDVIRTUAL_CALL(_convert, p_resource, ret)) { + return ret; + } + + return Ref<Resource>(); +} diff --git a/editor/plugins/editor_resource_conversion_plugin.h b/editor/plugins/editor_resource_conversion_plugin.h new file mode 100644 index 0000000000..34b0837383 --- /dev/null +++ b/editor/plugins/editor_resource_conversion_plugin.h @@ -0,0 +1,54 @@ +/*************************************************************************/ +/* editor_resource_conversion_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef EDITOR_RESOURCE_CONVERSION_PLUGIN_H +#define EDITOR_RESOURCE_CONVERSION_PLUGIN_H + +#include "core/io/resource.h" +#include "core/object/gdvirtual.gen.inc" +#include "core/object/script_language.h" + +class EditorResourceConversionPlugin : public RefCounted { + GDCLASS(EditorResourceConversionPlugin, RefCounted); + +protected: + static void _bind_methods(); + + GDVIRTUAL0RC(String, _converts_to) + GDVIRTUAL1RC(bool, _handles, Ref<Resource>) + GDVIRTUAL1RC(Ref<Resource>, _convert, Ref<Resource>) + +public: + virtual String converts_to() const; + virtual bool handles(const Ref<Resource> &p_resource) const; + virtual Ref<Resource> convert(const Ref<Resource> &p_resource) const; +}; + +#endif // EDITOR_RESOURCE_CONVERSION_PLUGIN_H diff --git a/editor/plugins/gradient_editor_plugin.cpp b/editor/plugins/gradient_editor_plugin.cpp index 4cdca95fca..542aee879b 100644 --- a/editor/plugins/gradient_editor_plugin.cpp +++ b/editor/plugins/gradient_editor_plugin.cpp @@ -33,6 +33,7 @@ #include "canvas_item_editor_plugin.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_settings.h" #include "node_3d_editor_plugin.h" Size2 GradientEditor::get_minimum_size() const { diff --git a/editor/plugins/material_editor_plugin.cpp b/editor/plugins/material_editor_plugin.cpp index 4f8f6568d1..00fc8cc7d6 100644 --- a/editor/plugins/material_editor_plugin.cpp +++ b/editor/plugins/material_editor_plugin.cpp @@ -32,6 +32,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_settings.h" #include "scene/gui/subviewport_container.h" #include "scene/resources/fog_material.h" #include "scene/resources/particles_material.h" diff --git a/editor/plugins/material_editor_plugin.h b/editor/plugins/material_editor_plugin.h index 9c6247d59b..fc3da5fd9f 100644 --- a/editor/plugins/material_editor_plugin.h +++ b/editor/plugins/material_editor_plugin.h @@ -32,7 +32,7 @@ #define MATERIAL_EDITOR_PLUGIN_H #include "editor/editor_plugin.h" -#include "editor/property_editor.h" +#include "editor/plugins/editor_resource_conversion_plugin.h" #include "scene/3d/camera_3d.h" #include "scene/3d/light_3d.h" #include "scene/3d/mesh_instance_3d.h" diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp index 2605a60d48..65b15a6001 100644 --- a/editor/plugins/path_3d_editor_plugin.cpp +++ b/editor/plugins/path_3d_editor_plugin.cpp @@ -34,6 +34,7 @@ #include "core/math/geometry_3d.h" #include "core/os/keyboard.h" #include "editor/editor_node.h" +#include "editor/editor_settings.h" #include "node_3d_editor_plugin.h" #include "scene/resources/curve.h" diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index e2f5d459e0..bbf5ffa462 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -591,7 +591,7 @@ void ScriptEditor::_go_to_tab(int p_idx) { } } - Control *c = Object::cast_to<Control>(tab_container->get_tab_control(p_idx)); + Control *c = tab_container->get_tab_control(p_idx); if (!c) { return; } @@ -813,7 +813,7 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) { if (history_pos >= 0) { idx = tab_container->get_tab_idx_from_control(history[history_pos].control); } - tab_container->set_current_tab(idx); + _go_to_tab(idx); } else { _update_selected_editor_menu(); } @@ -1448,20 +1448,20 @@ void ScriptEditor::_menu_option(int p_option) { case WINDOW_MOVE_UP: { if (tab_container->get_current_tab() > 0) { tab_container->move_child(current, tab_container->get_current_tab() - 1); - tab_container->set_current_tab(tab_container->get_current_tab() - 1); + tab_container->set_current_tab(tab_container->get_current_tab()); _update_script_names(); } } break; case WINDOW_MOVE_DOWN: { if (tab_container->get_current_tab() < tab_container->get_tab_count() - 1) { tab_container->move_child(current, tab_container->get_current_tab() + 1); - tab_container->set_current_tab(tab_container->get_current_tab() + 1); + tab_container->set_current_tab(tab_container->get_current_tab()); _update_script_names(); } } break; default: { if (p_option >= WINDOW_SELECT_BASE) { - tab_container->set_current_tab(p_option - WINDOW_SELECT_BASE); + _go_to_tab(p_option - WINDOW_SELECT_BASE); _update_script_names(); } } @@ -1494,14 +1494,14 @@ void ScriptEditor::_menu_option(int p_option) { case WINDOW_MOVE_UP: { if (tab_container->get_current_tab() > 0) { tab_container->move_child(help, tab_container->get_current_tab() - 1); - tab_container->set_current_tab(tab_container->get_current_tab() - 1); + tab_container->set_current_tab(tab_container->get_current_tab()); _update_script_names(); } } break; case WINDOW_MOVE_DOWN: { if (tab_container->get_current_tab() < tab_container->get_tab_count() - 1) { tab_container->move_child(help, tab_container->get_current_tab() + 1); - tab_container->set_current_tab(tab_container->get_current_tab() + 1); + tab_container->set_current_tab(tab_container->get_current_tab()); _update_script_names(); } } break; @@ -2125,8 +2125,8 @@ void ScriptEditor::_update_script_names() { sd.index = i; sedata.set(i, sd); } - tab_container->set_current_tab(new_prev_tab); - tab_container->set_current_tab(new_cur_tab); + _go_to_tab(new_prev_tab); + _go_to_tab(new_cur_tab); _sort_list_on_update = false; } @@ -2154,7 +2154,6 @@ void ScriptEditor::_update_script_names() { } if (tab_container->get_current_tab() == sedata_filtered[i].index) { script_list->select(index); - _script_selected(index); script_name_label->set_text(sedata_filtered[i].name); script_icon->set_texture(sedata_filtered[i].icon); diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index f77e23e63e..e700412188 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -41,7 +41,6 @@ #include "editor/filesystem_dock.h" #include "editor/plugins/visual_shader_editor_plugin.h" #include "editor/project_settings_editor.h" -#include "editor/property_editor.h" #include "editor/shader_create_dialog.h" #include "scene/gui/split_container.h" #include "servers/display_server.h" diff --git a/editor/plugins/shader_file_editor_plugin.cpp b/editor/plugins/shader_file_editor_plugin.cpp index 4564f60fa5..4874944d33 100644 --- a/editor/plugins/shader_file_editor_plugin.cpp +++ b/editor/plugins/shader_file_editor_plugin.cpp @@ -37,7 +37,6 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" -#include "editor/property_editor.h" #include "servers/display_server.h" #include "servers/rendering/shader_types.h" diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp index 2e9b89f1f3..196d87da36 100644 --- a/editor/plugins/text_editor.cpp +++ b/editor/plugins/text_editor.cpp @@ -32,6 +32,7 @@ #include "core/os/keyboard.h" #include "editor/editor_node.h" +#include "editor/editor_settings.h" void TextEditor::add_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) { ERR_FAIL_COND(p_highlighter.is_null()); diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index a2b09b95eb..0bd8a8a484 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -35,7 +35,9 @@ #include "core/os/keyboard.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_settings.h" #include "scene/gui/check_box.h" +#include "scene/gui/separator.h" #include "scene/gui/view_panner.h" #include "scene/resources/texture.h" diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index b4eab5f811..2a61ea0baa 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -36,6 +36,7 @@ #include "editor/editor_resource_picker.h" #include "editor/editor_scale.h" #include "editor/progress_dialog.h" +#include "scene/gui/color_picker.h" void ThemeItemImportTree::_update_items_tree() { import_items_tree->clear(); diff --git a/editor/plugins/theme_editor_preview.cpp b/editor/plugins/theme_editor_preview.cpp index 0c7303dda4..b5c6c6d651 100644 --- a/editor/plugins/theme_editor_preview.cpp +++ b/editor/plugins/theme_editor_preview.cpp @@ -36,6 +36,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "scene/gui/button.h" +#include "scene/gui/check_button.h" #include "scene/gui/color_picker.h" #include "scene/gui/progress_bar.h" #include "scene/resources/packed_scene.h" diff --git a/editor/plugins/version_control_editor_plugin.cpp b/editor/plugins/version_control_editor_plugin.cpp index 443d5975cd..cf55465417 100644 --- a/editor/plugins/version_control_editor_plugin.cpp +++ b/editor/plugins/version_control_editor_plugin.cpp @@ -35,6 +35,8 @@ #include "editor/editor_file_system.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_settings.h" +#include "scene/gui/separator.h" VersionControlEditorPlugin *VersionControlEditorPlugin::singleton = nullptr; diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h index 2feed6108a..ecaad1e540 100644 --- a/editor/plugins/visual_shader_editor_plugin.h +++ b/editor/plugins/visual_shader_editor_plugin.h @@ -33,6 +33,7 @@ #include "editor/editor_plugin.h" #include "editor/plugins/curve_editor_plugin.h" +#include "editor/plugins/editor_resource_conversion_plugin.h" #include "editor/property_editor.h" #include "scene/gui/button.h" #include "scene/gui/code_edit.h" diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index be1ad1ca9c..fba8c5c522 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -34,6 +34,7 @@ #include "editor/editor_log.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_settings.h" #include "servers/movie_writer/movie_writer.h" ProjectSettingsEditor *ProjectSettingsEditor::singleton = nullptr; @@ -74,8 +75,13 @@ void ProjectSettingsEditor::_setting_edited(const String &p_name) { queue_save(); } +void ProjectSettingsEditor::_update_advanced(bool p_is_advanced) { + custom_properties->set_visible(p_is_advanced); +} + void ProjectSettingsEditor::_advanced_toggled(bool p_button_pressed) { EditorSettings::get_singleton()->set_project_metadata("project_settings", "advanced_mode", p_button_pressed); + _update_advanced(p_button_pressed); general_settings_inspector->set_restrict_to_basic_settings(!p_button_pressed); } @@ -584,35 +590,35 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { advanced->connect("toggled", callable_mp(this, &ProjectSettingsEditor::_advanced_toggled)); search_bar->add_child(advanced); - HBoxContainer *header = memnew(HBoxContainer); - general_editor->add_child(header); + custom_properties = memnew(HBoxContainer); + general_editor->add_child(custom_properties); property_box = memnew(LineEdit); property_box->set_placeholder(TTR("Select a Setting or Type its Name")); property_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); property_box->connect("text_changed", callable_mp(this, &ProjectSettingsEditor::_property_box_changed)); - header->add_child(property_box); + custom_properties->add_child(property_box); feature_box = memnew(OptionButton); feature_box->set_custom_minimum_size(Size2(120, 0) * EDSCALE); feature_box->connect("item_selected", callable_mp(this, &ProjectSettingsEditor::_feature_selected)); - header->add_child(feature_box); + custom_properties->add_child(feature_box); type_box = memnew(OptionButton); type_box->set_custom_minimum_size(Size2(120, 0) * EDSCALE); - header->add_child(type_box); + custom_properties->add_child(type_box); add_button = memnew(Button); add_button->set_text(TTR("Add")); add_button->set_disabled(true); add_button->connect("pressed", callable_mp(this, &ProjectSettingsEditor::_add_setting)); - header->add_child(add_button); + custom_properties->add_child(add_button); del_button = memnew(Button); del_button->set_text(TTR("Delete")); del_button->set_disabled(true); del_button->connect("pressed", callable_mp(this, &ProjectSettingsEditor::_delete_setting)); - header->add_child(del_button); + custom_properties->add_child(del_button); general_settings_inspector = memnew(SectionedInspector); general_settings_inspector->get_inspector()->set_undo_redo(EditorNode::get_singleton()->get_undo_redo()); @@ -693,6 +699,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { advanced->set_pressed(true); } + _update_advanced(use_advanced); general_settings_inspector->set_restrict_to_basic_settings(!use_advanced); import_defaults_editor = memnew(ImportDefaultsEditor); diff --git a/editor/project_settings_editor.h b/editor/project_settings_editor.h index 455aaae11c..849c50b9ba 100644 --- a/editor/project_settings_editor.h +++ b/editor/project_settings_editor.h @@ -61,6 +61,7 @@ class ProjectSettingsEditor : public AcceptDialog { LineEdit *search_box = nullptr; CheckButton *advanced = nullptr; + HBoxContainer *custom_properties = nullptr; LineEdit *property_box = nullptr; OptionButton *feature_box = nullptr; OptionButton *type_box = nullptr; @@ -77,6 +78,7 @@ class ProjectSettingsEditor : public AcceptDialog { UndoRedo *undo_redo = nullptr; void _advanced_toggled(bool p_button_pressed); + void _update_advanced(bool p_is_advanced); void _property_box_changed(const String &p_text); void _update_property_box(); void _feature_selected(int p_index); diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp index 5f4375b424..4092cc47e3 100644 --- a/editor/property_editor.cpp +++ b/editor/property_editor.cpp @@ -51,6 +51,7 @@ #include "editor/editor_settings.h" #include "editor/filesystem_dock.h" #include "editor/multi_node_edit.h" +#include "editor/plugins/editor_resource_conversion_plugin.h" #include "editor/property_selector.h" #include "editor/scene_tree_dock.h" #include "scene/gui/label.h" @@ -59,39 +60,6 @@ #include "scene/resources/packed_scene.h" #include "scene/scene_string_names.h" -void EditorResourceConversionPlugin::_bind_methods() { - GDVIRTUAL_BIND(_converts_to); - GDVIRTUAL_BIND(_handles, "resource"); - GDVIRTUAL_BIND(_convert, "resource"); -} - -String EditorResourceConversionPlugin::converts_to() const { - String ret; - if (GDVIRTUAL_CALL(_converts_to, ret)) { - return ret; - } - - return ""; -} - -bool EditorResourceConversionPlugin::handles(const Ref<Resource> &p_resource) const { - bool ret; - if (GDVIRTUAL_CALL(_handles, p_resource, ret)) { - return ret; - } - - return false; -} - -Ref<Resource> EditorResourceConversionPlugin::convert(const Ref<Resource> &p_resource) const { - Ref<Resource> ret; - if (GDVIRTUAL_CALL(_convert, p_resource, ret)) { - return ret; - } - - return Ref<Resource>(); -} - void CustomPropertyEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_WM_CLOSE_REQUEST: { diff --git a/editor/property_editor.h b/editor/property_editor.h index 27d1cabc3d..195eb89b81 100644 --- a/editor/property_editor.h +++ b/editor/property_editor.h @@ -51,22 +51,6 @@ class EditorFileDialog; class PropertyValueEvaluator; class PropertySelector; -class EditorResourceConversionPlugin : public RefCounted { - GDCLASS(EditorResourceConversionPlugin, RefCounted); - -protected: - static void _bind_methods(); - - GDVIRTUAL0RC(String, _converts_to) - GDVIRTUAL1RC(bool, _handles, Ref<Resource>) - GDVIRTUAL1RC(Ref<Resource>, _convert, Ref<Resource>) - -public: - virtual String converts_to() const; - virtual bool handles(const Ref<Resource> &p_resource) const; - virtual Ref<Resource> convert(const Ref<Resource> &p_resource) const; -}; - class CustomPropertyEditor : public PopupPanel { GDCLASS(CustomPropertyEditor, PopupPanel); diff --git a/editor/rename_dialog.cpp b/editor/rename_dialog.cpp index 3222fb6eec..665aca6a37 100644 --- a/editor/rename_dialog.cpp +++ b/editor/rename_dialog.cpp @@ -42,6 +42,7 @@ #include "plugins/script_editor_plugin.h" #include "scene/gui/control.h" #include "scene/gui/label.h" +#include "scene/gui/separator.h" #include "scene/gui/tab_container.h" RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_undo_redo) { diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index 4372bb33ba..a23f19de85 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -623,11 +623,11 @@ void GDScriptSyntaxHighlighter::_update_cache() { annotation_color = Color(1.0, 0.7, 0.45); string_name_color = Color(1.0, 0.66, 0.72); } else { - function_definition_color = Color(0.0, 0.65, 0.73); - node_path_color = Color(0.62, 0.67, 0.39); - node_ref_color = Color(0.32, 0.55, 0.29); - annotation_color = Color(0.8, 0.5, 0.25); - string_name_color = Color(0.9, 0.56, 0.62); + function_definition_color = Color(0, 0.6, 0.6); + node_path_color = Color(0.18, 0.55, 0); + node_ref_color = Color(0.0, 0.5, 0); + annotation_color = Color(0.8, 0.37, 0); + string_name_color = Color(0.8, 0.46, 0.52); } EDITOR_DEF("text_editor/theme/highlighting/gdscript/function_definition_color", function_definition_color); diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp index 7460f8edff..c0d5856be5 100644 --- a/modules/gdscript/language_server/gdscript_language_protocol.cpp +++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp @@ -34,6 +34,7 @@ #include "editor/doc_tools.h" #include "editor/editor_log.h" #include "editor/editor_node.h" +#include "editor/editor_settings.h" GDScriptLanguageProtocol *GDScriptLanguageProtocol::singleton = nullptr; diff --git a/modules/gdscript/language_server/gdscript_language_server.cpp b/modules/gdscript/language_server/gdscript_language_server.cpp index 14337e87da..ead4ef1987 100644 --- a/modules/gdscript/language_server/gdscript_language_server.cpp +++ b/modules/gdscript/language_server/gdscript_language_server.cpp @@ -34,6 +34,7 @@ #include "core/os/os.h" #include "editor/editor_log.h" #include "editor/editor_node.h" +#include "editor/editor_settings.h" GDScriptLanguageServer::GDScriptLanguageServer() { _EDITOR_DEF("network/language_server/remote_host", host); diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp index 959651c024..ded2a7b4d4 100644 --- a/modules/gdscript/language_server/gdscript_workspace.cpp +++ b/modules/gdscript/language_server/gdscript_workspace.cpp @@ -38,6 +38,7 @@ #include "editor/editor_file_system.h" #include "editor/editor_help.h" #include "editor/editor_node.h" +#include "editor/editor_settings.h" #include "gdscript_language_protocol.h" #include "scene/resources/packed_scene.h" diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp index cfbb937aac..f830c7ffe1 100644 --- a/modules/mono/editor/editor_internal_calls.cpp +++ b/modules/mono/editor/editor_internal_calls.cpp @@ -41,6 +41,7 @@ #include "editor/editor_node.h" #include "editor/editor_paths.h" #include "editor/editor_scale.h" +#include "editor/editor_settings.h" #include "editor/plugins/script_editor_plugin.h" #include "main/main.h" diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs new file mode 100644 index 0000000000..d774021131 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs @@ -0,0 +1,820 @@ +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif +using System; +using System.Runtime.InteropServices; + +namespace Godot +{ + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public struct Projection : IEquatable<Projection> + { + /// <summary> + /// Enumerated index values for the planes. + /// </summary> + public enum Planes + { + /// <summary> + /// The projection's near plane. + /// </summary> + Near, + /// <summary> + /// The projection's far plane. + /// </summary> + Far, + /// <summary> + /// The projection's left plane. + /// </summary> + Left, + /// <summary> + /// The projection's top plane. + /// </summary> + Top, + /// <summary> + /// The projection's right plane. + /// </summary> + Right, + /// <summary> + /// The projection's bottom plane. + /// </summary> + Bottom, + } + + /// <summary> + /// The projections's X column. Also accessible by using the index position <c>[0]</c>. + /// </summary> + public Vector4 x; + + /// <summary> + /// The projections's Y column. Also accessible by using the index position <c>[1]</c>. + /// </summary> + public Vector4 y; + + /// <summary> + /// The projections's Z column. Also accessible by using the index position <c>[2]</c>. + /// </summary> + public Vector4 z; + + /// <summary> + /// The projections's W column. Also accessible by using the index position <c>[3]</c>. + /// </summary> + public Vector4 w; + + /// <summary> + /// Constructs a projection from 4 vectors (matrix columns). + /// </summary> + /// <param name="x">The X column, or column index 0.</param> + /// <param name="y">The Y column, or column index 1.</param> + /// <param name="z">The Z column, or column index 2.</param> + /// <param name="w">The W column, or column index 3.</param> + public Projection(Vector4 x, Vector4 y, Vector4 z, Vector4 w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + /// <summary> + /// Constructs a new <see cref="Projection"/> from an existing <see cref="Projection"/>. + /// </summary> + /// <param name="proj">The existing <see cref="Projection"/>.</param> + public Projection(Projection proj) + { + x = proj.x; + y = proj.y; + z = proj.z; + w = proj.w; + } + + /// <summary> + /// Constructs a new <see cref="Projection"/> from a <see cref="Transform3D"/>. + /// </summary> + /// <param name="transform">The <see cref="Transform3D"/>.</param> + public Projection(Transform3D transform) + { + x = new Vector4(transform.basis.Row0.x, transform.basis.Row1.x, transform.basis.Row2.x, 0); + y = new Vector4(transform.basis.Row0.y, transform.basis.Row1.y, transform.basis.Row2.y, 0); + z = new Vector4(transform.basis.Row0.z, transform.basis.Row1.z, transform.basis.Row2.z, 0); + w = new Vector4(transform.origin.x, transform.origin.y, transform.origin.z, 1); + } + + /// <summary> + /// Constructs a new <see cref="Transform3D"/> from the <see cref="Projection"/>. + /// </summary> + /// <param name="proj">The <see cref="Projection"/>.</param> + public static explicit operator Transform3D(Projection proj) + { + return new Transform3D( + new Basis( + new Vector3(proj.x.x, proj.x.y, proj.x.z), + new Vector3(proj.y.x, proj.y.y, proj.y.z), + new Vector3(proj.z.x, proj.z.y, proj.z.z) + ), + new Vector3(proj.w.x, proj.w.y, proj.w.z) + ); + } + + public static Projection CreateDepthCorrection(bool flipY) + { + return new Projection( + new Vector4(1, 0, 0, 0), + new Vector4(0, flipY ? -1 : 1, 0, 0), + new Vector4(0, 0, (real_t)0.5, 0), + new Vector4(0, 0, (real_t)0.5, 1) + ); + } + + public static Projection CreateFitAabb(AABB aabb) + { + Vector3 min = aabb.Position; + Vector3 max = aabb.Position + aabb.Size; + + return new Projection( + new Vector4(2 / (max.x - min.x), 0, 0, 0), + new Vector4(0, 2 / (max.y - min.y), 0, 0), + new Vector4(0, 0, 2 / (max.z - min.z), 0), + new Vector4(-(max.x + min.x) / (max.x - min.x), -(max.y + min.y) / (max.y - min.y), -(max.z + min.z) / (max.z - min.z), 1) + ); + } + + public static Projection CreateForHmd(int eye, real_t aspect, real_t intraocularDist, real_t displayWidth, real_t displayToLens, real_t oversample, real_t zNear, real_t zFar) + { + real_t f1 = (intraocularDist * (real_t)0.5) / displayToLens; + real_t f2 = ((displayWidth - intraocularDist) * (real_t)0.5) / displayToLens; + real_t f3 = (displayWidth / (real_t)4.0) / displayToLens; + + real_t add = ((f1 + f2) * (oversample - (real_t)1.0)) / (real_t)2.0; + f1 += add; + f2 += add; + f3 *= oversample; + + f3 /= aspect; + + switch (eye) + { + case 1: + return CreateFrustum(-f2 * zNear, f1 * zNear, -f3 * zNear, f3 * zNear, zNear, zFar); + case 2: + return CreateFrustum(-f1 * zNear, f2 * zNear, -f3 * zNear, f3 * zNear, zNear, zFar); + default: + return Zero; + } + } + + public static Projection CreateFrustum(real_t left, real_t right, real_t bottom, real_t top, real_t near, real_t far) + { + if (right <= left) + { + throw new ArgumentException("right is less or equal to left."); + } + if (top <= bottom) + { + throw new ArgumentException("top is less or equal to bottom."); + } + if (far <= near) + { + throw new ArgumentException("far is less or equal to near."); + } + + real_t x = 2 * near / (right - left); + real_t y = 2 * near / (top - bottom); + + real_t a = (right + left) / (right - left); + real_t b = (top + bottom) / (top - bottom); + real_t c = -(far + near) / (far - near); + real_t d = -2 * far * near / (far - near); + + return new Projection( + new Vector4(x, 0, 0, 0), + new Vector4(0, y, 0, 0), + new Vector4(a, b, c, -1), + new Vector4(0, 0, d, 0) + ); + } + + public static Projection CreateFrustumAspect(real_t size, real_t aspect, Vector2 offset, real_t near, real_t far, bool flipFov) + { + if (!flipFov) + { + size *= aspect; + } + return CreateFrustum(-size / 2 + offset.x, +size / 2 + offset.x, -size / aspect / 2 + offset.y, +size / aspect / 2 + offset.y, near, far); + } + + public static Projection CreateLightAtlasRect(Rect2 rect) + { + return new Projection( + new Vector4(rect.Size.x, 0, 0, 0), + new Vector4(0, rect.Size.y, 0, 0), + new Vector4(0, 0, 1, 0), + new Vector4(rect.Position.x, rect.Position.y, 0, 1) + ); + } + + public static Projection CreateOrthogonal(real_t left, real_t right, real_t bottom, real_t top, real_t zNear, real_t zFar) + { + Projection proj = Projection.Identity; + proj.x.x = (real_t)2.0 / (right - left); + proj.w.x = -((right + left) / (right - left)); + proj.y.y = (real_t)2.0 / (top - bottom); + proj.w.y = -((top + bottom) / (top - bottom)); + proj.z.z = (real_t)(-2.0) / (zFar - zNear); + proj.w.z = -((zFar + zNear) / (zFar - zNear)); + proj.w.w = (real_t)1.0; + return proj; + } + + public static Projection CreateOrthogonalAspect(real_t size, real_t aspect, real_t zNear, real_t zFar, bool flipFov) + { + if (!flipFov) + { + size *= aspect; + } + return CreateOrthogonal(-size / 2, +size / 2, -size / aspect / 2, +size / aspect / 2, zNear, zFar); + } + + public static Projection CreatePerspective(real_t fovyDegrees, real_t aspect, real_t zNear, real_t zFar, bool flipFov) + { + if (flipFov) + { + fovyDegrees = GetFovy(fovyDegrees, (real_t)1.0 / aspect); + } + real_t radians = Mathf.Deg2Rad(fovyDegrees / (real_t)2.0); + real_t deltaZ = zFar - zNear; + real_t sine = Mathf.Sin(radians); + + if ((deltaZ == 0) || (sine == 0) || (aspect == 0)) + { + return Zero; + } + + real_t cotangent = Mathf.Cos(radians) / sine; + + Projection proj = Projection.Identity; + + proj.x.x = cotangent / aspect; + proj.y.y = cotangent; + proj.z.z = -(zFar + zNear) / deltaZ; + proj.z.w = -1; + proj.w.z = -2 * zNear * zFar / deltaZ; + proj.w.w = 0; + + return proj; + } + + public static Projection CreatePerspectiveHmd(real_t fovyDegrees, real_t aspect, real_t zNear, real_t zFar, bool flipFov, int eye, real_t intraocularDist, real_t convergenceDist) + { + if (flipFov) + { + fovyDegrees = GetFovy(fovyDegrees, (real_t)1.0 / aspect); + } + + real_t ymax = zNear * Mathf.Tan(Mathf.Deg2Rad(fovyDegrees / (real_t)2.0)); + real_t xmax = ymax * aspect; + real_t frustumshift = (intraocularDist / (real_t)2.0) * zNear / convergenceDist; + real_t left; + real_t right; + real_t modeltranslation; + switch (eye) + { + case 1: + left = -xmax + frustumshift; + right = xmax + frustumshift; + modeltranslation = intraocularDist / (real_t)2.0; + break; + case 2: + left = -xmax - frustumshift; + right = xmax - frustumshift; + modeltranslation = -intraocularDist / (real_t)2.0; + break; + default: + left = -xmax; + right = xmax; + modeltranslation = (real_t)0.0; + break; + } + Projection proj = CreateFrustum(left, right, -ymax, ymax, zNear, zFar); + Projection cm = Projection.Identity; + cm.w.x = modeltranslation; + return proj * cm; + } + + public real_t Determinant() + { + return x.w * y.z * z.y * w.x - x.z * y.w * z.y * w.x - + x.w * y.y * z.z * w.x + x.y * y.w * z.z * w.x + + x.z * y.y * z.w * w.x - x.y * y.z * z.w * w.x - + x.w * y.z * z.x * w.y + x.z * y.w * z.x * w.y + + x.w * y.x * z.z * w.y - x.x * y.w * z.z * w.y - + x.z * y.x * z.w * w.y + x.x * y.z * z.w * w.y + + x.w * y.y * z.x * w.z - x.y * y.w * z.x * w.z - + x.w * y.x * z.y * w.z + x.x * y.w * z.y * w.z + + x.y * y.x * z.w * w.z - x.x * y.y * z.w * w.z - + x.z * y.y * z.x * w.w + x.y * y.z * z.x * w.w + + x.z * y.x * z.y * w.w - x.x * y.z * z.y * w.w - + x.y * y.x * z.z * w.w + x.x * y.y * z.z * w.w; + } + + public real_t GetAspect() + { + Vector2 vpHe = GetViewportHalfExtents(); + return vpHe.x / vpHe.y; + } + + public real_t GetFov() + { + Plane rightPlane = new Plane(x.w - x.x, y.w - y.x, z.w - z.x, -w.w + w.x).Normalized(); + if (z.x == 0 && z.y == 0) + { + return Mathf.Rad2Deg(Mathf.Acos(Mathf.Abs(rightPlane.Normal.x))) * (real_t)2.0; + } + else + { + Plane leftPlane = new Plane(x.w + x.x, y.w + y.x, z.w + z.x, w.w + w.x).Normalized(); + return Mathf.Rad2Deg(Mathf.Acos(Mathf.Abs(leftPlane.Normal.x))) + Mathf.Rad2Deg(Mathf.Acos(Mathf.Abs(rightPlane.Normal.x))); + } + } + + public static real_t GetFovy(real_t fovx, real_t aspect) + { + return Mathf.Rad2Deg(Mathf.Atan(aspect * Mathf.Tan(Mathf.Deg2Rad(fovx) * (real_t)0.5)) * (real_t)2.0); + } + + public real_t GetLodMultiplier() + { + if (IsOrthogonal()) + { + return GetViewportHalfExtents().x; + } + else + { + real_t zn = GetZNear(); + real_t width = GetViewportHalfExtents().x * (real_t)2.0; + return (real_t)1.0 / (zn / width); + } + } + + public int GetPixelsPerMeter(int forPixelWidth) + { + Vector3 result = Xform(new Vector3(1, 0, -1)); + + return (int)((result.x * (real_t)0.5 + (real_t)0.5) * forPixelWidth); + } + + public Plane GetProjectionPlane(Planes plane) + { + Plane newPlane = plane switch + { + Planes.Near => new Plane(x.w + x.z, y.w + y.z, z.w + z.z, w.w + w.z), + Planes.Far => new Plane(x.w - x.z, y.w - y.z, z.w - z.z, w.w - w.z), + Planes.Left => new Plane(x.w + x.x, y.w + y.x, z.w + z.x, w.w + w.x), + Planes.Top => new Plane(x.w - x.y, y.w - y.y, z.w - z.y, w.w - w.y), + Planes.Right => new Plane(x.w - x.x, y.w - y.x, z.w - z.x, w.w - w.x), + Planes.Bottom => new Plane(x.w + x.y, y.w + y.y, z.w + z.y, w.w + w.y), + _ => new Plane(), + }; + newPlane.Normal = -newPlane.Normal; + return newPlane.Normalized(); + } + + public Vector2 GetFarPlaneHalfExtents() + { + var res = GetProjectionPlane(Planes.Far).Intersect3(GetProjectionPlane(Planes.Right), GetProjectionPlane(Planes.Top)); + return new Vector2(res.Value.x, res.Value.y); + } + + public Vector2 GetViewportHalfExtents() + { + var res = GetProjectionPlane(Planes.Near).Intersect3(GetProjectionPlane(Planes.Right), GetProjectionPlane(Planes.Top)); + return new Vector2(res.Value.x, res.Value.y); + } + + public real_t GetZFar() + { + return GetProjectionPlane(Planes.Far).D; + } + + public real_t GetZNear() + { + return -GetProjectionPlane(Planes.Near).D; + } + + public Projection FlippedY() + { + Projection proj = this; + proj.y = -proj.y; + return proj; + } + + public Projection PerspectiveZNearAdjusted(real_t newZNear) + { + Projection proj = this; + real_t zFar = GetZFar(); + real_t zNear = newZNear; + real_t deltaZ = zFar - zNear; + proj.z.z = -(zFar + zNear) / deltaZ; + proj.w.z = -2 * zNear * zFar / deltaZ; + return proj; + } + + public Projection JitterOffseted(Vector2 offset) + { + Projection proj = this; + proj.w.x += offset.x; + proj.w.y += offset.y; + return proj; + } + + public Projection Inverse() + { + Projection proj = this; + int i, j, k; + int[] pvt_i = new int[4]; + int[] pvt_j = new int[4]; /* Locations of pivot matrix */ + real_t pvt_val; /* Value of current pivot element */ + real_t hold; /* Temporary storage */ + real_t determinant = 1.0f; + for (k = 0; k < 4; k++) + { + /* Locate k'th pivot element */ + pvt_val = proj[k][k]; /* Initialize for search */ + pvt_i[k] = k; + pvt_j[k] = k; + for (i = k; i < 4; i++) + { + for (j = k; j < 4; j++) + { + if (Mathf.Abs(proj[i][j]) > Mathf.Abs(pvt_val)) + { + pvt_i[k] = i; + pvt_j[k] = j; + pvt_val = proj[i][j]; + } + } + } + + /* Product of pivots, gives determinant when finished */ + determinant *= pvt_val; + if (Mathf.IsZeroApprox(determinant)) + { + return Zero; + } + + /* "Interchange" rows (with sign change stuff) */ + i = pvt_i[k]; + if (i != k) + { /* If rows are different */ + for (j = 0; j < 4; j++) + { + hold = -proj[k][j]; + proj[k, j] = proj[i][j]; + proj[i, j] = hold; + } + } + + /* "Interchange" columns */ + j = pvt_j[k]; + if (j != k) + { /* If columns are different */ + for (i = 0; i < 4; i++) + { + hold = -proj[i][k]; + proj[i, k] = proj[i][j]; + proj[i, j] = hold; + } + } + + /* Divide column by minus pivot value */ + for (i = 0; i < 4; i++) + { + if (i != k) + { + proj[i, k] /= (-pvt_val); + } + } + + /* Reduce the matrix */ + for (i = 0; i < 4; i++) + { + hold = proj[i][k]; + for (j = 0; j < 4; j++) + { + if (i != k && j != k) + { + proj[i, j] += hold * proj[k][j]; + } + } + } + + /* Divide row by pivot */ + for (j = 0; j < 4; j++) + { + if (j != k) + { + proj[k, j] /= pvt_val; + } + } + + /* Replace pivot by reciprocal (at last we can touch it). */ + proj[k, k] = (real_t)1.0 / pvt_val; + } + + /* That was most of the work, one final pass of row/column interchange */ + /* to finish */ + for (k = 4 - 2; k >= 0; k--) + { /* Don't need to work with 1 by 1 corner*/ + i = pvt_j[k]; /* Rows to swap correspond to pivot COLUMN */ + if (i != k) + { /* If rows are different */ + for (j = 0; j < 4; j++) + { + hold = proj[k][j]; + proj[k, j] = -proj[i][j]; + proj[i, j] = hold; + } + } + + j = pvt_i[k]; /* Columns to swap correspond to pivot ROW */ + if (j != k) + { /* If columns are different */ + for (i = 0; i < 4; i++) + { + hold = proj[i][k]; + proj[i, k] = -proj[i][j]; + proj[i, j] = hold; + } + } + } + return proj; + } + + public bool IsOrthogonal() + { + return w.w == (real_t)1.0; + } + + /// <summary> + /// Composes these two projections by multiplying them + /// together. This has the effect of applying the right + /// and then the left projection. + /// </summary> + /// <param name="left">The parent transform.</param> + /// <param name="right">The child transform.</param> + /// <returns>The composed projection.</returns> + public static Projection operator *(Projection left, Projection right) + { + return new Projection( + new Vector4( + left.x.x * right.x.x + left.y.x * right.x.y + left.z.x * right.x.z + left.w.x * right.x.w, + left.x.y * right.x.x + left.y.y * right.x.y + left.z.y * right.x.z + left.w.y * right.x.w, + left.x.z * right.x.x + left.y.z * right.x.y + left.z.z * right.x.z + left.w.z * right.x.w, + left.x.w * right.x.x + left.y.w * right.x.y + left.z.w * right.x.z + left.w.w * right.x.w + ), new Vector4( + left.x.x * right.y.x + left.y.x * right.y.y + left.z.x * right.y.z + left.w.x * right.y.w, + left.x.y * right.y.x + left.y.y * right.y.y + left.z.y * right.y.z + left.w.y * right.y.w, + left.x.z * right.y.x + left.y.z * right.y.y + left.z.z * right.y.z + left.w.z * right.y.w, + left.x.w * right.y.x + left.y.w * right.y.y + left.z.w * right.y.z + left.w.w * right.y.w + ), new Vector4( + left.x.x * right.z.x + left.y.x * right.z.y + left.z.x * right.z.z + left.w.x * right.z.w, + left.x.y * right.z.x + left.y.y * right.z.y + left.z.y * right.z.z + left.w.y * right.z.w, + left.x.z * right.z.x + left.y.z * right.z.y + left.z.z * right.z.z + left.w.z * right.z.w, + left.x.w * right.z.x + left.y.w * right.z.y + left.z.w * right.z.z + left.w.w * right.z.w + ), new Vector4( + left.x.x * right.w.x + left.y.x * right.w.y + left.z.x * right.w.z + left.w.x * right.w.w, + left.x.y * right.w.x + left.y.y * right.w.y + left.z.y * right.w.z + left.w.y * right.w.w, + left.x.z * right.w.x + left.y.z * right.w.y + left.z.z * right.w.z + left.w.z * right.w.w, + left.x.w * right.w.x + left.y.w * right.w.y + left.z.w * right.w.z + left.w.w * right.w.w + ) + ); + } + + /// <summary> + /// Returns a vector transformed (multiplied) by this projection. + /// </summary> + /// <param name="proj">The projection to apply.</param> + /// <param name="v">A vector to transform.</param> + /// <returns>The transformed vector.</returns> + public static Vector4 operator *(Projection proj, Vector4 v) + { + return new Vector4( + proj.x.x * v.x + proj.y.x * v.y + proj.z.x * v.z + proj.w.x * v.w, + proj.x.y * v.x + proj.y.y * v.y + proj.z.y * v.z + proj.w.y * v.w, + proj.x.z * v.x + proj.y.z * v.y + proj.z.z * v.z + proj.w.z * v.w, + proj.x.w * v.x + proj.y.w * v.y + proj.z.w * v.z + proj.w.w * v.w + ); + } + + /// <summary> + /// Returns <see langword="true"/> if the projections are exactly equal. + /// </summary> + /// <param name="left">The left projection.</param> + /// <param name="right">The right projection.</param> + /// <returns>Whether or not the projections are exactly equal.</returns> + public static bool operator ==(Projection left, Projection right) + { + return left.Equals(right); + } + + /// <summary> + /// Returns <see langword="true"/> if the projections are not exactly equal. + /// </summary> + /// <param name="left">The left projection.</param> + /// <param name="right">The right projection.</param> + /// <returns>Whether or not the projections are not exactly equal.</returns> + public static bool operator !=(Projection left, Projection right) + { + return !left.Equals(right); + } + + /// <summary> + /// Access whole columns in the form of <see cref="Vector4"/>. + /// </summary> + /// <param name="column">Which column vector.</param> + public Vector4 this[int column] + { + get + { + switch (column) + { + case 0: + return x; + case 1: + return y; + case 2: + return z; + case 3: + return w; + default: + throw new IndexOutOfRangeException(); + } + } + set + { + switch (column) + { + case 0: + x = value; + return; + case 1: + y = value; + return; + case 2: + z = value; + return; + case 3: + w = value; + return; + default: + throw new IndexOutOfRangeException(); + } + } + } + + /// <summary> + /// Access single values. + /// </summary> + /// <param name="column">Which column vector.</param> + /// <param name="row">Which row of the column.</param> + public real_t this[int column, int row] + { + get + { + switch (column) + { + case 0: + return x[row]; + case 1: + return y[row]; + case 2: + return z[row]; + case 3: + return w[row]; + default: + throw new IndexOutOfRangeException(); + } + } + set + { + switch (column) + { + case 0: + x[row] = value; + return; + case 1: + y[row] = value; + return; + case 2: + z[row] = value; + return; + case 3: + w[row] = value; + return; + default: + throw new IndexOutOfRangeException(); + } + } + } + + /// <summary> + /// Returns a vector transformed (multiplied) by this projection. + /// </summary> + /// <param name="v">A vector to transform.</param> + /// <returns>The transformed vector.</returns> + private Vector3 Xform(Vector3 v) + { + Vector3 ret = new Vector3( + x.x * v.x + y.x * v.y + z.x * v.z + w.x, + x.y * v.x + y.y * v.y + z.y * v.z + w.y, + x.z * v.x + y.z * v.y + z.z * v.z + w.z + ); + return ret / (x.w * v.x + y.w * v.y + z.w * v.z + w.w); + } + + // Constants + private static readonly Projection _zero = new Projection( + new Vector4(0, 0, 0, 0), + new Vector4(0, 0, 0, 0), + new Vector4(0, 0, 0, 0), + new Vector4(0, 0, 0, 0) + ); + private static readonly Projection _identity = new Projection( + new Vector4(1, 0, 0, 0), + new Vector4(0, 1, 0, 0), + new Vector4(0, 0, 1, 0), + new Vector4(0, 0, 0, 1) + ); + + /// <summary> + /// Zero projection, a projection with all components set to <c>0</c>. + /// </summary> + /// <value>Equivalent to <c>new Projection(Vector4.Zero, Vector4.Zero, Vector4.Zero, Vector4.Zero)</c>.</value> + public static Projection Zero { get { return _zero; } } + + /// <summary> + /// The identity projection, with no distortion applied. + /// This is used as a replacement for <c>Projection()</c> in GDScript. + /// Do not use <c>new Projection()</c> with no arguments in C#, because it sets all values to zero. + /// </summary> + /// <value>Equivalent to <c>new Projection(new Vector4(1, 0, 0, 0), new Vector4(0, 1, 0, 0), new Vector4(0, 0, 1, 0), new Vector4(0, 0, 0, 1))</c>.</value> + public static Projection Identity { get { return _identity; } } + + /// <summary> + /// Serves as the hash function for <see cref="Projection"/>. + /// </summary> + /// <returns>A hash code for this projection.</returns> + public override int GetHashCode() + { + return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode(); + } + + /// <summary> + /// Converts this <see cref="Projection"/> to a string. + /// </summary> + /// <returns>A string representation of this projection.</returns> + public override string ToString() + { + return $"{x.x}, {x.y}, {x.z}, {x.w}\n{y.x}, {y.y}, {y.z}, {y.w}\n{z.x}, {z.y}, {z.z}, {z.w}\n{w.x}, {w.y}, {w.z}, {w.w}\n"; + } + + /// <summary> + /// Converts this <see cref="Projection"/> to a string with the given <paramref name="format"/>. + /// </summary> + /// <returns>A string representation of this projection.</returns> + public string ToString(string format) + { + return $"{x.x.ToString(format)}, {x.y.ToString(format)}, {x.z.ToString(format)}, {x.w.ToString(format)}\n" + + $"{y.x.ToString(format)}, {y.y.ToString(format)}, {y.z.ToString(format)}, {y.w.ToString(format)}\n" + + $"{z.x.ToString(format)}, {z.y.ToString(format)}, {z.z.ToString(format)}, {z.w.ToString(format)}\n" + + $"{w.x.ToString(format)}, {w.y.ToString(format)}, {w.z.ToString(format)}, {w.w.ToString(format)}\n"; + } + + /// <summary> + /// Returns <see langword="true"/> if the projection is exactly equal + /// to the given object (<see paramref="obj"/>). + /// </summary> + /// <param name="obj">The object to compare with.</param> + /// <returns>Whether or not the vector and the object are equal.</returns> + public override bool Equals(object obj) + { + if (obj is Projection) + { + return Equals((Projection)obj); + } + return false; + } + + /// <summary> + /// Returns <see langword="true"/> if the projections are exactly equal. + /// </summary> + /// <param name="other">The other projection.</param> + /// <returns>Whether or not the projections are exactly equal.</returns> + public bool Equals(Projection other) + { + return x == other.x && y == other.y && z == other.z && w == other.w; + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs new file mode 100644 index 0000000000..72fe9cb16f --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs @@ -0,0 +1,730 @@ +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif +using System; +using System.Runtime.InteropServices; + +namespace Godot +{ + /// <summary> + /// 4-element structure that can be used to represent positions in 4D space or any other pair of numeric values. + /// </summary> + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public struct Vector4 : IEquatable<Vector4> + { + /// <summary> + /// Enumerated index values for the axes. + /// Returned by <see cref="MaxAxisIndex"/> and <see cref="MinAxisIndex"/>. + /// </summary> + public enum Axis + { + /// <summary> + /// The vector's X axis. + /// </summary> + X = 0, + /// <summary> + /// The vector's Y axis. + /// </summary> + Y, + /// <summary> + /// The vector's Z axis. + /// </summary> + Z, + /// <summary> + /// The vector's W axis. + /// </summary> + W + } + + /// <summary> + /// The vector's X component. Also accessible by using the index position <c>[0]</c>. + /// </summary> + public real_t x; + + /// <summary> + /// The vector's Y component. Also accessible by using the index position <c>[1]</c>. + /// </summary> + public real_t y; + + /// <summary> + /// The vector's Z component. Also accessible by using the index position <c>[2]</c>. + /// </summary> + public real_t z; + + /// <summary> + /// The vector's W component. Also accessible by using the index position <c>[3]</c>. + /// </summary> + public real_t w; + + /// <summary> + /// Access vector components using their index. + /// </summary> + /// <exception cref="IndexOutOfRangeException"> + /// Thrown when the given the <paramref name="index"/> is not 0, 1, 2 or 3. + /// </exception> + /// <value> + /// <c>[0]</c> is equivalent to <see cref="x"/>, + /// <c>[1]</c> is equivalent to <see cref="y"/>, + /// <c>[2]</c> is equivalent to <see cref="z"/>. + /// <c>[3]</c> is equivalent to <see cref="w"/>. + /// </value> + public real_t this[int index] + { + get + { + switch (index) + { + case 0: + return x; + case 1: + return y; + case 2: + return z; + case 3: + return w; + default: + throw new IndexOutOfRangeException(); + } + } + set + { + switch (index) + { + case 0: + x = value; + return; + case 1: + y = value; + return; + case 2: + z = value; + return; + case 3: + w = value; + return; + default: + throw new IndexOutOfRangeException(); + } + } + } + + /// <summary> + /// Helper method for deconstruction into a tuple. + /// </summary> + public void Deconstruct(out real_t x, out real_t y, out real_t z, out real_t w) + { + x = this.x; + y = this.y; + z = this.z; + w = this.w; + } + + internal void Normalize() + { + real_t lengthsq = LengthSquared(); + + if (lengthsq == 0) + { + x = y = z = w = 0f; + } + else + { + real_t length = Mathf.Sqrt(lengthsq); + x /= length; + y /= length; + z /= length; + w /= length; + } + } + + + /// <summary> + /// Returns a new vector with all components in absolute values (i.e. positive). + /// </summary> + /// <returns>A vector with <see cref="Mathf.Abs(real_t)"/> called on each component.</returns> + public Vector4 Abs() + { + return new Vector4(Mathf.Abs(x), Mathf.Abs(y), Mathf.Abs(z), Mathf.Abs(w)); + } + + /// <summary> + /// Returns a new vector with all components rounded up (towards positive infinity). + /// </summary> + /// <returns>A vector with <see cref="Mathf.Ceil"/> called on each component.</returns> + public Vector4 Ceil() + { + return new Vector4(Mathf.Ceil(x), Mathf.Ceil(y), Mathf.Ceil(z), Mathf.Ceil(w)); + } + + /// <summary> + /// Returns a new vector with all components clamped between the + /// components of <paramref name="min"/> and <paramref name="max"/> using + /// <see cref="Mathf.Clamp(real_t, real_t, real_t)"/>. + /// </summary> + /// <param name="min">The vector with minimum allowed values.</param> + /// <param name="max">The vector with maximum allowed values.</param> + /// <returns>The vector with all components clamped.</returns> + public Vector4 Clamp(Vector4 min, Vector4 max) + { + return new Vector4 + ( + Mathf.Clamp(x, min.x, max.x), + Mathf.Clamp(y, min.y, max.y), + Mathf.Clamp(z, min.z, max.z), + Mathf.Clamp(w, min.w, max.w) + ); + } + + + /// <summary> + /// Returns a new vector with all components rounded down (towards negative infinity). + /// </summary> + /// <returns>A vector with <see cref="Mathf.Floor"/> called on each component.</returns> + public Vector4 Floor() + { + return new Vector4(Mathf.Floor(x), Mathf.Floor(y), Mathf.Floor(z), Mathf.Floor(w)); + } + + + /// <summary> + /// Returns the dot product of this vector and <paramref name="with"/>. + /// </summary> + /// <param name="with">The other vector to use.</param> + /// <returns>The dot product of the two vectors.</returns> + public real_t Dot(Vector4 with) + { + return (x * with.x) + (y * with.y) + (z * with.z) + (w + with.w); + } + + /// <summary> + /// Returns the inverse of this vector. This is the same as <c>new Vector4(1 / v.x, 1 / v.y, 1 / v.z, 1 / v.w)</c>. + /// </summary> + /// <returns>The inverse of this vector.</returns> + public Vector4 Inverse() + { + return new Vector4(1 / x, 1 / y, 1 / z, 1 / w); + } + + /// <summary> + /// Returns <see langword="true"/> if the vector is normalized, and <see langword="false"/> otherwise. + /// </summary> + /// <returns>A <see langword="bool"/> indicating whether or not the vector is normalized.</returns> + public bool IsNormalized() + { + return Mathf.Abs(LengthSquared() - 1.0f) < Mathf.Epsilon; + } + + /// <summary> + /// Returns the length (magnitude) of this vector. + /// </summary> + /// <seealso cref="LengthSquared"/> + /// <returns>The length of this vector.</returns> + public real_t Length() + { + real_t x2 = x * x; + real_t y2 = y * y; + real_t z2 = z * z; + real_t w2 = w * w; + + return Mathf.Sqrt(x2 + y2 + z2 + w2); + } + + /// <summary> + /// Returns the squared length (squared magnitude) of this vector. + /// This method runs faster than <see cref="Length"/>, so prefer it if + /// you need to compare vectors or need the squared length for some formula. + /// </summary> + /// <returns>The squared length of this vector.</returns> + public real_t LengthSquared() + { + real_t x2 = x * x; + real_t y2 = y * y; + real_t z2 = z * z; + real_t w2 = w * w; + + return x2 + y2 + z2 + w2; + } + + /// <summary> + /// Returns the result of the linear interpolation between + /// this vector and <paramref name="to"/> by amount <paramref name="weight"/>. + /// </summary> + /// <param name="to">The destination vector for interpolation.</param> + /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> + /// <returns>The resulting vector of the interpolation.</returns> + public Vector4 Lerp(Vector4 to, real_t weight) + { + return new Vector4 + ( + Mathf.Lerp(x, to.x, weight), + Mathf.Lerp(y, to.y, weight), + Mathf.Lerp(z, to.z, weight), + Mathf.Lerp(w, to.w, weight) + ); + } + + /// <summary> + /// Returns the axis of the vector's highest value. See <see cref="Axis"/>. + /// If all components are equal, this method returns <see cref="Axis.X"/>. + /// </summary> + /// <returns>The index of the highest axis.</returns> + public Axis MaxAxisIndex() + { + int max_index = 0; + real_t max_value = x; + for (int i = 1; i < 4; i++) + { + if (this[i] > max_value) + { + max_index = i; + max_value = this[i]; + } + } + return (Axis)max_index; + } + + /// <summary> + /// Returns the axis of the vector's lowest value. See <see cref="Axis"/>. + /// If all components are equal, this method returns <see cref="Axis.W"/>. + /// </summary> + /// <returns>The index of the lowest axis.</returns> + public Axis MinAxisIndex() + { + int min_index = 0; + real_t min_value = x; + for (int i = 1; i < 4; i++) + { + if (this[i] <= min_value) + { + min_index = i; + min_value = this[i]; + } + } + return (Axis)min_index; + } + + /// <summary> + /// Returns the vector scaled to unit length. Equivalent to <c>v / v.Length()</c>. + /// </summary> + /// <returns>A normalized version of the vector.</returns> + public Vector4 Normalized() + { + Vector4 v = this; + v.Normalize(); + return v; + } + + /// <summary> + /// Returns this vector with all components rounded to the nearest integer, + /// with halfway cases rounded towards the nearest multiple of two. + /// </summary> + /// <returns>The rounded vector.</returns> + public Vector4 Round() + { + return new Vector4(Mathf.Round(x), Mathf.Round(y), Mathf.Round(z), Mathf.Round(w)); + } + + /// <summary> + /// Returns a vector with each component set to one or negative one, depending + /// on the signs of this vector's components, or zero if the component is zero, + /// by calling <see cref="Mathf.Sign(real_t)"/> on each component. + /// </summary> + /// <returns>A vector with all components as either <c>1</c>, <c>-1</c>, or <c>0</c>.</returns> + public Vector4 Sign() + { + Vector4 v; + v.x = Mathf.Sign(x); + v.y = Mathf.Sign(y); + v.z = Mathf.Sign(z); + v.w = Mathf.Sign(w); + return v; + } + + // Constants + private static readonly Vector4 _zero = new Vector4(0, 0, 0, 0); + private static readonly Vector4 _one = new Vector4(1, 1, 1, 1); + private static readonly Vector4 _inf = new Vector4(Mathf.Inf, Mathf.Inf, Mathf.Inf, Mathf.Inf); + + /// <summary> + /// Zero vector, a vector with all components set to <c>0</c>. + /// </summary> + /// <value>Equivalent to <c>new Vector4(0, 0, 0, 0)</c>.</value> + public static Vector4 Zero { get { return _zero; } } + /// <summary> + /// One vector, a vector with all components set to <c>1</c>. + /// </summary> + /// <value>Equivalent to <c>new Vector4(1, 1, 1, 1)</c>.</value> + public static Vector4 One { get { return _one; } } + /// <summary> + /// Infinity vector, a vector with all components set to <see cref="Mathf.Inf"/>. + /// </summary> + /// <value>Equivalent to <c>new Vector4(Mathf.Inf, Mathf.Inf, Mathf.Inf, Mathf.Inf)</c>.</value> + public static Vector4 Inf { get { return _inf; } } + + /// <summary> + /// Constructs a new <see cref="Vector4"/> with the given components. + /// </summary> + /// <param name="x">The vector's X component.</param> + /// <param name="y">The vector's Y component.</param> + /// <param name="z">The vector's Z component.</param> + /// <param name="w">The vector's W component.</param> + public Vector4(real_t x, real_t y, real_t z, real_t w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + /// <summary> + /// Constructs a new <see cref="Vector4"/> from an existing <see cref="Vector4"/>. + /// </summary> + /// <param name="v">The existing <see cref="Vector4"/>.</param> + public Vector4(Vector4 v) + { + x = v.x; + y = v.y; + z = v.z; + w = v.w; + } + + /// <summary> + /// Adds each component of the <see cref="Vector4"/> + /// with the components of the given <see cref="Vector4"/>. + /// </summary> + /// <param name="left">The left vector.</param> + /// <param name="right">The right vector.</param> + /// <returns>The added vector.</returns> + public static Vector4 operator +(Vector4 left, Vector4 right) + { + left.x += right.x; + left.y += right.y; + left.z += right.z; + left.w += right.w; + return left; + } + + /// <summary> + /// Subtracts each component of the <see cref="Vector4"/> + /// by the components of the given <see cref="Vector4"/>. + /// </summary> + /// <param name="left">The left vector.</param> + /// <param name="right">The right vector.</param> + /// <returns>The subtracted vector.</returns> + public static Vector4 operator -(Vector4 left, Vector4 right) + { + left.x -= right.x; + left.y -= right.y; + left.z -= right.z; + left.w -= right.w; + return left; + } + + /// <summary> + /// Returns the negative value of the <see cref="Vector4"/>. + /// This is the same as writing <c>new Vector4(-v.x, -v.y, -v.z, -v.w)</c>. + /// This operation flips the direction of the vector while + /// keeping the same magnitude. + /// With floats, the number zero can be either positive or negative. + /// </summary> + /// <param name="vec">The vector to negate/flip.</param> + /// <returns>The negated/flipped vector.</returns> + public static Vector4 operator -(Vector4 vec) + { + vec.x = -vec.x; + vec.y = -vec.y; + vec.z = -vec.z; + vec.w = -vec.w; + return vec; + } + + /// <summary> + /// Multiplies each component of the <see cref="Vector4"/> + /// by the given <see cref="real_t"/>. + /// </summary> + /// <param name="vec">The vector to multiply.</param> + /// <param name="scale">The scale to multiply by.</param> + /// <returns>The multiplied vector.</returns> + public static Vector4 operator *(Vector4 vec, real_t scale) + { + vec.x *= scale; + vec.y *= scale; + vec.z *= scale; + vec.w *= scale; + return vec; + } + + /// <summary> + /// Multiplies each component of the <see cref="Vector4"/> + /// by the given <see cref="real_t"/>. + /// </summary> + /// <param name="scale">The scale to multiply by.</param> + /// <param name="vec">The vector to multiply.</param> + /// <returns>The multiplied vector.</returns> + public static Vector4 operator *(real_t scale, Vector4 vec) + { + vec.x *= scale; + vec.y *= scale; + vec.z *= scale; + vec.w *= scale; + return vec; + } + + /// <summary> + /// Multiplies each component of the <see cref="Vector4"/> + /// by the components of the given <see cref="Vector4"/>. + /// </summary> + /// <param name="left">The left vector.</param> + /// <param name="right">The right vector.</param> + /// <returns>The multiplied vector.</returns> + public static Vector4 operator *(Vector4 left, Vector4 right) + { + left.x *= right.x; + left.y *= right.y; + left.z *= right.z; + left.w *= right.w; + return left; + } + + /// <summary> + /// Divides each component of the <see cref="Vector4"/> + /// by the given <see cref="real_t"/>. + /// </summary> + /// <param name="vec">The dividend vector.</param> + /// <param name="divisor">The divisor value.</param> + /// <returns>The divided vector.</returns> + public static Vector4 operator /(Vector4 vec, real_t divisor) + { + vec.x /= divisor; + vec.y /= divisor; + vec.z /= divisor; + vec.w /= divisor; + return vec; + } + + /// <summary> + /// Divides each component of the <see cref="Vector4"/> + /// by the components of the given <see cref="Vector4"/>. + /// </summary> + /// <param name="vec">The dividend vector.</param> + /// <param name="divisorv">The divisor vector.</param> + /// <returns>The divided vector.</returns> + public static Vector4 operator /(Vector4 vec, Vector4 divisorv) + { + vec.x /= divisorv.x; + vec.y /= divisorv.y; + vec.z /= divisorv.z; + vec.w /= divisorv.w; + return vec; + } + + /// <summary> + /// Returns <see langword="true"/> if the vectors are exactly equal. + /// Note: Due to floating-point precision errors, consider using + /// <see cref="IsEqualApprox"/> instead, which is more reliable. + /// </summary> + /// <param name="left">The left vector.</param> + /// <param name="right">The right vector.</param> + /// <returns>Whether or not the vectors are exactly equal.</returns> + public static bool operator ==(Vector4 left, Vector4 right) + { + return left.Equals(right); + } + + /// <summary> + /// Returns <see langword="true"/> if the vectors are not equal. + /// Note: Due to floating-point precision errors, consider using + /// <see cref="IsEqualApprox"/> instead, which is more reliable. + /// </summary> + /// <param name="left">The left vector.</param> + /// <param name="right">The right vector.</param> + /// <returns>Whether or not the vectors are not equal.</returns> + public static bool operator !=(Vector4 left, Vector4 right) + { + return !left.Equals(right); + } + + /// <summary> + /// Compares two <see cref="Vector4"/> vectors by first checking if + /// the X value of the <paramref name="left"/> vector is less than + /// the X value of the <paramref name="right"/> vector. + /// If the X values are exactly equal, then it repeats this check + /// with the Y, Z and finally W values of the two vectors. + /// This operator is useful for sorting vectors. + /// </summary> + /// <param name="left">The left vector.</param> + /// <param name="right">The right vector.</param> + /// <returns>Whether or not the left is less than the right.</returns> + public static bool operator <(Vector4 left, Vector4 right) + { + if (left.x == right.x) + { + if (left.y == right.y) + { + if (left.z == right.z) + { + return left.w < right.w; + } + return left.z < right.z; + } + return left.y < right.y; + } + return left.x < right.x; + } + + /// <summary> + /// Compares two <see cref="Vector4"/> vectors by first checking if + /// the X value of the <paramref name="left"/> vector is greater than + /// the X value of the <paramref name="right"/> vector. + /// If the X values are exactly equal, then it repeats this check + /// with the Y, Z and finally W values of the two vectors. + /// This operator is useful for sorting vectors. + /// </summary> + /// <param name="left">The left vector.</param> + /// <param name="right">The right vector.</param> + /// <returns>Whether or not the left is greater than the right.</returns> + public static bool operator >(Vector4 left, Vector4 right) + { + if (left.x == right.x) + { + if (left.y == right.y) + { + if (left.z == right.z) + { + return left.w > right.w; + } + return left.z > right.z; + } + return left.y > right.y; + } + return left.x > right.x; + } + + /// <summary> + /// Compares two <see cref="Vector4"/> vectors by first checking if + /// the X value of the <paramref name="left"/> vector is less than + /// or equal to the X value of the <paramref name="right"/> vector. + /// If the X values are exactly equal, then it repeats this check + /// with the Y, Z and finally W values of the two vectors. + /// This operator is useful for sorting vectors. + /// </summary> + /// <param name="left">The left vector.</param> + /// <param name="right">The right vector.</param> + /// <returns>Whether or not the left is less than or equal to the right.</returns> + public static bool operator <=(Vector4 left, Vector4 right) + { + if (left.x == right.x) + { + if (left.y == right.y) + { + if (left.z == right.z) + { + return left.w <= right.w; + } + return left.z < right.z; + } + return left.y < right.y; + } + return left.x < right.x; + } + + /// <summary> + /// Compares two <see cref="Vector4"/> vectors by first checking if + /// the X value of the <paramref name="left"/> vector is greater than + /// or equal to the X value of the <paramref name="right"/> vector. + /// If the X values are exactly equal, then it repeats this check + /// with the Y, Z and finally W values of the two vectors. + /// This operator is useful for sorting vectors. + /// </summary> + /// <param name="left">The left vector.</param> + /// <param name="right">The right vector.</param> + /// <returns>Whether or not the left is greater than or equal to the right.</returns> + public static bool operator >=(Vector4 left, Vector4 right) + { + if (left.x == right.x) + { + if (left.y == right.y) + { + if (left.z == right.z) + { + return left.w >= right.w; + } + return left.z > right.z; + } + return left.y > right.y; + } + return left.x > right.x; + } + + /// <summary> + /// Returns <see langword="true"/> if the vector is exactly equal + /// to the given object (<see paramref="obj"/>). + /// Note: Due to floating-point precision errors, consider using + /// <see cref="IsEqualApprox"/> instead, which is more reliable. + /// </summary> + /// <param name="obj">The object to compare with.</param> + /// <returns>Whether or not the vector and the object are equal.</returns> + public override bool Equals(object obj) + { + if (obj is Vector4) + { + return Equals((Vector4)obj); + } + + return false; + } + + /// <summary> + /// Returns <see langword="true"/> if the vectors are exactly equal. + /// Note: Due to floating-point precision errors, consider using + /// <see cref="IsEqualApprox"/> instead, which is more reliable. + /// </summary> + /// <param name="other">The other vector.</param> + /// <returns>Whether or not the vectors are exactly equal.</returns> + public bool Equals(Vector4 other) + { + return x == other.x && y == other.y && z == other.z && w == other.w; + } + + /// <summary> + /// Returns <see langword="true"/> if this vector and <paramref name="other"/> are approximately equal, + /// by running <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component. + /// </summary> + /// <param name="other">The other vector to compare.</param> + /// <returns>Whether or not the vectors are approximately equal.</returns> + public bool IsEqualApprox(Vector4 other) + { + return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z) && Mathf.IsEqualApprox(w, other.w); + } + + /// <summary> + /// Serves as the hash function for <see cref="Vector4"/>. + /// </summary> + /// <returns>A hash code for this vector.</returns> + public override int GetHashCode() + { + return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode(); + } + + /// <summary> + /// Converts this <see cref="Vector4"/> to a string. + /// </summary> + /// <returns>A string representation of this vector.</returns> + public override string ToString() + { + return $"({x}, {y}, {z}, {w})"; + } + + /// <summary> + /// Converts this <see cref="Vector4"/> to a string with the given <paramref name="format"/>. + /// </summary> + /// <returns>A string representation of this vector.</returns> + public string ToString(string format) + { + return $"({x.ToString(format)}, {y.ToString(format)}, {z.ToString(format)}, {w.ToString(format)})"; + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs new file mode 100644 index 0000000000..365dcef486 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs @@ -0,0 +1,702 @@ +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif +using System; +using System.Runtime.InteropServices; + +namespace Godot +{ + /// <summary> + /// 4-element structure that can be used to represent 4D grid coordinates or sets of integers. + /// </summary> + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public struct Vector4i : IEquatable<Vector4i> + { + /// <summary> + /// Enumerated index values for the axes. + /// Returned by <see cref="MaxAxisIndex"/> and <see cref="MinAxisIndex"/>. + /// </summary> + public enum Axis + { + /// <summary> + /// The vector's X axis. + /// </summary> + X = 0, + /// <summary> + /// The vector's Y axis. + /// </summary> + Y, + /// <summary> + /// The vector's Z axis. + /// </summary> + Z, + /// <summary> + /// The vector's W axis. + /// </summary> + W + } + + /// <summary> + /// The vector's X component. Also accessible by using the index position <c>[0]</c>. + /// </summary> + public int x; + + /// <summary> + /// The vector's Y component. Also accessible by using the index position <c>[1]</c>. + /// </summary> + public int y; + + /// <summary> + /// The vector's Z component. Also accessible by using the index position <c>[2]</c>. + /// </summary> + public int z; + + /// <summary> + /// The vector's W component. Also accessible by using the index position <c>[3]</c>. + /// </summary> + public int w; + + /// <summary> + /// Access vector components using their <paramref name="index"/>. + /// </summary> + /// <exception cref="IndexOutOfRangeException"> + /// Thrown when the given the <paramref name="index"/> is not 0, 1, 2 or 3. + /// </exception> + /// <value> + /// <c>[0]</c> is equivalent to <see cref="x"/>, + /// <c>[1]</c> is equivalent to <see cref="y"/>, + /// <c>[2]</c> is equivalent to <see cref="z"/>. + /// <c>[3]</c> is equivalent to <see cref="w"/>. + /// </value> + public int this[int index] + { + get + { + switch (index) + { + case 0: + return x; + case 1: + return y; + case 2: + return z; + case 3: + return w; + default: + throw new IndexOutOfRangeException(); + } + } + set + { + switch (index) + { + case 0: + x = value; + return; + case 1: + y = value; + return; + case 2: + z = value; + return; + case 3: + w = value; + return; + default: + throw new IndexOutOfRangeException(); + } + } + } + + /// <summary> + /// Helper method for deconstruction into a tuple. + /// </summary> + public void Deconstruct(out int x, out int y, out int z, out int w) + { + x = this.x; + y = this.y; + z = this.z; + w = this.w; + } + + /// <summary> + /// Returns a new vector with all components in absolute values (i.e. positive). + /// </summary> + /// <returns>A vector with <see cref="Mathf.Abs(int)"/> called on each component.</returns> + public Vector4i Abs() + { + return new Vector4i(Mathf.Abs(x), Mathf.Abs(y), Mathf.Abs(z), Mathf.Abs(w)); + } + + /// <summary> + /// Returns a new vector with all components clamped between the + /// components of <paramref name="min"/> and <paramref name="max"/> using + /// <see cref="Mathf.Clamp(int, int, int)"/>. + /// </summary> + /// <param name="min">The vector with minimum allowed values.</param> + /// <param name="max">The vector with maximum allowed values.</param> + /// <returns>The vector with all components clamped.</returns> + public Vector4i Clamp(Vector4i min, Vector4i max) + { + return new Vector4i + ( + Mathf.Clamp(x, min.x, max.x), + Mathf.Clamp(y, min.y, max.y), + Mathf.Clamp(z, min.z, max.z), + Mathf.Clamp(w, min.w, max.w) + ); + } + + /// <summary> + /// Returns the length (magnitude) of this vector. + /// </summary> + /// <seealso cref="LengthSquared"/> + /// <returns>The length of this vector.</returns> + public real_t Length() + { + int x2 = x * x; + int y2 = y * y; + int z2 = z * z; + int w2 = w * w; + + return Mathf.Sqrt(x2 + y2 + z2 + w2); + } + + /// <summary> + /// Returns the squared length (squared magnitude) of this vector. + /// This method runs faster than <see cref="Length"/>, so prefer it if + /// you need to compare vectors or need the squared length for some formula. + /// </summary> + /// <returns>The squared length of this vector.</returns> + public int LengthSquared() + { + int x2 = x * x; + int y2 = y * y; + int z2 = z * z; + int w2 = w * w; + + return x2 + y2 + z2 + w2; + } + + /// <summary> + /// Returns the axis of the vector's highest value. See <see cref="Axis"/>. + /// If all components are equal, this method returns <see cref="Axis.X"/>. + /// </summary> + /// <returns>The index of the highest axis.</returns> + public Axis MaxAxisIndex() + { + int max_index = 0; + int max_value = x; + for (int i = 1; i < 4; i++) + { + if (this[i] > max_value) + { + max_index = i; + max_value = this[i]; + } + } + return (Axis)max_index; + } + + /// <summary> + /// Returns the axis of the vector's lowest value. See <see cref="Axis"/>. + /// If all components are equal, this method returns <see cref="Axis.W"/>. + /// </summary> + /// <returns>The index of the lowest axis.</returns> + public Axis MinAxisIndex() + { + int min_index = 0; + int min_value = x; + for (int i = 1; i < 4; i++) + { + if (this[i] <= min_value) + { + min_index = i; + min_value = this[i]; + } + } + return (Axis)min_index; + } + + /// <summary> + /// Returns a vector with each component set to one or negative one, depending + /// on the signs of this vector's components, or zero if the component is zero, + /// by calling <see cref="Mathf.Sign(int)"/> on each component. + /// </summary> + /// <returns>A vector with all components as either <c>1</c>, <c>-1</c>, or <c>0</c>.</returns> + public Vector4i Sign() + { + return new Vector4i(Mathf.Sign(x), Mathf.Sign(y), Mathf.Sign(z), Mathf.Sign(w)); + } + + // Constants + private static readonly Vector4i _zero = new Vector4i(0, 0, 0, 0); + private static readonly Vector4i _one = new Vector4i(1, 1, 1, 1); + + /// <summary> + /// Zero vector, a vector with all components set to <c>0</c>. + /// </summary> + /// <value>Equivalent to <c>new Vector4i(0, 0, 0, 0)</c>.</value> + public static Vector4i Zero { get { return _zero; } } + /// <summary> + /// One vector, a vector with all components set to <c>1</c>. + /// </summary> + /// <value>Equivalent to <c>new Vector4i(1, 1, 1, 1)</c>.</value> + public static Vector4i One { get { return _one; } } + + /// <summary> + /// Constructs a new <see cref="Vector4i"/> with the given components. + /// </summary> + /// <param name="x">The vector's X component.</param> + /// <param name="y">The vector's Y component.</param> + /// <param name="z">The vector's Z component.</param> + /// <param name="w">The vector's W component.</param> + public Vector4i(int x, int y, int z, int w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + /// <summary> + /// Constructs a new <see cref="Vector4i"/> from an existing <see cref="Vector4i"/>. + /// </summary> + /// <param name="vi">The existing <see cref="Vector4i"/>.</param> + public Vector4i(Vector4i vi) + { + this.x = vi.x; + this.y = vi.y; + this.z = vi.z; + this.w = vi.w; + } + + /// <summary> + /// Constructs a new <see cref="Vector4i"/> from an existing <see cref="Vector4"/> + /// by rounding the components via <see cref="Mathf.RoundToInt(real_t)"/>. + /// </summary> + /// <param name="v">The <see cref="Vector4"/> to convert.</param> + public Vector4i(Vector4 v) + { + this.x = Mathf.RoundToInt(v.x); + this.y = Mathf.RoundToInt(v.y); + this.z = Mathf.RoundToInt(v.z); + this.w = Mathf.RoundToInt(v.w); + } + + /// <summary> + /// Adds each component of the <see cref="Vector4i"/> + /// with the components of the given <see cref="Vector4i"/>. + /// </summary> + /// <param name="left">The left vector.</param> + /// <param name="right">The right vector.</param> + /// <returns>The added vector.</returns> + public static Vector4i operator +(Vector4i left, Vector4i right) + { + left.x += right.x; + left.y += right.y; + left.z += right.z; + left.w += right.w; + return left; + } + + /// <summary> + /// Subtracts each component of the <see cref="Vector4i"/> + /// by the components of the given <see cref="Vector4i"/>. + /// </summary> + /// <param name="left">The left vector.</param> + /// <param name="right">The right vector.</param> + /// <returns>The subtracted vector.</returns> + public static Vector4i operator -(Vector4i left, Vector4i right) + { + left.x -= right.x; + left.y -= right.y; + left.z -= right.z; + left.w -= right.w; + return left; + } + + /// <summary> + /// Returns the negative value of the <see cref="Vector4i"/>. + /// This is the same as writing <c>new Vector4i(-v.x, -v.y, -v.z, -v.w)</c>. + /// This operation flips the direction of the vector while + /// keeping the same magnitude. + /// </summary> + /// <param name="vec">The vector to negate/flip.</param> + /// <returns>The negated/flipped vector.</returns> + public static Vector4i operator -(Vector4i vec) + { + vec.x = -vec.x; + vec.y = -vec.y; + vec.z = -vec.z; + vec.w = -vec.w; + return vec; + } + + /// <summary> + /// Multiplies each component of the <see cref="Vector4i"/> + /// by the given <see langword="int"/>. + /// </summary> + /// <param name="vec">The vector to multiply.</param> + /// <param name="scale">The scale to multiply by.</param> + /// <returns>The multiplied vector.</returns> + public static Vector4i operator *(Vector4i vec, int scale) + { + vec.x *= scale; + vec.y *= scale; + vec.z *= scale; + vec.w *= scale; + return vec; + } + + /// <summary> + /// Multiplies each component of the <see cref="Vector4i"/> + /// by the given <see langword="int"/>. + /// </summary> + /// <param name="scale">The scale to multiply by.</param> + /// <param name="vec">The vector to multiply.</param> + /// <returns>The multiplied vector.</returns> + public static Vector4i operator *(int scale, Vector4i vec) + { + vec.x *= scale; + vec.y *= scale; + vec.z *= scale; + vec.w *= scale; + return vec; + } + + /// <summary> + /// Multiplies each component of the <see cref="Vector4i"/> + /// by the components of the given <see cref="Vector4i"/>. + /// </summary> + /// <param name="left">The left vector.</param> + /// <param name="right">The right vector.</param> + /// <returns>The multiplied vector.</returns> + public static Vector4i operator *(Vector4i left, Vector4i right) + { + left.x *= right.x; + left.y *= right.y; + left.z *= right.z; + left.w *= right.w; + return left; + } + + /// <summary> + /// Divides each component of the <see cref="Vector4i"/> + /// by the given <see langword="int"/>. + /// </summary> + /// <param name="vec">The dividend vector.</param> + /// <param name="divisor">The divisor value.</param> + /// <returns>The divided vector.</returns> + public static Vector4i operator /(Vector4i vec, int divisor) + { + vec.x /= divisor; + vec.y /= divisor; + vec.z /= divisor; + vec.w /= divisor; + return vec; + } + + /// <summary> + /// Divides each component of the <see cref="Vector4i"/> + /// by the components of the given <see cref="Vector4i"/>. + /// </summary> + /// <param name="vec">The dividend vector.</param> + /// <param name="divisorv">The divisor vector.</param> + /// <returns>The divided vector.</returns> + public static Vector4i operator /(Vector4i vec, Vector4i divisorv) + { + vec.x /= divisorv.x; + vec.y /= divisorv.y; + vec.z /= divisorv.z; + vec.w /= divisorv.w; + return vec; + } + + /// <summary> + /// Gets the remainder of each component of the <see cref="Vector4i"/> + /// with the components of the given <see langword="int"/>. + /// This operation uses truncated division, which is often not desired + /// as it does not work well with negative numbers. + /// </summary> + /// <example> + /// <code> + /// GD.Print(new Vecto43i(10, -20, 30, -40) % 7); // Prints "(3, -6, 2, -5)" + /// </code> + /// </example> + /// <param name="vec">The dividend vector.</param> + /// <param name="divisor">The divisor value.</param> + /// <returns>The remainder vector.</returns> + public static Vector4i operator %(Vector4i vec, int divisor) + { + vec.x %= divisor; + vec.y %= divisor; + vec.z %= divisor; + vec.w %= divisor; + return vec; + } + + /// <summary> + /// Gets the remainder of each component of the <see cref="Vector4i"/> + /// with the components of the given <see cref="Vector4i"/>. + /// This operation uses truncated division, which is often not desired + /// as it does not work well with negative numbers. + /// </summary> + /// <example> + /// <code> + /// GD.Print(new Vector4i(10, -20, 30, -40) % new Vector4i(6, 7, 8, 9)); // Prints "(4, -6, 6, -4)" + /// </code> + /// </example> + /// <param name="vec">The dividend vector.</param> + /// <param name="divisorv">The divisor vector.</param> + /// <returns>The remainder vector.</returns> + public static Vector4i operator %(Vector4i vec, Vector4i divisorv) + { + vec.x %= divisorv.x; + vec.y %= divisorv.y; + vec.z %= divisorv.z; + vec.w %= divisorv.w; + return vec; + } + + /// <summary> + /// Performs a bitwise AND operation with this <see cref="Vector4i"/> + /// and the given <see langword="int"/>. + /// </summary> + /// <param name="vec">The vector to AND with.</param> + /// <param name="and">The integer to AND with.</param> + /// <returns>The result of the bitwise AND.</returns> + public static Vector4i operator &(Vector4i vec, int and) + { + vec.x &= and; + vec.y &= and; + vec.z &= and; + vec.w &= and; + return vec; + } + + /// <summary> + /// Performs a bitwise AND operation with this <see cref="Vector4i"/> + /// and the given <see cref="Vector4i"/>. + /// </summary> + /// <param name="vec">The left vector to AND with.</param> + /// <param name="andv">The right vector to AND with.</param> + /// <returns>The result of the bitwise AND.</returns> + public static Vector4i operator &(Vector4i vec, Vector4i andv) + { + vec.x &= andv.x; + vec.y &= andv.y; + vec.z &= andv.z; + vec.w &= andv.w; + return vec; + } + + /// <summary> + /// Returns <see langword="true"/> if the vectors are equal. + /// </summary> + /// <param name="left">The left vector.</param> + /// <param name="right">The right vector.</param> + /// <returns>Whether or not the vectors are equal.</returns> + public static bool operator ==(Vector4i left, Vector4i right) + { + return left.Equals(right); + } + + /// <summary> + /// Returns <see langword="true"/> if the vectors are not equal. + /// </summary> + /// <param name="left">The left vector.</param> + /// <param name="right">The right vector.</param> + /// <returns>Whether or not the vectors are not equal.</returns> + public static bool operator !=(Vector4i left, Vector4i right) + { + return !left.Equals(right); + } + + /// <summary> + /// Compares two <see cref="Vector4i"/> vectors by first checking if + /// the X value of the <paramref name="left"/> vector is less than + /// the X value of the <paramref name="right"/> vector. + /// If the X values are exactly equal, then it repeats this check + /// with the Y, Z and finally W values of the two vectors. + /// This operator is useful for sorting vectors. + /// </summary> + /// <param name="left">The left vector.</param> + /// <param name="right">The right vector.</param> + /// <returns>Whether or not the left is less than the right.</returns> + public static bool operator <(Vector4i left, Vector4i right) + { + if (left.x == right.x) + { + if (left.y == right.y) + { + if (left.z == right.z) + { + return left.w < right.w; + } + return left.z < right.z; + } + return left.y < right.y; + } + return left.x < right.x; + } + + /// <summary> + /// Compares two <see cref="Vector4i"/> vectors by first checking if + /// the X value of the <paramref name="left"/> vector is greater than + /// the X value of the <paramref name="right"/> vector. + /// If the X values are exactly equal, then it repeats this check + /// with the Y, Z and finally W values of the two vectors. + /// This operator is useful for sorting vectors. + /// </summary> + /// <param name="left">The left vector.</param> + /// <param name="right">The right vector.</param> + /// <returns>Whether or not the left is greater than the right.</returns> + public static bool operator >(Vector4i left, Vector4i right) + { + if (left.x == right.x) + { + if (left.y == right.y) + { + if (left.z == right.z) + { + return left.w > right.w; + } + return left.z > right.z; + } + return left.y > right.y; + } + return left.x > right.x; + } + + /// <summary> + /// Compares two <see cref="Vector4i"/> vectors by first checking if + /// the X value of the <paramref name="left"/> vector is less than + /// or equal to the X value of the <paramref name="right"/> vector. + /// If the X values are exactly equal, then it repeats this check + /// with the Y, Z and finally W values of the two vectors. + /// This operator is useful for sorting vectors. + /// </summary> + /// <param name="left">The left vector.</param> + /// <param name="right">The right vector.</param> + /// <returns>Whether or not the left is less than or equal to the right.</returns> + public static bool operator <=(Vector4i left, Vector4i right) + { + if (left.x == right.x) + { + if (left.y == right.y) + { + if (left.z == right.z) + { + return left.w <= right.w; + } + return left.z < right.z; + } + return left.y < right.y; + } + return left.x < right.x; + } + + /// <summary> + /// Compares two <see cref="Vector4i"/> vectors by first checking if + /// the X value of the <paramref name="left"/> vector is greater than + /// or equal to the X value of the <paramref name="right"/> vector. + /// If the X values are exactly equal, then it repeats this check + /// with the Y, Z and finally W values of the two vectors. + /// This operator is useful for sorting vectors. + /// </summary> + /// <param name="left">The left vector.</param> + /// <param name="right">The right vector.</param> + /// <returns>Whether or not the left is greater than or equal to the right.</returns> + public static bool operator >=(Vector4i left, Vector4i right) + { + if (left.x == right.x) + { + if (left.y == right.y) + { + if (left.z == right.z) + { + return left.w >= right.w; + } + return left.z > right.z; + } + return left.y > right.y; + } + return left.x > right.x; + } + + /// <summary> + /// Converts this <see cref="Vector4i"/> to a <see cref="Vector4"/>. + /// </summary> + /// <param name="value">The vector to convert.</param> + public static implicit operator Vector4(Vector4i value) + { + return new Vector4(value.x, value.y, value.z, value.w); + } + + /// <summary> + /// Converts a <see cref="Vector4"/> to a <see cref="Vector4i"/>. + /// </summary> + /// <param name="value">The vector to convert.</param> + public static explicit operator Vector4i(Vector4 value) + { + return new Vector4i(value); + } + + /// <summary> + /// Returns <see langword="true"/> if the vector is equal + /// to the given object (<see paramref="obj"/>). + /// </summary> + /// <param name="obj">The object to compare with.</param> + /// <returns>Whether or not the vector and the object are equal.</returns> + public override bool Equals(object obj) + { + if (obj is Vector4i) + { + return Equals((Vector4i)obj); + } + + return false; + } + + /// <summary> + /// Returns <see langword="true"/> if the vectors are equal. + /// </summary> + /// <param name="other">The other vector.</param> + /// <returns>Whether or not the vectors are equal.</returns> + public bool Equals(Vector4i other) + { + return x == other.x && y == other.y && z == other.z && w == other.w; + } + + /// <summary> + /// Serves as the hash function for <see cref="Vector4i"/>. + /// </summary> + /// <returns>A hash code for this vector.</returns> + public override int GetHashCode() + { + return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode(); + } + + /// <summary> + /// Converts this <see cref="Vector4i"/> to a string. + /// </summary> + /// <returns>A string representation of this vector.</returns> + public override string ToString() + { + return $"({x}, {y}, {z}, {w})"; + } + + /// <summary> + /// Converts this <see cref="Vector4i"/> to a string with the given <paramref name="format"/>. + /// </summary> + /// <returns>A string representation of this vector.</returns> + public string ToString(string format) + { + return $"({x.ToString(format)}, {y.ToString(format)}, {z.ToString(format)}), {w.ToString(format)})"; + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index e59f45bbf6..4f55ce47e8 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -50,6 +50,7 @@ <Compile Include="Core\NodePath.cs" /> <Compile Include="Core\Object.base.cs" /> <Compile Include="Core\Plane.cs" /> + <Compile Include="Core\Projection.cs" /> <Compile Include="Core\Quaternion.cs" /> <Compile Include="Core\Rect2.cs" /> <Compile Include="Core\Rect2i.cs" /> @@ -65,6 +66,8 @@ <Compile Include="Core\Vector2i.cs" /> <Compile Include="Core\Vector3.cs" /> <Compile Include="Core\Vector3i.cs" /> + <Compile Include="Core\Vector4.cs" /> + <Compile Include="Core\Vector4i.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> </ItemGroup> <!-- diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h index 3b6fd25d71..51f11ab18a 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.h +++ b/modules/mono/mono_gd/gd_mono_marshal.h @@ -262,7 +262,7 @@ enum { offsetof(Vector4, z) == (sizeof(real_t) * 2) && offsetof(Vector4, w) == (sizeof(real_t) * 3)), - MATCHES_Vector4i = (MATCHES_int && (sizeof(Vector4i) == (sizeof(int32_t) * 4i)) && + MATCHES_Vector4i = (MATCHES_int && (sizeof(Vector4i) == (sizeof(int32_t) * 4)) && offsetof(Vector4i, x) == (sizeof(int32_t) * 0) && offsetof(Vector4i, y) == (sizeof(int32_t) * 1) && offsetof(Vector4i, z) == (sizeof(int32_t) * 2) && diff --git a/modules/msdfgen/SCsub b/modules/msdfgen/SCsub index 227369f4bd..0c269bc7f4 100644 --- a/modules/msdfgen/SCsub +++ b/modules/msdfgen/SCsub @@ -36,7 +36,7 @@ if env["builtin_msdfgen"]: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_msdfgen.Append(CPPPATH=["#thirdparty/freetype/include", "#thirdparty/msdfgen", "#thirdparty/nanosvg"]) + env_msdfgen.Prepend(CPPPATH=["#thirdparty/freetype/include", "#thirdparty/msdfgen", "#thirdparty/nanosvg"]) lib = env_msdfgen.add_library("msdfgen_builtin", thirdparty_sources) thirdparty_obj += lib diff --git a/modules/multiplayer/doc_classes/MultiplayerSpawner.xml b/modules/multiplayer/doc_classes/MultiplayerSpawner.xml index 44ab34f52c..881796ed26 100644 --- a/modules/multiplayer/doc_classes/MultiplayerSpawner.xml +++ b/modules/multiplayer/doc_classes/MultiplayerSpawner.xml @@ -1,65 +1,83 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="MultiplayerSpawner" inherits="Node" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> + Automatically replicates spawnable nodes from the authority to other multiplayer peers. </brief_description> <description> - This node uses [method MultiplayerAPI.object_configuration_add] to notify spawns passing the spawned node as the [code]object[/code] and itself as the [code]configuration[/code], and [method MultiplayerAPI.object_configuration_remove] to notify despawns in a similar way. + Spawnable scenes can be configured in the editor or through code (see [method add_spawnable_scene]). + Also supports custom node spawns through [method spawn], calling [method _spawn_custom] on all peers. + + Internally, [MultiplayerSpawner] uses [method MultiplayerAPI.object_configuration_add] to notify spawns passing the spawned node as the [code]object[/code] and itself as the [code]configuration[/code], and [method MultiplayerAPI.object_configuration_remove] to notify despawns in a similar way. </description> <tutorials> </tutorials> <methods> <method name="_spawn_custom" qualifiers="virtual"> - <return type="Object" /> + <return type="Node" /> <argument index="0" name="data" type="Variant" /> <description> + Method called on all peers when a custom spawn was requested by the authority using [method spawn]. Should return a [Node] that is not in the scene tree. + + [b]Note:[/b] Spawned nodes should [b]not[/b] be added to the scene with `add_child`. This is done automatically. </description> </method> <method name="add_spawnable_scene"> <return type="void" /> <argument index="0" name="path" type="String" /> <description> + Adds a scene path to spawnable scenes, making it automatically replicated from the multiplayer authority to other peers when added as children of the node pointed by [member spawn_path]. </description> </method> <method name="clear_spawnable_scenes"> <return type="void" /> <description> + Clears all spawnable scenes. Does not despawn existing instances on remote peers. </description> </method> <method name="get_spawnable_scene" qualifiers="const"> <return type="String" /> - <argument index="0" name="path" type="int" /> + <argument index="0" name="index" type="int" /> <description> + Returns the spawnable scene path by index. </description> </method> <method name="get_spawnable_scene_count" qualifiers="const"> <return type="int" /> <description> + Returns the count of spawnable scene paths. </description> </method> <method name="spawn"> <return type="Node" /> <argument index="0" name="data" type="Variant" default="null" /> <description> + Requests a custom spawn, with [code]data[/code] passed to [method _spawn_custom] on all peers. Returns the locally spawned node instance already inside the scene tree, and added as a child of the node pointed by [member spawn_path]. + + [b]Note:[/b] Spawnable scenes are spawned automatically. [method spawn] is only needed for custom spawns. </description> </method> </methods> <members> <member name="spawn_limit" type="int" setter="set_spawn_limit" getter="get_spawn_limit" default="0"> + Maximum nodes that is allowed to be spawned by this spawner. Includes both spawnable scenes and custom spawns. + + When set to [code]0[/code] (the default), there is no limit. </member> <member name="spawn_path" type="NodePath" setter="set_spawn_path" getter="get_spawn_path" default="NodePath("")"> + Path to the spawn root. Spawnable scenes that are added as direct children are replicated to other peers. </member> </members> <signals> <signal name="despawned"> - <argument index="0" name="scene_id" type="int" /> - <argument index="1" name="node" type="Node" /> + <argument index="0" name="node" type="Node" /> <description> + Emitted when a spawnable scene or custom spawn was despawned by the multiplayer authority. Only called on puppets. </description> </signal> <signal name="spawned"> - <argument index="0" name="scene_id" type="int" /> - <argument index="1" name="node" type="Node" /> + <argument index="0" name="node" type="Node" /> <description> + Emitted when a spawnable scene or custom spawn was spawned by the multiplayer authority. Only called on puppets. </description> </signal> </signals> diff --git a/modules/multiplayer/doc_classes/MultiplayerSynchronizer.xml b/modules/multiplayer/doc_classes/MultiplayerSynchronizer.xml index ebd1b50201..a2ea64061c 100644 --- a/modules/multiplayer/doc_classes/MultiplayerSynchronizer.xml +++ b/modules/multiplayer/doc_classes/MultiplayerSynchronizer.xml @@ -1,9 +1,15 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="MultiplayerSynchronizer" inherits="Node" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> + Synchronizes properties from the multiplayer authority to the remote peers. </brief_description> <description> - The [MultiplayerSynchronizer] uses [method MultiplayerAPI.object_configuration_add] to notify synchronization start passing the [Node] at [member root_path] as the [code]object[/code] and itself as the [code]configuration[/code], and uses [method MultiplayerAPI.object_configuration_remove] to notify synchronization end in a similar way. + By default, [MultiplayerSynchronizer] synchronizes configured properties to all peers. + Visiblity can be handled directly with [method set_visibility_for] or as-needed with [method add_visibility_filter] and [method update_visibility]. + + [MultiplayerSpawner]s will handle nodes according to visibility of synchronizers as long as the node at [member root_path] was spawned by one. + + Internally, [MultiplayerSynchronizer] uses [method MultiplayerAPI.object_configuration_add] to notify synchronization start passing the [Node] at [member root_path] as the [code]object[/code] and itself as the [code]configuration[/code], and uses [method MultiplayerAPI.object_configuration_remove] to notify synchronization end in a similar way. </description> <tutorials> </tutorials> @@ -12,18 +18,23 @@ <return type="void" /> <argument index="0" name="filter" type="Callable" /> <description> + Adds a peer visibility filter for this synchronizer. + + [code]filter[/code] should take a peer id [int] and return a [bool]. </description> </method> <method name="get_visibility_for" qualifiers="const"> <return type="bool" /> <argument index="0" name="peer" type="int" /> <description> + Queries the current visibility for peer [code]peer[/code]. </description> </method> <method name="remove_visibility_filter"> <return type="void" /> <argument index="0" name="filter" type="Callable" /> <description> + Removes a peer visiblity filter from this synchronizer. </description> </method> <method name="set_visibility_for"> @@ -31,40 +42,52 @@ <argument index="0" name="peer" type="int" /> <argument index="1" name="visible" type="bool" /> <description> + Sets the visibility of [code]peer[/code] to [code]visible[/code]. If [code]peer[/code] is [code]0[/code], the value of [member public_visibility] will be updated instead. </description> </method> <method name="update_visibility"> <return type="void" /> <argument index="0" name="for_peer" type="int" default="0" /> <description> + Updates the visibility of [code]peer[/code] according to visibility filters. If [code]peer[/code] is [code]0[/code] (the default), all peers' visibilties are updated. </description> </method> </methods> <members> <member name="public_visibility" type="bool" setter="set_visibility_public" getter="is_visibility_public" default="true"> + Whether synchronization should be visible to all peers by default. See [method set_visibility_for] and [method add_visibility_filter] for ways of configuring fine-grained visibility options. </member> <member name="replication_config" type="SceneReplicationConfig" setter="set_replication_config" getter="get_replication_config"> + Resource containing which properties to synchronize. </member> <member name="replication_interval" type="float" setter="set_replication_interval" getter="get_replication_interval" default="0.0"> + Time interval between synchronizes. When set to [code]0.0[/code] (the default), synchronizes happen every network process frame. </member> <member name="root_path" type="NodePath" setter="set_root_path" getter="get_root_path" default="NodePath("..")"> + Node path that replicated properties are relative to. + If [member root_path] was spawned by a [MultiplayerSpawner], the node will be also be spawned and despawned based on this synchronizer visibility options. </member> <member name="visibility_update_mode" type="int" setter="set_visibility_update_mode" getter="get_visibility_update_mode" enum="MultiplayerSynchronizer.VisibilityUpdateMode" default="0"> + Specifies when visibility filters are updated (see [enum VisibilityUpdateMode] for options). </member> </members> <signals> <signal name="visibility_changed"> <argument index="0" name="for_peer" type="int" /> <description> + Emitted when visibility of [code]for_peer[/code] is updated. See [method update_visibility]. </description> </signal> </signals> <constants> <constant name="VISIBILITY_PROCESS_IDLE" value="0" enum="VisibilityUpdateMode"> + Visibility filters are updated every idle process frame. </constant> <constant name="VISIBILITY_PROCESS_PHYSICS" value="1" enum="VisibilityUpdateMode"> + Visibility filters are updated every physics process frame. </constant> <constant name="VISIBILITY_PROCESS_NONE" value="2" enum="VisibilityUpdateMode"> + Visibility filters are not updated automatically, and must be updated manually by calling [method update_visibility]. </constant> </constants> </class> diff --git a/modules/multiplayer/doc_classes/SceneReplicationConfig.xml b/modules/multiplayer/doc_classes/SceneReplicationConfig.xml index 1d6dec2f92..fc91592c7a 100644 --- a/modules/multiplayer/doc_classes/SceneReplicationConfig.xml +++ b/modules/multiplayer/doc_classes/SceneReplicationConfig.xml @@ -1,6 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="SceneReplicationConfig" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> + Configuration for properties to synchronize with a [MultiplayerSynchronizer]. </brief_description> <description> </description> @@ -12,35 +13,41 @@ <argument index="0" name="path" type="NodePath" /> <argument index="1" name="index" type="int" default="-1" /> <description> + Adds the property identified by the given [code]path[/code] to the list of the properties being synchronized, optionally passing an [code]index[/code]. </description> </method> <method name="get_properties" qualifiers="const"> <return type="NodePath[]" /> <description> + Returns a list of synchronized property [NodePath]s. </description> </method> <method name="has_property" qualifiers="const"> <return type="bool" /> <argument index="0" name="path" type="NodePath" /> <description> + Returns whether the given [code]path[/code] is configured for synchronization. </description> </method> <method name="property_get_index" qualifiers="const"> <return type="int" /> <argument index="0" name="path" type="NodePath" /> <description> + Finds the index of the given [code]path[/code]. </description> </method> <method name="property_get_spawn"> <return type="bool" /> <argument index="0" name="path" type="NodePath" /> <description> + Returns whether the property identified by the given [code]path[/code] is configured to be synchronized on spawn. </description> </method> <method name="property_get_sync"> <return type="bool" /> <argument index="0" name="path" type="NodePath" /> <description> + Returns whether the property identified by the given [code]path[/code] is configured to be synchronized on process. </description> </method> <method name="property_set_spawn"> @@ -48,6 +55,7 @@ <argument index="0" name="path" type="NodePath" /> <argument index="1" name="enabled" type="bool" /> <description> + Sets whether the property identified by the given [code]path[/code] is configured to be synchronized on spawn. </description> </method> <method name="property_set_sync"> @@ -55,12 +63,14 @@ <argument index="0" name="path" type="NodePath" /> <argument index="1" name="enabled" type="bool" /> <description> + Sets whether the property identified by the given [code]path[/code] is configured to be synchronized on process. </description> </method> <method name="remove_property"> <return type="void" /> <argument index="0" name="path" type="NodePath" /> <description> + Removes the property identified by the given [code]path[/code] from the configuration. </description> </method> </methods> diff --git a/modules/multiplayer/editor/replication_editor_plugin.h b/modules/multiplayer/editor/replication_editor_plugin.h index 57fa4c82fa..0bb2cb888d 100644 --- a/modules/multiplayer/editor/replication_editor_plugin.h +++ b/modules/multiplayer/editor/replication_editor_plugin.h @@ -34,7 +34,6 @@ #include "editor/editor_plugin.h" #include "editor/editor_spin_slider.h" -#include "editor/property_editor.h" #include "editor/property_selector.h" #include "../scene_replication_config.h" diff --git a/modules/multiplayer/multiplayer_spawner.cpp b/modules/multiplayer/multiplayer_spawner.cpp index f5edffbb0c..e8f3aecc69 100644 --- a/modules/multiplayer/multiplayer_spawner.cpp +++ b/modules/multiplayer/multiplayer_spawner.cpp @@ -126,7 +126,7 @@ void MultiplayerSpawner::_set_spawnable_scenes(const Vector<String> &p_scenes) { void MultiplayerSpawner::_bind_methods() { ClassDB::bind_method(D_METHOD("add_spawnable_scene", "path"), &MultiplayerSpawner::add_spawnable_scene); ClassDB::bind_method(D_METHOD("get_spawnable_scene_count"), &MultiplayerSpawner::get_spawnable_scene_count); - ClassDB::bind_method(D_METHOD("get_spawnable_scene", "path"), &MultiplayerSpawner::get_spawnable_scene); + ClassDB::bind_method(D_METHOD("get_spawnable_scene", "index"), &MultiplayerSpawner::get_spawnable_scene); ClassDB::bind_method(D_METHOD("clear_spawnable_scenes"), &MultiplayerSpawner::clear_spawnable_scenes); ClassDB::bind_method(D_METHOD("_get_spawnable_scenes"), &MultiplayerSpawner::_get_spawnable_scenes); @@ -146,8 +146,8 @@ void MultiplayerSpawner::_bind_methods() { GDVIRTUAL_BIND(_spawn_custom, "data"); - ADD_SIGNAL(MethodInfo("despawned", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); - ADD_SIGNAL(MethodInfo("spawned", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); + ADD_SIGNAL(MethodInfo("despawned", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); + ADD_SIGNAL(MethodInfo("spawned", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); } void MultiplayerSpawner::_update_spawn_node() { @@ -277,12 +277,11 @@ Node *MultiplayerSpawner::instantiate_scene(int p_id) { Node *MultiplayerSpawner::instantiate_custom(const Variant &p_data) { ERR_FAIL_COND_V_MSG(spawn_limit && spawn_limit <= tracked_nodes.size(), nullptr, "Spawn limit reached!"); - Object *obj = nullptr; Node *node = nullptr; - if (GDVIRTUAL_CALL(_spawn_custom, p_data, obj)) { - node = Object::cast_to<Node>(obj); + if (GDVIRTUAL_CALL(_spawn_custom, p_data, node)) { + return node; } - return node; + ERR_FAIL_V_MSG(nullptr, "Method '_spawn_custom' is not implemented on this peer."); } Node *MultiplayerSpawner::spawn(const Variant &p_data) { diff --git a/modules/multiplayer/multiplayer_spawner.h b/modules/multiplayer/multiplayer_spawner.h index 80bb878a74..fc3befc2d4 100644 --- a/modules/multiplayer/multiplayer_spawner.h +++ b/modules/multiplayer/multiplayer_spawner.h @@ -91,7 +91,9 @@ protected: void _get_property_list(List<PropertyInfo> *p_list) const; #endif public: - Node *get_spawn_node() const { return spawn_node.is_valid() ? Object::cast_to<Node>(ObjectDB::get_instance(spawn_node)) : nullptr; } + Node *get_spawn_node() const { + return spawn_node.is_valid() ? Object::cast_to<Node>(ObjectDB::get_instance(spawn_node)) : nullptr; + } void add_spawnable_scene(const String &p_path); int get_spawnable_scene_count() const; @@ -110,7 +112,7 @@ public: Node *instantiate_custom(const Variant &p_data); Node *instantiate_scene(int p_idx); - GDVIRTUAL1R(Object *, _spawn_custom, const Variant &); + GDVIRTUAL1R(Node *, _spawn_custom, const Variant &); MultiplayerSpawner() {} }; diff --git a/modules/multiplayer/scene_replication_interface.cpp b/modules/multiplayer/scene_replication_interface.cpp index c89270fbe8..6e3dbfab47 100644 --- a/modules/multiplayer/scene_replication_interface.cpp +++ b/modules/multiplayer/scene_replication_interface.cpp @@ -396,6 +396,8 @@ Error SceneReplicationInterface::on_spawn_receive(int p_from, const uint8_t *p_b pending_buffer_size = state_len; } parent->add_child(node); + spawner->emit_signal(SNAME("spawned"), node); + pending_spawn = ObjectID(); pending_buffer = nullptr; pending_buffer_size = 0; @@ -411,10 +413,17 @@ Error SceneReplicationInterface::on_despawn_receive(int p_from, const uint8_t *p Error err = rep_state->peer_del_remote(p_from, net_id, &node); ERR_FAIL_COND_V(err != OK, err); ERR_FAIL_COND_V(!node, ERR_BUG); + + MultiplayerSpawner *spawner = rep_state->get_spawner(node->get_instance_id()); + ERR_FAIL_COND_V(!spawner, ERR_DOES_NOT_EXIST); + ERR_FAIL_COND_V(p_from != spawner->get_multiplayer_authority(), ERR_UNAUTHORIZED); + if (node->get_parent() != nullptr) { node->get_parent()->remove_child(node); } node->queue_delete(); + spawner->emit_signal(SNAME("despawned"), node); + return OK; } diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub index 2d764a4006..d212fe62b4 100644 --- a/modules/text_server_adv/SCsub +++ b/modules/text_server_adv/SCsub @@ -117,11 +117,11 @@ if env["builtin_harfbuzz"]: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_harfbuzz.Append(CPPPATH=["#thirdparty/harfbuzz/src"]) + env_harfbuzz.Prepend(CPPPATH=["#thirdparty/harfbuzz/src"]) env_harfbuzz.Append(CCFLAGS=["-DHAVE_ICU"]) if env["builtin_icu"]: - env_harfbuzz.Append(CPPPATH=["#thirdparty/icu4c/common/"]) + env_harfbuzz.Prepend(CPPPATH=["#thirdparty/icu4c/common/"]) env_harfbuzz.Append(CCFLAGS=["-DU_HAVE_LIB_SUFFIX=1", "-DU_LIB_SUFFIX_C_NAME=_godot", "-DHAVE_ICU_BUILTIN"]) if freetype_enabled: @@ -132,9 +132,9 @@ if env["builtin_harfbuzz"]: ] ) if env["builtin_freetype"]: - env_harfbuzz.Append(CPPPATH=["#thirdparty/freetype/include"]) + env_harfbuzz.Prepend(CPPPATH=["#thirdparty/freetype/include"]) if env["builtin_graphite"]: - env_harfbuzz.Append(CPPPATH=["#thirdparty/graphite/include"]) + env_harfbuzz.Prepend(CPPPATH=["#thirdparty/graphite/include"]) env_harfbuzz.Append(CCFLAGS=["-DGRAPHITE2_STATIC"]) if env["platform"] == "android" or env["platform"] == "linuxbsd": @@ -146,7 +146,7 @@ if env["builtin_harfbuzz"]: else: env_harfbuzz.Append(CCFLAGS=["-DHB_NO_MT"]) - env_text_server_adv.Append(CPPPATH=["#thirdparty/harfbuzz/src"]) + env_text_server_adv.Prepend(CPPPATH=["#thirdparty/harfbuzz/src"]) lib = env_harfbuzz.add_library("harfbuzz_builtin", thirdparty_sources) thirdparty_obj += lib @@ -209,7 +209,7 @@ if env["builtin_graphite"] and freetype_enabled: thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_graphite.Append(CPPPATH=["#thirdparty/graphite/src", "#thirdparty/graphite/include"]) + env_graphite.Prepend(CPPPATH=["#thirdparty/graphite/src", "#thirdparty/graphite/include"]) env_graphite.Append( CCFLAGS=[ "-DGRAPHITE2_STATIC", @@ -447,11 +447,11 @@ if env["builtin_icu"]: if env_icu["tools"]: env_icu.Depends("#thirdparty/icu4c/icudata.gen.h", "#thirdparty/icu4c/" + icu_data_name) env_icu.Command("#thirdparty/icu4c/icudata.gen.h", "#thirdparty/icu4c/" + icu_data_name, make_icu_data) - env_text_server_adv.Append(CPPPATH=["#thirdparty/icu4c/"]) + env_text_server_adv.Prepend(CPPPATH=["#thirdparty/icu4c/"]) else: thirdparty_sources += ["icu_data/icudata_stub.cpp"] - env_icu.Append(CPPPATH=["#thirdparty/icu4c/common/"]) + env_icu.Prepend(CPPPATH=["#thirdparty/icu4c/common/"]) env_icu.Append( CXXFLAGS=[ "-DU_STATIC_IMPLEMENTATION", @@ -480,7 +480,7 @@ if env["builtin_icu"]: if env_text_server_adv["tools"]: env_text_server_adv.Append(CXXFLAGS=["-DICU_STATIC_DATA"]) - env_text_server_adv.Append(CPPPATH=["#thirdparty/icu4c/common/"]) + env_text_server_adv.Prepend(CPPPATH=["#thirdparty/icu4c/common/"]) lib = env_icu.add_library("icu_builtin", thirdparty_sources) thirdparty_obj += lib @@ -504,13 +504,13 @@ if env["builtin_icu"]: module_obj = [] if env["builtin_msdfgen"] and msdfgen_enabled: - env_text_server_adv.Append(CPPPATH=["#thirdparty/msdfgen"]) + env_text_server_adv.Prepend(CPPPATH=["#thirdparty/msdfgen"]) if env["builtin_freetype"] and freetype_enabled: - env_text_server_adv.Append(CPPPATH=["#thirdparty/freetype/include"]) + env_text_server_adv.Prepend(CPPPATH=["#thirdparty/freetype/include"]) if env["builtin_graphite"] and freetype_enabled: - env_text_server_adv.Append(CPPPATH=["#thirdparty/graphite/include"]) + env_text_server_adv.Prepend(CPPPATH=["#thirdparty/graphite/include"]) env_text_server_adv.add_source_files(module_obj, "*.cpp") env.modules_sources += module_obj diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index fa234081f0..64788bfeff 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -1359,7 +1359,13 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f fd->underline_position = (-FT_MulFix(fd->face->underline_position, fd->face->size->metrics.y_scale) / 64.0) / fd->oversampling * fd->scale; fd->underline_thickness = (FT_MulFix(fd->face->underline_thickness, fd->face->size->metrics.y_scale) / 64.0) / fd->oversampling * fd->scale; +#if HB_VERSION_ATLEAST(3, 3, 0) hb_font_set_synthetic_slant(fd->hb_handle, p_font_data->transform[0][1]); +#else +#ifndef _MSC_VER +#warning Building with HarfBuzz < 3.3.0, synthetic slant offset correction disabled. +#endif +#endif if (!p_font_data->face_init) { // Get style flags and name. @@ -1626,6 +1632,7 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f for (unsigned int i = 0; i < count; i++) { Dictionary ftr; +#if HB_VERSION_ATLEAST(2, 1, 0) hb_ot_name_id_t lbl_id; if (hb_ot_layout_feature_get_name_ids(hb_face, HB_OT_TAG_GSUB, i, &lbl_id, nullptr, nullptr, nullptr, nullptr)) { PackedInt32Array lbl; @@ -1635,6 +1642,11 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f hb_ot_name_get_utf32(hb_face, lbl_id, hb_language_from_string(TranslationServer::get_singleton()->get_tool_locale().ascii().get_data(), -1), &text_size, (uint32_t *)lbl.ptrw()); ftr["label"] = String((const char32_t *)lbl.ptr()); } +#else +#ifndef _MSC_VER +#warning Building with HarfBuzz < 2.1.0, readable OpenType feature names disabled. +#endif +#endif ftr["type"] = _get_tag_type(feature_tags[i]); ftr["hidden"] = _get_tag_hidden(feature_tags[i]); @@ -1649,6 +1661,7 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f for (unsigned int i = 0; i < count; i++) { Dictionary ftr; +#if HB_VERSION_ATLEAST(2, 1, 0) hb_ot_name_id_t lbl_id; if (hb_ot_layout_feature_get_name_ids(hb_face, HB_OT_TAG_GPOS, i, &lbl_id, nullptr, nullptr, nullptr, nullptr)) { PackedInt32Array lbl; @@ -1658,6 +1671,11 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f hb_ot_name_get_utf32(hb_face, lbl_id, hb_language_from_string(TranslationServer::get_singleton()->get_tool_locale().ascii().get_data(), -1), &text_size, (uint32_t *)lbl.ptrw()); ftr["label"] = String((const char32_t *)lbl.ptr()); } +#else +#ifndef _MSC_VER +#warning Building with HarfBuzz < 2.1.0, readable OpenType feature names disabled. +#endif +#endif ftr["type"] = _get_tag_type(feature_tags[i]); ftr["hidden"] = _get_tag_hidden(feature_tags[i]); diff --git a/modules/text_server_fb/SCsub b/modules/text_server_fb/SCsub index 121f38fcd5..ca9322e450 100644 --- a/modules/text_server_fb/SCsub +++ b/modules/text_server_fb/SCsub @@ -9,9 +9,9 @@ msdfgen_enabled = env.module_check_dependencies("text_server_fb", ["msdfgen"], T env_text_server_fb = env_modules.Clone() if env["builtin_msdfgen"] and msdfgen_enabled: - env_text_server_fb.Append(CPPPATH=["#thirdparty/msdfgen"]) + env_text_server_fb.Prepend(CPPPATH=["#thirdparty/msdfgen"]) if env["builtin_freetype"] and freetype_enabled: - env_text_server_fb.Append(CPPPATH=["#thirdparty/freetype/include"]) + env_text_server_fb.Prepend(CPPPATH=["#thirdparty/freetype/include"]) env_text_server_fb.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/visual_script/editor/visual_script_property_selector.cpp b/modules/visual_script/editor/visual_script_property_selector.cpp index f98ce5bb26..712c89368b 100644 --- a/modules/visual_script/editor/visual_script_property_selector.cpp +++ b/modules/visual_script/editor/visual_script_property_selector.cpp @@ -39,6 +39,7 @@ #include "editor/doc_tools.h" #include "editor/editor_feature_profile.h" #include "editor/editor_scale.h" +#include "editor/editor_settings.h" #include "scene/main/node.h" #include "scene/main/window.h" diff --git a/modules/visual_script/editor/visual_script_property_selector.h b/modules/visual_script/editor/visual_script_property_selector.h index 41f8eea735..4de626467e 100644 --- a/modules/visual_script/editor/visual_script_property_selector.h +++ b/modules/visual_script/editor/visual_script_property_selector.h @@ -33,8 +33,8 @@ #include "../visual_script.h" #include "editor/editor_help.h" -#include "editor/property_editor.h" #include "scene/gui/rich_text_label.h" +#include "scene/gui/tree.h" class VisualScriptPropertySelector : public ConfirmationDialog { GDCLASS(VisualScriptPropertySelector, ConfirmationDialog); diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index 85e8621aa0..9ca2948542 100644 --- a/platform/ios/export/export_plugin.cpp +++ b/platform/ios/export/export_plugin.cpp @@ -30,6 +30,7 @@ #include "export_plugin.h" +#include "core/string/translation.h" #include "editor/editor_node.h" void EditorExportPlatformIOS::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const { diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp index 825c1b6638..ea236f62f7 100644 --- a/platform/javascript/export/export.cpp +++ b/platform/javascript/export/export.cpp @@ -30,6 +30,7 @@ #include "export.h" +#include "editor/editor_settings.h" #include "export_plugin.h" void register_javascript_exporter() { diff --git a/platform/javascript/export/export_plugin.cpp b/platform/javascript/export/export_plugin.cpp index a9912edef7..b99f88d067 100644 --- a/platform/javascript/export/export_plugin.cpp +++ b/platform/javascript/export/export_plugin.cpp @@ -31,6 +31,7 @@ #include "export_plugin.h" #include "core/config/project_settings.h" +#include "editor/editor_settings.h" Error EditorExportPlatformJavaScript::_extract_template(const String &p_template, const String &p_dir, const String &p_name, bool pwa) { Ref<FileAccess> io_fa; diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp index 2ec2bb92d3..bcc2636c07 100644 --- a/platform/macos/export/export_plugin.cpp +++ b/platform/macos/export/export_plugin.cpp @@ -32,6 +32,7 @@ #include "codesign.h" +#include "core/string/translation.h" #include "editor/editor_node.h" #include "editor/editor_paths.h" diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp index efba006985..31105824a5 100644 --- a/platform/uwp/export/export.cpp +++ b/platform/uwp/export/export.cpp @@ -30,6 +30,7 @@ #include "export.h" +#include "editor/editor_settings.h" #include "export_plugin.h" void register_uwp_exporter() { diff --git a/platform/uwp/export/export_plugin.cpp b/platform/uwp/export/export_plugin.cpp index a99776497b..070c46242f 100644 --- a/platform/uwp/export/export_plugin.cpp +++ b/platform/uwp/export/export_plugin.cpp @@ -30,6 +30,7 @@ #include "export_plugin.h" +#include "editor/editor_settings.h" #include "platform/uwp/logo.gen.h" String EditorExportPlatformUWP::get_name() const { diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index f51031765c..42f434f0ac 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -710,6 +710,9 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { int type = gn->get_connection_output_type(j); if ((type == connecting_type || valid_connection_types.has(ConnType(connecting_type, type))) && is_in_output_hotzone(gn, j, mpos, port_size)) { + if (!is_node_hover_valid(gn->get_name(), j, connecting_from, connecting_index)) { + continue; + } connecting_target = true; connecting_to = pos; connecting_target_to = gn->get_name(); @@ -725,6 +728,9 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { int type = gn->get_connection_input_type(j); if ((type == connecting_type || valid_connection_types.has(ConnType(connecting_type, type))) && is_in_input_hotzone(gn, j, mpos, port_size)) { + if (!is_node_hover_valid(connecting_from, connecting_index, gn->get_name(), j)) { + continue; + } connecting_target = true; connecting_to = pos; connecting_target_to = gn->get_name(); @@ -1453,6 +1459,14 @@ void GraphEdit::force_connection_drag_end() { emit_signal(SNAME("connection_drag_ended")); } +bool GraphEdit::is_node_hover_valid(const StringName &p_from, const int p_from_port, const StringName &p_to, const int p_to_port) { + bool valid; + if (GDVIRTUAL_CALL(_is_node_hover_valid, p_from, p_from_port, p_to, p_to_port, valid)) { + return valid; + } + return true; +} + void GraphEdit::set_panning_scheme(PanningScheme p_scheme) { panning_scheme = p_scheme; panner->set_control_scheme((ViewPanner::ControlScheme)p_scheme); @@ -2315,6 +2329,7 @@ void GraphEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_selected", "node"), &GraphEdit::set_selected); GDVIRTUAL_BIND(_get_connection_line, "from", "to") + GDVIRTUAL_BIND(_is_node_hover_valid, "from", "from_slot", "to", "to_slot"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "right_disconnects"), "set_right_disconnects", "is_right_disconnects_enabled"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scroll_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_scroll_ofs", "get_scroll_ofs"); diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index beb17ec4cf..cf35aeb8b2 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -280,6 +280,7 @@ protected: GDVIRTUAL2RC(Vector<Vector2>, _get_connection_line, Vector2, Vector2) GDVIRTUAL3R(bool, _is_in_input_hotzone, Object *, int, Vector2) GDVIRTUAL3R(bool, _is_in_output_hotzone, Object *, int, Vector2) + GDVIRTUAL4R(bool, _is_node_hover_valid, StringName, int, StringName, int); public: Error connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port); @@ -287,6 +288,7 @@ public: void disconnect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port); void clear_connections(); void force_connection_drag_end(); + virtual bool is_node_hover_valid(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port); void set_connection_activity(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, float p_activity); diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index 19545167c8..69b30b72b0 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -1563,33 +1563,35 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { return -1; } -void Animation::track_insert_key(int p_track, double p_time, const Variant &p_key, real_t p_transition) { - ERR_FAIL_INDEX(p_track, tracks.size()); +int Animation::track_insert_key(int p_track, double p_time, const Variant &p_key, real_t p_transition) { + ERR_FAIL_INDEX_V(p_track, tracks.size(), -1); Track *t = tracks[p_track]; + int ret = -1; + switch (t->type) { case TYPE_POSITION_3D: { - ERR_FAIL_COND((p_key.get_type() != Variant::VECTOR3) && (p_key.get_type() != Variant::VECTOR3I)); - int idx = position_track_insert_key(p_track, p_time, p_key); - track_set_key_transition(p_track, idx, p_transition); + ERR_FAIL_COND_V((p_key.get_type() != Variant::VECTOR3) && (p_key.get_type() != Variant::VECTOR3I), -1); + ret = position_track_insert_key(p_track, p_time, p_key); + track_set_key_transition(p_track, ret, p_transition); } break; case TYPE_ROTATION_3D: { - ERR_FAIL_COND((p_key.get_type() != Variant::QUATERNION) && (p_key.get_type() != Variant::BASIS)); - int idx = rotation_track_insert_key(p_track, p_time, p_key); - track_set_key_transition(p_track, idx, p_transition); + ERR_FAIL_COND_V((p_key.get_type() != Variant::QUATERNION) && (p_key.get_type() != Variant::BASIS), -1); + ret = rotation_track_insert_key(p_track, p_time, p_key); + track_set_key_transition(p_track, ret, p_transition); } break; case TYPE_SCALE_3D: { - ERR_FAIL_COND((p_key.get_type() != Variant::VECTOR3) && (p_key.get_type() != Variant::VECTOR3I)); - int idx = scale_track_insert_key(p_track, p_time, p_key); - track_set_key_transition(p_track, idx, p_transition); + ERR_FAIL_COND_V((p_key.get_type() != Variant::VECTOR3) && (p_key.get_type() != Variant::VECTOR3I), -1); + ret = scale_track_insert_key(p_track, p_time, p_key); + track_set_key_transition(p_track, ret, p_transition); } break; case TYPE_BLEND_SHAPE: { - ERR_FAIL_COND((p_key.get_type() != Variant::FLOAT) && (p_key.get_type() != Variant::INT)); - int idx = blend_shape_track_insert_key(p_track, p_time, p_key); - track_set_key_transition(p_track, idx, p_transition); + ERR_FAIL_COND_V((p_key.get_type() != Variant::FLOAT) && (p_key.get_type() != Variant::INT), -1); + ret = blend_shape_track_insert_key(p_track, p_time, p_key); + track_set_key_transition(p_track, ret, p_transition); } break; case TYPE_VALUE: { @@ -1599,17 +1601,17 @@ void Animation::track_insert_key(int p_track, double p_time, const Variant &p_ke k.time = p_time; k.transition = p_transition; k.value = p_key; - _insert(p_time, vt->values, k); + ret = _insert(p_time, vt->values, k); } break; case TYPE_METHOD: { MethodTrack *mt = static_cast<MethodTrack *>(t); - ERR_FAIL_COND(p_key.get_type() != Variant::DICTIONARY); + ERR_FAIL_COND_V(p_key.get_type() != Variant::DICTIONARY, -1); Dictionary d = p_key; - ERR_FAIL_COND(!d.has("method") || (d["method"].get_type() != Variant::STRING_NAME && d["method"].get_type() != Variant::STRING)); - ERR_FAIL_COND(!d.has("args") || !d["args"].is_array()); + ERR_FAIL_COND_V(!d.has("method") || (d["method"].get_type() != Variant::STRING_NAME && d["method"].get_type() != Variant::STRING), -1); + ERR_FAIL_COND_V(!d.has("args") || !d["args"].is_array(), -1); MethodKey k; @@ -1618,14 +1620,14 @@ void Animation::track_insert_key(int p_track, double p_time, const Variant &p_ke k.method = d["method"]; k.params = d["args"]; - _insert(p_time, mt->methods, k); + ret = _insert(p_time, mt->methods, k); } break; case TYPE_BEZIER: { BezierTrack *bt = static_cast<BezierTrack *>(t); Array arr = p_key; - ERR_FAIL_COND(arr.size() != 6); + ERR_FAIL_COND_V(arr.size() != 6, -1); TKey<BezierKey> k; k.time = p_time; @@ -1635,23 +1637,23 @@ void Animation::track_insert_key(int p_track, double p_time, const Variant &p_ke k.value.out_handle.x = arr[3]; k.value.out_handle.y = arr[4]; k.value.handle_mode = static_cast<HandleMode>((int)arr[5]); - _insert(p_time, bt->values, k); + ret = _insert(p_time, bt->values, k); } break; case TYPE_AUDIO: { AudioTrack *at = static_cast<AudioTrack *>(t); Dictionary k = p_key; - ERR_FAIL_COND(!k.has("start_offset")); - ERR_FAIL_COND(!k.has("end_offset")); - ERR_FAIL_COND(!k.has("stream")); + ERR_FAIL_COND_V(!k.has("start_offset"), -1); + ERR_FAIL_COND_V(!k.has("end_offset"), -1); + ERR_FAIL_COND_V(!k.has("stream"), -1); TKey<AudioKey> ak; ak.time = p_time; ak.value.start_offset = k["start_offset"]; ak.value.end_offset = k["end_offset"]; ak.value.stream = k["stream"]; - _insert(p_time, at->values, ak); + ret = _insert(p_time, at->values, ak); } break; case TYPE_ANIMATION: { @@ -1661,12 +1663,14 @@ void Animation::track_insert_key(int p_track, double p_time, const Variant &p_ke ak.time = p_time; ak.value = p_key; - _insert(p_time, at->values, ak); + ret = _insert(p_time, at->values, ak); } break; } emit_changed(); + + return ret; } int Animation::track_get_key_count(int p_track) const { diff --git a/scene/resources/animation.h b/scene/resources/animation.h index abbcaa92bc..bf9f786a0d 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -392,7 +392,7 @@ public: void track_set_enabled(int p_track, bool p_enabled); bool track_is_enabled(int p_track) const; - void track_insert_key(int p_track, double p_time, const Variant &p_key, real_t p_transition = 1); + int track_insert_key(int p_track, double p_time, const Variant &p_key, real_t p_transition = 1); void track_set_key_transition(int p_track, int p_key_idx, real_t p_transition); void track_set_key_value(int p_track, int p_key_idx, const Variant &p_value); void track_set_key_time(int p_track, int p_key_idx, double p_time); |