diff options
33 files changed, 302 insertions, 220 deletions
diff --git a/core/io/logger.cpp b/core/io/logger.cpp index eeb82bfce4..9175f6a262 100644 --- a/core/io/logger.cpp +++ b/core/io/logger.cpp @@ -39,7 +39,7 @@ // va_copy, otherwise you have to use the internal version (__va_copy). #if !defined(va_copy) #if defined(__GNUC__) -#define va_copy(d, s) __va_copy(d, s) +#define va_copy(d, s) __va_copy((d), (s)) #else #define va_copy(d, s) ((d) = (s)) #endif diff --git a/core/math/geometry.cpp b/core/math/geometry.cpp index 194a6f6352..a84b5a16c7 100644 --- a/core/math/geometry.cpp +++ b/core/math/geometry.cpp @@ -515,7 +515,7 @@ static inline void _build_faces(uint8_t ***p_cell_status, int x, int y, int z, i Vector3(1,1,1), }; */ -#define vert(m_idx) Vector3((m_idx & 4) >> 2, (m_idx & 2) >> 1, m_idx & 1) +#define vert(m_idx) Vector3(((m_idx)&4) >> 2, ((m_idx)&2) >> 1, (m_idx)&1) static const uint8_t indices[6][4] = { { 7, 6, 4, 5 }, diff --git a/core/typedefs.h b/core/typedefs.h index 03514466c0..78688ff311 100644 --- a/core/typedefs.h +++ b/core/typedefs.h @@ -137,7 +137,7 @@ T *_nullptr() { /** Generic swap template */ #ifndef SWAP -#define SWAP(m_x, m_y) __swap_tmpl(m_x, m_y) +#define SWAP(m_x, m_y) __swap_tmpl((m_x), (m_y)) template <class T> inline void __swap_tmpl(T &x, T &y) { diff --git a/doc/classes/AnimatedTexture.xml b/doc/classes/AnimatedTexture.xml index 08cd79e78f..871ae1a701 100644 --- a/doc/classes/AnimatedTexture.xml +++ b/doc/classes/AnimatedTexture.xml @@ -4,7 +4,7 @@ Proxy texture for simple frame-based animations. </brief_description> <description> - [code]AnimatedTexture[/code] is a resource format for simple frame-based animations, where multiple frames textures can be chained automatically with a predefined delay for each frame. It's not a [Node], contrarily to [AnimationPlayer] or [AnimatedSprite], but has the advantage of being usable at any place where a [Texture] resource can be used, e.g. in a [TileSet]. + [code]AnimatedTexture[/code] is a resource format for frame-based animations, where multiple textures can be chained automatically with a predefined delay for each frame. Unlike [AnimationPlayer] or [AnimatedSprite], it isn't a [Node], but has the advantage of being usable anywhere a [Texture] resource can be used, e.g. in a [TileSet]. The playback of the animation is controlled by the [member fps] property as well as each frame's optional delay (see [method set_frame_delay]). The animation loops, i.e. it will restart at frame 0 automatically after playing the last frame. [code]AnimatedTexture[/code] currently requires all frame textures to have the same size, otherwise the bigger ones will be cropped to match the smallest one. </description> @@ -19,7 +19,7 @@ <argument index="0" name="frame" type="int"> </argument> <description> - Retrieves the delayed assigned to the given [code]frame[/code] ID. + Returns the given frame's delay value. </description> </method> <method name="get_frame_texture" qualifiers="const"> @@ -28,7 +28,7 @@ <argument index="0" name="frame" type="int"> </argument> <description> - Retrieves the [Texture] assigned to the given [code]frame[/code] ID. + Returns the given frame's [Texture]. </description> </method> <method name="set_frame_delay"> @@ -39,7 +39,7 @@ <argument index="1" name="delay" type="float"> </argument> <description> - Defines an additional delay (in seconds) between this frame and the next one, that will be added to the time interval defined by [member fps]. By default, frames have no delay defined. If a delay value is defined, the final time interval between this frame and the next will be [code]1.0 / fps + delay[/code]. + Sets an additional delay (in seconds) between this frame and the next one, that will be added to the time interval defined by [member fps]. By default, frames have no delay defined. If a delay value is defined, the final time interval between this frame and the next will be [code]1.0 / fps + delay[/code]. For example, for an animation with 3 frames, 2 FPS and a frame delay on the second frame of 1.2, the resulting playback will be: [codeblock] Frame 0: 0.5 s (1 / fps) @@ -57,15 +57,15 @@ <argument index="1" name="texture" type="Texture"> </argument> <description> - Assigns a [Texture] to the given [code]frame[/code] ID. IDs start at 0 (so the first frame has ID 0, and the last frame of the animation has ID [member frames] - 1). - You can define any frame texture up to [constant MAX_FRAMES], but keep in mind that only frames from 0 to [member frames] - 1 will be part of the animation. + Assigns a [Texture] to the given frame. Frame IDs start at 0, so the first frame has ID 0, and the last frame of the animation has ID [member frames] - 1. + You can define any number of textures up to [constant MAX_FRAMES], but keep in mind that only frames from 0 to [member frames] - 1 will be part of the animation. </description> </method> </methods> <members> <member name="fps" type="float" setter="set_fps" getter="get_fps"> - Number of frames per second. This value defines the default time interval between two frames of the animation, and thus the overall duration of the animation loop based on the [member frames] property. A value of 0 means no predefined number of frames per second, the animation will play according to each frame's frame delay (see [method set_frame_delay]). Default value: 4. - For example, an animation with 8 frames, no frame delay and a [code]fps[/code] value of 2 will run over 4 seconds, with one frame each 0.5 seconds. + Animation speed in frames per second. This value defines the default time interval between two frames of the animation, and thus the overall duration of the animation loop based on the [member frames] property. A value of 0 means no predefined number of frames per second, the animation will play according to each frame's frame delay (see [method set_frame_delay]). Default value: 4. + For example, an animation with 8 frames, no frame delay and a [code]fps[/code] value of 2 will run for 4 seconds, with each frame lasting 0.5 seconds. </member> <member name="frames" type="int" setter="set_frames" getter="get_frames"> Number of frames to use in the animation. While you can create the frames independently with [method set_frame_texture], you need to set this value for the animation to take new frames into account. The maximum number of frames is [constant MAX_FRAMES]. Default value: 1. diff --git a/doc/classes/Dictionary.xml b/doc/classes/Dictionary.xml index 9c78853cfe..d87b029676 100644 --- a/doc/classes/Dictionary.xml +++ b/doc/classes/Dictionary.xml @@ -6,8 +6,18 @@ <description> Dictionary type. Associative container which contains values referenced by unique keys. Dictionaries are always passed by reference. Erasing elements while iterating over them [b]is not supported[/b]. + Creating a dictionary: + [codeblock] + var d = {4: 5, "A key": "A value", 28: [1, 2, 3]} + [/codeblock] + To add a key to an existing dictionary, access it like an existing key and assign to it: + [codeblock] + d[4] = "hello" # Add integer 4 as a key and assign the String "hello" as its value. + d["Godot"] = 3.01 # Add String "Godot" as a key and assign the value 3.01 to it. + [/codeblock] </description> <tutorials> + <link>https://docs.godotengine.org/en/latest/getting_started/scripting/gdscript/gdscript_basics.html#dictionary</link> </tutorials> <demos> </demos> diff --git a/doc/classes/KinematicBody2D.xml b/doc/classes/KinematicBody2D.xml index d5bfc91f66..8aaa7c4ace 100644 --- a/doc/classes/KinematicBody2D.xml +++ b/doc/classes/KinematicBody2D.xml @@ -27,6 +27,12 @@ </argument> <description> Returns a [KinematicCollision2D], which contains information about a collision that occurred during the last [method move_and_slide] call. Since the body can collide several times in a single call to [method move_and_slide], you must specify the index of the collision in the range 0 to ([method get_slide_count] - 1). + Example usage: + [codeblock] + for i in get_slide_count(): + var collision = get_slide_collision(i) + print("Collided with: ", collision.collider.name) + [/codeblock] </description> </method> <method name="get_slide_count" qualifiers="const"> @@ -94,7 +100,7 @@ If the body is standing on a slope and the horizontal speed (relative to the floor's speed) goes below [code]slope_stop_min_velocity[/code], the body will stop completely. This prevents the body from sliding down slopes when you include gravity in [code]linear_velocity[/code]. When set to lower values, the body will not be able to stand still on steep slopes. If the body collides, it will change direction a maximum of [code]max_slides[/code] times before it stops. [code]floor_max_angle[/code] is the maximum angle (in radians) where a slope is still considered a floor (or a ceiling), rather than a wall. The default value equals 45 degrees. - Returns the [code]linear_velocity[/code] vector, rotated and/or scaled if a slide collision occurred. To get more detailed information about collisions that occurred, use [method get_slide_collision]. + Returns the [code]linear_velocity[/code] vector, rotated and/or scaled if a slide collision occurred. To get detailed information about collisions that occurred, use [method get_slide_collision]. </description> </method> <method name="move_and_slide_with_snap"> diff --git a/doc/classes/Particles2D.xml b/doc/classes/Particles2D.xml index 91c1a0ca9b..53088e909e 100644 --- a/doc/classes/Particles2D.xml +++ b/doc/classes/Particles2D.xml @@ -8,6 +8,7 @@ Use the [code]process_material[/code] property to add a [ParticlesMaterial] to configure particle appearance and behavior. Alternatively, you can add a [ShaderMaterial] which will be applied to all particles. </description> <tutorials> + <link>https://docs.godotengine.org/en/latest/tutorials/2d/particle_systems_2d.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/RayCast.xml b/doc/classes/RayCast.xml index 61f2737c01..22c70b789f 100644 --- a/doc/classes/RayCast.xml +++ b/doc/classes/RayCast.xml @@ -5,10 +5,10 @@ </brief_description> <description> A RayCast represents a line from its origin to its destination position, [code]cast_to[/code]. It is used to query the 3D space in order to find the closest object along the path of the ray. - RayCast can ignore some objects by adding them to the exception list via [code]add_exception[/code], by setting proper filtering with collision layers, or by filtering object types with type masks. + RayCast can ignore some objects by adding them to the exception list via [code]add_exception[/code] or by setting proper filtering with collision layers and masks. RayCast can be configured to report collisions with [Area]s ([member collide_with_areas]) and/or [PhysicsBody]s ([member collide_with_bodies]). Only enabled raycasts will be able to query the space and report collisions. - RayCast calculates intersection every physics frame (see [Node]), and the result is cached so it can be used later until the next frame. If multiple queries are required between physics frames (or during the same frame) use [method force_raycast_update] after adjusting the raycast. + RayCast calculates intersection every physics frame (see [Node]), and the result is cached so it can be used later until the next frame. If multiple queries are required between physics frames (or during the same frame), use [method force_raycast_update] after adjusting the raycast. </description> <tutorials> </tutorials> diff --git a/doc/classes/Rect2.xml b/doc/classes/Rect2.xml index 45bfd16cb8..248b3a6010 100644 --- a/doc/classes/Rect2.xml +++ b/doc/classes/Rect2.xml @@ -145,7 +145,7 @@ <argument index="0" name="b" type="Rect2"> </argument> <description> - Returns a larger Rect2 that contains this Rect2 and [code]with[/code]. + Returns a larger Rect2 that contains this Rect2 and [code]b[/code]. </description> </method> </methods> diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 7ee4d5b03b..0b06839ecf 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -1085,6 +1085,79 @@ void CodeTextEditor::clone_lines_down() { text_editor->update(); } +void CodeTextEditor::toggle_inline_comment(const String &delimiter) { + text_editor->begin_complex_operation(); + if (text_editor->is_selection_active()) { + int begin = text_editor->get_selection_from_line(); + int end = text_editor->get_selection_to_line(); + + // End of selection ends on the first column of the last line, ignore it. + if (text_editor->get_selection_to_column() == 0) + end -= 1; + + int col_to = text_editor->get_selection_to_column(); + int cursor_pos = text_editor->cursor_get_column(); + + // Check if all lines in the selected block are commented + bool is_commented = true; + for (int i = begin; i <= end; i++) { + if (!text_editor->get_line(i).begins_with(delimiter)) { + is_commented = false; + break; + } + } + for (int i = begin; i <= end; i++) { + String line_text = text_editor->get_line(i); + + if (line_text.strip_edges().empty()) { + line_text = delimiter; + } else { + if (is_commented) { + line_text = line_text.substr(delimiter.length(), line_text.length()); + } else { + line_text = delimiter + line_text; + } + } + text_editor->set_line(i, line_text); + } + + // Adjust selection & cursor position. + int offset = (is_commented ? -1 : 1) * delimiter.length(); + int col_from = text_editor->get_selection_from_column() > 0 ? text_editor->get_selection_from_column() + offset : 0; + + if (is_commented && text_editor->cursor_get_column() == text_editor->get_line(text_editor->cursor_get_line()).length() + 1) + cursor_pos += 1; + + if (text_editor->get_selection_to_column() != 0 && col_to != text_editor->get_line(text_editor->get_selection_to_line()).length() + 1) + col_to += offset; + + if (text_editor->cursor_get_column() != 0) + cursor_pos += offset; + + text_editor->select(begin, col_from, text_editor->get_selection_to_line(), col_to); + text_editor->cursor_set_column(cursor_pos); + + } else { + int begin = text_editor->cursor_get_line(); + String line_text = text_editor->get_line(begin); + int delimiter_length = delimiter.length(); + + int col = text_editor->cursor_get_column(); + if (line_text.begins_with(delimiter)) { + line_text = line_text.substr(delimiter_length, line_text.length()); + col -= delimiter_length; + } else { + line_text = delimiter + line_text; + col += delimiter_length; + } + + text_editor->set_line(begin, line_text); + text_editor->cursor_set_column(col); + } + text_editor->end_complex_operation(); + text_editor->update(); +} + void CodeTextEditor::goto_line(int p_line) { text_editor->deselect(); text_editor->unfold_line(p_line); diff --git a/editor/code_editor.h b/editor/code_editor.h index 67e8fc9d3c..e3dbfe1ce0 100644 --- a/editor/code_editor.h +++ b/editor/code_editor.h @@ -211,6 +211,10 @@ public: void delete_lines(); void clone_lines_down(); + /// Toggle inline comment on currently selected lines, or on current line if nothing is selected, + /// by adding or removing comment delimiter + void toggle_inline_comment(const String &delimiter); + void goto_line(int p_line); void goto_line_selection(int p_line, int p_begin, int p_end); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 1e03adc219..6140412a32 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -136,10 +136,10 @@ void EditorNode::_update_scene_tabs() { Ref<Texture> script_icon = gui_base->get_icon("Script", "EditorIcons"); for (int i = 0; i < editor_data.get_edited_scene_count(); i++) { - String type = editor_data.get_scene_type(i); + Node *type_node = editor_data.get_edited_scene_root(i); Ref<Texture> icon; - if (type != String()) { - icon = get_class_icon(type, "Node"); + if (type_node) { + icon = EditorNode::get_singleton()->get_object_icon(type_node, "Node"); } int current = editor_data.get_edited_scene(); @@ -4232,7 +4232,13 @@ bool EditorNode::are_bottom_panels_hidden() const { void EditorNode::hide_bottom_panel() { - _bottom_panel_switch(false, 0); + for (int i = 0; i < bottom_panel_items.size(); i++) { + + if (bottom_panel_items[i].control->is_visible()) { + _bottom_panel_switch(false, i); + break; + } + } } void EditorNode::make_bottom_panel_item_visible(Control *p_item) { @@ -4269,7 +4275,7 @@ void EditorNode::remove_bottom_panel_item(Control *p_item) { if (bottom_panel_items[i].control == p_item) { if (p_item->is_visible_in_tree()) { - _bottom_panel_switch(false, 0); + _bottom_panel_switch(false, i); } bottom_panel_vb->remove_child(bottom_panel_items[i].control); bottom_panel_hb_editors->remove_child(bottom_panel_items[i].button); @@ -4289,6 +4295,10 @@ void EditorNode::_bottom_panel_switch(bool p_enable, int p_idx) { ERR_FAIL_INDEX(p_idx, bottom_panel_items.size()); + if (bottom_panel_items[p_idx].control->is_visible() == p_enable) { + return; + } + if (p_enable) { for (int i = 0; i < bottom_panel_items.size(); i++) { @@ -4309,11 +4319,8 @@ void EditorNode::_bottom_panel_switch(bool p_enable, int p_idx) { } else { bottom_panel->add_style_override("panel", gui_base->get_stylebox("panel", "TabContainer")); - for (int i = 0; i < bottom_panel_items.size(); i++) { - - bottom_panel_items[i].button->set_pressed(false); - bottom_panel_items[i].control->set_visible(false); - } + bottom_panel_items[p_idx].button->set_pressed(false); + bottom_panel_items[p_idx].control->set_visible(false); center_split->set_dragger_visibility(SplitContainer::DRAGGER_HIDDEN); center_split->set_collapsed(true); bottom_panel_raise->hide(); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 8d590e5268..9166db2741 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -336,9 +336,9 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("interface/theme/icon_and_font_color", 0); hints["interface/theme/icon_and_font_color"] = PropertyInfo(Variant::INT, "interface/theme/icon_and_font_color", PROPERTY_HINT_ENUM, "Auto,Dark,Light", PROPERTY_USAGE_DEFAULT); _initial_set("interface/theme/base_color", Color::html("#323b4f")); - hints["interface/theme/accent_color"] = PropertyInfo(Variant::COLOR, "interface/theme/accent_color", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT); - _initial_set("interface/theme/accent_color", Color::html("#699ce8")); hints["interface/theme/base_color"] = PropertyInfo(Variant::COLOR, "interface/theme/base_color", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT); + _initial_set("interface/theme/accent_color", Color::html("#699ce8")); + hints["interface/theme/accent_color"] = PropertyInfo(Variant::COLOR, "interface/theme/accent_color", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT); _initial_set("interface/theme/contrast", 0.25); hints["interface/theme/contrast"] = PropertyInfo(Variant::REAL, "interface/theme/contrast", PROPERTY_HINT_RANGE, "0.01, 1, 0.01"); _initial_set("interface/theme/relationship_line_opacity", 0.1); diff --git a/editor/plugins/mesh_library_editor_plugin.cpp b/editor/plugins/mesh_library_editor_plugin.cpp index aedac7b45d..89eb253afe 100644 --- a/editor/plugins/mesh_library_editor_plugin.cpp +++ b/editor/plugins/mesh_library_editor_plugin.cpp @@ -127,7 +127,7 @@ void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library, continue; MeshLibrary::ShapeData shape_data; shape_data.shape = collision; - shape_data.local_transform = sb->shape_owner_get_transform(E->get()); + shape_data.local_transform = sb->get_transform() * sb->shape_owner_get_transform(E->get()); collisions.push_back(shape_data); } } @@ -136,17 +136,20 @@ void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library, p_library->set_item_shapes(id, collisions); Ref<NavigationMesh> navmesh; + Transform navmesh_transform; for (int j = 0; j < mi->get_child_count(); j++) { Node *child2 = mi->get_child(j); if (!Object::cast_to<NavigationMeshInstance>(child2)) continue; NavigationMeshInstance *sb = Object::cast_to<NavigationMeshInstance>(child2); navmesh = sb->get_navigation_mesh(); + navmesh_transform = sb->get_transform(); if (!navmesh.is_null()) break; } if (!navmesh.is_null()) { p_library->set_item_navmesh(id, navmesh); + p_library->set_item_navmesh_transform(id, navmesh_transform); } } diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index e95b1356bf..8c3f8fd3e8 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -620,7 +620,9 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c } ScriptLanguage::LookupResult result; - if (p_symbol.is_resource_file()) { + if (ScriptServer::is_global_class(p_symbol)) { + EditorNode::get_singleton()->load_resource(ScriptServer::get_global_class_path(p_symbol)); + } else if (p_symbol.is_resource_file()) { List<String> scene_extensions; ResourceLoader::get_recognized_extensions_for_type("PackedScene", &scene_extensions); @@ -794,92 +796,7 @@ void ScriptTextEditor::_edit_option(int p_op) { } break; case EDIT_TOGGLE_COMMENT: { - Ref<Script> scr = script; - if (scr.is_null()) - return; - - String delimiter = "#"; - List<String> comment_delimiters; - scr->get_language()->get_comment_delimiters(&comment_delimiters); - - for (List<String>::Element *E = comment_delimiters.front(); E; E = E->next()) { - String script_delimiter = E->get(); - if (script_delimiter.find(" ") == -1) { - delimiter = script_delimiter; - break; - } - } - - tx->begin_complex_operation(); - if (tx->is_selection_active()) { - int begin = tx->get_selection_from_line(); - int end = tx->get_selection_to_line(); - - // End of selection ends on the first column of the last line, ignore it. - if (tx->get_selection_to_column() == 0) - end -= 1; - - int col_to = tx->get_selection_to_column(); - int cursor_pos = tx->cursor_get_column(); - - // Check if all lines in the selected block are commented - bool is_commented = true; - for (int i = begin; i <= end; i++) { - if (!tx->get_line(i).begins_with(delimiter)) { - is_commented = false; - break; - } - } - for (int i = begin; i <= end; i++) { - String line_text = tx->get_line(i); - - if (line_text.strip_edges().empty()) { - line_text = delimiter; - } else { - if (is_commented) { - line_text = line_text.substr(delimiter.length(), line_text.length()); - } else { - line_text = delimiter + line_text; - } - } - tx->set_line(i, line_text); - } - - // Adjust selection & cursor position. - int offset = is_commented ? -1 : 1; - int col_from = tx->get_selection_from_column() > 0 ? tx->get_selection_from_column() + offset : 0; - - if (is_commented && tx->cursor_get_column() == tx->get_line(tx->cursor_get_line()).length() + 1) - cursor_pos += 1; - - if (tx->get_selection_to_column() != 0 && col_to != tx->get_line(tx->get_selection_to_line()).length() + 1) - col_to += offset; - - if (tx->cursor_get_column() != 0) - cursor_pos += offset; - - tx->select(begin, col_from, tx->get_selection_to_line(), col_to); - tx->cursor_set_column(cursor_pos); - - } else { - int begin = tx->cursor_get_line(); - String line_text = tx->get_line(begin); - - int col = tx->cursor_get_column(); - if (line_text.begins_with(delimiter)) { - line_text = line_text.substr(delimiter.length(), line_text.length()); - col -= 1; - } else { - line_text = delimiter + line_text; - col += 1; - } - - tx->set_line(begin, line_text); - tx->cursor_set_column(col); - } - tx->end_complex_operation(); - tx->update(); - + _edit_option_toggle_inline_comment(); } break; case EDIT_COMPLETE: { @@ -1066,6 +983,25 @@ void ScriptTextEditor::_edit_option(int p_op) { } } +void ScriptTextEditor::_edit_option_toggle_inline_comment() { + if (script.is_null()) + return; + + String delimiter = "#"; + List<String> comment_delimiters; + script->get_language()->get_comment_delimiters(&comment_delimiters); + + for (List<String>::Element *E = comment_delimiters.front(); E; E = E->next()) { + String script_delimiter = E->get(); + if (script_delimiter.find(" ") == -1) { + delimiter = script_delimiter; + break; + } + } + + code_editor->toggle_inline_comment(delimiter); +} + void ScriptTextEditor::add_syntax_highlighter(SyntaxHighlighter *p_highlighter) { highlighters[p_highlighter->get_name()] = p_highlighter; highlighter_menu->add_radio_check_item(p_highlighter->get_name()); diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h index f83aadddef..b081a31c18 100644 --- a/editor/plugins/script_text_editor.h +++ b/editor/plugins/script_text_editor.h @@ -136,6 +136,7 @@ protected: void _change_syntax_highlighter(int p_idx); void _edit_option(int p_op); + void _edit_option_toggle_inline_comment(); void _make_context_menu(bool p_selection, bool p_color, bool p_foldable, bool p_open_docs, bool p_goto_definition); void _text_edit_gui_input(const Ref<InputEvent> &ev); void _color_changed(const Color &p_color); diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 2b6ceac8e2..d39e521113 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -248,19 +248,19 @@ void ShaderEditor::_menu_option(int p_option) { } break; case EDIT_INDENT_LEFT: { - TextEdit *tx = shader_editor->get_text_edit(); if (shader.is_null()) return; + TextEdit *tx = shader_editor->get_text_edit(); tx->indent_left(); } break; case EDIT_INDENT_RIGHT: { - TextEdit *tx = shader_editor->get_text_edit(); if (shader.is_null()) return; + TextEdit *tx = shader_editor->get_text_edit(); tx->indent_right(); } break; @@ -272,54 +272,10 @@ void ShaderEditor::_menu_option(int p_option) { } break; case EDIT_TOGGLE_COMMENT: { - TextEdit *tx = shader_editor->get_text_edit(); if (shader.is_null()) return; - tx->begin_complex_operation(); - if (tx->is_selection_active()) { - int begin = tx->get_selection_from_line(); - int end = tx->get_selection_to_line(); - - // End of selection ends on the first column of the last line, ignore it. - if (tx->get_selection_to_column() == 0) - end -= 1; - - // Check if all lines in the selected block are commented - bool is_commented = true; - for (int i = begin; i <= end; i++) { - if (!tx->get_line(i).begins_with("//")) { - is_commented = false; - break; - } - } - for (int i = begin; i <= end; i++) { - String line_text = tx->get_line(i); - - if (line_text.strip_edges().empty()) { - line_text = "//"; - } else { - if (is_commented) { - line_text = line_text.substr(2, line_text.length()); - } else { - line_text = "//" + line_text; - } - } - tx->set_line(i, line_text); - } - } else { - int begin = tx->cursor_get_line(); - String line_text = tx->get_line(begin); - - if (line_text.begins_with("//")) - line_text = line_text.substr(2, line_text.length()); - else - line_text = "//" + line_text; - tx->set_line(begin, line_text); - } - tx->end_complex_operation(); - tx->update(); - //tx->deselect(); + shader_editor->toggle_inline_comment("//"); } break; case EDIT_COMPLETE: { diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index ad945d7916..765d198f79 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -1021,7 +1021,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { } _edit.mouse_pos = b->get_position(); - _edit.snap = false; + _edit.snap = spatial_editor->is_snap_enabled(); _edit.mode = TRANSFORM_NONE; //gizmo has priority over everything @@ -1772,7 +1772,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { if (ED_IS_SHORTCUT("spatial_editor/snap", p_event)) { if (_edit.mode != TRANSFORM_NONE) { - _edit.snap = true; + _edit.snap = !_edit.snap; } } if (ED_IS_SHORTCUT("spatial_editor/bottom_view", p_event)) { diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index 1c6359a46b..21ba6efb82 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -703,7 +703,7 @@ void ProjectSettingsEditor::_update_actions() { item->add_button(2, get_icon("Add", "EditorIcons"), 1, false, TTR("Add Event")); if (!ProjectSettings::get_singleton()->get_input_presets().find(pi.name)) { item->add_button(2, get_icon("Remove", "EditorIcons"), 2, false, TTR("Remove")); - item->set_editable(2, true); + item->set_editable(0, true); } for (int i = 0; i < events.size(); i++) { diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 66163d7e0a..19b46ef90f 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -377,7 +377,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } break; case TOOL_CLEAR_SCRIPT: { - List<Node *> selection = editor_selection->get_selected_node_list(); + Array selection = editor_selection->get_selected_nodes(); if (selection.empty()) return; @@ -385,13 +385,14 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { editor_data->get_undo_redo().create_action(TTR("Clear Script")); editor_data->get_undo_redo().add_do_method(editor, "push_item", (Script *)NULL); - for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { + for (int i = 0; i < selection.size(); i++) { - Ref<Script> existing = E->get()->get_script(); + Node *n = Object::cast_to<Node>(selection[i]); + Ref<Script> existing = n->get_script(); if (existing.is_valid()) { const RefPtr empty; - editor_data->get_undo_redo().add_do_method(E->get(), "set_script", empty); - editor_data->get_undo_redo().add_undo_method(E->get(), "set_script", existing); + editor_data->get_undo_redo().add_do_method(n, "set_script", empty); + editor_data->get_undo_redo().add_undo_method(n, "set_script", existing); } } @@ -1653,9 +1654,9 @@ void SceneTreeDock::_update_script_button() { } } else { button_create_script->show(); - List<Node *> selection = EditorNode::get_singleton()->get_editor_selection()->get_selected_node_list(); - for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { - Node *n = E->get(); + Array selection = editor_selection->get_selected_nodes(); + for (int i = 0; i < selection.size(); i++) { + Node *n = Object::cast_to<Node>(selection[i]); if (!n->get_script().is_null()) { button_clear_script->show(); return; diff --git a/main/tests/test_gdscript.cpp b/main/tests/test_gdscript.cpp index 55e31a18c3..27180f84aa 100644 --- a/main/tests/test_gdscript.cpp +++ b/main/tests/test_gdscript.cpp @@ -563,7 +563,7 @@ static void _disassemble_class(const Ref<GDScript> &p_class, const Vector<String case GDScriptFunction::OPCODE_OPERATOR: { int op = code[ip + 1]; - txt += "op "; + txt += " op "; String opname = Variant::get_operator_name(Variant::Operator(op)); diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index b2b7b1c260..5f521c682a 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -1396,11 +1396,6 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo case GDScriptParser::ControlFlowNode::CF_IF: { -#ifdef DEBUG_ENABLED - codegen.opcodes.push_back(GDScriptFunction::OPCODE_LINE); - codegen.opcodes.push_back(cf->line); - codegen.current_line = cf->line; -#endif int ret2 = _parse_expression(codegen, cf->arguments[0], p_stack_level, false); if (ret2 < 0) return ERR_PARSE_ERROR; diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index f005f88d2e..da69181a43 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -2741,6 +2741,8 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { } break; case GDScriptTokenizer::TK_NEWLINE: { + int line = tokenizer->get_token_line(); + if (!_parse_newline()) { if (!error_set) { p_block->end_line = tokenizer->get_token_line(); @@ -2750,7 +2752,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { } NewLineNode *nl2 = alloc_node<NewLineNode>(); - nl2->line = tokenizer->get_token_line(); + nl2->line = line; p_block->statements.push_back(nl2); } break; diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index fe1eac6dc9..32a014e76d 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -517,7 +517,7 @@ bool GridMap::_octant_update(const OctantKey &p_key) { Ref<NavigationMesh> navmesh = mesh_library->get_item_navmesh(c.item); if (navmesh.is_valid()) { Octant::NavMesh nm; - nm.xform = xform; + nm.xform = xform * mesh_library->get_item_navmesh_transform(c.item); if (navigation) { nm.id = navigation->navmesh_add(navmesh, xform, this); diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index bc7a576785..17eb6f674c 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -717,6 +717,26 @@ void GridMapEditor::_set_display_mode(int p_mode) { update_palette(); } +void GridMapEditor::_text_changed(const String &p_text) { + update_palette(); +} + +void GridMapEditor::_sbox_input(const Ref<InputEvent> &p_ie) { + + Ref<InputEventKey> k = p_ie; + + if (k.is_valid() && (k->get_scancode() == KEY_UP || k->get_scancode() == KEY_DOWN || k->get_scancode() == KEY_PAGEUP || k->get_scancode() == KEY_PAGEDOWN)) { + + mesh_library_palette->call("_gui_input", k); + search_box->accept_event(); + } +} + +void GridMapEditor::_icon_size_changed(float p_value) { + mesh_library_palette->set_icon_scale(p_value); + update_palette(); +} + void GridMapEditor::update_palette() { int selected = mesh_library_palette->get_current(); @@ -730,8 +750,9 @@ void GridMapEditor::update_palette() { } float min_size = EDITOR_DEF("editors/grid_map/preview_size", 64); + min_size *= EDSCALE; mesh_library_palette->set_fixed_icon_size(Size2(min_size, min_size)); - mesh_library_palette->set_fixed_column_width(min_size * 3 / 2); + mesh_library_palette->set_fixed_column_width(min_size * MAX(size_slider->get_value(), 1.5)); mesh_library_palette->set_max_text_lines(2); Ref<MeshLibrary> mesh_library = node->get_mesh_library(); @@ -754,23 +775,28 @@ void GridMapEditor::update_palette() { } il.sort(); + String filter = search_box->get_text().strip_edges(); + int item = 0; for (List<_CGMEItemSort>::Element *E = il.front(); E; E = E->next()) { int id = E->get().id; - - mesh_library_palette->add_item(""); - String name = mesh_library->get_item_name(id); Ref<Texture> preview = mesh_library->get_item_preview(id); + if (name == "") { + name = "#" + itos(id); + } + + if (filter != "" && !filter.is_subsequence_ofi(name)) + continue; + + mesh_library_palette->add_item(""); if (!preview.is_null()) { mesh_library_palette->set_item_icon(item, preview); mesh_library_palette->set_item_tooltip(item, name); } - if (name != "") { - mesh_library_palette->set_item_text(item, name); - } + mesh_library_palette->set_item_text(item, name); mesh_library_palette->set_item_metadata(item, id); item++; @@ -985,6 +1011,7 @@ void GridMapEditor::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: { options->set_icon(get_icon("GridMap", "EditorIcons")); + search_box->set_right_icon(get_icon("Search", "EditorIcons")); } break; } } @@ -1031,6 +1058,9 @@ void GridMapEditor::_floor_changed(float p_value) { void GridMapEditor::_bind_methods() { + ClassDB::bind_method("_text_changed", &GridMapEditor::_text_changed); + ClassDB::bind_method("_sbox_input", &GridMapEditor::_sbox_input); + ClassDB::bind_method("_icon_size_changed", &GridMapEditor::_icon_size_changed); ClassDB::bind_method("_menu_option", &GridMapEditor::_menu_option); ClassDB::bind_method("_configure", &GridMapEditor::_configure); ClassDB::bind_method("_item_selected_cbk", &GridMapEditor::_item_selected_cbk); @@ -1132,6 +1162,12 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { add_child(hb); hb->set_h_size_flags(SIZE_EXPAND_FILL); + search_box = memnew(LineEdit); + search_box->set_h_size_flags(SIZE_EXPAND_FILL); + hb->add_child(search_box); + search_box->connect("text_changed", this, "_text_changed"); + search_box->connect("gui_input", this, "_sbox_input"); + mode_thumbnail = memnew(ToolButton); mode_thumbnail->set_toggle_mode(true); mode_thumbnail->set_pressed(true); @@ -1146,6 +1182,15 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { hb->add_child(mode_list); mode_list->connect("pressed", this, "_set_display_mode", varray(DISPLAY_LIST)); + size_slider = memnew(HSlider); + size_slider->set_h_size_flags(SIZE_EXPAND_FILL); + size_slider->set_min(0.1f); + size_slider->set_max(4.0f); + size_slider->set_step(0.1f); + size_slider->set_value(1.0f); + size_slider->connect("value_changed", this, "_icon_size_changed"); + add_child(size_slider); + EDITOR_DEF("editors/grid_map/preview_size", 64); display_mode = DISPLAY_THUMBNAIL; diff --git a/modules/gridmap/grid_map_editor_plugin.h b/modules/gridmap/grid_map_editor_plugin.h index 81a21a76ae..59b8ac13da 100644 --- a/modules/gridmap/grid_map_editor_plugin.h +++ b/modules/gridmap/grid_map_editor_plugin.h @@ -79,6 +79,8 @@ class GridMapEditor : public VBoxContainer { double accumulated_floor_delta; ToolButton *mode_thumbnail; ToolButton *mode_list; + LineEdit *search_box; + HSlider *size_slider; HBoxContainer *spatial_editor_hb; ConfirmationDialog *settings_dialog; VBoxContainer *settings_vbc; @@ -193,6 +195,11 @@ class GridMapEditor : public VBoxContainer { void _update_cursor_instance(); void _update_clip(); + void _text_changed(const String &p_text); + void _sbox_input(const Ref<InputEvent> &p_ie); + + void _icon_size_changed(float p_value); + void _update_duplicate_indicator(); void _duplicate_paste(); void _update_selection_transform(); diff --git a/platform/iphone/app_delegate.mm b/platform/iphone/app_delegate.mm index d160553050..64405bfa5b 100644 --- a/platform/iphone/app_delegate.mm +++ b/platform/iphone/app_delegate.mm @@ -615,18 +615,6 @@ static int frame_count = 0; // Create a full-screen window window = [[UIWindow alloc] initWithFrame:rect]; - // window.autoresizesSubviews = YES; - //[window setAutoresizingMask:UIViewAutoresizingFlexibleWidth | - // UIViewAutoresizingFlexibleWidth]; - - // Create the OpenGL ES view and add it to the window - GLView *glView = [[GLView alloc] initWithFrame:rect]; - printf("glview is %p\n", glView); - //[window addSubview:glView]; - glView.delegate = self; - // glView.autoresizesSubviews = YES; - //[glView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | - // UIViewAutoresizingFlexibleWidth]; OS::VideoMode vm = _get_video_mode(); @@ -641,6 +629,12 @@ static int frame_count = 0; return FALSE; }; + // WARNING: We must *always* create the GLView after we have constructed the + // OS with iphone_main. This allows the GLView to access project settings so + // it can properly initialize the OpenGL context + GLView *glView = [[GLView alloc] initWithFrame:rect]; + glView.delegate = self; + view_controller = [[ViewController alloc] init]; view_controller.view = glView; window.rootViewController = view_controller; diff --git a/platform/iphone/gl_view.mm b/platform/iphone/gl_view.mm index 6f4d0ddb57..1cb8d0e44e 100644 --- a/platform/iphone/gl_view.mm +++ b/platform/iphone/gl_view.mm @@ -284,19 +284,37 @@ static void clear_touches() { kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil]; + bool fallback_gl2 = false; + // Create a GL ES 3 context based on the gl driver from project settings + if (GLOBAL_GET("rendering/quality/driver/driver_name") == "GLES3") { + context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; + NSLog(@"Setting up an OpenGL ES 3.0 context. Based on Project Settings \"rendering/quality/driver/driver_name\""); + if (!context && GLOBAL_GET("rendering/quality/driver/fallback_to_gles2")) { + gles3_available = false; + fallback_gl2 = true; + NSLog(@"Failed to create OpenGL ES 3.0 context. Falling back to OpenGL ES 2.0"); + } + } - // Create our EAGLContext, and if successful make it current and create our framebuffer. - context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; - - if (!context || ![EAGLContext setCurrentContext:context] || ![self createFramebuffer]) { + // Create GL ES 2 context + if (GLOBAL_GET("rendering/quality/driver/driver_name") == "GLES2" || fallback_gl2) { context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; - gles3_available = false; - if (!context || ![EAGLContext setCurrentContext:context] || ![self createFramebuffer]) { - [self release]; + NSLog(@"Setting up an OpenGL ES 2.0 context."); + if (!context) { + NSLog(@"Failed to create OpenGL ES 2.0 context!"); return nil; } } + if (![EAGLContext setCurrentContext:context]) { + NSLog(@"Failed to set EAGLContext!"); + return nil; + } + if (![self createFramebuffer]) { + NSLog(@"Failed to create frame buffer!"); + return nil; + } + // Default the animation interval to 1/60th of a second. animationInterval = 1.0 / 60.0; return self; diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 9bcf10d5e7..8968923b2c 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -900,21 +900,13 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const { - if (!underline_meta || selection.click) + if (selection.click) return CURSOR_ARROW; if (main->first_invalid_line < main->lines.size()) return CURSOR_ARROW; //invalid - int line = 0; - Item *item = NULL; - - ((RichTextLabel *)(this))->_find_click(main, p_pos, &item, &line); - - if (item && ((RichTextLabel *)(this))->_find_meta(item, NULL)) - return CURSOR_POINTING_HAND; - - return CURSOR_ARROW; + return get_default_cursor_shape(); } void RichTextLabel::_gui_input(Ref<InputEvent> p_event) { diff --git a/scene/resources/mesh_library.cpp b/scene/resources/mesh_library.cpp index c5125d576d..d40a5dee2e 100644 --- a/scene/resources/mesh_library.cpp +++ b/scene/resources/mesh_library.cpp @@ -56,6 +56,8 @@ bool MeshLibrary::_set(const StringName &p_name, const Variant &p_value) { set_item_preview(idx, p_value); else if (what == "navmesh") set_item_navmesh(idx, p_value); + else if (what == "navmesh_transform") + set_item_navmesh_transform(idx, p_value); else return false; @@ -80,6 +82,8 @@ bool MeshLibrary::_get(const StringName &p_name, Variant &r_ret) const { r_ret = _get_item_shapes(idx); else if (what == "navmesh") r_ret = get_item_navmesh(idx); + else if (what == "navmesh_transform") + r_ret = get_item_navmesh_transform(idx); else if (what == "preview") r_ret = get_item_preview(idx); else @@ -95,8 +99,10 @@ void MeshLibrary::_get_property_list(List<PropertyInfo> *p_list) const { String name = "item/" + itos(E->key()) + "/"; p_list->push_back(PropertyInfo(Variant::STRING, name + "name")); p_list->push_back(PropertyInfo(Variant::OBJECT, name + "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh")); + p_list->push_back(PropertyInfo(Variant::TRANSFORM, name + "mesh_transform")); p_list->push_back(PropertyInfo(Variant::ARRAY, name + "shapes")); p_list->push_back(PropertyInfo(Variant::OBJECT, name + "navmesh", PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh")); + p_list->push_back(PropertyInfo(Variant::TRANSFORM, name + "navmesh_transform")); p_list->push_back(PropertyInfo(Variant::OBJECT, name + "preview", PROPERTY_HINT_RESOURCE_TYPE, "Texture", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_HELPER)); } } @@ -116,6 +122,7 @@ void MeshLibrary::set_item_name(int p_item, const String &p_name) { emit_changed(); _change_notify(); } + void MeshLibrary::set_item_mesh(int p_item, const Ref<Mesh> &p_mesh) { ERR_FAIL_COND(!item_map.has(p_item)); @@ -145,6 +152,15 @@ void MeshLibrary::set_item_navmesh(int p_item, const Ref<NavigationMesh> &p_navm _change_notify(); } +void MeshLibrary::set_item_navmesh_transform(int p_item, const Transform &p_transform) { + + ERR_FAIL_COND(!item_map.has(p_item)); + item_map[p_item].navmesh_transform = p_transform; + notify_change_to_owners(); + emit_changed(); + _change_notify(); +} + void MeshLibrary::set_item_preview(int p_item, const Ref<Texture> &p_preview) { ERR_FAIL_COND(!item_map.has(p_item)); @@ -152,11 +168,13 @@ void MeshLibrary::set_item_preview(int p_item, const Ref<Texture> &p_preview) { emit_changed(); _change_notify(); } + String MeshLibrary::get_item_name(int p_item) const { ERR_FAIL_COND_V(!item_map.has(p_item), ""); return item_map[p_item].name; } + Ref<Mesh> MeshLibrary::get_item_mesh(int p_item) const { ERR_FAIL_COND_V(!item_map.has(p_item), Ref<Mesh>()); @@ -175,6 +193,12 @@ Ref<NavigationMesh> MeshLibrary::get_item_navmesh(int p_item) const { return item_map[p_item].navmesh; } +Transform MeshLibrary::get_item_navmesh_transform(int p_item) const { + + ERR_FAIL_COND_V(!item_map.has(p_item), Transform()); + return item_map[p_item].navmesh_transform; +} + Ref<Texture> MeshLibrary::get_item_preview(int p_item) const { ERR_FAIL_COND_V(!item_map.has(p_item), Ref<Texture>()); @@ -268,11 +292,13 @@ void MeshLibrary::_bind_methods() { ClassDB::bind_method(D_METHOD("set_item_name", "id", "name"), &MeshLibrary::set_item_name); ClassDB::bind_method(D_METHOD("set_item_mesh", "id", "mesh"), &MeshLibrary::set_item_mesh); ClassDB::bind_method(D_METHOD("set_item_navmesh", "id", "navmesh"), &MeshLibrary::set_item_navmesh); + ClassDB::bind_method(D_METHOD("set_item_navmesh_transform", "id", "navmesh"), &MeshLibrary::set_item_navmesh_transform); ClassDB::bind_method(D_METHOD("set_item_shapes", "id", "shapes"), &MeshLibrary::_set_item_shapes); ClassDB::bind_method(D_METHOD("set_item_preview", "id", "texture"), &MeshLibrary::set_item_preview); ClassDB::bind_method(D_METHOD("get_item_name", "id"), &MeshLibrary::get_item_name); ClassDB::bind_method(D_METHOD("get_item_mesh", "id"), &MeshLibrary::get_item_mesh); ClassDB::bind_method(D_METHOD("get_item_navmesh", "id"), &MeshLibrary::get_item_navmesh); + ClassDB::bind_method(D_METHOD("get_item_navmesh_transform", "id"), &MeshLibrary::get_item_navmesh_transform); ClassDB::bind_method(D_METHOD("get_item_shapes", "id"), &MeshLibrary::_get_item_shapes); ClassDB::bind_method(D_METHOD("get_item_preview", "id"), &MeshLibrary::get_item_preview); ClassDB::bind_method(D_METHOD("remove_item", "id"), &MeshLibrary::remove_item); diff --git a/scene/resources/mesh_library.h b/scene/resources/mesh_library.h index 849a0577eb..4ae4fc6483 100644 --- a/scene/resources/mesh_library.h +++ b/scene/resources/mesh_library.h @@ -52,6 +52,7 @@ public: Ref<Mesh> mesh; Vector<ShapeData> shapes; Ref<Texture> preview; + Transform navmesh_transform; Ref<NavigationMesh> navmesh; }; @@ -72,11 +73,13 @@ public: void set_item_name(int p_item, const String &p_name); void set_item_mesh(int p_item, const Ref<Mesh> &p_mesh); void set_item_navmesh(int p_item, const Ref<NavigationMesh> &p_navmesh); + void set_item_navmesh_transform(int p_item, const Transform &p_transform); void set_item_shapes(int p_item, const Vector<ShapeData> &p_shapes); void set_item_preview(int p_item, const Ref<Texture> &p_preview); String get_item_name(int p_item) const; Ref<Mesh> get_item_mesh(int p_item) const; Ref<NavigationMesh> get_item_navmesh(int p_item) const; + Transform get_item_navmesh_transform(int p_item) const; Vector<ShapeData> get_item_shapes(int p_item) const; Ref<Texture> get_item_preview(int p_item) const; diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index 210fbe9f20..b6efca9acc 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -1645,7 +1645,8 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r } } - f->store_string("\n"); + if (E->next()) + f->store_line(String()); } if (packed_scene.is_valid()) { @@ -1714,7 +1715,8 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r f->store_string(_valprop(String(state->get_node_property_name(i, j))) + " = " + vars + "\n"); } - f->store_line(String()); + if (i < state->get_node_count() - 1) + f->store_line(String()); } for (int i = 0; i < state->get_connection_count(); i++) { diff --git a/servers/visual_server.cpp b/servers/visual_server.cpp index 3dbe516f74..19c6106502 100644 --- a/servers/visual_server.cpp +++ b/servers/visual_server.cpp @@ -776,7 +776,7 @@ Error VisualServer::_surface_set_data(Array p_arrays, uint32_t p_format, uint32_ continue; //break; ERR_FAIL_INDEX_V(idx, total_bones, ERR_INVALID_DATA); - if (bptr->size.x < 0) { + if (bptr[idx].size.x < 0) { //first bptr[idx] = AABB(v, SMALL_VEC3); any_valid = true; |