diff options
50 files changed, 321 insertions, 128 deletions
diff --git a/core/math/expression.cpp b/core/math/expression.cpp index da52bb9465..d1ec987d56 100644 --- a/core/math/expression.cpp +++ b/core/math/expression.cpp @@ -434,14 +434,13 @@ Error Expression::_get_token(Token &r_token) { } return OK; - } else if (is_ascii_char(cchar) || is_underscore(cchar)) { - String id; - bool first = true; + } else if (is_unicode_identifier_start(cchar)) { + String id = String::chr(cchar); + cchar = GET_CHAR(); - while (is_ascii_char(cchar) || is_underscore(cchar) || (!first && is_digit(cchar))) { + while (is_unicode_identifier_continue(cchar)) { id += String::chr(cchar); cchar = GET_CHAR(); - first = false; } str_ofs--; //go back one diff --git a/doc/classes/AnimatableBody3D.xml b/doc/classes/AnimatableBody3D.xml index 2a08c4c8f1..0733780bf7 100644 --- a/doc/classes/AnimatableBody3D.xml +++ b/doc/classes/AnimatableBody3D.xml @@ -7,6 +7,7 @@ Animatable body for 3D physics. An animatable body can't be moved by external forces or contacts, but can be moved by script or animation to affect other bodies in its path. It is ideal for implementing moving objects in the environment, such as moving platforms or doors. When the body is moved manually, either from code or from an [AnimationPlayer] (with [member AnimationPlayer.playback_process_mode] set to [code]physics[/code]), the physics will automatically compute an estimate of their linear and angular velocity. This makes them very useful for moving platforms or other AnimationPlayer-controlled objects (like a door, a bridge that opens, etc). + [b]Warning:[/b] With a non-uniform scale this node will probably not function as expected. Please make sure to keep its scale uniform (i.e. the same on all axes), and change the size(s) of its collision shape(s) instead. </description> <tutorials> <link title="3D Physics Tests Demo">https://godotengine.org/asset-library/asset/675</link> diff --git a/doc/classes/Area3D.xml b/doc/classes/Area3D.xml index 8923ac8aae..d40bca99d8 100644 --- a/doc/classes/Area3D.xml +++ b/doc/classes/Area3D.xml @@ -7,6 +7,7 @@ 3D area that detects [CollisionObject3D] nodes overlapping, entering, or exiting. Can also alter or override local physics parameters (gravity, damping) and route audio to custom audio buses. To give the area its shape, add a [CollisionShape3D] or a [CollisionPolygon3D] node as a [i]direct[/i] child (or add multiple such nodes as direct children) of the area. [b]Warning:[/b] See [ConcavePolygonShape3D] (also called "trimesh") for a warning about possibly unexpected behavior when using that shape for an area. + [b]Warning:[/b] With a non-uniform scale this node will probably not function as expected. Please make sure to keep its scale uniform (i.e. the same on all axes), and change the size(s) of its collision shape(s) instead. </description> <tutorials> <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/125</link> diff --git a/doc/classes/CharacterBody3D.xml b/doc/classes/CharacterBody3D.xml index 821117122c..2ff207acb7 100644 --- a/doc/classes/CharacterBody3D.xml +++ b/doc/classes/CharacterBody3D.xml @@ -5,8 +5,9 @@ </brief_description> <description> Character bodies are special types of bodies that are meant to be user-controlled. They are not affected by physics at all; to other types of bodies, such as a rigid body, these are the same as a [AnimatableBody3D]. However, they have two main uses: - [b]Kinematic characters:[/b] Character bodies have an API for moving objects with walls and slopes detection ([method move_and_slide] method), in addition to collision detection (also done with [method PhysicsBody3D.move_and_collide]). This makes them really useful to implement characters that move in specific ways and collide with the world, but don't require advanced physics. - [b]Kinematic motion:[/b] Character bodies can also be used for kinematic motion (same functionality as [AnimatableBody3D]), which allows them to be moved by code and push other bodies on their path. + [i]Kinematic characters:[/i] Character bodies have an API for moving objects with walls and slopes detection ([method move_and_slide] method), in addition to collision detection (also done with [method PhysicsBody3D.move_and_collide]). This makes them really useful to implement characters that move in specific ways and collide with the world, but don't require advanced physics. + [i]Kinematic motion:[/i] Character bodies can also be used for kinematic motion (same functionality as [AnimatableBody3D]), which allows them to be moved by code and push other bodies on their path. + [b]Warning:[/b] With a non-uniform scale this node will probably not function as expected. Please make sure to keep its scale uniform (i.e. the same on all axes), and change the size(s) of its collision shape(s) instead. </description> <tutorials> <link title="Kinematic character (2D)">$DOCS_URL/tutorials/physics/kinematic_character_2d.html</link> diff --git a/doc/classes/CollisionObject3D.xml b/doc/classes/CollisionObject3D.xml index 31b5842930..01b0d88326 100644 --- a/doc/classes/CollisionObject3D.xml +++ b/doc/classes/CollisionObject3D.xml @@ -5,6 +5,7 @@ </brief_description> <description> CollisionObject3D is the base class for physics objects. It can hold any number of collision [Shape3D]s. Each shape must be assigned to a [i]shape owner[/i]. The CollisionObject3D can have any number of shape owners. Shape owners are not nodes and do not appear in the editor, but are accessible through code using the [code]shape_owner_*[/code] methods. + [b]Warning:[/b] With a non-uniform scale this node will probably not function as expected. Please make sure to keep its scale uniform (i.e. the same on all axes), and change the size(s) of its collision shape(s) instead. </description> <tutorials> </tutorials> diff --git a/doc/classes/CollisionPolygon3D.xml b/doc/classes/CollisionPolygon3D.xml index 7d718cff27..29e55367a8 100644 --- a/doc/classes/CollisionPolygon3D.xml +++ b/doc/classes/CollisionPolygon3D.xml @@ -6,6 +6,7 @@ <description> Allows editing a concave or convex collision polygon's vertices on a selected plane. Can also set a depth perpendicular to that plane. This class is only available in the editor. It will not appear in the scene tree at run-time. Creates several [ConvexPolygonShape3D]s at run-time to represent the original polygon using convex decomposition. [b]Note:[/b] Since this is an editor-only helper, properties modified during gameplay will have no effect. + [b]Warning:[/b] A non-uniformly scaled CollisionPolygon3D node will probably not function as expected. Please make sure to keep its scale uniform (i.e. the same on all axes), and change its [member polygon]'s vertices instead. </description> <tutorials> </tutorials> diff --git a/doc/classes/CollisionShape3D.xml b/doc/classes/CollisionShape3D.xml index 304b46ba27..c5d05670e9 100644 --- a/doc/classes/CollisionShape3D.xml +++ b/doc/classes/CollisionShape3D.xml @@ -6,6 +6,7 @@ <description> Editor facility for creating and editing collision shapes in 3D space. Set the [member shape] property to configure the shape. [b]IMPORTANT[/b]: this is an Editor-only helper to create shapes, use [method CollisionObject3D.shape_owner_get_shape] to get the actual shape. You can use this node to represent all sorts of collision shapes, for example, add this to an [Area3D] to give it a detection shape, or add it to a [PhysicsBody3D] to create a solid object. + [b]Warning:[/b] A non-uniformly scaled CollisionShape3D node will probably not function as expected. Please make sure to keep its scale uniform (i.e. the same on all axes), and change the size of its [member shape] resource instead. </description> <tutorials> <link title="Physics introduction">$DOCS_URL/tutorials/physics/physics_introduction.html</link> diff --git a/doc/classes/PhysicalBone3D.xml b/doc/classes/PhysicalBone3D.xml index 0768df31cc..f38130fe0c 100644 --- a/doc/classes/PhysicalBone3D.xml +++ b/doc/classes/PhysicalBone3D.xml @@ -3,6 +3,7 @@ <brief_description> </brief_description> <description> + [b]Warning:[/b] With a non-uniform scale this node will probably not function as expected. Please make sure to keep its scale uniform (i.e. the same on all axes), and change the size(s) of its collision shape(s) instead. </description> <tutorials> </tutorials> diff --git a/doc/classes/PhysicsBody3D.xml b/doc/classes/PhysicsBody3D.xml index 2ef54683f2..3e100e3280 100644 --- a/doc/classes/PhysicsBody3D.xml +++ b/doc/classes/PhysicsBody3D.xml @@ -4,7 +4,8 @@ Base class for all objects affected by physics in 3D space. </brief_description> <description> - PhysicsBody3D is an abstract base class for implementing a physics body. All *Body types inherit from it. + PhysicsBody3D is an abstract base class for implementing a physics body. All *Body3D types inherit from it. + [b]Warning:[/b] With a non-uniform scale this node will probably not function as expected. Please make sure to keep its scale uniform (i.e. the same on all axes), and change the size(s) of its collision shape(s) instead. </description> <tutorials> <link title="Physics introduction">$DOCS_URL/tutorials/physics/physics_introduction.html</link> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index bd68dc7a64..be272976f4 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -384,6 +384,9 @@ <member name="debug/gdscript/warnings/assert_always_true" type="int" setter="" getter="" default="1"> When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an [code]assert[/code] call always evaluates to true. </member> + <member name="debug/gdscript/warnings/confusable_identifier" type="int" setter="" getter="" default="1"> + When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an indentifier contains characters that can be confused with something else, like when mixing different alphabets. + </member> <member name="debug/gdscript/warnings/constant_used_as_function" type="int" setter="" getter="" default="1"> When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a constant is used as a function. </member> diff --git a/doc/classes/RigidBody3D.xml b/doc/classes/RigidBody3D.xml index 8380d56de3..148cdf96ee 100644 --- a/doc/classes/RigidBody3D.xml +++ b/doc/classes/RigidBody3D.xml @@ -8,6 +8,7 @@ You can switch the body's behavior using [member lock_rotation], [member freeze], and [member freeze_mode]. [b]Note:[/b] Don't change a RigidBody3D's position every frame or very often. Sporadic changes work fine, but physics runs at a different granularity (fixed Hz) than usual rendering (process callback) and maybe even in a separate thread, so changing this from a process loop may result in strange behavior. If you need to directly affect the body's state, use [method _integrate_forces], which allows you to directly access the physics state. If you need to override the default physics behavior, you can write a custom force integration function. See [member custom_integrator]. + [b]Warning:[/b] With a non-uniform scale this node will probably not function as expected. Please make sure to keep its scale uniform (i.e. the same on all axes), and change the size(s) of its collision shape(s) instead. </description> <tutorials> <link title="Physics introduction">$DOCS_URL/tutorials/physics/physics_introduction.html</link> diff --git a/doc/classes/StaticBody3D.xml b/doc/classes/StaticBody3D.xml index 0beaa6bb52..7bebd46004 100644 --- a/doc/classes/StaticBody3D.xml +++ b/doc/classes/StaticBody3D.xml @@ -7,8 +7,9 @@ Static body for 3D physics. A static body is a simple body that doesn't move under physics simulation, i.e. it can't be moved by external forces or contacts but its transformation can still be updated manually by the user. It is ideal for implementing objects in the environment, such as walls or platforms. In contrast to [RigidBody3D], it doesn't consume any CPU resources as long as they don't move. They have extra functionalities to move and affect other bodies: - [b]Static transform change:[/b] Static bodies can be moved by animation or script. In this case, they are just teleported and don't affect other bodies on their path. - [b]Constant velocity:[/b] When [member constant_linear_velocity] or [member constant_angular_velocity] is set, static bodies don't move themselves but affect touching bodies as if they were moving. This is useful for simulating conveyor belts or conveyor wheels. + [i]Static transform change:[/i] Static bodies can be moved by animation or script. In this case, they are just teleported and don't affect other bodies on their path. + [i]Constant velocity:[/i] When [member constant_linear_velocity] or [member constant_angular_velocity] is set, static bodies don't move themselves but affect touching bodies as if they were moving. This is useful for simulating conveyor belts or conveyor wheels. + [b]Warning:[/b] With a non-uniform scale this node will probably not function as expected. Please make sure to keep its scale uniform (i.e. the same on all axes), and change the size(s) of its collision shape(s) instead. </description> <tutorials> <link title="3D Physics Tests Demo">https://godotengine.org/asset-library/asset/675</link> diff --git a/doc/classes/VehicleBody3D.xml b/doc/classes/VehicleBody3D.xml index e1689133de..9f905c0ec5 100644 --- a/doc/classes/VehicleBody3D.xml +++ b/doc/classes/VehicleBody3D.xml @@ -7,6 +7,7 @@ This node implements all the physics logic needed to simulate a car. It is based on the raycast vehicle system commonly found in physics engines. You will need to add a [CollisionShape3D] for the main body of your vehicle and add [VehicleWheel3D] nodes for the wheels. You should also add a [MeshInstance3D] to this node for the 3D model of your car but this model should not include meshes for the wheels. You should control the vehicle by using the [member brake], [member engine_force], and [member steering] properties and not change the position or orientation of this node directly. [b]Note:[/b] The origin point of your VehicleBody3D will determine the center of gravity of your vehicle so it is better to keep this low and move the [CollisionShape3D] and [MeshInstance3D] upwards. [b]Note:[/b] This class has known issues and isn't designed to provide realistic 3D vehicle physics. If you want advanced vehicle physics, you will probably have to write your own physics integration using another [PhysicsBody3D] class. + [b]Warning:[/b] With a non-uniform scale this node will probably not function as expected. Please make sure to keep its scale uniform (i.e. the same on all axes), and change the size(s) of its collision shape(s) instead. </description> <tutorials> <link title="3D Truck Town Demo">https://godotengine.org/asset-library/asset/524</link> diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index 12def52d50..67526bc003 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -1596,9 +1596,13 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } +#ifndef IOS_ENABLED if (use_multiview) { glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, rt->color, 0, 0, rt->view_count); } else { +#else + { +#endif glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0); } @@ -1623,9 +1627,13 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } +#ifndef IOS_ENABLED if (use_multiview) { glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, rt->depth, 0, 0, rt->view_count); } else { +#else + { +#endif glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rt->depth, 0); } diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 05e0c6750e..19d991b112 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -3653,12 +3653,12 @@ void EditorNode::_set_main_scene_state(Dictionary p_state, Node *p_for_scene) { if (get_edited_scene()) { if (current_tab < 2) { - // Use heuristic instead. - int n2d = 0, n3d = 0; - _find_node_types(get_edited_scene(), n2d, n3d); - if (n2d > n3d) { + Node *editor_node = SceneTreeDock::get_singleton()->get_tree_editor()->get_selected(); + editor_node = editor_node == nullptr ? get_edited_scene() : editor_node; + + if (Object::cast_to<Node2D>(editor_node) || Object::cast_to<Control>(editor_node)) { editor_select(EDITOR_2D); - } else if (n3d > n2d) { + } else if (Object::cast_to<Node3D>(editor_node)) { editor_select(EDITOR_3D); } } diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index f41792af7f..54d5ddacab 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -2648,7 +2648,7 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, Vector<Str p_popup->add_icon_shortcut(get_theme_icon(SNAME("Filesystem"), SNAME("EditorIcons")), ED_GET_SHORTCUT("filesystem_dock/show_in_explorer"), FILE_SHOW_IN_EXPLORER); p_popup->set_item_text(p_popup->get_item_index(FILE_SHOW_IN_EXPLORER), item_text); if (!is_directory) { - p_popup->add_icon_item(get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), TTR("Open in External Program"), FILE_OPEN_EXTERNAL); + p_popup->add_icon_shortcut(get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), ED_GET_SHORTCUT("filesystem_dock/open_in_external_program"), FILE_OPEN_EXTERNAL); } path = fpath; } @@ -2869,6 +2869,8 @@ void FileSystemDock::_tree_gui_input(Ref<InputEvent> p_event) { _tree_rmb_option(FILE_RENAME); } else if (ED_IS_SHORTCUT("filesystem_dock/show_in_explorer", p_event)) { _tree_rmb_option(FILE_SHOW_IN_EXPLORER); + } else if (ED_IS_SHORTCUT("filesystem_dock/open_in_external_program", p_event)) { + _tree_rmb_option(FILE_OPEN_EXTERNAL); } else if (ED_IS_SHORTCUT("editor/open_search", p_event)) { focus_on_filter(); } else { @@ -3098,6 +3100,7 @@ FileSystemDock::FileSystemDock() { ED_SHORTCUT("filesystem_dock/rename", TTR("Rename..."), Key::F2); ED_SHORTCUT_OVERRIDE("filesystem_dock/rename", "macos", Key::ENTER); ED_SHORTCUT("filesystem_dock/show_in_explorer", TTR("Open in File Manager")); + ED_SHORTCUT("filesystem_dock/open_in_external_program", TTR("Open in External Program")); VBoxContainer *top_vbc = memnew(VBoxContainer); add_child(top_vbc); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 9806cf8555..e7522b000c 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -3990,6 +3990,10 @@ void CanvasItemEditor::_selection_changed() { } void CanvasItemEditor::edit(CanvasItem *p_canvas_item) { + if (!p_canvas_item) { + return; + } + Array selection = editor_selection->get_selected_nodes(); if (selection.size() != 1 || Object::cast_to<Node>(selection[0]) != p_canvas_item) { _reset_drag(); diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index 40aac77a99..14ff59b442 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -3335,8 +3335,10 @@ void ThemeTypeEditor::set_edited_theme(const Ref<Theme> &p_theme) { } edited_theme = p_theme; - edited_theme->connect("changed", callable_mp(this, &ThemeTypeEditor::_update_type_list_debounced)); - _update_type_list(); + if (edited_theme.is_valid()) { + edited_theme->connect("changed", callable_mp(this, &ThemeTypeEditor::_update_type_list_debounced)); + _update_type_list(); + } add_type_dialog->set_edited_theme(edited_theme); } @@ -3496,7 +3498,9 @@ void ThemeEditor::edit(const Ref<Theme> &p_theme) { preview_tab->set_preview_theme(p_theme); } - theme_name->set_text(TTR("Theme:") + " " + theme->get_path().get_file()); + if (theme.is_valid()) { + theme_name->set_text(TTR("Theme:") + " " + theme->get_path().get_file()); + } } Ref<Theme> ThemeEditor::get_edited_theme() { diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index f5d3306376..4228046ba2 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -41,6 +41,7 @@ #include "core/os/os.h" #include "core/string/string_builder.h" #include "gdscript_warning.h" +#include "servers/text_server.h" #endif // DEBUG_ENABLED #ifdef TOOLS_ENABLED @@ -186,24 +187,6 @@ void GDScriptParser::push_error(const String &p_message, const Node *p_origin) { } #ifdef DEBUG_ENABLED -void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_code, const String &p_symbol1, const String &p_symbol2, const String &p_symbol3, const String &p_symbol4) { - ERR_FAIL_COND(p_source == nullptr); - Vector<String> symbols; - if (!p_symbol1.is_empty()) { - symbols.push_back(p_symbol1); - } - if (!p_symbol2.is_empty()) { - symbols.push_back(p_symbol2); - } - if (!p_symbol3.is_empty()) { - symbols.push_back(p_symbol3); - } - if (!p_symbol4.is_empty()) { - symbols.push_back(p_symbol4); - } - push_warning(p_source, p_code, symbols); -} - void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_code, const Vector<String> &p_symbols) { ERR_FAIL_COND(p_source == nullptr); if (is_ignoring_warnings) { @@ -2251,7 +2234,14 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_expression(bool p_can_assi } GDScriptParser::IdentifierNode *GDScriptParser::parse_identifier() { - return static_cast<IdentifierNode *>(parse_identifier(nullptr, false)); + IdentifierNode *identifier = static_cast<IdentifierNode *>(parse_identifier(nullptr, false)); +#ifdef DEBUG_ENABLED + // Check for spoofing here (if available in TextServer) since this isn't called inside expressions. This is only relevant for declarations. + if (identifier && TS->has_feature(TextServer::FEATURE_UNICODE_SECURITY) && TS->spoof_check(identifier->name.operator String())) { + push_warning(identifier, GDScriptWarning::CONFUSABLE_IDENTIFIER, identifier->name.operator String()); + } +#endif + return identifier; } GDScriptParser::ExpressionNode *GDScriptParser::parse_identifier(ExpressionNode *p_previous_operand, bool p_can_assign) { diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 0903f62061..f6d2a8feee 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -1361,8 +1361,11 @@ private: void clear(); void push_error(const String &p_message, const Node *p_origin = nullptr); #ifdef DEBUG_ENABLED - void push_warning(const Node *p_source, GDScriptWarning::Code p_code, const String &p_symbol1 = String(), const String &p_symbol2 = String(), const String &p_symbol3 = String(), const String &p_symbol4 = String()); void push_warning(const Node *p_source, GDScriptWarning::Code p_code, const Vector<String> &p_symbols); + template <typename... Symbols> + void push_warning(const Node *p_source, GDScriptWarning::Code p_code, const Symbols &...p_symbols) { + push_warning(p_source, p_code, Vector<String>{ p_symbols... }); + } #endif void make_completion_context(CompletionType p_type, Node *p_node, int p_argument = -1, bool p_force = false); diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index e17a804003..d7f1114fd3 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -31,10 +31,14 @@ #include "gdscript_tokenizer.h" #include "core/error/error_macros.h" +#include "core/string/char_utils.h" #ifdef TOOLS_ENABLED #include "editor/editor_settings.h" #endif +#ifdef DEBUG_ENABLED +#include "servers/text_server.h" +#endif static const char *token_names[] = { "Empty", // EMPTY, @@ -435,10 +439,12 @@ GDScriptTokenizer::Token GDScriptTokenizer::check_vcs_marker(char32_t p_test, To } GDScriptTokenizer::Token GDScriptTokenizer::annotation() { - if (!is_ascii_identifier_char(_peek())) { + if (is_unicode_identifier_start(_peek())) { + _advance(); // Consume start character. + } else { push_error("Expected annotation identifier after \"@\"."); } - while (is_ascii_identifier_char(_peek())) { + while (is_unicode_identifier_continue(_peek())) { // Consume all identifier characters. _advance(); } @@ -447,7 +453,6 @@ GDScriptTokenizer::Token GDScriptTokenizer::annotation() { return annotation; } -GDScriptTokenizer::Token GDScriptTokenizer::potential_identifier() { #define KEYWORDS(KEYWORD_GROUP, KEYWORD) \ KEYWORD_GROUP('a') \ KEYWORD("as", Token::AS) \ @@ -512,8 +517,21 @@ GDScriptTokenizer::Token GDScriptTokenizer::potential_identifier() { #define MIN_KEYWORD_LENGTH 2 #define MAX_KEYWORD_LENGTH 10 - // Consume all alphanumeric characters. - while (is_ascii_identifier_char(_peek())) { +#ifdef DEBUG_ENABLED +void GDScriptTokenizer::make_keyword_list() { +#define KEYWORD_LINE(keyword, token_type) keyword, +#define KEYWORD_GROUP_IGNORE(group) + keyword_list = { + KEYWORDS(KEYWORD_GROUP_IGNORE, KEYWORD_LINE) + }; +#undef KEYWORD_LINE +#undef KEYWORD_GROUP_IGNORE +} +#endif // DEBUG_ENABLED + +GDScriptTokenizer::Token GDScriptTokenizer::potential_identifier() { + // Consume all identifier characters. + while (is_unicode_identifier_continue(_peek())) { _advance(); } @@ -565,15 +583,28 @@ GDScriptTokenizer::Token GDScriptTokenizer::potential_identifier() { } // Not a keyword, so must be an identifier. - return make_identifier(name); + Token id = make_identifier(name); + +#ifdef DEBUG_ENABLED + // Additional checks for identifiers but only in debug and if it's available in TextServer. + if (TS->has_feature(TextServer::FEATURE_UNICODE_SECURITY)) { + int64_t confusable = TS->is_confusable(name, keyword_list); + if (confusable >= 0) { + push_error(vformat(R"(Identifier "%s" is visually similar to the GDScript keyword "%s" and thus not allowed.)", name, keyword_list[confusable])); + } + } +#endif // DEBUG_ENABLED + + return id; -#undef KEYWORDS -#undef MIN_KEYWORD_LENGTH -#undef MAX_KEYWORD_LENGTH #undef KEYWORD_GROUP_CASE #undef KEYWORD } +#undef MAX_KEYWORD_LENGTH +#undef MIN_KEYWORD_LENGTH +#undef KEYWORDS + void GDScriptTokenizer::newline(bool p_make_token) { // Don't overwrite previous newline, nor create if we want a line continuation. if (p_make_token && !pending_newline && !line_continuation) { @@ -720,7 +751,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() { error.rightmost_column = column + 1; push_error(error); has_error = true; - } else if (is_ascii_identifier_char(_peek())) { + } else if (is_unicode_identifier_start(_peek()) || is_unicode_identifier_continue(_peek())) { // Letter at the end of the number. push_error("Invalid numeric notation."); } @@ -1311,7 +1342,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::scan() { if (is_digit(c)) { return number(); - } else if (is_ascii_identifier_char(c)) { + } else if (is_unicode_identifier_start(c)) { return potential_identifier(); } @@ -1504,7 +1535,11 @@ GDScriptTokenizer::Token GDScriptTokenizer::scan() { } default: - return make_error(vformat(R"(Unknown character "%s".)", String(&c, 1))); + if (is_whitespace(c)) { + return make_error(vformat(R"(Invalid white space character "\\u%X".)", static_cast<int32_t>(c))); + } else { + return make_error(vformat(R"(Unknown character "%s".)", String(&c, 1))); + } } } @@ -1514,4 +1549,7 @@ GDScriptTokenizer::GDScriptTokenizer() { tab_size = EditorSettings::get_singleton()->get_setting("text_editor/behavior/indent/size"); } #endif // TOOLS_ENABLED +#ifdef DEBUG_ENABLED + make_keyword_list(); +#endif // DEBUG_ENABLED } diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h index 9588922122..608840d3f1 100644 --- a/modules/gdscript/gdscript_tokenizer.h +++ b/modules/gdscript/gdscript_tokenizer.h @@ -224,6 +224,9 @@ private: char32_t indent_char = '\0'; int position = 0; int length = 0; +#ifdef DEBUG_ENABLED + Vector<String> keyword_list; +#endif // DEBUG_ENABLED #ifdef TOOLS_ENABLED HashMap<int, CommentData> comments; @@ -239,6 +242,10 @@ private: void _skip_whitespace(); void check_indent(); +#ifdef DEBUG_ENABLED + void make_keyword_list(); +#endif // DEBUG_ENABLED + Token make_error(const String &p_message); void push_error(const String &p_message); void push_error(const Token &p_error); diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp index 184cecb316..a6cbb7f6ae 100644 --- a/modules/gdscript/gdscript_warning.cpp +++ b/modules/gdscript/gdscript_warning.cpp @@ -155,6 +155,10 @@ String GDScriptWarning::get_message() const { CHECK_SYMBOLS(2); return vformat(R"(The function '%s()' is a static function but was called from an instance. Instead, it should be directly called from the type: '%s.%s()'.)", symbols[0], symbols[1], symbols[0]); } + case CONFUSABLE_IDENTIFIER: { + CHECK_SYMBOLS(1); + return vformat(R"(The identifier "%s" has misleading characters and might be confused with something else.)", symbols[0]); + } case WARNING_MAX: break; // Can't happen, but silences warning } @@ -219,6 +223,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) { "SHADOWED_GLOBAL_IDENTIFIER", "INT_ASSIGNED_TO_ENUM", "STATIC_CALLED_ON_INSTANCE", + "CONFUSABLE_IDENTIFIER", }; static_assert((sizeof(names) / sizeof(*names)) == WARNING_MAX, "Amount of warning types don't match the amount of warning names."); diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h index e3aee45f33..b485f02b9c 100644 --- a/modules/gdscript/gdscript_warning.h +++ b/modules/gdscript/gdscript_warning.h @@ -78,6 +78,7 @@ public: SHADOWED_GLOBAL_IDENTIFIER, // A global class or function has the same name as variable. INT_ASSIGNED_TO_ENUM, // An integer value was assigned to an enum-typed variable without casting. STATIC_CALLED_ON_INSTANCE, // A static method was called on an instance of a class instead of on the class itself. + CONFUSABLE_IDENTIFIER, // The identifier contains misleading characters that can be confused. E.g. "usеr" (has Cyrillic "е" instead of Latin "e"). WARNING_MAX, }; diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/lambda_unused_arg.out b/modules/gdscript/tests/scripts/analyzer/warnings/lambda_unused_arg.out index b018091c18..32e230fc80 100644 --- a/modules/gdscript/tests/scripts/analyzer/warnings/lambda_unused_arg.out +++ b/modules/gdscript/tests/scripts/analyzer/warnings/lambda_unused_arg.out @@ -2,4 +2,4 @@ GDTEST_OK >> WARNING >> Line: 2 >> UNUSED_PARAMETER ->> +>> The parameter 'unused' is never used in the function ''. If this is intended, prefix it with an underscore: '_unused' diff --git a/modules/gdscript/tests/scripts/parser/errors/identifier_similar_to_keyword.gd b/modules/gdscript/tests/scripts/parser/errors/identifier_similar_to_keyword.gd new file mode 100644 index 0000000000..4b1f284070 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/identifier_similar_to_keyword.gd @@ -0,0 +1,3 @@ +func test(): + var аs # Using Cyrillic "а". + print(аs) diff --git a/modules/gdscript/tests/scripts/parser/errors/identifier_similar_to_keyword.out b/modules/gdscript/tests/scripts/parser/errors/identifier_similar_to_keyword.out new file mode 100644 index 0000000000..337dec2f4d --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/identifier_similar_to_keyword.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Identifier "аs" is visually similar to the GDScript keyword "as" and thus not allowed. diff --git a/modules/gdscript/tests/scripts/parser/features/unicode_identifiers.gd b/modules/gdscript/tests/scripts/parser/features/unicode_identifiers.gd new file mode 100644 index 0000000000..523959a016 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/unicode_identifiers.gd @@ -0,0 +1,35 @@ +const π = PI +var ㄥ = π + +func test(): + var փորձարկում = "test" + prints("փորձարկում", փորձարկում) + var امتحان = "test" + prints("امتحان", امتحان) + var পরীক্ষা = "test" + prints("পরীক্ষা", পরীক্ষা) + var тест = "test" + prints("тест", тест) + var जाँच = "test" + prints("जाँच", जाँच) + var 기준 = "test" + prints("기준", 기준) + var 测试 = "test" + prints("测试", 测试) + var テスト = "test" + prints("テスト", テスト) + var 試験 = "test" + prints("試験", 試験) + var പരീക്ഷ = "test" + prints("പരീക്ഷ", പരീക്ഷ) + var ทดสอบ = "test" + prints("ทดสอบ", ทดสอบ) + var δοκιμή = "test" + prints("δοκιμή", δοκιμή) + + const d = 1.1 + _process(d) + print(is_equal_approx(ㄥ, PI + (d * PI))) + +func _process(Δ: float) -> void: + ㄥ += Δ * π diff --git a/modules/gdscript/tests/scripts/parser/features/unicode_identifiers.out b/modules/gdscript/tests/scripts/parser/features/unicode_identifiers.out new file mode 100644 index 0000000000..c071380a8f --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/unicode_identifiers.out @@ -0,0 +1,14 @@ +GDTEST_OK +փորձարկում test +امتحان test +পরীক্ষা test +тест test +जाँच test +기준 test +测试 test +テスト test +試験 test +പരീക്ഷ test +ทดสอบ test +δοκιμή test +true diff --git a/modules/gdscript/tests/scripts/parser/warnings/confusable_identifier.gd b/modules/gdscript/tests/scripts/parser/warnings/confusable_identifier.gd new file mode 100644 index 0000000000..e2caac8ffd --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/confusable_identifier.gd @@ -0,0 +1,5 @@ +func test(): + var port = 0 # Only latin characters. + var pοrt = 1 # The "ο" is Greek omicron. + + prints(port, pοrt) diff --git a/modules/gdscript/tests/scripts/parser/warnings/confusable_identifier.out b/modules/gdscript/tests/scripts/parser/warnings/confusable_identifier.out new file mode 100644 index 0000000000..c483396443 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/confusable_identifier.out @@ -0,0 +1,6 @@ +GDTEST_OK +>> WARNING +>> Line: 3 +>> CONFUSABLE_IDENTIFIER +>> The identifier "pοrt" has misleading characters and might be confused with something else. +0 1 diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 5634c51c61..b243ba933d 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -6072,6 +6072,7 @@ void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_ const int track_idx = animation->get_track_count(); animation->add_track(Animation::TYPE_BLEND_SHAPE); animation->track_set_path(track_idx, blend_path); + animation->track_set_imported(track_idx, true); //helps merging later // Only LINEAR and STEP (NEAREST) can be supported out of the box by Godot's Animation, // the other modes have to be baked. diff --git a/platform/ios/detect.py b/platform/ios/detect.py index 38e62134b5..71612cf1fa 100644 --- a/platform/ios/detect.py +++ b/platform/ios/detect.py @@ -99,8 +99,8 @@ def configure(env: "Environment"): if env["ios_simulator"]: detect_darwin_sdk_path("iossimulator", env) - env.Append(ASFLAGS=["-mios-simulator-version-min=13.0"]) - env.Append(CCFLAGS=["-mios-simulator-version-min=13.0"]) + env.Append(ASFLAGS=["-mios-simulator-version-min=11.0"]) + env.Append(CCFLAGS=["-mios-simulator-version-min=11.0"]) env.extra_suffix = ".simulator" + env.extra_suffix else: detect_darwin_sdk_path("ios", env) @@ -153,3 +153,11 @@ def configure(env: "Environment"): if env["vulkan"]: env.Append(CPPDEFINES=["VULKAN_ENABLED"]) + + if env["opengl3"]: + env.Append(CPPDEFINES=["GLES3_ENABLED"]) + env.Prepend( + CPPPATH=[ + "$IOS_SDK_PATH/System/Library/Frameworks/OpenGLES.framework/Headers", + ] + ) diff --git a/platform/ios/display_layer.h b/platform/ios/display_layer.h index 10f42264df..e719a42cd9 100644 --- a/platform/ios/display_layer.h +++ b/platform/ios/display_layer.h @@ -33,7 +33,8 @@ @protocol DisplayLayer <NSObject> -- (void)renderDisplayLayer; +- (void)startRenderDisplayLayer; +- (void)stopRenderDisplayLayer; - (void)initializeDisplayLayer; - (void)layoutDisplayLayer; diff --git a/platform/ios/display_layer.mm b/platform/ios/display_layer.mm index 24614fea56..3129ebb276 100644 --- a/platform/ios/display_layer.mm +++ b/platform/ios/display_layer.mm @@ -60,7 +60,10 @@ - (void)layoutDisplayLayer { } -- (void)renderDisplayLayer { +- (void)startRenderDisplayLayer { +} + +- (void)stopRenderDisplayLayer { } @end @@ -76,8 +79,6 @@ } - (void)initializeDisplayLayer { - // Get our backing layer - // Configure it so that it is opaque, does not retain the contents of the backbuffer when displayed, and uses RGBA8888 color. self.opaque = YES; self.drawableProperties = [NSDictionary @@ -87,8 +88,6 @@ kEAGLDrawablePropertyColorFormat, nil]; - // FIXME: Add Vulkan support via MoltenVK. Add fallback code back? - // Create GL ES 3 context if (GLOBAL_GET("rendering/renderer/rendering_method") == "gl_compatibility") { context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; @@ -115,8 +114,22 @@ [self createFramebuffer]; } -- (void)renderDisplayLayer { +- (void)startRenderDisplayLayer { [EAGLContext setCurrentContext:context]; + + glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); +} + +- (void)stopRenderDisplayLayer { + glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); + [context presentRenderbuffer:GL_RENDERBUFFER_OES]; + +#ifdef DEBUG_ENABLED + GLenum err = glGetError(); + if (err) { + NSLog(@"DrawView: %x error", err); + } +#endif } - (void)dealloc { @@ -154,11 +167,15 @@ return NO; } + GLES3::TextureStorage::system_fbo = viewFramebuffer; + return YES; } // Clean up any buffers we have allocated. - (void)destroyFramebuffer { + GLES3::TextureStorage::system_fbo = 0; + glDeleteFramebuffersOES(1, &viewFramebuffer); viewFramebuffer = 0; glDeleteRenderbuffersOES(1, &viewRenderbuffer); diff --git a/platform/ios/display_server_ios.h b/platform/ios/display_server_ios.h index dd1157f668..5a21f3bbad 100644 --- a/platform/ios/display_server_ios.h +++ b/platform/ios/display_server_ios.h @@ -47,6 +47,10 @@ #endif #endif +#if defined(GLES3_ENABLED) +#include "drivers/gles3/rasterizer_gles3.h" +#endif // GLES3_ENABLED + #import <Foundation/Foundation.h> #import <QuartzCore/CAMetalLayer.h> @@ -216,6 +220,7 @@ public: virtual bool screen_is_kept_on() const override; void resize_window(CGSize size); + virtual void swap_buffers() override {} }; #endif // DISPLAY_SERVER_IOS_H diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm index 86ea602a98..881945c183 100644 --- a/platform/ios/display_server_ios.mm +++ b/platform/ios/display_server_ios.mm @@ -55,28 +55,7 @@ DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode // Init TTS tts = [[TTS_IOS alloc] init]; -#if defined(GLES3_ENABLED) - if (rendering_driver == "opengl3") { - bool gl_initialization_error = false; - - if (RasterizerGLES3::is_viable() == OK) { - RasterizerGLES3::register_config(); - RasterizerGLES3::make_current(); - } else { - gl_initialization_error = true; - } - - if (gl_initialization_error) { - OS::get_singleton()->alert( - "Your device seems not to support the required OpenGL ES 3.0 version.\n\n", - "Unable to initialize OpenGL video driver"); - } - } -#endif - #if defined(VULKAN_ENABLED) - rendering_driver = "vulkan"; - context_vulkan = nullptr; rendering_device_vulkan = nullptr; @@ -91,13 +70,14 @@ DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode CALayer *layer = [AppDelegate.viewController.godotView initializeRenderingForDriver:@"vulkan"]; if (!layer) { - ERR_FAIL_MSG("Failed to create iOS rendering layer."); + ERR_FAIL_MSG("Failed to create iOS Vulkan rendering layer."); } Size2i size = Size2i(layer.bounds.size.width, layer.bounds.size.height) * screen_get_max_scale(); if (context_vulkan->window_create(MAIN_WINDOW_ID, p_vsync_mode, layer, size.width, size.height) != OK) { memdelete(context_vulkan); context_vulkan = nullptr; + r_error = ERR_UNAVAILABLE; ERR_FAIL_MSG("Failed to create Vulkan window."); } @@ -108,6 +88,18 @@ DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode } #endif +#if defined(GLES3_ENABLED) + if (rendering_driver == "opengl3") { + CALayer *layer = [AppDelegate.viewController.godotView initializeRenderingForDriver:@"opengl3"]; + + if (!layer) { + ERR_FAIL_MSG("Failed to create iOS OpenGLES rendering layer."); + } + + RasterizerGLES3::make_current(); + } +#endif + bool keep_screen_on = bool(GLOBAL_GET("display/window/energy_saving/keep_screen_on")); screen_set_keep_on(keep_screen_on); @@ -670,15 +662,18 @@ void DisplayServerIOS::resize_window(CGSize viewSize) { void DisplayServerIOS::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) { _THREAD_SAFE_METHOD_ #if defined(VULKAN_ENABLED) - context_vulkan->set_vsync_mode(p_window, p_vsync_mode); + if (context_vulkan) { + context_vulkan->set_vsync_mode(p_window, p_vsync_mode); + } #endif } DisplayServer::VSyncMode DisplayServerIOS::window_get_vsync_mode(WindowID p_window) const { _THREAD_SAFE_METHOD_ #if defined(VULKAN_ENABLED) - return context_vulkan->get_vsync_mode(p_window); -#else - return DisplayServer::VSYNC_ENABLED; + if (context_vulkan) { + return context_vulkan->get_vsync_mode(p_window); + } #endif + return DisplayServer::VSYNC_ENABLED; } diff --git a/platform/ios/godot_view.mm b/platform/ios/godot_view.mm index 1c8c578bd2..fbe3a1150a 100644 --- a/platform/ios/godot_view.mm +++ b/platform/ios/godot_view.mm @@ -74,16 +74,20 @@ static const float earth_gravity = 9.80665; CALayer<DisplayLayer> *layer; if ([driverName isEqualToString:@"vulkan"]) { +#if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR + if (@available(iOS 13, *)) { + layer = [GodotMetalLayer layer]; + } else { + return nil; + } +#else layer = [GodotMetalLayer layer]; +#endif } else if ([driverName isEqualToString:@"opengl3"]) { if (@available(iOS 13, *)) { NSLog(@"OpenGL ES is deprecated on iOS 13"); } -#if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR - return nil; -#else layer = [GodotOpenGLLayer layer]; -#endif } else { return nil; } @@ -238,7 +242,7 @@ static const float earth_gravity = 9.80665; [self.displayLink setPaused:NO]; } - [self.renderingLayer renderDisplayLayer]; + [self.renderingLayer startRenderDisplayLayer]; if (!self.renderer) { return; @@ -258,6 +262,8 @@ static const float earth_gravity = 9.80665; [self handleMotion]; [self.renderer renderOnView:self]; + + [self.renderingLayer stopRenderDisplayLayer]; } - (BOOL)canRender { diff --git a/platform/macos/detect.py b/platform/macos/detect.py index 14e6e92bfa..cd46dab4f3 100644 --- a/platform/macos/detect.py +++ b/platform/macos/detect.py @@ -230,7 +230,7 @@ def configure(env: "Environment"): env.Append(LIBS=["pthread", "z"]) if env["opengl3"]: - env.Append(CPPDEFINES=["GLES_ENABLED", "GLES3_ENABLED"]) + env.Append(CPPDEFINES=["GLES3_ENABLED"]) env.Append(LINKFLAGS=["-framework", "OpenGL"]) env.Append(LINKFLAGS=["-rpath", "@executable_path/../Frameworks", "-rpath", "@executable_path"]) diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index d992467042..fddd57aef2 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -3142,7 +3142,9 @@ ObjectID DisplayServerMacOS::window_get_attached_instance_id(WindowID p_window) void DisplayServerMacOS::gl_window_make_current(DisplayServer::WindowID p_window_id) { #if defined(GLES3_ENABLED) - gl_manager->window_make_current(p_window_id); + if (gl_manager) { + gl_manager->window_make_current(p_window_id); + } #endif } diff --git a/platform/windows/joypad_windows.cpp b/platform/windows/joypad_windows.cpp index 183958cf40..7ae26e6cf4 100644 --- a/platform/windows/joypad_windows.cpp +++ b/platform/windows/joypad_windows.cpp @@ -101,10 +101,12 @@ bool JoypadWindows::is_xinput_device(const GUID *p_guid) { static GUID IID_ValveStreamingGamepad = { MAKELONG(0x28DE, 0x11FF), 0x28DE, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } }; static GUID IID_X360WiredGamepad = { MAKELONG(0x045E, 0x02A1), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } }; static GUID IID_X360WirelessGamepad = { MAKELONG(0x045E, 0x028E), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } }; + static GUID IID_XSWirelessGamepad = { MAKELONG(0x045E, 0x0B13), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } }; if (memcmp(p_guid, &IID_ValveStreamingGamepad, sizeof(*p_guid)) == 0 || memcmp(p_guid, &IID_X360WiredGamepad, sizeof(*p_guid)) == 0 || - memcmp(p_guid, &IID_X360WirelessGamepad, sizeof(*p_guid)) == 0) + memcmp(p_guid, &IID_X360WirelessGamepad, sizeof(*p_guid)) == 0 || + memcmp(p_guid, &IID_XSWirelessGamepad, sizeof(*p_guid)) == 0) return true; PRAWINPUTDEVICELIST dev_list = nullptr; diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp index 26ada1da5a..19d1b83cab 100644 --- a/scene/3d/collision_object_3d.cpp +++ b/scene/3d/collision_object_3d.cpp @@ -43,6 +43,11 @@ void CollisionObject3D::_notification(int p_what) { } _update_debug_shapes(); } +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + set_notify_local_transform(true); // Used for warnings and only in editor. + } +#endif } break; case NOTIFICATION_EXIT_TREE: { @@ -78,6 +83,14 @@ void CollisionObject3D::_notification(int p_what) { _update_pickable(); } break; +#ifdef TOOLS_ENABLED + case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { + if (Engine::get_singleton()->is_editor_hint()) { + update_configuration_warnings(); + } + } break; +#endif + case NOTIFICATION_TRANSFORM_CHANGED: { if (only_update_transform_changes) { return; @@ -724,6 +737,11 @@ PackedStringArray CollisionObject3D::get_configuration_warnings() const { warnings.push_back(RTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape3D or CollisionPolygon3D as a child to define its shape.")); } + Vector3 scale = get_transform().get_basis().get_scale(); + if (!(Math::is_zero_approx(scale.x - scale.y) && Math::is_zero_approx(scale.y - scale.z))) { + warnings.push_back(RTR("With a non-uniform scale this node will probably not function as expected.\nPlease make its scale uniform (i.e. the same on all axes), and change the size in children collision shapes instead.")); + } + return warnings; } diff --git a/scene/3d/collision_polygon_3d.cpp b/scene/3d/collision_polygon_3d.cpp index 5fb8970085..53a61c1368 100644 --- a/scene/3d/collision_polygon_3d.cpp +++ b/scene/3d/collision_polygon_3d.cpp @@ -104,6 +104,11 @@ void CollisionPolygon3D::_notification(int p_what) { if (parent) { _update_in_shape_owner(true); } +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + update_configuration_warnings(); + } +#endif } break; case NOTIFICATION_UNPARENTED: { @@ -171,13 +176,18 @@ PackedStringArray CollisionPolygon3D::get_configuration_warnings() const { PackedStringArray warnings = Node::get_configuration_warnings(); if (!Object::cast_to<CollisionObject3D>(get_parent())) { - warnings.push_back(RTR("CollisionPolygon3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidBody3D, CharacterBody3D, etc. to give them a shape.")); + warnings.push_back(RTR("CollisionPolygon3D only serves to provide a collision shape to a CollisionObject3D derived node.\nPlease only use it as a child of Area3D, StaticBody3D, RigidBody3D, CharacterBody3D, etc. to give them a shape.")); } if (polygon.is_empty()) { warnings.push_back(RTR("An empty CollisionPolygon3D has no effect on collision.")); } + Vector3 scale = get_transform().get_basis().get_scale(); + if (!(Math::is_zero_approx(scale.x - scale.y) && Math::is_zero_approx(scale.y - scale.z))) { + warnings.push_back(RTR("A non-uniformly scaled CollisionPolygon3D node will probably not function as expected.\nPlease make its scale uniform (i.e. the same on all axes), and change its polygon's vertices instead.")); + } + return warnings; } diff --git a/scene/3d/collision_shape_3d.cpp b/scene/3d/collision_shape_3d.cpp index 1709a17bce..dbd50cfd19 100644 --- a/scene/3d/collision_shape_3d.cpp +++ b/scene/3d/collision_shape_3d.cpp @@ -99,6 +99,11 @@ void CollisionShape3D::_notification(int p_what) { if (parent) { _update_in_shape_owner(true); } +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + update_configuration_warnings(); + } +#endif } break; case NOTIFICATION_UNPARENTED: { @@ -134,6 +139,11 @@ PackedStringArray CollisionShape3D::get_configuration_warnings() const { } } + Vector3 scale = get_transform().get_basis().get_scale(); + if (!(Math::is_zero_approx(scale.x - scale.y) && Math::is_zero_approx(scale.y - scale.z))) { + warnings.push_back(RTR("A non-uniformly scaled CollisionShape3D node will probably not function as expected.\nPlease make its scale uniform (i.e. the same on all axes), and change the size of its shape resource instead.")); + } + return warnings; } diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index 106efbc596..28f3dbd409 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -977,12 +977,11 @@ TypedArray<Node3D> RigidBody3D::get_colliding_bodies() const { } PackedStringArray RigidBody3D::get_configuration_warnings() const { - Transform3D t = get_transform(); + PackedStringArray warnings = CollisionObject3D::get_configuration_warnings(); - PackedStringArray warnings = Node::get_configuration_warnings(); - - if (ABS(t.basis.get_column(0).length() - 1.0) > 0.05 || ABS(t.basis.get_column(1).length() - 1.0) > 0.05 || ABS(t.basis.get_column(2).length() - 1.0) > 0.05) { - warnings.push_back(RTR("Size changes to RigidBody will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); + Vector3 scale = get_transform().get_basis().get_scale(); + if (ABS(scale.x - 1.0) > 0.05 || ABS(scale.y - 1.0) > 0.05 || ABS(scale.z - 1.0) > 0.05) { + warnings.push_back(RTR("Scale changes to RigidBody3D will be overridden by the physics engine when running.\nPlease change the size in children collision shapes instead.")); } return warnings; diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_body_3d.cpp index e8b51ceb92..e7e3084037 100644 --- a/scene/3d/soft_body_3d.cpp +++ b/scene/3d/soft_body_3d.cpp @@ -302,14 +302,6 @@ void SoftBody3D::_notification(int p_what) { _prepare_physics_server(); } } break; - -#ifdef TOOLS_ENABLED - case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { - if (Engine::get_singleton()->is_editor_hint()) { - update_configuration_warnings(); - } - } break; -#endif } } @@ -391,11 +383,6 @@ PackedStringArray SoftBody3D::get_configuration_warnings() const { warnings.push_back(RTR("This body will be ignored until you set a mesh.")); } - Transform3D t = get_transform(); - if ((ABS(t.basis.get_column(0).length() - 1.0) > 0.05 || ABS(t.basis.get_column(1).length() - 1.0) > 0.05 || ABS(t.basis.get_column(2).length() - 1.0) > 0.05)) { - warnings.push_back(RTR("Size changes to SoftBody3D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); - } - return warnings; } diff --git a/scene/resources/camera_attributes.cpp b/scene/resources/camera_attributes.cpp index b5e302cce5..61df56523d 100644 --- a/scene/resources/camera_attributes.cpp +++ b/scene/resources/camera_attributes.cpp @@ -286,10 +286,10 @@ void CameraAttributesPractical::_bind_methods() { ADD_GROUP("DOF Blur", "dof_blur_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dof_blur_far_enabled"), "set_dof_blur_far_enabled", "is_dof_blur_far_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_distance", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp,suffix:m"), "set_dof_blur_far_distance", "get_dof_blur_far_distance"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_transition", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp"), "set_dof_blur_far_transition", "get_dof_blur_far_transition"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_transition", PROPERTY_HINT_RANGE, "-1,8192,0.01,exp"), "set_dof_blur_far_transition", "get_dof_blur_far_transition"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dof_blur_near_enabled"), "set_dof_blur_near_enabled", "is_dof_blur_near_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_distance", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp,suffix:m"), "set_dof_blur_near_distance", "get_dof_blur_near_distance"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_transition", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp"), "set_dof_blur_near_transition", "get_dof_blur_near_transition"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_transition", PROPERTY_HINT_RANGE, "-1,8192,0.01,exp"), "set_dof_blur_near_transition", "get_dof_blur_near_transition"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_amount", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_dof_blur_amount", "get_dof_blur_amount"); ADD_GROUP("Auto Exposure", "auto_exposure_"); diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index c93b51552f..991e060e94 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -1122,17 +1122,6 @@ bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) { } int idx = sname.substr(8, sl - 8).to_int(); - // This is a bit of a hack to ensure compatibility with older material - // overrides that start indexing at 1. - // We assume that idx 0 is always read first, if its not, this won't work. - if (idx == 0) { - surface_index_0 = true; - } - if (!surface_index_0) { - // This means the file was created when the indexing started at 1, so decrease by one. - idx--; - } - String what = sname.get_slicec('/', 1); if (what == "material") { surface_set_material(idx, p_value); diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl index d32e6d717f..46c53c11ae 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl @@ -1798,7 +1798,7 @@ void fragment_shader(in SceneData scene_data) { shadow = float(shadow1 >> ((i - 4u) * 8u) & 0xFFu) / 255.0; } - shadow = shadow * directional_lights.data[i].shadow_opacity + 1.0 - directional_lights.data[i].shadow_opacity; + shadow = mix(1.0, shadow, directional_lights.data[i].shadow_opacity); #endif blur_shadow(shadow); diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl index 5e64d4e651..2966a2ff65 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl @@ -1515,6 +1515,8 @@ void main() { } else { shadow = float(shadow1 >> ((i - 4) * 8) & 0xFF) / 255.0; } + + shadow = mix(1.0, shadow, directional_lights.data[i].shadow_opacity); #endif blur_shadow(shadow); |