summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/linux_builds.yml39
-rw-r--r--core/core_bind.cpp6
-rw-r--r--core/core_bind.h2
-rw-r--r--doc/classes/Animation.xml4
-rw-r--r--doc/classes/EditorProperty.xml7
-rw-r--r--doc/classes/GraphEdit.xml23
-rw-r--r--doc/classes/ResourceLoader.xml2
-rw-r--r--editor/animation_track_editor.h1
-rw-r--r--editor/debugger/debug_adapter/debug_adapter_protocol.cpp1
-rw-r--r--editor/debugger/debug_adapter/debug_adapter_server.cpp1
-rw-r--r--editor/debugger/editor_debugger_tree.cpp33
-rw-r--r--editor/debugger/editor_debugger_tree.h2
-rw-r--r--editor/debugger/script_editor_debugger.cpp1
-rw-r--r--editor/dependency_editor.cpp1
-rw-r--r--editor/editor_audio_buses.cpp1
-rw-r--r--editor/editor_command_palette.cpp1
-rw-r--r--editor/editor_help_search.cpp1
-rw-r--r--editor/editor_inspector.cpp165
-rw-r--r--editor/editor_inspector.h9
-rw-r--r--editor/editor_log.cpp2
-rw-r--r--editor/editor_node.cpp2
-rw-r--r--editor/editor_node.h3
-rw-r--r--editor/editor_plugin_settings.cpp1
-rw-r--r--editor/editor_plugin_settings.h4
-rw-r--r--editor/editor_properties.cpp6
-rw-r--r--editor/editor_properties.h1
-rw-r--r--editor/editor_resource_picker.cpp1
-rw-r--r--editor/editor_run_native.cpp1
-rw-r--r--editor/editor_themes.cpp39
-rw-r--r--editor/export/editor_export_platform.cpp1
-rw-r--r--editor/export/export_template_manager.cpp2
-rw-r--r--editor/import/scene_import_settings.cpp1
-rw-r--r--editor/import_defaults_editor.cpp1
-rw-r--r--editor/import_dock.cpp1
-rw-r--r--editor/inspector_dock.cpp10
-rw-r--r--editor/inspector_dock.h2
-rw-r--r--editor/plugins/abstract_polygon_2d_editor.cpp2
-rw-r--r--editor/plugins/animation_blend_space_1d_editor.cpp1
-rw-r--r--editor/plugins/animation_blend_space_1d_editor.h2
-rw-r--r--editor/plugins/animation_blend_space_2d_editor.cpp1
-rw-r--r--editor/plugins/animation_blend_space_2d_editor.h2
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.cpp1
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.h1
-rw-r--r--editor/plugins/animation_state_machine_editor.cpp1
-rw-r--r--editor/plugins/animation_state_machine_editor.h1
-rw-r--r--editor/plugins/animation_tree_editor_plugin.h1
-rw-r--r--editor/plugins/control_editor_plugin.cpp2
-rw-r--r--editor/plugins/curve_editor_plugin.cpp1
-rw-r--r--editor/plugins/debugger_editor_plugin.cpp1
-rw-r--r--editor/plugins/editor_resource_conversion_plugin.cpp64
-rw-r--r--editor/plugins/editor_resource_conversion_plugin.h54
-rw-r--r--editor/plugins/gradient_editor_plugin.cpp1
-rw-r--r--editor/plugins/material_editor_plugin.cpp1
-rw-r--r--editor/plugins/material_editor_plugin.h2
-rw-r--r--editor/plugins/path_3d_editor_plugin.cpp1
-rw-r--r--editor/plugins/script_editor_plugin.cpp19
-rw-r--r--editor/plugins/shader_editor_plugin.cpp1
-rw-r--r--editor/plugins/shader_file_editor_plugin.cpp1
-rw-r--r--editor/plugins/text_editor.cpp1
-rw-r--r--editor/plugins/texture_region_editor_plugin.cpp2
-rw-r--r--editor/plugins/theme_editor_plugin.cpp1
-rw-r--r--editor/plugins/theme_editor_preview.cpp1
-rw-r--r--editor/plugins/version_control_editor_plugin.cpp2
-rw-r--r--editor/plugins/visual_shader_editor_plugin.h1
-rw-r--r--editor/project_settings_editor.cpp21
-rw-r--r--editor/project_settings_editor.h2
-rw-r--r--editor/property_editor.cpp34
-rw-r--r--editor/property_editor.h16
-rw-r--r--editor/rename_dialog.cpp1
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp10
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.cpp1
-rw-r--r--modules/gdscript/language_server/gdscript_language_server.cpp1
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.cpp1
-rw-r--r--modules/mono/editor/editor_internal_calls.cpp1
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs820
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs730
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs702
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj3
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.h2
-rw-r--r--modules/msdfgen/SCsub2
-rw-r--r--modules/multiplayer/doc_classes/MultiplayerSpawner.xml32
-rw-r--r--modules/multiplayer/doc_classes/MultiplayerSynchronizer.xml25
-rw-r--r--modules/multiplayer/doc_classes/SceneReplicationConfig.xml10
-rw-r--r--modules/multiplayer/editor/replication_editor_plugin.h1
-rw-r--r--modules/multiplayer/multiplayer_spawner.cpp13
-rw-r--r--modules/multiplayer/multiplayer_spawner.h6
-rw-r--r--modules/multiplayer/scene_replication_interface.cpp9
-rw-r--r--modules/text_server_adv/SCsub24
-rw-r--r--modules/text_server_adv/text_server_adv.cpp18
-rw-r--r--modules/text_server_fb/SCsub4
-rw-r--r--modules/visual_script/editor/visual_script_property_selector.cpp1
-rw-r--r--modules/visual_script/editor/visual_script_property_selector.h2
-rw-r--r--platform/ios/export/export_plugin.cpp1
-rw-r--r--platform/javascript/export/export.cpp1
-rw-r--r--platform/javascript/export/export_plugin.cpp1
-rw-r--r--platform/macos/export/export_plugin.cpp1
-rw-r--r--platform/uwp/export/export.cpp1
-rw-r--r--platform/uwp/export/export_plugin.cpp1
-rw-r--r--scene/gui/graph_edit.cpp15
-rw-r--r--scene/gui/graph_edit.h2
-rw-r--r--scene/resources/animation.cpp56
-rw-r--r--scene/resources/animation.h2
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="&quot;&quot;" />
<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, &current) && !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(&quot;&quot;)">
+ 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(&quot;..&quot;)">
+ 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);