diff options
27 files changed, 718 insertions, 168 deletions
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index ac8e2ece12..779270fe47 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -3507,6 +3507,27 @@ char32_t String::unicode_at(int p_idx) const { return operator[](p_idx); } +String String::indent(const String &p_prefix) const { + String new_string; + int line_start = 0; + + for (int i = 0; i < length(); i++) { + const char32_t c = operator[](i); + if (c == '\n') { + if (i == line_start) { + new_string += c; // Leave empty lines empty. + } else { + new_string += p_prefix + substr(line_start, i - line_start + 1); + } + line_start = i + 1; + } + } + if (line_start != length()) { + new_string += p_prefix + substr(line_start); + } + return new_string; +} + String String::dedent() const { String new_string; String indent; diff --git a/core/string/ustring.h b/core/string/ustring.h index 396c996050..780515c12e 100644 --- a/core/string/ustring.h +++ b/core/string/ustring.h @@ -356,6 +356,7 @@ public: String left(int p_pos) const; String right(int p_pos) const; + String indent(const String &p_prefix) const; String dedent() const; String strip_edges(bool left = true, bool right = true) const; String strip_escapes() const; diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index 82f547e78c..51b9119933 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1411,6 +1411,7 @@ static void _register_variant_builtin_methods() { bind_method(String, get_basename, sarray(), varray()); bind_method(String, plus_file, sarray("file"), varray()); bind_method(String, unicode_at, sarray("at"), varray()); + bind_method(String, indent, sarray("prefix"), varray()); bind_method(String, dedent, sarray(), varray()); bind_method(String, hash, sarray(), varray()); bind_method(String, md5_text, sarray(), varray()); diff --git a/doc/classes/GraphEdit.xml b/doc/classes/GraphEdit.xml index 76b255e273..c432410d3b 100644 --- a/doc/classes/GraphEdit.xml +++ b/doc/classes/GraphEdit.xml @@ -18,6 +18,43 @@ Virtual method which can be overridden to customize how connections are drawn. </description> </method> + <method name="_is_in_input_hotzone" qualifiers="virtual"> + <return type="bool" /> + <argument index="0" name="graph_node" type="Object" /> + <argument index="1" name="slot_index" type="int" /> + <argument index="2" name="mouse_position" type="Vector2" /> + <description> + Returns whether the [code]mouse_position[/code] is in the input hot zone. + By default, a hot zone is a [Rect2] positioned such that its center is at [code]graph_node[/code].[method GraphNode.get_connection_input_position]([code]slot_index[/code]) (For output's case, call [method GraphNode.get_connection_output_position] instead). The hot zone's width is twice the Theme Property [code]port_grab_distance_horizontal[/code], and its height is twice the [code]port_grab_distance_vertical[/code]. + Below is a sample code to help get started: + [codeblock] + func _is_in_input_hotzone(graph_node, slot_index, mouse_position): + var slot_size : Vector2 = Vector2(get_theme_constant("port_grab_distance_horizontal"), get_theme_constant("port_grab_distance_vertical")) + var slot_pos : Vector2 = graph_node.get_position() + graph_node.get_connection_input_position(slot_index) - slot_size / 2 + var rect = Rect2(slot_pos, slot_size) + + return rect.has_point(mouse_position) + [/codeblock] + </description> + </method> + <method name="_is_in_output_hotzone" qualifiers="virtual"> + <return type="bool" /> + <argument index="0" name="graph_node" type="Object" /> + <argument index="1" name="slot_index" type="int" /> + <argument index="2" name="mouse_position" type="Vector2" /> + <description> + Returns whether the [code]mouse_position[/code] is in the output hot zone. For more information on hot zones, see [method _is_in_input_hotzone]. + Below is a sample code to help get started: + [codeblock] + func _is_in_output_hotzone(graph_node, slot_index, mouse_position): + var slot_size : Vector2 = Vector2(get_theme_constant("port_grab_distance_horizontal"), get_theme_constant("port_grab_distance_vertical")) + var slot_pos : Vector2 = graph_node.get_position() + graph_node.get_connection_output_position(slot_index) - slot_size / 2 + var rect = Rect2(slot_pos, slot_size) + + return rect.has_point(mouse_position) + [/codeblock] + </description> + </method> <method name="add_valid_connection_type"> <return type="void" /> <argument index="0" name="from_type" type="int" /> diff --git a/doc/classes/HTTPRequest.xml b/doc/classes/HTTPRequest.xml index aaaf863c69..c92f751c60 100644 --- a/doc/classes/HTTPRequest.xml +++ b/doc/classes/HTTPRequest.xml @@ -208,6 +208,24 @@ Returns [constant OK] if request is successfully created. (Does not imply that the server has responded), [constant ERR_UNCONFIGURED] if not in the tree, [constant ERR_BUSY] if still processing previous request, [constant ERR_INVALID_PARAMETER] if given string is not a valid URL format, or [constant ERR_CANT_CONNECT] if not using thread and the [HTTPClient] cannot connect to host. </description> </method> + <method name="set_http_proxy"> + <return type="void" /> + <argument index="0" name="host" type="String" /> + <argument index="1" name="port" type="int" /> + <description> + Sets the proxy server for HTTP requests. + The proxy server is unset if [code]host[/code] is empty or [code]port[/code] is -1. + </description> + </method> + <method name="set_https_proxy"> + <return type="void" /> + <argument index="0" name="host" type="String" /> + <argument index="1" name="port" type="int" /> + <description> + Sets the proxy server for HTTPS requests. + The proxy server is unset if [code]host[/code] is empty or [code]port[/code] is -1. + </description> + </method> </methods> <members> <member name="accept_gzip" type="bool" setter="set_accept_gzip" getter="is_accepting_gzip" default="true"> diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml index a5bd8e2768..ffc02f09a9 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -270,7 +270,60 @@ <method name="get_name" qualifiers="const"> <return type="String" /> <description> - Returns the name of the host OS. Possible values are: [code]"Android"[/code], [code]"iOS"[/code], [code]"HTML5"[/code], [code]"macOS"[/code], [code]"Server"[/code], [code]"Windows"[/code], [code]"UWP"[/code], [code]"X11"[/code]. + Returns the name of the host OS. + On Windows, this is [code]"Windows"[/code] or [code]"UWP"[/code] (Universal Windows Platform) if exported thereon. + On macOS, this is [code]"macOS"[/code]. + On Linux-based operating systems, this is [code]"Linux"[/code]. + On BSD-based operating systems, this is [code]"FreeBSD"[/code], [code]"NetBSD"[/code], [code]"OpenBSD"[/code], or [code]"BSD"[/code] as a fallback. + On Android, this is [code]"Android"[/code]. + On iOS, this is [code]"iOS"[/code]. + On the web, this is [code]"HTML5"[/code]. + [b]Note:[/b] Custom builds of the engine may support additional platforms, such as consoles, yielding other return values. + [codeblocks] + [gdscript] + match OS.get_name(): + "Windows", "UWP": + print("Windows") + "macOS": + print("macOS") + "Linux", "FreeBSD", "NetBSD", "OpenBSD", "BSD": + print("Linux/BSD") + "Android": + print("Android") + "iOS": + print("iOS") + "HTML5": + print("Web") + [/gdscript] + [csharp] + switch (OS.GetName()) + { + case "Windows": + case "UWP": + GD.Print("Windows"); + break; + case "macOS": + GD.Print("macOS"); + break; + case "Linux": + case "FreeBSD": + case "NetBSD": + case "OpenBSD" + case "BSD": + GD.Print("Linux/BSD"); + break; + case "Android": + GD.Print("Android"); + break; + case "iOS": + GD.Print("iOS"); + break; + case "HTML5": + GD.Print("Web"); + break; + } + [/csharp] + [/codeblocks] </description> </method> <method name="get_process_id" qualifiers="const"> @@ -327,11 +380,13 @@ <return type="String" /> <description> Returns the absolute directory path where user data is written ([code]user://[/code]). - On Linux, this is [code]~/.local/share/godot/app_userdata/[project_name][/code], or [code]~/.local/share/[custom_name][/code] if [code]use_custom_user_dir[/code] is set. + On Windows, this is [code]%AppData%\Godot\app_userdata\[project_name][/code], or [code]%AppData%\[custom_name][/code] if [code]use_custom_user_dir[/code] is set. [code]%AppData%[/code] expands to [code]%UserProfile%\AppData\Roaming[/code]. On macOS, this is [code]~/Library/Application Support/Godot/app_userdata/[project_name][/code], or [code]~/Library/Application Support/[custom_name][/code] if [code]use_custom_user_dir[/code] is set. - On Windows, this is [code]%APPDATA%\Godot\app_userdata\[project_name][/code], or [code]%APPDATA%\[custom_name][/code] if [code]use_custom_user_dir[/code] is set. [code]%APPDATA%[/code] expands to [code]%USERPROFILE%\AppData\Roaming[/code]. - If the project name is empty, [code]user://[/code] falls back to [code]res://[/code]. - Not to be confused with [method get_data_dir], which returns the [i]global[/i] (non-project-specific) user data directory. + On Linux and BSD, this is [code]~/.local/share/godot/app_userdata/[project_name][/code], or [code]~/.local/share/[custom_name][/code] if [code]use_custom_user_dir[/code] is set. + On Android and iOS, this is a sandboxed directory in either internal or external storage, depending on the user's configuration. + On the web, this is a virtual directory managed by the browser. + If the project name is empty, [code][project_name][/code] falls back to [code][unnamed project][/code]. + Not to be confused with [method get_data_dir], which returns the [i]global[/i] (non-project-specific) user home directory. </description> </method> <method name="has_environment" qualifiers="const"> diff --git a/doc/classes/Script.xml b/doc/classes/Script.xml index ab88bdaa73..4174e1afeb 100644 --- a/doc/classes/Script.xml +++ b/doc/classes/Script.xml @@ -5,6 +5,7 @@ </brief_description> <description> A class stored as a resource. A script extends the functionality of all objects that instance it. + This is the base class for all scripts and should not be used directly. Trying to create a new script with this class will result in an error. The [code]new[/code] method of a script subclass creates a new instance. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes. </description> <tutorials> diff --git a/doc/classes/String.xml b/doc/classes/String.xml index ce902c1216..cd6fc10931 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -124,7 +124,7 @@ <method name="dedent" qualifiers="const"> <return type="String" /> <description> - Returns a copy of the string with indentation (leading tabs and spaces) removed. + Returns a copy of the string with indentation (leading tabs and spaces) removed. See also [method indent] to add indentation. </description> </method> <method name="ends_with" qualifiers="const"> @@ -243,6 +243,15 @@ <description> </description> </method> + <method name="indent" qualifiers="const"> + <return type="String" /> + <argument index="0" name="prefix" type="String" /> + <description> + Returns a copy of the string with lines indented with [code]prefix[/code]. + For example, the string can be indented with two tabs using [code]"\t\t"[/code], or four spaces using [code]" "[/code]. The prefix can be any string so it can also be used to comment out strings with e.g. [code]"# "[/code]. See also [method dedent] to remove indentation. + [b]Note:[/b] Empty lines are kept empty. + </description> + </method> <method name="insert" qualifiers="const"> <return type="String" /> <argument index="0" name="position" type="int" /> diff --git a/doc/classes/VisualShaderNodeTextureUniform.xml b/doc/classes/VisualShaderNodeTextureUniform.xml index c2e66ccb96..8da4325564 100644 --- a/doc/classes/VisualShaderNodeTextureUniform.xml +++ b/doc/classes/VisualShaderNodeTextureUniform.xml @@ -12,6 +12,12 @@ <member name="color_default" type="int" setter="set_color_default" getter="get_color_default" enum="VisualShaderNodeTextureUniform.ColorDefault" default="0"> Sets the default color if no texture is assigned to the uniform. </member> + <member name="texture_filter" type="int" setter="set_texture_filter" getter="get_texture_filter" enum="VisualShaderNodeTextureUniform.TextureFilter" default="0"> + Sets the texture filtering mode. See [enum TextureFilter] for options. + </member> + <member name="texture_repeat" type="int" setter="set_texture_repeat" getter="get_texture_repeat" enum="VisualShaderNodeTextureUniform.TextureRepeat" default="0"> + Sets the texture repeating mode. See [enum TextureRepeat] for options. + </member> <member name="texture_type" type="int" setter="set_texture_type" getter="get_texture_type" enum="VisualShaderNodeTextureUniform.TextureType" default="0"> Defines the type of data provided by the source texture. See [enum TextureType] for options. </member> @@ -41,5 +47,31 @@ <constant name="COLOR_DEFAULT_MAX" value="2" enum="ColorDefault"> Represents the size of the [enum ColorDefault] enum. </constant> + <constant name="FILTER_DEFAULT" value="0" enum="TextureFilter"> + </constant> + <constant name="FILTER_NEAREST" value="1" enum="TextureFilter"> + </constant> + <constant name="FILTER_LINEAR" value="2" enum="TextureFilter"> + </constant> + <constant name="FILTER_NEAREST_MIPMAP" value="3" enum="TextureFilter"> + </constant> + <constant name="FILTER_LINEAR_MIPMAP" value="4" enum="TextureFilter"> + </constant> + <constant name="FILTER_NEAREST_MIPMAP_ANISOTROPIC" value="5" enum="TextureFilter"> + </constant> + <constant name="FILTER_LINEAR_MIPMAP_ANISOTROPIC" value="6" enum="TextureFilter"> + </constant> + <constant name="FILTER_MAX" value="7" enum="TextureFilter"> + Represents the size of the [enum TextureFilter] enum. + </constant> + <constant name="REPEAT_DEFAULT" value="0" enum="TextureRepeat"> + </constant> + <constant name="REPEAT_ENABLED" value="1" enum="TextureRepeat"> + </constant> + <constant name="REPEAT_DISABLED" value="2" enum="TextureRepeat"> + </constant> + <constant name="REPEAT_MAX" value="3" enum="TextureRepeat"> + Represents the size of the [enum TextureRepeat] enum. + </constant> </constants> </class> diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp index 8191b343f7..b9491998a0 100644 --- a/editor/doc_tools.cpp +++ b/editor/doc_tools.cpp @@ -37,12 +37,42 @@ #include "core/io/dir_access.h" #include "core/io/marshalls.h" #include "core/object/script_language.h" +#include "core/string/translation.h" #include "core/version.h" #include "scene/resources/theme.h" // Used for a hack preserving Mono properties on non-Mono builds. #include "modules/modules_enabled.gen.h" // For mono. +static String _get_indent(const String &p_text) { + String indent; + bool has_text = false; + int line_start = 0; + + for (int i = 0; i < p_text.length(); i++) { + const char32_t c = p_text[i]; + if (c == '\n') { + line_start = i + 1; + } else if (c > 32) { + has_text = true; + indent = p_text.substr(line_start, i - line_start); + break; // Indentation of the first line that has text. + } + } + if (!has_text) { + return p_text; + } + return indent; +} + +static String _translate_doc_string(const String &p_text) { + const String indent = _get_indent(p_text); + const String message = p_text.dedent().strip_edges(); + const String translated = TranslationServer::get_singleton()->doc_translate(message, ""); + // No need to restore stripped edges because they'll be stripped again later. + return translated.indent(indent); +} + void DocTools::merge_from(const DocTools &p_data) { for (KeyValue<String, DocData::ClassDoc> &E : class_list) { DocData::ClassDoc &c = E.value; @@ -1289,7 +1319,7 @@ static void _write_method_doc(FileAccess *f, const String &p_name, Vector<DocDat } _write_string(f, 3, "<description>"); - _write_string(f, 4, m.description.strip_edges().xml_escape()); + _write_string(f, 4, _translate_doc_string(m.description).strip_edges().xml_escape()); _write_string(f, 3, "</description>"); _write_string(f, 2, "</" + p_name + ">"); @@ -1327,11 +1357,11 @@ Error DocTools::save_classes(const String &p_default_path, const Map<String, Str _write_string(f, 0, header); _write_string(f, 1, "<brief_description>"); - _write_string(f, 2, c.brief_description.strip_edges().xml_escape()); + _write_string(f, 2, _translate_doc_string(c.brief_description).strip_edges().xml_escape()); _write_string(f, 1, "</brief_description>"); _write_string(f, 1, "<description>"); - _write_string(f, 2, c.description.strip_edges().xml_escape()); + _write_string(f, 2, _translate_doc_string(c.description).strip_edges().xml_escape()); _write_string(f, 1, "</description>"); _write_string(f, 1, "<tutorials>"); @@ -1366,7 +1396,7 @@ Error DocTools::save_classes(const String &p_default_path, const Map<String, Str _write_string(f, 2, "<member name=\"" + p.name + "\" type=\"" + p.type + "\" setter=\"" + p.setter + "\" getter=\"" + p.getter + "\" overrides=\"" + p.overrides + "\"" + additional_attributes + " />"); } else { _write_string(f, 2, "<member name=\"" + p.name + "\" type=\"" + p.type + "\" setter=\"" + p.setter + "\" getter=\"" + p.getter + "\"" + additional_attributes + ">"); - _write_string(f, 3, p.description.strip_edges().xml_escape()); + _write_string(f, 3, _translate_doc_string(p.description).strip_edges().xml_escape()); _write_string(f, 2, "</member>"); } } @@ -1392,7 +1422,7 @@ Error DocTools::save_classes(const String &p_default_path, const Map<String, Str _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"platform-dependent\">"); } } - _write_string(f, 3, k.description.strip_edges().xml_escape()); + _write_string(f, 3, _translate_doc_string(k.description).strip_edges().xml_escape()); _write_string(f, 2, "</constant>"); } @@ -1412,7 +1442,7 @@ Error DocTools::save_classes(const String &p_default_path, const Map<String, Str _write_string(f, 2, "<theme_item name=\"" + ti.name + "\" data_type=\"" + ti.data_type + "\" type=\"" + ti.type + "\">"); } - _write_string(f, 3, ti.description.strip_edges().xml_escape()); + _write_string(f, 3, _translate_doc_string(ti.description).strip_edges().xml_escape()); _write_string(f, 2, "</theme_item>"); } diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 95248b22b5..68ac122e63 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -33,21 +33,17 @@ #include "core/config/project_settings.h" #include "core/input/input_map.h" #include "core/io/certs_compressed.gen.h" -#include "core/io/compression.h" #include "core/io/config_file.h" #include "core/io/dir_access.h" #include "core/io/file_access.h" -#include "core/io/file_access_memory.h" #include "core/io/ip.h" #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" -#include "core/io/translation_loader_po.h" #include "core/os/keyboard.h" #include "core/os/os.h" #include "core/version.h" -#include "editor/doc_translations.gen.h" #include "editor/editor_node.h" -#include "editor/editor_translations.gen.h" +#include "editor/editor_translation.h" #include "scene/main/node.h" #include "scene/main/scene_tree.h" #include "scene/main/window.h" @@ -369,16 +365,11 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { } String best; - EditorTranslationList *etl = _editor_translations; - - while (etl->data) { - const String &locale = etl->lang; - + for (const String &locale : get_editor_locales()) { // Skip locales which we can't render properly (see above comment). // Test against language code without regional variants (e.g. ur_PK). String lang_code = locale.get_slice("_", 0); if (locales_to_skip.find(lang_code) != -1) { - etl++; continue; } @@ -392,8 +383,6 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { if (best.is_empty() && host_lang.begins_with(locale)) { best = locale; } - - etl++; } if (best.is_empty()) { @@ -922,50 +911,10 @@ void EditorSettings::setup_language() { return; // Default, nothing to do. } // Load editor translation for configured/detected locale. - EditorTranslationList *etl = _editor_translations; - while (etl->data) { - if (etl->lang == lang) { - Vector<uint8_t> data; - data.resize(etl->uncomp_size); - Compression::decompress(data.ptrw(), etl->uncomp_size, etl->data, etl->comp_size, Compression::MODE_DEFLATE); - - FileAccessMemory *fa = memnew(FileAccessMemory); - fa->open_custom(data.ptr(), data.size()); - - Ref<Translation> tr = TranslationLoaderPO::load_translation(fa); - - if (tr.is_valid()) { - tr->set_locale(etl->lang); - TranslationServer::get_singleton()->set_tool_translation(tr); - break; - } - } - - etl++; - } + load_editor_translations(lang); // Load class reference translation. - DocTranslationList *dtl = _doc_translations; - while (dtl->data) { - if (dtl->lang == lang) { - Vector<uint8_t> data; - data.resize(dtl->uncomp_size); - Compression::decompress(data.ptrw(), dtl->uncomp_size, dtl->data, dtl->comp_size, Compression::MODE_DEFLATE); - - FileAccessMemory *fa = memnew(FileAccessMemory); - fa->open_custom(data.ptr(), data.size()); - - Ref<Translation> tr = TranslationLoaderPO::load_translation(fa); - - if (tr.is_valid()) { - tr->set_locale(dtl->lang); - TranslationServer::get_singleton()->set_doc_translation(tr); - break; - } - } - - dtl++; - } + load_doc_translations(lang); } void EditorSettings::setup_network() { diff --git a/editor/editor_translation.cpp b/editor/editor_translation.cpp new file mode 100644 index 0000000000..23145c27c8 --- /dev/null +++ b/editor/editor_translation.cpp @@ -0,0 +1,99 @@ +/*************************************************************************/ +/* editor_translation.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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/editor_translation.h" + +#include "core/io/compression.h" +#include "core/io/file_access_memory.h" +#include "core/io/translation_loader_po.h" +#include "editor/doc_translations.gen.h" +#include "editor/editor_translations.gen.h" + +Vector<String> get_editor_locales() { + Vector<String> locales; + + EditorTranslationList *etl = _editor_translations; + while (etl->data) { + const String &locale = etl->lang; + locales.push_back(locale); + + etl++; + } + + return locales; +} + +void load_editor_translations(const String &p_locale) { + EditorTranslationList *etl = _editor_translations; + while (etl->data) { + if (etl->lang == p_locale) { + Vector<uint8_t> data; + data.resize(etl->uncomp_size); + Compression::decompress(data.ptrw(), etl->uncomp_size, etl->data, etl->comp_size, Compression::MODE_DEFLATE); + + FileAccessMemory *fa = memnew(FileAccessMemory); + fa->open_custom(data.ptr(), data.size()); + + Ref<Translation> tr = TranslationLoaderPO::load_translation(fa); + + if (tr.is_valid()) { + tr->set_locale(etl->lang); + TranslationServer::get_singleton()->set_tool_translation(tr); + break; + } + } + + etl++; + } +} + +void load_doc_translations(const String &p_locale) { + DocTranslationList *dtl = _doc_translations; + while (dtl->data) { + if (dtl->lang == p_locale) { + Vector<uint8_t> data; + data.resize(dtl->uncomp_size); + Compression::decompress(data.ptrw(), dtl->uncomp_size, dtl->data, dtl->comp_size, Compression::MODE_DEFLATE); + + FileAccessMemory *fa = memnew(FileAccessMemory); + fa->open_custom(data.ptr(), data.size()); + + Ref<Translation> tr = TranslationLoaderPO::load_translation(fa); + + if (tr.is_valid()) { + tr->set_locale(dtl->lang); + TranslationServer::get_singleton()->set_doc_translation(tr); + break; + } + } + + dtl++; + } +} diff --git a/editor/editor_translation.h b/editor/editor_translation.h new file mode 100644 index 0000000000..41703f0fd0 --- /dev/null +++ b/editor/editor_translation.h @@ -0,0 +1,41 @@ +/*************************************************************************/ +/* editor_translation.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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_TRANSLATION_H +#define EDITOR_TRANSLATION_H + +#include "core/string/ustring.h" +#include "core/templates/vector.h" + +Vector<String> get_editor_locales(); +void load_editor_translations(const String &p_locale); +void load_doc_translations(const String &p_locale); + +#endif // EDITOR_TRANSLATION_H diff --git a/editor/export_template_manager.cpp b/editor/export_template_manager.cpp index d9613687f1..7ae7195deb 100644 --- a/editor/export_template_manager.cpp +++ b/editor/export_template_manager.cpp @@ -147,6 +147,11 @@ void ExportTemplateManager::_download_template(const String &p_url, bool p_skip_ download_templates->set_download_file(EditorPaths::get_singleton()->get_cache_dir().plus_file("tmp_templates.tpz")); download_templates->set_use_threads(true); + const String proxy_host = EDITOR_DEF("network/http_proxy/host", ""); + const int proxy_port = EDITOR_DEF("network/http_proxy/port", -1); + download_templates->set_http_proxy(proxy_host, proxy_port); + download_templates->set_https_proxy(proxy_host, proxy_port); + Error err = download_templates->request(p_url); if (err != OK) { _set_current_progress_status(TTR("Error requesting URL:") + " " + p_url, true); diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index 951af92467..994e89d96f 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -39,6 +39,15 @@ #include "editor/editor_settings.h" #include "editor/project_settings_editor.h" +static inline void setup_http_request(HTTPRequest *request) { + request->set_use_threads(EDITOR_DEF("asset_library/use_threads", true)); + + const String proxy_host = EDITOR_DEF("network/http_proxy/host", ""); + const int proxy_port = EDITOR_DEF("network/http_proxy/port", -1); + request->set_http_proxy(proxy_host, proxy_port); + request->set_https_proxy(proxy_host, proxy_port); +} + void EditorAssetLibraryItem::configure(const String &p_title, int p_asset_id, const String &p_category, int p_category_id, const String &p_author, int p_author_id, const String &p_cost) { title->set_text(p_title); asset_id = p_asset_id; @@ -534,7 +543,7 @@ EditorAssetLibraryItemDownload::EditorAssetLibraryItemDownload() { download = memnew(HTTPRequest); add_child(download); download->connect("request_completed", callable_mp(this, &EditorAssetLibraryItemDownload::_http_download_completed)); - download->set_use_threads(EDITOR_DEF("asset_library/use_threads", true)); + setup_http_request(download); download_error = memnew(AcceptDialog); add_child(download_error); @@ -869,7 +878,7 @@ void EditorAssetLibrary::_request_image(ObjectID p_for, String p_image_url, Imag iq.image_index = p_image_index; iq.image_type = p_type; iq.request = memnew(HTTPRequest); - iq.request->set_use_threads(EDITOR_DEF("asset_library/use_threads", true)); + setup_http_request(iq.request); iq.target = p_for; iq.queue_id = ++last_queue_id; @@ -1476,7 +1485,7 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) { request = memnew(HTTPRequest); add_child(request); - request->set_use_threads(EDITOR_DEF("asset_library/use_threads", true)); + setup_http_request(request); request->connect("request_completed", callable_mp(this, &EditorAssetLibrary::_http_request_completed)); last_queue_id = 0; diff --git a/main/main.cpp b/main/main.cpp index 6cc877f8c2..21e5935adc 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -83,6 +83,7 @@ #include "editor/doc_tools.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" +#include "editor/editor_translation.h" #include "editor/progress_dialog.h" #include "editor/project_manager.h" #ifndef NO_EDITOR_SPLASH @@ -1937,7 +1938,6 @@ Error Main::setup2(Thread::ID p_main_tid_override) { } _start_success = true; - locale = String(); ClassDB::set_current_api(ClassDB::API_NONE); //no more APIs are registered at this point @@ -2049,6 +2049,11 @@ bool Main::start() { // Needed to instance editor-only classes for their default values Engine::get_singleton()->set_editor_hint(true); + // Translate the class reference only when `-l LOCALE` parameter is given. + if (!locale.is_empty() && locale != "en") { + load_doc_translations(locale); + } + { DirAccessRef da = DirAccess::open(doc_tool_path); ERR_FAIL_COND_V_MSG(!da, false, "Argument supplied to --doctool must be a valid directory path."); diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java index 17ff3c75c0..2fecf2e6ba 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java @@ -727,45 +727,78 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC }); } - @Override - public void onSensorChanged(SensorEvent event) { + public float[] getRotatedValues(float values[]) { + if (values == null || values.length != 3) { + return values; + } + Display display = ((WindowManager)getActivity().getSystemService(WINDOW_SERVICE)).getDefaultDisplay(); int displayRotation = display.getRotation(); - float[] adjustedValues = new float[3]; - final int[][] axisSwap = { - { 1, -1, 0, 1 }, // ROTATION_0 - { -1, -1, 1, 0 }, // ROTATION_90 - { -1, 1, 0, 1 }, // ROTATION_180 - { 1, 1, 1, 0 } - }; // ROTATION_270 + float[] rotatedValues = new float[3]; + switch (displayRotation) { + case Surface.ROTATION_0: + rotatedValues[0] = values[0]; + rotatedValues[1] = values[1]; + rotatedValues[2] = values[2]; + break; + case Surface.ROTATION_90: + rotatedValues[0] = -values[1]; + rotatedValues[1] = values[0]; + rotatedValues[2] = values[2]; + break; + case Surface.ROTATION_180: + rotatedValues[0] = -values[0]; + rotatedValues[1] = -values[1]; + rotatedValues[2] = values[2]; + break; + case Surface.ROTATION_270: + rotatedValues[0] = values[1]; + rotatedValues[1] = -values[0]; + rotatedValues[2] = values[2]; + break; + } - final int[] as = axisSwap[displayRotation]; - adjustedValues[0] = (float)as[0] * event.values[as[2]]; - adjustedValues[1] = (float)as[1] * event.values[as[3]]; - adjustedValues[2] = event.values[2]; + return rotatedValues; + } - final float x = adjustedValues[0]; - final float y = adjustedValues[1]; - final float z = adjustedValues[2]; + @Override + public void onSensorChanged(SensorEvent event) { + if (mRenderView == null) { + return; + } final int typeOfSensor = event.sensor.getType(); - if (mRenderView != null) { - mRenderView.queueOnRenderThread(() -> { - if (typeOfSensor == Sensor.TYPE_ACCELEROMETER) { - GodotLib.accelerometer(-x, y, -z); - } - if (typeOfSensor == Sensor.TYPE_GRAVITY) { - GodotLib.gravity(-x, y, -z); - } - if (typeOfSensor == Sensor.TYPE_MAGNETIC_FIELD) { - GodotLib.magnetometer(-x, y, -z); - } - if (typeOfSensor == Sensor.TYPE_GYROSCOPE) { - GodotLib.gyroscope(x, -y, z); - } - }); + switch (typeOfSensor) { + case Sensor.TYPE_ACCELEROMETER: { + float[] rotatedValues = getRotatedValues(event.values); + mRenderView.queueOnRenderThread(() -> { + GodotLib.accelerometer(-rotatedValues[0], -rotatedValues[1], -rotatedValues[2]); + }); + break; + } + case Sensor.TYPE_GRAVITY: { + float[] rotatedValues = getRotatedValues(event.values); + mRenderView.queueOnRenderThread(() -> { + GodotLib.gravity(-rotatedValues[0], -rotatedValues[1], -rotatedValues[2]); + }); + break; + } + case Sensor.TYPE_MAGNETIC_FIELD: { + float[] rotatedValues = getRotatedValues(event.values); + mRenderView.queueOnRenderThread(() -> { + GodotLib.magnetometer(-rotatedValues[0], -rotatedValues[1], -rotatedValues[2]); + }); + break; + } + case Sensor.TYPE_GYROSCOPE: { + float[] rotatedValues = getRotatedValues(event.values); + mRenderView.queueOnRenderThread(() -> { + GodotLib.gyroscope(rotatedValues[0], rotatedValues[1], rotatedValues[2]); + }); + break; + } } } diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 32c62b7226..a008ed7180 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -549,15 +549,13 @@ bool GraphEdit::_filter_input(const Point2 &p_point) { } for (int j = 0; j < gn->get_connection_output_count(); j++) { - Vector2 pos = gn->get_connection_output_position(j) + gn->get_position(); - if (is_in_hot_zone(pos / zoom, p_point / zoom, port_size, false)) { + if (is_in_output_hotzone(gn, j, p_point / zoom, port_size)) { return true; } } for (int j = 0; j < gn->get_connection_input_count(); j++) { - Vector2 pos = gn->get_connection_input_position(j) + gn->get_position(); - if (is_in_hot_zone(pos / zoom, p_point / zoom, port_size, true)) { + if (is_in_input_hotzone(gn, j, p_point / zoom, port_size)) { return true; } } @@ -582,7 +580,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { for (int j = 0; j < gn->get_connection_output_count(); j++) { Vector2 pos = gn->get_connection_output_position(j) + gn->get_position(); - if (is_in_hot_zone(pos / zoom, click_pos, port_size, false)) { + if (is_in_output_hotzone(gn, j, click_pos, port_size)) { if (valid_left_disconnect_types.has(gn->get_connection_output_type(j))) { //check disconnect for (const Connection &E : connections) { @@ -624,7 +622,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { for (int j = 0; j < gn->get_connection_input_count(); j++) { Vector2 pos = gn->get_connection_input_position(j) + gn->get_position(); - if (is_in_hot_zone(pos / zoom, click_pos, port_size, true)) { + if (is_in_input_hotzone(gn, j, click_pos, port_size)) { if (right_disconnects || valid_right_disconnect_types.has(gn->get_connection_input_type(j))) { //check disconnect for (const Connection &E : connections) { @@ -690,7 +688,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { for (int j = 0; j < gn->get_connection_output_count(); j++) { Vector2 pos = gn->get_connection_output_position(j) + gn->get_position(); int type = gn->get_connection_output_type(j); - if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos / zoom, mpos, port_size, false)) { + if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_output_hotzone(gn, j, mpos, port_size)) { connecting_target = true; connecting_to = pos; connecting_target_to = gn->get_name(); @@ -702,7 +700,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { for (int j = 0; j < gn->get_connection_input_count(); j++) { Vector2 pos = gn->get_connection_input_position(j) + gn->get_position(); int type = gn->get_connection_input_type(j); - if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos / zoom, mpos, port_size, true)) { + if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_input_hotzone(gn, j, mpos, port_size)) { connecting_target = true; connecting_to = pos; connecting_target_to = gn->get_name(); @@ -773,25 +771,27 @@ bool GraphEdit::_check_clickable_control(Control *p_control, const Vector2 &pos) } } -bool GraphEdit::is_in_hot_zone(const Vector2 &pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left) { - if (p_left) { - if (!Rect2( - pos.x - p_port_size.x / 2 - port_grab_distance_horizontal, - pos.y - p_port_size.y / 2 - port_grab_distance_vertical / 2, - p_port_size.x + port_grab_distance_horizontal, - p_port_size.y + port_grab_distance_vertical) - .has_point(p_mouse_pos)) { - return false; - } +bool GraphEdit::is_in_input_hotzone(GraphNode *p_graph_node, int p_slot_index, const Vector2 &p_mouse_pos, const Vector2i &p_port_size) { + if (get_script_instance() && get_script_instance()->has_method("_is_in_input_hotzone")) { + return get_script_instance()->call("_is_in_input_hotzone", p_graph_node, p_slot_index, p_mouse_pos); } else { - if (!Rect2( - pos.x - p_port_size.x / 2, - pos.y - p_port_size.y / 2 - port_grab_distance_vertical / 2, - p_port_size.x + port_grab_distance_horizontal, - p_port_size.y + port_grab_distance_vertical) - .has_point(p_mouse_pos)) { - return false; - } + Vector2 pos = p_graph_node->get_connection_input_position(p_slot_index) + p_graph_node->get_position(); + return is_in_port_hotzone(pos / zoom, p_mouse_pos, p_port_size, true); + } +} + +bool GraphEdit::is_in_output_hotzone(GraphNode *p_graph_node, int p_slot_index, const Vector2 &p_mouse_pos, const Vector2i &p_port_size) { + if (get_script_instance() && get_script_instance()->has_method("_is_in_output_hotzone")) { + return get_script_instance()->call("_is_in_output_hotzone", p_graph_node, p_slot_index, p_mouse_pos); + } else { + Vector2 pos = p_graph_node->get_connection_output_position(p_slot_index) + p_graph_node->get_position(); + return is_in_port_hotzone(pos / zoom, p_mouse_pos, p_port_size, false); + } +} + +bool GraphEdit::is_in_port_hotzone(const Vector2 &pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left) { + if (!Rect2(pos.x - port_grab_distance_horizontal, pos.y - port_grab_distance_vertical, port_grab_distance_horizontal * 2, port_grab_distance_vertical * 2).has_point(p_mouse_pos)) { + return false; } for (int i = 0; i < get_child_count(); i++) { @@ -2216,6 +2216,8 @@ void GraphEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("is_right_disconnects_enabled"), &GraphEdit::is_right_disconnects_enabled); ClassDB::bind_method(D_METHOD("_update_scroll_offset"), &GraphEdit::_update_scroll_offset); + ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "_is_in_input_hotzone", PropertyInfo(Variant::OBJECT, "graph_node"), PropertyInfo(Variant::INT, "slot_index"), PropertyInfo(Variant::VECTOR2, "mouse_position"))); + ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "_is_in_output_hotzone", PropertyInfo(Variant::OBJECT, "graph_node"), PropertyInfo(Variant::INT, "slot_index"), PropertyInfo(Variant::VECTOR2, "mouse_position"))); ClassDB::bind_method(D_METHOD("get_zoom_hbox"), &GraphEdit::get_zoom_hbox); diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index 6c11f9df6a..e064c237d0 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -183,7 +183,9 @@ private: GraphEditMinimap *minimap; void _top_layer_input(const Ref<InputEvent> &p_ev); - bool is_in_hot_zone(const Vector2 &pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left); + bool is_in_input_hotzone(GraphNode *p_graph_node, int p_slot_index, const Vector2 &p_mouse_pos, const Vector2i &p_port_size); + bool is_in_output_hotzone(GraphNode *p_graph_node, int p_slot_index, const Vector2 &p_mouse_pos, const Vector2i &p_port_size); + bool is_in_port_hotzone(const Vector2 &pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left); void _top_layer_draw(); void _connections_layer_draw(); diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp index 4797729871..2dbfb43ad3 100644 --- a/scene/main/http_request.cpp +++ b/scene/main/http_request.cpp @@ -554,6 +554,14 @@ int HTTPRequest::get_body_size() const { return body_len; } +void HTTPRequest::set_http_proxy(const String &p_host, int p_port) { + client->set_http_proxy(p_host, p_port); +} + +void HTTPRequest::set_https_proxy(const String &p_host, int p_port) { + client->set_https_proxy(p_host, p_port); +} + void HTTPRequest::set_timeout(int p_timeout) { ERR_FAIL_COND(p_timeout < 0); timeout = p_timeout; @@ -602,6 +610,9 @@ void HTTPRequest::_bind_methods() { ClassDB::bind_method(D_METHOD("set_download_chunk_size", "chunk_size"), &HTTPRequest::set_download_chunk_size); ClassDB::bind_method(D_METHOD("get_download_chunk_size"), &HTTPRequest::get_download_chunk_size); + ClassDB::bind_method(D_METHOD("set_http_proxy", "host", "port"), &HTTPRequest::set_http_proxy); + ClassDB::bind_method(D_METHOD("set_https_proxy", "host", "port"), &HTTPRequest::set_https_proxy); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "download_file", PROPERTY_HINT_FILE), "set_download_file", "get_download_file"); ADD_PROPERTY(PropertyInfo(Variant::INT, "download_chunk_size", PROPERTY_HINT_RANGE, "256,16777216"), "set_download_chunk_size", "get_download_chunk_size"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_threads"), "set_use_threads", "is_using_threads"); diff --git a/scene/main/http_request.h b/scene/main/http_request.h index 673cf3a740..38c2f704ec 100644 --- a/scene/main/http_request.h +++ b/scene/main/http_request.h @@ -154,6 +154,9 @@ public: int get_downloaded_bytes() const; int get_body_size() const; + void set_http_proxy(const String &p_host, int p_port); + void set_https_proxy(const String &p_host, int p_port); + HTTPRequest(); ~HTTPRequest(); }; diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 50e2de91f7..3f14c7bb62 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -449,10 +449,10 @@ void BaseMaterial3D::_update_shader() { texfilter_str = "filter_linear_mipmap"; break; case TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC: - texfilter_str = "filter_nearest_mipmap_anisotropy"; + texfilter_str = "filter_nearest_mipmap_anisotropic"; break; case TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC: - texfilter_str = "filter_linear_mipmap_anisotropy"; + texfilter_str = "filter_linear_mipmap_anisotropic"; break; case TEXTURE_FILTER_MAX: break; // Internal value, skip. @@ -2569,7 +2569,7 @@ void BaseMaterial3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "uv2_world_triplanar"), "set_flag", "get_flag", FLAG_UV2_USE_WORLD_TRIPLANAR); ADD_GROUP("Sampling", "texture_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Anisotropy,Linear Mipmap Anisotropy"), "set_texture_filter", "get_texture_filter"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Anisotropic,Linear Mipmap Anisotropic"), "set_texture_filter", "get_texture_filter"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "texture_repeat"), "set_flag", "get_flag", FLAG_USE_TEXTURE_REPEAT); ADD_GROUP("Shadows", ""); diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index 9e60a1243f..afd46aa461 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -4857,34 +4857,106 @@ String VisualShaderNodeTextureUniform::get_output_port_name(int p_port) const { } String VisualShaderNodeTextureUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + bool has_colon = false; String code = _get_qual_str() + "uniform sampler2D " + get_uniform_name(); - switch (texture_type) { - case TYPE_DATA: - if (color_default == COLOR_DEFAULT_BLACK) { - code += " : hint_black;\n"; + // type + { + String type_code; + + switch (texture_type) { + case TYPE_DATA: + if (color_default == COLOR_DEFAULT_BLACK) { + type_code = "hint_black"; + } + break; + case TYPE_COLOR: + if (color_default == COLOR_DEFAULT_BLACK) { + type_code = "hint_black_albedo"; + } else { + type_code = "hint_albedo"; + } + break; + case TYPE_NORMAL_MAP: + type_code = "hint_normal"; + break; + case TYPE_ANISOTROPY: + type_code = "hint_anisotropy"; + break; + default: + break; + } + + if (!type_code.is_empty()) { + code += " : " + type_code; + has_colon = true; + } + } + + // filter + { + String filter_code; + + switch (texture_filter) { + case FILTER_NEAREST: + filter_code = "filter_nearest"; + break; + case FILTER_LINEAR: + filter_code = "filter_linear"; + break; + case FILTER_NEAREST_MIPMAP: + filter_code = "filter_nearest_mipmap"; + break; + case FILTER_LINEAR_MIPMAP: + filter_code = "filter_linear_mipmap"; + break; + case FILTER_NEAREST_MIPMAP_ANISOTROPIC: + filter_code = "filter_nearest_mipmap_anisotropic"; + break; + case FILTER_LINEAR_MIPMAP_ANISOTROPIC: + filter_code = "filter_linear_mipmap_anisotropic"; + break; + default: + break; + } + + if (!filter_code.is_empty()) { + if (!has_colon) { + code += " : "; + has_colon = true; } else { - code += ";\n"; + code += ", "; } - break; - case TYPE_COLOR: - if (color_default == COLOR_DEFAULT_BLACK) { - code += " : hint_black_albedo;\n"; + code += filter_code; + } + } + + // repeat + { + String repeat_code; + + switch (texture_repeat) { + case REPEAT_ENABLED: + repeat_code = "repeat_enable"; + break; + case REPEAT_DISABLED: + repeat_code = "repeat_disable"; + break; + default: + break; + } + + if (!repeat_code.is_empty()) { + if (!has_colon) { + code += " : "; } else { - code += " : hint_albedo;\n"; + code += ", "; } - break; - case TYPE_NORMAL_MAP: - code += " : hint_normal;\n"; - break; - case TYPE_ANISOTROPY: - code += " : hint_anisotropy;\n"; - break; - default: - code += ";\n"; - break; + code += repeat_code; + } } + code += ";\n"; return code; } @@ -4947,13 +5019,56 @@ VisualShaderNodeTextureUniform::ColorDefault VisualShaderNodeTextureUniform::get return color_default; } +void VisualShaderNodeTextureUniform::set_texture_filter(TextureFilter p_filter) { + ERR_FAIL_INDEX(int(p_filter), int(FILTER_MAX)); + if (texture_filter == p_filter) { + return; + } + texture_filter = p_filter; + emit_changed(); +} + +VisualShaderNodeTextureUniform::TextureFilter VisualShaderNodeTextureUniform::get_texture_filter() const { + return texture_filter; +} + +void VisualShaderNodeTextureUniform::set_texture_repeat(TextureRepeat p_repeat) { + ERR_FAIL_INDEX(int(p_repeat), int(REPEAT_MAX)); + if (texture_repeat == p_repeat) { + return; + } + texture_repeat = p_repeat; + emit_changed(); +} + +VisualShaderNodeTextureUniform::TextureRepeat VisualShaderNodeTextureUniform::get_texture_repeat() const { + return texture_repeat; +} + Vector<StringName> VisualShaderNodeTextureUniform::get_editable_properties() const { Vector<StringName> props = VisualShaderNodeUniform::get_editable_properties(); props.push_back("texture_type"); - props.push_back("color_default"); + if (texture_type == TYPE_DATA || texture_type == TYPE_COLOR) { + props.push_back("color_default"); + } + props.push_back("texture_filter"); + props.push_back("texture_repeat"); return props; } +bool VisualShaderNodeTextureUniform::is_show_prop_names() const { + return true; +} + +Map<StringName, String> VisualShaderNodeTextureUniform::get_editable_properties_names() const { + Map<StringName, String> names; + names.insert("texture_type", TTR("Type")); + names.insert("color_default", TTR("Default Color")); + names.insert("texture_filter", TTR("Filter")); + names.insert("texture_repeat", TTR("Repeat")); + return names; +} + void VisualShaderNodeTextureUniform::_bind_methods() { ClassDB::bind_method(D_METHOD("set_texture_type", "type"), &VisualShaderNodeTextureUniform::set_texture_type); ClassDB::bind_method(D_METHOD("get_texture_type"), &VisualShaderNodeTextureUniform::get_texture_type); @@ -4961,8 +5076,16 @@ void VisualShaderNodeTextureUniform::_bind_methods() { ClassDB::bind_method(D_METHOD("set_color_default", "type"), &VisualShaderNodeTextureUniform::set_color_default); ClassDB::bind_method(D_METHOD("get_color_default"), &VisualShaderNodeTextureUniform::get_color_default); + ClassDB::bind_method(D_METHOD("set_texture_filter", "filter"), &VisualShaderNodeTextureUniform::set_texture_filter); + ClassDB::bind_method(D_METHOD("get_texture_filter"), &VisualShaderNodeTextureUniform::get_texture_filter); + + ClassDB::bind_method(D_METHOD("set_texture_repeat", "type"), &VisualShaderNodeTextureUniform::set_texture_repeat); + ClassDB::bind_method(D_METHOD("get_texture_repeat"), &VisualShaderNodeTextureUniform::get_texture_repeat); + ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normal Map,Anisotropic"), "set_texture_type", "get_texture_type"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "color_default", PROPERTY_HINT_ENUM, "White Default,Black Default"), "set_color_default", "get_color_default"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "color_default", PROPERTY_HINT_ENUM, "White,Black"), "set_color_default", "get_color_default"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Default,Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Anisotropic,Linear Mipmap Anisotropic"), "set_texture_filter", "get_texture_filter"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_repeat", PROPERTY_HINT_ENUM, "Default,Enabled,Disabled"), "set_texture_repeat", "get_texture_repeat"); BIND_ENUM_CONSTANT(TYPE_DATA); BIND_ENUM_CONSTANT(TYPE_COLOR); @@ -4973,6 +5096,20 @@ void VisualShaderNodeTextureUniform::_bind_methods() { BIND_ENUM_CONSTANT(COLOR_DEFAULT_WHITE); BIND_ENUM_CONSTANT(COLOR_DEFAULT_BLACK); BIND_ENUM_CONSTANT(COLOR_DEFAULT_MAX); + + BIND_ENUM_CONSTANT(FILTER_DEFAULT); + BIND_ENUM_CONSTANT(FILTER_NEAREST); + BIND_ENUM_CONSTANT(FILTER_LINEAR); + BIND_ENUM_CONSTANT(FILTER_NEAREST_MIPMAP); + BIND_ENUM_CONSTANT(FILTER_LINEAR_MIPMAP); + BIND_ENUM_CONSTANT(FILTER_NEAREST_MIPMAP_ANISOTROPIC); + BIND_ENUM_CONSTANT(FILTER_LINEAR_MIPMAP_ANISOTROPIC); + BIND_ENUM_CONSTANT(FILTER_MAX); + + BIND_ENUM_CONSTANT(REPEAT_DEFAULT); + BIND_ENUM_CONSTANT(REPEAT_ENABLED); + BIND_ENUM_CONSTANT(REPEAT_DISABLED); + BIND_ENUM_CONSTANT(REPEAT_MAX); } String VisualShaderNodeTextureUniform::get_input_port_default_hint(int p_port) const { diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h index a9ad762b32..a9fa21f164 100644 --- a/scene/resources/visual_shader_nodes.h +++ b/scene/resources/visual_shader_nodes.h @@ -1953,9 +1953,29 @@ public: COLOR_DEFAULT_MAX, }; + enum TextureFilter { + FILTER_DEFAULT, + FILTER_NEAREST, + FILTER_LINEAR, + FILTER_NEAREST_MIPMAP, + FILTER_LINEAR_MIPMAP, + FILTER_NEAREST_MIPMAP_ANISOTROPIC, + FILTER_LINEAR_MIPMAP_ANISOTROPIC, + FILTER_MAX, + }; + + enum TextureRepeat { + REPEAT_DEFAULT, + REPEAT_ENABLED, + REPEAT_DISABLED, + REPEAT_MAX, + }; + protected: TextureType texture_type = TYPE_DATA; ColorDefault color_default = COLOR_DEFAULT_WHITE; + TextureFilter texture_filter = FILTER_DEFAULT; + TextureRepeat texture_repeat = REPEAT_DEFAULT; protected: static void _bind_methods(); @@ -1975,6 +1995,8 @@ public: virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override; virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; + virtual Map<StringName, String> get_editable_properties_names() const override; + virtual bool is_show_prop_names() const override; virtual bool is_code_generated() const override; Vector<StringName> get_editable_properties() const override; @@ -1985,6 +2007,12 @@ public: void set_color_default(ColorDefault p_default); ColorDefault get_color_default() const; + void set_texture_filter(TextureFilter p_filter); + TextureFilter get_texture_filter() const; + + void set_texture_repeat(TextureRepeat p_repeat); + TextureRepeat get_texture_repeat() const; + bool is_qualifier_supported(Qualifier p_qual) const override; bool is_convertible_to_constant() const override; @@ -1993,6 +2021,8 @@ public: VARIANT_ENUM_CAST(VisualShaderNodeTextureUniform::TextureType) VARIANT_ENUM_CAST(VisualShaderNodeTextureUniform::ColorDefault) +VARIANT_ENUM_CAST(VisualShaderNodeTextureUniform::TextureFilter) +VARIANT_ENUM_CAST(VisualShaderNodeTextureUniform::TextureRepeat) /////////////////////////////////////// diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index 5a329f90e3..0127fcf813 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -214,8 +214,8 @@ const char *ShaderLanguage::token_names[TK_MAX] = { "FILTER_LINEAR", "FILTER_NEAREST_MIPMAP", "FILTER_LINEAR_MIPMAP", - "FILTER_NEAREST_MIPMAP_ANISOTROPY", - "FILTER_LINEAR_MIPMAP_ANISOTROPY", + "FILTER_NEAREST_MIPMAP_ANISOTROPIC", + "FILTER_LINEAR_MIPMAP_ANISOTROPIC", "REPEAT_ENABLE", "REPEAT_DISABLE", "SHADER_TYPE", @@ -328,8 +328,8 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = { { TK_FILTER_LINEAR, "filter_linear" }, { TK_FILTER_NEAREST_MIPMAP, "filter_nearest_mipmap" }, { TK_FILTER_LINEAR_MIPMAP, "filter_linear_mipmap" }, - { TK_FILTER_NEAREST_MIPMAP_ANISOTROPY, "filter_nearest_mipmap_anisotropy" }, - { TK_FILTER_LINEAR_MIPMAP_ANISOTROPY, "filter_linear_mipmap_anisotropy" }, + { TK_FILTER_NEAREST_MIPMAP_ANISOTROPIC, "filter_nearest_mipmap_anisotropic" }, + { TK_FILTER_LINEAR_MIPMAP_ANISOTROPIC, "filter_linear_mipmap_anisotropic" }, { TK_REPEAT_ENABLE, "repeat_enable" }, { TK_REPEAT_DISABLE, "repeat_disable" }, { TK_SHADER_TYPE, "shader_type" }, @@ -8111,10 +8111,10 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct uniform2.filter = FILTER_NEAREST_MIPMAP; } else if (tk.type == TK_FILTER_LINEAR_MIPMAP) { uniform2.filter = FILTER_LINEAR_MIPMAP; - } else if (tk.type == TK_FILTER_NEAREST_MIPMAP_ANISOTROPY) { - uniform2.filter = FILTER_NEAREST_MIPMAP_ANISOTROPY; - } else if (tk.type == TK_FILTER_LINEAR_MIPMAP_ANISOTROPY) { - uniform2.filter = FILTER_LINEAR_MIPMAP_ANISOTROPY; + } else if (tk.type == TK_FILTER_NEAREST_MIPMAP_ANISOTROPIC) { + uniform2.filter = FILTER_NEAREST_MIPMAP_ANISOTROPIC; + } else if (tk.type == TK_FILTER_LINEAR_MIPMAP_ANISOTROPIC) { + uniform2.filter = FILTER_LINEAR_MIPMAP_ANISOTROPIC; } else if (tk.type == TK_REPEAT_DISABLE) { uniform2.repeat = REPEAT_DISABLE; } else if (tk.type == TK_REPEAT_ENABLE) { @@ -9569,10 +9569,10 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_ if (options.is_empty()) { options.push_back("filter_linear"); options.push_back("filter_linear_mipmap"); - options.push_back("filter_linear_mipmap_anisotropy"); + options.push_back("filter_linear_mipmap_anisotropic"); options.push_back("filter_nearest"); options.push_back("filter_nearest_mipmap"); - options.push_back("filter_nearest_mipmap_anisotropy"); + options.push_back("filter_nearest_mipmap_anisotropic"); options.push_back("hint_albedo"); options.push_back("hint_anisotropy"); options.push_back("hint_black"); diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index 02a2ef3a67..8502a250b6 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -179,8 +179,8 @@ public: TK_FILTER_LINEAR, TK_FILTER_NEAREST_MIPMAP, TK_FILTER_LINEAR_MIPMAP, - TK_FILTER_NEAREST_MIPMAP_ANISOTROPY, - TK_FILTER_LINEAR_MIPMAP_ANISOTROPY, + TK_FILTER_NEAREST_MIPMAP_ANISOTROPIC, + TK_FILTER_LINEAR_MIPMAP_ANISOTROPIC, TK_REPEAT_ENABLE, TK_REPEAT_DISABLE, TK_SHADER_TYPE, @@ -322,8 +322,8 @@ public: FILTER_LINEAR, FILTER_NEAREST_MIPMAP, FILTER_LINEAR_MIPMAP, - FILTER_NEAREST_MIPMAP_ANISOTROPY, - FILTER_LINEAR_MIPMAP_ANISOTROPY, + FILTER_NEAREST_MIPMAP_ANISOTROPIC, + FILTER_LINEAR_MIPMAP_ANISOTROPIC, FILTER_DEFAULT, }; diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h index eef1cac894..bb9b079e79 100644 --- a/tests/core/string/test_string.h +++ b/tests/core/string/test_string.h @@ -1132,6 +1132,25 @@ TEST_CASE("[String] c-escape/unescape") { CHECK(s.c_escape().c_unescape() == s); } +TEST_CASE("[String] indent") { + static const char *input[] = { + "", + "aaa\nbbb", + "\tcontains\n\tindent", + "empty\n\nline", + }; + static const char *expected[] = { + "", + "\taaa\n\tbbb", + "\t\tcontains\n\t\tindent", + "\tempty\n\n\tline", + }; + + for (int i = 0; i < 3; i++) { + CHECK(String(input[i]).indent("\t") == expected[i]); + } +} + TEST_CASE("[String] dedent") { String s = " aaa\n bbb"; String t = "aaa\nbbb"; |