diff options
78 files changed, 815 insertions, 385 deletions
diff --git a/core/input/input_builders.py b/core/input/input_builders.py index a7729c9af2..76e199e0ff 100644 --- a/core/input/input_builders.py +++ b/core/input/input_builders.py @@ -45,7 +45,7 @@ def make_default_controller_mappings(target, source, env): platform_mappings[current_platform][guid] = line platform_variables = { - "Linux": "#if X11_ENABLED", + "Linux": "#if LINUXBSD_ENABLED", "Windows": "#ifdef WINDOWS_ENABLED", "Mac OS X": "#ifdef MACOS_ENABLED", "Android": "#if defined(__ANDROID__)", diff --git a/core/variant/variant_setget.cpp b/core/variant/variant_setget.cpp index b8d4495702..a74556d88f 100644 --- a/core/variant/variant_setget.cpp +++ b/core/variant/variant_setget.cpp @@ -1253,7 +1253,7 @@ bool Variant::iter_init(Variant &r_iter, bool &valid) const { return _data._int > 0; } break; case FLOAT: { - r_iter = 0; + r_iter = 0.0; return _data._float > 0.0; } break; case VECTOR2: { @@ -1457,7 +1457,7 @@ bool Variant::iter_next(Variant &r_iter, bool &valid) const { return true; } break; case FLOAT: { - int64_t idx = r_iter; + double idx = r_iter; idx++; if (idx >= _data._float) { return false; diff --git a/doc/classes/AnimatedSprite2D.xml b/doc/classes/AnimatedSprite2D.xml index e20fb71c7e..cd9c0cee98 100644 --- a/doc/classes/AnimatedSprite2D.xml +++ b/doc/classes/AnimatedSprite2D.xml @@ -7,7 +7,6 @@ [AnimatedSprite2D] is similar to the [Sprite2D] node, except it carries multiple textures as animation frames. Animations are created using a [SpriteFrames] resource, which allows you to import image files (or a folder containing said files) to provide the animation frames for the sprite. The [SpriteFrames] resource can be configured in the editor via the SpriteFrames bottom panel. After setting up [member frames], [method play] may be called. It's also possible to select an [member animation] and toggle [member playing], even within the editor. To pause the current animation, set [member playing] to [code]false[/code]. Alternatively, setting [member speed_scale] to [code]0[/code] also preserves the current frame's elapsed time. - [b]Note:[/b] You can associate a set of normal or specular maps by creating additional [SpriteFrames] resources with a [code]_normal[/code] or [code]_specular[/code] suffix. For example, having 3 [SpriteFrames] resources [code]run[/code], [code]run_normal[/code], and [code]run_specular[/code] will make it so the [code]run[/code] animation uses normal and specular maps. </description> <tutorials> <link title="2D Sprite animation">$DOCS_URL/tutorials/2d/2d_sprite_animation.html</link> diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index 1dc4dd3ff0..832adb6e98 100644 --- a/doc/classes/DisplayServer.xml +++ b/doc/classes/DisplayServer.xml @@ -1695,7 +1695,7 @@ [b]Note:[/b] This flag is implemented on macOS. </constant> <constant name="WINDOW_FLAG_MOUSE_PASSTHROUGH" value="7" enum="WindowFlags"> - All mouse event as passed to the underlying window of the same application. + All mouse events are passed to the underlying window of the same application. </constant> <constant name="WINDOW_FLAG_MAX" value="8" enum="WindowFlags"> Max value of the [enum WindowFlags]. diff --git a/doc/classes/SpriteFrames.xml b/doc/classes/SpriteFrames.xml index 87b823bd2a..195f3598d5 100644 --- a/doc/classes/SpriteFrames.xml +++ b/doc/classes/SpriteFrames.xml @@ -5,7 +5,6 @@ </brief_description> <description> Sprite frame library for an [AnimatedSprite2D] or [AnimatedSprite3D] node. Contains frames and animation data for playback. - [b]Note:[/b] You can associate a set of normal or specular maps by creating additional [SpriteFrames] resources with a [code]_normal[/code] or [code]_specular[/code] suffix. For example, having 3 [SpriteFrames] resources [code]run[/code], [code]run_normal[/code], and [code]run_specular[/code] will make it so the [code]run[/code] animation uses normal and specular maps. </description> <tutorials> </tutorials> @@ -14,7 +13,7 @@ <return type="void" /> <param index="0" name="anim" type="StringName" /> <description> - Adds a new animation to the library. + Adds a new [param anim] animation to the library. </description> </method> <method name="add_frame"> @@ -24,20 +23,20 @@ <param index="2" name="duration" type="float" default="1.0" /> <param index="3" name="at_position" type="int" default="-1" /> <description> - Adds a frame to the given animation. + Adds a frame to the [param anim] animation. If [param at_position] is [code]-1[/code], the frame will be added to the end of the animation. </description> </method> <method name="clear"> <return type="void" /> <param index="0" name="anim" type="StringName" /> <description> - Removes all frames from the given animation. + Removes all frames from the [param anim] animation. </description> </method> <method name="clear_all"> <return type="void" /> <description> - Removes all animations. A "default" animation will be created. + Removes all animations. An empty [code]default[/code] animation will be created. </description> </method> <method name="get_animation_loop" qualifiers="const"> @@ -91,14 +90,14 @@ <return type="bool" /> <param index="0" name="anim" type="StringName" /> <description> - If [code]true[/code], the named animation exists. + Returns [code]true[/code] if the [param anim] animation exists. </description> </method> <method name="remove_animation"> <return type="void" /> <param index="0" name="anim" type="StringName" /> <description> - Removes the given animation. + Removes the [param anim] animation. </description> </method> <method name="remove_frame"> @@ -106,7 +105,7 @@ <param index="0" name="anim" type="StringName" /> <param index="1" name="idx" type="int" /> <description> - Removes the animation's selected frame. + Removes the [param anim] animation's frame [param idx]. </description> </method> <method name="rename_animation"> @@ -114,7 +113,7 @@ <param index="0" name="anim" type="StringName" /> <param index="1" name="newname" type="StringName" /> <description> - Changes the animation's name to [param newname]. + Changes the [param anim] animation's name to [param newname]. </description> </method> <method name="set_animation_loop"> @@ -122,7 +121,7 @@ <param index="0" name="anim" type="StringName" /> <param index="1" name="loop" type="bool" /> <description> - If [code]true[/code], the animation will loop. + If [param loop] is [code]true[/code], the [param anim] animation will loop when it reaches the end, or the start if it is played in reverse. </description> </method> <method name="set_animation_speed"> @@ -140,7 +139,7 @@ <param index="2" name="texture" type="Texture2D" /> <param index="3" name="duration" type="float" default="1.0" /> <description> - Sets the texture and the duration of the frame [param idx] in the [param anim] animation. + Sets the [param texture] and the [param duration] of the frame [param idx] in the [param anim] animation. </description> </method> </methods> diff --git a/doc/classes/Window.xml b/doc/classes/Window.xml index dd145c016c..da31e6761e 100644 --- a/doc/classes/Window.xml +++ b/doc/classes/Window.xml @@ -729,6 +729,7 @@ [b]Note:[/b] This flag is implemented on macOS. </constant> <constant name="FLAG_MOUSE_PASSTHROUGH" value="7" enum="Flags"> + All mouse events are passed to the underlying window of the same application. </constant> <constant name="FLAG_MAX" value="8" enum="Flags"> Max value of the [enum Flags]. diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 2887b6b44f..a946a2ceb5 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -905,10 +905,12 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend if (rect->flags & CANVAS_RECT_FLIP_H) { src_rect.size.x *= -1; + state.instance_data_array[r_index].flags |= FLAGS_FLIP_H; } if (rect->flags & CANVAS_RECT_FLIP_V) { src_rect.size.y *= -1; + state.instance_data_array[r_index].flags |= FLAGS_FLIP_V; } if (rect->flags & CANVAS_RECT_TRANSPOSE) { diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h index db9203b249..916e12057c 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.h +++ b/drivers/gles3/rasterizer_canvas_gles3.h @@ -75,6 +75,9 @@ class RasterizerCanvasGLES3 : public RendererCanvasRender { FLAGS_USE_MSDF = (1 << 28), FLAGS_USE_LCD = (1 << 29), + + FLAGS_FLIP_H = (1 << 30), + FLAGS_FLIP_V = (1 << 31), }; enum { diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl index 9a90f33674..8c53ab6529 100644 --- a/drivers/gles3/shaders/canvas.glsl +++ b/drivers/gles3/shaders/canvas.glsl @@ -579,6 +579,12 @@ void main() { if (normal_used || (using_light && bool(read_draw_data_flags & FLAGS_DEFAULT_NORMAL_MAP_USED))) { normal.xy = texture(normal_texture, uv).xy * vec2(2.0, -2.0) - vec2(1.0, -1.0); + if (bool(read_draw_data_flags & FLAGS_FLIP_H)) { + normal.x = -normal.x; + } + if (bool(read_draw_data_flags & FLAGS_FLIP_V)) { + normal.y = -normal.y; + } normal.z = sqrt(1.0 - dot(normal.xy, normal.xy)); normal_used = true; } else { diff --git a/drivers/gles3/shaders/canvas_uniforms_inc.glsl b/drivers/gles3/shaders/canvas_uniforms_inc.glsl index f65558f042..d53c0fcb26 100644 --- a/drivers/gles3/shaders/canvas_uniforms_inc.glsl +++ b/drivers/gles3/shaders/canvas_uniforms_inc.glsl @@ -27,6 +27,9 @@ #define FLAGS_USE_MSDF uint(1 << 28) #define FLAGS_USE_LCD uint(1 << 29) +#define FLAGS_FLIP_H uint(1 << 30) +#define FLAGS_FLIP_V uint(1 << 31) + layout(std140) uniform GlobalShaderUniformData { //ubo:1 vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS]; }; diff --git a/drivers/unix/dir_access_unix.cpp b/drivers/unix/dir_access_unix.cpp index 557060c907..a162f46103 100644 --- a/drivers/unix/dir_access_unix.cpp +++ b/drivers/unix/dir_access_unix.cpp @@ -189,7 +189,7 @@ void DirAccessUnix::list_dir_end() { _cisdir = false; } -#if defined(HAVE_MNTENT) && defined(X11_ENABLED) +#if defined(HAVE_MNTENT) && defined(LINUXBSD_ENABLED) static bool _filter_drive(struct mntent *mnt) { // Ignore devices that don't point to /dev if (strncmp(mnt->mnt_fsname, "/dev", 4) != 0) { @@ -213,7 +213,7 @@ static void _get_drives(List<String> *list) { // Add root. list->push_back("/"); -#if defined(HAVE_MNTENT) && defined(X11_ENABLED) +#if defined(HAVE_MNTENT) && defined(LINUXBSD_ENABLED) // Check /etc/mtab for the list of mounted partitions. FILE *mtab = setmntent("/etc/mtab", "r"); if (mtab) { diff --git a/editor/import/resource_importer_obj.cpp b/editor/import/resource_importer_obj.cpp index f1fd1d5a03..ad6d41e10c 100644 --- a/editor/import/resource_importer_obj.cpp +++ b/editor/import/resource_importer_obj.cpp @@ -218,7 +218,8 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_ Vector<Vector3> normals; Vector<Vector2> uvs; Vector<Color> colors; - String name; + const String default_name = "Mesh"; + String name = default_name; HashMap<String, HashMap<String, Ref<StandardMaterial3D>>> material_map; @@ -395,9 +396,12 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_ if (l.begins_with("o ") || f->eof_reached()) { if (!p_single_mesh) { - mesh->set_name(name); - r_meshes.push_back(mesh); - mesh.instantiate(); + if (mesh->get_surface_count() > 0) { + mesh->set_name(name); + r_meshes.push_back(mesh); + mesh.instantiate(); + } + name = default_name; current_group = ""; current_material = ""; } @@ -460,6 +464,7 @@ Node *EditorOBJImporter::import_scene(const String &p_path, uint32_t p_flags, co for (const Ref<Mesh> &m : meshes) { Ref<ImporterMesh> mesh; mesh.instantiate(); + mesh->set_name(m->get_name()); for (int i = 0; i < m->get_surface_count(); i++) { mesh->add_surface(m->surface_get_primitive_type(i), m->surface_get_arrays(i), Array(), Dictionary(), m->surface_get_material(i)); } diff --git a/editor/import/scene_import_settings.cpp b/editor/import/scene_import_settings.cpp index f6ec7da158..60415ff926 100644 --- a/editor/import/scene_import_settings.cpp +++ b/editor/import/scene_import_settings.cpp @@ -703,15 +703,17 @@ void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) { } MeshData &md = mesh_map[p_id]; - if (p_from != mesh_tree) { - md.mesh_node->uncollapse_tree(); - md.mesh_node->select(0); - mesh_tree->ensure_cursor_is_visible(); - } - if (p_from != scene_tree) { - md.scene_node->uncollapse_tree(); - md.scene_node->select(0); - scene_tree->ensure_cursor_is_visible(); + if (md.mesh_node != nullptr) { + if (p_from != mesh_tree) { + md.mesh_node->uncollapse_tree(); + md.mesh_node->select(0); + mesh_tree->ensure_cursor_is_visible(); + } + if (p_from != scene_tree) { + md.scene_node->uncollapse_tree(); + md.scene_node->select(0); + scene_tree->ensure_cursor_is_visible(); + } } mesh_preview->set_mesh(md.mesh); diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp index 3c6ed0f049..0ac375407c 100644 --- a/editor/plugins/tiles/tile_atlas_view.cpp +++ b/editor/plugins/tiles/tile_atlas_view.cpp @@ -454,21 +454,31 @@ void TileAtlasView::set_padding(Side p_side, int p_padding) { margin_container_paddings[p_side] = p_padding; } -Vector2i TileAtlasView::get_atlas_tile_coords_at_pos(const Vector2 p_pos) const { +Vector2i TileAtlasView::get_atlas_tile_coords_at_pos(const Vector2 p_pos, bool p_clamp) const { Ref<Texture2D> texture = tile_set_atlas_source->get_texture(); - if (texture.is_valid()) { - Vector2i margins = tile_set_atlas_source->get_margins(); - Vector2i separation = tile_set_atlas_source->get_separation(); - Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size(); + if (!texture.is_valid()) { + return TileSetSource::INVALID_ATLAS_COORDS; + } + + Vector2i margins = tile_set_atlas_source->get_margins(); + Vector2i separation = tile_set_atlas_source->get_separation(); + Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size(); - // Compute index in atlas - Vector2 pos = p_pos - margins; - Vector2i ret = (pos / (texture_region_size + separation)).floor(); + // Compute index in atlas + Vector2 pos = p_pos - margins; + Vector2i ret = (pos / (texture_region_size + separation)).floor(); - return ret; + // Return invalid value (without clamp). + Rect2i rect = Rect2(Vector2i(), tile_set_atlas_source->get_atlas_grid_size()); + if (!p_clamp && !rect.has_point(ret)) { + return TileSetSource::INVALID_ATLAS_COORDS; } - return TileSetSource::INVALID_ATLAS_COORDS; + // Clamp. + ret.x = CLAMP(ret.x, 0, rect.size.x - 1); + ret.y = CLAMP(ret.y, 0, rect.size.y - 1); + + return ret; } void TileAtlasView::_update_alternative_tiles_rect_cache() { diff --git a/editor/plugins/tiles/tile_atlas_view.h b/editor/plugins/tiles/tile_atlas_view.h index 166abca2a3..f719bee704 100644 --- a/editor/plugins/tiles/tile_atlas_view.h +++ b/editor/plugins/tiles/tile_atlas_view.h @@ -128,7 +128,7 @@ public: void set_texture_grid_visible(bool p_visible) { base_tiles_texture_grid->set_visible(p_visible); }; void set_tile_shape_grid_visible(bool p_visible) { base_tiles_shape_grid->set_visible(p_visible); }; - Vector2i get_atlas_tile_coords_at_pos(const Vector2 p_pos) const; + Vector2i get_atlas_tile_coords_at_pos(const Vector2 p_pos, bool p_clamp = false) const; void add_control_over_atlas_tiles(Control *p_control, bool scaled = true) { if (scaled) { diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp index 966d394ca1..81aa9bf272 100644 --- a/editor/plugins/tiles/tile_data_editors.cpp +++ b/editor/plugins/tiles/tile_data_editors.cpp @@ -497,11 +497,11 @@ void GenericTilePolygonEditor::_base_control_gui_input(Ref<InputEvent> p_event) Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid()) { - if (mb->get_button_index() == MouseButton::WHEEL_UP && mb->is_ctrl_pressed()) { + if (mb->get_button_index() == MouseButton::WHEEL_UP && mb->is_command_or_control_pressed()) { editor_zoom_widget->set_zoom_by_increments(1); _zoom_changed(); accept_event(); - } else if (mb->get_button_index() == MouseButton::WHEEL_DOWN && mb->is_ctrl_pressed()) { + } else if (mb->get_button_index() == MouseButton::WHEEL_DOWN && mb->is_command_or_control_pressed()) { editor_zoom_widget->set_zoom_by_increments(-1); _zoom_changed(); accept_event(); @@ -926,8 +926,8 @@ void TileDataDefaultEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas_ p_canvas_item->draw_set_transform_matrix(p_transform); Rect2i rect; - rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos)); - rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()))); + rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos, true)); + rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()), true)); rect = rect.abs(); RBSet<TileMapCell> edited; @@ -961,7 +961,7 @@ void TileDataDefaultEditor::forward_painting_atlas_gui_input(TileAtlasView *p_ti Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid()) { if (drag_type == DRAG_TYPE_PAINT) { - Vector<Vector2i> line = Geometry2D::bresenham_line(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_pos), p_tile_atlas_view->get_atlas_tile_coords_at_pos(mm->get_position())); + Vector<Vector2i> line = Geometry2D::bresenham_line(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_pos, true), p_tile_atlas_view->get_atlas_tile_coords_at_pos(mm->get_position(), true)); for (int i = 0; i < line.size(); i++) { Vector2i coords = p_tile_set_atlas_source->get_tile_at_coords(line[i]); if (coords != TileSetSource::INVALID_ATLAS_COORDS) { @@ -984,14 +984,14 @@ void TileDataDefaultEditor::forward_painting_atlas_gui_input(TileAtlasView *p_ti if (mb.is_valid()) { if (mb->get_button_index() == MouseButton::LEFT) { if (mb->is_pressed()) { - if (picker_button->is_pressed()) { - Vector2i coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position()); + if (picker_button->is_pressed() || (mb->is_command_or_control_pressed() && !mb->is_shift_pressed())) { + Vector2i coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position(), true); coords = p_tile_set_atlas_source->get_tile_at_coords(coords); if (coords != TileSetSource::INVALID_ATLAS_COORDS) { _set_painted_value(p_tile_set_atlas_source, coords, 0); picker_button->set_pressed(false); } - } else if (mb->is_ctrl_pressed()) { + } else if (mb->is_command_or_control_pressed() && mb->is_shift_pressed()) { drag_type = DRAG_TYPE_PAINT_RECT; drag_modified.clear(); drag_painted_value = _get_painted_value(); @@ -1000,7 +1000,7 @@ void TileDataDefaultEditor::forward_painting_atlas_gui_input(TileAtlasView *p_ti drag_type = DRAG_TYPE_PAINT; drag_modified.clear(); drag_painted_value = _get_painted_value(); - Vector2i coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position()); + Vector2i coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position(), true); coords = p_tile_set_atlas_source->get_tile_at_coords(coords); if (coords != TileSetSource::INVALID_ATLAS_COORDS) { TileMapCell cell; @@ -1015,8 +1015,8 @@ void TileDataDefaultEditor::forward_painting_atlas_gui_input(TileAtlasView *p_ti } else { if (drag_type == DRAG_TYPE_PAINT_RECT) { Rect2i rect; - rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos)); - rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position())); + rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos, true)); + rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position(), true)); rect = rect.abs(); drag_modified.clear(); @@ -1532,26 +1532,32 @@ Variant TileDataCollisionEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas } void TileDataCollisionEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value) { - Array new_array = p_new_value; + Dictionary new_dict = p_new_value; EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); for (KeyValue<TileMapCell, Variant> &E : p_previous_values) { - Array old_array = E.value; - Vector2i coords = E.key.get_atlas_coords(); - undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygons_count", coords.x, coords.y, E.key.alternative_tile, physics_layer), old_array.size()); - for (int i = 0; i < old_array.size(); i++) { - Dictionary dict = old_array[i]; - undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/points", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), dict["points"]); - undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), dict["one_way"]); - undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way_margin", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), dict["one_way_margin"]); + + Dictionary old_dict = E.value; + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/linear_velocity", coords.x, coords.y, E.key.alternative_tile, physics_layer), old_dict["linear_velocity"]); + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/angular_velocity", coords.x, coords.y, E.key.alternative_tile, physics_layer), old_dict["angular_velocity"]); + Array old_polygon_array = old_dict["polygons"]; + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygons_count", coords.x, coords.y, E.key.alternative_tile, physics_layer), old_polygon_array.size()); + for (int i = 0; i < old_polygon_array.size(); i++) { + Dictionary polygon_dict = old_polygon_array[i]; + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/points", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), polygon_dict["points"]); + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), polygon_dict["one_way"]); + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way_margin", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), polygon_dict["one_way_margin"]); } - undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygons_count", coords.x, coords.y, E.key.alternative_tile, physics_layer), new_array.size()); - for (int i = 0; i < new_array.size(); i++) { - Dictionary dict = new_array[i]; - undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/points", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), dict["points"]); - undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), dict["one_way"]); - undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way_margin", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), dict["one_way_margin"]); + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/linear_velocity", coords.x, coords.y, E.key.alternative_tile, physics_layer), new_dict["linear_velocity"]); + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/angular_velocity", coords.x, coords.y, E.key.alternative_tile, physics_layer), new_dict["angular_velocity"]); + Array new_polygon_array = new_dict["polygons"]; + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygons_count", coords.x, coords.y, E.key.alternative_tile, physics_layer), new_polygon_array.size()); + for (int i = 0; i < new_polygon_array.size(); i++) { + Dictionary polygon_dict = new_polygon_array[i]; + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/points", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), polygon_dict["points"]); + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), polygon_dict["one_way"]); + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way_margin", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), polygon_dict["one_way_margin"]); } } } @@ -1812,8 +1818,8 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas p_canvas_item->draw_set_transform_matrix(p_transform); Rect2i rect; - rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos)); - rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()))); + rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos, true)); + rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()), true)); rect = rect.abs(); RBSet<TileMapCell> edited; @@ -1842,8 +1848,8 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas int terrain_set = int(painted["terrain_set"]); Rect2i rect; - rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos)); - rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()))); + rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos, true)); + rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()), true)); rect = rect.abs(); RBSet<TileMapCell> edited; @@ -2003,7 +2009,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid()) { if (drag_type == DRAG_TYPE_PAINT_TERRAIN_SET) { - Vector<Vector2i> line = Geometry2D::bresenham_line(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_pos), p_tile_atlas_view->get_atlas_tile_coords_at_pos(mm->get_position())); + Vector<Vector2i> line = Geometry2D::bresenham_line(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_pos, true), p_tile_atlas_view->get_atlas_tile_coords_at_pos(mm->get_position(), true)); for (int i = 0; i < line.size(); i++) { Vector2i coords = p_tile_set_atlas_source->get_tile_at_coords(line[i]); if (coords != TileSetSource::INVALID_ATLAS_COORDS) { @@ -2037,7 +2043,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t } else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_BITS) { int terrain_set = Dictionary(drag_painted_value)["terrain_set"]; int terrain = Dictionary(drag_painted_value)["terrain"]; - Vector<Vector2i> line = Geometry2D::bresenham_line(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_pos), p_tile_atlas_view->get_atlas_tile_coords_at_pos(mm->get_position())); + Vector<Vector2i> line = Geometry2D::bresenham_line(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_pos, true), p_tile_atlas_view->get_atlas_tile_coords_at_pos(mm->get_position(), true)); for (int i = 0; i < line.size(); i++) { Vector2i coords = p_tile_set_atlas_source->get_tile_at_coords(line[i]); if (coords != TileSetSource::INVALID_ATLAS_COORDS) { @@ -2091,7 +2097,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t if (mb.is_valid()) { if (mb->get_button_index() == MouseButton::LEFT || mb->get_button_index() == MouseButton::RIGHT) { if (mb->is_pressed()) { - if (mb->get_button_index() == MouseButton::LEFT && picker_button->is_pressed()) { + if (picker_button->is_pressed() || (mb->is_command_or_control_pressed() && !mb->is_shift_pressed())) { Vector2i coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position()); coords = p_tile_set_atlas_source->get_tile_at_coords(coords); if (coords != TileSetSource::INVALID_ATLAS_COORDS) { @@ -2134,7 +2140,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t if (mb->get_button_index() == MouseButton::RIGHT) { terrain_set = -1; } - if (mb->is_ctrl_pressed()) { + if (mb->is_command_or_control_pressed() && mb->is_shift_pressed()) { // Paint terrain set with rect. drag_type = DRAG_TYPE_PAINT_TERRAIN_SET_RECT; drag_modified.clear(); @@ -2175,7 +2181,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t if (mb->get_button_index() == MouseButton::RIGHT) { terrain = -1; } - if (mb->is_ctrl_pressed()) { + if (mb->is_command_or_control_pressed() && mb->is_shift_pressed()) { // Paint terrain bits with rect. drag_type = DRAG_TYPE_PAINT_TERRAIN_BITS_RECT; drag_modified.clear(); @@ -2238,8 +2244,8 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); if (drag_type == DRAG_TYPE_PAINT_TERRAIN_SET_RECT) { Rect2i rect; - rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos)); - rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position())); + rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos, true)); + rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position(), true)); rect = rect.abs(); RBSet<TileMapCell> edited; @@ -2326,8 +2332,8 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t int terrain = int(painted["terrain"]); Rect2i rect; - rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos)); - rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position())); + rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos, true)); + rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position(), true)); rect = rect.abs(); RBSet<TileMapCell> edited; diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index 81dd8bc8a6..23b6da438c 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -1716,8 +1716,8 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_draw() { // Draw the selection rect. if (tile_set_dragging_selection) { - Vector2i start_tile = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_set_drag_start_mouse_pos); - Vector2i end_tile = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position()); + Vector2i start_tile = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_set_drag_start_mouse_pos, true); + Vector2i end_tile = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position(), true); Rect2i region = Rect2i(start_tile, end_tile - start_tile).abs(); region.size += Vector2i(1, 1); @@ -1812,8 +1812,8 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_gui_input(const Ref<InputEven tile_set_selection.clear(); } // Compute the covered area. - Vector2i start_tile = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_set_drag_start_mouse_pos); - Vector2i end_tile = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position()); + Vector2i start_tile = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_set_drag_start_mouse_pos, true); + Vector2i end_tile = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position(), true); if (start_tile != TileSetSource::INVALID_ATLAS_COORDS && end_tile != TileSetSource::INVALID_ATLAS_COORDS) { Rect2i region = Rect2i(start_tile, end_tile - start_tile).abs(); region.size += Vector2i(1, 1); diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index 43eaafc02c..9e2cb47c23 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -1015,9 +1015,9 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven // Handle the event. Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid()) { - Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos); - Vector2i last_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_mouse_pos); - Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position()); + Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos, true); + Vector2i last_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_mouse_pos, true); + Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position(), true); Vector2i grid_size = tile_set_atlas_source->get_atlas_grid_size(); @@ -1066,7 +1066,6 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven // Move tile. Vector2 mouse_offset = (Vector2(tile_set_atlas_source->get_tile_size_in_atlas(drag_current_tile)) / 2.0 - Vector2(0.5, 0.5)) * tile_set->get_tile_size(); Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position() - mouse_offset); - coords = coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1)); if (drag_current_tile != coords && tile_set_atlas_source->has_room_for_tile(coords, tile_set_atlas_source->get_tile_size_in_atlas(drag_current_tile), tile_set_atlas_source->get_tile_animation_columns(drag_current_tile), tile_set_atlas_source->get_tile_animation_separation(drag_current_tile), tile_set_atlas_source->get_tile_animation_frames_count(drag_current_tile), drag_current_tile)) { tile_set_atlas_source->move_tile_in_atlas(drag_current_tile, coords); selection.clear(); @@ -1334,8 +1333,8 @@ void TileSetAtlasSourceEditor::_end_dragging() { undo_redo->commit_action(); } break; case DRAG_TYPE_CREATE_TILES_USING_RECT: { - Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos); - Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position()); + Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos, true); + Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position(), true); Rect2i area = Rect2i(start_base_tiles_coords, new_base_tiles_coords - start_base_tiles_coords).abs(); area.set_end((area.get_end() + Vector2i(1, 1)).min(tile_set_atlas_source->get_atlas_grid_size())); undo_redo->create_action(TTR("Create tiles")); @@ -1351,8 +1350,8 @@ void TileSetAtlasSourceEditor::_end_dragging() { undo_redo->commit_action(); } break; case DRAG_TYPE_REMOVE_TILES_USING_RECT: { - Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos); - Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position()); + Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos, true); + Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position(), true); Rect2i area = Rect2i(start_base_tiles_coords, new_base_tiles_coords - start_base_tiles_coords).abs(); area.set_end((area.get_end() + Vector2i(1, 1)).min(tile_set_atlas_source->get_atlas_grid_size())); List<PropertyInfo> list; @@ -1402,8 +1401,8 @@ void TileSetAtlasSourceEditor::_end_dragging() { } break; case DRAG_TYPE_RECT_SELECT: { - Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos); - Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position()); + Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos, true); + Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position(), true); ERR_FAIL_COND(start_base_tiles_coords == TileSetSource::INVALID_ATLAS_COORDS); ERR_FAIL_COND(new_base_tiles_coords == TileSetSource::INVALID_ATLAS_COORDS); @@ -1771,8 +1770,8 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() { } } else if (drag_type == DRAG_TYPE_RECT_SELECT || drag_type == DRAG_TYPE_REMOVE_TILES_USING_RECT) { // Draw tiles to be removed. - Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos); - Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position()); + Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos, true); + Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position(), true); Rect2i area = Rect2i(start_base_tiles_coords, new_base_tiles_coords - start_base_tiles_coords).abs(); area.set_end((area.get_end() + Vector2i(1, 1)).min(tile_set_atlas_source->get_atlas_grid_size())); @@ -1801,8 +1800,8 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() { Vector2i separation = tile_set_atlas_source->get_separation(); Vector2i tile_size = tile_set_atlas_source->get_texture_region_size(); - Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos); - Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position()); + Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos, true); + Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position(), true); Rect2i area = Rect2i(start_base_tiles_coords, new_base_tiles_coords - start_base_tiles_coords).abs(); area.set_end((area.get_end() + Vector2i(1, 1)).min(tile_set_atlas_source->get_atlas_grid_size())); for (int x = area.get_position().x; x < area.get_end().x; x++) { @@ -1819,8 +1818,8 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() { // Draw the hovered tile. if (drag_type == DRAG_TYPE_REMOVE_TILES_USING_RECT || drag_type == DRAG_TYPE_CREATE_TILES_USING_RECT) { // Draw the rect. - Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos); - Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position()); + Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos, true); + Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position(), true); Rect2i area = Rect2i(start_base_tiles_coords, new_base_tiles_coords - start_base_tiles_coords).abs(); area.set_end((area.get_end() + Vector2i(1, 1)).min(tile_set_atlas_source->get_atlas_grid_size())); Vector2i margins = tile_set_atlas_source->get_margins(); diff --git a/misc/hooks/asmessage.applescript b/misc/hooks/asmessage.applescript new file mode 100644 index 0000000000..15ba94dc37 --- /dev/null +++ b/misc/hooks/asmessage.applescript @@ -0,0 +1,59 @@ +on run argv + set vButtons to { "OK" } + set vButtonCodes to { 0 } + set vDbutton to "OK" + set vText to "" + set vTitle to "" + set vTimeout to -1 + + repeat with i from 1 to length of argv + try + set vArg to item i of argv + if vArg = "-buttons" then + set vButtonsAndCodes to my fSplit(item (i + 1) of argv, ",") + set vButtons to {} + set vButtonCodes to {} + repeat with j from 1 to length of vButtonsAndCodes + set vBtn to my fSplit(item j of vButtonsAndCodes, ":") + copy (item 1 of vBtn) to the end of the vButtons + copy (item 2 of vBtn) to the end of the vButtonCodes + end repeat + else if vArg = "-title" then + set vTitle to item (i + 1) of argv + else if vArg = "-center" then + -- not supported + else if vArg = "-default" then + set vDbutton to item (i + 1) of argv + else if vArg = "-geometry" then + -- not supported + else if vArg = "-nearmouse" then + -- not supported + else if vArg = "-timeout" then + set vTimeout to item (i + 1) of argv as integer + else if vArg = "-file" then + set vText to read (item (i + 1) of argv) as string + else if vArg = "-text" then + set vText to item (i + 1) of argv + end if + end try + end repeat + + set vDlg to display dialog vText buttons vButtons default button vDbutton with title vTitle giving up after vTimeout with icon stop + set vRet to button returned of vDlg + repeat with i from 1 to length of vButtons + set vBtn to item i of vButtons + if vBtn = vRet + return item i of vButtonCodes + end if + end repeat + + return 0 +end run + +on fSplit(vString, vDelimiter) + set oldDelimiters to AppleScript's text item delimiters + set AppleScript's text item delimiters to vDelimiter + set vArray to every text item of vString + set AppleScript's text item delimiters to oldDelimiters + return vArray +end fSplit diff --git a/misc/hooks/pre-commit-black b/misc/hooks/pre-commit-black index b7335685ae..bbad6a690a 100755 --- a/misc/hooks/pre-commit-black +++ b/misc/hooks/pre-commit-black @@ -34,6 +34,9 @@ XMSG=`which xmessage 2>/dev/null` # Path to powershell (Windows only) PWSH=`which powershell 2>/dev/null` +# Path to osascript (macOS only) +OSA=`which osascript 2>/dev/null` + ################################################################## # There should be no need to change anything below this line. @@ -69,6 +72,10 @@ if [ ! -x "$BLACK" ] ; then elif [ -x "$XMSG" ] ; then $XMSG -center -title "Error" "Error: black executable not found." exit 1 + elif [ -x "$OSA" ] ; then + asmessage="$(canonicalize_filename "$(dirname -- "$0")/asmessage.applescript")" + $OSA "$asmessage" -center -title "Error" --text "Error: black executable not found." + exit 1 elif [ \( \( "$OSTYPE" = "msys" \) -o \( "$OSTYPE" = "win32" \) \) -a \( -x "$PWSH" \) ]; then winmessage="$(canonicalize_filename "$(dirname -- "$0")/winmessage.ps1")" $PWSH -noprofile -executionpolicy bypass -file "$winmessage" -center -title "Error" --text "Error: black executable not found." @@ -159,6 +166,16 @@ while true; do else yn="N" fi + elif [ -x "$OSA" ] ; then + asmessage="$(canonicalize_filename "$(dirname -- "$0")/asmessage.applescript")" + choice=`$OSA "$asmessage" -file "$patch" -buttons "Apply":100,"Apply and stage":200,"Do not apply":0 -center -default "Do not apply" -geometry 800x600 -title "Do you want to apply that patch?"` + if [ "$choice" = "100" ] ; then + yn="Y" + elif [ "$choice" = "200" ] ; then + yn="S" + else + yn="N" + fi elif [ \( \( "$OSTYPE" = "msys" \) -o \( "$OSTYPE" = "win32" \) \) -a \( -x "$PWSH" \) ]; then winmessage="$(canonicalize_filename "$(dirname -- "$0")/winmessage.ps1")" $PWSH -noprofile -executionpolicy bypass -file "$winmessage" -file "$patch" -buttons "Apply":100,"Apply and stage":200,"Do not apply":0 -center -default "Do not apply" -geometry 800x600 -title "Do you want to apply that patch?" @@ -171,7 +188,7 @@ while true; do yn="N" fi else - printf "Error: zenity, xmessage, or powershell executable not found.\n" + printf "Error: zenity, xmessage, osascript, or powershell executable not found.\n" exit 1 fi else diff --git a/misc/hooks/pre-commit-clang-format b/misc/hooks/pre-commit-clang-format index 9570d5120b..fd0213c175 100755 --- a/misc/hooks/pre-commit-clang-format +++ b/misc/hooks/pre-commit-clang-format @@ -47,6 +47,9 @@ XMSG=`which xmessage 2>/dev/null` # Path to powershell (Windows only) PWSH=`which powershell 2>/dev/null` +# Path to osascript (macOS only) +OSA=`which osascript 2>/dev/null` + ################################################################## # There should be no need to change anything below this line. @@ -89,6 +92,10 @@ if [ ! -x "$CLANG_FORMAT" ] ; then elif [ -x "$XMSG" ] ; then $XMSG -center -title "Error" "$message" exit 1 + elif [ -x "$OSA" ] ; then + asmessage="$(canonicalize_filename "$(dirname -- "$0")/asmessage.applescript")" + $OSA "$asmessage" -center -title "Error" --text "$message" + exit 1 elif [ \( \( "$OSTYPE" = "msys" \) -o \( "$OSTYPE" = "win32" \) \) -a \( -x "$PWSH" \) ]; then winmessage="$(canonicalize_filename "$(dirname -- "$0")/winmessage.ps1")" $PWSH -noprofile -executionpolicy bypass -file "$winmessage" -center -title "Error" --text "$message" @@ -199,6 +206,16 @@ while true; do else yn="N" fi + elif [ -x "$OSA" ] ; then + asmessage="$(canonicalize_filename "$(dirname -- "$0")/asmessage.applescript")" + choice=`$OSA "$asmessage" -file "$patch" -buttons "Apply":100,"Apply and stage":200,"Do not apply":0 -center -default "Do not apply" -geometry 800x600 -title "Do you want to apply that patch?"` + if [ "$choice" = "100" ] ; then + yn="Y" + elif [ "$choice" = "200" ] ; then + yn="S" + else + yn="N" + fi elif [ \( \( "$OSTYPE" = "msys" \) -o \( "$OSTYPE" = "win32" \) \) -a \( -x "$PWSH" \) ]; then winmessage="$(canonicalize_filename "$(dirname -- "$0")/winmessage.ps1")" $PWSH -noprofile -executionpolicy bypass -file "$winmessage" -file "$patch" -buttons "Apply":100,"Apply and stage":200,"Do not apply":0 -center -default "Do not apply" -geometry 800x600 -title "Do you want to apply that patch?" @@ -211,7 +228,7 @@ while true; do yn="N" fi else - printf "Error: zenity, xmessage, or powershell executable not found.\n" + printf "Error: zenity, xmessage, osascript, or powershell executable not found.\n" exit 1 fi else diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 1b488d560c..edd94da824 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -1722,21 +1722,52 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { if (list_resolved) { variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; variable_type.kind = GDScriptParser::DataType::BUILTIN; - variable_type.builtin_type = Variant::INT; // Can this ever be a float or something else? - p_for->variable->set_datatype(variable_type); + variable_type.builtin_type = Variant::INT; } else if (p_for->list) { resolve_node(p_for->list, false); - if (p_for->list->datatype.has_container_element_type()) { - variable_type = p_for->list->datatype.get_container_element_type(); - variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; - } else if (p_for->list->datatype.is_typed_container_type()) { - variable_type = p_for->list->datatype.get_typed_container_type(); - variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; - } else { - // Last resort - // TODO: Must other cases be handled? Must we mark as unsafe? - variable_type.type_source = GDScriptParser::DataType::UNDETECTED; + GDScriptParser::DataType list_type = p_for->list->get_datatype(); + if (!list_type.is_hard_type()) { + mark_node_unsafe(p_for->list); + } + if (list_type.is_variant()) { + variable_type.kind = GDScriptParser::DataType::VARIANT; + mark_node_unsafe(p_for->list); + } else if (list_type.has_container_element_type()) { + variable_type = list_type.get_container_element_type(); + variable_type.type_source = list_type.type_source; + } else if (list_type.is_typed_container_type()) { + variable_type = list_type.get_typed_container_type(); + variable_type.type_source = list_type.type_source; + } else if (list_type.builtin_type == Variant::INT || list_type.builtin_type == Variant::FLOAT || list_type.builtin_type == Variant::STRING) { + variable_type.type_source = list_type.type_source; + variable_type.kind = GDScriptParser::DataType::BUILTIN; + variable_type.builtin_type = list_type.builtin_type; + } else if (list_type.builtin_type == Variant::VECTOR2I || list_type.builtin_type == Variant::VECTOR3I) { + variable_type.type_source = list_type.type_source; + variable_type.kind = GDScriptParser::DataType::BUILTIN; + variable_type.builtin_type = Variant::INT; + } else if (list_type.builtin_type == Variant::VECTOR2 || list_type.builtin_type == Variant::VECTOR3) { + variable_type.type_source = list_type.type_source; + variable_type.kind = GDScriptParser::DataType::BUILTIN; + variable_type.builtin_type = Variant::FLOAT; + } else if (list_type.builtin_type == Variant::OBJECT) { + GDScriptParser::DataType return_type; + List<GDScriptParser::DataType> par_types; + int default_arg_count = 0; + bool is_static = false; + bool is_vararg = false; + if (get_function_signature(p_for->list, false, list_type, CoreStringNames::get_singleton()->_iter_get, return_type, par_types, default_arg_count, is_static, is_vararg)) { + variable_type = return_type; + variable_type.type_source = list_type.type_source; + } else if (!list_type.is_hard_type()) { + variable_type.kind = GDScriptParser::DataType::VARIANT; + } else { + push_error(vformat(R"(Unable to iterate on object of type "%s".)", list_type.to_string()), p_for->list); + } + } else if (list_type.builtin_type == Variant::ARRAY || list_type.builtin_type == Variant::DICTIONARY || !list_type.is_hard_type()) { variable_type.kind = GDScriptParser::DataType::VARIANT; + } else { + push_error(vformat(R"(Unable to iterate on value of type "%s".)", list_type.to_string()), p_for->list); } } if (p_for->variable) { @@ -2910,8 +2941,8 @@ void GDScriptAnalyzer::reduce_identifier_from_base_set_class(GDScriptParser::Ide p_identifier->set_datatype(p_identifier_datatype); Error err = OK; - GDScript *scr = GDScriptCache::get_full_script(p_identifier_datatype.script_path, err).ptr(); - ERR_FAIL_COND_MSG(err != OK, "Error while getting full script."); + GDScript *scr = GDScriptCache::get_shallow_script(p_identifier_datatype.script_path, err).ptr(); + ERR_FAIL_COND_MSG(err != OK, vformat(R"(Error while getting cache for script "%s".)", p_identifier_datatype.script_path)); scr = scr->find_class(p_identifier_datatype.class_type->fqcn); p_identifier->reduced_value = scr; p_identifier->is_constant = true; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 77c6690d20..d63a1b4536 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -213,7 +213,7 @@ static bool _have_exact_arguments(const MethodBind *p_method, const Vector<GDScr } GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root, bool p_initializer, const GDScriptCodeGenerator::Address &p_index_addr) { - if (p_expression->is_constant && !p_expression->get_datatype().is_meta_type) { + if (p_expression->is_constant && !(p_expression->get_datatype().is_meta_type && p_expression->get_datatype().kind == GDScriptParser::DataType::CLASS)) { return codegen.add_constant(p_expression->reduced_value); } diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 397c43b14b..3fc0924b4c 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -1275,6 +1275,14 @@ static void _find_identifiers(const GDScriptParser::CompletionContext &p_context } r_result.insert(option.display, option); } + + // Global classes + List<StringName> global_classes; + ScriptServer::get_global_class_list(&global_classes); + for (const StringName &E : global_classes) { + ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_OTHER_USER_CODE); + r_result.insert(option.display, option); + } } static GDScriptCompletionIdentifier _type_from_variant(const Variant &p_value) { diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 97d5c1d8b2..f5d3306376 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -540,43 +540,28 @@ void GDScriptParser::parse_program() { head = alloc_node<ClassNode>(); head->fqcn = script_path; current_class = head; + bool can_have_class_or_extends = true; - // If we happen to parse an annotation before extends or class_name keywords, track it. - // @tool is allowed, but others should fail. - AnnotationNode *premature_annotation = nullptr; - - if (match(GDScriptTokenizer::Token::ANNOTATION)) { - // Check for @tool, script-level, or standalone annotation. + while (match(GDScriptTokenizer::Token::ANNOTATION)) { AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL); if (annotation != nullptr) { - if (annotation->name == SNAME("@tool")) { - // TODO: don't allow @tool anywhere else. (Should all script annotations be the first thing?). - _is_tool = true; - if (previous.type != GDScriptTokenizer::Token::NEWLINE) { - push_error(R"(Expected newline after "@tool" annotation.)"); - } - // @tool annotation has no specific target. - annotation->apply(this, nullptr); - } else if (annotation->applies_to(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE)) { - premature_annotation = annotation; - if (previous.type != GDScriptTokenizer::Token::NEWLINE) { - push_error(R"(Expected newline after a standalone annotation.)"); - } + if (annotation->applies_to(AnnotationInfo::SCRIPT)) { annotation->apply(this, head); } else { - premature_annotation = annotation; annotation_stack.push_back(annotation); + // This annotation must appear after script-level annotations + // and class_name/extends (ex: could be @onready or @export), + // so we stop looking for script-level stuff. + can_have_class_or_extends = false; + break; } } } - for (bool should_break = false; !should_break;) { + while (can_have_class_or_extends) { // Order here doesn't matter, but there should be only one of each at most. switch (current.type) { case GDScriptTokenizer::Token::CLASS_NAME: - if (premature_annotation != nullptr) { - push_error(R"("class_name" should be used before annotations (except @tool).)"); - } advance(); if (head->identifier != nullptr) { push_error(R"("class_name" can only be used once.)"); @@ -585,9 +570,6 @@ void GDScriptParser::parse_program() { } break; case GDScriptTokenizer::Token::EXTENDS: - if (premature_annotation != nullptr) { - push_error(R"("extends" should be used before annotations (except @tool).)"); - } advance(); if (head->extends_used) { push_error(R"("extends" can only be used once.)"); @@ -597,7 +579,8 @@ void GDScriptParser::parse_program() { } break; default: - should_break = true; + // No tokens are allowed between script annotations and class/extends. + can_have_class_or_extends = false; break; } @@ -606,21 +589,6 @@ void GDScriptParser::parse_program() { } } - if (match(GDScriptTokenizer::Token::ANNOTATION)) { - // Check for a script-level, or standalone annotation. - AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL); - if (annotation != nullptr) { - if (annotation->applies_to(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE)) { - if (previous.type != GDScriptTokenizer::Token::NEWLINE) { - push_error(R"(Expected newline after a standalone annotation.)"); - } - annotation->apply(this, head); - } else { - annotation_stack.push_back(annotation); - } - } - } - parse_class_body(true); complete_extents(head); diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.gd new file mode 100644 index 0000000000..cf56a0a933 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.gd @@ -0,0 +1,6 @@ +const constant_float = 1.0 + +func test(): + for x in constant_float: + if x is String: + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.out new file mode 100644 index 0000000000..e309831b3e --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Expression is of type "float" so it can't be of type "String". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.gd new file mode 100644 index 0000000000..5ee8ac19e1 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.gd @@ -0,0 +1,6 @@ +const constant_int = 1 + +func test(): + for x in constant_int: + if x is String: + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.out new file mode 100644 index 0000000000..54c190cf8a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Expression is of type "int" so it can't be of type "String". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.gd new file mode 100644 index 0000000000..b3db4f3b49 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.gd @@ -0,0 +1,6 @@ +enum { enum_value = 1 } + +func test(): + for x in enum_value: + if x is String: + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.out new file mode 100644 index 0000000000..54c190cf8a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Expression is of type "int" so it can't be of type "String". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.gd new file mode 100644 index 0000000000..87c54f7402 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.gd @@ -0,0 +1,6 @@ +func test(): + var hard_float := 1.0 + + for x in hard_float: + if x is String: + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.out new file mode 100644 index 0000000000..e309831b3e --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Expression is of type "float" so it can't be of type "String". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.gd new file mode 100644 index 0000000000..2a43f5a930 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.gd @@ -0,0 +1,6 @@ +func test(): + var hard_int := 1 + + for x in hard_int: + if x is String: + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.out new file mode 100644 index 0000000000..54c190cf8a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Expression is of type "int" so it can't be of type "String". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.gd new file mode 100644 index 0000000000..c3920d35b3 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.gd @@ -0,0 +1,14 @@ +class Iterator: + func _iter_init(_count): + return true + func _iter_next(_count): + return false + func _iter_get(_count) -> StringName: + return &'custom' + +func test(): + var hard_iterator := Iterator.new() + + for x in hard_iterator: + if x is int: + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.out new file mode 100644 index 0000000000..a48591a3b4 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Expression is of type "StringName" so it can't be of type "int". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.gd new file mode 100644 index 0000000000..b36d87aabe --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.gd @@ -0,0 +1,6 @@ +func test(): + var hard_string := 'a' + + for x in hard_string: + if x is int: + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.out new file mode 100644 index 0000000000..92c5ebc599 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Expression is of type "String" so it can't be of type "int". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.gd new file mode 100644 index 0000000000..060a8bedf9 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.gd @@ -0,0 +1,3 @@ +func test(): + for x in true: + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.out new file mode 100644 index 0000000000..94cb038885 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Unable to iterate on value of type "bool". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.gd new file mode 100644 index 0000000000..6cfc822482 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.gd @@ -0,0 +1,4 @@ +func test(): + for x in 1: + if x is String: + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.out new file mode 100644 index 0000000000..54c190cf8a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Expression is of type "int" so it can't be of type "String". diff --git a/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.gd b/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.gd new file mode 100644 index 0000000000..7b74be6f2c --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.gd @@ -0,0 +1,15 @@ +func test(): + var variant_int: Variant = 1 + var weak_int = 1 + + for x in variant_int: + if x is String: + print('never') + print(x) + + for x in weak_int: + if x is String: + print('never') + print(x) + + print('ok') diff --git a/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.out b/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.out new file mode 100644 index 0000000000..7677671cfd --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.out @@ -0,0 +1,4 @@ +GDTEST_OK +0 +0 +ok diff --git a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd index 179e454073..0085b3f367 100644 --- a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd +++ b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd @@ -1,6 +1,6 @@ -# Error here. `class_name` should be used *before* annotations, not after (except @tool). -@icon("res://path/to/optional/icon.svg") +# Error here. Annotations should be used before `class_name`, not after. class_name HelloWorld +@icon("res://path/to/optional/icon.svg") func test(): pass diff --git a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out index 02b33c8692..a598ff8424 100644 --- a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out +++ b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out @@ -1,2 +1,2 @@ GDTEST_PARSER_ERROR -"class_name" should be used before annotations (except @tool). +Annotation "@icon" is not allowed in this level. diff --git a/modules/gdscript/tests/scripts/parser/features/class_name.gd b/modules/gdscript/tests/scripts/parser/features/class_name.gd index 8bd188e247..19009e433d 100644 --- a/modules/gdscript/tests/scripts/parser/features/class_name.gd +++ b/modules/gdscript/tests/scripts/parser/features/class_name.gd @@ -1,5 +1,5 @@ -class_name HelloWorld @icon("res://path/to/optional/icon.svg") +class_name HelloWorld func test(): pass diff --git a/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.gd b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.gd new file mode 100644 index 0000000000..81355e0255 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.gd @@ -0,0 +1,51 @@ +const constant_float = 1.0 +const constant_int = 1 +enum { enum_value = 1 } + +class Iterator: + func _iter_init(_count): + return true + func _iter_next(_count): + return false + func _iter_get(_count) -> StringName: + return &'custom' + +func test(): + var hard_float := 1.0 + var hard_int := 1 + var hard_string := '0' + var hard_iterator := Iterator.new() + + var variant_float: Variant = hard_float + var variant_int: Variant = hard_int + var variant_string: Variant = hard_string + var variant_iterator: Variant = hard_iterator + + for i in 1.0: + print(typeof(i) == TYPE_FLOAT) + for i in 1: + print(typeof(i) == TYPE_INT) + for i in 'a': + print(typeof(i) == TYPE_STRING) + for i in Iterator.new(): + print(typeof(i) == TYPE_STRING_NAME) + + for i in hard_float: + print(typeof(i) == TYPE_FLOAT) + for i in hard_int: + print(typeof(i) == TYPE_INT) + for i in hard_string: + print(typeof(i) == TYPE_STRING) + for i in hard_iterator: + print(typeof(i) == TYPE_STRING_NAME) + + for i in variant_float: + print(typeof(i) == TYPE_FLOAT) + for i in variant_int: + print(typeof(i) == TYPE_INT) + for i in variant_string: + print(typeof(i) == TYPE_STRING) + for i in variant_iterator: + print(typeof(i) == TYPE_STRING_NAME) + + print('ok') diff --git a/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.out b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.out new file mode 100644 index 0000000000..b3e82d52ef --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.out @@ -0,0 +1,14 @@ +GDTEST_OK +true +true +true +true +true +true +true +true +true +true +true +true +ok diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs index 19f7c54847..b57317e1d0 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs @@ -816,26 +816,26 @@ namespace Godot public Basis(Vector3 axis, real_t angle) { Vector3 axisSq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z); - real_t cosine = Mathf.Cos(angle); - Row0.x = axisSq.x + cosine * (1.0f - axisSq.x); - Row1.y = axisSq.y + cosine * (1.0f - axisSq.y); - Row2.z = axisSq.z + cosine * (1.0f - axisSq.z); + (real_t sin, real_t cos) = Mathf.SinCos(angle); - real_t sine = Mathf.Sin(angle); - real_t t = 1.0f - cosine; + Row0.x = axisSq.x + cos * (1.0f - axisSq.x); + Row1.y = axisSq.y + cos * (1.0f - axisSq.y); + Row2.z = axisSq.z + cos * (1.0f - axisSq.z); + + real_t t = 1.0f - cos; real_t xyzt = axis.x * axis.y * t; - real_t zyxs = axis.z * sine; + real_t zyxs = axis.z * sin; Row0.y = xyzt - zyxs; Row1.x = xyzt + zyxs; xyzt = axis.x * axis.z * t; - zyxs = axis.y * sine; + zyxs = axis.y * sin; Row0.z = xyzt + zyxs; Row2.x = xyzt - zyxs; xyzt = axis.y * axis.z * t; - zyxs = axis.x * sine; + zyxs = axis.x * sin; Row1.z = xyzt - zyxs; Row2.y = xyzt + zyxs; } @@ -885,19 +885,29 @@ namespace Godot /// <param name="order">The order to compose the Euler angles.</param> public static Basis FromEuler(Vector3 euler, EulerOrder order = EulerOrder.Yxz) { - real_t c, s; - - c = Mathf.Cos(euler.x); - s = Mathf.Sin(euler.x); - Basis xmat = new Basis(new Vector3(1, 0, 0), new Vector3(0, c, s), new Vector3(0, -s, c)); + (real_t sin, real_t cos) = Mathf.SinCos(euler.x); + Basis xmat = new Basis + ( + new Vector3(1, 0, 0), + new Vector3(0, cos, sin), + new Vector3(0, -sin, cos) + ); - c = Mathf.Cos(euler.y); - s = Mathf.Sin(euler.y); - Basis ymat = new Basis(new Vector3(c, 0, -s), new Vector3(0, 1, 0), new Vector3(s, 0, c)); + (sin, cos) = Mathf.SinCos(euler.y); + Basis ymat = new Basis + ( + new Vector3(cos, 0, -sin), + new Vector3(0, 1, 0), + new Vector3(sin, 0, cos) + ); - c = Mathf.Cos(euler.z); - s = Mathf.Sin(euler.z); - Basis zmat = new Basis(new Vector3(c, s, 0), new Vector3(-s, c, 0), new Vector3(0, 0, 1)); + (sin, cos) = Mathf.SinCos(euler.z); + Basis zmat = new Basis + ( + new Vector3(cos, sin, 0), + new Vector3(-sin, cos, 0), + new Vector3(0, 0, 1) + ); switch (order) { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs index ea05c1547c..72a1868964 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs @@ -83,6 +83,17 @@ namespace Godot } /// <summary> + /// Returns the sine and cosine of angle <paramref name="s"/> in radians. + /// </summary> + /// <param name="s">The angle in radians.</param> + /// <returns>The sine and cosine of that angle.</returns> + public static (real_t Sin, real_t Cos) SinCos(real_t s) + { + (double sin, double cos) = Math.SinCos(s); + return ((real_t)sin, (real_t)cos); + } + + /// <summary> /// Returns <see langword="true"/> if <paramref name="a"/> and <paramref name="b"/> are approximately /// equal to each other. /// The comparison is done using the provided tolerance value. diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs index fd37f8d9e8..f11b3c553a 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs @@ -381,14 +381,14 @@ namespace Godot } real_t radians = Mathf.DegToRad(fovyDegrees / (real_t)2.0); real_t deltaZ = zFar - zNear; - real_t sine = Mathf.Sin(radians); + (real_t sin, real_t cos) = Mathf.SinCos(radians); - if ((deltaZ == 0) || (sine == 0) || (aspect == 0)) + if ((deltaZ == 0) || (sin == 0) || (aspect == 0)) { return Zero; } - real_t cotangent = Mathf.Cos(radians) / sine; + real_t cotangent = cos / sin; Projection proj = Projection.Identity; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs index 47106bb402..8e4f9178f7 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs @@ -542,14 +542,13 @@ namespace Godot } else { - real_t sinAngle = Mathf.Sin(angle * 0.5f); - real_t cosAngle = Mathf.Cos(angle * 0.5f); - real_t s = sinAngle / d; + (real_t sin, real_t cos) = Mathf.SinCos(angle * 0.5f); + real_t s = sin / d; x = axis.x * s; y = axis.y * s; z = axis.z * s; - w = cosAngle; + w = cos; } } @@ -593,12 +592,9 @@ namespace Godot // Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-6) // a3 is the angle of the first rotation, following the notation in this reference. - real_t cosA1 = Mathf.Cos(halfA1); - real_t sinA1 = Mathf.Sin(halfA1); - real_t cosA2 = Mathf.Cos(halfA2); - real_t sinA2 = Mathf.Sin(halfA2); - real_t cosA3 = Mathf.Cos(halfA3); - real_t sinA3 = Mathf.Sin(halfA3); + (real_t sinA1, real_t cosA1) = Mathf.SinCos(halfA1); + (real_t sinA2, real_t cosA2) = Mathf.SinCos(halfA2); + (real_t sinA3, real_t cosA3) = Mathf.SinCos(halfA3); return new Quaternion( (sinA1 * cosA2 * sinA3) + (cosA1 * sinA2 * cosA3), diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs index 6dda150c2b..fa060e3a53 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs @@ -195,8 +195,10 @@ namespace Godot Vector2 s2 = transform.GetScale(); // Slerp rotation - var v1 = new Vector2(Mathf.Cos(r1), Mathf.Sin(r1)); - var v2 = new Vector2(Mathf.Cos(r2), Mathf.Sin(r2)); + (real_t sin1, real_t cos1) = Mathf.SinCos(r1); + (real_t sin2, real_t cos2) = Mathf.SinCos(r2); + var v1 = new Vector2(cos1, sin1); + var v2 = new Vector2(cos2, sin2); real_t dot = v1.Dot(v2); @@ -213,7 +215,8 @@ namespace Godot { real_t angle = weight * Mathf.Acos(dot); Vector2 v3 = (v2 - (v1 * dot)).Normalized(); - v = (v1 * Mathf.Cos(angle)) + (v3 * Mathf.Sin(angle)); + (real_t sine, real_t cos) = Mathf.SinCos(angle); + v = (v1 * sine) + (v3 * cos); } // Extract parameters @@ -434,8 +437,9 @@ namespace Godot /// <param name="origin">The origin vector, or column index 2.</param> public Transform2D(real_t rotation, Vector2 origin) { - x.x = y.y = Mathf.Cos(rotation); - x.y = y.x = Mathf.Sin(rotation); + (real_t sin, real_t cos) = Mathf.SinCos(rotation); + x.x = y.y = cos; + x.y = y.x = sin; y.x *= -1; this.origin = origin; } @@ -451,10 +455,12 @@ namespace Godot /// <param name="origin">The origin vector, or column index 2.</param> public Transform2D(real_t rotation, Vector2 scale, real_t skew, Vector2 origin) { - x.x = Mathf.Cos(rotation) * scale.x; - y.y = Mathf.Cos(rotation + skew) * scale.y; - y.x = -Mathf.Sin(rotation + skew) * scale.y; - x.y = Mathf.Sin(rotation) * scale.x; + (real_t rotationSin, real_t rotationCos) = Mathf.SinCos(rotation); + (real_t rotationSkewSin, real_t rotationSkewCos) = Mathf.SinCos(rotation + skew); + x.x = rotationCos * scale.x; + y.y = rotationSkewCos * scale.y; + y.x = -rotationSkewSin * scale.y; + x.y = rotationSin * scale.x; this.origin = origin; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs index 57cbef1c5c..1e88e18b3d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs @@ -539,11 +539,12 @@ namespace Godot /// <returns>The rotated vector.</returns> public readonly Vector2 Rotated(real_t angle) { - real_t sine = Mathf.Sin(angle); - real_t cosi = Mathf.Cos(angle); - return new Vector2( - x * cosi - y * sine, - x * sine + y * cosi); + (real_t sin, real_t cos) = Mathf.SinCos(angle); + return new Vector2 + ( + x * cos - y * sin, + x * sin + y * cos + ); } /// <summary> @@ -693,7 +694,8 @@ namespace Godot /// <returns>The resulting vector.</returns> public static Vector2 FromAngle(real_t angle) { - return new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)); + (real_t sin, real_t cos) = Mathf.SinCos(angle); + return new Vector2(cos, sin); } /// <summary> diff --git a/modules/multiplayer/multiplayer_spawner.cpp b/modules/multiplayer/multiplayer_spawner.cpp index 7ed69a84d0..0aa54b69f9 100644 --- a/modules/multiplayer/multiplayer_spawner.cpp +++ b/modules/multiplayer/multiplayer_spawner.cpp @@ -199,10 +199,6 @@ void MultiplayerSpawner::_notification(int p_what) { Node *node = Object::cast_to<Node>(ObjectDB::get_instance(E.key)); ERR_CONTINUE(!node); node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &MultiplayerSpawner::_node_exit)); - // This is unlikely, but might still crash the engine. - if (node->is_connected(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready))) { - node->disconnect(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready)); - } get_multiplayer()->object_configuration_remove(node, this); } tracked_nodes.clear(); @@ -244,11 +240,11 @@ void MultiplayerSpawner::_track(Node *p_node, const Variant &p_argument, int p_s if (!tracked_nodes.has(oid)) { tracked_nodes[oid] = SpawnInfo(p_argument.duplicate(true), p_scene_id); p_node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &MultiplayerSpawner::_node_exit).bind(p_node->get_instance_id()), CONNECT_ONE_SHOT); - p_node->connect(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready).bind(p_node->get_instance_id()), CONNECT_ONE_SHOT); + _spawn_notify(p_node->get_instance_id()); } } -void MultiplayerSpawner::_node_ready(ObjectID p_id) { +void MultiplayerSpawner::_spawn_notify(ObjectID p_id) { get_multiplayer()->object_configuration_add(ObjectDB::get_instance(p_id), this); } diff --git a/modules/multiplayer/multiplayer_spawner.h b/modules/multiplayer/multiplayer_spawner.h index 8d401a6818..8a54140e32 100644 --- a/modules/multiplayer/multiplayer_spawner.h +++ b/modules/multiplayer/multiplayer_spawner.h @@ -77,7 +77,7 @@ private: void _track(Node *p_node, const Variant &p_argument, int p_scene_id = INVALID_ID); void _node_added(Node *p_node); void _node_exit(ObjectID p_id); - void _node_ready(ObjectID p_id); + void _spawn_notify(ObjectID p_id); Vector<String> _get_spawnable_scenes() const; void _set_spawnable_scenes(const Vector<String> &p_scenes); diff --git a/modules/multiplayer/scene_replication_interface.cpp b/modules/multiplayer/scene_replication_interface.cpp index e1b7b0c346..233ff76c7d 100644 --- a/modules/multiplayer/scene_replication_interface.cpp +++ b/modules/multiplayer/scene_replication_interface.cpp @@ -125,6 +125,20 @@ void SceneReplicationInterface::on_reset() { } void SceneReplicationInterface::on_network_process() { + // Prevent endless stalling in case of unforseen spawn errors. + if (spawn_queue.size()) { + ERR_PRINT("An error happened during last spawn, this usually means the 'ready' signal was not emitted by the spawned node."); + for (const ObjectID &oid : spawn_queue) { + Node *node = get_id_as<Node>(oid); + ERR_CONTINUE(!node); + if (node->is_connected(SceneStringNames::get_singleton()->ready, callable_mp(this, &SceneReplicationInterface::_node_ready))) { + node->disconnect(SceneStringNames::get_singleton()->ready, callable_mp(this, &SceneReplicationInterface::_node_ready)); + } + } + spawn_queue.clear(); + } + + // Process timed syncs. uint64_t msec = OS::get_singleton()->get_ticks_msec(); for (KeyValue<int, PeerInfo> &E : peers_info) { const HashSet<ObjectID> to_sync = E.value.sync_nodes; @@ -144,17 +158,39 @@ Error SceneReplicationInterface::on_spawn(Object *p_obj, Variant p_config) { // Track node. const ObjectID oid = node->get_instance_id(); TrackedNode &tobj = _track(oid); + + // Spawn state needs to be callected after "ready", but the spawn order follows "enter_tree". ERR_FAIL_COND_V(tobj.spawner != ObjectID(), ERR_ALREADY_IN_USE); tobj.spawner = spawner->get_instance_id(); - spawned_nodes.insert(oid); + spawn_queue.insert(oid); + node->connect(SceneStringNames::get_singleton()->ready, callable_mp(this, &SceneReplicationInterface::_node_ready).bind(oid), Node::CONNECT_ONE_SHOT); + return OK; +} + +void SceneReplicationInterface::_node_ready(const ObjectID &p_oid) { + ERR_FAIL_COND(!spawn_queue.has(p_oid)); // Bug. - if (multiplayer->has_multiplayer_peer() && spawner->is_multiplayer_authority()) { - if (tobj.net_id == 0) { - tobj.net_id = ++last_net_id; + // If we are a nested spawn, we need to wait until the parent is ready. + if (p_oid != *(spawn_queue.begin())) { + return; + } + + for (const ObjectID &oid : spawn_queue) { + ERR_CONTINUE(!tracked_nodes.has(oid)); + + TrackedNode &tobj = tracked_nodes[oid]; + MultiplayerSpawner *spawner = get_id_as<MultiplayerSpawner>(tobj.spawner); + ERR_CONTINUE(!spawner); + + spawned_nodes.insert(oid); + if (multiplayer->has_multiplayer_peer() && spawner->is_multiplayer_authority()) { + if (tobj.net_id == 0) { + tobj.net_id = ++last_net_id; + } + _update_spawn_visibility(0, oid); } - _update_spawn_visibility(0, oid); } - return OK; + spawn_queue.clear(); } Error SceneReplicationInterface::on_despawn(Object *p_obj, Variant p_config) { diff --git a/modules/multiplayer/scene_replication_interface.h b/modules/multiplayer/scene_replication_interface.h index a5e610cff6..cf45db2138 100644 --- a/modules/multiplayer/scene_replication_interface.h +++ b/modules/multiplayer/scene_replication_interface.h @@ -51,7 +51,6 @@ private: bool operator==(const ObjectID &p_other) { return id == p_other; } - _FORCE_INLINE_ MultiplayerSpawner *get_spawner() const { return spawner.is_valid() ? Object::cast_to<MultiplayerSpawner>(ObjectDB::get_instance(spawner)) : nullptr; } TrackedNode() {} TrackedNode(const ObjectID &p_id) { id = p_id; } TrackedNode(const ObjectID &p_id, uint32_t p_net_id) { @@ -75,7 +74,10 @@ private: HashSet<ObjectID> spawned_nodes; HashSet<ObjectID> sync_nodes; - // Pending spawn information. + // Pending local spawn information (handles spawning nested nodes during ready). + HashSet<ObjectID> spawn_queue; + + // Pending remote spawn information. ObjectID pending_spawn; int pending_spawn_remote = 0; const uint8_t *pending_buffer = nullptr; @@ -89,6 +91,7 @@ private: TrackedNode &_track(const ObjectID &p_id); void _untrack(const ObjectID &p_id); + void _node_ready(const ObjectID &p_oid); void _send_sync(int p_peer, const HashSet<ObjectID> p_synchronizers, uint16_t p_sync_net_time, uint64_t p_msec); Error _make_spawn_packet(Node *p_node, MultiplayerSpawner *p_spawner, int &r_len); diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index ccd3480d11..87b599bc81 100644 --- a/platform/ios/export/export_plugin.cpp +++ b/platform/ios/export/export_plugin.cpp @@ -1572,7 +1572,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p int ret = unzGoToFirstFile(src_pkg_zip); Vector<uint8_t> project_file_data; while (ret == UNZ_OK) { -#if defined(MACOS_ENABLED) || defined(X11_ENABLED) +#if defined(MACOS_ENABLED) || defined(LINUXBSD_ENABLED) bool is_execute = false; #endif @@ -1605,7 +1605,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p continue; //ignore! } found_library = true; -#if defined(MACOS_ENABLED) || defined(X11_ENABLED) +#if defined(MACOS_ENABLED) || defined(LINUXBSD_ENABLED) is_execute = true; #endif file = file.replace(library_to_use, binary_name + ".xcframework"); @@ -1648,7 +1648,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p f->store_buffer(data.ptr(), data.size()); } -#if defined(MACOS_ENABLED) || defined(X11_ENABLED) +#if defined(MACOS_ENABLED) || defined(LINUXBSD_ENABLED) if (is_execute) { // we need execute rights on this file chmod(file.utf8().get_data(), 0755); diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py index 747dcbd76c..f4d36f1a87 100644 --- a/platform/linuxbsd/detect.py +++ b/platform/linuxbsd/detect.py @@ -338,12 +338,17 @@ def configure(env: "Environment"): env.Prepend(CPPPATH=["#platform/linuxbsd"]) + env.Append( + CPPDEFINES=[ + "LINUXBSD_ENABLED", + "UNIX_ENABLED", + ("_FILE_OFFSET_BITS", 64), + ] + ) + if env["x11"]: env.Append(CPPDEFINES=["X11_ENABLED"]) - env.Append(CPPDEFINES=["UNIX_ENABLED"]) - env.Append(CPPDEFINES=[("_FILE_OFFSET_BITS", 64)]) - if env["vulkan"]: env.Append(CPPDEFINES=["VULKAN_ENABLED"]) if not env["use_volk"]: diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index a2c34bb874..2cb00728c8 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -1432,28 +1432,23 @@ void DisplayServerX11::_update_window_mouse_passthrough(WindowID p_window) { int event_base, error_base; const Bool ext_okay = XShapeQueryExtension(x11_display, &event_base, &error_base); if (ext_okay) { - Region region; if (windows[p_window].mpass) { - region = XCreateRegion(); + Region region = XCreateRegion(); + XShapeCombineRegion(x11_display, windows[p_window].x11_window, ShapeInput, 0, 0, region, ShapeSet); + XDestroyRegion(region); } else if (region_path.size() == 0) { - region = XCreateRegion(); - XRectangle rect; - rect.x = 0; - rect.y = 0; - rect.width = window_get_size_with_decorations(p_window).x; - rect.height = window_get_size_with_decorations(p_window).y; - XUnionRectWithRegion(&rect, region, region); + XShapeCombineMask(x11_display, windows[p_window].x11_window, ShapeInput, 0, 0, None, ShapeSet); } else { XPoint *points = (XPoint *)memalloc(sizeof(XPoint) * region_path.size()); for (int i = 0; i < region_path.size(); i++) { points[i].x = region_path[i].x; points[i].y = region_path[i].y; } - region = XPolygonRegion(points, region_path.size(), EvenOddRule); + Region region = XPolygonRegion(points, region_path.size(), EvenOddRule); memfree(points); + XShapeCombineRegion(x11_display, windows[p_window].x11_window, ShapeInput, 0, 0, region, ShapeSet); + XDestroyRegion(region); } - XShapeCombineRegion(x11_display, windows[p_window].x11_window, ShapeInput, 0, 0, region, ShapeSet); - XDestroyRegion(region); } } diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index a6d63619df..6940bad4a6 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -763,7 +763,15 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa MeshesFound &mf = meshes_found.write[m_i]; - Size2i lightmap_size = mf.mesh->get_lightmap_size_hint() * mf.lightmap_scale; + Size2i lightmap_size = mf.mesh->get_lightmap_size_hint(); + + if (lightmap_size == Size2i(0, 0)) { + // TODO we should compute a size if no lightmap hint is set, as we did in 3.x. + // For now set to basic size to avoid crash. + lightmap_size = Size2i(64, 64); + } + + lightmap_size *= mf.lightmap_scale; TypedArray<RID> overrides; overrides.resize(mf.overrides.size()); for (int i = 0; i < mf.overrides.size(); i++) { diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index cafea83f16..f59702835c 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -222,6 +222,7 @@ void Label::_shape() { } } lines_dirty = false; + lines_shaped_last_width = get_size().width; } _update_visible(); @@ -596,7 +597,13 @@ void Label::_notification(int p_what) { } break; case NOTIFICATION_RESIZED: { - lines_dirty = true; + // It may happen that the reshaping due to this size change triggers a cascade of re-layout + // across the hierarchy where this label belongs to in a way that its size changes multiple + // times, but ending up with the original size it was already shaped for. + // This check prevents the catastrophic, freezing infinite cascade of re-layout. + if (lines_shaped_last_width != get_size().width) { + lines_dirty = true; + } } break; } } diff --git a/scene/gui/label.h b/scene/gui/label.h index 2350525236..b80646810b 100644 --- a/scene/gui/label.h +++ b/scene/gui/label.h @@ -49,6 +49,8 @@ private: bool uppercase = false; bool lines_dirty = true; + int lines_shaped_last_width = -1; + bool dirty = true; bool font_dirty = true; RID text_rid; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 9c7e04c433..ef58386b45 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1402,7 +1402,7 @@ Control *Viewport::gui_find_control(const Point2 &p_global) { xform = sw->get_canvas_transform(); } - Control *ret = _gui_find_control_at_pos(sw, p_global, xform, gui.focus_inv_xform); + Control *ret = _gui_find_control_at_pos(sw, p_global, xform); if (ret) { return ret; } @@ -1411,7 +1411,7 @@ Control *Viewport::gui_find_control(const Point2 &p_global) { return nullptr; } -Control *Viewport::_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_global, const Transform2D &p_xform, Transform2D &r_inv_xform) { +Control *Viewport::_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_global, const Transform2D &p_xform) { if (!p_node->is_visible()) { return nullptr; // Canvas item hidden, discard. } @@ -1431,7 +1431,7 @@ Control *Viewport::_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_ continue; } - Control *ret = _gui_find_control_at_pos(ci, p_global, matrix, r_inv_xform); + Control *ret = _gui_find_control_at_pos(ci, p_global, matrix); if (ret) { return ret; } @@ -1449,7 +1449,6 @@ Control *Viewport::_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_ Control *drag_preview = _gui_get_drag_preview(); if (!drag_preview || (c != drag_preview && !drag_preview->is_ancestor_of(c))) { - r_inv_xform = matrix; return c; } @@ -1496,12 +1495,11 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { Point2 mpos = mb->get_position(); if (mb->is_pressed()) { - Size2 pos = mpos; if (!gui.mouse_focus_mask.is_empty()) { // Do not steal mouse focus and stuff while a focus mask exists. gui.mouse_focus_mask.set_flag(mouse_button_to_mask(mb->get_button_index())); } else { - gui.mouse_focus = gui_find_control(pos); + gui.mouse_focus = gui_find_control(mpos); gui.last_mouse_focus = gui.mouse_focus; if (!gui.mouse_focus) { @@ -1520,10 +1518,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { mb = mb->xformed_by(Transform2D()); // Make a copy of the event. - mb->set_global_position(pos); - - pos = gui.focus_inv_xform.xform(pos); - + Point2 pos = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(mpos); mb->set_position(pos); #ifdef DEBUG_ENABLED @@ -1587,11 +1582,8 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { return; } - Size2 pos = mpos; - mb = mb->xformed_by(Transform2D()); // Make a copy. - mb->set_global_position(pos); - pos = gui.focus_inv_xform.xform(pos); + Point2 pos = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(mpos); mb->set_position(pos); Control *mouse_focus = gui.mouse_focus; @@ -1894,11 +1886,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { bool stopped = false; if (over->can_process()) { touch_event = touch_event->xformed_by(Transform2D()); // Make a copy. - if (over == gui.mouse_focus) { - pos = gui.focus_inv_xform.xform(pos); - } else { - pos = over->get_global_transform_with_canvas().affine_inverse().xform(pos); - } + pos = over->get_global_transform_with_canvas().affine_inverse().xform(pos); touch_event->set_position(pos); stopped = _gui_call_input(over, touch_event); } @@ -1913,11 +1901,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { Control *over = control_id.is_valid() ? Object::cast_to<Control>(ObjectDB::get_instance(control_id)) : nullptr; if (over && over->can_process()) { touch_event = touch_event->xformed_by(Transform2D()); // Make a copy. - if (over == gui.last_mouse_focus) { - pos = gui.focus_inv_xform.xform(pos); - } else { - pos = over->get_global_transform_with_canvas().affine_inverse().xform(pos); - } + pos = gui.last_mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(pos); touch_event->set_position(pos); stopped = _gui_call_input(over, touch_event); @@ -1943,11 +1927,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { bool stopped = false; if (over->can_process()) { gesture_event = gesture_event->xformed_by(Transform2D()); // Make a copy. - if (over == gui.mouse_focus) { - pos = gui.focus_inv_xform.xform(pos); - } else { - pos = over->get_global_transform_with_canvas().affine_inverse().xform(pos); - } + pos = over->get_global_transform_with_canvas().affine_inverse().xform(pos); gesture_event->set_position(pos); stopped = _gui_call_input(over, gesture_event); } @@ -2394,7 +2374,6 @@ void Viewport::_post_gui_grab_click_focus() { } gui.mouse_focus = focus_grabber; - gui.focus_inv_xform = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse(); click = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(gui.last_mouse_pos); for (int i = 0; i < 3; i++) { diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 9f182682d7..603e92b071 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -374,7 +374,6 @@ private: ObjectID drag_preview_id; Ref<SceneTreeTimer> tooltip_timer; double tooltip_delay = 0.0; - Transform2D focus_inv_xform; bool roots_order_dirty = false; List<Control *> roots; int canvas_sort_index = 0; //for sorting items with canvas as root @@ -403,7 +402,7 @@ private: void _gui_call_notification(Control *p_control, int p_what); void _gui_sort_roots(); - Control *_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_global, const Transform2D &p_xform, Transform2D &r_inv_xform); + Control *_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_global, const Transform2D &p_xform); void _gui_input_event(Ref<InputEvent> p_event); void _perform_drop(Control *p_control = nullptr, Point2 p_pos = Point2()); diff --git a/scene/resources/immediate_mesh.cpp b/scene/resources/immediate_mesh.cpp index 247927163b..f4a0db3930 100644 --- a/scene/resources/immediate_mesh.cpp +++ b/scene/resources/immediate_mesh.cpp @@ -194,25 +194,20 @@ void ImmediateMesh::surface_end() { if (uses_normals) { uint32_t *normal = (uint32_t *)&surface_vertex_ptr[i * vertex_stride + normal_offset]; - Vector3 n = normals[i] * Vector3(0.5, 0.5, 0.5) + Vector3(0.5, 0.5, 0.5); + Vector2 n = normals[i].octahedron_encode(); uint32_t value = 0; - value |= CLAMP(int(n.x * 1023.0), 0, 1023); - value |= CLAMP(int(n.y * 1023.0), 0, 1023) << 10; - value |= CLAMP(int(n.z * 1023.0), 0, 1023) << 20; + value |= (uint16_t)CLAMP(n.x * 65535, 0, 65535); + value |= (uint16_t)CLAMP(n.y * 65535, 0, 65535) << 16; *normal = value; } if (uses_tangents) { uint32_t *tangent = (uint32_t *)&surface_vertex_ptr[i * vertex_stride + tangent_offset]; - Plane t = tangents[i]; + Vector2 t = tangents[i].normal.octahedron_tangent_encode(tangents[i].d); uint32_t value = 0; - value |= CLAMP(int((t.normal.x * 0.5 + 0.5) * 1023.0), 0, 1023); - value |= CLAMP(int((t.normal.y * 0.5 + 0.5) * 1023.0), 0, 1023) << 10; - value |= CLAMP(int((t.normal.z * 0.5 + 0.5) * 1023.0), 0, 1023) << 20; - if (t.d > 0) { - value |= 3UL << 30; - } + value |= (uint16_t)CLAMP(t.x * 65535, 0, 65535); + value |= (uint16_t)CLAMP(t.y * 65535, 0, 65535) << 16; *tangent = value; } diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index 432d5a5b59..985bcb442e 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -6407,7 +6407,7 @@ VisualShaderNodeTexture2DParameter::VisualShaderNodeTexture2DParameter() { ////////////// Texture Parameter (Triplanar) String VisualShaderNodeTextureParameterTriplanar::get_caption() const { - return "TextureUniformTriplanar"; + return "TextureParameterTriplanar"; } int VisualShaderNodeTextureParameterTriplanar::get_input_port_count() const { diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp index 55f88d9bf1..df9dfaa276 100644 --- a/servers/rendering/renderer_canvas_cull.cpp +++ b/servers/rendering/renderer_canvas_cull.cpp @@ -769,6 +769,49 @@ void RendererCanvasCull::canvas_item_add_line(RID p_item, const Point2 &p_from, } } +static Vector2 compute_polyline_segment_dir(const Vector<Point2> &p_points, int p_index, const Vector2 &p_prev_segment_dir) { + int point_count = p_points.size(); + + bool is_last_point = (p_index == point_count - 1); + + Vector2 segment_dir; + + if (is_last_point) { + segment_dir = p_prev_segment_dir; + } else { + segment_dir = (p_points[p_index + 1] - p_points[p_index]).normalized(); + + if (segment_dir.is_zero_approx()) { + segment_dir = p_prev_segment_dir; + } + } + + return segment_dir; +} + +static Vector2 compute_polyline_edge_offset_clamped(const Vector2 &p_segment_dir, const Vector2 &p_prev_segment_dir) { + Vector2 bisector; + float length = 1.0f; + + bisector = (p_prev_segment_dir * p_segment_dir.length() - p_segment_dir * p_prev_segment_dir.length()).normalized(); + + float angle = atan2f(bisector.cross(p_prev_segment_dir), bisector.dot(p_prev_segment_dir)); + float sin_angle = sinf(angle); + + if (!Math::is_zero_approx(sin_angle) && !p_segment_dir.is_equal_approx(p_prev_segment_dir)) { + length = 1.0f / sin_angle; + length = CLAMP(length, -3.0f, 3.0f); + } else { + bisector = p_segment_dir.orthogonal(); + } + + if (bisector.is_zero_approx()) { + bisector = p_segment_dir.orthogonal(); + } + + return bisector * length; +} + void RendererCanvasCull::canvas_item_add_polyline(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width, bool p_antialiased) { ERR_FAIL_COND(p_points.size() < 2); Item *canvas_item = canvas_item_owner.get_or_null(p_item); @@ -777,11 +820,30 @@ void RendererCanvasCull::canvas_item_add_polyline(RID p_item, const Vector<Point Color color = Color(1, 1, 1, 1); Vector<int> indices; - int pc = p_points.size(); - int pc2 = pc * 2; + int point_count = p_points.size(); + int polyline_point_count = point_count * 2; + + bool loop = p_points[0].is_equal_approx(p_points[point_count - 1]); + Vector2 first_segment_dir; + Vector2 last_segment_dir; - Vector2 prev_t; - int j2; + // Search for first non-zero vector between two segments. + for (int i = 1; i < point_count; i++) { + first_segment_dir = (p_points[i] - p_points[i - 1]).normalized(); + + if (!first_segment_dir.is_zero_approx()) { + break; + } + } + + // Search for last non-zero vector between two segments. + for (int i = point_count - 1; i >= 1; i--) { + last_segment_dir = (p_points[i] - p_points[i - 1]).normalized(); + + if (!last_segment_dir.is_zero_approx()) { + break; + } + } Item::CommandPolygon *pline = canvas_item->alloc_command<Item::CommandPolygon>(); ERR_FAIL_COND(!pline); @@ -789,8 +851,8 @@ void RendererCanvasCull::canvas_item_add_polyline(RID p_item, const Vector<Point PackedColorArray colors; PackedVector2Array points; - colors.resize(pc2); - points.resize(pc2); + colors.resize(polyline_point_count); + points.resize(polyline_point_count); Vector2 *points_ptr = points.ptrw(); Color *colors_ptr = colors.ptrw(); @@ -845,14 +907,14 @@ void RendererCanvasCull::canvas_item_add_polyline(RID p_item, const Vector<Point PackedColorArray colors_left; PackedVector2Array points_left; - colors_left.resize(pc2); - points_left.resize(pc2); + colors_left.resize(polyline_point_count); + points_left.resize(polyline_point_count); PackedColorArray colors_right; PackedVector2Array points_right; - colors_right.resize(pc2); - points_right.resize(pc2); + colors_right.resize(polyline_point_count); + points_right.resize(polyline_point_count); Item::CommandPolygon *pline_begin = canvas_item->alloc_command<Item::CommandPolygon>(); ERR_FAIL_COND(!pline_begin); @@ -898,79 +960,81 @@ void RendererCanvasCull::canvas_item_add_polyline(RID p_item, const Vector<Point Color *colors_left_ptr = colors_left.ptrw(); Color *colors_right_ptr = colors_right.ptrw(); - for (int i = 0, j = 0; i < pc; i++, j += 2) { - bool is_begin = i == 0; - bool is_end = i == pc - 1; + Vector2 prev_segment_dir; + for (int i = 0; i < point_count; i++) { + bool is_first_point = (i == 0); + bool is_last_point = (i == point_count - 1); - Vector2 t; - Vector2 end_border; - Vector2 begin_border; - if (is_end) { - t = prev_t; - end_border = (p_points[i] - p_points[i - 1]).normalized() * border_size; - } else { - t = (p_points[i + 1] - p_points[i]).normalized().orthogonal(); - if (is_begin) { - prev_t = t; - begin_border = (p_points[i] - p_points[i + 1]).normalized() * border_size; - } + Vector2 segment_dir = compute_polyline_segment_dir(p_points, i, prev_segment_dir); + if (is_first_point && loop) { + prev_segment_dir = last_segment_dir; + } else if (is_last_point && loop) { + prev_segment_dir = first_segment_dir; } - j2 = j + 1; + Vector2 base_edge_offset; + if (is_first_point && !loop) { + base_edge_offset = first_segment_dir.orthogonal(); + } else if (is_last_point && !loop) { + base_edge_offset = last_segment_dir.orthogonal(); + } else { + base_edge_offset = compute_polyline_edge_offset_clamped(segment_dir, prev_segment_dir); + } - Vector2 dir = (t + prev_t).normalized(); - Vector2 tangent = dir * p_width * 0.5; - Vector2 border = dir * border_size; + Vector2 edge_offset = base_edge_offset * (p_width * 0.5f); + Vector2 border = base_edge_offset * border_size; Vector2 pos = p_points[i]; - points_ptr[j] = pos + tangent; - points_ptr[j2] = pos - tangent; + points_ptr[i * 2 + 0] = pos + edge_offset; + points_ptr[i * 2 + 1] = pos - edge_offset; - points_left_ptr[j] = pos + tangent + border; - points_left_ptr[j2] = pos + tangent; + points_left_ptr[i * 2 + 0] = pos + edge_offset + border; + points_left_ptr[i * 2 + 1] = pos + edge_offset; - points_right_ptr[j] = pos - tangent; - points_right_ptr[j2] = pos - tangent - border; + points_right_ptr[i * 2 + 0] = pos - edge_offset; + points_right_ptr[i * 2 + 1] = pos - edge_offset - border; if (i < p_colors.size()) { color = p_colors[i]; color2 = Color(color.r, color.g, color.b, 0); } - colors_ptr[j] = color; - colors_ptr[j2] = color; + colors_ptr[i * 2 + 0] = color; + colors_ptr[i * 2 + 1] = color; - colors_left_ptr[j] = color2; - colors_left_ptr[j2] = color; + colors_left_ptr[i * 2 + 0] = color2; + colors_left_ptr[i * 2 + 1] = color; - colors_right_ptr[j] = color; - colors_right_ptr[j2] = color2; + colors_right_ptr[i * 2 + 0] = color; + colors_right_ptr[i * 2 + 1] = color2; - if (is_begin) { - points_begin_ptr[0] = pos + tangent + begin_border; - points_begin_ptr[1] = pos - tangent + begin_border; - points_begin_ptr[2] = pos + tangent; - points_begin_ptr[3] = pos - tangent; + if (is_first_point) { + Vector2 begin_border = loop ? Vector2() : -segment_dir * border_size; + + points_begin_ptr[0] = pos + edge_offset + begin_border; + points_begin_ptr[1] = pos - edge_offset + begin_border; + points_begin_ptr[2] = pos + edge_offset; + points_begin_ptr[3] = pos - edge_offset; colors_begin_ptr[0] = color2; colors_begin_ptr[1] = color2; colors_begin_ptr[2] = color; colors_begin_ptr[3] = color; - points_begin_left_corner_ptr[0] = pos - tangent - border; - points_begin_left_corner_ptr[1] = pos - tangent + begin_border - border; - points_begin_left_corner_ptr[2] = pos - tangent; - points_begin_left_corner_ptr[3] = pos - tangent + begin_border; + points_begin_left_corner_ptr[0] = pos - edge_offset - border; + points_begin_left_corner_ptr[1] = pos - edge_offset + begin_border - border; + points_begin_left_corner_ptr[2] = pos - edge_offset; + points_begin_left_corner_ptr[3] = pos - edge_offset + begin_border; colors_begin_left_corner_ptr[0] = color2; colors_begin_left_corner_ptr[1] = color2; colors_begin_left_corner_ptr[2] = color; colors_begin_left_corner_ptr[3] = color2; - points_begin_right_corner_ptr[0] = pos + tangent + begin_border; - points_begin_right_corner_ptr[1] = pos + tangent + begin_border + border; - points_begin_right_corner_ptr[2] = pos + tangent; - points_begin_right_corner_ptr[3] = pos + tangent + border; + points_begin_right_corner_ptr[0] = pos + edge_offset + begin_border; + points_begin_right_corner_ptr[1] = pos + edge_offset + begin_border + border; + points_begin_right_corner_ptr[2] = pos + edge_offset; + points_begin_right_corner_ptr[3] = pos + edge_offset + border; colors_begin_right_corner_ptr[0] = color2; colors_begin_right_corner_ptr[1] = color2; @@ -978,31 +1042,33 @@ void RendererCanvasCull::canvas_item_add_polyline(RID p_item, const Vector<Point colors_begin_right_corner_ptr[3] = color2; } - if (is_end) { - points_end_ptr[0] = pos + tangent + end_border; - points_end_ptr[1] = pos - tangent + end_border; - points_end_ptr[2] = pos + tangent; - points_end_ptr[3] = pos - tangent; + if (is_last_point) { + Vector2 end_border = loop ? Vector2() : prev_segment_dir * border_size; + + points_end_ptr[0] = pos + edge_offset + end_border; + points_end_ptr[1] = pos - edge_offset + end_border; + points_end_ptr[2] = pos + edge_offset; + points_end_ptr[3] = pos - edge_offset; colors_end_ptr[0] = color2; colors_end_ptr[1] = color2; colors_end_ptr[2] = color; colors_end_ptr[3] = color; - points_end_left_corner_ptr[0] = pos - tangent - border; - points_end_left_corner_ptr[1] = pos - tangent + end_border - border; - points_end_left_corner_ptr[2] = pos - tangent; - points_end_left_corner_ptr[3] = pos - tangent + end_border; + points_end_left_corner_ptr[0] = pos - edge_offset - border; + points_end_left_corner_ptr[1] = pos - edge_offset + end_border - border; + points_end_left_corner_ptr[2] = pos - edge_offset; + points_end_left_corner_ptr[3] = pos - edge_offset + end_border; colors_end_left_corner_ptr[0] = color2; colors_end_left_corner_ptr[1] = color2; colors_end_left_corner_ptr[2] = color; colors_end_left_corner_ptr[3] = color2; - points_end_right_corner_ptr[0] = pos + tangent + end_border; - points_end_right_corner_ptr[1] = pos + tangent + end_border + border; - points_end_right_corner_ptr[2] = pos + tangent; - points_end_right_corner_ptr[3] = pos + tangent + border; + points_end_right_corner_ptr[0] = pos + edge_offset + end_border; + points_end_right_corner_ptr[1] = pos + edge_offset + end_border + border; + points_end_right_corner_ptr[2] = pos + edge_offset; + points_end_right_corner_ptr[3] = pos + edge_offset + border; colors_end_right_corner_ptr[0] = color2; colors_end_right_corner_ptr[1] = color2; @@ -1010,7 +1076,7 @@ void RendererCanvasCull::canvas_item_add_polyline(RID p_item, const Vector<Point colors_end_right_corner_ptr[3] = color2; } - prev_t = t; + prev_segment_dir = segment_dir; } pline_begin->primitive = RS::PRIMITIVE_TRIANGLE_STRIP; @@ -1039,33 +1105,41 @@ void RendererCanvasCull::canvas_item_add_polyline(RID p_item, const Vector<Point } else { // Makes a single triangle strip for drawing the line. - for (int i = 0, j = 0; i < pc; i++, j += 2) { - Vector2 t; - if (i == pc - 1) { - t = prev_t; - } else { - t = (p_points[i + 1] - p_points[i]).normalized().orthogonal(); - if (i == 0) { - prev_t = t; - } + Vector2 prev_segment_dir; + for (int i = 0; i < point_count; i++) { + bool is_first_point = (i == 0); + bool is_last_point = (i == point_count - 1); + + Vector2 segment_dir = compute_polyline_segment_dir(p_points, i, prev_segment_dir); + if (is_first_point && loop) { + prev_segment_dir = last_segment_dir; + } else if (is_last_point && loop) { + prev_segment_dir = first_segment_dir; } - j2 = j + 1; + Vector2 base_edge_offset; + if (is_first_point && !loop) { + base_edge_offset = first_segment_dir.orthogonal(); + } else if (is_last_point && !loop) { + base_edge_offset = last_segment_dir.orthogonal(); + } else { + base_edge_offset = compute_polyline_edge_offset_clamped(segment_dir, prev_segment_dir); + } - Vector2 tangent = ((t + prev_t).normalized()) * p_width * 0.5; + Vector2 edge_offset = base_edge_offset * (p_width * 0.5f); Vector2 pos = p_points[i]; - points_ptr[j] = pos + tangent; - points_ptr[j2] = pos - tangent; + points_ptr[i * 2 + 0] = pos + edge_offset; + points_ptr[i * 2 + 1] = pos - edge_offset; if (i < p_colors.size()) { color = p_colors[i]; } - colors_ptr[j] = color; - colors_ptr[j2] = color; + colors_ptr[i * 2 + 0] = color; + colors_ptr[i * 2 + 1] = color; - prev_t = t; + prev_segment_dir = segment_dir; } } diff --git a/servers/rendering/renderer_rd/environment/sky.cpp b/servers/rendering/renderer_rd/environment/sky.cpp index 863e8d6c15..7e02f98ce9 100644 --- a/servers/rendering/renderer_rd/environment/sky.cpp +++ b/servers/rendering/renderer_rd/environment/sky.cpp @@ -1085,8 +1085,8 @@ void SkyRD::setup_sky(RID p_env, Ref<RenderSceneBuffersRD> p_render_buffers, con sky->reflection.dirty = true; } + sky_scene_state.ubo.directional_light_count = 0; if (shader_data->uses_light) { - sky_scene_state.ubo.directional_light_count = 0; // Run through the list of lights in the scene and pick out the Directional Lights. // This can't be done in RenderSceneRenderRD::_setup lights because that needs to be called // after the depth prepass, but this runs before the depth prepass diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index 0c6dcb553a..07557eab81 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -525,10 +525,12 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend if (rect->flags & CANVAS_RECT_FLIP_H) { src_rect.size.x *= -1; + push_constant.flags |= FLAGS_FLIP_H; } if (rect->flags & CANVAS_RECT_FLIP_V) { src_rect.size.y *= -1; + push_constant.flags |= FLAGS_FLIP_V; } if (rect->flags & CANVAS_RECT_TRANSPOSE) { diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h index 266083fc49..7dea4a1a65 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h @@ -86,6 +86,9 @@ class RendererCanvasRenderRD : public RendererCanvasRender { FLAGS_USE_MSDF = (1 << 28), FLAGS_USE_LCD = (1 << 29), + + FLAGS_FLIP_H = (1 << 30), + FLAGS_FLIP_V = (1 << 31), }; enum { diff --git a/servers/rendering/renderer_rd/shaders/canvas.glsl b/servers/rendering/renderer_rd/shaders/canvas.glsl index eb5f68849e..1fb8b28b15 100644 --- a/servers/rendering/renderer_rd/shaders/canvas.glsl +++ b/servers/rendering/renderer_rd/shaders/canvas.glsl @@ -502,6 +502,12 @@ void main() { if (normal_used || (using_light && bool(draw_data.flags & FLAGS_DEFAULT_NORMAL_MAP_USED))) { normal.xy = texture(sampler2D(normal_texture, texture_sampler), uv).xy * vec2(2.0, -2.0) - vec2(1.0, -1.0); + if (bool(draw_data.flags & FLAGS_FLIP_H)) { + normal.x = -normal.x; + } + if (bool(draw_data.flags & FLAGS_FLIP_V)) { + normal.y = -normal.y; + } normal.z = sqrt(1.0 - dot(normal.xy, normal.xy)); normal_used = true; } else { diff --git a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl index 1b627a3e81..fe4bf4bed0 100644 --- a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl @@ -27,6 +27,9 @@ #define FLAGS_USE_MSDF (1 << 28) #define FLAGS_USE_LCD (1 << 29) +#define FLAGS_FLIP_H (1 << 30) +#define FLAGS_FLIP_V (1 << 31) + #define SAMPLER_NEAREST_CLAMP 0 #define SAMPLER_LINEAR_CLAMP 1 #define SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP 2 diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp index 6401d0f5d0..e4149f6bbd 100644 --- a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp @@ -1415,7 +1415,6 @@ void ParticlesStorage::update_particles() { } bool zero_time_scale = Engine::get_singleton()->get_time_scale() <= 0.0; - bool updated = false; if (particles->clear && particles->pre_process_time > 0.0) { double frame_time; @@ -1430,7 +1429,6 @@ void ParticlesStorage::update_particles() { while (todo >= 0) { _particles_process(particles, frame_time); todo -= frame_time; - updated = true; } } @@ -1452,10 +1450,9 @@ void ParticlesStorage::update_particles() { } double todo = particles->frame_remainder + delta; - while (todo >= frame_time || (particles->clear && !updated)) { + while (todo >= frame_time || particles->clear) { _particles_process(particles, frame_time); todo -= decr; - updated = true; } particles->frame_remainder = todo; @@ -1463,16 +1460,16 @@ void ParticlesStorage::update_particles() { } else { if (zero_time_scale) { _particles_process(particles, 0.0); - updated = true; } else { _particles_process(particles, RendererCompositorRD::singleton->get_frame_delta_time()); - updated = true; } } - //copy particles to instance buffer + // Ensure that memory is initialized (the code above should ensure that _particles_process is always called at least once upon clearing). + DEV_ASSERT(!particles->clear); - if (updated && particles->draw_order != RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY) { + // Copy particles to instance buffer. + if (particles->draw_order != RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY) { //does not need view dependent operation, do copy here ParticlesShader::CopyPushConstant copy_push_constant; diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index b82019dd9d..3886f5b379 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -1138,6 +1138,7 @@ void RendererViewport::viewport_set_screen_space_aa(RID p_viewport, RS::Viewport void RendererViewport::viewport_set_use_taa(RID p_viewport, bool p_use_taa) { Viewport *viewport = viewport_owner.get_or_null(p_viewport); ERR_FAIL_COND(!viewport); + ERR_FAIL_COND_EDMSG(OS::get_singleton()->get_current_rendering_method() != "forward_plus", "TAA is only available when using the Forward+ renderer."); if (viewport->use_taa == p_use_taa) { return; |