diff options
Diffstat (limited to 'editor')
-rw-r--r-- | editor/animation_bezier_editor.cpp | 956 | ||||
-rw-r--r-- | editor/animation_bezier_editor.h | 44 | ||||
-rw-r--r-- | editor/animation_track_editor.cpp | 73 | ||||
-rw-r--r-- | editor/animation_track_editor.h | 5 | ||||
-rw-r--r-- | editor/debugger/editor_network_profiler.cpp | 22 | ||||
-rw-r--r-- | editor/debugger/editor_network_profiler.h | 6 | ||||
-rw-r--r-- | editor/debugger/script_editor_debugger.cpp | 48 | ||||
-rw-r--r-- | editor/editor_about.cpp | 3 | ||||
-rw-r--r-- | editor/editor_node.cpp | 1 | ||||
-rw-r--r-- | editor/plugins/node_3d_editor_plugin.cpp | 113 | ||||
-rw-r--r-- | editor/plugins/node_3d_editor_plugin.h | 79 | ||||
-rw-r--r-- | editor/project_manager.cpp | 1 | ||||
-rw-r--r-- | editor/scene_tree_dock.cpp | 2 |
13 files changed, 874 insertions, 479 deletions
diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp index da376c588e..67cdba043a 100644 --- a/editor/animation_bezier_editor.cpp +++ b/editor/animation_bezier_editor.cpp @@ -35,6 +35,8 @@ #include "scene/gui/view_panner.h" #include "scene/resources/text_line.h" +#include <limits.h> + float AnimationBezierTrackEdit::_bezier_h_to_pixel(float p_h) { float h = p_h; h = (h - v_scroll) / v_zoom; @@ -55,15 +57,16 @@ static _FORCE_INLINE_ Vector2 _bezier_interp(real_t t, const Vector2 &start, con void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) { float scale = timeline->get_zoom_scale(); + int limit = timeline->get_name_limit(); - int right_limit = get_size().width - timeline->get_buttons_width(); + int right_limit = get_size().width; //selection may have altered the order of keys Map<float, int> key_order; for (int i = 0; i < animation->track_get_key_count(p_track); i++) { float ofs = animation->track_get_key_time(p_track, i); - if (moving_selection && track == p_track && selection.has(i)) { + if (moving_selection && selection.has(IntPair(p_track, i))) { ofs += moving_selection_offset.x; } @@ -82,11 +85,11 @@ void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) { float offset = animation->track_get_key_time(p_track, i); float height = animation->bezier_track_get_key_value(p_track, i); Vector2 out_handle = animation->bezier_track_get_key_out_handle(p_track, i); - if (track == p_track && moving_handle != 0 && moving_handle_key == i) { + if (p_track == moving_handle_track && moving_handle != 0 && moving_handle_key == i) { out_handle = moving_handle_right; } - if (moving_selection && track == p_track && selection.has(i)) { + if (moving_selection && selection.has(IntPair(p_track, i))) { offset += moving_selection_offset.x; height += moving_selection_offset.y; } @@ -96,11 +99,11 @@ void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) { float offset_n = animation->track_get_key_time(p_track, i_n); float height_n = animation->bezier_track_get_key_value(p_track, i_n); Vector2 in_handle = animation->bezier_track_get_key_in_handle(p_track, i_n); - if (track == p_track && moving_handle != 0 && moving_handle_key == i_n) { + if (p_track == moving_handle_track && moving_handle != 0 && moving_handle_key == i_n) { in_handle = moving_handle_left; } - if (moving_selection && track == p_track && selection.has(i_n)) { + if (moving_selection && selection.has(IntPair(p_track, i_n))) { offset_n += moving_selection_offset.x; height_n += moving_selection_offset.y; } @@ -221,20 +224,10 @@ void AnimationBezierTrackEdit::_notification(int p_what) { panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/animation_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EditorSettings::get_singleton()->get("editors/panning/simple_panning"))); } if (p_what == NOTIFICATION_THEME_CHANGED || p_what == NOTIFICATION_ENTER_TREE) { - close_button->set_icon(get_theme_icon(SNAME("Close"), SNAME("EditorIcons"))); - bezier_icon = get_theme_icon(SNAME("KeyBezierPoint"), SNAME("EditorIcons")); bezier_handle_icon = get_theme_icon(SNAME("KeyBezierHandle"), SNAME("EditorIcons")); selected_icon = get_theme_icon(SNAME("KeyBezierSelected"), SNAME("EditorIcons")); } - if (p_what == NOTIFICATION_RESIZED) { - int right_limit = get_size().width - timeline->get_buttons_width(); - int hsep = get_theme_constant(SNAME("hseparation"), SNAME("ItemList")); - int vsep = get_theme_constant(SNAME("vseparation"), SNAME("ItemList")); - - right_column->set_position(Vector2(right_limit + hsep, vsep)); - right_column->set_size(Vector2(timeline->get_buttons_width() - hsep * 2, get_size().y - vsep * 2)); - } if (p_what == NOTIFICATION_DRAW) { if (animation.is_null()) { return; @@ -258,101 +251,191 @@ void AnimationBezierTrackEdit::_notification(int p_what) { draw_line(Point2(limit, 0), Point2(limit, get_size().height), linecolor, Math::round(EDSCALE)); - int right_limit = get_size().width - timeline->get_buttons_width(); + int right_limit = get_size().width; - draw_line(Point2(right_limit, 0), Point2(right_limit, get_size().height), linecolor, Math::round(EDSCALE)); - - String base_path = animation->track_get_path(track); - int end = base_path.find(":"); - if (end != -1) { - base_path = base_path.substr(0, end + 1); - } - - // NAMES AND ICON int vofs = vsep; int margin = 0; - { - NodePath path = animation->track_get_path(track); + Map<int, Color> subtrack_colors; + Color selected_track_color; + subtracks.clear(); + subtrack_icons.clear(); + + Map<String, Vector<int>> track_indices; + int track_count = animation->get_track_count(); + for (int i = 0; i < track_count; ++i) { + if (animation->track_get_type(i) != Animation::TrackType::TYPE_BEZIER) { + continue; + } - Node *node = nullptr; + String base_path = animation->track_get_path(i); + if (is_filtered) { + if (root && root->has_node(base_path)) { + Node *node = root->get_node(base_path); + if (!node) { + continue; // No node, no filter. + } + if (!EditorNode::get_singleton()->get_editor_selection()->is_selected(node)) { + continue; // Skip track due to not selected. + } + } + } - if (root && root->has_node(path)) { - node = root->get_node(path); + int end = base_path.find(":"); + if (end != -1) { + base_path = base_path.substr(0, end + 1); } + Vector<int> indices = track_indices.has(base_path) ? track_indices[base_path] : Vector<int>(); + indices.push_back(i); + track_indices[base_path] = indices; + } - String text; + for (const KeyValue<String, Vector<int>> &E : track_indices) { + String base_path = E.key; - if (node) { - int ofs = 0; + Vector<int> tracks = E.value; - Ref<Texture2D> icon = EditorNode::get_singleton()->get_object_icon(node, "Node"); + // NAMES AND ICON + { + NodePath path = animation->track_get_path(tracks[0]); - text = node->get_name(); - ofs += hsep; - ofs += icon->get_width(); + Node *node = nullptr; - TextLine text_buf = TextLine(text, font, font_size); - text_buf.set_width(limit - ofs - hsep); + if (root && root->has_node(path)) { + node = root->get_node(path); + } - int h = MAX(text_buf.get_size().y, icon->get_height()); + String text; - draw_texture(icon, Point2(ofs, vofs + int(h - icon->get_height()) / 2)); + if (node) { + int ofs = 0; - margin = icon->get_width(); + Ref<Texture2D> icon = EditorNode::get_singleton()->get_object_icon(node, "Node"); - Vector2 string_pos = Point2(ofs, vofs + (h - text_buf.get_size().y) / 2 + text_buf.get_line_ascent()); - string_pos = string_pos.floor(); - text_buf.draw(get_canvas_item(), string_pos, color); + text = node->get_name(); + ofs += hsep; - vofs += h + vsep; - } - } + TextLine text_buf = TextLine(text, font, font_size); + text_buf.set_width(limit - ofs - icon->get_width() - hsep); - // RELATED TRACKS TITLES + int h = MAX(text_buf.get_size().y, icon->get_height()); - Map<int, Color> subtrack_colors; - subtracks.clear(); + draw_texture(icon, Point2(ofs, vofs + int(h - icon->get_height()) / 2)); + ofs += icon->get_width(); - for (int i = 0; i < animation->get_track_count(); i++) { - if (animation->track_get_type(i) != Animation::TYPE_BEZIER) { - continue; - } - String path = animation->track_get_path(i); - if (!path.begins_with(base_path)) { - continue; //another node + margin = icon->get_width(); + + Vector2 string_pos = Point2(ofs, vofs); + string_pos = string_pos.floor(); + text_buf.draw(get_canvas_item(), string_pos, color); + + vofs += h + vsep; + } } - path = path.replace_first(base_path, ""); - Color cc = color; - TextLine text_buf = TextLine(path, font, font_size); - text_buf.set_width(limit - margin - hsep); + Ref<Texture2D> remove = get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")); + float remove_hpos = limit - hsep - remove->get_width(); + + Ref<Texture2D> lock = get_theme_icon(SNAME("Lock"), SNAME("EditorIcons")); + Ref<Texture2D> unlock = get_theme_icon(SNAME("Unlock"), SNAME("EditorIcons")); + float lock_hpos = remove_hpos - hsep - lock->get_width(); + + Ref<Texture2D> visible = get_theme_icon(SNAME("GuiVisibilityVisible"), SNAME("EditorIcons")); + Ref<Texture2D> hidden = get_theme_icon(SNAME("GuiVisibilityHidden"), SNAME("EditorIcons")); + float visibility_hpos = lock_hpos - hsep - visible->get_width(); + + Ref<Texture2D> solo = get_theme_icon(SNAME("AudioBusSolo"), SNAME("EditorIcons")); + float solo_hpos = visibility_hpos - hsep - solo->get_width(); + + float buttons_width = remove->get_width() + lock->get_width() + visible->get_width() + solo->get_width() + hsep * 3; + + for (int i = 0; i < tracks.size(); ++i) { + // RELATED TRACKS TITLES + + int current_track = tracks[i]; + + String path = animation->track_get_path(current_track); + path = path.replace_first(base_path, ""); + + Color cc = color; + TextLine text_buf = TextLine(path, font, font_size); + text_buf.set_width(limit - margin - buttons_width); + + Rect2 rect = Rect2(margin, vofs, solo_hpos - hsep - solo->get_width(), text_buf.get_size().y + vsep); - Rect2 rect = Rect2(margin, vofs, limit - margin - hsep, text_buf.get_size().y + vsep); - if (i != track) { cc.a *= 0.7; - uint32_t hash = path.hash(); - hash = ((hash >> 16) ^ hash) * 0x45d9f3b; - hash = ((hash >> 16) ^ hash) * 0x45d9f3b; - hash = (hash >> 16) ^ hash; - float h = (hash % 65535) / 65536.0; - Color subcolor; - subcolor.set_hsv(h, 0.2, 0.8); - subcolor.a = 0.5; - draw_rect(Rect2(0, vofs + text_buf.get_size().y * 0.1, margin - hsep, text_buf.get_size().y * 0.8), subcolor); - subtrack_colors[i] = subcolor; - - subtracks[i] = rect; - } else { - Color ac = get_theme_color(SNAME("accent_color"), SNAME("Editor")); - ac.a = 0.5; - draw_rect(rect, ac); - } + float h; + if (path.ends_with(":x")) { + h = 0; + } else if (path.ends_with(":y")) { + h = 0.33f; + } else if (path.ends_with(":z")) { + h = 0.66f; + } else { + uint32_t hash = path.hash(); + hash = ((hash >> 16) ^ hash) * 0x45d9f3b; + hash = ((hash >> 16) ^ hash) * 0x45d9f3b; + hash = (hash >> 16) ^ hash; + h = (hash % 65535) / 65536.0; + } + + if (current_track != selected_track) { + Color track_color; + if (locked_tracks.has(current_track)) { + track_color.set_hsv(h, 0, 0.4); + } else { + track_color.set_hsv(h, 0.2, 0.8); + } + track_color.a = 0.5; + draw_rect(Rect2(0, vofs, margin - hsep, text_buf.get_size().y * 0.8), track_color); + subtrack_colors[current_track] = track_color; + + subtracks[current_track] = rect; + } else { + Color ac = get_theme_color(SNAME("accent_color"), SNAME("Editor")); + ac.a = 0.5; + draw_rect(rect, ac); + if (locked_tracks.has(selected_track)) { + selected_track_color.set_hsv(h, 0.0, 0.4); + } else { + selected_track_color.set_hsv(h, 0.8, 0.8); + } + } + + Vector2 string_pos = Point2(margin, vofs); + text_buf.draw(get_canvas_item(), string_pos, cc); + + float icon_start_height = vofs + rect.size.y / 2; + Rect2 remove_rect = Rect2(remove_hpos, icon_start_height - remove->get_height() / 2, remove->get_width(), remove->get_height()); + draw_texture(remove, remove_rect.position); + + Rect2 lock_rect = Rect2(lock_hpos, icon_start_height - lock->get_height() / 2, lock->get_width(), lock->get_height()); + if (locked_tracks.has(current_track)) { + draw_texture(lock, lock_rect.position); + } else { + draw_texture(unlock, lock_rect.position); + } + + Rect2 visible_rect = Rect2(visibility_hpos, icon_start_height - visible->get_height() / 2, visible->get_width(), visible->get_height()); + if (hidden_tracks.has(current_track)) { + draw_texture(hidden, visible_rect.position); + } else { + draw_texture(visible, visible_rect.position); + } - Vector2 string_pos = Point2(margin, vofs + text_buf.get_line_ascent()); - text_buf.draw(get_canvas_item(), string_pos, cc); + Rect2 solo_rect = Rect2(solo_hpos, icon_start_height - solo->get_height() / 2, solo->get_width(), solo->get_height()); + draw_texture(solo, solo_rect.position); - vofs += text_buf.get_size().y + vsep; + Map<int, Rect2> track_icons; + track_icons[REMOVE_ICON] = remove_rect; + track_icons[LOCK_ICON] = lock_rect; + track_icons[VISIBILITY_ICON] = visible_rect; + track_icons[SOLO_ICON] = solo_rect; + + subtrack_icons[current_track] = track_icons; + + vofs += text_buf.get_size().y + vsep; + } } Color accent = get_theme_color(SNAME("accent_color"), SNAME("Editor")); @@ -398,6 +481,9 @@ void AnimationBezierTrackEdit::_notification(int p_what) { float scale = timeline->get_zoom_scale(); Ref<Texture2D> point = get_theme_icon(SNAME("KeyValue"), SNAME("EditorIcons")); for (const KeyValue<int, Color> &E : subtrack_colors) { + if (hidden_tracks.has(E.key)) { + continue; + } _draw_track(E.key, E.value); for (int i = 0; i < animation->track_get_key_count(E.key); i++) { @@ -412,70 +498,116 @@ void AnimationBezierTrackEdit::_notification(int p_what) { } } - //draw edited curve - const Color highlight = get_theme_color(SNAME("highlight_color"), SNAME("Editor")); - _draw_track(track, highlight); + if (track_count > 0 && !hidden_tracks.has(selected_track)) { + //draw edited curve + _draw_track(selected_track, selected_track_color); + } } //draw editor handles { edit_points.clear(); - float scale = timeline->get_zoom_scale(); - for (int i = 0; i < animation->track_get_key_count(track); i++) { - float offset = animation->track_get_key_time(track, i); - float value = animation->bezier_track_get_key_value(track, i); - if (moving_selection && selection.has(i)) { - offset += moving_selection_offset.x; - value += moving_selection_offset.y; + for (int i = 0; i < track_count; ++i) { + if (animation->track_get_type(i) != Animation::TrackType::TYPE_BEZIER || hidden_tracks.has(i)) { + continue; } - Vector2 pos((offset - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value)); - - Vector2 in_vec = animation->bezier_track_get_key_in_handle(track, i); - if (moving_handle != 0 && moving_handle_key == i) { - in_vec = moving_handle_left; + if (hidden_tracks.has(i) || locked_tracks.has(i)) { + continue; } - Vector2 pos_in(((offset + in_vec.x) - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value + in_vec.y)); - Vector2 out_vec = animation->bezier_track_get_key_out_handle(track, i); + int key_count = animation->track_get_key_count(i); + String path = animation->track_get_path(i); - if (moving_handle != 0 && moving_handle_key == i) { - out_vec = moving_handle_right; + if (is_filtered) { + if (root && root->has_node(path)) { + Node *node = root->get_node(path); + if (!node) { + continue; // No node, no filter. + } + if (!EditorNode::get_singleton()->get_editor_selection()->is_selected(node)) { + continue; // Skip track due to not selected. + } + } } - Vector2 pos_out(((offset + out_vec.x) - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value + out_vec.y)); + for (int j = 0; j < key_count; ++j) { + float offset = animation->track_get_key_time(i, j); + float value = animation->bezier_track_get_key_value(i, j); + + if (moving_selection && selection.has(IntPair(i, j))) { + offset += moving_selection_offset.x; + value += moving_selection_offset.y; + } - _draw_line_clipped(pos, pos_in, accent, limit, right_limit); - _draw_line_clipped(pos, pos_out, accent, limit, right_limit); + Vector2 pos((offset - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value)); - EditPoint ep; - if (pos.x >= limit && pos.x <= right_limit) { - ep.point_rect.position = (pos - bezier_icon->get_size() / 2).floor(); - ep.point_rect.size = bezier_icon->get_size(); - if (selection.has(i)) { - draw_texture(selected_icon, ep.point_rect.position); - draw_string(font, ep.point_rect.position + Vector2(8, -font->get_height(font_size) - 8), TTR("Time:") + " " + TS->format_number(rtos(Math::snapped(offset, 0.001))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, accent); - draw_string(font, ep.point_rect.position + Vector2(8, -8), TTR("Value:") + " " + TS->format_number(rtos(Math::snapped(value, 0.001))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, accent); - } else { - draw_texture(bezier_icon, ep.point_rect.position); + Vector2 in_vec = animation->bezier_track_get_key_in_handle(i, j); + if (moving_handle != 0 && moving_handle_track == i && moving_handle_key == j) { + in_vec = moving_handle_left; + } + Vector2 pos_in(((offset + in_vec.x) - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value + in_vec.y)); + + Vector2 out_vec = animation->bezier_track_get_key_out_handle(i, j); + + if (moving_handle != 0 && moving_handle_track == i && moving_handle_key == j) { + out_vec = moving_handle_right; + } + + Vector2 pos_out(((offset + out_vec.x) - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value + out_vec.y)); + + if (i == selected_track || selection.has(IntPair(i, j))) { + _draw_line_clipped(pos, pos_in, accent, limit, right_limit); + _draw_line_clipped(pos, pos_out, accent, limit, right_limit); + } + + EditPoint ep; + ep.track = i; + ep.key = j; + if (pos.x >= limit && pos.x <= right_limit) { + ep.point_rect.position = (pos - bezier_icon->get_size() / 2).floor(); + ep.point_rect.size = bezier_icon->get_size(); + if (selection.has(IntPair(i, j))) { + draw_texture(selected_icon, ep.point_rect.position); + draw_string(font, ep.point_rect.position + Vector2(8, -font->get_height(font_size) - 8), TTR("Time:") + " " + TS->format_number(rtos(Math::snapped(offset, 0.001))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, accent); + draw_string(font, ep.point_rect.position + Vector2(8, -8), TTR("Value:") + " " + TS->format_number(rtos(Math::snapped(value, 0.001))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, accent); + } else { + Color track_color = Color(1, 1, 1, 1); + if (i != selected_track) { + track_color = subtrack_colors[i]; + } + draw_texture(bezier_icon, ep.point_rect.position, track_color); + } + ep.point_rect = ep.point_rect.grow(ep.point_rect.size.width * 0.5); + } + if (i == selected_track || selection.has(IntPair(i, j))) { + if (pos_in.x >= limit && pos_in.x <= right_limit) { + ep.in_rect.position = (pos_in - bezier_handle_icon->get_size() / 2).floor(); + ep.in_rect.size = bezier_handle_icon->get_size(); + draw_texture(bezier_handle_icon, ep.in_rect.position); + ep.in_rect = ep.in_rect.grow(ep.in_rect.size.width * 0.5); + } + if (pos_out.x >= limit && pos_out.x <= right_limit) { + ep.out_rect.position = (pos_out - bezier_handle_icon->get_size() / 2).floor(); + ep.out_rect.size = bezier_handle_icon->get_size(); + draw_texture(bezier_handle_icon, ep.out_rect.position); + ep.out_rect = ep.out_rect.grow(ep.out_rect.size.width * 0.5); + } + } + if (!locked_tracks.has(i)) { + edit_points.push_back(ep); } - ep.point_rect = ep.point_rect.grow(ep.point_rect.size.width * 0.5); - } - if (pos_in.x >= limit && pos_in.x <= right_limit) { - ep.in_rect.position = (pos_in - bezier_handle_icon->get_size() / 2).floor(); - ep.in_rect.size = bezier_handle_icon->get_size(); - draw_texture(bezier_handle_icon, ep.in_rect.position); - ep.in_rect = ep.in_rect.grow(ep.in_rect.size.width * 0.5); } - if (pos_out.x >= limit && pos_out.x <= right_limit) { - ep.out_rect.position = (pos_out - bezier_handle_icon->get_size() / 2).floor(); - ep.out_rect.size = bezier_handle_icon->get_size(); - draw_texture(bezier_handle_icon, ep.out_rect.position); - ep.out_rect = ep.out_rect.grow(ep.out_rect.size.width * 0.5); + } + + for (int i = 0; i < edit_points.size(); ++i) { + if (edit_points[i].track == selected_track) { + EditPoint ep = edit_points[i]; + edit_points.remove_at(i); + edit_points.insert(0, ep); } - edit_points.push_back(ep); } } @@ -506,15 +638,7 @@ Ref<Animation> AnimationBezierTrackEdit::get_animation() const { void AnimationBezierTrackEdit::set_animation_and_track(const Ref<Animation> &p_animation, int p_track) { animation = p_animation; - track = p_track; - if (is_connected("select_key", Callable(editor, "_key_selected"))) { - disconnect("select_key", Callable(editor, "_key_selected")); - } - if (is_connected("deselect_key", Callable(editor, "_key_deselected"))) { - disconnect("deselect_key", Callable(editor, "_key_deselected")); - } - connect("select_key", Callable(editor, "_key_selected"), varray(p_track), CONNECT_DEFERRED); - connect("deselect_key", Callable(editor, "_key_deselected"), varray(p_track), CONNECT_DEFERRED); + selected_track = p_track; update(); } @@ -529,11 +653,14 @@ void AnimationBezierTrackEdit::set_undo_redo(UndoRedo *p_undo_redo) { void AnimationBezierTrackEdit::set_timeline(AnimationTimelineEdit *p_timeline) { timeline = p_timeline; timeline->connect("zoom_changed", callable_mp(this, &AnimationBezierTrackEdit::_zoom_changed)); + timeline->connect("name_limit_changed", callable_mp(this, &AnimationBezierTrackEdit::_zoom_changed)); } void AnimationBezierTrackEdit::set_editor(AnimationTrackEditor *p_editor) { editor = p_editor; connect("clear_selection", Callable(editor, "_clear_selection"), varray(false)); + connect("select_key", Callable(editor, "_key_selected"), varray(), CONNECT_DEFERRED); + connect("deselect_key", Callable(editor, "_key_deselected"), varray(), CONNECT_DEFERRED); } void AnimationBezierTrackEdit::_play_position_draw() { @@ -544,9 +671,11 @@ void AnimationBezierTrackEdit::_play_position_draw() { float scale = timeline->get_zoom_scale(); int h = get_size().height; - int px = (-timeline->get_value() + play_position_pos) * scale + timeline->get_name_limit(); + int limit = timeline->get_name_limit(); + + int px = (-timeline->get_value() + play_position_pos) * scale + limit; - if (px >= timeline->get_name_limit() && px < (get_size().width - timeline->get_buttons_width())) { + if (px >= limit && px < (get_size().width)) { Color color = get_theme_color(SNAME("accent_color"), SNAME("Editor")); play_position->draw_line(Point2(px, 0), Point2(px, h), color, Math::round(2 * EDSCALE)); } @@ -565,11 +694,84 @@ void AnimationBezierTrackEdit::set_root(Node *p_root) { root = p_root; } +void AnimationBezierTrackEdit::set_filtered(bool p_filtered) { + is_filtered = p_filtered; + if (animation == nullptr) { + return; + } + String base_path = animation->track_get_path(selected_track); + if (is_filtered) { + if (root && root->has_node(base_path)) { + Node *node = root->get_node(base_path); + if (!node || !EditorNode::get_singleton()->get_editor_selection()->is_selected(node)) { + for (int i = 0; i < animation->get_track_count(); ++i) { + if (animation->track_get_type(i) != Animation::TrackType::TYPE_BEZIER) { + continue; + } + + base_path = animation->track_get_path(i); + if (root && root->has_node(base_path)) { + node = root->get_node(base_path); + if (!node) { + continue; // No node, no filter. + } + if (!EditorNode::get_singleton()->get_editor_selection()->is_selected(node)) { + continue; // Skip track due to not selected. + } + + set_animation_and_track(animation, i); + break; + } + } + } + } + } + update(); +} + void AnimationBezierTrackEdit::_zoom_changed() { update(); play_position->update(); } +void AnimationBezierTrackEdit::_update_locked_tracks_after(int p_track) { + if (locked_tracks.has(p_track)) { + locked_tracks.erase(p_track); + } + + Vector<int> updated_locked_tracks; + for (Set<int>::Element *E = locked_tracks.front(); E; E = E->next()) { + updated_locked_tracks.push_back(E->get()); + } + locked_tracks.clear(); + for (int i = 0; i < updated_locked_tracks.size(); ++i) { + if (updated_locked_tracks[i] > p_track) { + locked_tracks.insert(updated_locked_tracks[i] - 1); + } else { + locked_tracks.insert(updated_locked_tracks[i]); + } + } +} + +void AnimationBezierTrackEdit::_update_hidden_tracks_after(int p_track) { + if (hidden_tracks.has(p_track)) { + hidden_tracks.erase(p_track); + } + + Vector<int> updated_hidden_tracks; + for (Set<int>::Element *E = hidden_tracks.front(); E; E = E->next()) { + updated_hidden_tracks.push_back(E->get()); + } + hidden_tracks.clear(); + for (int i = 0; i < updated_hidden_tracks.size(); ++i) { + if (updated_hidden_tracks[i] > p_track) { + hidden_tracks.insert(updated_hidden_tracks[i] - 1); + } else { + hidden_tracks.insert(updated_hidden_tracks[i]); + } + } +} + String AnimationBezierTrackEdit::get_tooltip(const Point2 &p_pos) const { return Control::get_tooltip(p_pos); } @@ -583,10 +785,10 @@ void AnimationBezierTrackEdit::_clear_selection() { void AnimationBezierTrackEdit::_change_selected_keys_handle_mode(Animation::HandleMode p_mode) { undo_redo->create_action(TTR("Update Selected Key Handles")); double ratio = timeline->get_zoom_scale() * v_zoom; - for (Set<int>::Element *E = selection.back(); E; E = E->prev()) { - const int key_index = E->get(); - undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_handle_mode", track, key_index, animation->bezier_track_get_key_handle_mode(track, key_index), ratio); - undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_handle_mode", track, key_index, p_mode, ratio); + for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) { + const IntPair track_key_pair = E->get(); + undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_handle_mode", track_key_pair.first, track_key_pair.second, animation->bezier_track_get_key_handle_mode(track_key_pair.first, track_key_pair.second), ratio); + undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_handle_mode", track_key_pair.first, track_key_pair.second, p_mode, ratio); } undo_redo->commit_action(); } @@ -606,8 +808,8 @@ void AnimationBezierTrackEdit::_select_at_anim(const Ref<Animation> &p_anim, int int idx = animation->track_find_key(p_track, p_pos, true); ERR_FAIL_COND(idx < 0); - selection.insert(idx); - emit_signal(SNAME("select_key"), idx, true); + selection.insert(IntPair(p_track, idx)); + emit_signal(SNAME("select_key"), p_track, idx, true); update(); } @@ -631,14 +833,100 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { } } + Ref<InputEventKey> key_press = p_event; + + if (key_press.is_valid() && key_press->is_pressed()) { + if (ED_GET_SHORTCUT("animation_bezier_editor/focus")->matches_event(p_event)) { + SelectionSet focused_keys; + if (selection.is_empty()) { + for (int i = 0; i < edit_points.size(); ++i) { + IntPair key_pair = IntPair(edit_points[i].track, edit_points[i].key); + focused_keys.insert(key_pair); + } + } else { + for (SelectionSet::Element *E = selection.front(); E; E = E->next()) { + focused_keys.insert(E->get()); + if (E->get().second > 0) { + IntPair previous_key = IntPair(E->get().first, E->get().second - 1); + focused_keys.insert(previous_key); + } + if (E->get().second < animation->track_get_key_count(E->get().first) - 1) { + IntPair next_key = IntPair(E->get().first, E->get().second + 1); + focused_keys.insert(next_key); + } + } + } + if (focused_keys.is_empty()) { + accept_event(); + return; + } + + float minimum_time = INFINITY; + float maximum_time = -INFINITY; + float minimum_value = INFINITY; + float maximum_value = -INFINITY; + + for (SelectionSet::Element *E = focused_keys.front(); E; E = E->next()) { + IntPair key_pair = E->get(); + + float time = animation->track_get_key_time(key_pair.first, key_pair.second); + float value = animation->bezier_track_get_key_value(key_pair.first, key_pair.second); + + minimum_time = MIN(time, minimum_time); + maximum_time = MAX(time, maximum_time); + minimum_value = MIN(value, minimum_value); + maximum_value = MAX(value, maximum_value); + } + + float width = get_size().width - timeline->get_name_limit() - timeline->get_buttons_width(); + float padding = width * 0.1; + float desired_scale = (width - padding / 2) / (maximum_time - minimum_time); + minimum_time = MAX(0, minimum_time - (padding / 2) / desired_scale); + + float zv = Math::pow(100 / desired_scale, 0.125f); + if (zv < 1) { + zv = Math::pow(desired_scale / 100, 0.125f) - 1; + zv = 1 - zv; + } + float zoom_value = timeline->get_zoom()->get_max() - zv; + + timeline->get_zoom()->set_value(zoom_value); + timeline->call_deferred("set_value", minimum_time); + + v_scroll = (maximum_value + minimum_value) / 2.0; + v_zoom = (maximum_value - minimum_value) / ((get_size().height - timeline->get_size().height) * 0.9); + + update(); + accept_event(); + return; + } else if (ED_GET_SHORTCUT("animation_bezier_editor/select_all_keys")->matches_event(p_event)) { + for (int i = 0; i < edit_points.size(); ++i) { + selection.insert(IntPair(edit_points[i].track, edit_points[i].key)); + } + + update(); + accept_event(); + return; + } else if (ED_GET_SHORTCUT("animation_bezier_editor/deselect_all_keys")->matches_event(p_event)) { + selection.clear(); + + update(); + accept_event(); + return; + } + } + Ref<InputEventMouseButton> mb = p_event; + int limit = timeline->get_name_limit(); if (mb.is_valid() && mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) { menu_insert_key = mb->get_position(); - if (menu_insert_key.x >= timeline->get_name_limit() && menu_insert_key.x <= get_size().width - timeline->get_buttons_width()) { + if (menu_insert_key.x >= limit && menu_insert_key.x <= get_size().width) { Vector2 popup_pos = get_screen_position() + mb->get_position(); menu->clear(); - menu->add_icon_item(bezier_icon, TTR("Insert Key Here"), MENU_KEY_INSERT); + if (!locked_tracks.has(selected_track) || locked_tracks.has(selected_track)) { + menu->add_icon_item(bezier_icon, TTR("Insert Key Here"), MENU_KEY_INSERT); + } if (selection.size()) { menu->add_separator(); menu->add_icon_item(get_theme_icon(SNAME("Duplicate"), SNAME("EditorIcons")), TTR("Duplicate Selected Key(s)"), MENU_KEY_DUPLICATE); @@ -649,50 +937,163 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { menu->add_icon_item(get_theme_icon(SNAME("BezierHandlesBalanced"), SNAME("EditorIcons")), TTR("Make Handles Balanced"), MENU_KEY_SET_HANDLE_BALANCED); } - menu->set_as_minsize(); - menu->set_position(popup_pos); - menu->popup(); + if (menu->get_item_count()) { + menu->set_as_minsize(); + menu->set_position(popup_pos); + menu->popup(); + } } } if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) { for (const KeyValue<int, Rect2> &E : subtracks) { if (E.value.has_point(mb->get_position())) { - set_animation_and_track(animation, E.key); - _clear_selection(); + if (!locked_tracks.has(E.key) && !hidden_tracks.has(E.key)) { + set_animation_and_track(animation, E.key); + _clear_selection(); + } return; } } + for (const KeyValue<int, Map<int, Rect2>> &E : subtrack_icons) { + int track = E.key; + Map<int, Rect2> track_icons = E.value; + for (const KeyValue<int, Rect2> &I : track_icons) { + if (I.value.has_point(mb->get_position())) { + if (I.key == REMOVE_ICON) { + undo_redo->create_action("Remove Bezier Track"); + + undo_redo->add_do_method(this, "_update_locked_tracks_after", track); + undo_redo->add_do_method(this, "_update_hidden_tracks_after", track); + + undo_redo->add_do_method(animation.ptr(), "remove_track", track); + + undo_redo->add_undo_method(animation.ptr(), "add_track", Animation::TrackType::TYPE_BEZIER, track); + undo_redo->add_undo_method(animation.ptr(), "track_set_path", track, animation->track_get_path(track)); + + for (int i = 0; i < animation->track_get_key_count(track); ++i) { + undo_redo->add_undo_method( + animation.ptr(), + "bezier_track_insert_key", + track, animation->track_get_key_time(track, i), + animation->bezier_track_get_key_value(track, i), + animation->bezier_track_get_key_in_handle(track, i), + animation->bezier_track_get_key_out_handle(track, i), + animation->bezier_track_get_key_handle_mode(track, i)); + } + + undo_redo->commit_action(); + + selected_track = CLAMP(selected_track, 0, animation->get_track_count() - 1); + return; + } else if (I.key == LOCK_ICON) { + if (locked_tracks.has(track)) { + locked_tracks.erase(track); + } else { + locked_tracks.insert(track); + if (selected_track == track) { + for (int i = 0; i < animation->get_track_count(); ++i) { + if (!locked_tracks.has(i) && animation->track_get_type(i) == Animation::TrackType::TYPE_BEZIER) { + set_animation_and_track(animation, i); + break; + } + } + } + } + update(); + return; + } else if (I.key == VISIBILITY_ICON) { + if (hidden_tracks.has(track)) { + hidden_tracks.erase(track); + } else { + hidden_tracks.insert(track); + if (selected_track == track) { + for (int i = 0; i < animation->get_track_count(); ++i) { + if (!hidden_tracks.has(i) && animation->track_get_type(i) == Animation::TrackType::TYPE_BEZIER) { + set_animation_and_track(animation, i); + break; + } + } + } + } + + Vector<int> visible_tracks; + for (int i = 0; i < animation->get_track_count(); ++i) { + if (!hidden_tracks.has(i) && animation->track_get_type(i) == Animation::TrackType::TYPE_BEZIER) { + visible_tracks.push_back(i); + } + } + + if (visible_tracks.size() == 1) { + solo_track = visible_tracks[0]; + } else { + solo_track = -1; + } + + update(); + return; + } else if (I.key == SOLO_ICON) { + if (solo_track == track) { + solo_track = -1; + + hidden_tracks.clear(); + } else { + if (hidden_tracks.has(track)) { + hidden_tracks.erase(track); + } + for (int i = 0; i < animation->get_track_count(); ++i) { + if (animation->track_get_type(i) == Animation::TrackType::TYPE_BEZIER) { + if (i != track && !hidden_tracks.has(i)) { + hidden_tracks.insert(i); + } + } + } + + set_animation_and_track(animation, track); + solo_track = track; + } + update(); + return; + } + return; + } + } + } + for (int i = 0; i < edit_points.size(); i++) { //first check point //command makes it ignore the main point, so control point editors can be force-edited //path 2D editing in the 3D and 2D editors works the same way if (!mb->is_command_pressed()) { if (edit_points[i].point_rect.has_point(mb->get_position())) { + IntPair pair = IntPair(edit_points[i].track, edit_points[i].key); if (mb->is_shift_pressed()) { //add to selection - if (selection.has(i)) { - selection.erase(i); + if (selection.has(pair)) { + selection.erase(pair); } else { - selection.insert(i); + selection.insert(pair); } update(); - select_single_attempt = -1; - } else if (selection.has(i)) { + select_single_attempt = IntPair(-1, -1); + } else if (selection.has(pair)) { moving_selection_attempt = true; moving_selection = false; - moving_selection_from_key = i; + moving_selection_from_key = pair.second; + moving_selection_from_track = pair.first; moving_selection_offset = Vector2(); - select_single_attempt = i; + select_single_attempt = pair; update(); } else { moving_selection_attempt = true; moving_selection = true; - moving_selection_from_key = i; + moving_selection_from_key = pair.second; + moving_selection_from_track = pair.first; moving_selection_offset = Vector2(); + set_animation_and_track(animation, pair.first); selection.clear(); - selection.insert(i); + selection.insert(pair); update(); } return; @@ -701,26 +1102,27 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { if (edit_points[i].in_rect.has_point(mb->get_position())) { moving_handle = -1; - moving_handle_key = i; - moving_handle_left = animation->bezier_track_get_key_in_handle(track, i); - moving_handle_right = animation->bezier_track_get_key_out_handle(track, i); + moving_handle_key = edit_points[i].key; + moving_handle_track = edit_points[i].track; + moving_handle_left = animation->bezier_track_get_key_in_handle(edit_points[i].track, edit_points[i].key); + moving_handle_right = animation->bezier_track_get_key_out_handle(edit_points[i].track, edit_points[i].key); update(); return; } if (edit_points[i].out_rect.has_point(mb->get_position())) { moving_handle = 1; - moving_handle_key = i; - moving_handle_left = animation->bezier_track_get_key_in_handle(track, i); - moving_handle_right = animation->bezier_track_get_key_out_handle(track, i); + moving_handle_key = edit_points[i].key; + moving_handle_track = edit_points[i].track; + moving_handle_left = animation->bezier_track_get_key_in_handle(edit_points[i].track, edit_points[i].key); + moving_handle_right = animation->bezier_track_get_key_out_handle(edit_points[i].track, edit_points[i].key); update(); return; - ; } } //insert new point - if (mb->is_command_pressed() && mb->get_position().x >= timeline->get_name_limit() && mb->get_position().x < get_size().width - timeline->get_buttons_width()) { + if (mb->get_position().x >= limit && mb->get_position().x < get_size().width && mb->is_command_pressed()) { Array new_point; new_point.resize(6); @@ -733,34 +1135,35 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { new_point[4] = 0; new_point[5] = 0; - float time = ((mb->get_position().x - timeline->get_name_limit()) / timeline->get_zoom_scale()) + timeline->get_value(); - while (animation->track_find_key(track, time, true) != -1) { + float time = ((mb->get_position().x - limit) / timeline->get_zoom_scale()) + timeline->get_value(); + while (animation->track_find_key(selected_track, time, true) != -1) { time += 0.001; } undo_redo->create_action(TTR("Add Bezier Point")); - undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, time, new_point); - undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, time); + undo_redo->add_do_method(animation.ptr(), "track_insert_key", selected_track, time, new_point); + undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", selected_track, time); undo_redo->commit_action(); //then attempt to move - int index = animation->track_find_key(track, time, true); + int index = animation->track_find_key(selected_track, time, true); ERR_FAIL_COND(index == -1); _clear_selection(); - selection.insert(index); + selection.insert(IntPair(selected_track, index)); moving_selection_attempt = true; moving_selection = false; moving_selection_from_key = index; + moving_selection_from_track = selected_track; moving_selection_offset = Vector2(); - select_single_attempt = -1; + select_single_attempt = IntPair(-1, -1); update(); return; } //box select - if (mb->get_position().x >= timeline->get_name_limit() && mb->get_position().x < get_size().width - timeline->get_buttons_width()) { + if (mb->get_position().x >= limit && mb->get_position().x < get_size().width) { box_selecting_attempt = true; box_selecting = false; box_selecting_add = false; @@ -786,14 +1189,44 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { } Rect2 selection_rect(bs_from, bs_to - bs_from); + bool track_set = false; for (int i = 0; i < edit_points.size(); i++) { if (edit_points[i].point_rect.intersects(selection_rect)) { - selection.insert(i); + selection.insert(IntPair(edit_points[i].track, edit_points[i].key)); + if (!track_set) { + track_set = true; + set_animation_and_track(animation, edit_points[i].track); + } } } } else { _clear_selection(); //clicked and nothing happened, so clear the selection + + //select by clicking on curve + int track_count = animation->get_track_count(); + + float animation_length = animation->get_length(); + animation->set_length(real_t(INT_MAX)); //bezier_track_interpolate doesn't find keys if they exist beyond anim length + + float time = ((mb->get_position().x - limit) / timeline->get_zoom_scale()) + timeline->get_value(); + + for (int i = 0; i < track_count; ++i) { + if (animation->track_get_type(i) != Animation::TrackType::TYPE_BEZIER || hidden_tracks.has(i) || locked_tracks.has(i)) { + continue; + } + + float track_h = animation->bezier_track_interpolate(i, time); + float track_height = _bezier_h_to_pixel(track_h); + + if (abs(mb->get_position().y - track_height) < 10) { + set_animation_and_track(animation, i); + break; + } + } + + animation->set_length(animation_length); } + box_selecting_attempt = false; box_selecting = false; update(); @@ -801,10 +1234,10 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { if (moving_handle != 0 && mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) { undo_redo->create_action(TTR("Move Bezier Points")); - undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_in_handle", track, moving_handle_key, moving_handle_left); - undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_out_handle", track, moving_handle_key, moving_handle_right); - undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_in_handle", track, moving_handle_key, animation->bezier_track_get_key_in_handle(track, moving_handle_key)); - undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", track, moving_handle_key, animation->bezier_track_get_key_out_handle(track, moving_handle_key)); + undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_in_handle", selected_track, moving_handle_key, moving_handle_left); + undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_out_handle", selected_track, moving_handle_key, moving_handle_right); + undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_in_handle", selected_track, moving_handle_key, animation->bezier_track_get_key_in_handle(selected_track, moving_handle_key)); + undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", selected_track, moving_handle_key, animation->bezier_track_get_key_out_handle(selected_track, moving_handle_key)); undo_redo->commit_action(); moving_handle = 0; @@ -819,60 +1252,60 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { List<AnimMoveRestore> to_restore; // 1-remove the keys - for (Set<int>::Element *E = selection.back(); E; E = E->prev()) { - undo_redo->add_do_method(animation.ptr(), "track_remove_key", track, E->get()); + for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) { + undo_redo->add_do_method(animation.ptr(), "track_remove_key", E->get().first, E->get().second); } // 2- remove overlapped keys - for (Set<int>::Element *E = selection.back(); E; E = E->prev()) { - float newtime = editor->snap_time(animation->track_get_key_time(track, E->get()) + moving_selection_offset.x); + for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) { + float newtime = editor->snap_time(animation->track_get_key_time(E->get().first, E->get().second) + moving_selection_offset.x); - int idx = animation->track_find_key(track, newtime, true); + int idx = animation->track_find_key(E->get().first, newtime, true); if (idx == -1) { continue; } - if (selection.has(idx)) { + if (selection.has(IntPair(E->get().first, idx))) { continue; //already in selection, don't save } - undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", track, newtime); + undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", E->get().first, newtime); AnimMoveRestore amr; - amr.key = animation->track_get_key_value(track, idx); - amr.track = track; + amr.key = animation->track_get_key_value(E->get().first, idx); + amr.track = E->get().first; amr.time = newtime; to_restore.push_back(amr); } // 3-move the keys (re insert them) - for (Set<int>::Element *E = selection.back(); E; E = E->prev()) { - float newpos = editor->snap_time(animation->track_get_key_time(track, E->get()) + moving_selection_offset.x); + for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) { + float newpos = editor->snap_time(animation->track_get_key_time(E->get().first, E->get().second) + moving_selection_offset.x); /* if (newpos<0) continue; //no add at the beginning */ - Array key = animation->track_get_key_value(track, E->get()); + Array key = animation->track_get_key_value(E->get().first, E->get().second); float h = key[0]; h += moving_selection_offset.y; key[0] = h; - undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, newpos, key, 1); + undo_redo->add_do_method(animation.ptr(), "track_insert_key", E->get().first, newpos, key, 1); } // 4-(undo) remove inserted keys - for (Set<int>::Element *E = selection.back(); E; E = E->prev()) { - float newpos = editor->snap_time(animation->track_get_key_time(track, E->get()) + moving_selection_offset.x); + for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) { + float newpos = editor->snap_time(animation->track_get_key_time(E->get().first, E->get().second) + moving_selection_offset.x); /* if (newpos<0) continue; //no remove what no inserted */ - undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, newpos); + undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", E->get().first, newpos); } // 5-(undo) reinsert keys - for (Set<int>::Element *E = selection.back(); E; E = E->prev()) { - float oldpos = animation->track_get_key_time(track, E->get()); - undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, oldpos, animation->track_get_key_value(track, E->get()), 1); + for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) { + float oldpos = animation->track_get_key_time(E->get().first, E->get().second); + undo_redo->add_undo_method(animation.ptr(), "track_insert_key", E->get().first, oldpos, animation->track_get_key_value(E->get().first, E->get().second), 1); } // 6-(undo) reinsert overlapped keys @@ -885,20 +1318,21 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { // 7-reselect - for (Set<int>::Element *E = selection.back(); E; E = E->prev()) { - float oldpos = animation->track_get_key_time(track, E->get()); + for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) { + float oldpos = animation->track_get_key_time(E->get().first, E->get().second); float newpos = editor->snap_time(oldpos + moving_selection_offset.x); - undo_redo->add_do_method(this, "_select_at_anim", animation, track, newpos); - undo_redo->add_undo_method(this, "_select_at_anim", animation, track, oldpos); + undo_redo->add_do_method(this, "_select_at_anim", animation, E->get().first, newpos); + undo_redo->add_undo_method(this, "_select_at_anim", animation, E->get().first, oldpos); } undo_redo->commit_action(); moving_selection = false; - } else if (select_single_attempt != -1) { + } else if (select_single_attempt != IntPair(-1, -1)) { selection.clear(); selection.insert(select_single_attempt); + set_animation_and_track(animation, select_single_attempt.first); } moving_selection_attempt = false; @@ -909,13 +1343,13 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { if (moving_selection_attempt && mm.is_valid()) { if (!moving_selection) { moving_selection = true; - select_single_attempt = -1; + select_single_attempt = IntPair(-1, -1); } float y = (get_size().height / 2 - mm->get_position().y) * v_zoom + v_scroll; - float x = editor->snap_time(((mm->get_position().x - timeline->get_name_limit()) / timeline->get_zoom_scale()) + timeline->get_value()); + float x = editor->snap_time(((mm->get_position().x - limit) / timeline->get_zoom_scale()) + timeline->get_value()); - moving_selection_offset = Vector2(x - animation->track_get_key_time(track, moving_selection_from_key), y - animation->bezier_track_get_key_value(track, moving_selection_from_key)); + moving_selection_offset = Vector2(x - animation->track_get_key_time(moving_selection_from_track, moving_selection_from_key), y - animation->bezier_track_get_key_value(moving_selection_from_track, moving_selection_from_key)); update(); } @@ -938,17 +1372,17 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { float y = (get_size().height / 2 - mm->get_position().y) * v_zoom + v_scroll; float x = editor->snap_time((mm->get_position().x - timeline->get_name_limit()) / timeline->get_zoom_scale()) + timeline->get_value(); - Vector2 key_pos = Vector2(animation->track_get_key_time(track, moving_handle_key), animation->bezier_track_get_key_value(track, moving_handle_key)); + Vector2 key_pos = Vector2(animation->track_get_key_time(selected_track, moving_handle_key), animation->bezier_track_get_key_value(selected_track, moving_handle_key)); Vector2 moving_handle_value = Vector2(x, y) - key_pos; - moving_handle_left = animation->bezier_track_get_key_in_handle(track, moving_handle_key); - moving_handle_right = animation->bezier_track_get_key_out_handle(track, moving_handle_key); + moving_handle_left = animation->bezier_track_get_key_in_handle(moving_handle_track, moving_handle_key); + moving_handle_right = animation->bezier_track_get_key_out_handle(moving_handle_track, moving_handle_key); if (moving_handle == -1) { moving_handle_left = moving_handle_value; - if (animation->bezier_track_get_key_handle_mode(track, moving_handle_key) == Animation::HANDLE_MODE_BALANCED) { + if (animation->bezier_track_get_key_handle_mode(moving_handle_track, moving_handle_key) == Animation::HANDLE_MODE_BALANCED) { double ratio = timeline->get_zoom_scale() * v_zoom; Transform2D xform; xform.set_scale(Vector2(1.0, 1.0 / ratio)); @@ -961,7 +1395,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { } else if (moving_handle == 1) { moving_handle_right = moving_handle_value; - if (animation->bezier_track_get_key_handle_mode(track, moving_handle_key) == Animation::HANDLE_MODE_BALANCED) { + if (animation->bezier_track_get_key_handle_mode(moving_handle_track, moving_handle_key) == Animation::HANDLE_MODE_BALANCED) { double ratio = timeline->get_zoom_scale() * v_zoom; Transform2D xform; xform.set_scale(Vector2(1.0, 1.0 / ratio)); @@ -980,12 +1414,12 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { undo_redo->create_action(TTR("Move Bezier Points")); if (moving_handle == -1) { double ratio = timeline->get_zoom_scale() * v_zoom; - undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_in_handle", track, moving_handle_key, moving_handle_left, ratio); - undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_in_handle", track, moving_handle_key, animation->bezier_track_get_key_in_handle(track, moving_handle_key), ratio); + undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_in_handle", moving_handle_track, moving_handle_key, moving_handle_left, ratio); + undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_in_handle", moving_handle_track, moving_handle_key, animation->bezier_track_get_key_in_handle(moving_handle_track, moving_handle_key), ratio); } else if (moving_handle == 1) { double ratio = timeline->get_zoom_scale() * v_zoom; - undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_out_handle", track, moving_handle_key, moving_handle_right, ratio); - undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", track, moving_handle_key, animation->bezier_track_get_key_out_handle(track, moving_handle_key), ratio); + undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_out_handle", moving_handle_track, moving_handle_key, moving_handle_right, ratio); + undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", moving_handle_track, moving_handle_key, animation->bezier_track_get_key_out_handle(moving_handle_track, moving_handle_key), ratio); } undo_redo->commit_action(); @@ -1028,27 +1462,32 @@ void AnimationBezierTrackEdit::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_or void AnimationBezierTrackEdit::_menu_selected(int p_index) { switch (p_index) { case MENU_KEY_INSERT: { - Array new_point; - new_point.resize(6); + if (animation->get_track_count() > 0) { + Array new_point; + new_point.resize(6); - float h = (get_size().height / 2 - menu_insert_key.y) * v_zoom + v_scroll; + float h = (get_size().height / 2 - menu_insert_key.y) * v_zoom + v_scroll; - new_point[0] = h; - new_point[1] = -0.25; - new_point[2] = 0; - new_point[3] = 0.25; - new_point[4] = 0; - new_point[5] = Animation::HANDLE_MODE_BALANCED; + new_point[0] = h; + new_point[1] = -0.25; + new_point[2] = 0; + new_point[3] = 0.25; + new_point[4] = 0; + new_point[5] = Animation::HANDLE_MODE_BALANCED; - float time = ((menu_insert_key.x - timeline->get_name_limit()) / timeline->get_zoom_scale()) + timeline->get_value(); - while (animation->track_find_key(track, time, true) != -1) { - time += 0.001; - } + int limit = timeline->get_name_limit(); - undo_redo->create_action(TTR("Add Bezier Point")); - undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, time, new_point); - undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, time); - undo_redo->commit_action(); + float time = ((menu_insert_key.x - limit) / timeline->get_zoom_scale()) + timeline->get_value(); + + while (animation->track_find_key(selected_track, time, true) != -1) { + time += 0.001; + } + + undo_redo->create_action(TTR("Add Bezier Point")); + undo_redo->add_do_method(animation.ptr(), "track_insert_key", selected_track, time, new_point); + undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", selected_track, time); + undo_redo->commit_action(); + } } break; case MENU_KEY_DUPLICATE: { @@ -1072,8 +1511,8 @@ void AnimationBezierTrackEdit::duplicate_selection() { } float top_time = 1e10; - for (Set<int>::Element *E = selection.back(); E; E = E->prev()) { - float t = animation->track_get_key_time(track, E->get()); + for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) { + float t = animation->track_get_key_time(E->get().first, E->get().second); if (t < top_time) { top_time = t; } @@ -1083,21 +1522,21 @@ void AnimationBezierTrackEdit::duplicate_selection() { List<Pair<int, float>> new_selection_values; - for (Set<int>::Element *E = selection.back(); E; E = E->prev()) { - float t = animation->track_get_key_time(track, E->get()); + for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) { + float t = animation->track_get_key_time(E->get().first, E->get().second); float dst_time = t + (timeline->get_play_position() - top_time); - int existing_idx = animation->track_find_key(track, dst_time, true); + int existing_idx = animation->track_find_key(E->get().first, dst_time, true); - undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, dst_time, animation->track_get_key_value(track, E->get()), animation->track_get_key_transition(track, E->get())); - undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, dst_time); + undo_redo->add_do_method(animation.ptr(), "track_insert_key", E->get().first, dst_time, animation->track_get_key_value(E->get().first, E->get().second), animation->track_get_key_transition(E->get().first, E->get().second)); + undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", E->get().first, dst_time); Pair<int, float> p; - p.first = track; + p.first = E->get().first; p.second = dst_time; new_selection_values.push_back(p); if (existing_idx != -1) { - undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, dst_time, animation->track_get_key_value(track, existing_idx), animation->track_get_key_transition(track, existing_idx)); + undo_redo->add_undo_method(animation.ptr(), "track_insert_key", E->get().first, dst_time, animation->track_get_key_value(E->get().first, existing_idx), animation->track_get_key_transition(E->get().first, existing_idx)); } } @@ -1116,7 +1555,7 @@ void AnimationBezierTrackEdit::duplicate_selection() { continue; } - selection.insert(existing_idx); + selection.insert(IntPair(track, existing_idx)); } update(); @@ -1126,9 +1565,9 @@ void AnimationBezierTrackEdit::delete_selection() { if (selection.size()) { undo_redo->create_action(TTR("Anim Delete Keys")); - for (Set<int>::Element *E = selection.back(); E; E = E->prev()) { - undo_redo->add_do_method(animation.ptr(), "track_remove_key", track, E->get()); - undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, animation->track_get_key_time(track, E->get()), animation->track_get_key_value(track, E->get()), 1); + for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) { + undo_redo->add_do_method(animation.ptr(), "track_remove_key", E->get().first, E->get().second); + undo_redo->add_undo_method(animation.ptr(), "track_insert_key", E->get().first, animation->track_get_key_time(E->get().first, E->get().second), animation->track_get_key_value(E->get().first, E->get().second), 1); } undo_redo->add_do_method(this, "_clear_selection_for_anim", animation); undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation); @@ -1142,12 +1581,14 @@ void AnimationBezierTrackEdit::_bind_methods() { ClassDB::bind_method("_clear_selection", &AnimationBezierTrackEdit::_clear_selection); ClassDB::bind_method("_clear_selection_for_anim", &AnimationBezierTrackEdit::_clear_selection_for_anim); ClassDB::bind_method("_select_at_anim", &AnimationBezierTrackEdit::_select_at_anim); + ClassDB::bind_method("_update_hidden_tracks_after", &AnimationBezierTrackEdit::_update_hidden_tracks_after); + ClassDB::bind_method("_update_locked_tracks_after", &AnimationBezierTrackEdit::_update_locked_tracks_after); ADD_SIGNAL(MethodInfo("timeline_changed", PropertyInfo(Variant::FLOAT, "position"), PropertyInfo(Variant::BOOL, "drag"))); ADD_SIGNAL(MethodInfo("remove_request", PropertyInfo(Variant::INT, "track"))); ADD_SIGNAL(MethodInfo("insert_key", PropertyInfo(Variant::FLOAT, "ofs"))); - ADD_SIGNAL(MethodInfo("select_key", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::BOOL, "single"))); - ADD_SIGNAL(MethodInfo("deselect_key", PropertyInfo(Variant::INT, "index"))); + ADD_SIGNAL(MethodInfo("select_key", PropertyInfo(Variant::INT, "track"), PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::BOOL, "single"))); + ADD_SIGNAL(MethodInfo("deselect_key", PropertyInfo(Variant::INT, "track"), PropertyInfo(Variant::INT, "index"))); ADD_SIGNAL(MethodInfo("clear_selection")); ADD_SIGNAL(MethodInfo("close_request")); @@ -1170,14 +1611,9 @@ AnimationBezierTrackEdit::AnimationBezierTrackEdit() { set_clip_contents(true); - close_button = memnew(Button); - close_button->connect("pressed", Callable(this, SNAME("emit_signal")), varray(SNAME("close_request"))); - close_button->set_text(TTR("Close")); - - right_column = memnew(VBoxContainer); - right_column->add_child(close_button); - right_column->add_spacer(); - add_child(right_column); + ED_SHORTCUT("animation_bezier_editor/focus", TTR("Focus"), Key::F); + ED_SHORTCUT("animation_bezier_editor/select_all_keys", TTR("Select All Keys"), KeyModifierMask::CMD | Key::A); + ED_SHORTCUT("animation_bezier_editor/deselect_all_keys", TTR("Deselect All Keys"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::A); menu = memnew(PopupMenu); add_child(menu); diff --git a/editor/animation_bezier_editor.h b/editor/animation_bezier_editor.h index cf719a0355..fa6fc405f2 100644 --- a/editor/animation_bezier_editor.h +++ b/editor/animation_bezier_editor.h @@ -46,9 +46,6 @@ class AnimationBezierTrackEdit : public Control { MENU_KEY_SET_HANDLE_BALANCED, }; - VBoxContainer *right_column; - Button *close_button; - AnimationTimelineEdit *timeline = nullptr; UndoRedo *undo_redo = nullptr; Node *root = nullptr; @@ -56,7 +53,7 @@ class AnimationBezierTrackEdit : public Control { float play_position_pos = 0; Ref<Animation> animation; - int track; + int selected_track; Vector<Rect2> view_rects; @@ -66,6 +63,19 @@ class AnimationBezierTrackEdit : public Control { Map<int, Rect2> subtracks; + enum { + REMOVE_ICON, + LOCK_ICON, + SOLO_ICON, + VISIBILITY_ICON + }; + + Map<int, Map<int, Rect2>> subtrack_icons; + Set<int> locked_tracks; + Set<int> hidden_tracks; + int solo_track = -1; + bool is_filtered = false; + float v_scroll = 0; float v_zoom = 1; @@ -73,6 +83,9 @@ class AnimationBezierTrackEdit : public Control { void _zoom_changed(); + void _update_locked_tracks_after(int p_track); + void _update_hidden_tracks_after(int p_track); + virtual void gui_input(const Ref<InputEvent> &p_event) override; void _menu_selected(int p_index); @@ -80,10 +93,13 @@ class AnimationBezierTrackEdit : public Control { Vector2 insert_at_pos; + typedef Pair<int, int> IntPair; + bool moving_selection_attempt = false; - int select_single_attempt = -1; + IntPair select_single_attempt; bool moving_selection = false; int moving_selection_from_key; + int moving_selection_from_track; Vector2 moving_selection_offset; @@ -95,6 +111,7 @@ class AnimationBezierTrackEdit : public Control { int moving_handle = 0; //0 no move -1 or +1 out int moving_handle_key = 0; + int moving_handle_track = 0; Vector2 moving_handle_left; Vector2 moving_handle_right; int moving_handle_mode; // value from Animation::HandleMode @@ -119,11 +136,25 @@ class AnimationBezierTrackEdit : public Control { Rect2 point_rect; Rect2 in_rect; Rect2 out_rect; + int track; + int key; }; Vector<EditPoint> edit_points; - Set<int> selection; + struct SelectionCompare { + bool operator()(const IntPair &lh, const IntPair &rh) { + if (lh.first == rh.first) { + return lh.second < rh.second; + } else { + return lh.first < rh.first; + } + } + }; + + typedef Set<IntPair, SelectionCompare> SelectionSet; + + SelectionSet selection; Ref<ViewPanner> panner; void _scroll_callback(Vector2 p_scroll_vec, bool p_alt); @@ -151,6 +182,7 @@ public: void set_timeline(AnimationTimelineEdit *p_timeline); void set_editor(AnimationTrackEditor *p_editor); void set_root(Node *p_root); + void set_filtered(bool p_filtered); void set_play_position(float p_pos); void update_play_position(); diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 2f33619a52..de924e84dc 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -570,7 +570,7 @@ public: p_list->push_back(PropertyInfo(Variant::VECTOR3, "position")); } break; case Animation::TYPE_ROTATION_3D: { - p_list->push_back(PropertyInfo(Variant::VECTOR3, "rotation")); + p_list->push_back(PropertyInfo(Variant::QUATERNION, "rotation")); } break; case Animation::TYPE_SCALE_3D: { p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale")); @@ -2118,23 +2118,19 @@ void AnimationTrackEdit::_notification(int p_what) { update_mode_rect.position.y = 0; update_mode_rect.size.y = get_size().height; - ofs += update_icon->get_width() + hsep; - update_mode_rect.size.x += hsep; + ofs += update_icon->get_width() + hsep / 2; + update_mode_rect.size.x += hsep / 2; if (animation->track_get_type(track) == Animation::TYPE_VALUE) { draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2)); update_mode_rect.size.x += down_icon->get_width(); - bezier_edit_rect = Rect2(); } else if (animation->track_get_type(track) == Animation::TYPE_BEZIER) { Ref<Texture2D> bezier_icon = get_theme_icon(SNAME("EditBezier"), SNAME("EditorIcons")); update_mode_rect.size.x += down_icon->get_width(); - bezier_edit_rect.position = update_mode_rect.position + (update_mode_rect.size - bezier_icon->get_size()) / 2; - bezier_edit_rect.size = bezier_icon->get_size(); - draw_texture(bezier_icon, bezier_edit_rect.position); + update_mode_rect = Rect2(); } else { update_mode_rect = Rect2(); - bezier_edit_rect = Rect2(); } ofs += down_icon->get_width(); @@ -2160,8 +2156,8 @@ void AnimationTrackEdit::_notification(int p_what) { interp_mode_rect.position.y = 0; interp_mode_rect.size.y = get_size().height; - ofs += icon->get_width() + hsep; - interp_mode_rect.size.x += hsep; + ofs += icon->get_width() + hsep / 2; + interp_mode_rect.size.x += hsep / 2; if (!animation->track_is_compressed(track) && (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_BLEND_SHAPE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D)) { draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2)); @@ -2193,8 +2189,8 @@ void AnimationTrackEdit::_notification(int p_what) { loop_wrap_rect.position.y = 0; loop_wrap_rect.size.y = get_size().height; - ofs += icon->get_width() + hsep; - loop_wrap_rect.size.x += hsep; + ofs += icon->get_width() + hsep / 2; + loop_wrap_rect.size.x += hsep / 2; if (!animation->track_is_compressed(track) && (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_BLEND_SHAPE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D)) { draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2)); @@ -2213,7 +2209,7 @@ void AnimationTrackEdit::_notification(int p_what) { Ref<Texture2D> icon = get_theme_icon(animation->track_is_compressed(track) ? SNAME("Lock") : SNAME("Remove"), SNAME("EditorIcons")); - remove_rect.position.x = ofs + ((get_size().width - ofs) - icon->get_width()) / 2; + remove_rect.position.x = ofs + ((get_size().width - ofs) - icon->get_width()); remove_rect.position.y = int(get_size().height - icon->get_height()) / 2; remove_rect.size = icon->get_size(); @@ -2792,11 +2788,6 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) { return; } - if (bezier_edit_rect.has_point(pos)) { - emit_signal(SNAME("bezier_edit")); - accept_event(); - } - // Check keyframes. if (!animation->track_is_compressed(track)) { // Selecting compressed keyframes for editing is not possible. @@ -3326,10 +3317,21 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim) { snap->set_disabled(false); snap_mode->set_disabled(false); + bezier_edit_icon->set_disabled(true); + imported_anim_warning->hide(); + bool import_warning_done = false; + bool bezier_done = false; for (int i = 0; i < animation->get_track_count(); i++) { if (animation->track_is_imported(i)) { imported_anim_warning->show(); + import_warning_done = true; + } + if (animation->track_get_type(i) == Animation::TrackType::TYPE_BEZIER) { + bezier_edit_icon->set_disabled(false); + bezier_done = true; + } + if (import_warning_done && bezier_done) { break; } } @@ -3343,6 +3345,7 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim) { step->set_read_only(true); snap->set_disabled(true); snap_mode->set_disabled(true); + bezier_edit_icon->set_disabled(true); } } @@ -4167,13 +4170,15 @@ AnimationTrackEditor::TrackIndices AnimationTrackEditor::_confirm_insert(InsertD } break; case Animation::TYPE_BEZIER: { Array array; - array.resize(5); + array.resize(6); array[0] = p_id.value; array[1] = -0.25; array[2] = 0; array[3] = 0.25; array[4] = 0; + array[5] = Animation::HANDLE_MODE_BALANCED; value = array; + bezier_edit_icon->set_disabled(false); } break; case Animation::TYPE_ANIMATION: { @@ -4399,7 +4404,6 @@ void AnimationTrackEditor::_update_tracks() { track_edit->connect("insert_key", callable_mp(this, &AnimationTrackEditor::_insert_key_from_track), varray(i), CONNECT_DEFERRED); track_edit->connect("select_key", callable_mp(this, &AnimationTrackEditor::_key_selected), varray(i), CONNECT_DEFERRED); track_edit->connect("deselect_key", callable_mp(this, &AnimationTrackEditor::_key_deselected), varray(i), CONNECT_DEFERRED); - track_edit->connect("bezier_edit", callable_mp(this, &AnimationTrackEditor::_bezier_edit), varray(i), CONNECT_DEFERRED); track_edit->connect("move_selection_begin", callable_mp(this, &AnimationTrackEditor::_move_selection_begin)); track_edit->connect("move_selection", callable_mp(this, &AnimationTrackEditor::_move_selection)); track_edit->connect("move_selection_commit", callable_mp(this, &AnimationTrackEditor::_move_selection_commit)); @@ -4515,6 +4519,7 @@ void AnimationTrackEditor::_notification(int p_what) { if (p_what == NOTIFICATION_THEME_CHANGED || p_what == NOTIFICATION_ENTER_TREE) { zoom_icon->set_texture(get_theme_icon(SNAME("Zoom"), SNAME("EditorIcons"))); + bezier_edit_icon->set_icon(get_theme_icon(SNAME("EditBezier"), SNAME("EditorIcons"))); snap->set_icon(get_theme_icon(SNAME("Snap"), SNAME("EditorIcons"))); view_group->set_icon(get_theme_icon(view_group->is_pressed() ? SNAME("AnimationTrackList") : SNAME("AnimationTrackGroup"), SNAME("EditorIcons"))); selected_filter->set_icon(get_theme_icon(SNAME("AnimationFilter"), SNAME("EditorIcons"))); @@ -4630,6 +4635,7 @@ void AnimationTrackEditor::_new_track_node_selected(NodePath p_path) { adding_track_path = path_to; prop_selector->set_type_filter(filter); prop_selector->select_property_from_instance(node); + bezier_edit_icon->set_disabled(false); } break; case Animation::TYPE_AUDIO: { if (!node->is_class("AudioStreamPlayer") && !node->is_class("AudioStreamPlayer2D") && !node->is_class("AudioStreamPlayer3D")) { @@ -4946,7 +4952,7 @@ void AnimationTrackEditor::_add_method_key(const String &p_method) { EditorNode::get_singleton()->show_warning(TTR("Method not found in object: ") + p_method); } -void AnimationTrackEditor::_key_selected(int p_key, bool p_single, int p_track) { +void AnimationTrackEditor::_key_selected(int p_track, int p_key, bool p_single) { ERR_FAIL_INDEX(p_track, animation->get_track_count()); ERR_FAIL_INDEX(p_key, animation->track_get_key_count(p_track)); @@ -5294,6 +5300,20 @@ void AnimationTrackEditor::_scroll_input(const Ref<InputEvent> &p_event) { } } +void AnimationTrackEditor::_toggle_bezier_edit() { + if (bezier_edit->is_visible()) { + _cancel_bezier_edit(); + } else { + int track_count = animation->get_track_count(); + for (int i = 0; i < track_count; ++i) { + if (animation->track_get_type(i) == Animation::TrackType::TYPE_BEZIER) { + _bezier_edit(i); + return; + } + } + } +} + void AnimationTrackEditor::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) { if (p_alt) { if (p_scroll_vec.x < 0 || p_scroll_vec.y < 0) { @@ -5322,6 +5342,7 @@ void AnimationTrackEditor::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin void AnimationTrackEditor::_cancel_bezier_edit() { bezier_edit->hide(); scroll->show(); + bezier_edit_icon->set_pressed(false); } void AnimationTrackEditor::_bezier_edit(int p_for_track) { @@ -5908,6 +5929,7 @@ void AnimationTrackEditor::_cleanup_animation(Ref<Animation> p_animation) { void AnimationTrackEditor::_view_group_toggle() { _update_tracks(); view_group->set_icon(get_theme_icon(view_group->is_pressed() ? SNAME("AnimationTrackList") : SNAME("AnimationTrackGroup"), SNAME("EditorIcons"))); + bezier_edit->set_filtered(selected_filter->is_pressed()); } bool AnimationTrackEditor::is_grouping_tracks() { @@ -6153,6 +6175,15 @@ AnimationTrackEditor::AnimationTrackEditor() { bottom_hb->add_spacer(); + bezier_edit_icon = memnew(Button); + bezier_edit_icon->set_flat(true); + bezier_edit_icon->set_disabled(true); + bezier_edit_icon->set_toggle_mode(true); + bezier_edit_icon->connect("pressed", callable_mp(this, &AnimationTrackEditor::_toggle_bezier_edit)); + bezier_edit_icon->set_tooltip(TTR("Toggle between the bezier curve editor and track editor.")); + + bottom_hb->add_child(bezier_edit_icon); + selected_filter = memnew(Button); selected_filter->set_flat(true); selected_filter->connect("pressed", callable_mp(this, &AnimationTrackEditor::_view_group_toggle)); // Same function works the same. diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index 50c5c692c0..edba784310 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -164,7 +164,6 @@ class AnimationTrackEdit : public Control { Rect2 interp_mode_rect; Rect2 loop_wrap_rect; Rect2 remove_rect; - Rect2 bezier_edit_rect; Ref<Texture2D> type_icon; Ref<Texture2D> selected_icon; @@ -300,6 +299,7 @@ class AnimationTrackEditor : public VBoxContainer { EditorSpinSlider *step; TextureRect *zoom_icon; Button *snap; + Button *bezier_edit_icon; OptionButton *snap_mode; Button *imported_anim_warning; @@ -406,7 +406,7 @@ class AnimationTrackEditor : public VBoxContainer { Map<SelectedKey, KeyInfo> selection; - void _key_selected(int p_key, bool p_single, int p_track); + void _key_selected(int p_track, int p_key, bool p_single); void _key_deselected(int p_key, int p_track); bool moving_selection; @@ -431,6 +431,7 @@ class AnimationTrackEditor : public VBoxContainer { Vector<Ref<AnimationTrackEditPlugin>> track_edit_plugins; + void _toggle_bezier_edit(); void _cancel_bezier_edit(); void _bezier_edit(int p_for_track); diff --git a/editor/debugger/editor_network_profiler.cpp b/editor/debugger/editor_network_profiler.cpp index 698e950f57..b05134144e 100644 --- a/editor/debugger/editor_network_profiler.cpp +++ b/editor/debugger/editor_network_profiler.cpp @@ -56,7 +56,7 @@ void EditorNetworkProfiler::_update_frame() { TreeItem *root = counters_display->create_item(); - for (const KeyValue<ObjectID, DebuggerMarshalls::MultiplayerNodeInfo> &E : nodes_data) { + for (const KeyValue<ObjectID, SceneDebugger::RPCNodeInfo> &E : nodes_data) { TreeItem *node = counters_display->create_item(root); for (int j = 0; j < counters_display->get_columns(); ++j) { @@ -65,9 +65,7 @@ void EditorNetworkProfiler::_update_frame() { node->set_text(0, E.value.node_path); node->set_text(1, E.value.incoming_rpc == 0 ? "-" : itos(E.value.incoming_rpc)); - node->set_text(2, E.value.incoming_rset == 0 ? "-" : itos(E.value.incoming_rset)); - node->set_text(3, E.value.outgoing_rpc == 0 ? "-" : itos(E.value.outgoing_rpc)); - node->set_text(4, E.value.outgoing_rset == 0 ? "-" : itos(E.value.outgoing_rset)); + node->set_text(2, E.value.outgoing_rpc == 0 ? "-" : itos(E.value.outgoing_rpc)); } } @@ -91,14 +89,12 @@ void EditorNetworkProfiler::_clear_pressed() { } } -void EditorNetworkProfiler::add_node_frame_data(const DebuggerMarshalls::MultiplayerNodeInfo p_frame) { +void EditorNetworkProfiler::add_node_frame_data(const SceneDebugger::RPCNodeInfo p_frame) { if (!nodes_data.has(p_frame.node)) { nodes_data.insert(p_frame.node, p_frame); } else { nodes_data[p_frame.node].incoming_rpc += p_frame.incoming_rpc; - nodes_data[p_frame.node].incoming_rset += p_frame.incoming_rset; nodes_data[p_frame.node].outgoing_rpc += p_frame.outgoing_rpc; - nodes_data[p_frame.node].outgoing_rset += p_frame.outgoing_rset; } if (frame_delay->is_stopped()) { @@ -174,7 +170,7 @@ EditorNetworkProfiler::EditorNetworkProfiler() { counters_display->set_v_size_flags(SIZE_EXPAND_FILL); counters_display->set_hide_folding(true); counters_display->set_hide_root(true); - counters_display->set_columns(5); + counters_display->set_columns(3); counters_display->set_column_titles_visible(true); counters_display->set_column_title(0, TTR("Node")); counters_display->set_column_expand(0, true); @@ -184,18 +180,10 @@ EditorNetworkProfiler::EditorNetworkProfiler() { counters_display->set_column_expand(1, false); counters_display->set_column_clip_content(1, true); counters_display->set_column_custom_minimum_width(1, 120 * EDSCALE); - counters_display->set_column_title(2, TTR("Incoming RSET")); + counters_display->set_column_title(2, TTR("Outgoing RPC")); counters_display->set_column_expand(2, false); counters_display->set_column_clip_content(2, true); counters_display->set_column_custom_minimum_width(2, 120 * EDSCALE); - counters_display->set_column_title(3, TTR("Outgoing RPC")); - counters_display->set_column_expand(3, false); - counters_display->set_column_clip_content(3, true); - counters_display->set_column_custom_minimum_width(3, 120 * EDSCALE); - counters_display->set_column_title(4, TTR("Outgoing RSET")); - counters_display->set_column_expand(4, false); - counters_display->set_column_clip_content(4, true); - counters_display->set_column_custom_minimum_width(4, 120 * EDSCALE); add_child(counters_display); frame_delay = memnew(Timer); diff --git a/editor/debugger/editor_network_profiler.h b/editor/debugger/editor_network_profiler.h index 320dd2a826..3e95eb0de6 100644 --- a/editor/debugger/editor_network_profiler.h +++ b/editor/debugger/editor_network_profiler.h @@ -31,7 +31,7 @@ #ifndef EDITORNETWORKPROFILER_H #define EDITORNETWORKPROFILER_H -#include "core/debugger/debugger_marshalls.h" +#include "scene/debugger/scene_debugger.h" #include "scene/gui/box_container.h" #include "scene/gui/button.h" #include "scene/gui/label.h" @@ -50,7 +50,7 @@ private: Timer *frame_delay; - Map<ObjectID, DebuggerMarshalls::MultiplayerNodeInfo> nodes_data; + Map<ObjectID, SceneDebugger::RPCNodeInfo> nodes_data; void _update_frame(); @@ -62,7 +62,7 @@ protected: static void _bind_methods(); public: - void add_node_frame_data(const DebuggerMarshalls::MultiplayerNodeInfo p_frame); + void add_node_frame_data(const SceneDebugger::RPCNodeInfo p_frame); void set_bandwidth(int p_incoming, int p_outgoing); bool is_profiling(); diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index 6aedfa6ccb..28e8edb26e 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -36,7 +36,6 @@ #include "core/io/marshalls.h" #include "core/string/ustring.h" #include "core/version.h" -#include "core/version_hash.gen.h" #include "editor/debugger/debug_adapter/debug_adapter_protocol.h" #include "editor/debugger/editor_network_profiler.h" #include "editor/debugger/editor_performance_profiler.h" @@ -64,6 +63,7 @@ #include "scene/gui/texture_button.h" #include "scene/gui/tree.h" #include "scene/resources/packed_scene.h" +#include "servers/debugger/servers_debugger.h" #include "servers/display_server.h" using CameraOverride = EditorDebuggerNode::CameraOverride; @@ -128,6 +128,7 @@ void ScriptEditorDebugger::debug_continue() { _clear_execution(); _put_msg("continue", Array()); + _put_msg("servers:foreground", Array()); } void ScriptEditorDebugger::update_tabs() { @@ -278,7 +279,7 @@ void ScriptEditorDebugger::_remote_object_property_updated(ObjectID p_id, const } void ScriptEditorDebugger::_video_mem_request() { - _put_msg("core:memory", Array()); + _put_msg("servers:memory", Array()); } void ScriptEditorDebugger::_video_mem_export() { @@ -344,15 +345,15 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da if (id.is_valid()) { emit_signal(SNAME("remote_object_updated"), id); } - } else if (p_msg == "memory:usage") { + } else if (p_msg == "servers:memory_usage") { vmem_tree->clear(); TreeItem *root = vmem_tree->create_item(); - DebuggerMarshalls::ResourceUsage usage; + ServersDebugger::ResourceUsage usage; usage.deserialize(p_data); uint64_t total = 0; - for (const DebuggerMarshalls::ResourceInfo &E : usage.infos) { + for (const ServersDebugger::ResourceInfo &E : usage.infos) { TreeItem *it = vmem_tree->create_item(root); String type = E.type; int bytes = E.vram; @@ -445,7 +446,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da performance_profiler->add_profile_frame(frame_data); } else if (p_msg == "visual:profile_frame") { - DebuggerMarshalls::VisualProfilerFrame frame; + ServersDebugger::VisualProfilerFrame frame; frame.deserialize(p_data); EditorVisualProfiler::Metric metric; @@ -592,13 +593,13 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da } else if (p_msg == "servers:function_signature") { // Cache a profiler signature. - DebuggerMarshalls::ScriptFunctionSignature sig; + ServersDebugger::ScriptFunctionSignature sig; sig.deserialize(p_data); profiler_signature[sig.id] = sig.name; } else if (p_msg == "servers:profile_frame" || p_msg == "servers:profile_total") { EditorProfiler::Metric metric; - DebuggerMarshalls::ServersProfilerFrame frame; + ServersDebugger::ServersProfilerFrame frame; frame.deserialize(p_data); metric.valid = true; metric.frame_number = frame.frame_number; @@ -642,7 +643,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da } for (int i = 0; i < frame.servers.size(); i++) { - const DebuggerMarshalls::ServerInfo &srv = frame.servers[i]; + const ServersDebugger::ServerInfo &srv = frame.servers[i]; EditorProfiler::Metric::Category c; const String name = srv.name; c.name = name.capitalize(); @@ -709,14 +710,14 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da profiler->add_frame_metric(metric, true); } - } else if (p_msg == "network:profile_frame") { - DebuggerMarshalls::NetworkProfilerFrame frame; + } else if (p_msg == "multiplayer:rpc") { + SceneDebugger::RPCProfilerFrame frame; frame.deserialize(p_data); for (int i = 0; i < frame.infos.size(); i++) { network_profiler->add_node_frame_data(frame.infos[i]); } - } else if (p_msg == "network:bandwidth") { + } else if (p_msg == "multiplayer:bandwidth") { ERR_FAIL_COND(p_data.size() < 2); network_profiler->set_bandwidth(p_data[0], p_data[1]); @@ -833,6 +834,9 @@ void ScriptEditorDebugger::_notification(int p_what) { msg.push_back(cam->get_far()); _put_msg("scene:override_camera_3D:transform", msg); } + if (breaked) { + _put_msg("servers:draw", Array()); + } } const uint64_t until = OS::get_singleton()->get_ticks_msec() + 20; @@ -971,7 +975,8 @@ void ScriptEditorDebugger::_profiler_activate(bool p_enable, int p_type) { data.push_back(p_enable); switch (p_type) { case PROFILER_NETWORK: - _put_msg("profiler:network", data); + _put_msg("profiler:multiplayer", data); + _put_msg("profiler:rpc", data); break; case PROFILER_VISUAL: _put_msg("profiler:visual", data); @@ -1543,19 +1548,10 @@ void ScriptEditorDebugger::_item_menu_id_pressed(int p_option) { const int line_number = file_line_number[1].to_int(); // Construct a GitHub repository URL and open it in the user's default web browser. - if (String(VERSION_HASH).length() >= 1) { - // Git commit hash information available; use it for greater accuracy, including for development versions. - OS::get_singleton()->shell_open(vformat("https://github.com/godotengine/godot/blob/%s/%s#L%d", - VERSION_HASH, - file, - line_number)); - } else { - // Git commit hash information unavailable; fall back to tagged releases. - OS::get_singleton()->shell_open(vformat("https://github.com/godotengine/godot/blob/%s-stable/%s#L%d", - VERSION_NUMBER, - file, - line_number)); - } + // If the commit hash is available, use it for greater accuracy. Otherwise fall back to tagged release. + String git_ref = String(VERSION_HASH).is_empty() ? String(VERSION_NUMBER) + "-stable" : String(VERSION_HASH); + OS::get_singleton()->shell_open(vformat("https://github.com/godotengine/godot/blob/%s/%s#L%d", + git_ref, file, line_number)); } break; case ACTION_DELETE_BREAKPOINT: { const TreeItem *selected = breakpoints_tree->get_selected(); diff --git a/editor/editor_about.cpp b/editor/editor_about.cpp index 54377971c6..4309f55a2b 100644 --- a/editor/editor_about.cpp +++ b/editor/editor_about.cpp @@ -29,13 +29,12 @@ /*************************************************************************/ #include "editor_about.h" -#include "editor_node.h" #include "core/authors.gen.h" #include "core/donors.gen.h" #include "core/license.gen.h" #include "core/version.h" -#include "core/version_hash.gen.h" +#include "editor_node.h" // The metadata key used to store and retrieve the version text to copy to the clipboard. static const String META_TEXT_TO_COPY = "text_to_copy"; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 10ce7228e0..4b2f1c5104 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -47,7 +47,6 @@ #include "core/string/print_string.h" #include "core/string/translation.h" #include "core/version.h" -#include "core/version_hash.gen.h" #include "main/main.h" #include "scene/3d/importer_mesh_instance_3d.h" #include "scene/gui/center_container.h" diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index f79b5027cb..e35af6dd64 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -886,15 +886,13 @@ void Node3DEditorViewport::_update_name() { view_menu->reset_size(); } -void Node3DEditorViewport::_compute_edit(const Point2 &p_point, const bool p_auto_center) { +void Node3DEditorViewport::_compute_edit(const Point2 &p_point) { _edit.original_local = spatial_editor->are_local_coords_enabled(); _edit.click_ray = _get_ray(p_point); _edit.click_ray_pos = _get_ray_pos(p_point); _edit.plane = TRANSFORM_VIEW; - if (p_auto_center) { - _edit.center = spatial_editor->get_gizmo_transform().origin; - } spatial_editor->update_transform_gizmo(); + _edit.center = spatial_editor->get_gizmo_transform().origin; Node3D *selected = spatial_editor->get_single_selected_node(); Node3DEditorSelectedItem *se = selected ? editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(selected) : nullptr; @@ -1366,7 +1364,6 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { } } - _edit.center = spatial_editor->get_gizmo_target_center(); Ref<InputEventMouseButton> b = p_event; if (b.is_valid()) { @@ -1471,6 +1468,8 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { } break; case MouseButton::LEFT: { if (b->is_pressed()) { + clicked_wants_append = b->is_shift_pressed(); + if (_edit.mode != TRANSFORM_NONE && _edit.instant) { commit_transform(); break; // just commit the edit, stop processing the event so we don't deselect the object @@ -1600,8 +1599,6 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { //clicking is always deferred to either move or release - clicked_wants_append = b->is_shift_pressed(); - if (clicked.is_null()) { //default to regionselect cursor.region_select = true; @@ -1730,6 +1727,12 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { _edit.mode = TRANSFORM_TRANSLATE; } + // enable region-select if nothing has been selected yet or multi-select (shift key) is active + if (movement_threshold_passed && (get_selected_count() == 0 || clicked_wants_append)) { + cursor.region_select = true; + cursor.region_begin = _edit.original_mouse_pos; + } + if (cursor.region_select) { cursor.region_end = m->get_position(); surface->update(); @@ -3367,7 +3370,7 @@ void Node3DEditorViewport::update_transform_gizmo_view() { Transform3D xform = spatial_editor->get_gizmo_transform(); - const Transform3D camera_xform = camera->get_transform(); + Transform3D camera_xform = camera->get_transform(); if (xform.origin.is_equal_approx(camera_xform.origin)) { for (int i = 0; i < 3; i++) { @@ -3386,63 +3389,11 @@ void Node3DEditorViewport::update_transform_gizmo_view() { const Vector3 camz = -camera_xform.get_basis().get_axis(2).normalized(); const Vector3 camy = -camera_xform.get_basis().get_axis(1).normalized(); const Plane p = Plane(camz, camera_xform.origin); - const real_t gizmo_d = CLAMP(Math::abs(p.distance_to(xform.origin)), camera->get_near() * 2, camera->get_far() / 2); + const real_t gizmo_d = MAX(Math::abs(p.distance_to(xform.origin)), CMP_EPSILON); const real_t d0 = camera->unproject_position(camera_xform.origin + camz * gizmo_d).y; const real_t d1 = camera->unproject_position(camera_xform.origin + camz * gizmo_d + camy).y; const real_t dd = MAX(Math::abs(d0 - d1), CMP_EPSILON); - // This code ensures the gizmo stays on the screen. This includes if - // the gizmo would otherwise be behind the camera, to the sides of - // the camera, too close to the edge of the screen, too close to - // the camera, or too far away from the camera. First we calculate - // where the gizmo would go on screen, then we put it there. - const Vector3 object_position = spatial_editor->get_gizmo_target_center(); - Vector2 gizmo_screen_position = camera->unproject_position(object_position); - const Vector2 viewport_size = viewport->get_size(); - // We would use "camera.is_position_behind(parent_translation)" instead of dot, - // except that it also accounts for the near clip plane, which we don't want. - const bool is_in_front = camera_xform.basis.get_column(2).dot(object_position - camera_xform.origin) < 0; - const bool is_in_viewport = is_in_front && Rect2(Vector2(0, 0), viewport_size).has_point(gizmo_screen_position); - if (spatial_editor->is_keep_gizmo_onscreen_enabled()) { - if (!spatial_editor->is_gizmo_visible() || is_in_viewport) { - // In this case, the gizmo is either not visible, or in the viewport - // already, so we should hide the offscreen line. - gizmo_offscreen_line->hide(); - } else { - // In this case, the point is not "normally" on screen, and - // it should be placed in the center. - const Vector2 half_viewport_size = viewport_size / 2; - gizmo_screen_position = half_viewport_size; - // The rest of this is for drawing the offscreen line. - // One point goes in the center of the viewport. - // Calculate where to put the other point of the line. - Vector2 unprojected_position = camera->unproject_position(object_position); - if (!is_in_front) { - // When the object is behind, we need to flip and grow the line. - unprojected_position -= half_viewport_size; - unprojected_position = unprojected_position.normalized() * -half_viewport_size.length_squared(); - unprojected_position += half_viewport_size; - } - gizmo_offscreen_line->point1 = half_viewport_size; - gizmo_offscreen_line->point2 = unprojected_position; - gizmo_offscreen_line->update(); - gizmo_offscreen_line->show(); - } - // Update the gizmo's position using what we calculated. - xform.origin = camera->project_position(gizmo_screen_position, gizmo_d); - } else { - // In this case, the user does not want the gizmo to be - // kept on screen, so we should hide the offscreen line, - // and just use the gizmo's unmodified position. - gizmo_offscreen_line->hide(); - if (is_in_viewport) { - xform.origin = camera->project_position(gizmo_screen_position, gizmo_d); - } else { - xform.origin = object_position; - } - } - spatial_editor->set_gizmo_transform(xform); - const real_t gizmo_size = EditorSettings::get_singleton()->get("editors/3d/manipulator_gizmo_size"); // At low viewport heights, multiply the gizmo scale based on the viewport height. // This prevents the gizmo from growing very large and going outside the viewport. @@ -3451,6 +3402,7 @@ void Node3DEditorViewport::update_transform_gizmo_view() { (gizmo_size / Math::abs(dd)) * MAX(1, EDSCALE) * MIN(viewport_base_height, subviewport_container->get_size().height) / viewport_base_height / subviewport_container->get_stretch_shrink(); + Vector3 scale = Vector3(1, 1, 1) * gizmo_scale; // if the determinant is zero, we should disable the gizmo from being rendered // this prevents supplying bad values to the renderer and then having to filter it out again @@ -3472,7 +3424,7 @@ void Node3DEditorViewport::update_transform_gizmo_view() { if (xform.basis.get_axis(i).normalized().dot(xform.basis.get_axis((i + 1) % 3).normalized()) < 1.0) { axis_angle = axis_angle.looking_at(xform.basis.get_axis(i).normalized(), xform.basis.get_axis((i + 1) % 3).normalized()); } - axis_angle.basis *= gizmo_scale; + axis_angle.basis.scale(scale); axis_angle.origin = xform.origin; RenderingServer::get_singleton()->instance_set_transform(move_gizmo_instance[i], axis_angle); RenderingServer::get_singleton()->instance_set_visible(move_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE)); @@ -3495,7 +3447,7 @@ void Node3DEditorViewport::update_transform_gizmo_view() { // Rotation white outline xform.orthonormalize(); - xform.basis *= gizmo_scale; + xform.basis.scale(scale); RenderingServer::get_singleton()->instance_set_transform(rotate_gizmo_instance[3], xform); RenderingServer::get_singleton()->instance_set_visible(rotate_gizmo_instance[3], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE)); } @@ -4077,7 +4029,7 @@ void Node3DEditorViewport::drop_data_fw(const Point2 &p_point, const Variant &p_ void Node3DEditorViewport::begin_transform(TransformMode p_mode, bool instant) { if (get_selected_count() > 0) { _edit.mode = p_mode; - _compute_edit(_edit.mouse_pos, false); + _compute_edit(_edit.mouse_pos); _edit.instant = instant; _edit.snap = spatial_editor->is_snap_enabled(); } @@ -4480,14 +4432,15 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, Edito zoom_indicator_delay = 0.0; spatial_editor = p_spatial_editor; - subviewport_container = memnew(SubViewportContainer); - subviewport_container->set_stretch(true); - add_child(subviewport_container); - subviewport_container->set_anchors_and_offsets_preset(Control::PRESET_WIDE); + SubViewportContainer *c = memnew(SubViewportContainer); + subviewport_container = c; + c->set_stretch(true); + add_child(c); + c->set_anchors_and_offsets_preset(Control::PRESET_WIDE); viewport = memnew(SubViewport); viewport->set_disable_input(true); - subviewport_container->add_child(viewport); + c->add_child(viewport); surface = memnew(Control); surface->set_drag_forwarding(this); add_child(surface); @@ -4500,9 +4453,6 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, Edito camera->make_current(); surface->set_focus_mode(FOCUS_ALL); - gizmo_offscreen_line = memnew(GizmoOffScreenLine); - subviewport_container->add_child(gizmo_offscreen_line); - VBoxContainer *vbox = memnew(VBoxContainer); surface->add_child(vbox); vbox->set_offset(SIDE_LEFT, 10 * EDSCALE); @@ -5127,7 +5077,6 @@ void Node3DEditor::update_transform_gizmo() { gizmo.visible = count > 0; gizmo.transform.origin = (count > 0) ? gizmo_center / count : Vector3(); gizmo.transform.basis = (count == 1) ? gizmo_basis : Basis(); - gizmo.target_center = gizmo_center / count; for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) { viewports[i]->update_transform_gizmo_view(); @@ -5280,7 +5229,6 @@ Dictionary Node3DEditor::get_state() const { d["show_grid"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_GRID)); d["show_origin"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_ORIGIN)); - d["keep_gizmo_onscreen"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_KEEP_GIZMO_ONSCREEN)); d["fov"] = get_fov(); d["znear"] = get_znear(); d["zfar"] = get_zfar(); @@ -5405,13 +5353,6 @@ void Node3DEditor::set_state(const Dictionary &p_state) { RenderingServer::get_singleton()->instance_set_visible(origin_instance, use); } } - if (d.has("keep_gizmo_onscreen")) { - bool use = d["keep_gizmo_onscreen"]; - - if (use != view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_KEEP_GIZMO_ONSCREEN))) { - _menu_item_pressed(MENU_KEEP_GIZMO_ONSCREEN); - } - } if (d.has("gizmos_status")) { Dictionary gizmos_status = d["gizmos_status"]; @@ -5765,13 +5706,7 @@ void Node3DEditor::_menu_item_pressed(int p_option) { _init_grid(); view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(p_option), grid_enabled); - } break; - case MENU_KEEP_GIZMO_ONSCREEN: { - keep_gizmo_onscreen = !view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(p_option)); - for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) { - get_editor_viewport(i)->update_transform_gizmo_view(); - } - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(p_option), keep_gizmo_onscreen); + } break; case MENU_VIEW_CAMERA_SETTINGS: { settings_dialog->popup_centered(settings_vbc->get_combined_minimum_size() + Size2(50, 50)); @@ -7756,14 +7691,12 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) { p->add_separator(); p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_origin", TTR("View Origin")), MENU_VIEW_ORIGIN); p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_grid", TTR("View Grid"), Key::NUMBERSIGN), MENU_VIEW_GRID); - p->add_check_shortcut(ED_SHORTCUT("spatial_editor/keep_gizmo_onscreen", TTR("Keep Gizmo On Screen"), KeyModifierMask::CMD + KeyModifierMask::ALT + Key::G), MENU_KEEP_GIZMO_ONSCREEN); p->add_separator(); p->add_shortcut(ED_SHORTCUT("spatial_editor/settings", TTR("Settings...")), MENU_VIEW_CAMERA_SETTINGS); p->set_item_checked(p->get_item_index(MENU_VIEW_ORIGIN), true); p->set_item_checked(p->get_item_index(MENU_VIEW_GRID), true); - p->set_item_checked(p->get_item_index(MENU_KEEP_GIZMO_ONSCREEN), true); p->connect("id_pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed)); diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index f14f8b90b9..bbe5615570 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -87,19 +87,6 @@ public: void set_viewport(Node3DEditorViewport *p_viewport); }; -class GizmoOffScreenLine : public Control { - GDCLASS(GizmoOffScreenLine, Control); - -public: - Vector2 point1; - Vector2 point2; - void _notification(int p_what) { - if (p_what == NOTIFICATION_DRAW && is_visible()) { - draw_line(point1, point2, Color(0.0f, 0.75f, 0.75f), 2); - } - } -}; - class Node3DEditorViewport : public Control { GDCLASS(Node3DEditorViewport, Control); friend class Node3DEditor; @@ -214,7 +201,6 @@ private: CheckBox *preview_camera; SubViewportContainer *subviewport_container; - GizmoOffScreenLine *gizmo_offscreen_line; MenuButton *view_menu; PopupMenu *display_submenu; @@ -251,7 +237,7 @@ private: }; void _update_name(); - void _compute_edit(const Point2 &p_point, const bool p_auto_center = true); + void _compute_edit(const Point2 &p_point); void _clear_selected(); void _select_clicked(bool p_allow_locked); ObjectID _select_ray(const Point2 &p_pos); @@ -521,35 +507,6 @@ class Node3DEditor : public VBoxContainer { public: static const unsigned int VIEWPORTS_COUNT = 4; - enum MenuOption { - MENU_GROUP_SELECTED, - MENU_KEEP_GIZMO_ONSCREEN, - MENU_LOCK_SELECTED, - MENU_SNAP_TO_FLOOR, - MENU_TOOL_LIST_SELECT, - MENU_TOOL_LOCAL_COORDS, - MENU_TOOL_MOVE, - MENU_TOOL_OVERRIDE_CAMERA, - MENU_TOOL_ROTATE, - MENU_TOOL_SCALE, - MENU_TOOL_SELECT, - MENU_TOOL_USE_SNAP, - MENU_TRANSFORM_CONFIGURE_SNAP, - MENU_TRANSFORM_DIALOG, - MENU_UNGROUP_SELECTED, - MENU_UNLOCK_SELECTED, - MENU_VIEW_CAMERA_SETTINGS, - MENU_VIEW_GIZMOS_3D_ICONS, - MENU_VIEW_GRID, - MENU_VIEW_ORIGIN, - MENU_VIEW_USE_1_VIEWPORT, - MENU_VIEW_USE_2_VIEWPORTS, - MENU_VIEW_USE_2_VIEWPORTS_ALT, - MENU_VIEW_USE_3_VIEWPORTS, - MENU_VIEW_USE_3_VIEWPORTS_ALT, - MENU_VIEW_USE_4_VIEWPORTS, - }; - enum ToolMode { TOOL_MODE_SELECT, TOOL_MODE_MOVE, @@ -568,6 +525,7 @@ public: TOOL_OPT_USE_SNAP, TOOL_OPT_OVERRIDE_CAMERA, TOOL_OPT_MAX + }; private: @@ -596,7 +554,6 @@ private: Camera3D::Projection grid_camera_last_update_perspective = Camera3D::PROJECTION_PERSPECTIVE; Vector3 grid_camera_last_update_position = Vector3(); - bool keep_gizmo_onscreen = true; Ref<ArrayMesh> move_gizmo[3], move_plane_gizmo[3], rotate_gizmo[4], scale_gizmo[3], scale_plane_gizmo[3], axis_gizmo[3]; Ref<StandardMaterial3D> gizmo_color[3]; Ref<StandardMaterial3D> plane_gizmo_color[3]; @@ -630,10 +587,37 @@ private: struct Gizmo { bool visible = false; real_t scale = 0; - Vector3 target_center; Transform3D transform; } gizmo; + enum MenuOption { + MENU_TOOL_SELECT, + MENU_TOOL_MOVE, + MENU_TOOL_ROTATE, + MENU_TOOL_SCALE, + MENU_TOOL_LIST_SELECT, + MENU_TOOL_LOCAL_COORDS, + MENU_TOOL_USE_SNAP, + MENU_TOOL_OVERRIDE_CAMERA, + MENU_TRANSFORM_CONFIGURE_SNAP, + MENU_TRANSFORM_DIALOG, + MENU_VIEW_USE_1_VIEWPORT, + MENU_VIEW_USE_2_VIEWPORTS, + MENU_VIEW_USE_2_VIEWPORTS_ALT, + MENU_VIEW_USE_3_VIEWPORTS, + MENU_VIEW_USE_3_VIEWPORTS_ALT, + MENU_VIEW_USE_4_VIEWPORTS, + MENU_VIEW_ORIGIN, + MENU_VIEW_GRID, + MENU_VIEW_GIZMOS_3D_ICONS, + MENU_VIEW_CAMERA_SETTINGS, + MENU_LOCK_SELECTED, + MENU_UNLOCK_SELECTED, + MENU_GROUP_SELECTED, + MENU_UNGROUP_SELECTED, + MENU_SNAP_TO_FLOOR + }; + Button *tool_button[TOOL_MAX]; Button *tool_option_button[TOOL_OPT_MAX]; @@ -792,10 +776,7 @@ public: float get_zfar() const { return settings_zfar->get_value(); } float get_fov() const { return settings_fov->get_value(); } - Vector3 get_gizmo_target_center() const { return gizmo.target_center; } Transform3D get_gizmo_transform() const { return gizmo.transform; } - void set_gizmo_transform(const Transform3D &p_transform) { gizmo.transform = p_transform; } - bool is_keep_gizmo_onscreen_enabled() const { return keep_gizmo_onscreen; } bool is_gizmo_visible() const; ToolMode get_tool_mode() const { return tool_mode; } diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index cfb42c0741..57e47a15fd 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -40,7 +40,6 @@ #include "core/os/os.h" #include "core/string/translation.h" #include "core/version.h" -#include "core/version_hash.gen.h" #include "editor/editor_vcs_interface.h" #include "editor_scale.h" #include "editor_settings.h" diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 0e362b13c6..a4f9563840 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -1560,7 +1560,7 @@ void SceneTreeDock::perform_node_renames(Node *p_base, Map<Node *, NodePath> *p_ for (int i = 0; i < anim->get_track_count(); i++) { NodePath track_np = anim->track_get_path(i); - Node *n = root->get_node(track_np); + Node *n = root->get_node_or_null(track_np); if (!n) { continue; } |