diff options
31 files changed, 317 insertions, 171 deletions
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/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/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/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/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index c0d88553ad..03cbfda1bd 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -1864,12 +1864,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte p_output.append("\n" OPEN_BLOCK_L1); if (getter) { - p_output.append(INDENT2 "get\n" - - // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that) - "#pragma warning disable CS0618 // Disable warning about obsolete method\n" - - OPEN_BLOCK_L2 INDENT3); + p_output.append(INDENT2 "get\n" OPEN_BLOCK_L2 INDENT3); p_output.append("return "); p_output.append(getter->proxy_name + "("); @@ -1884,21 +1879,11 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte p_output.append(itos(p_iprop.index)); } } - p_output.append(");\n" - - CLOSE_BLOCK_L2 - - // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that) - "#pragma warning restore CS0618\n"); + p_output.append(");\n" CLOSE_BLOCK_L2); } if (setter) { - p_output.append(INDENT2 "set\n" - - // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that) - "#pragma warning disable CS0618 // Disable warning about obsolete method\n" - - OPEN_BLOCK_L2 INDENT3); + p_output.append(INDENT2 "set\n" OPEN_BLOCK_L2 INDENT3); p_output.append(setter->proxy_name + "("); if (p_iprop.index != -1) { @@ -1912,12 +1897,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte p_output.append(itos(p_iprop.index) + ", "); } } - p_output.append("value);\n" - - CLOSE_BLOCK_L2 - - // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that) - "#pragma warning restore CS0618\n"); + p_output.append("value);\n" CLOSE_BLOCK_L2); } p_output.append(CLOSE_BLOCK_L1); @@ -3056,12 +3036,10 @@ bool BindingsGenerator::_populate_object_type_interfaces() { HashMap<StringName, StringName>::Iterator accessor = accessor_methods.find(imethod.cname); if (accessor) { - const PropertyInterface *accessor_property = itype.find_property_by_name(accessor->value); - - // We only deprecate an accessor method if it's in the same class as the property. It's easier this way, but also - // we don't know if an accessor method in a different class could have other purposes, so better leave those untouched. - imethod.is_deprecated = true; - imethod.deprecation_message = imethod.proxy_name + " is deprecated. Use the " + accessor_property->proxy_name + " property instead."; + // We only make internal an accessor method if it's in the same class as the property. + // It's easier this way, but also we don't know if an accessor method in a different class + // could have other purposes, so better leave those untouched. + imethod.is_internal = true; } if (itype.class_doc) { 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/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/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/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_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; |