diff options
Diffstat (limited to 'editor')
47 files changed, 2971 insertions, 469 deletions
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 8807a01f64..9f6fa74dd3 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -109,9 +109,17 @@ public: ERR_FAIL_COND_V(key == -1, false); String name = p_name; - if (name == "time") { + if (name == "time" || name == "frame") { float new_time = p_value; + + if (name == "frame") { + float fps = animation->get_step(); + if (fps > 0) { + fps = 1.0 / fps; + } + new_time /= fps; + } if (new_time == key_ofs) return true; @@ -413,6 +421,13 @@ public: if (name == "time") { r_ret = key_ofs; return true; + } else if (name == "frame") { + float fps = animation->get_step(); + if (fps > 0) { + fps = 1.0 / fps; + } + r_ret = key_ofs * fps; + return true; } else if (name == "easing") { r_ret = animation->track_get_key_transition(track, key); return true; @@ -527,7 +542,12 @@ public: int key = animation->track_find_key(track, key_ofs, true); ERR_FAIL_COND(key == -1); - p_list->push_back(PropertyInfo(Variant::REAL, "time", PROPERTY_HINT_RANGE, "0," + rtos(animation->get_length()) + ",0.01")); + if (use_fps && animation->get_step() > 0) { + float max_frame = animation->get_length() / animation->get_step(); + p_list->push_back(PropertyInfo(Variant::REAL, "frame", PROPERTY_HINT_RANGE, "0," + rtos(max_frame) + ",0.01")); + } else { + p_list->push_back(PropertyInfo(Variant::REAL, "time", PROPERTY_HINT_RANGE, "0," + rtos(animation->get_length()) + ",0.01")); + } switch (animation->track_get_type(track)) { @@ -648,6 +668,7 @@ public: PropertyInfo hint; NodePath base; + bool use_fps; void notify_change() { @@ -658,7 +679,13 @@ public: return root_path; } + void set_use_fps(bool p_enable) { + use_fps = p_enable; + _change_notify(); + } + AnimationTrackKeyEdit() { + use_fps = false; key_ofs = 0; track = -1; setting = false; @@ -690,6 +717,9 @@ void AnimationTimelineEdit::_anim_length_changed(double p_new_len) { return; p_new_len = MAX(0.001, p_new_len); + if (use_fps && animation->get_step() > 0) { + p_new_len *= animation->get_step(); + } editing = true; undo_redo->create_action(TTR("Change Animation Length")); @@ -887,24 +917,53 @@ void AnimationTimelineEdit::_notification(int p_what) { decimals = 0; } - for (int i = 0; i < zoomw; i++) { + if (use_fps) { + + float step_size = animation->get_step(); + if (step_size > 0) { + + int prev_frame_ofs = -10000000; - float pos = get_value() + double(i) / scale; - float prev = get_value() + (double(i) - 1.0) / scale; + for (int i = 0; i < zoomw; i++) { - int sc = int(Math::floor(pos * SC_ADJ)); - int prev_sc = int(Math::floor(prev * SC_ADJ)); - bool sub = (sc % SC_ADJ); + float pos = get_value() + double(i) / scale; + float prev = get_value() + (double(i) - 1.0) / scale; - if ((sc / step) != (prev_sc / step) || (prev_sc < 0 && sc >= 0)) { + int frame = pos / step_size; + int prev_frame = prev / step_size; - int scd = sc < 0 ? prev_sc : sc; - draw_line(Point2(get_name_limit() + i, 0), Point2(get_name_limit() + i, h), linecolor); - draw_string(font, Point2(get_name_limit() + i + 3, (h - font->get_height()) / 2 + font->get_ascent()).floor(), String::num((scd - (scd % step)) / double(SC_ADJ), decimals), sub ? color_time_dec : color_time_sec, zoomw - i); + bool sub = Math::floor(prev) == Math::floor(pos); + + if (frame != prev_frame && i >= prev_frame_ofs) { + + draw_line(Point2(get_name_limit() + i, 0), Point2(get_name_limit() + i, h), linecolor, Math::round(EDSCALE)); + + draw_string(font, Point2(get_name_limit() + i + 3 * EDSCALE, (h - font->get_height()) / 2 + font->get_ascent()).floor(), itos(frame), sub ? color_time_dec : color_time_sec, zoomw - i); + prev_frame_ofs = i + font->get_string_size(itos(frame)).x + 5 * EDSCALE; + } + } + } + + } else { + for (int i = 0; i < zoomw; i++) { + + float pos = get_value() + double(i) / scale; + float prev = get_value() + (double(i) - 1.0) / scale; + + int sc = int(Math::floor(pos * SC_ADJ)); + int prev_sc = int(Math::floor(prev * SC_ADJ)); + bool sub = (sc % SC_ADJ); + + if ((sc / step) != (prev_sc / step) || (prev_sc < 0 && sc >= 0)) { + + int scd = sc < 0 ? prev_sc : sc; + draw_line(Point2(get_name_limit() + i, 0), Point2(get_name_limit() + i, h), linecolor, Math::round(EDSCALE)); + draw_string(font, Point2(get_name_limit() + i + 3, (h - font->get_height()) / 2 + font->get_ascent()).floor(), String::num((scd - (scd % step)) / double(SC_ADJ), decimals), sub ? color_time_dec : color_time_sec, zoomw - i); + } } } - draw_line(Vector2(0, get_size().height), get_size(), linecolor); + draw_line(Vector2(0, get_size().height), get_size(), linecolor, Math::round(EDSCALE)); } } @@ -961,7 +1020,11 @@ void AnimationTimelineEdit::update_values() { return; editing = true; - length->set_value(animation->get_length()); + if (use_fps && animation->get_step() > 0) { + length->set_value(animation->get_length() / animation->get_step()); + } else { + length->set_value(animation->get_length()); + } loop->set_pressed(animation->has_loop()); editing = false; } @@ -978,7 +1041,7 @@ void AnimationTimelineEdit::_play_position_draw() { if (px >= get_name_limit() && px < (play_position->get_size().width - get_buttons_width())) { Color color = get_color("accent_color", "Editor"); - play_position->draw_line(Point2(px, 0), Point2(px, h), color); + play_position->draw_line(Point2(px, 0), Point2(px, h), color, Math::round(EDSCALE)); } } @@ -1046,6 +1109,15 @@ void AnimationTimelineEdit::_gui_input(const Ref<InputEvent> &p_event) { } } +void AnimationTimelineEdit::set_use_fps(bool p_use_fps) { + use_fps = p_use_fps; + update_values(); + update(); +} +bool AnimationTimelineEdit::is_using_fps() const { + return use_fps; +} + void AnimationTimelineEdit::set_hscroll(HScrollBar *p_hscroll) { hscroll = p_hscroll; @@ -1072,6 +1144,7 @@ void AnimationTimelineEdit::_bind_methods() { AnimationTimelineEdit::AnimationTimelineEdit() { + use_fps = false; editing = false; name_limit = 150; zoom = NULL; @@ -1099,7 +1172,7 @@ AnimationTimelineEdit::AnimationTimelineEdit() { len_hb->add_child(time_icon); length = memnew(EditorSpinSlider); length->set_min(0.001); - length->set_max(3600); + length->set_max(36000); length->set_step(0.01); length->set_allow_greater(true); length->set_custom_minimum_size(Vector2(70 * EDSCALE, 0)); @@ -1237,7 +1310,7 @@ void AnimationTrackEdit::_notification(int p_what) { string_pos = string_pos.floor(); draw_string(font, string_pos, text, text_color, limit - ofs - hsep); - draw_line(Point2(limit, 0), Point2(limit, get_size().height), linecolor); + draw_line(Point2(limit, 0), Point2(limit, get_size().height), linecolor, Math::round(EDSCALE)); } // KEYFAMES // @@ -1297,7 +1370,7 @@ void AnimationTrackEdit::_notification(int p_what) { Ref<Texture> down_icon = get_icon("select_arrow", "Tree"); - draw_line(Point2(ofs, 0), Point2(ofs, get_size().height), linecolor); + draw_line(Point2(ofs, 0), Point2(ofs, get_size().height), linecolor, Math::round(EDSCALE)); ofs += hsep; { @@ -1344,7 +1417,7 @@ void AnimationTrackEdit::_notification(int p_what) { } ofs += down_icon->get_width(); - draw_line(Point2(ofs + hsep * 0.5, 0), Point2(ofs + hsep * 0.5, get_size().height), linecolor); + draw_line(Point2(ofs + hsep * 0.5, 0), Point2(ofs + hsep * 0.5, get_size().height), linecolor, Math::round(EDSCALE)); ofs += hsep; } @@ -1377,7 +1450,7 @@ void AnimationTrackEdit::_notification(int p_what) { } ofs += down_icon->get_width(); - draw_line(Point2(ofs + hsep * 0.5, 0), Point2(ofs + hsep * 0.5, get_size().height), linecolor); + draw_line(Point2(ofs + hsep * 0.5, 0), Point2(ofs + hsep * 0.5, get_size().height), linecolor, Math::round(EDSCALE)); ofs += hsep; } @@ -1410,7 +1483,7 @@ void AnimationTrackEdit::_notification(int p_what) { } ofs += down_icon->get_width(); - draw_line(Point2(ofs + hsep * 0.5, 0), Point2(ofs + hsep * 0.5, get_size().height), linecolor); + draw_line(Point2(ofs + hsep * 0.5, 0), Point2(ofs + hsep * 0.5, get_size().height), linecolor, Math::round(EDSCALE)); ofs += hsep; } @@ -1428,17 +1501,17 @@ void AnimationTrackEdit::_notification(int p_what) { } if (in_group) { - draw_line(Vector2(timeline->get_name_limit(), get_size().height), get_size(), linecolor); + draw_line(Vector2(timeline->get_name_limit(), get_size().height), get_size(), linecolor, Math::round(EDSCALE)); } else { - draw_line(Vector2(0, get_size().height), get_size(), linecolor); + draw_line(Vector2(0, get_size().height), get_size(), linecolor, Math::round(EDSCALE)); } if (dropping_at != 0) { Color drop_color = get_color("accent_color", "Editor"); if (dropping_at < 0) { - draw_line(Vector2(0, 0), Vector2(get_size().width, 0), drop_color); + draw_line(Vector2(0, 0), Vector2(get_size().width, 0), drop_color, Math::round(EDSCALE)); } else { - draw_line(Vector2(0, get_size().height), get_size(), drop_color); + draw_line(Vector2(0, get_size().height), get_size(), drop_color, Math::round(EDSCALE)); } } } @@ -1487,7 +1560,7 @@ void AnimationTrackEdit::draw_key_link(int p_index, float p_pixels_sec, int p_x, int from_x = MAX(p_x, p_clip_left); int to_x = MIN(p_next_x, p_clip_right); - draw_line(Point2(from_x + 1, get_size().height / 2), Point2(to_x, get_size().height / 2), color, 2); + draw_line(Point2(from_x + 1, get_size().height / 2), Point2(to_x, get_size().height / 2), color, Math::round(2 * EDSCALE)); } void AnimationTrackEdit::draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) { @@ -1667,7 +1740,7 @@ void AnimationTrackEdit::_play_position_draw() { if (px >= timeline->get_name_limit() && px < (get_size().width - timeline->get_buttons_width())) { Color color = get_color("accent_color", "Editor"); - play_position->draw_line(Point2(px, 0), Point2(px, h), color); + play_position->draw_line(Point2(px, 0), Point2(px, h), color, Math::round(EDSCALE)); } } @@ -2370,9 +2443,9 @@ void AnimationTrackEditGroup::_notification(int p_what) { Color linecolor = color; linecolor.a = 0.2; - draw_line(Point2(), Point2(get_size().width, 0), linecolor); - draw_line(Point2(timeline->get_name_limit(), 0), Point2(timeline->get_name_limit(), get_size().height), linecolor); - draw_line(Point2(get_size().width - timeline->get_buttons_width(), 0), Point2(get_size().width - timeline->get_buttons_width(), get_size().height), linecolor); + draw_line(Point2(), Point2(get_size().width, 0), linecolor, Math::round(EDSCALE)); + draw_line(Point2(timeline->get_name_limit(), 0), Point2(timeline->get_name_limit(), get_size().height), linecolor, Math::round(EDSCALE)); + draw_line(Point2(get_size().width - timeline->get_buttons_width(), 0), Point2(get_size().width - timeline->get_buttons_width(), get_size().height), linecolor, Math::round(EDSCALE)); int ofs = 0; draw_texture(icon, Point2(ofs, int(get_size().height - icon->get_height()) / 2)); @@ -2383,7 +2456,7 @@ void AnimationTrackEditGroup::_notification(int p_what) { if (px >= timeline->get_name_limit() && px < (get_size().width - timeline->get_buttons_width())) { Color accent = get_color("accent_color", "Editor"); - draw_line(Point2(px, 0), Point2(px, get_size().height), accent); + draw_line(Point2(px, 0), Point2(px, get_size().height), accent, Math::round(EDSCALE)); } } } @@ -2462,10 +2535,12 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim) { hscroll->show(); edit->set_disabled(false); step->set_block_signals(true); - step->set_value(animation->get_step()); + + _update_step_spinbox(); step->set_block_signals(false); step->set_read_only(false); snap->set_disabled(false); + snap_mode->set_disabled(false); } else { hscroll->hide(); edit->set_disabled(true); @@ -2474,6 +2549,7 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim) { step->set_block_signals(false); step->set_read_only(true); snap->set_disabled(true); + snap_mode->set_disabled(true); } } @@ -2518,6 +2594,43 @@ void AnimationTrackEditor::update_keying() { bool AnimationTrackEditor::has_keying() const { return keying; } +Dictionary AnimationTrackEditor::get_state() const { + Dictionary state; + state["fps_mode"] = timeline->is_using_fps(); + state["zoom"] = zoom->get_value(); + state["offset"] = timeline->get_value(); + state["v_scroll"] = scroll->get_v_scrollbar()->get_value(); + return state; +} +void AnimationTrackEditor::set_state(const Dictionary &p_state) { + if (p_state.has("fps_mode")) { + bool fps_mode = p_state["fps_mode"]; + if (fps_mode) { + snap_mode->select(1); + } else { + snap_mode->select(0); + } + _snap_mode_changed(snap_mode->get_selected()); + } else { + snap_mode->select(0); + _snap_mode_changed(snap_mode->get_selected()); + } + if (p_state.has("zoom")) { + zoom->set_value(p_state["zoom"]); + } else { + zoom->set_value(1.0); + } + if (p_state.has("offset")) { + timeline->set_value(p_state["offset"]); + } else { + timeline->set_value(0); + } + if (p_state.has("v_scroll")) { + scroll->get_v_scrollbar()->set_value(p_state["v_scroll"]); + } else { + scroll->get_v_scrollbar()->set_value(0); + } +} void AnimationTrackEditor::cleanup() { set_animation(Ref<Animation>()); @@ -3417,6 +3530,34 @@ void AnimationTrackEditor::_animation_changed() { call_deferred("_animation_update"); } +void AnimationTrackEditor::_snap_mode_changed(int p_mode) { + + timeline->set_use_fps(p_mode == 1); + if (key_edit) { + key_edit->set_use_fps(p_mode == 1); + } + _update_step_spinbox(); +} + +void AnimationTrackEditor::_update_step_spinbox() { + if (!animation.is_valid()) { + return; + } + step->set_block_signals(true); + + if (timeline->is_using_fps()) { + if (animation->get_step() == 0) { + step->set_value(0); + } else { + step->set_value(1.0 / animation->get_step()); + } + + } else { + step->set_value(animation->get_step()); + } + + step->set_block_signals(false); +} void AnimationTrackEditor::_animation_update() { timeline->update(); @@ -3454,9 +3595,7 @@ void AnimationTrackEditor::_animation_update() { bezier_edit->update(); - step->set_block_signals(true); - step->set_value(animation->get_step()); - step->set_block_signals(false); + _update_step_spinbox(); animation_changing_awaiting_update = false; } @@ -3497,12 +3636,18 @@ void AnimationTrackEditor::_update_scroll(double) { void AnimationTrackEditor::_update_step(double p_new_step) { undo_redo->create_action(TTR("Change Animation Step")); - undo_redo->add_do_method(animation.ptr(), "set_step", p_new_step); + float step_value = p_new_step; + if (timeline->is_using_fps()) { + if (step_value != 0.0) { + step_value = 1.0 / step_value; + } + } + undo_redo->add_do_method(animation.ptr(), "set_step", step_value); undo_redo->add_undo_method(animation.ptr(), "set_step", animation->get_step()); step->set_block_signals(true); undo_redo->commit_action(); step->set_block_signals(false); - emit_signal("animation_step_changed", p_new_step); + emit_signal("animation_step_changed", step_value); } void AnimationTrackEditor::_update_length(double p_new_len) { @@ -4787,6 +4932,7 @@ void AnimationTrackEditor::_bind_methods() { ClassDB::bind_method("_edit_menu_pressed", &AnimationTrackEditor::_edit_menu_pressed); ClassDB::bind_method("_view_group_toggle", &AnimationTrackEditor::_view_group_toggle); ClassDB::bind_method("_selection_changed", &AnimationTrackEditor::_selection_changed); + ClassDB::bind_method("_snap_mode_changed", &AnimationTrackEditor::_snap_mode_changed); ADD_SIGNAL(MethodInfo("timeline_changed", PropertyInfo(Variant::REAL, "position"), PropertyInfo(Variant::BOOL, "drag"))); ADD_SIGNAL(MethodInfo("keying_changed")); @@ -4875,7 +5021,7 @@ AnimationTrackEditor::AnimationTrackEditor() { bottom_hb->add_child(memnew(VSeparator)); snap = memnew(ToolButton); - snap->set_text(TTR("Snap (s): ")); + snap->set_text(TTR("Snap: ")); bottom_hb->add_child(snap); snap->set_disabled(true); snap->set_toggle_mode(true); @@ -4883,7 +5029,7 @@ AnimationTrackEditor::AnimationTrackEditor() { step = memnew(EditorSpinSlider); step->set_min(0); - step->set_max(1000); + step->set_max(1000000); step->set_step(0.01); step->set_hide_slider(true); step->set_custom_minimum_size(Size2(100, 0) * EDSCALE); @@ -4892,6 +5038,13 @@ AnimationTrackEditor::AnimationTrackEditor() { step->connect("value_changed", this, "_update_step"); step->set_read_only(true); + snap_mode = memnew(OptionButton); + snap_mode->add_item(TTR("Seconds")); + snap_mode->add_item(TTR("FPS")); + bottom_hb->add_child(snap_mode); + snap_mode->connect("item_selected", this, "_snap_mode_changed"); + snap_mode->set_disabled(true); + bottom_hb->add_child(memnew(VSeparator)); zoom_icon = memnew(TextureRect); diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index 29ce4f189e..5ac5999b68 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -76,6 +76,7 @@ class AnimationTimelineEdit : public Range { Rect2 hsize_rect; bool editing; + bool use_fps; bool panning_timeline; float panning_timeline_from; @@ -110,6 +111,9 @@ public: void update_values(); + void set_use_fps(bool p_use_fps); + bool is_using_fps() const; + void set_hscroll(HScrollBar *p_hscroll); AnimationTimelineEdit(); @@ -303,7 +307,9 @@ class AnimationTrackEditor : public VBoxContainer { EditorSpinSlider *step; TextureRect *zoom_icon; ToolButton *snap; + OptionButton *snap_mode; + void _snap_mode_changed(int p_mode); Vector<AnimationTrackEdit *> track_edits; Vector<AnimationTrackEditGroup *> groups; @@ -328,6 +334,8 @@ class AnimationTrackEditor : public VBoxContainer { void _new_track_node_selected(NodePath p_path); void _new_track_property_selected(String p_name); + void _update_step_spinbox(); + PropertySelector *prop_selector; PropertySelector *method_selector; SceneTreeDialog *pick_track; @@ -484,6 +492,9 @@ public: void update_keying(); bool has_keying() const; + Dictionary get_state() const; + void set_state(const Dictionary &p_state); + void cleanup(); void set_anim_pos(float p_pos); diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 134384c167..ec159ab173 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -1180,17 +1180,57 @@ Variant CodeTextEditor::get_edit_state() { Dictionary state; state["scroll_position"] = text_editor->get_v_scroll(); + state["h_scroll_position"] = text_editor->get_h_scroll(); state["column"] = text_editor->cursor_get_column(); state["row"] = text_editor->cursor_get_line(); + state["selection"] = get_text_edit()->is_selection_active(); + if (get_text_edit()->is_selection_active()) { + state["selection_from_line"] = text_editor->get_selection_from_line(); + state["selection_from_column"] = text_editor->get_selection_from_column(); + state["selection_to_line"] = text_editor->get_selection_to_line(); + state["selection_to_column"] = text_editor->get_selection_to_column(); + } + + state["folded_lines"] = text_editor->get_folded_lines(); + state["breakpoints"] = text_editor->get_breakpoints_array(); + + state["syntax_highlighter"] = TTR("Standard"); + SyntaxHighlighter *syntax_highlighter = text_editor->_get_syntax_highlighting(); + if (syntax_highlighter) { + state["syntax_highlighter"] = syntax_highlighter->get_name(); + } + return state; } void CodeTextEditor::set_edit_state(const Variant &p_state) { Dictionary state = p_state; - text_editor->cursor_set_column(state["column"]); + + /* update the row first as it sets the column to 0 */ text_editor->cursor_set_line(state["row"]); + text_editor->cursor_set_column(state["column"]); text_editor->set_v_scroll(state["scroll_position"]); + text_editor->set_h_scroll(state["h_scroll_position"]); + + if (state.has("selection")) { + text_editor->select(state["selection_from_line"], state["selection_from_column"], state["selection_to_line"], state["selection_to_column"]); + } + + if (state.has("folded_lines")) { + Vector<int> folded_lines = state["folded_lines"]; + for (int i = 0; i < folded_lines.size(); i++) { + text_editor->fold_line(folded_lines[i]); + } + } + + if (state.has("breakpoints")) { + Array breakpoints = state["breakpoints"]; + for (int i = 0; i < breakpoints.size(); i++) { + text_editor->set_line_as_breakpoint(breakpoints[i], true); + } + } + text_editor->grab_focus(); } diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index 045158504a..685c5de76c 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -37,6 +37,25 @@ #include "scene/gui/label.h" #include "scene/gui/popup_menu.h" +static Node *_find_first_script(Node *p_root, Node *p_node) { + if (p_node != p_root && p_node->get_owner() != p_root) { + return NULL; + } + if (!p_node->get_script().is_null()) { + return p_node; + } + + for (int i = 0; i < p_node->get_child_count(); i++) { + + Node *ret = _find_first_script(p_root, p_node->get_child(i)); + if (ret) { + return ret; + } + } + + return NULL; +} + class ConnectDialogBinds : public Object { GDCLASS(ConnectDialogBinds, Object); @@ -122,17 +141,8 @@ void ConnectDialog::_tree_node_selected() { Node *current = tree->get_selected(); - if (!current) { - make_callback->hide(); - return; - } - - if (current->get_script().is_null()) - make_callback->hide(); - else - make_callback->show(); - - dst_path->set_text(source->get_path_to(current)); + dst_path = source->get_path_to(current); + get_ok()->set_disabled(false); } /* @@ -195,6 +205,7 @@ void ConnectDialog::_notification(int p_what) { void ConnectDialog::_bind_methods() { + ClassDB::bind_method("_advanced_pressed", &ConnectDialog::_advanced_pressed); ClassDB::bind_method("_cancel", &ConnectDialog::_cancel_pressed); ClassDB::bind_method("_tree_node_selected", &ConnectDialog::_tree_node_selected); ClassDB::bind_method("_add_bind", &ConnectDialog::_add_bind); @@ -215,7 +226,7 @@ StringName ConnectDialog::get_signal_name() const { NodePath ConnectDialog::get_dst_path() const { - return dst_path->get_text(); + return dst_path; } void ConnectDialog::set_dst_node(Node *p_node) { @@ -272,8 +283,13 @@ void ConnectDialog::init(Connection c, bool bEdit) { tree->set_selected(NULL); tree->set_marked(source, true); - set_dst_node(static_cast<Node *>(c.target)); - set_dst_method(c.method); + if (c.target) { + get_ok()->set_disabled(false); + set_dst_node(static_cast<Node *>(c.target)); + set_dst_method(c.method); + } else { + get_ok()->set_disabled(true); + } bool bDeferred = (c.flags & CONNECT_DEFERRED) == CONNECT_DEFERRED; bool bOneshot = (c.flags & CONNECT_ONESHOT) == CONNECT_ONESHOT; @@ -288,6 +304,36 @@ void ConnectDialog::init(Connection c, bool bEdit) { bEditMode = bEdit; } +void ConnectDialog::popup_dialog(const String &p_for_signal, bool p_advanced) { + + advanced->set_pressed(p_advanced); + from_signal->set_text(p_for_signal); + error_label->add_color_override("font_color", get_color("error_color", "Editor")); + + if (p_advanced) { + + popup_centered(Size2(900, 500) * EDSCALE); + connect_to_label->set_text("Connect to Node:"); + tree->set_connect_to_script_mode(false); + error_label->hide(); + } else { + popup_centered(Size2(700, 500) * EDSCALE); + connect_to_label->set_text("Connect to Script:"); + tree->set_connect_to_script_mode(true); + + if (!_find_first_script(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root())) { + error_label->show(); + } else { + error_label->hide(); + } + } +} + +void ConnectDialog::_advanced_pressed() { + vbc_right->set_visible(advanced->is_pressed()); + popup_dialog(from_signal->get_text(), advanced->is_pressed()); +} + ConnectDialog::ConnectDialog() { VBoxContainer *vbc = memnew(VBoxContainer); @@ -301,15 +347,27 @@ ConnectDialog::ConnectDialog() { main_hb->add_child(vbc_left); vbc_left->set_h_size_flags(SIZE_EXPAND_FILL); + from_signal = memnew(LineEdit); + from_signal->set_editable(false); + vbc_left->add_margin_child(TTR("From Signal:"), from_signal); + tree = memnew(SceneTreeEditor(false)); tree->get_scene_tree()->connect("item_activated", this, "_ok"); tree->connect("node_selected", this, "_tree_node_selected"); + tree->set_connect_to_script_mode(true); - vbc_left->add_margin_child(TTR("Connect To Node:"), tree, true); + Node *mc = vbc_left->add_margin_child(TTR("Connect To Script:"), tree, true); + connect_to_label = Object::cast_to<Label>(vbc_left->get_child(mc->get_index() - 1)); - VBoxContainer *vbc_right = memnew(VBoxContainer); + error_label = memnew(Label); + error_label->set_text(TTR("Scene does not contain any script.")); + vbc_left->add_child(error_label); + error_label->hide(); + + vbc_right = memnew(VBoxContainer); main_hb->add_child(vbc_right); vbc_right->set_h_size_flags(SIZE_EXPAND_FILL); + vbc_right->hide(); HBoxContainer *add_bind_hb = memnew(HBoxContainer); @@ -347,16 +405,18 @@ ConnectDialog::ConnectDialog() { vbc_right->add_margin_child(TTR("Extra Call Arguments:"), bind_editor, true); - dst_path = memnew(LineEdit); - vbc->add_margin_child(TTR("Path to Node:"), dst_path); - HBoxContainer *dstm_hb = memnew(HBoxContainer); - vbc->add_margin_child("Method In Node:", dstm_hb); + vbc_left->add_margin_child("Method to Create:", dstm_hb); dst_method = memnew(LineEdit); dst_method->set_h_size_flags(SIZE_EXPAND_FILL); dstm_hb->add_child(dst_method); + advanced = memnew(CheckBox); + dstm_hb->add_child(advanced); + advanced->set_text(TTR("Advanced..")); + advanced->connect("pressed", this, "_advanced_pressed"); + /* dst_method_list = memnew( MenuButton ); dst_method_list->set_text("List..."); @@ -368,19 +428,13 @@ ConnectDialog::ConnectDialog() { dst_method_list->set_end( Point2( 15,39 ) ); */ - make_callback = memnew(CheckButton); - make_callback->set_toggle_mode(true); - make_callback->set_pressed(EDITOR_DEF("text_editor/tools/create_signal_callbacks", true)); - make_callback->set_text(TTR("Make Function")); - dstm_hb->add_child(make_callback); - deferred = memnew(CheckButton); deferred->set_text(TTR("Deferred")); - dstm_hb->add_child(deferred); + vbc_right->add_child(deferred); oneshot = memnew(CheckButton); oneshot->set_text(TTR("Oneshot")); - dstm_hb->add_child(oneshot); + vbc_right->add_child(oneshot); set_as_toplevel(true); @@ -429,7 +483,8 @@ void ConnectionsDock::_make_or_edit_connection() { bool oshot = connect_dialog->get_oneshot(); cToMake.flags = CONNECT_PERSIST | (defer ? CONNECT_DEFERRED : 0) | (oshot ? CONNECT_ONESHOT : 0); - bool add_script_function = connect_dialog->get_make_callback(); + //conditions to add function, must have a script and must have a method + bool add_script_function = !target->get_script().is_null() && !ClassDB::has_method(target->get_class(), cToMake.method); PoolStringArray script_function_args; if (add_script_function) { // pick up args here before "it" is deleted by update_tree @@ -568,6 +623,7 @@ bool ConnectionsDock::_is_item_signal(TreeItem &item) { /* Open connection dialog with TreeItem data to CREATE a brand-new connection. */ + void ConnectionsDock::_open_connection_dialog(TreeItem &item) { String signal = item.get_metadata(0).operator Dictionary()["name"]; @@ -590,6 +646,10 @@ void ConnectionsDock::_open_connection_dialog(TreeItem &item) { } Node *dst_node = selectedNode->get_owner() ? selectedNode->get_owner() : selectedNode; + if (!dst_node || dst_node->get_script().is_null()) { + dst_node = _find_first_script(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root()); + } + StringName dst_method = "_on_" + midname + "_" + signal; Connection c; @@ -598,9 +658,10 @@ void ConnectionsDock::_open_connection_dialog(TreeItem &item) { c.target = dst_node; c.method = dst_method; + //connect_dialog->set_title(TTR("Connect Signal: ") + signalname); + connect_dialog->popup_dialog(signalname, false); connect_dialog->init(c); - connect_dialog->set_title(TTR("Connect Signal: ") + signalname); - connect_dialog->popup_centered_ratio(); + connect_dialog->set_title(TTR("Connect a Signal to a Method")); } /* @@ -612,9 +673,9 @@ void ConnectionsDock::_open_connection_dialog(Connection cToEdit) { Node *dst = static_cast<Node *>(cToEdit.target); if (src && dst) { - connect_dialog->init(cToEdit, true); - connect_dialog->set_title(TTR("Edit Connection: ") + cToEdit.signal); + connect_dialog->set_title(TTR("Edit Connection:") + cToEdit.signal); connect_dialog->popup_centered_ratio(); + connect_dialog->init(cToEdit, true); } } diff --git a/editor/connections_dialog.h b/editor/connections_dialog.h index 0e7e172ebb..59fe6dacfe 100644 --- a/editor/connections_dialog.h +++ b/editor/connections_dialog.h @@ -53,12 +53,15 @@ class ConnectDialog : public ConfirmationDialog { GDCLASS(ConnectDialog, ConfirmationDialog); + Label *connect_to_label; + LineEdit *from_signal; Node *source; StringName signal; - LineEdit *dst_path; LineEdit *dst_method; ConnectDialogBinds *cdbinds; bool bEditMode; + NodePath dst_path; + VBoxContainer *vbc_right; SceneTreeEditor *tree; ConfirmationDialog *error; @@ -66,13 +69,16 @@ class ConnectDialog : public ConfirmationDialog { OptionButton *type_list; CheckButton *deferred; CheckButton *oneshot; - CheckButton *make_callback; + CheckBox *advanced; + + Label *error_label; void ok_pressed(); void _cancel_pressed(); void _tree_node_selected(); void _add_bind(); void _remove_bind(); + void _advanced_pressed(); protected: void _notification(int p_what); @@ -87,13 +93,13 @@ public: void set_dst_method(const StringName &p_method); Vector<Variant> get_binds() const; - bool get_make_callback() { return make_callback->is_visible() && make_callback->is_pressed(); } bool get_deferred() const; bool get_oneshot() const; bool is_editing() const; void init(Connection c, bool bEdit = false); + void popup_dialog(const String &p_for_signal, bool p_advanced); ConnectDialog(); ~ConnectDialog(); }; diff --git a/editor/doc/doc_data.cpp b/editor/doc/doc_data.cpp index 8f1d0d9677..c2a492bc9a 100644 --- a/editor/doc/doc_data.cpp +++ b/editor/doc/doc_data.cpp @@ -54,7 +54,6 @@ void DocData::merge_from(const DocData &p_data) { c.description = cf.description; c.brief_description = cf.brief_description; c.tutorials = cf.tutorials; - c.demos = cf.demos; for (int i = 0; i < c.methods.size(); i++) { @@ -837,10 +836,6 @@ Error DocData::_load(Ref<XMLParser> parser) { } else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END && parser->get_node_name() == "tutorials") break; //end of <tutorials> } - } else if (name2 == "demos") { - parser->read(); - if (parser->get_node_type() == XMLParser::NODE_TEXT) - c.demos = parser->get_node_data(); } else if (name2 == "methods") { Error err2 = _parse_methods(parser, c.methods); @@ -987,8 +982,7 @@ Error DocData::save_classes(const String &p_default_path, const Map<String, Stri FileAccessRef f = FileAccess::open(save_file, FileAccess::WRITE, &err); if (err) { ERR_EXPLAIN("Can't write doc file: " + save_file); - - ERR_FAIL_V(err); + ERR_CONTINUE(err); } _write_string(f, 0, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"); @@ -1015,9 +1009,6 @@ Error DocData::save_classes(const String &p_default_path, const Map<String, Stri _write_string(f, 2, "<link>" + c.tutorials.get(i).xml_escape() + "</link>"); } _write_string(f, 1, "</tutorials>"); - _write_string(f, 1, "<demos>"); - _write_string(f, 2, c.demos.strip_edges().xml_escape()); - _write_string(f, 1, "</demos>"); _write_string(f, 1, "<methods>"); c.methods.sort(); diff --git a/editor/doc/doc_data.h b/editor/doc/doc_data.h index 8156acd3d0..d3844adb7e 100644 --- a/editor/doc/doc_data.h +++ b/editor/doc/doc_data.h @@ -86,7 +86,6 @@ public: String brief_description; String description; Vector<String> tutorials; - String demos; Vector<MethodDoc> methods; Vector<MethodDoc> signals; Vector<ConstantDoc> constants; diff --git a/editor/editor_atlas_packer.cpp b/editor/editor_atlas_packer.cpp new file mode 100644 index 0000000000..4e1d98399a --- /dev/null +++ b/editor/editor_atlas_packer.cpp @@ -0,0 +1,265 @@ +#include "editor_atlas_packer.h" + +void EditorAtlasPacker::_plot_triangle(Ref<BitMap> p_bitmap, Vector2i *vertices) { + + int width = p_bitmap->get_size().width; + int height = p_bitmap->get_size().height; + int x[3]; + int y[3]; + + for (int j = 0; j < 3; j++) { + + x[j] = vertices[j].x; + y[j] = vertices[j].y; + } + + // sort the points vertically + if (y[1] > y[2]) { + SWAP(x[1], x[2]); + SWAP(y[1], y[2]); + } + if (y[0] > y[1]) { + SWAP(x[0], x[1]); + SWAP(y[0], y[1]); + } + if (y[1] > y[2]) { + SWAP(x[1], x[2]); + SWAP(y[1], y[2]); + } + + double dx_far = double(x[2] - x[0]) / (y[2] - y[0] + 1); + double dx_upper = double(x[1] - x[0]) / (y[1] - y[0] + 1); + double dx_low = double(x[2] - x[1]) / (y[2] - y[1] + 1); + double xf = x[0]; + double xt = x[0] + dx_upper; // if y[0] == y[1], special case + for (int yi = y[0]; yi <= (y[2] > height - 1 ? height - 1 : y[2]); yi++) { + if (yi >= 0) { + for (int xi = (xf > 0 ? int(xf) : 0); xi <= (xt < width ? xt : width - 1); xi++) { + //pixels[int(x + y * width)] = color; + + p_bitmap->set_bit(Point2(xi, yi), true); + } + + for (int xi = (xf < width ? int(xf) : width - 1); xi >= (xt > 0 ? xt : 0); xi--) { + + p_bitmap->set_bit(Point2(xi, yi), true); + } + } + xf += dx_far; + if (yi < y[1]) + xt += dx_upper; + else + xt += dx_low; + } +} +void EditorAtlasPacker::chart_pack(Vector<Chart> &charts, int &r_width, int &r_height, int p_atlas_max_size, int p_cell_resolution) { + + int divide_by = MIN(64, p_cell_resolution); + Vector<PlottedBitmap> bitmaps; + + int max_w = 0; + + for (int i = 0; i < charts.size(); i++) { + + const Chart &chart = charts[i]; + + //generate aabb + + Rect2i aabb; + int vertex_count = chart.vertices.size(); + const Vector2 *vertices = chart.vertices.ptr(); + + for (int j = 0; j < vertex_count; j++) { + + if (j == 0) { + aabb.position = vertices[j]; + } else { + aabb.expand_to(vertices[j]); + } + } + + Ref<BitMap> src_bitmap; + src_bitmap.instance(); + src_bitmap->create(aabb.size / divide_by); + + int w = src_bitmap->get_size().width; + int h = src_bitmap->get_size().height; + + //plot triangles, using divisor + + for (int j = 0; j < chart.faces.size(); j++) { + + Vector2i v[3]; + for (int k = 0; k < 3; k++) { + Vector2 vtx = chart.vertices[chart.faces[j].vertex[k]]; + vtx -= aabb.position; + vtx /= divide_by; + v[k] = vtx; + } + + _plot_triangle(src_bitmap, v); + } + + //src_bitmap->convert_to_image()->save_png("bitmap" + itos(i) + ".png"); + + //grow by 1 for each side + + int bmw = src_bitmap->get_size().width + 2; + int bmh = src_bitmap->get_size().height + 2; + + int heights_size = -1; + bool transpose = false; + if (chart.can_transpose && bmh > bmw) { + heights_size = bmh; + transpose = true; + } else { + heights_size = bmw; + } + + max_w = MAX(max_w, heights_size); + + Vector<int> top_heights; + Vector<int> bottom_heights; + top_heights.resize(heights_size); + bottom_heights.resize(heights_size); + + for (int x = 0; x < heights_size; x++) { + top_heights.write[x] = -1; + bottom_heights.write[x] = 0x7FFFFFFF; + } + + for (int x = 0; x < bmw; x++) { + for (int y = 0; y < bmh; y++) { + bool found_pixel = false; + for (int lx = x - 1; lx < x + 2 && !found_pixel; lx++) { + for (int ly = y - 1; ly < y + 2 && !found_pixel; ly++) { + + int px = lx - 1; + if (px < 0 || px >= w) + continue; + int py = ly - 1; + if (py < 0 || py >= h) + continue; + + if (src_bitmap->get_bit(Vector2(px, py))) { + found_pixel = true; + } + } + } + if (found_pixel) { + + if (transpose) { + if (x > top_heights[y]) { + top_heights.write[y] = x; + } + if (x < bottom_heights[y]) { + bottom_heights.write[y] = x; + } + } else { + if (y > top_heights[x]) { + top_heights.write[x] = y; + } + if (y < bottom_heights[x]) { + bottom_heights.write[x] = y; + } + } + } + } + } + + String row; + for (int j = 0; j < top_heights.size(); j++) { + row += "(" + itos(top_heights[j]) + "-" + itos(bottom_heights[j]) + "),"; + } + + PlottedBitmap plotted_bitmap; + plotted_bitmap.offset = aabb.position; + plotted_bitmap.top_heights = top_heights; + plotted_bitmap.bottom_heights = bottom_heights; + plotted_bitmap.chart_index = i; + plotted_bitmap.transposed = transpose; + plotted_bitmap.area = bmw * bmh; + + bitmaps.push_back(plotted_bitmap); + } + + bitmaps.sort(); + + int atlas_max_width = nearest_power_of_2_templated(p_atlas_max_size) / divide_by; + int atlas_w = nearest_power_of_2_templated(max_w); + int atlas_h; + while (true) { + atlas_h = 0; + + //do a tetris + Vector<int> heights; + heights.resize(atlas_w); + for (int i = 0; i < atlas_w; i++) { + heights.write[i] = 0; + } + + int *atlas_ptr = heights.ptrw(); + + for (int i = 0; i < bitmaps.size(); i++) { + + int best_height = 0x7FFFFFFF; + int best_height_offset = -1; + int w = bitmaps[i].top_heights.size(); + + const int *top_heights = bitmaps[i].top_heights.ptr(); + const int *bottom_heights = bitmaps[i].bottom_heights.ptr(); + + for (int j = 0; j < atlas_w - w; j++) { + + int height = 0; + + for (int k = 0; k < w; k++) { + + int pixmap_h = bottom_heights[k]; + if (pixmap_h == -1) { + continue; //no pixel here, anything is fine + } + + int h = MAX(0, atlas_ptr[j + k] - pixmap_h); + if (h > height) { + height = h; + } + } + + if (height < best_height) { + best_height = height; + best_height_offset = j; + } + } + + for (int j = 0; j < w; j++) { //add + if (top_heights[j] == -1) { //unused + continue; + } + int height = best_height + top_heights[j] + 1; + atlas_ptr[j + best_height_offset] = height; + atlas_h = MAX(atlas_h, height); + } + + // set + Vector2 offset = bitmaps[i].offset; + if (bitmaps[i].transposed) { + SWAP(offset.x, offset.y); + } + + Vector2 final_pos = Vector2(best_height_offset * divide_by, best_height * divide_by) + Vector2(divide_by, divide_by) - offset; + charts.write[bitmaps[i].chart_index].final_offset = final_pos; + charts.write[bitmaps[i].chart_index].transposed = bitmaps[i].transposed; + } + + if (atlas_h <= atlas_w * 2 || atlas_w >= atlas_max_width) { + break; //ok this one is enough + } + + //try again + atlas_w *= 2; + } + + r_width = atlas_w * divide_by; + r_height = atlas_h * divide_by; +} diff --git a/editor/editor_atlas_packer.h b/editor/editor_atlas_packer.h new file mode 100644 index 0000000000..dd9caa340e --- /dev/null +++ b/editor/editor_atlas_packer.h @@ -0,0 +1,45 @@ +#ifndef EDITOR_ATLAS_PACKER_H +#define EDITOR_ATLAS_PACKER_H + +#include "core/math/vector2.h" + +#include "core/vector.h" +#include "scene/resources/bit_map.h" + +class EditorAtlasPacker { +public: + struct Chart { + Vector<Vector2> vertices; + struct Face { + int vertex[3]; + }; + Vector<Face> faces; + bool can_transpose; + + Vector2 final_offset; + bool transposed; + }; + +private: + struct PlottedBitmap { + int chart_index; + Vector2i offset; + int area; + Vector<int> top_heights; + Vector<int> bottom_heights; + bool transposed; + + Vector2 final_pos; + + bool operator<(const PlottedBitmap &p_bm) const { + return area > p_bm.area; + } + }; + + static void _plot_triangle(Ref<BitMap> p_bitmap, Vector2i *vertices); + +public: + static void chart_pack(Vector<Chart> &charts, int &r_width, int &r_height, int p_atlas_max_size = 2048, int p_cell_resolution = 4); +}; + +#endif // EDITOR_ATLAS_PACKER_H diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp index 49d40e6d90..2beb0153f4 100644 --- a/editor/editor_audio_buses.cpp +++ b/editor/editor_audio_buses.cpp @@ -756,7 +756,7 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) { add_child(vb); set_v_size_flags(SIZE_EXPAND_FILL); - set_custom_minimum_size(Size2(100, 0) * EDSCALE); + set_custom_minimum_size(Size2(110, 0) * EDSCALE); track_name = memnew(LineEdit); track_name->connect("text_entered", this, "_name_changed"); @@ -1394,7 +1394,7 @@ void EditorAudioMeterNotches::_notification(int p_what) { } void EditorAudioMeterNotches::_draw_audio_notches() { - Ref<Font> font = get_font("source", "EditorFonts"); + Ref<Font> font = get_font("font", "Label"); float font_height = font->get_height(); for (uint8_t i = 0; i < notches.size(); i++) { diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 3d9d5e26be..abd3bea951 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -43,7 +43,7 @@ EditorFileSystem *EditorFileSystem::singleton = NULL; //the name is the version, to keep compatibility with different versions of Godot -#define CACHE_FILE_NAME "filesystem_cache5" +#define CACHE_FILE_NAME "filesystem_cache6" void EditorFileSystemDirectory::sort_files() { @@ -241,7 +241,7 @@ void EditorFileSystem::_scan_filesystem() { } else { Vector<String> split = l.split("::"); - ERR_CONTINUE(split.size() != 7); + ERR_CONTINUE(split.size() != 8); String name = split[0]; String file; @@ -253,11 +253,12 @@ void EditorFileSystem::_scan_filesystem() { fc.modification_time = split[2].to_int64(); fc.import_modification_time = split[3].to_int64(); fc.import_valid = split[4].to_int64() != 0; - fc.script_class_name = split[5].get_slice("<>", 0); - fc.script_class_extends = split[5].get_slice("<>", 1); - fc.script_class_icon_path = split[5].get_slice("<>", 2); + fc.import_group_file = split[5].strip_edges(); + fc.script_class_name = split[6].get_slice("<>", 0); + fc.script_class_extends = split[6].get_slice("<>", 1); + fc.script_class_icon_path = split[6].get_slice("<>", 2); - String deps = split[6].strip_edges(); + String deps = split[7].strip_edges(); if (deps.length()) { Vector<String> dp = deps.split("<>"); for (int i = 0; i < dp.size(); i++) { @@ -318,6 +319,9 @@ void EditorFileSystem::_scan_filesystem() { } void EditorFileSystem::_save_filesystem_cache() { + + group_file_cache.clear(); + String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file(CACHE_FILE_NAME); FileAccess *f = FileAccess::open(fscache, FileAccess::WRITE); @@ -771,6 +775,7 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess fi->import_valid = fc->import_valid; fi->script_class_name = fc->script_class_name; + fi->import_group_file = fc->import_group_file; fi->script_class_extends = fc->script_class_extends; fi->script_class_icon_path = fc->script_class_icon_path; @@ -784,6 +789,7 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess if (fc->type == String()) { fi->type = ResourceLoader::get_resource_type(path); + fi->import_group_file = ResourceLoader::get_import_group_file(path); //there is also the chance that file type changed due to reimport, must probably check this somehow here (or kind of note it for next time in another file?) //note: I think this should not happen any longer.. } @@ -791,6 +797,7 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess } else { fi->type = ResourceFormatImporter::get_singleton()->get_resource_type(path); + fi->import_group_file = ResourceFormatImporter::get_singleton()->get_import_group_file(path); fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends, &fi->script_class_icon_path); fi->modified_time = 0; fi->import_modified_time = 0; @@ -918,6 +925,7 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, const fi->type = ResourceLoader::get_resource_type(path); fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends, &fi->script_class_icon_path); fi->import_valid = ResourceLoader::is_import_valid(path); + fi->import_group_file = ResourceLoader::get_import_group_file(path); { ItemAction ia; @@ -1187,7 +1195,10 @@ void EditorFileSystem::_save_filesystem_cache(EditorFileSystemDirectory *p_dir, for (int i = 0; i < p_dir->files.size(); i++) { - String s = p_dir->files[i]->file + "::" + p_dir->files[i]->type + "::" + itos(p_dir->files[i]->modified_time) + "::" + itos(p_dir->files[i]->import_modified_time) + "::" + itos(p_dir->files[i]->import_valid) + "::" + p_dir->files[i]->script_class_name + "<>" + p_dir->files[i]->script_class_extends + "<>" + p_dir->files[i]->script_class_icon_path; + if (p_dir->files[i]->import_group_file != String()) { + group_file_cache.insert(p_dir->files[i]->import_group_file); + } + String s = p_dir->files[i]->file + "::" + p_dir->files[i]->type + "::" + itos(p_dir->files[i]->modified_time) + "::" + itos(p_dir->files[i]->import_modified_time) + "::" + itos(p_dir->files[i]->import_valid) + "::" + p_dir->files[i]->import_group_file + "::" + p_dir->files[i]->script_class_name + "<>" + p_dir->files[i]->script_class_extends + "<>" + p_dir->files[i]->script_class_icon_path; s += "::"; for (int j = 0; j < p_dir->files[i]->deps.size(); j++) { @@ -1523,6 +1534,7 @@ void EditorFileSystem::update_file(const String &p_file) { fs->files[cpos]->type = type; fs->files[cpos]->script_class_name = _get_global_script_class(type, p_file, &fs->files[cpos]->script_class_extends, &fs->files[cpos]->script_class_icon_path); + fs->files[cpos]->import_group_file = ResourceLoader::get_import_group_file(p_file); fs->files[cpos]->modified_time = FileAccess::get_modified_time(p_file); fs->files[cpos]->deps = _get_dependencies(p_file); fs->files[cpos]->import_valid = ResourceLoader::is_import_valid(p_file); @@ -1534,6 +1546,168 @@ void EditorFileSystem::update_file(const String &p_file) { _queue_update_script_classes(); } +Error EditorFileSystem::_reimport_group(const String &p_group_file, const Vector<String> &p_files) { + + String importer_name; + + Map<String, Map<StringName, Variant> > source_file_options; + Map<String, String> base_paths; + for (int i = 0; i < p_files.size(); i++) { + + Ref<ConfigFile> config; + config.instance(); + Error err = config->load(p_files[i] + ".import"); + ERR_CONTINUE(err != OK); + ERR_CONTINUE(!config->has_section_key("remap", "importer")); + String file_importer_name = config->get_value("remap", "importer"); + ERR_CONTINUE(file_importer_name == String()); + + if (importer_name != String() && importer_name != file_importer_name) { + print_line("one importer: " + importer_name + " the other: " + file_importer_name); + EditorNode::get_singleton()->show_warning(vformat(TTR("There are multiple importers for different types pointing to file %s, import aborted"), p_group_file)); + ERR_FAIL_V(ERR_FILE_CORRUPT); + } + + source_file_options[p_files[i]] = Map<StringName, Variant>(); + importer_name = file_importer_name; + + Ref<ResourceImporter> importer = ResourceFormatImporter::get_singleton()->get_importer_by_name(importer_name); + ERR_FAIL_COND_V(!importer.is_valid(), ERR_FILE_CORRUPT); + List<ResourceImporter::ImportOption> options; + importer->get_import_options(&options); + //set default values + for (List<ResourceImporter::ImportOption>::Element *E = options.front(); E; E = E->next()) { + + source_file_options[p_files[i]][E->get().option.name] = E->get().default_value; + } + + if (config->has_section("params")) { + List<String> sk; + config->get_section_keys("params", &sk); + for (List<String>::Element *E = sk.front(); E; E = E->next()) { + String param = E->get(); + Variant value = config->get_value("params", param); + //override with whathever is in file + source_file_options[p_files[i]][param] = value; + } + } + + base_paths[p_files[i]] = ResourceFormatImporter::get_singleton()->get_import_base_path(p_files[i]); + } + + ERR_FAIL_COND_V(importer_name == String(), ERR_UNCONFIGURED); + + Ref<ResourceImporter> importer = ResourceFormatImporter::get_singleton()->get_importer_by_name(importer_name); + + Error err = importer->import_group_file(p_group_file, source_file_options, base_paths); + + //all went well, overwrite config files with proper remaps and md5s + for (Map<String, Map<StringName, Variant> >::Element *E = source_file_options.front(); E; E = E->next()) { + + String file = E->key(); + String base_path = ResourceFormatImporter::get_singleton()->get_import_base_path(file); + FileAccessRef f = FileAccess::open(file + ".import", FileAccess::WRITE); + ERR_FAIL_COND_V(!f, ERR_FILE_CANT_OPEN); + + //write manually, as order matters ([remap] has to go first for performance). + f->store_line("[remap]"); + f->store_line(""); + f->store_line("importer=\"" + importer->get_importer_name() + "\""); + if (importer->get_resource_type() != "") { + f->store_line("type=\"" + importer->get_resource_type() + "\""); + } + + Vector<String> dest_paths; + + if (err == OK) { + String path = base_path + "." + importer->get_save_extension(); + f->store_line("path=\"" + path + "\""); + dest_paths.push_back(path); + } + + f->store_line("group_file=" + Variant(p_group_file).get_construct_string()); + + if (err == OK) { + f->store_line("valid=true"); + } else { + f->store_line("valid=false"); + } + f->store_line("[deps]\n"); + + f->store_line(""); + + f->store_line("source_file=" + Variant(file).get_construct_string()); + if (dest_paths.size()) { + Array dp; + for (int i = 0; i < dest_paths.size(); i++) { + dp.push_back(dest_paths[i]); + } + f->store_line("dest_files=" + Variant(dp).get_construct_string() + "\n"); + } + f->store_line("[params]"); + f->store_line(""); + + //store options in provided order, to avoid file changing. Order is also important because first match is accepted first. + + List<ResourceImporter::ImportOption> options; + importer->get_import_options(&options); + //set default values + for (List<ResourceImporter::ImportOption>::Element *F = options.front(); F; F = F->next()) { + + String base = F->get().option.name; + Variant v = F->get().default_value; + if (source_file_options[file].has(base)) { + v = source_file_options[file][base]; + } + String value; + VariantWriter::write_to_string(v, value); + f->store_line(base + "=" + value); + } + + f->close(); + + // Store the md5's of the various files. These are stored separately so that the .import files can be version controlled. + FileAccessRef md5s = FileAccess::open(base_path + ".md5", FileAccess::WRITE); + ERR_FAIL_COND_V(!md5s, ERR_FILE_CANT_OPEN); + + md5s->store_line("source_md5=\"" + FileAccess::get_md5(file) + "\""); + if (dest_paths.size()) { + md5s->store_line("dest_md5=\"" + FileAccess::get_multiple_md5(dest_paths) + "\"\n"); + } + md5s->close(); + + EditorFileSystemDirectory *fs = NULL; + int cpos = -1; + bool found = _find_file(file, &fs, cpos); + ERR_FAIL_COND_V(!found, ERR_UNCONFIGURED); + + //update modified times, to avoid reimport + fs->files[cpos]->modified_time = FileAccess::get_modified_time(file); + fs->files[cpos]->import_modified_time = FileAccess::get_modified_time(file + ".import"); + fs->files[cpos]->deps = _get_dependencies(file); + fs->files[cpos]->type = importer->get_resource_type(); + fs->files[cpos]->import_valid = err == OK; + + //if file is currently up, maybe the source it was loaded from changed, so import math must be updated for it + //to reload properly + if (ResourceCache::has(file)) { + + Resource *r = ResourceCache::get(file); + + if (r->get_import_path() != String()) { + + String dst_path = ResourceFormatImporter::get_singleton()->get_internal_resource_path(file); + r->set_import_path(dst_path); + r->set_import_last_modified_time(0); + } + } + + EditorResourcePreview::get_singleton()->check_for_invalidation(file); + } + + return err; +} + void EditorFileSystem::_reimport_file(const String &p_file) { EditorFileSystemDirectory *fs = NULL; @@ -1738,6 +1912,24 @@ void EditorFileSystem::_reimport_file(const String &p_file) { EditorResourcePreview::get_singleton()->check_for_invalidation(p_file); } +void EditorFileSystem::_find_group_files(EditorFileSystemDirectory *efd, Map<String, Vector<String> > &group_files, Set<String> &groups_to_reimport) { + + int fc = efd->files.size(); + const EditorFileSystemDirectory::FileInfo *const *files = efd->files.ptr(); + for (int i = 0; i < fc; i++) { + if (groups_to_reimport.has(files[i]->import_group_file)) { + if (!group_files.has(files[i]->import_group_file)) { + group_files[files[i]->import_group_file] = Vector<String>(); + } + group_files[files[i]->import_group_file].push_back(efd->get_file_path(i)); + } + } + + for (int i = 0; i < efd->get_subdir_count(); i++) { + _find_group_files(efd->get_subdir(i), group_files, groups_to_reimport); + } +} + void EditorFileSystem::reimport_files(const Vector<String> &p_files) { { //check that .import folder exists @@ -1757,22 +1949,58 @@ void EditorFileSystem::reimport_files(const Vector<String> &p_files) { EditorProgress pr("reimport", TTR("(Re)Importing Assets"), p_files.size()); Vector<ImportFile> files; + Set<String> groups_to_reimport; for (int i = 0; i < p_files.size(); i++) { - ImportFile ifile; - ifile.path = p_files[i]; - ifile.order = ResourceFormatImporter::get_singleton()->get_import_order(p_files[i]); - files.push_back(ifile); + + String group_file = ResourceFormatImporter::get_singleton()->get_import_group_file(p_files[i]); + + if (group_file_cache.has(p_files[i])) { + //maybe the file itself is a group! + groups_to_reimport.insert(p_files[i]); + //groups do not belong to grups + group_file = String(); + } else if (group_file != String()) { + //it's a group file, add group to import and skip this file + groups_to_reimport.insert(group_file); + } else { + //it's a regular file + ImportFile ifile; + ifile.path = p_files[i]; + ifile.order = ResourceFormatImporter::get_singleton()->get_import_order(p_files[i]); + files.push_back(ifile); + } + + //group may have changed, so also update group reference + EditorFileSystemDirectory *fs = NULL; + int cpos = -1; + if (_find_file(p_files[i], &fs, cpos)) { + + fs->files.write[cpos]->import_group_file = group_file; + } } files.sort(); for (int i = 0; i < files.size(); i++) { pr.step(files[i].path.get_file(), i); - _reimport_file(files[i].path); } + //reimport groups + + if (groups_to_reimport.size()) { + Map<String, Vector<String> > group_files; + _find_group_files(filesystem, group_files, groups_to_reimport); + for (Map<String, Vector<String> >::Element *E = group_files.front(); E; E = E->next()) { + + Error err = _reimport_group(E->key(), E->get()); + if (err == OK) { + _reimport_file(E->key()); + } + } + } + _save_filesystem_cache(); importing = false; if (!is_scanning()) { @@ -1793,6 +2021,63 @@ Error EditorFileSystem::_resource_import(const String &p_path) { return OK; } +bool EditorFileSystem::is_group_file(const String &p_path) const { + return group_file_cache.has(p_path); +} + +void EditorFileSystem::_move_group_files(EditorFileSystemDirectory *efd, const String &p_group_file, const String &p_new_location) { + + int fc = efd->files.size(); + EditorFileSystemDirectory::FileInfo *const *files = efd->files.ptrw(); + for (int i = 0; i < fc; i++) { + + if (files[i]->import_group_file == p_group_file) { + + files[i]->import_group_file = p_new_location; + + Ref<ConfigFile> config; + config.instance(); + String path = efd->get_file_path(i) + ".import"; + Error err = config->load(path); + if (err != OK) { + continue; + } + if (config->has_section_key("remap", "group_file")) { + + config->set_value("remap", "group_file", p_new_location); + } + + List<String> sk; + config->get_section_keys("params", &sk); + for (List<String>::Element *E = sk.front(); E; E = E->next()) { + //not very clean, but should work + String param = E->get(); + String value = config->get_value("params", param); + if (value == p_group_file) { + config->set_value("params", param, p_new_location); + } + } + + config->save(path); + } + } + + for (int i = 0; i < efd->get_subdir_count(); i++) { + _move_group_files(efd->get_subdir(i), p_group_file, p_new_location); + } +} + +void EditorFileSystem::move_group_file(const String &p_path, const String &p_new_path) { + + if (get_filesystem()) { + _move_group_files(get_filesystem(), p_path, p_new_path); + if (group_file_cache.has(p_path)) { + group_file_cache.erase(p_path); + group_file_cache.insert(p_new_path); + } + } +} + void EditorFileSystem::_bind_methods() { ClassDB::bind_method(D_METHOD("get_filesystem"), &EditorFileSystem::get_filesystem); diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h index 2a9e325454..8943706202 100644 --- a/editor/editor_file_system.h +++ b/editor/editor_file_system.h @@ -56,6 +56,7 @@ class EditorFileSystemDirectory : public Object { uint64_t modified_time; uint64_t import_modified_time; bool import_valid; + String import_group_file; Vector<String> deps; bool verified; //used for checking changes String script_class_name; @@ -167,6 +168,7 @@ class EditorFileSystem : public Node { uint64_t import_modification_time; Vector<String> deps; bool import_valid; + String import_group_file; String script_class_name; String script_class_extends; String script_class_icon_path; @@ -211,6 +213,7 @@ class EditorFileSystem : public Node { void _update_extensions(); void _reimport_file(const String &p_file); + Error _reimport_group(const String &p_group_file, const Vector<String> &p_files); bool _test_for_reimport(const String &p_path, bool p_only_imported_files); @@ -236,6 +239,12 @@ class EditorFileSystem : public Node { bool using_fat_32; //workaround for projects in FAT32 filesystem (pendrives, most of the time) + void _find_group_files(EditorFileSystemDirectory *efd, Map<String, Vector<String> > &group_files, Set<String> &groups_to_reimport); + + void _move_group_files(EditorFileSystemDirectory *efd, const String &p_group_file, const String &p_new_location); + + Set<String> group_file_cache; + protected: void _notification(int p_what); static void _bind_methods(); @@ -260,6 +269,9 @@ public: void update_script_classes(); + bool is_group_file(const String &p_path) const; + void move_group_file(const String &p_path, const String &p_new_path); + EditorFileSystem(); ~EditorFileSystem(); }; diff --git a/editor/editor_folding.cpp b/editor/editor_folding.cpp index 77c0f7491e..f6079624de 100644 --- a/editor/editor_folding.cpp +++ b/editor/editor_folding.cpp @@ -92,7 +92,7 @@ void EditorFolding::load_resource_folding(RES p_resource, const String &p_path) _set_unfolds(p_resource.ptr(), unfolds); } -void EditorFolding::_fill_folds(const Node *p_root, const Node *p_node, Array &p_folds, Array &resource_folds, Set<RES> &resources) { +void EditorFolding::_fill_folds(const Node *p_root, const Node *p_node, Array &p_folds, Array &resource_folds, Array& nodes_folded,Set<RES> &resources) { if (p_root != p_node) { if (!p_node->get_owner()) { return; //not owned, bye @@ -102,6 +102,9 @@ void EditorFolding::_fill_folds(const Node *p_root, const Node *p_node, Array &p } } + if (p_node->is_displayed_folded()) { + nodes_folded.push_back(p_root->get_path_to(p_node)); + } PoolVector<String> unfolds = _get_unfolds(p_node); if (unfolds.size()) { @@ -112,20 +115,22 @@ void EditorFolding::_fill_folds(const Node *p_root, const Node *p_node, Array &p List<PropertyInfo> plist; p_node->get_property_list(&plist); for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) { - if (E->get().type == Variant::OBJECT) { - RES res = p_node->get(E->get().name); - if (res.is_valid() && !resources.has(res) && res->get_path() != String() && !res->get_path().is_resource_file()) { - - PoolVector<String> res_unfolds = _get_unfolds(res.ptr()); - resource_folds.push_back(res->get_path()); - resource_folds.push_back(res_unfolds); - resources.insert(res); + if (E->get().usage & PROPERTY_USAGE_EDITOR) { + if (E->get().type == Variant::OBJECT) { + RES res = p_node->get(E->get().name); + if (res.is_valid() && !resources.has(res) && res->get_path() != String() && !res->get_path().is_resource_file()) { + + PoolVector<String> res_unfolds = _get_unfolds(res.ptr()); + resource_folds.push_back(res->get_path()); + resource_folds.push_back(res_unfolds); + resources.insert(res); + } } } } for (int i = 0; i < p_node->get_child_count(); i++) { - _fill_folds(p_root, p_node->get_child(i), p_folds, resource_folds, resources); + _fill_folds(p_root, p_node->get_child(i), p_folds, resource_folds, nodes_folded,resources); } } void EditorFolding::save_scene_folding(const Node *p_scene, const String &p_path) { @@ -135,10 +140,12 @@ void EditorFolding::save_scene_folding(const Node *p_scene, const String &p_path Array unfolds, res_unfolds; Set<RES> resources; - _fill_folds(p_scene, p_scene, unfolds, res_unfolds, resources); + Array nodes_folded; + _fill_folds(p_scene, p_scene, unfolds, res_unfolds, nodes_folded, resources); config->set_value("folding", "node_unfolds", unfolds); config->set_value("folding", "resource_unfolds", res_unfolds); + config->set_value("folding", "nodes_folded", nodes_folded); String path = EditorSettings::get_singleton()->get_project_settings_dir(); String file = p_path.get_file() + "-folding-" + p_path.md5_text() + ".cfg"; @@ -166,6 +173,10 @@ void EditorFolding::load_scene_folding(Node *p_scene, const String &p_path) { if (config->has_section_key("folding", "resource_unfolds")) { res_unfolds = config->get_value("folding", "resource_unfolds"); } + Array nodes_folded; + if (config->has_section_key("folding", "nodes_folded")) { + nodes_folded = config->get_value("folding", "nodes_folded"); + } ERR_FAIL_COND(unfolds.size() & 1); ERR_FAIL_COND(res_unfolds.size() & 1); @@ -193,6 +204,14 @@ void EditorFolding::load_scene_folding(Node *p_scene, const String &p_path) { PoolVector<String> unfolds2 = res_unfolds[i + 1]; _set_unfolds(res.ptr(), unfolds2); } + + for(int i=0;i<nodes_folded.size();i++) { + NodePath fold_path = nodes_folded[i]; + if (p_scene->has_node(fold_path)) { + Node *node = p_scene->get_node(fold_path); + node->set_display_folded(true); + } + } } bool EditorFolding::has_folding_data(const String &p_path) { diff --git a/editor/editor_folding.h b/editor/editor_folding.h index e4f7dbba80..5fc980c4a9 100644 --- a/editor/editor_folding.h +++ b/editor/editor_folding.h @@ -38,7 +38,7 @@ class EditorFolding { PoolVector<String> _get_unfolds(const Object *p_object); void _set_unfolds(Object *p_object, const PoolVector<String> &p_unfolds); - void _fill_folds(const Node *p_root, const Node *p_node, Array &p_folds, Array &resource_folds, Set<RES> &resources); + void _fill_folds(const Node *p_root, const Node *p_node, Array &p_folds, Array &resource_folds, Array &nodes_folded, Set<RES> &resources); void _do_object_unfolds(Object *p_object, Set<RES> &resources); void _do_node_unfolds(Node *p_root, Node *p_node, Set<RES> &resources); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index c705f9af2b..f1f68a14de 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -470,10 +470,12 @@ bool EditorPropertyRevert::can_property_revert(Object *p_object, const StringNam if (!has_revert && !p_object->get_script().is_null()) { Ref<Script> scr = p_object->get_script(); - Variant orig_value; - if (scr->get_property_default_value(p_property, orig_value)) { - if (orig_value != p_object->get(p_property)) { - has_revert = true; + if (scr.is_valid()) { + Variant orig_value; + if (scr->get_property_default_value(p_property, orig_value)) { + if (orig_value != p_object->get(p_property)) { + has_revert = true; + } } } } @@ -668,11 +670,13 @@ void EditorProperty::_gui_input(const Ref<InputEvent> &p_event) { if (!object->get_script().is_null()) { Ref<Script> scr = object->get_script(); - Variant orig_value; - if (scr->get_property_default_value(property, orig_value)) { - emit_changed(property, orig_value); - update_property(); - return; + if (scr.is_valid()) { + Variant orig_value; + if (scr->get_property_default_value(property, orig_value)) { + emit_changed(property, orig_value); + update_property(); + return; + } } } @@ -1523,7 +1527,7 @@ void EditorInspector::update_tree() { if (E) { descr = E->get().brief_description; } - class_descr_cache[type2] = descr.word_wrap(80); + class_descr_cache[type2] = descr; } category->set_tooltip(p.name + "::" + (class_descr_cache[type2] == "" ? "" : class_descr_cache[type2])); @@ -1675,7 +1679,7 @@ void EditorInspector::update_tree() { while (F && descr == String()) { for (int i = 0; i < F->get().properties.size(); i++) { if (F->get().properties[i].name == propname.operator String()) { - descr = F->get().properties[i].description.strip_edges().word_wrap(80); + descr = F->get().properties[i].description.strip_edges(); break; } } diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 8a67002503..09e582bbe7 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -65,6 +65,7 @@ #include "editor/import/resource_importer_obj.h" #include "editor/import/resource_importer_scene.h" #include "editor/import/resource_importer_texture.h" +#include "editor/import/resource_importer_texture_atlas.h" #include "editor/import/resource_importer_wav.h" #include "editor/plugins/animation_blend_space_1d_editor.h" #include "editor/plugins/animation_blend_space_2d_editor.h" @@ -1306,6 +1307,7 @@ void EditorNode::_dialog_action(String p_file) { _save_default_environment(); _save_scene_with_preview(p_file, scene_idx); _add_to_recent_scenes(p_file); + save_layout(); if (scene_idx != -1) _discard_changes(); @@ -2521,6 +2523,7 @@ int EditorNode::_next_unsaved_scene(bool p_valid_filename, int p_start) { void EditorNode::_exit_editor() { exiting = true; resource_preview->stop(); //stop early to avoid crashes + _save_docks(); get_tree()->quit(); } @@ -4069,6 +4072,7 @@ void EditorNode::_load_open_scenes_from_config(Ref<ConfigFile> p_layout, const S for (int i = 0; i < scenes.size(); i++) { load_scene(scenes[i]); } + save_layout(); restoring_scenes = false; } @@ -5119,6 +5123,10 @@ EditorNode::EditorNode() { import_image.instance(); ResourceFormatImporter::get_singleton()->add_importer(import_image); + Ref<ResourceImporterTextureAtlas> import_texture_atlas; + import_texture_atlas.instance(); + ResourceFormatImporter::get_singleton()->add_importer(import_texture_atlas); + Ref<ResourceImporterCSVTranslation> import_csv_translation; import_csv_translation.instance(); ResourceFormatImporter::get_singleton()->add_importer(import_csv_translation); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 31f53305e2..9d936d9b99 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -2993,13 +2993,17 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ EditorPropertyClassName *editor = memnew(EditorPropertyClassName); editor->setup("Object", p_hint_text); add_property_editor(p_path, editor); - } else if (p_hint == PROPERTY_HINT_DIR || p_hint == PROPERTY_HINT_FILE || p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE) { + } else if (p_hint == PROPERTY_HINT_DIR || p_hint == PROPERTY_HINT_FILE || p_hint == PROPERTY_HINT_SAVE_FILE || p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE) { Vector<String> extensions = p_hint_text.split(","); bool global = p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE; bool folder = p_hint == PROPERTY_HINT_DIR || p_hint == PROPERTY_HINT_GLOBAL_DIR; + bool save = p_hint == PROPERTY_HINT_SAVE_FILE; EditorPropertyPath *editor = memnew(EditorPropertyPath); editor->setup(extensions, folder, global); + if (save) { + editor->set_save_mode(); + } add_property_editor(p_path, editor); } else if (p_hint == PROPERTY_HINT_METHOD_OF_VARIANT_TYPE || p_hint == PROPERTY_HINT_METHOD_OF_BASE_TYPE || diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 0869f6ce77..9641e10114 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -687,7 +687,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_constant("button_margin", "Tree", default_margin_size * EDSCALE); theme->set_constant("draw_relationship_lines", "Tree", relationship_line_opacity >= 0.01); theme->set_constant("draw_guides", "Tree", relationship_line_opacity < 0.01); - theme->set_constant("scroll_border", "Tree", default_margin_size * EDSCALE); + theme->set_constant("scroll_border", "Tree", 40 * EDSCALE); theme->set_constant("scroll_speed", "Tree", 12); Ref<StyleBoxFlat> style_tree_btn = style_default->duplicate(); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 5a1383be6d..aade606412 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -1021,6 +1021,7 @@ void FileSystemDock::_try_move_item(const FileOrFolder &p_item, const String &p_ for (int j = 0; j < ed->get_edited_scene_count(); j++) { if (ed->get_scene_path(j) == file_changed_paths[i]) { ed->get_edited_scene_root(j)->set_filename(new_item_path); + editor->save_layout(); break; } } @@ -1256,6 +1257,10 @@ void FileSystemDock::_rename_operation_confirm() { return; } + if (EditorFileSystem::get_singleton()->is_group_file(old_path)) { + EditorFileSystem::get_singleton()->move_group_file(old_path, new_path); + } + //Present a more user friendly warning for name conflict DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES); #if defined(WINDOWS_ENABLED) || defined(UWP_ENABLED) @@ -1353,6 +1358,16 @@ void FileSystemDock::_move_operation_confirm(const String &p_to_path, bool overw } } + //check groups + for (int i = 0; i < to_move.size(); i++) { + + print_line("is group: " + to_move[i].path + ": " + itos(EditorFileSystem::get_singleton()->is_group_file(to_move[i].path))); + if (to_move[i].is_file && EditorFileSystem::get_singleton()->is_group_file(to_move[i].path)) { + print_line("move to: " + p_to_path.plus_file(to_move[i].path.get_file())); + EditorFileSystem::get_singleton()->move_group_file(to_move[i].path, p_to_path.plus_file(to_move[i].path.get_file())); + } + } + Map<String, String> file_renames; Map<String, String> folder_renames; bool is_moved = false; diff --git a/editor/icons/icon_auto_key.svg b/editor/icons/icon_auto_key.svg new file mode 100644 index 0000000000..cbafe1ac38 --- /dev/null +++ b/editor/icons/icon_auto_key.svg @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16" + height="16" + version="1.1" + viewBox="0 0 16 16" + id="svg6" + sodipodi:docname="icon_auto_key.svg" + inkscape:version="0.92.4 (5da689c313, 2019-01-14)"> + <metadata + id="metadata12"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs10" /> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1854" + inkscape:window-height="1016" + id="namedview8" + showgrid="false" + inkscape:zoom="10.429825" + inkscape:cx="10.199345" + inkscape:cy="-4.0344119" + inkscape:window-x="66" + inkscape:window-y="27" + inkscape:window-maximized="1" + inkscape:current-layer="svg6" /> + <path + style="fill:#e0e0e0;fill-opacity:1;stroke-width:0.0333107" + d="M 3.5469681,13.426786 C 2.7965829,13.263778 2.2774312,12.503915 2.4037297,11.753472 c 0.1081234,-0.642451 0.6006808,-1.135008 1.2431317,-1.243131 0.9667125,-0.162696 1.8555225,0.726112 1.6928259,1.692826 -0.103766,0.616558 -0.5592173,1.098057 -1.1588427,1.225117 -0.2719576,0.05763 -0.3626872,0.05741 -0.6338765,-0.0014 z m 8.0861339,-0.08275 c -0.746862,-0.13829 -1.23937,-0.720718 -1.23937,-1.465649 0,-0.527377 0.244831,-0.978806 0.679757,-1.253362 0.471386,-0.297574 1.114188,-0.297574 1.585574,0 0.682727,0.430986 0.892336,1.362194 0.460575,2.046149 -0.307786,0.487563 -0.940521,0.773963 -1.486536,0.672862 z M 0.60726032,9.8305658 V 7.7161233 L 1.1770842,7.7070075 1.7469079,7.6978939 3.1889882,5.1995916 4.6310686,2.7012893 h 3.1726318 3.1726316 l 1.442755,2.4983023 1.442755,2.4983023 0.651097,0.00903 0.651096,0.00903 v 2.1145264 2.1145257 h -0.566282 -0.566281 v -0.161225 c 0,-0.234927 -0.113135,-0.639704 -0.255664,-0.914727 -0.16895,-0.326004 -0.574198,-0.731251 -0.900202,-0.9002019 -0.656732,-0.3403483 -1.428549,-0.3403483 -2.085281,0 -0.326004,0.1689519 -0.731252,0.5741989 -0.9002019,0.9002029 -0.1425297,0.275023 -0.2556639,0.6798 -0.2556639,0.914727 v 0.161225 H 7.8570969 6.0797346 L 6.0617736,11.686851 C 6.006289,10.889347 5.447548,10.170679 4.6603773,9.884336 4.4466221,9.8065798 4.3737631,9.797427 3.9716406,9.7978134 3.5871254,9.7981885 3.4905638,9.809405 3.3054265,9.8752358 2.5067319,10.159236 1.9362359,10.884501 1.8813215,11.68568 l -0.017772,0.259329 H 1.2354063 0.60726287 Z M 12.399247,7.7466889 c 0,-0.037287 -0.02623,-0.1073444 -0.0583,-0.1556843 -0.03206,-0.04834 -0.561225,-0.958444 -1.17592,-2.0224529 L 10.047407,3.6339894 7.6977565,3.6254406 C 5.4917229,3.6174174 5.3450379,3.6204563 5.2979001,3.6754094 5.1898818,3.8013046 2.9723198,7.6840061 2.9723198,7.7472381 c 0,0.067139 0.00758,0.067247 4.7134636,0.067247 h 4.7134636 z" + id="path6243" + inkscape:connector-curvature="0" /> +</svg> diff --git a/editor/icons/icon_sprite_sheet.svg b/editor/icons/icon_sprite_sheet.svg new file mode 100644 index 0000000000..eeb804f8b9 --- /dev/null +++ b/editor/icons/icon_sprite_sheet.svg @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16" + height="16" + version="1.1" + viewBox="0 0 16 16" + id="svg6" + sodipodi:docname="icon_sprite_sheet.svg" + inkscape:version="0.92.3 (2405546, 2018-03-11)"> + <metadata + id="metadata12"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs10" /> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="773" + inkscape:window-height="480" + id="namedview8" + showgrid="false" + inkscape:zoom="14.75" + inkscape:cx="8" + inkscape:cy="8" + inkscape:window-x="551" + inkscape:window-y="278" + inkscape:window-maximized="0" + inkscape:current-layer="g4" /> + <g + transform="translate(0 -1036.4)" + id="g4"> + <path + transform="translate(0 1036.4)" + d="m3 1c-1.1046 0-2 0.89543-2 2v10c0 1.1046 0.89543 2 2 2h10c1.1046 0 2-0.89543 2-2v-10c0-1.1046-0.89543-2-2-2h-10zm0 2h2v2h-2v-2zm4 0h2v2h-2v-2zm4 0h2v2h-2v-2zm-8 4h2v2h-2v-2zm4 0h2v2h-2v-2zm4 0h2v2h-2v-2zm-8 4h2v2h-2v-2zm4 0h2v2h-2v-2zm4 0h2v2h-2v-2z" + fill="#a5efac" + id="path2" + style="fill:#e0e0e0;fill-opacity:1" /> + </g> +</svg> diff --git a/editor/import/atlas_import_failed.xpm b/editor/import/atlas_import_failed.xpm new file mode 100644 index 0000000000..52db6b76a6 --- /dev/null +++ b/editor/import/atlas_import_failed.xpm @@ -0,0 +1,414 @@ +/* XPM */ +static const char * atlas_import_failed_xpm[] = { +"128 128 283 2", +" c None", +". c #FFFFFF", +"+ c #FFDADA", +"@ c #FF0000", +"# c #FFD8D8", +"$ c #FFF7F7", +"% c #FF2E2E", +"& c #FFD4D4", +"* c #FFD6D6", +"= c #FFE3E3", +"- c #FFB3B3", +"; c #FFC8C8", +"> c #FF3535", +", c #FF8D8D", +"' c #FF7878", +") c #FF6E6E", +"! c #FFB5B5", +"~ c #FF0D0D", +"{ c #FFF0F0", +"] c #FFE8E8", +"^ c #FFC2C2", +"/ c #FFEDED", +"( c #FFBBBB", +"_ c #FFB9B9", +": c #FFA4A4", +"< c #FFFEFE", +"[ c #FFD9D9", +"} c #FF9393", +"| c #FF5858", +"1 c #FF3232", +"2 c #FF7575", +"3 c #FFC9C9", +"4 c #FFFCFC", +"5 c #FFBDBD", +"6 c #FF3838", +"7 c #FF9494", +"8 c #FFE2E2", +"9 c #FFD1D1", +"0 c #FFDEDE", +"a c #FFCACA", +"b c #FF6969", +"c c #FF8484", +"d c #FFEAEA", +"e c #FFE9E9", +"f c #FF3B3B", +"g c #FFC0C0", +"h c #FF6868", +"i c #FF7373", +"j c #FFF6F6", +"k c #FFADAD", +"l c #FF5D5D", +"m c #FF2626", +"n c #FF5C5C", +"o c #FFABAB", +"p c #FFCECE", +"q c #FF7070", +"r c #FF5555", +"s c #FF1C1C", +"t c #FFF4F4", +"u c #FF8282", +"v c #FF6060", +"w c #FFE7E7", +"x c #FF9D9D", +"y c #FF5656", +"z c #FF4242", +"A c #FF9B9B", +"B c #FFD0D0", +"C c #FFF8F8", +"D c #FF6A6A", +"E c #FF5151", +"F c #FFFBFB", +"G c #FF4949", +"H c #FFCDCD", +"I c #FFDDDD", +"J c #FF9E9E", +"K c #FFF9F9", +"L c #FFDCDC", +"M c #FF8F8F", +"N c #FFCBCB", +"O c #FFF5F5", +"P c #FF4747", +"Q c #FF9C9C", +"R c #FFEEEE", +"S c #FFFAFA", +"T c #FF1616", +"U c #FF8888", +"V c #FFC5C5", +"W c #FF2222", +"X c #FF4B4B", +"Y c #FFB8B8", +"Z c #FF7F7F", +"` c #FFE6E6", +" . c #FF8080", +".. c #FFB4B4", +"+. c #FFC3C3", +"@. c #FFD2D2", +"#. c #FFD7D7", +"$. c #FFDFDF", +"%. c #FFB7B7", +"&. c #FFF1F1", +"*. c #FF6262", +"=. c #FF8A8A", +"-. c #FFA9A9", +";. c #FFAEAE", +">. c #FFAAAA", +",. c #FF8B8B", +"'. c #FF4F4F", +"). c #FFFDFD", +"!. c #FFA3A3", +"~. c #FF2A2A", +"{. c #FFCFCF", +"]. c #FF8585", +"^. c #FF7676", +"/. c #FFD3D3", +"(. c #FFD5D5", +"_. c #FF8181", +":. c #FFC6C6", +"<. c #FFDBDB", +"[. c #FF9090", +"}. c #FFAFAF", +"|. c #FFA1A1", +"1. c #FFBABA", +"2. c #FF6C6C", +"3. c #FF5F5F", +"4. c #FF3D3D", +"5. c #FF9999", +"6. c #FFE0E0", +"7. c #FF8383", +"8. c #FFEFEF", +"9. c #FFF3F3", +"0. c #FFA8A8", +"a. c #FFB6B6", +"b. c #FF9F9F", +"c. c #FF4545", +"d. c #FFE5E5", +"e. c #FFE4E4", +"f. c #FFC7C7", +"g. c #FF6565", +"h. c #FFACAC", +"i. c #FF5A5A", +"j. c #FF7272", +"k. c #FF7C7C", +"l. c #FFBFBF", +"m. c #FF7171", +"n. c #FFECEC", +"o. c #FF8989", +"p. c #FF7777", +"q. c #FFC4C4", +"r. c #FF9898", +"s. c #FF8C8C", +"t. c #FF7A7A", +"u. c #FF8E8E", +"v. c #FFF2F2", +"w. c #FF9797", +"x. c #FFC1C1", +"y. c #FFA6A6", +"z. c #FFEBEB", +"A. c #FF4040", +"B. c #EDEDED", +"C. c #000000", +"D. c #AAAAAA", +"E. c #F6F6F6", +"F. c #1C1C1C", +"G. c #888888", +"H. c #7C7C7C", +"I. c #626262", +"J. c #B3B3B3", +"K. c #2A2A2A", +"L. c #959595", +"M. c #FDFDFD", +"N. c #C5C5C5", +"O. c #666666", +"P. c #353535", +"Q. c #777777", +"R. c #DEDEDE", +"S. c #6C6C6C", +"T. c #F5F5F5", +"U. c #ADADAD", +"V. c #DDDDDD", +"W. c #D8D8D8", +"X. c #B4B4B4", +"Y. c #FAFAFA", +"Z. c #949494", +"`. c #3B3B3B", +" + c #A8A8A8", +".+ c #C8C8C8", +"++ c #D4D4D4", +"@+ c #B9B9B9", +"#+ c #2E2E2E", +"$+ c #FEFEFE", +"%+ c #BABABA", +"&+ c #FCFCFC", +"*+ c #B2B2B2", +"=+ c #CACACA", +"-+ c #696969", +";+ c #222222", +">+ c #F2F2F2", +",+ c #555555", +"'+ c #C4C4C4", +")+ c #EBEBEB", +"!+ c #727272", +"~+ c #585858", +"{+ c #0D0D0D", +"]+ c #B1B1B1", +"^+ c #E5E5E5", +"/+ c #C0C0C0", +"(+ c #8F8F8F", +"_+ c #4D4D4D", +":+ c #F4F4F4", +"<+ c #7D7D7D", +"[+ c #E4E4E4", +"}+ c #F3F3F3", +"|+ c #383838", +"1+ c #A9A9A9", +"2+ c #D6D6D6", +"3+ c #D5D5D5", +"4+ c #5F5F5F", +"5+ c #C6C6C6", +"6+ c #E2E2E2", +"7+ c #FBFBFB", +"8+ c #404040", +"9+ c #909090", +"0+ c #EEEEEE", +"a+ c #878787", +"b+ c #E8E8E8", +"c+ c #494949", +"d+ c #424242", +"e+ c #E6E6E6", +"f+ c #CFCFCF", +"g+ c #DCDCDC", +"h+ c #161616", +"i+ c #BBBBBB", +"j+ c #CCCCCC", +"k+ c #B0B0B0", +"l+ c #C7C7C7", +"m+ c #858585", +"n+ c #F8F8F8", +"o+ c #D7D7D7", +"p+ c #BDBDBD", +"q+ c #ECECEC", +"r+ c #939393", +"s+ c #A1A1A1", +"t+ c #7A7A7A", +"u+ c #4B4B4B", +"v+ c #E9E9E9", +"w+ c #717171", +"x+ c #AFAFAF", +"y+ c #454545", +"z+ c #F9F9F9", +"A+ c #DBDBDB", +"B+ c #C1C1C1", +"C+ c #707070", +"D+ c #323232", +"E+ c #9D9D9D", +"F+ c #D1D1D1", +"G+ c #6D6D6D", +"H+ c #262626", +"I+ c #6E6E6E", +"J+ c #808080", +"K+ c #BFBFBF", +"L+ c #999999", +"M+ c #F1F1F1", +"N+ c #DADADA", +"O+ c #9F9F9F", +"P+ c #8B8B8B", +"Q+ c #7F7F7F", +"R+ c #9E9E9E", +"S+ c #F0F0F0", +"T+ c #A4A4A4", +"U+ c #A5A5A5", +"V+ c #CDCDCD", +"W+ c #CBCBCB", +"X+ c #9B9B9B", +"Y+ c #D9D9D9", +"Z+ c #A0A0A0", +"`+ c #9C9C9C", +" @ c #C2C2C2", +".@ c #636363", +"+@ c #D0D0D0", +"@@ c #6A6A6A", +"#@ c #898989", +"$@ c #C3C3C3", +"%@ c #A7A7A7", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . + @ # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . $ % @ & . . . . . * @ + . . . . + @ # . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . * @ + . . . . . . . . . . . . . ", +". . . . . . . . . . . . . ; @ > , . . . . . * @ + . . . . + @ # . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . * @ + . . . . . . . . . . . . . ", +". . . . . . . . . . . . . ' ) ! ~ { . . . . * @ + . . . . + @ # . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . * @ + . . . . . . . . . . . . . ", +". . . . . . . . . . . . ] @ ^ / @ ( . . . _ @ @ @ @ @ : . + @ # . . < [ } | 1 2 3 . . . . 4 5 ) 6 | 7 8 . . . . . . . . = @ - . . 9 @ 0 a b > c d . e , f | g . . . . 9 @ 0 a h % i & . . . . j k l m n o j . . . 9 @ 0 p q m r @ @ @ @ @ : . . . . . . . . . . ", +". . . . . . . . . . . . k s t . u v < . . _ @ @ @ @ @ : . + @ # . . w @ @ @ @ @ @ 5 . . . k @ @ @ @ @ % . . . . . . . . = @ - . . 9 @ x @ @ @ @ y d z @ @ @ @ * . . . 9 @ A @ @ @ @ @ B . . C D @ @ @ @ @ h C . . 9 @ x @ @ @ E @ @ @ @ @ : . . . . . . . . . . ", +". . . . . . . . . . . F G 7 . . H @ I . . . * @ + . . . . + @ # . . ] J 8 j K 0 h 6 K . . l l = F j L M . . . . . . . . = @ - . . 9 @ % N j O J @ P Q R S & T U . . . 9 @ s ^ O j V W X F . Y @ Z ` S w .@ ... . 9 @ s +.t . . * @ + . . . . . . . . . . . . . ", +". . . . . . . . . . . @.@ #.. . K 6 x . . . * @ + . . . . + @ # . . . . . . . . # @ $.. . ' M . . . . . . . . . . . . . = @ - . . 9 @ %.. . . &.@ *.4 . . . =.r . . . 9 @ -.. . . . ;.@ 0 . q 6 t . . . t f h . . 9 @ >.. . . . * @ + . . . . . . . . . . . . . ", +". . . . . . . . . . . ,.'.).. . . !.~.j . . * @ + . . . . + @ # . . . {.].P ~ @ @ @ 9 . . 8 r ^.- /.j . . . . . . . . . = @ - . . 9 @ + . . . . @ A . . . . !.~ . . . 9 @ (.. . . . # @ N . 1 _.. . . . . c s . . 9 @ (.. . . . * @ + . . . . . . . . . . . . . ", +". . . . . . . . . . { ~ @ @ @ @ @ @ @ :.. . * @ + . . . . + @ # . . ^ @ @ @ @ @ @ @ H . . . F <._ [.> }.. . . . . . . . = @ - . . 9 @ 0 . . . . @ |.. . . . : @ . . . 9 @ (.. . . . # @ N . 1 u . . . . . c s . . 9 @ 0 . . . . * @ + . . . . . . . . . . . . . ", +". . . . . . . . . . 1.@ @ @ @ @ @ @ @ ^.. . #.@ # . . . . + @ # . . 2.r $.$ < . [ @ H . . . . . . . + @ * . . . . . . . = @ - . . 9 @ 0 . . . . @ |.. . . . : @ . . . 9 @ -.. . . . ;.@ 0 . q 6 t . . . O f h . . 9 @ 0 . . . . #.@ # . . . . . . . . . . . . . ", +". . . . . . . . . < 3.4.K . . . . . 5.@ w . 6.@ ;.F . . . + @ # . . 2.n ` 4 $ + *.@ H . 4 7.@.8.4 9.k @ ^ . . . . . . . = @ - . . 9 @ 0 . . . . @ |.. . . . : @ . . . 9 @ s ^ O j V W X F . %.@ u w F ] u @ - . . 9 @ 0 . . . . 6.@ ;.F . . . . . . . . . . . . ", +". . . . . . . . . L @ 0.. . . . . . L @ o . K > @ @ @ : . + @ # . . a.@ @ @ @ @ b.@ H . F @ @ @ @ @ @ > / . . . . . . . = @ - . . 9 @ 0 . . . . @ |.. . . . : @ . . . 9 @ A @ @ @ @ @ B . . C D @ @ @ @ @ h $ . . 9 @ 0 . . . . K > @ @ @ : . . . . . . . . . . ", +". . . . . . . . . A @ ` . . . . . . < *.c.F . # ^.~.@ : . + @ # . . 4 _ *.% q N d.@ H . . e.: h % l b.e . . . . . . . . = @ - . . 9 @ 0 . . . . @ |.. . . . : @ . . . 9 @ 0 f.g.~.i /.. . . . j h.n W i.>.O . . . 9 @ 0 . . . . . # ^.~.@ : . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 @ 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 @ 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 @ 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 @ 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + @ # . . + @ # . . . . . . . . . . . . . . . . . . . . j.k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ @ @ @ @ @ @ # . . . . . . . . . . + @ # . . + @ # . . . . . . . . . . . . . . . . . . . . j.k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ @ @ @ @ @ @ # . . . . . . . . . . . . . . . + @ # . . . . . . . . . . . . . . . . . . . . j.k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . . . . . . . . . . . . . . . . . + @ # . . . . . . . . . . . . . . . . . . . . j.k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . < [ } | 1 2 3 . . . . + @ # . . + @ # . . . 4 l.m.% G 5.n.. . . . &.Q z c.x O j.k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . w @ @ @ @ @ @ 5 . . . + @ # . . + @ # . . ).o.@ @ @ @ @ c./ . . $ v @ @ @ @ p.2.k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ @ @ @ @ @ U ] J 8 j K 0 h 6 K . . + @ # . . + @ # . . q.@ } w F O q.s r.. . a.@ s.d S 8 ) @ k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ @ @ @ @ @ U . . . . . . # @ $.. . + @ # . . + @ # . . t.| ).. . . . !.> < . q G C . . . d @ k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . . {.].P ~ @ @ @ 9 . . + @ # . . + @ # . . > @ @ @ @ @ @ @ @ O . % u.. . . . . y k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . ^ @ @ @ @ @ @ @ H . . + @ # . . + @ # . . 1 @ @ @ @ @ @ @ @ v.. % u.. . . . . r k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . 2.r $.$ < . [ @ H . . + @ # . . + @ # . . ^.h < . . . . . . . . q G C . . . d @ k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . 2.n ` 4 $ + *.@ H . . + @ # . . + @ # . . +.@ w.e.C S d x.y.. . a.@ s.z.S 8 ) @ k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . a.@ @ @ @ @ b.@ H . . + @ # . . + @ # . . ).M @ @ @ @ @ @ s.. . $ v @ @ @ @ ^.2.k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . = @ - . . . . . 4 _ *.% q N d.@ H . . + @ # . . + @ # . . . ).f.k.6 z ' Y v.. . . &.A A.z A O j.k.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.B.C.D.B.C.D.. . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E.C.C.C.C.F.. B.C.D.B.C.D.B.C.D.. . . . C.G.. . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E.C.H.. . . . . . . B.C.D.B.C.D.. . . . C.G.. . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E.C.H.. . . . B.C.D.B.C.D.B.C.D.. . . I.C.C.C.J.B.C.C.C.K.L.M.. N.O.P.Q.R.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E.C.C.C.C.H.. B.C.D.B.C.D.B.C.D.. . . . C.G.. . B.C.S.T.U.C.V.W.C.X.Y.Z.`.E.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E.C.H.. . . . B.C.D.B.C.D.B.C.D.. . . . C.G.. . B.C. +. .+C.++@+C.C.C.C.C.R.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E.C.H.. . . . B.C.D.B.C.D.B.C.D.. . . . #+Q.$+. B.C.D.. .+C.++W.C.%+&+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E.C.H.. . . . B.C.D.B.C.D.B.C.D.. . . . *+`.C.=+B.C.D.. .+C.++. N.-+;+C.C.Y.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . B.C.D.B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Y.C.>+,+R.. . '+C.C.)+. . . C.G.. . B.C.D.. . . . . . . . . . . . . . . . E.C.C.C.C.F.. B.C.D.B.C.D.. . . . . . . Y.C.>+,+R.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Y.C.>+,+R.. . !+~+{+]+. . . C.G.. . B.C.D.. . . . . . . . . . . . . . . . E.C.H.. . . . . . . B.C.D.. . . . . . . Y.C.>+,+R.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Y.C.>+,+R.. ^+C./+(+_+&+. I.C.C.C.J.B.C.D.:+C.C.P.<+[+. }+G.|+C.C.>+. . . E.C.H.. . . . B.C.D.B.C.D.. N.O.P.Q.R.. Y.C.>+,+R.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1+;+T.2+C.3+. . C.G.. . B.C.D.. . . T.4+Q.. 5+C.X.6+Y.. . . . E.C.C.C.C.H.. B.C.D.B.C.D.W.C.X.Y.Z.`.E.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7+8+C.C.C.C.9+. . C.G.. . B.C.D.0+a+8+C.C.|+. b+c+C.C.d+e+. . . E.C.H.. . . . B.C.D.B.C.D.@+C.C.C.C.C.R.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . f+C.g+. . J.h+>+. #+Q.$+. B.C.D.i+C.j+0+,+#+. . &+e+k+C.l+. . . E.C.H.. . . . B.C.D.B.C.D.W.C.%+&+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . m+h+n+. . o+C.p+. *+`.C.=+B.C.D.q+!+{+C.C.#+. =+C.C.8+r+E.. . . E.C.H.. . . . B.C.D.B.C.D.. N.-+;+C.C.Y.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . V.4+C.s+C.U.. . . . . . . B.C.D.. . . . N.C.W.. . . B.C.D.. . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . t+u+Y.v+C.U.. . . . . . . B.C.D.. . . . N.C.W.. . . B.C.D.. . . . . . . . . . . C.G.. . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . u+w+. . . . . . . . . . . B.C.D.. . . . N.C.W.. . . . . . . . . . . . . . . . . C.G.. . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . H.C.C.C.C.C.U.. N.O.P.Q.R.. B.C.D.. x+d+C.C.C.W.. . . B.C.D.B.C.C.C.K.L.M.. . . I.C.C.C.J.B.C.C.C.K.L.M.. N.O.P.Q.R.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . y+!+. v+C.U.W.C.X.Y.Z.`.E.B.C.D.2+C.x+z+(+C.W.. . . B.C.D.B.C.S.T.U.C.V.. . . . C.G.. . B.C.S.T.U.C.V.W.C.X.Y.Z.`.E.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . y+!+. v+C.U.@+C.C.C.C.C.R.B.C.D.%+C.A+. B+C.W.. . . B.C.D.B.C. +. .+C.++. . . . C.G.. . B.C. +. .+C.++@+C.C.C.C.C.R.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . y+!+. v+C.U.W.C.%+&+. . . B.C.D.2+C.k+z+9+C.W.. . . B.C.D.B.C.D.. .+C.++. . . . #+Q.$+. B.C.D.. .+C.++W.C.%+&+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . y+!+. v+C.U.. N.-+;+C.C.Y.B.C.D.. x+d+C.C.C.W.. . . B.C.D.B.C.D.. .+C.++. . . . *+`.C.=+B.C.D.. .+C.++. N.-+;+C.C.Y.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . N.C.W.. . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . N.C.W.. . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C.G.. . . . . . . . . N.C.W.. . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . N.C.W.. . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C.G.. . . . . . . . . N.C.W.. . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . N.C.W.. . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . B.C.D.)+C.C.C.K.C+O.K.!+B.B.C.C.C.D+E+7+. '+O.P.C+F+. B.C.C.C.,+I.C.C.C.J.. . . . x+d+C.C.C.W.. '+O.P.C+F+. . .+G+H+C.R.B.C.D.>+I+I.q+. . . :+C.C.P.<+[+. B.C.C.C.K.L.M.. x+d+C.C.C.W.. . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . B.C.D.)+C.!+T.J+C.=+g+C.]+B.C.I+E.=+C.K+W.C.]+Y.L+h+b+B.C.O.M+. . C.G.. . . . . 2+C.x+z+(+C.W.W.C.]+Y.L+h+b+N+C.O+z+. . B.C.P+4+Q+T.. . . . . . . T.4+Q.. B.C.S.T.U.C.V.2+C.x+z+(+C.W.. . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . B.C.D.)+C. +. R+C.M+S+C.T+B.C.U+. M+C.E+@+C.V.. =+C.V+B.C. +. . . C.G.. . . . . %+C.A+. B+C.W.@+C.V.. =+C.V+@+C.A+. . . B.C.C.C.R.. . . . . 0+a+8+C.C.|+. B.C. +. .+C.++%+C.A+. B+C.W.. . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . B.C.D.)+C.D.. R+C.>+S+C.T+B.C.C+E.W+C.K+W.C.*+Y.X+{+b+B.C.D.. . . #+Q.$+. . . . 2+C.k+z+9+C.W.W.C.*+Y.X+{+b+Y+C.s+z+. . B.C.Z+m+|+V.. . . . i+C.j+0+,+#+. B.C.D.. .+C.++2+C.k+z+9+C.W.. . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . B.C.D.)+C.D.. R+C.>+S+C.T+B.C.C.C.D+`+7+. @.@D+G++@. B.C.D.. . . *+`.C.=+. . . . x+d+C.C.C.W.. @.@D+G++@. . l+@@H+C.R.B.C.D.Y.#@K.2+. . . q+!+{+C.C.#+. B.C.D.. .+C.++. x+d+C.C.C.W.. . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C.G.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C.G.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.C.C.,+. N.O.P.Q.R.. . . . . B.C.D.)+C.C.C.K.C+O.K.!+B.B.C.C.C.D+E+7+. '+O.P.C+F+. B.C.C.C.,+I.C.C.C.J.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.O.M+. W.C.X.Y.Z.`.E.$@C.C.%@B.C.D.)+C.!+T.J+C.=+g+C.]+B.C.I+E.=+C.K+W.C.]+Y.L+h+b+B.C.O.M+. . C.G.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C. +. . @+C.C.C.C.C.R.. . . . B.C.D.)+C. +. R+C.M+S+C.T+B.C.U+. M+C.E+@+C.V.. =+C.V+B.C. +. . . C.G.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.. . W.C.%+&+. . . . . . . B.C.D.)+C.D.. R+C.>+S+C.T+B.C.C+E.W+C.K+W.C.*+Y.X+{+b+B.C.D.. . . #+Q.$+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.. . . N.-+;+C.C.Y.. . . . B.C.D.)+C.D.. R+C.>+S+C.T+B.C.C.C.D+`+7+. @.@D+G++@. B.C.D.. . . *+`.C.=+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.C.D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . "}; diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index fb2e3c0401..ab515785da 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -279,7 +279,24 @@ static String _fixstr(const String &p_what, const String &p_str) { return what; } -Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<ArrayMesh>, Ref<Shape> > &collision_map, LightBakeMode p_light_bake_mode) { +static void _gen_shape_list(const Ref<Mesh> &mesh, List<Ref<Shape> > &r_shape_list, bool p_convex) { + + if (!p_convex) { + + Ref<Shape> shape = mesh->create_trimesh_shape(); + r_shape_list.push_back(shape); + } else { + + Vector<Ref<Shape> > cd = mesh->convex_decompose(); + if (cd.size()) { + for (int i = 0; i < cd.size(); i++) { + r_shape_list.push_back(cd[i]); + } + } + } +} + +Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh>, List<Ref<Shape> > > &collision_map, LightBakeMode p_light_bake_mode) { // children first for (int i = 0; i < p_node->get_child_count(); i++) { @@ -365,37 +382,52 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Array return p_node; MeshInstance *mi = Object::cast_to<MeshInstance>(p_node); if (mi) { - Node *col = NULL; - - if (_teststr(name, "colonly")) { - col = mi->create_trimesh_collision_node(); - if (col == NULL) { - ERR_PRINTS("Error generating collision for mesh: " + name); - } else { - - col->set_name(_fixstr(name, "colonly")); + Ref<Mesh> mesh = mi->get_mesh(); + + if (mesh.is_valid()) { + List<Ref<Shape> > shapes; + String fixed_name; + if (collision_map.has(mesh)) { + shapes = collision_map[mesh]; + } else if (_teststr(name, "colonly")) { + _gen_shape_list(mesh, shapes, false); + collision_map[mesh] = shapes; + } else if (_teststr(name, "convcolonly")) { + _gen_shape_list(mesh, shapes, true); + collision_map[mesh] = shapes; } - } else { - col = mi->create_convex_collision_node(); - if (col == NULL) { - ERR_PRINTS("Error generating collision for mesh: " + name); - } else { - col->set_name(_fixstr(name, "convcolonly")); + if (_teststr(name, "colonly")) { + fixed_name = _fixstr(name, "colonly"); + } else if (_teststr(name, "convcolonly")) { + fixed_name = _fixstr(name, "convcolonly"); } - } - if (col) { - Object::cast_to<Spatial>(col)->set_transform(mi->get_transform()); - p_node->replace_by(col); - memdelete(p_node); - p_node = col; + ERR_FAIL_COND_V(fixed_name == String(), NULL); + + if (shapes.size()) { + + StaticBody *col = memnew(StaticBody); + col->set_transform(mi->get_transform()); + col->set_name(fixed_name); + p_node->replace_by(col); + memdelete(p_node); + p_node = col; + + int idx = 0; + for (List<Ref<Shape> >::Element *E = shapes.front(); E; E = E->next()) { + + CollisionShape *cshape = memnew(CollisionShape); + cshape->set_shape(E->get()); + col->add_child(cshape); - StaticBody *sb = Object::cast_to<StaticBody>(col); - CollisionShape *colshape = Object::cast_to<CollisionShape>(sb->get_child(0)); - colshape->set_name("shape"); - colshape->set_owner(p_node->get_owner()); + cshape->set_name("shape" + itos(idx)); + cshape->set_owner(col->get_owner()); + idx++; + } + } } + } else if (p_node->has_meta("empty_draw_type")) { String empty_draw_type = String(p_node->get_meta("empty_draw_type")); StaticBody *sb = memnew(StaticBody); @@ -434,77 +466,91 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Array if (isroot) return p_node; - // get mesh instance and bounding box MeshInstance *mi = Object::cast_to<MeshInstance>(p_node); - AABB aabb = mi->get_aabb(); - - // create a new rigid body collision node - RigidBody *rigid_body = memnew(RigidBody); - Node *col = rigid_body; - ERR_FAIL_COND_V(!col, NULL); - - // remove node name postfix - col->set_name(_fixstr(name, "rigid")); - // get mesh instance xform matrix to the rigid body collision node - Object::cast_to<Spatial>(col)->set_transform(mi->get_transform()); - // save original node by duplicating it into a new instance and correcting the name - Node *mesh = p_node->duplicate(); - mesh->set_name(_fixstr(name, "rigid")); - // reset the xform matrix of the duplicated node so it can inherit parent node xform - Object::cast_to<Spatial>(mesh)->set_transform(Transform(Basis())); - // reparent the new mesh node to the rigid body collision node - p_node->add_child(mesh); - mesh->set_owner(p_node->get_owner()); - // replace the original node with the rigid body collision node - p_node->replace_by(col); - memdelete(p_node); - p_node = col; - - // create an alias for the rigid body collision node - RigidBody *rb = Object::cast_to<RigidBody>(col); - // create a new Box collision shape and set the right extents - Ref<BoxShape> shape = memnew(BoxShape); - shape->set_extents(aabb.get_size() * 0.5); - CollisionShape *colshape = memnew(CollisionShape); - colshape->set_name("shape"); - colshape->set_shape(shape); - // reparent the new collision shape to the rigid body collision node - rb->add_child(colshape); - colshape->set_owner(p_node->get_owner()); + Ref<Mesh> mesh = mi->get_mesh(); + + if (mesh.is_valid()) { + List<Ref<Shape> > shapes; + if (collision_map.has(mesh)) { + shapes = collision_map[mesh]; + } else { + _gen_shape_list(mesh, shapes, true); + } + + RigidBody *rigid_body = memnew(RigidBody); + rigid_body->set_name(_fixstr(name, "rigid")); + p_node->replace_by(rigid_body); + rigid_body->set_transform(mi->get_transform()); + p_node = rigid_body; + mi->set_name("mesh"); + mi->set_transform(Transform()); + rigid_body->add_child(mi); + mi->set_owner(rigid_body->get_owner()); + + int idx = 0; + for (List<Ref<Shape> >::Element *E = shapes.front(); E; E = E->next()) { + + CollisionShape *cshape = memnew(CollisionShape); + cshape->set_shape(E->get()); + rigid_body->add_child(cshape); + + cshape->set_name("shape" + itos(idx)); + cshape->set_owner(p_node->get_owner()); + idx++; + } + } } else if ((_teststr(name, "col") || (_teststr(name, "convcol"))) && Object::cast_to<MeshInstance>(p_node)) { MeshInstance *mi = Object::cast_to<MeshInstance>(p_node); - Node *col; - if (_teststr(name, "col")) { - String new_name = _fixstr(name, "col"); - if (mi->get_parent() && !mi->get_parent()->has_node(new_name)) { - mi->set_name(new_name); + Ref<Mesh> mesh = mi->get_mesh(); + + if (mesh.is_valid()) { + List<Ref<Shape> > shapes; + String fixed_name; + if (collision_map.has(mesh)) { + shapes = collision_map[mesh]; + } else if (_teststr(name, "col")) { + _gen_shape_list(mesh, shapes, false); + collision_map[mesh] = shapes; + } else if (_teststr(name, "convcol")) { + _gen_shape_list(mesh, shapes, true); + collision_map[mesh] = shapes; } - col = mi->create_trimesh_collision_node(); - ERR_FAIL_COND_V(!col, NULL); - col->set_name("col"); - } else { - String new_name = _fixstr(name, "convcol"); - if (mi->get_parent() && !mi->get_parent()->has_node(new_name)) { - mi->set_name(new_name); + if (_teststr(name, "col")) { + fixed_name = _fixstr(name, "col"); + } else if (_teststr(name, "convcol")) { + fixed_name = _fixstr(name, "convcol"); } - col = mi->create_convex_collision_node(); - ERR_FAIL_COND_V(!col, NULL); - col->set_name("convcol"); - } + if (fixed_name != String()) { + if (mi->get_parent() && !mi->get_parent()->has_node(fixed_name)) { + mi->set_name(fixed_name); + } + } + + if (shapes.size()) { + StaticBody *col = memnew(StaticBody); + col->set_name("static_collision"); + mi->add_child(col); + col->set_owner(mi->get_owner()); - p_node->add_child(col); + int idx = 0; + for (List<Ref<Shape> >::Element *E = shapes.front(); E; E = E->next()) { - StaticBody *sb = Object::cast_to<StaticBody>(col); - CollisionShape *colshape = Object::cast_to<CollisionShape>(sb->get_child(0)); - colshape->set_name("shape"); - col->add_child(colshape); - colshape->set_owner(p_node->get_owner()); - sb->set_owner(p_node->get_owner()); + CollisionShape *cshape = memnew(CollisionShape); + cshape->set_shape(E->get()); + col->add_child(cshape); + + cshape->set_name("shape" + itos(idx)); + cshape->set_owner(p_node->get_owner()); + + idx++; + } + } + } } else if (_teststr(name, "navmesh") && Object::cast_to<MeshInstance>(p_node)) { @@ -574,48 +620,35 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Array Ref<ArrayMesh> mesh = mi->get_mesh(); if (!mesh.is_null()) { - if (_teststr(mesh->get_name(), "col") || _teststr(mesh->get_name(), "convcol")) { - Ref<Shape> shape; - if (_teststr(mesh->get_name(), "col")) { - mesh->set_name(_fixstr(mesh->get_name(), "col")); - - if (collision_map.has(mesh)) { - shape = collision_map[mesh]; - - } else { - - shape = mesh->create_trimesh_shape(); - if (!shape.is_null()) - collision_map[mesh] = shape; - } - } else if (_teststr(mesh->get_name(), "convcol")) { - mesh->set_name(_fixstr(mesh->get_name(), "convcol")); + List<Ref<Shape> > shapes; + if (collision_map.has(mesh)) { + shapes = collision_map[mesh]; + } else if (_teststr(mesh->get_name(), "col")) { + _gen_shape_list(mesh, shapes, false); + collision_map[mesh] = shapes; + mesh->set_name(_fixstr(mesh->get_name(), "col")); + } else if (_teststr(mesh->get_name(), "convcol")) { + _gen_shape_list(mesh, shapes, true); + collision_map[mesh] = shapes; + mesh->set_name(_fixstr(mesh->get_name(), "convcol")); + } - if (collision_map.has(mesh)) { - shape = collision_map[mesh]; + if (shapes.size()) { + StaticBody *col = memnew(StaticBody); + col->set_name("static_collision"); + p_node->add_child(col); + col->set_owner(p_node->get_owner()); - } else { + int idx = 0; + for (List<Ref<Shape> >::Element *E = shapes.front(); E; E = E->next()) { - shape = mesh->create_convex_shape(); - if (!shape.is_null()) - collision_map[mesh] = shape; - } - } - - if (!shape.is_null()) { - StaticBody *col = memnew(StaticBody); CollisionShape *cshape = memnew(CollisionShape); - cshape->set_shape(shape); + cshape->set_shape(E->get()); col->add_child(cshape); - col->set_transform(mi->get_transform()); - col->set_name(mi->get_name()); - p_node->replace_by(col); - memdelete(p_node); - p_node = col; - - cshape->set_name("shape"); + cshape->set_name("shape" + itos(idx)); cshape->set_owner(p_node->get_owner()); + idx++; } } } @@ -1269,7 +1302,7 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p float anim_optimizer_maxang = p_options["animation/optimizer/max_angle"]; int light_bake_mode = p_options["meshes/light_baking"]; - Map<Ref<ArrayMesh>, Ref<Shape> > collision_map; + Map<Ref<Mesh>, List<Ref<Shape> > > collision_map; scene = _fix_node(scene, scene, collision_map, LightBakeMode(light_bake_mode)); diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h index 99f8b1a8e0..b10c4da2e5 100644 --- a/editor/import/resource_importer_scene.h +++ b/editor/import/resource_importer_scene.h @@ -146,7 +146,7 @@ public: void _make_external_resources(Node *p_node, const String &p_base_path, bool p_make_animations, bool p_keep_animations, bool p_make_materials, bool p_keep_materials, bool p_make_meshes, Map<Ref<Animation>, Ref<Animation> > &p_animations, Map<Ref<Material>, Ref<Material> > &p_materials, Map<Ref<ArrayMesh>, Ref<ArrayMesh> > &p_meshes); - Node *_fix_node(Node *p_node, Node *p_root, Map<Ref<ArrayMesh>, Ref<Shape> > &collision_map, LightBakeMode p_light_bake_mode); + Node *_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh>, List<Ref<Shape> > > &collision_map, LightBakeMode p_light_bake_mode); void _create_clips(Node *scene, const Array &p_clips, bool p_bake_all); void _filter_anim_tracks(Ref<Animation> anim, Set<String> &keep); diff --git a/editor/import/resource_importer_texture_atlas.cpp b/editor/import/resource_importer_texture_atlas.cpp new file mode 100644 index 0000000000..35fdd32e2c --- /dev/null +++ b/editor/import/resource_importer_texture_atlas.cpp @@ -0,0 +1,382 @@ +#include "resource_importer_texture_atlas.h" + +#include "atlas_import_failed.xpm" +#include "core/io/image_loader.h" +#include "core/io/resource_saver.h" +#include "core/os/file_access.h" +#include "editor/editor_atlas_packer.h" +#include "scene/resources/mesh.h" +#include "scene/resources/texture.h" + +String ResourceImporterTextureAtlas::get_importer_name() const { + + return "texture_atlas"; +} + +String ResourceImporterTextureAtlas::get_visible_name() const { + + return "TextureAtlas"; +} +void ResourceImporterTextureAtlas::get_recognized_extensions(List<String> *p_extensions) const { + + ImageLoader::get_recognized_extensions(p_extensions); +} + +String ResourceImporterTextureAtlas::get_save_extension() const { + return "res"; +} + +String ResourceImporterTextureAtlas::get_resource_type() const { + + return "Texture"; +} + +bool ResourceImporterTextureAtlas::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { + + return true; +} + +int ResourceImporterTextureAtlas::get_preset_count() const { + return 0; +} +String ResourceImporterTextureAtlas::get_preset_name(int p_idx) const { + + return String(); +} + +void ResourceImporterTextureAtlas::get_import_options(List<ImportOption> *r_options, int p_preset) const { + + r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "atlas_file", PROPERTY_HINT_SAVE_FILE, "*.png"), "")); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_mode", PROPERTY_HINT_ENUM, "Region,Mesh2D"), 0)); +} + +String ResourceImporterTextureAtlas::get_option_group_file() const { + return "atlas_file"; +} + +Error ResourceImporterTextureAtlas::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { + + /* If this happens, it's because the atlas_file field was not filled, so just import a broken texture */ + + //use an xpm because it's size independent, the editor images are vector and size dependent + //it's a simple hack + Ref<Image> broken = memnew(Image((const char **)atlas_import_failed_xpm)); + Ref<ImageTexture> broken_texture; + broken_texture.instance(); + broken_texture->create_from_image(broken); + + String target_file = p_save_path + ".tex"; + + ResourceSaver::save(target_file, broken_texture); + + return OK; +} + +static void _plot_triangle(Vector2 *vertices, const Vector2 &p_offset, bool p_transposed, Ref<Image> p_image, const Ref<Image> &p_src_image) { + + int width = p_image->get_width(); + int height = p_image->get_height(); + int src_width = p_src_image->get_width(); + int src_height = p_src_image->get_height(); + + int x[3]; + int y[3]; + + for (int j = 0; j < 3; j++) { + + x[j] = vertices[j].x; + y[j] = vertices[j].y; + } + + // sort the points vertically + if (y[1] > y[2]) { + SWAP(x[1], x[2]); + SWAP(y[1], y[2]); + } + if (y[0] > y[1]) { + SWAP(x[0], x[1]); + SWAP(y[0], y[1]); + } + if (y[1] > y[2]) { + SWAP(x[1], x[2]); + SWAP(y[1], y[2]); + } + + double dx_far = double(x[2] - x[0]) / (y[2] - y[0] + 1); + double dx_upper = double(x[1] - x[0]) / (y[1] - y[0] + 1); + double dx_low = double(x[2] - x[1]) / (y[2] - y[1] + 1); + double xf = x[0]; + double xt = x[0] + dx_upper; // if y[0] == y[1], special case + for (int yi = y[0]; yi <= (y[2] > height - 1 ? height - 1 : y[2]); yi++) { + if (yi >= 0) { + for (int xi = (xf > 0 ? int(xf) : 0); xi <= (xt < width ? xt : width - 1); xi++) { + + int px = xi, py = yi; + int sx = px, sy = py; + sx = CLAMP(sx, 0, src_width); + sy = CLAMP(sy, 0, src_height); + Color color = p_src_image->get_pixel(sx, sy); + if (p_transposed) { + SWAP(px, py); + } + px += p_offset.x; + py += p_offset.y; + + //may have been cropped, so don't blit what is not visible? + if (px < 0 || px >= width) { + continue; + } + if (py < 0 || py >= height) { + continue; + } + p_image->set_pixel(px, py, color); + } + + for (int xi = (xf < width ? int(xf) : width - 1); xi >= (xt > 0 ? xt : 0); xi--) { + int px = xi, py = yi; + int sx = px, sy = py; + sx = CLAMP(sx, 0, src_width); + sy = CLAMP(sy, 0, src_height); + Color color = p_src_image->get_pixel(sx, sy); + if (p_transposed) { + SWAP(px, py); + } + px += p_offset.x; + py += p_offset.y; + + //may have been cropped, so don't blit what is not visible? + if (px < 0 || px >= width) { + continue; + } + if (py < 0 || py >= height) { + continue; + } + p_image->set_pixel(px, py, color); + } + } + xf += dx_far; + if (yi < y[1]) + xt += dx_upper; + else + xt += dx_low; + } +} + +Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file, const Map<String, Map<StringName, Variant> > &p_source_file_options, const Map<String, String> &p_base_paths) { + + ERR_FAIL_COND_V(p_source_file_options.size() == 0, ERR_BUG); //should never happen + + Vector<EditorAtlasPacker::Chart> charts; + Vector<PackData> pack_data_files; + + pack_data_files.resize(p_source_file_options.size()); + + int idx = 0; + for (const Map<String, Map<StringName, Variant> >::Element *E = p_source_file_options.front(); E; E = E->next(), idx++) { + + PackData &pack_data = pack_data_files.write[idx]; + String source = E->key(); + const Map<StringName, Variant> &options = E->get(); + + Ref<Image> image; + image.instance(); + Error err = ImageLoader::load_image(source, image); + ERR_CONTINUE(err != OK); + + pack_data.image = image; + + int mode = options["import_mode"]; + + if (mode == IMPORT_MODE_REGION) { + + pack_data.is_mesh = false; + + EditorAtlasPacker::Chart chart; + + //clip a region from the image + Rect2 used_rect = image->get_used_rect(); + pack_data.region = used_rect; + + chart.vertices.push_back(used_rect.position); + chart.vertices.push_back(used_rect.position + Vector2(used_rect.size.x, 0)); + chart.vertices.push_back(used_rect.position + Vector2(used_rect.size.x, used_rect.size.y)); + chart.vertices.push_back(used_rect.position + Vector2(0, used_rect.size.y)); + EditorAtlasPacker::Chart::Face f; + f.vertex[0] = 0; + f.vertex[1] = 1; + f.vertex[2] = 2; + chart.faces.push_back(f); + f.vertex[0] = 0; + f.vertex[1] = 2; + f.vertex[2] = 3; + chart.faces.push_back(f); + chart.can_transpose = false; + pack_data.chart_vertices.push_back(chart.vertices); + pack_data.chart_pieces.push_back(charts.size()); + charts.push_back(chart); + + } else { + pack_data.is_mesh = true; + + Ref<BitMap> bit_map; + bit_map.instance(); + bit_map->create_from_image_alpha(image); + Vector<Vector<Vector2> > polygons = bit_map->clip_opaque_to_polygons(Rect2(0, 0, image->get_width(), image->get_height())); + + for (int j = 0; j < polygons.size(); j++) { + + EditorAtlasPacker::Chart chart; + chart.vertices = polygons[j]; + chart.can_transpose = true; + + Vector<int> poly = Geometry::triangulate_polygon(polygons[j]); + for (int i = 0; i < poly.size(); i += 3) { + + EditorAtlasPacker::Chart::Face f; + f.vertex[0] = poly[i + 0]; + f.vertex[1] = poly[i + 1]; + f.vertex[2] = poly[i + 2]; + chart.faces.push_back(f); + } + + pack_data.chart_pieces.push_back(charts.size()); + charts.push_back(chart); + + pack_data.chart_vertices.push_back(polygons[j]); + } + } + } + + //pack the charts + int atlas_width, atlas_height; + EditorAtlasPacker::chart_pack(charts, atlas_width, atlas_height); + + //blit the atlas + Ref<Image> new_atlas; + new_atlas.instance(); + new_atlas->create(atlas_width, atlas_height, false, Image::FORMAT_RGBA8); + + new_atlas->lock(); + + for (int i = 0; i < pack_data_files.size(); i++) { + + PackData &pack_data = pack_data_files.write[i]; + pack_data.image->lock(); + for (int j = 0; j < pack_data.chart_pieces.size(); j++) { + const EditorAtlasPacker::Chart &chart = charts[pack_data.chart_pieces[j]]; + for (int k = 0; k < chart.faces.size(); k++) { + Vector2 positions[3]; + for (int l = 0; l < 3; l++) { + int vertex_idx = chart.faces[k].vertex[l]; + positions[l] = chart.vertices[vertex_idx]; + } + + _plot_triangle(positions, chart.final_offset, chart.transposed, new_atlas, pack_data.image); + } + } + pack_data.image->unlock(); + } + new_atlas->unlock(); + + //save the atlas + + new_atlas->save_png(p_group_file); + + //update cache if existing, else create + Ref<Texture> cache; + if (ResourceCache::has(p_group_file)) { + Resource *resptr = ResourceCache::get(p_group_file); + cache.reference_ptr(resptr); + } else { + Ref<ImageTexture> res_cache; + res_cache.instance(); + res_cache->create_from_image(new_atlas); + res_cache->set_path(p_group_file); + cache = res_cache; + } + + //save the images + idx = 0; + for (const Map<String, Map<StringName, Variant> >::Element *E = p_source_file_options.front(); E; E = E->next(), idx++) { + + PackData &pack_data = pack_data_files.write[idx]; + + Ref<Texture> texture; + + if (!pack_data.is_mesh) { + Vector2 offset = charts[pack_data.chart_pieces[0]].vertices[0] + charts[pack_data.chart_pieces[0]].final_offset; + + //region + Ref<AtlasTexture> atlas_texture; + atlas_texture.instance(); + atlas_texture->set_atlas(cache); + atlas_texture->set_region(Rect2(offset, pack_data.region.size)); + atlas_texture->set_margin(Rect2(pack_data.region.position, Size2(pack_data.image->get_width(), pack_data.image->get_height()) - pack_data.region.size)); + + texture = atlas_texture; + } else { + Ref<ArrayMesh> mesh; + mesh.instance(); + + for (int i = 0; i < pack_data.chart_pieces.size(); i++) { + const EditorAtlasPacker::Chart &chart = charts[pack_data.chart_pieces[i]]; + PoolVector<Vector2> vertices; + PoolVector<int> indices; + PoolVector<Vector2> uvs; + int vc = chart.vertices.size(); + int fc = chart.faces.size(); + vertices.resize(vc); + uvs.resize(vc); + indices.resize(fc * 3); + + { + PoolVector<Vector2>::Write vw = vertices.write(); + PoolVector<int>::Write iw = indices.write(); + PoolVector<Vector2>::Write uvw = uvs.write(); + + for (int j = 0; j < vc; j++) { + vw[j] = chart.vertices[j]; + Vector2 uv = chart.vertices[j]; + if (chart.transposed) { + SWAP(uv.x, uv.y); + } + uv += chart.final_offset; + uv /= new_atlas->get_size(); //normalize uv to 0-1 range + uvw[j] = uv; + } + + for (int j = 0; j < fc; j++) { + iw[j * 3 + 0] = chart.faces[j].vertex[0]; + iw[j * 3 + 1] = chart.faces[j].vertex[1]; + iw[j * 3 + 2] = chart.faces[j].vertex[2]; + } + } + + Array arrays; + arrays.resize(Mesh::ARRAY_MAX); + arrays[Mesh::ARRAY_VERTEX] = vertices; + arrays[Mesh::ARRAY_TEX_UV] = uvs; + arrays[Mesh::ARRAY_INDEX] = indices; + + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arrays); + } + + Ref<MeshTexture> mesh_texture; + mesh_texture.instance(); + mesh_texture->set_base_texture(cache); + mesh_texture->set_image_size(pack_data.image->get_size()); + mesh_texture->set_mesh(mesh); + + texture = mesh_texture; + //mesh + } + + String save_path = p_base_paths[E->key()] + ".res"; + ResourceSaver::save(save_path, texture); + } + + return OK; +} + +ResourceImporterTextureAtlas::ResourceImporterTextureAtlas() { +} diff --git a/editor/import/resource_importer_texture_atlas.h b/editor/import/resource_importer_texture_atlas.h new file mode 100644 index 0000000000..62be570dc6 --- /dev/null +++ b/editor/import/resource_importer_texture_atlas.h @@ -0,0 +1,42 @@ +#ifndef RESOURCE_IMPORTER_TEXTURE_ATLAS_H +#define RESOURCE_IMPORTER_TEXTURE_ATLAS_H + +#include "core/image.h" +#include "core/io/resource_importer.h" +class ResourceImporterTextureAtlas : public ResourceImporter { + GDCLASS(ResourceImporterTextureAtlas, ResourceImporter) + + struct PackData { + Rect2 region; + bool is_mesh; + Vector<int> chart_pieces; //one for region, many for mesh + Vector<Vector<Vector2> > chart_vertices; //for mesh + Ref<Image> image; + }; + +public: + enum ImportMode { + IMPORT_MODE_REGION, + IMPORT_MODE_2D_MESH + }; + + virtual String get_importer_name() const; + virtual String get_visible_name() const; + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual String get_save_extension() const; + virtual String get_resource_type() const; + + virtual int get_preset_count() const; + virtual String get_preset_name(int p_idx) const; + + virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const; + virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const; + virtual String get_option_group_file() const; + + virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL, Variant *r_metadata = NULL); + virtual Error import_group_file(const String &p_group_file, const Map<String, Map<StringName, Variant> > &p_source_file_options, const Map<String, String> &p_base_paths); + + ResourceImporterTextureAtlas(); +}; + +#endif // RESOURCE_IMPORTER_TEXTURE_ATLAS_H diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp index 15539ee3db..b307ec649a 100644 --- a/editor/import_dock.cpp +++ b/editor/import_dock.cpp @@ -438,6 +438,8 @@ void ImportDock::_reimport() { Error err = config->load(params->paths[i] + ".import"); ERR_CONTINUE(err != OK); + String importer_name = params->importer->get_importer_name(); + if (params->checking) { //update only what edited (checkboxes) for (List<PropertyInfo>::Element *E = params->properties.front(); E; E = E->next()) { @@ -447,7 +449,7 @@ void ImportDock::_reimport() { } } else { //override entirely - config->set_value("remap", "importer", params->importer->get_importer_name()); + config->set_value("remap", "importer", importer_name); config->erase_section("params"); for (List<PropertyInfo>::Element *E = params->properties.front(); E; E = E->next()) { @@ -455,6 +457,19 @@ void ImportDock::_reimport() { } } + //handle group file + Ref<ResourceImporter> importer = ResourceFormatImporter::get_singleton()->get_importer_by_name(importer_name); + ERR_CONTINUE(!importer.is_valid()); + String group_file_property = importer->get_option_group_file(); + if (group_file_property != String()) { + //can import from a group (as in, atlas) + ERR_CONTINUE(!params->values.has(group_file_property)); + String group_file = params->values[group_file_property]; + config->set_value("remap", "group_file", group_file); + } else { + config->set_value("remap", "group_file", Variant()); //clear group file if unused + } + config->save(params->paths[i] + ".import"); } diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index bbaf41e3cc..41f35c3bed 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -670,6 +670,7 @@ Dictionary AnimationPlayerEditor::get_state() const { if (EditorNode::get_singleton()->get_edited_scene() && is_visible_in_tree() && player) { d["player"] = EditorNode::get_singleton()->get_edited_scene()->get_path_to(player); d["animation"] = player->get_assigned_animation(); + d["track_editor_state"] = track_editor->get_state(); } return d; @@ -696,6 +697,10 @@ void AnimationPlayerEditor::set_state(const Dictionary &p_state) { _animation_edit(); } } + + if (p_state.has("track_editor_state")) { + track_editor->set_state(p_state["track_editor_state"]); + } } } diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index c3cac582ad..66f7b932a2 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -1340,6 +1340,10 @@ bool CanvasItemEditor::_gui_input_rotate(const Ref<InputEvent> &p_event) { // Confirms the node rotation if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && !b->is_pressed()) { _commit_canvas_item_state(drag_selection, TTR("Rotate CanvasItem")); + if (key_auto_insert_button->is_pressed()) { + _insert_animation_keys(false, true, false, true); + } + drag_type = DRAG_NONE; return true; } @@ -1641,6 +1645,9 @@ bool CanvasItemEditor::_gui_input_resize(const Ref<InputEvent> &p_event) { // Confirm resize if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && !b->is_pressed()) { _commit_canvas_item_state(drag_selection, TTR("Resize CanvasItem")); + if (key_auto_insert_button->is_pressed()) { + _insert_animation_keys(false, false, true, true); + } drag_type = DRAG_NONE; viewport->update(); return true; @@ -1747,6 +1754,10 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) { // Confirm resize if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && !b->is_pressed()) { _commit_canvas_item_state(drag_selection, TTR("Scale CanvasItem")); + if (key_auto_insert_button->is_pressed()) { + _insert_animation_keys(false, false, true, true); + } + drag_type = DRAG_NONE; viewport->update(); return true; @@ -1852,6 +1863,9 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { _commit_canvas_item_state(drag_selection, TTR("Move CanvasItem"), true); } + if (key_auto_insert_button->is_pressed()) { + _insert_animation_keys(true, false, false, true); + } drag_type = DRAG_NONE; viewport->update(); return true; @@ -3384,6 +3398,7 @@ void CanvasItemEditor::_notification(int p_what) { key_rot_button->set_icon(get_icon("KeyRotation", "EditorIcons")); key_scale_button->set_icon(get_icon("KeyScale", "EditorIcons")); key_insert_button->set_icon(get_icon("Key", "EditorIcons")); + key_auto_insert_button->set_icon(get_icon("AutoKey", "EditorIcons")); zoom_minus->set_icon(get_icon("ZoomLess", "EditorIcons")); zoom_reset->set_icon(get_icon("ZoomReset", "EditorIcons")); @@ -3716,6 +3731,77 @@ void CanvasItemEditor::_button_tool_select(int p_index) { tool = (Tool)p_index; } +void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation, bool p_scale, bool p_on_existing) { + + Map<Node *, Object *> &selection = editor_selection->get_selection(); + + for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) { + + CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->key()); + if (!canvas_item || !canvas_item->is_visible_in_tree()) + continue; + + if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root()) + continue; + + if (Object::cast_to<Node2D>(canvas_item)) { + Node2D *n2d = Object::cast_to<Node2D>(canvas_item); + + if (key_pos && p_location) + AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "position", n2d->get_position(), p_on_existing); + if (key_rot && p_rotation) + AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "rotation_degrees", Math::rad2deg(n2d->get_rotation()), p_on_existing); + if (key_scale && p_scale) + AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "scale", n2d->get_scale(), p_on_existing); + + if (n2d->has_meta("_edit_bone_") && n2d->get_parent_item()) { + //look for an IK chain + List<Node2D *> ik_chain; + + Node2D *n = Object::cast_to<Node2D>(n2d->get_parent_item()); + bool has_chain = false; + + while (n) { + + ik_chain.push_back(n); + if (n->has_meta("_edit_ik_")) { + has_chain = true; + break; + } + + if (!n->get_parent_item()) + break; + n = Object::cast_to<Node2D>(n->get_parent_item()); + } + + if (has_chain && ik_chain.size()) { + + for (List<Node2D *>::Element *F = ik_chain.front(); F; F = F->next()) { + + if (key_pos) + AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F->get(), "position", F->get()->get_position(), p_on_existing); + if (key_rot) + AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F->get(), "rotation_degrees", Math::rad2deg(F->get()->get_rotation()), p_on_existing); + if (key_scale) + AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F->get(), "scale", F->get()->get_scale(), p_on_existing); + } + } + } + + } else if (Object::cast_to<Control>(canvas_item)) { + + Control *ctrl = Object::cast_to<Control>(canvas_item); + + if (key_pos) + AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_position", ctrl->get_position(), p_on_existing); + if (key_rot) + AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_rotation", ctrl->get_rotation_degrees(), p_on_existing); + if (key_scale) + AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_size", ctrl->get_size(), p_on_existing); + } + } +} + void CanvasItemEditor::_popup_callback(int p_op) { last_option = MenuOption(p_op); @@ -3983,73 +4069,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { bool existing = p_op == ANIM_INSERT_KEY_EXISTING; - Map<Node *, Object *> &selection = editor_selection->get_selection(); - - for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) { - - CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->key()); - if (!canvas_item || !canvas_item->is_visible_in_tree()) - continue; - - if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root()) - continue; - - if (Object::cast_to<Node2D>(canvas_item)) { - Node2D *n2d = Object::cast_to<Node2D>(canvas_item); - - if (key_pos) - AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "position", n2d->get_position(), existing); - if (key_rot) - AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "rotation_degrees", Math::rad2deg(n2d->get_rotation()), existing); - if (key_scale) - AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "scale", n2d->get_scale(), existing); - - if (n2d->has_meta("_edit_bone_") && n2d->get_parent_item()) { - //look for an IK chain - List<Node2D *> ik_chain; - - Node2D *n = Object::cast_to<Node2D>(n2d->get_parent_item()); - bool has_chain = false; - - while (n) { - - ik_chain.push_back(n); - if (n->has_meta("_edit_ik_")) { - has_chain = true; - break; - } - - if (!n->get_parent_item()) - break; - n = Object::cast_to<Node2D>(n->get_parent_item()); - } - - if (has_chain && ik_chain.size()) { - - for (List<Node2D *>::Element *F = ik_chain.front(); F; F = F->next()) { - - if (key_pos) - AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F->get(), "position", F->get()->get_position(), existing); - if (key_rot) - AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F->get(), "rotation_degrees", Math::rad2deg(F->get()->get_rotation()), existing); - if (key_scale) - AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F->get(), "scale", F->get()->get_scale(), existing); - } - } - } - - } else if (Object::cast_to<Control>(canvas_item)) { - - Control *ctrl = Object::cast_to<Control>(canvas_item); - - if (key_pos) - AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_position", ctrl->get_position(), existing); - if (key_rot) - AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_rotation", ctrl->get_rotation_degrees(), existing); - if (key_scale) - AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_size", ctrl->get_size(), existing); - } - } + _insert_animation_keys(true, true, true, existing); } break; case ANIM_INSERT_POS: { @@ -4757,6 +4777,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { hb->add_child(snap_config_menu); snap_config_menu->set_h_size_flags(SIZE_SHRINK_END); snap_config_menu->set_tooltip(TTR("Snapping Options")); + snap_config_menu->set_switch_on_hover(true); PopupMenu *p = snap_config_menu->get_popup(); p->connect("id_pressed", this, "_popup_callback"); @@ -4808,6 +4829,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { skeleton_menu = memnew(MenuButton); hb->add_child(skeleton_menu); skeleton_menu->set_tooltip(TTR("Skeleton Options")); + skeleton_menu->set_switch_on_hover(true); p = skeleton_menu->get_popup(); p->set_hide_on_checkable_item_selection(false); @@ -4826,8 +4848,10 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { view_menu->set_text(TTR("View")); hb->add_child(view_menu); view_menu->get_popup()->connect("id_pressed", this, "_popup_callback"); + view_menu->set_switch_on_hover(true); p = view_menu->get_popup(); + p->set_hide_on_checkable_item_selection(false); p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_grid", TTR("Show Grid"), KEY_G), SHOW_GRID); p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_helpers", TTR("Show Helpers"), KEY_H), SHOW_HELPERS); p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_rulers", TTR("Show Rulers"), KEY_R), SHOW_RULERS); @@ -4846,6 +4870,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { presets_menu->set_text(TTR("Layout")); hb->add_child(presets_menu); presets_menu->hide(); + presets_menu->set_switch_on_hover(true); p = presets_menu->get_popup(); p->connect("id_pressed", this, "_popup_callback"); @@ -4866,6 +4891,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { key_loc_button->set_pressed(true); key_loc_button->set_focus_mode(FOCUS_NONE); key_loc_button->connect("pressed", this, "_popup_callback", varray(ANIM_INSERT_POS)); + key_loc_button->set_tooltip(TTR("Translation mask for inserting keys.")); animation_hb->add_child(key_loc_button); key_rot_button = memnew(Button); key_rot_button->set_toggle_mode(true); @@ -4873,26 +4899,36 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { key_rot_button->set_pressed(true); key_rot_button->set_focus_mode(FOCUS_NONE); key_rot_button->connect("pressed", this, "_popup_callback", varray(ANIM_INSERT_ROT)); + key_rot_button->set_tooltip(TTR("Rotation mask for inserting keys.")); animation_hb->add_child(key_rot_button); key_scale_button = memnew(Button); key_scale_button->set_toggle_mode(true); key_scale_button->set_flat(true); key_scale_button->set_focus_mode(FOCUS_NONE); key_scale_button->connect("pressed", this, "_popup_callback", varray(ANIM_INSERT_SCALE)); + key_scale_button->set_tooltip(TTR("Scale mask for inserting keys.")); animation_hb->add_child(key_scale_button); key_insert_button = memnew(Button); key_insert_button->set_flat(true); key_insert_button->set_focus_mode(FOCUS_NONE); key_insert_button->connect("pressed", this, "_popup_callback", varray(ANIM_INSERT_KEY)); - key_insert_button->set_tooltip(TTR("Insert keys.")); + key_insert_button->set_tooltip(TTR("Insert keys (based on mask).")); key_insert_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/anim_insert_key", TTR("Insert Key"), KEY_INSERT)); - animation_hb->add_child(key_insert_button); + key_auto_insert_button = memnew(Button); + key_auto_insert_button->set_flat(true); + key_auto_insert_button->set_toggle_mode(true); + key_auto_insert_button->set_focus_mode(FOCUS_NONE); + //key_auto_insert_button->connect("pressed", this, "_popup_callback", varray(ANIM_INSERT_KEY)); + key_auto_insert_button->set_tooltip(TTR("Auto insert keys when objects are translated, rotated on scaled (based on mask).\nKeys are only added to existing tracks, no new tracks will be created.\nKeys must be inserted manually for the first time.")); + key_auto_insert_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/anim_auto_insert_key", TTR("Auto Insert Key"))); + animation_hb->add_child(key_auto_insert_button); animation_menu = memnew(MenuButton); animation_menu->set_text(TTR("Animation")); animation_hb->add_child(animation_menu); animation_menu->get_popup()->connect("id_pressed", this, "_popup_callback"); + animation_menu->set_switch_on_hover(true); p = animation_menu->get_popup(); diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index 9173c55ae0..14ea81f302 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -351,6 +351,7 @@ private: Button *key_rot_button; Button *key_scale_button; Button *key_insert_button; + Button *key_auto_insert_button; PopupMenu *selection_menu; @@ -422,6 +423,8 @@ private: Object *_get_editor_data(Object *p_what); + void _insert_animation_keys(bool p_location, bool p_rotation, bool p_scale, bool p_on_existing); + void _keying_changed(); void _unhandled_key_input(const Ref<InputEvent> &p_ev); diff --git a/editor/plugins/item_list_editor_plugin.h b/editor/plugins/item_list_editor_plugin.h index 679235e316..701632e576 100644 --- a/editor/plugins/item_list_editor_plugin.h +++ b/editor/plugins/item_list_editor_plugin.h @@ -157,7 +157,7 @@ public: virtual void set_item_enabled(int p_idx, int p_enabled) { pp->set_item_disabled(p_idx, !p_enabled); } virtual bool is_item_enabled(int p_idx) const { return !pp->is_item_disabled(p_idx); } - virtual void set_item_id(int p_idx, int p_id) { pp->set_item_id(p_idx, p_idx); } + virtual void set_item_id(int p_idx, int p_id) { pp->set_item_id(p_idx, p_id); } virtual int get_item_id(int p_idx) const { return pp->get_item_id(p_idx); } virtual void set_item_separator(int p_idx, bool p_separator) { pp->set_item_as_separator(p_idx, p_separator); } diff --git a/editor/plugins/mesh_instance_editor_plugin.cpp b/editor/plugins/mesh_instance_editor_plugin.cpp index 3e10cdbbfa..cf111dc4ce 100644 --- a/editor/plugins/mesh_instance_editor_plugin.cpp +++ b/editor/plugins/mesh_instance_editor_plugin.cpp @@ -95,10 +95,7 @@ void MeshInstanceEditor::_menu_option(int p_option) { return; } - if (trimesh_shape) - ur->create_action(TTR("Create Static Trimesh Body")); - else - ur->create_action(TTR("Create Static Convex Body")); + ur->create_action(TTR("Create Static Trimesh Body")); for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { @@ -132,8 +129,7 @@ void MeshInstanceEditor::_menu_option(int p_option) { } break; - case MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE: - case MENU_OPTION_CREATE_CONVEX_COLLISION_SHAPE: { + case MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE: { if (node == get_tree()->get_edited_scene_root()) { err_dialog->set_text(TTR("This doesn't work on scene root!")); @@ -141,9 +137,7 @@ void MeshInstanceEditor::_menu_option(int p_option) { return; } - bool trimesh_shape = (p_option == MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE); - - Ref<Shape> shape = trimesh_shape ? mesh->create_trimesh_shape() : mesh->create_convex_shape(); + Ref<Shape> shape = mesh->create_trimesh_shape(); if (shape.is_null()) return; @@ -154,10 +148,7 @@ void MeshInstanceEditor::_menu_option(int p_option) { UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); - if (trimesh_shape) - ur->create_action(TTR("Create Trimesh Shape")); - else - ur->create_action(TTR("Create Convex Shape")); + ur->create_action(TTR("Create Trimesh Static Shape")); ur->add_do_method(node->get_parent(), "add_child", cshape); ur->add_do_method(node->get_parent(), "move_child", cshape, node->get_index() + 1); @@ -165,6 +156,40 @@ void MeshInstanceEditor::_menu_option(int p_option) { ur->add_do_reference(cshape); ur->add_undo_method(node->get_parent(), "remove_child", cshape); ur->commit_action(); + } break; + case MENU_OPTION_CREATE_CONVEX_COLLISION_SHAPE: { + + if (node == get_tree()->get_edited_scene_root()) { + err_dialog->set_text(TTR("This doesn't work on scene root!")); + err_dialog->popup_centered_minsize(); + return; + } + + Vector<Ref<Shape> > shapes = mesh->convex_decompose(); + + if (!shapes.size()) { + err_dialog->set_text(TTR("Failed creating shapes!")); + err_dialog->popup_centered_minsize(); + return; + } + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + + ur->create_action(TTR("Create Convex Shape(s)")); + + for (int i = 0; i < shapes.size(); i++) { + + CollisionShape *cshape = memnew(CollisionShape); + cshape->set_shape(shapes[i]); + + Node *owner = node->get_owner(); + + ur->add_do_method(node->get_parent(), "add_child", cshape); + ur->add_do_method(node->get_parent(), "move_child", cshape, node->get_index() + 1); + ur->add_do_method(cshape, "set_owner", owner); + ur->add_do_reference(cshape); + ur->add_undo_method(node->get_parent(), "remove_child", cshape); + } + ur->commit_action(); } break; @@ -393,10 +418,9 @@ MeshInstanceEditor::MeshInstanceEditor() { options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("MeshInstance", "EditorIcons")); options->get_popup()->add_item(TTR("Create Trimesh Static Body"), MENU_OPTION_CREATE_STATIC_TRIMESH_BODY); - options->get_popup()->add_item(TTR("Create Convex Static Body"), MENU_OPTION_CREATE_STATIC_CONVEX_BODY); options->get_popup()->add_separator(); options->get_popup()->add_item(TTR("Create Trimesh Collision Sibling"), MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE); - options->get_popup()->add_item(TTR("Create Convex Collision Sibling"), MENU_OPTION_CREATE_CONVEX_COLLISION_SHAPE); + options->get_popup()->add_item(TTR("Create Convex Collision Sibling(s)"), MENU_OPTION_CREATE_CONVEX_COLLISION_SHAPE); options->get_popup()->add_separator(); options->get_popup()->add_item(TTR("Create Navigation Mesh"), MENU_OPTION_CREATE_NAVMESH); options->get_popup()->add_separator(); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index d7d4cec07d..8fdeb1cd39 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -2482,22 +2482,33 @@ void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) { for (int i = 0; i < scripts.size(); i++) { String path = scripts[i]; + + Dictionary script_info = scripts[i]; + if (!script_info.empty()) { + path = script_info["path"]; + } + if (!FileAccess::exists(path)) continue; if (extensions.find(path.get_extension())) { Ref<Script> scr = ResourceLoader::load(path); - if (scr.is_valid()) { - edit(scr); + if (!scr.is_valid()) { continue; } + edit(scr); + } else { + Error error; + Ref<TextFile> text_file = _load_text_file(path, &error); + if (error != OK || !text_file.is_valid()) { + continue; + } + edit(text_file); } - Error error; - Ref<TextFile> text_file = _load_text_file(path, &error); - if (error == OK && text_file.is_valid()) { - edit(text_file); - continue; + if (!script_info.empty()) { + ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i)); + se->set_edit_state(script_info["state"]); } } @@ -2537,7 +2548,11 @@ void ScriptEditor::get_window_layout(Ref<ConfigFile> p_layout) { if (!path.is_resource_file()) continue; - scripts.push_back(path); + Dictionary script_info; + script_info["path"] = path; + script_info["state"] = se->get_edit_state(); + + scripts.push_back(script_info); } EditorHelp *eh = Object::cast_to<EditorHelp>(tab_container->get_child(i)); diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 9fc42e3862..4e5034b6b7 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -81,6 +81,8 @@ void ScriptTextEditor::set_edited_resource(const RES &p_res) { emit_signal("name_changed"); code_editor->update_line_and_column(); + + _validate_script(); } void ScriptTextEditor::_update_member_keywords() { @@ -286,7 +288,7 @@ void ScriptTextEditor::_warning_clicked(Variant p_line) { code_editor->get_text_edit()->cursor_set_line(p_line.operator int64_t()); } else if (p_line.get_type() == Variant::DICTIONARY) { Dictionary meta = p_line.operator Dictionary(); - code_editor->get_text_edit()->insert_at("#warning-ignore:" + meta["code"].operator String(), meta["line"].operator int64_t() - 1); + code_editor->get_text_edit()->insert_at("# warning-ignore:" + meta["code"].operator String(), meta["line"].operator int64_t() - 1); _validate_script(); } } @@ -318,7 +320,6 @@ void ScriptTextEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: _load_theme_settings(); - _change_syntax_highlighter(EditorSettings::get_singleton()->get_project_metadata("script_text_editor", "syntax_highlighter", 0)); break; } } @@ -363,6 +364,14 @@ Variant ScriptTextEditor::get_edit_state() { void ScriptTextEditor::set_edit_state(const Variant &p_state) { code_editor->set_edit_state(p_state); + + Dictionary state = p_state; + if (state.has("syntax_highlighter")) { + int idx = highlighter_menu->get_item_idx_from_text(state["syntax_highlighter"]); + if (idx >= 0) { + _change_syntax_highlighter(idx); + } + } } void ScriptTextEditor::_convert_case(CodeTextEditor::CaseStyle p_case) { @@ -1024,7 +1033,6 @@ void ScriptTextEditor::_change_syntax_highlighter(int p_idx) { } // highlighter_menu->set_item_checked(p_idx, true); set_syntax_highlighter(highlighters[highlighter_menu->get_item_text(p_idx)]); - EditorSettings::get_singleton()->set_project_metadata("script_text_editor", "syntax_highlighter", p_idx); } void ScriptTextEditor::_bind_methods() { diff --git a/editor/plugins/skeleton_2d_editor_plugin.cpp b/editor/plugins/skeleton_2d_editor_plugin.cpp index ef3e17279c..0ccb60e39e 100644 --- a/editor/plugins/skeleton_2d_editor_plugin.cpp +++ b/editor/plugins/skeleton_2d_editor_plugin.cpp @@ -108,6 +108,7 @@ Skeleton2DEditor::Skeleton2DEditor() { options->get_popup()->add_item(TTR("Make Rest Pose (From Bones)"), MENU_OPTION_MAKE_REST); options->get_popup()->add_separator(); options->get_popup()->add_item(TTR("Set Bones to Rest Pose"), MENU_OPTION_SET_REST); + options->set_switch_on_hover(true); options->get_popup()->connect("id_pressed", this, "_menu_option"); diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index ba297539d3..47f00dc480 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -269,11 +269,21 @@ void SpatialEditorViewport::_select_clicked(bool p_append, bool p_single) { if (!clicked) return; - Spatial *sp = Object::cast_to<Spatial>(ObjectDB::get_instance(clicked)); - if (!sp) + Node *node = Object::cast_to<Node>(ObjectDB::get_instance(clicked)); + Spatial *selected = Object::cast_to<Spatial>(node); + if (!selected) return; - _select(sp, clicked_wants_append, true); + // Replace the node by the group if grouped + while (node && node != editor->get_edited_scene()->get_parent()) { + Spatial *selected_tmp = Object::cast_to<Spatial>(node); + if (selected_tmp && node->has_meta("_edit_group_")) { + selected = selected_tmp; + } + node = node->get_parent(); + } + + _select(selected, clicked_wants_append, true); } void SpatialEditorViewport::_select(Node *p_node, bool p_append, bool p_single) { @@ -511,6 +521,19 @@ void SpatialEditorViewport::_select_region() { item = item->get_owner(); } + // Replace the node by the group if grouped + if (item->is_class("Spatial")) { + Spatial *sel = Object::cast_to<Spatial>(item); + while (item && item != editor->get_edited_scene()->get_parent()) { + Spatial *selected_tmp = Object::cast_to<Spatial>(item); + if (selected_tmp && item->has_meta("_edit_group_")) { + sel = selected_tmp; + } + item = item->get_parent(); + } + item = sel; + } + if (selected.find(item) != -1) continue; Ref<EditorSpatialGizmo> seg = sp->get_gizmo(); @@ -1827,7 +1850,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { if (!sp) continue; - emit_signal("transform_key_request", sp, "", sp->get_transform()); + spatial_editor->emit_signal("transform_key_request", sp, "", sp->get_transform()); } set_message(TTR("Animation Key Inserted.")); @@ -4529,6 +4552,44 @@ void SpatialEditor::_menu_item_pressed(int p_option) { _refresh_menu_icons(); } break; + case MENU_GROUP_SELECTED: { + + List<Node *> &selection = editor_selection->get_selected_node_list(); + + for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { + + Spatial *spatial = Object::cast_to<Spatial>(E->get()); + if (!spatial || !spatial->is_visible_in_tree()) + continue; + + if (spatial->get_viewport() != EditorNode::get_singleton()->get_scene_root()) + continue; + + spatial->set_meta("_edit_group_", true); + emit_signal("item_group_status_changed"); + } + + _refresh_menu_icons(); + } break; + case MENU_UNGROUP_SELECTED: { + + List<Node *> &selection = editor_selection->get_selected_node_list(); + + for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { + + Spatial *spatial = Object::cast_to<Spatial>(E->get()); + if (!spatial || !spatial->is_visible_in_tree()) + continue; + + if (spatial->get_viewport() != EditorNode::get_singleton()->get_scene_root()) + continue; + + spatial->set_meta("_edit_group_", Variant()); + emit_signal("item_group_status_changed"); + } + + _refresh_menu_icons(); + } break; } } @@ -4971,11 +5032,13 @@ bool SpatialEditor::is_any_freelook_active() const { void SpatialEditor::_refresh_menu_icons() { bool all_locked = true; + bool all_grouped = true; List<Node *> &selection = editor_selection->get_selected_node_list(); if (selection.empty()) { all_locked = false; + all_grouped = false; } else { for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { if (Object::cast_to<Spatial>(E->get()) && !Object::cast_to<Spatial>(E->get())->has_meta("_edit_lock_")) { @@ -4983,11 +5046,21 @@ void SpatialEditor::_refresh_menu_icons() { break; } } + for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { + if (Object::cast_to<Spatial>(E->get()) && !Object::cast_to<Spatial>(E->get())->has_meta("_edit_group_")) { + all_grouped = false; + break; + } + } } tool_button[TOOL_LOCK_SELECTED]->set_visible(!all_locked); tool_button[TOOL_LOCK_SELECTED]->set_disabled(selection.empty()); tool_button[TOOL_UNLOCK_SELECTED]->set_visible(all_locked); + + tool_button[TOOL_GROUP_SELECTED]->set_visible(!all_grouped); + tool_button[TOOL_GROUP_SELECTED]->set_disabled(selection.empty()); + tool_button[TOOL_UNGROUP_SELECTED]->set_visible(all_grouped); } template <typename T> @@ -5157,6 +5230,8 @@ void SpatialEditor::_notification(int p_what) { tool_button[SpatialEditor::TOOL_MODE_LIST_SELECT]->set_icon(get_icon("ListSelect", "EditorIcons")); tool_button[SpatialEditor::TOOL_LOCK_SELECTED]->set_icon(get_icon("Lock", "EditorIcons")); tool_button[SpatialEditor::TOOL_UNLOCK_SELECTED]->set_icon(get_icon("Unlock", "EditorIcons")); + tool_button[SpatialEditor::TOOL_GROUP_SELECTED]->set_icon(get_icon("Group", "EditorIcons")); + tool_button[SpatialEditor::TOOL_UNGROUP_SELECTED]->set_icon(get_icon("Ungroup", "EditorIcons")); tool_option_button[SpatialEditor::TOOL_OPT_LOCAL_COORDS]->set_icon(get_icon("Object", "EditorIcons")); tool_option_button[SpatialEditor::TOOL_OPT_USE_SNAP]->set_icon(get_icon("Snap", "EditorIcons")); @@ -5351,6 +5426,7 @@ void SpatialEditor::_bind_methods() { ADD_SIGNAL(MethodInfo("transform_key_request")); ADD_SIGNAL(MethodInfo("item_lock_status_changed")); + ADD_SIGNAL(MethodInfo("item_group_status_changed")); } void SpatialEditor::clear() { @@ -5464,6 +5540,18 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { tool_button[TOOL_UNLOCK_SELECTED]->connect("pressed", this, "_menu_item_pressed", button_binds); tool_button[TOOL_UNLOCK_SELECTED]->set_tooltip(TTR("Unlock the selected object (can be moved).")); + tool_button[TOOL_GROUP_SELECTED] = memnew(ToolButton); + hbc_menu->add_child(tool_button[TOOL_GROUP_SELECTED]); + button_binds.write[0] = MENU_GROUP_SELECTED; + tool_button[TOOL_GROUP_SELECTED]->connect("pressed", this, "_menu_item_pressed", button_binds); + tool_button[TOOL_GROUP_SELECTED]->set_tooltip(TTR("Makes sure the object's children are not selectable.")); + + tool_button[TOOL_UNGROUP_SELECTED] = memnew(ToolButton); + hbc_menu->add_child(tool_button[TOOL_UNGROUP_SELECTED]); + button_binds.write[0] = MENU_UNGROUP_SELECTED; + tool_button[TOOL_UNGROUP_SELECTED]->connect("pressed", this, "_menu_item_pressed", button_binds); + tool_button[TOOL_UNGROUP_SELECTED]->set_tooltip(TTR("Restores the object's children's ability to be selected.")); + hbc_menu->add_child(memnew(VSeparator)); tool_option_button[TOOL_OPT_LOCAL_COORDS] = memnew(ToolButton); @@ -5598,11 +5686,11 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { snap_dialog_vbc->add_margin_child(TTR("Translate Snap:"), snap_translate); snap_rotate = memnew(LineEdit); - snap_rotate->set_text("5"); + snap_rotate->set_text("15"); snap_dialog_vbc->add_margin_child(TTR("Rotate Snap (deg.):"), snap_rotate); snap_scale = memnew(LineEdit); - snap_scale->set_text("5"); + snap_scale->set_text("10"); snap_dialog_vbc->add_margin_child(TTR("Scale Snap (%):"), snap_scale); /* SETTINGS DIALOG */ @@ -5760,6 +5848,39 @@ Vector3 SpatialEditor::snap_point(Vector3 p_target, Vector3 p_start) const { return p_target; } +float SpatialEditor::get_translate_snap() const { + float snap_value; + if (Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { + snap_value = snap_translate->get_text().to_double() / 10.0; + } else { + snap_value = snap_translate->get_text().to_double(); + } + + return snap_value; +} + +float SpatialEditor::get_rotate_snap() const { + float snap_value; + if (Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { + snap_value = snap_rotate->get_text().to_double() / 3.0; + } else { + snap_value = snap_rotate->get_text().to_double(); + } + + return snap_value; +} + +float SpatialEditor::get_scale_snap() const { + float snap_value; + if (Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { + snap_value = snap_scale->get_text().to_double() / 2.0; + } else { + snap_value = snap_scale->get_text().to_double(); + } + + return snap_value; +} + void SpatialEditorPlugin::_bind_methods() { ClassDB::bind_method("snap_cursor_to_plane", &SpatialEditorPlugin::snap_cursor_to_plane); @@ -5814,7 +5935,7 @@ SpatialEditorPlugin::SpatialEditorPlugin(EditorNode *p_node) { editor->get_viewport()->add_child(spatial_editor); spatial_editor->hide(); - spatial_editor->connect("transform_key_request", editor, "_transform_keyed"); + spatial_editor->connect("transform_key_request", editor->get_inspector_dock(), "_transform_keyed"); } SpatialEditorPlugin::~SpatialEditorPlugin() { diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h index 4a9d34a7f7..f3a1e657cc 100644 --- a/editor/plugins/spatial_editor_plugin.h +++ b/editor/plugins/spatial_editor_plugin.h @@ -485,6 +485,8 @@ public: TOOL_MODE_LIST_SELECT, TOOL_LOCK_SELECTED, TOOL_UNLOCK_SELECTED, + TOOL_GROUP_SELECTED, + TOOL_UNGROUP_SELECTED, TOOL_MAX }; @@ -570,6 +572,8 @@ private: MENU_VIEW_CAMERA_SETTINGS, MENU_LOCK_SELECTED, MENU_UNLOCK_SELECTED, + MENU_GROUP_SELECTED, + MENU_UNGROUP_SELECTED, MENU_SNAP_TO_FLOOR }; @@ -673,9 +677,9 @@ public: ToolMode get_tool_mode() const { return tool_mode; } bool are_local_coords_enabled() const { return tool_option_button[SpatialEditor::TOOL_OPT_LOCAL_COORDS]->is_pressed(); } bool is_snap_enabled() const { return snap_enabled ^ snap_key_enabled; } - float get_translate_snap() const { return snap_translate->get_text().to_double(); } - float get_rotate_snap() const { return snap_rotate->get_text().to_double(); } - float get_scale_snap() const { return snap_scale->get_text().to_double(); } + float get_translate_snap() const; + float get_rotate_snap() const; + float get_scale_snap() const; Ref<ArrayMesh> get_move_gizmo(int idx) const { return move_gizmo[idx]; } Ref<ArrayMesh> get_move_plane_gizmo(int idx) const { return move_plane_gizmo[idx]; } diff --git a/editor/plugins/sprite_editor_plugin.cpp b/editor/plugins/sprite_editor_plugin.cpp index fbc72b1396..7642bfaf04 100644 --- a/editor/plugins/sprite_editor_plugin.cpp +++ b/editor/plugins/sprite_editor_plugin.cpp @@ -549,6 +549,7 @@ SpriteEditor::SpriteEditor() { options->get_popup()->add_item(TTR("Convert to Polygon2D"), MENU_OPTION_CONVERT_TO_POLYGON_2D); options->get_popup()->add_item(TTR("Create CollisionPolygon2D Sibling"), MENU_OPTION_CREATE_COLLISION_POLY_2D); options->get_popup()->add_item(TTR("Create LightOccluder2D Sibling"), MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D); + options->set_switch_on_hover(true); options->get_popup()->connect("id_pressed", this, "_menu_option"); diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index 5ba2fde763..afe0b23fd9 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -38,11 +38,167 @@ void SpriteFramesEditor::_gui_input(Ref<InputEvent> p_event) { } +void SpriteFramesEditor::_open_sprite_sheet() { + + file_split_sheet->clear_filters(); + List<String> extensions; + ResourceLoader::get_recognized_extensions_for_type("Texture", &extensions); + for (int i = 0; i < extensions.size(); i++) { + file_split_sheet->add_filter("*." + extensions[i]); + } + + file_split_sheet->popup_centered_ratio(); +} + +void SpriteFramesEditor::_sheet_preview_draw() { + Size2i size = split_sheet_preview->get_size(); + int h = split_sheet_h->get_value(); + int v = split_sheet_v->get_value(); + const float a = 0.3; + for (int i = 1; i < h; i++) { + for (int j = 1; j < v; j++) { + + int x = i * size.width / h; + int y = j * size.height / v; + + split_sheet_preview->draw_line(Point2(x, 0), Point2(x, size.height), Color(1, 1, 1, a)); + split_sheet_preview->draw_line(Point2(x + 1, 0), Point2(x + 1, size.height), Color(0, 0, 0, a)); + + split_sheet_preview->draw_line(Point2(0, y), Point2(size.width, y), Color(1, 1, 1, a)); + split_sheet_preview->draw_line(Point2(0, y + 1), Point2(size.width, y + 1), Color(0, 0, 0, a)); + } + } + + Color accent = get_color("accent_color", "Editor"); + + for (Set<int>::Element *E = frames_selected.front(); E; E = E->next()) { + int idx = E->get(); + int x = (idx % h) * size.width / h; + int y = (idx / v) * size.height / v; + int width = size.width / h; + int height = size.height / v; + + split_sheet_preview->draw_rect(Rect2(x + 5, y + 5, width - 10, height - 10), Color(0, 0, 0, 0.35), true); + split_sheet_preview->draw_rect(Rect2(x + 0, y + 0, width - 0, height - 0), Color(0, 0, 0, 1), false); + split_sheet_preview->draw_rect(Rect2(x + 1, y + 1, width - 2, height - 2), Color(0, 0, 0, 1), false); + split_sheet_preview->draw_rect(Rect2(x + 2, y + 2, width - 4, height - 4), accent, false); + split_sheet_preview->draw_rect(Rect2(x + 3, y + 3, width - 6, height - 6), accent, false); + split_sheet_preview->draw_rect(Rect2(x + 4, y + 4, width - 8, height - 8), Color(0, 0, 0, 1), false); + split_sheet_preview->draw_rect(Rect2(x + 5, y + 5, width - 10, height - 10), Color(0, 0, 0, 1), false); + } + + if (frames_selected.size() == 0) { + split_sheet_dialog->get_ok()->set_disabled(true); + split_sheet_dialog->get_ok()->set_text(TTR("No frames selected")); + } else { + split_sheet_dialog->get_ok()->set_disabled(false); + split_sheet_dialog->get_ok()->set_text(vformat(TTR("Add %d frame(s)"), frames_selected.size())); + } +} +void SpriteFramesEditor::_sheet_preview_input(const Ref<InputEvent> &p_event) { + + Ref<InputEventMouseButton> mb = p_event; + + if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + Size2i size = split_sheet_preview->get_size(); + int h = split_sheet_h->get_value(); + int v = split_sheet_v->get_value(); + + int x = CLAMP(int(mb->get_position().x) * h / size.width, 0, h - 1); + int y = CLAMP(int(mb->get_position().y) * v / size.height, 0, v - 1); + + int idx = h * y + x; + + if (mb->get_shift() && last_frame_selected >= 0) { + //select multiple + int from = idx; + int to = last_frame_selected; + if (from > to) { + SWAP(from, to); + } + + for (int i = from; i <= to; i++) { + if (mb->get_control()) { + frames_selected.erase(i); + } else { + frames_selected.insert(i); + } + } + } else { + if (frames_selected.has(idx)) { + frames_selected.erase(idx); + } else { + frames_selected.insert(idx); + } + } + + last_frame_selected = idx; + split_sheet_preview->update(); + } +} + +void SpriteFramesEditor::_sheet_add_frames() { + + Size2i size = split_sheet_preview->get_size(); + int h = split_sheet_h->get_value(); + int v = split_sheet_v->get_value(); + + undo_redo->create_action(TTR("Add Frame")); + + int fc = frames->get_frame_count(edited_anim); + + for (Set<int>::Element *E = frames_selected.front(); E; E = E->next()) { + int idx = E->get(); + int x = (idx % h) * size.width / h; + int y = (idx / v) * size.height / v; + int width = size.width / h; + int height = size.height / v; + + Ref<AtlasTexture> at; + at.instance(); + at->set_atlas(split_sheet_preview->get_texture()); + at->set_region(Rect2(x, y, width, height)); + + undo_redo->add_do_method(frames, "add_frame", edited_anim, at, -1); + undo_redo->add_undo_method(frames, "remove_frame", edited_anim, fc); + } + + undo_redo->add_do_method(this, "_update_library"); + undo_redo->add_undo_method(this, "_update_library"); + undo_redo->commit_action(); +} + +void SpriteFramesEditor::_sheet_spin_changed(double) { + frames_selected.clear(); + last_frame_selected = -1; + split_sheet_preview->update(); +} + +void SpriteFramesEditor::_prepare_sprite_sheet(const String &p_file) { + + Ref<Resource> texture = ResourceLoader::load(p_file); + if (!texture.is_valid()) { + EditorNode::get_singleton()->show_warning("Unable to load images"); + ERR_FAIL_COND(!texture.is_valid()); + } + if (texture != split_sheet_preview->get_texture()) { + //different texture, reset to 4x4 + split_sheet_h->set_value(4); + split_sheet_v->set_value(4); + } + frames_selected.clear(); + last_frame_selected = -1; + + split_sheet_preview->set_texture(texture); + split_sheet_dialog->popup_centered_ratio(0.65); +} + void SpriteFramesEditor::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { load->set_icon(get_icon("Load", "EditorIcons")); + load_sheet->set_icon(get_icon("SpriteSheet", "EditorIcons")); copy->set_icon(get_icon("ActionCopy", "EditorIcons")); paste->set_icon(get_icon("ActionPaste", "EditorIcons")); empty->set_icon(get_icon("InsertBefore", "EditorIcons")); @@ -72,6 +228,7 @@ void SpriteFramesEditor::_file_load_request(const PoolVector<String> &p_path, in if (resource.is_null()) { dialog->set_text(TTR("ERROR: Couldn't load frame resource!")); dialog->set_title(TTR("Error!")); + //dialog->get_cancel()->set_text("Close"); dialog->get_ok()->set_text(TTR("Close")); dialog->popup_centered_minsize(); @@ -655,6 +812,12 @@ void SpriteFramesEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &SpriteFramesEditor::get_drag_data_fw); ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &SpriteFramesEditor::can_drop_data_fw); ClassDB::bind_method(D_METHOD("drop_data_fw"), &SpriteFramesEditor::drop_data_fw); + ClassDB::bind_method(D_METHOD("_prepare_sprite_sheet"), &SpriteFramesEditor::_prepare_sprite_sheet); + ClassDB::bind_method(D_METHOD("_open_sprite_sheet"), &SpriteFramesEditor::_open_sprite_sheet); + ClassDB::bind_method(D_METHOD("_sheet_preview_draw"), &SpriteFramesEditor::_sheet_preview_draw); + ClassDB::bind_method(D_METHOD("_sheet_preview_input"), &SpriteFramesEditor::_sheet_preview_input); + ClassDB::bind_method(D_METHOD("_sheet_spin_changed"), &SpriteFramesEditor::_sheet_spin_changed); + ClassDB::bind_method(D_METHOD("_sheet_add_frames"), &SpriteFramesEditor::_sheet_add_frames); } SpriteFramesEditor::SpriteFramesEditor() { @@ -712,9 +875,15 @@ SpriteFramesEditor::SpriteFramesEditor() { sub_vb->add_child(hbc); load = memnew(ToolButton); - load->set_tooltip(TTR("Load Resource")); + load->set_tooltip(TTR("Add a Texture from File")); hbc->add_child(load); + load_sheet = memnew(ToolButton); + load_sheet->set_tooltip(TTR("Add frames from a Sprite Sheet")); + hbc->add_child(load_sheet); + + hbc->add_child(memnew(VSeparator)); + copy = memnew(ToolButton); copy->set_tooltip(TTR("Copy")); hbc->add_child(copy); @@ -723,6 +892,8 @@ SpriteFramesEditor::SpriteFramesEditor() { paste->set_tooltip(TTR("Paste")); hbc->add_child(paste); + hbc->add_spacer(false); + empty = memnew(ToolButton); empty->set_tooltip(TTR("Insert Empty (Before)")); hbc->add_child(empty); @@ -731,7 +902,7 @@ SpriteFramesEditor::SpriteFramesEditor() { empty2->set_tooltip(TTR("Insert Empty (After)")); hbc->add_child(empty2); - hbc->add_spacer(false); + hbc->add_child(memnew(VSeparator)); move_up = memnew(ToolButton); move_up->set_tooltip(TTR("Move (Before)")); @@ -766,6 +937,7 @@ SpriteFramesEditor::SpriteFramesEditor() { add_child(dialog); load->connect("pressed", this, "_load_pressed"); + load_sheet->connect("pressed", this, "_open_sprite_sheet"); _delete->connect("pressed", this, "_delete_pressed"); copy->connect("pressed", this, "_copy_pressed"); paste->connect("pressed", this, "_paste_pressed"); @@ -780,6 +952,60 @@ SpriteFramesEditor::SpriteFramesEditor() { updating = false; edited_anim = "default"; + + split_sheet_dialog = memnew(ConfirmationDialog); + add_child(split_sheet_dialog); + VBoxContainer *split_sheet_vb = memnew(VBoxContainer); + split_sheet_dialog->add_child(split_sheet_vb); + split_sheet_dialog->set_title(TTR("Select Frames")); + split_sheet_dialog->connect("confirmed", this, "_sheet_add_frames"); + + ScrollContainer *scroll = memnew(ScrollContainer); + split_sheet_preview = memnew(TextureRect); + split_sheet_preview->set_expand(false); + split_sheet_preview->set_mouse_filter(MOUSE_FILTER_PASS); + split_sheet_preview->connect("draw", this, "_sheet_preview_draw"); + split_sheet_preview->connect("gui_input", this, "_sheet_preview_input"); + + scroll->set_enable_h_scroll(true); + scroll->set_enable_v_scroll(true); + CenterContainer *cc = memnew(CenterContainer); + cc->add_child(split_sheet_preview); + cc->set_h_size_flags(SIZE_EXPAND_FILL); + cc->set_v_size_flags(SIZE_EXPAND_FILL); + scroll->add_child(cc); + + split_sheet_vb->add_margin_child(TTR("Base Image:"), scroll, true); + + HBoxContainer *split_sheet_hb = memnew(HBoxContainer); + split_sheet_hb->add_spacer(); + Label *ss_label = memnew(Label(TTR("Horizontal:"))); + split_sheet_hb->add_child(ss_label); + split_sheet_h = memnew(SpinBox); + split_sheet_h->set_min(1); + split_sheet_h->set_max(128); + split_sheet_h->set_step(1); + split_sheet_hb->add_child(split_sheet_h); + split_sheet_hb->add_spacer(); + split_sheet_h->connect("value_changed", this, "_sheet_spin_changed"); + + ss_label = memnew(Label(TTR("Vertical:"))); + split_sheet_hb->add_child(ss_label); + split_sheet_v = memnew(SpinBox); + split_sheet_v->set_min(1); + split_sheet_v->set_max(128); + split_sheet_v->set_step(1); + split_sheet_hb->add_child(split_sheet_v); + split_sheet_hb->add_spacer(); + split_sheet_v->connect("value_changed", this, "_sheet_spin_changed"); + + split_sheet_vb->add_margin_child(TTR("Split Settings:"), split_sheet_hb); + + file_split_sheet = memnew(EditorFileDialog); + file_split_sheet->set_title(TTR("Create frames from Sprite Sheet")); + file_split_sheet->set_mode(EditorFileDialog::MODE_OPEN_FILE); + add_child(file_split_sheet); + file_split_sheet->connect("file_selected", this, "_prepare_sprite_sheet"); } void SpriteFramesEditorPlugin::edit(Object *p_object) { diff --git a/editor/plugins/sprite_frames_editor_plugin.h b/editor/plugins/sprite_frames_editor_plugin.h index 55dd10074e..383e99f87e 100644 --- a/editor/plugins/sprite_frames_editor_plugin.h +++ b/editor/plugins/sprite_frames_editor_plugin.h @@ -37,6 +37,7 @@ #include "scene/gui/dialogs.h" #include "scene/gui/file_dialog.h" #include "scene/gui/split_container.h" +#include "scene/gui/texture_rect.h" #include "scene/gui/tree.h" class SpriteFramesEditor : public HSplitContainer { @@ -44,6 +45,7 @@ class SpriteFramesEditor : public HSplitContainer { GDCLASS(SpriteFramesEditor, HSplitContainer); ToolButton *load; + ToolButton *load_sheet; ToolButton *_delete; ToolButton *copy; ToolButton *paste; @@ -71,6 +73,14 @@ class SpriteFramesEditor : public HSplitContainer { StringName edited_anim; + ConfirmationDialog *split_sheet_dialog; + TextureRect *split_sheet_preview; + SpinBox *split_sheet_h; + SpinBox *split_sheet_v; + EditorFileDialog *file_split_sheet; + Set<int> frames_selected; + int last_frame_selected; + void _load_pressed(); void _load_scene_pressed(); void _file_load_request(const PoolVector<String> &p_path, int p_at_pos = -1); @@ -99,6 +109,13 @@ class SpriteFramesEditor : public HSplitContainer { bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); + void _open_sprite_sheet(); + void _prepare_sprite_sheet(const String &p_file); + void _sheet_preview_draw(); + void _sheet_spin_changed(double); + void _sheet_preview_input(const Ref<InputEvent> &p_event); + void _sheet_add_frames(); + protected: void _notification(int p_what); void _gui_input(Ref<InputEvent> p_event); diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp index fe32c97a64..2886b3dc51 100644 --- a/editor/plugins/text_editor.cpp +++ b/editor/plugins/text_editor.cpp @@ -66,7 +66,6 @@ void TextEditor::_change_syntax_highlighter(int p_idx) { el = el->next(); } set_syntax_highlighter(highlighters[highlighter_menu->get_item_text(p_idx)]); - EditorSettings::get_singleton()->set_project_metadata("text_editor", "syntax_highlighter", p_idx); } void TextEditor::_load_theme_settings() { @@ -234,6 +233,14 @@ Variant TextEditor::get_edit_state() { void TextEditor::set_edit_state(const Variant &p_state) { code_editor->set_edit_state(p_state); + + Dictionary state = p_state; + if (state.has("syntax_highlighter")) { + int idx = highlighter_menu->get_item_idx_from_text(state["syntax_highlighter"]); + if (idx >= 0) { + _change_syntax_highlighter(idx); + } + } } void TextEditor::trim_trailing_whitespace() { @@ -299,7 +306,6 @@ void TextEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: _load_theme_settings(); - _change_syntax_highlighter(EditorSettings::get_singleton()->get_project_metadata("text_editor", "syntax_highlighter", 0)); break; } } diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp index 03277159fc..8cf00cf67d 100644 --- a/editor/plugins/tile_set_editor_plugin.cpp +++ b/editor/plugins/tile_set_editor_plugin.cpp @@ -1038,25 +1038,26 @@ void TileSetEditor::_on_workspace_overlay_draw() { tileset->get_tile_list(tiles); for (List<int>::Element *E = tiles->front(); E; E = E->next()) { int t_id = E->get(); - if (tileset->tile_get_texture(t_id)->get_rid() == current_texture_rid) { - Rect2i region = tileset->tile_get_region(t_id); - region.position += WORKSPACE_MARGIN; - region.position *= workspace->get_scale().x; - Color c; - if (tileset->tile_get_tile_mode(t_id) == TileSet::SINGLE_TILE) - c = COLOR_SINGLE; - else if (tileset->tile_get_tile_mode(t_id) == TileSet::AUTO_TILE) - c = COLOR_AUTOTILE; - else if (tileset->tile_get_tile_mode(t_id) == TileSet::ATLAS_TILE) - c = COLOR_ATLAS; - String tile_id_name = String::num(t_id, 0) + ": " + tileset->tile_get_name(t_id); - Ref<Font> font = get_font("font", "Label"); - region.set_size(font->get_string_size(tile_id_name)); - workspace_overlay->draw_rect(region, c); - region.position.y += region.size.y - 2; - c = Color(0.1, 0.1, 0.1); - workspace_overlay->draw_string(font, region.position, tile_id_name, c); - } + if (tileset->tile_get_texture(t_id)->get_rid() != current_texture_rid) + continue; + + Rect2 region = tileset->tile_get_region(t_id); + region.position += WORKSPACE_MARGIN; + region.position *= workspace->get_scale().x; + Color c; + if (tileset->tile_get_tile_mode(t_id) == TileSet::SINGLE_TILE) + c = COLOR_SINGLE; + else if (tileset->tile_get_tile_mode(t_id) == TileSet::AUTO_TILE) + c = COLOR_AUTOTILE; + else if (tileset->tile_get_tile_mode(t_id) == TileSet::ATLAS_TILE) + c = COLOR_ATLAS; + String tile_id_name = String::num(t_id, 0) + ": " + tileset->tile_get_name(t_id); + Ref<Font> font = get_font("font", "Label"); + region.set_size(font->get_string_size(tile_id_name)); + workspace_overlay->draw_rect(region, c); + region.position.y += region.size.y - 2; + c = Color(0.1, 0.1, 0.1); + workspace_overlay->draw_string(font, region.position, tile_id_name, c); } } diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 0eeb104777..e3fab34768 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -172,6 +172,7 @@ void VisualShaderEditor::_update_options_menu() { int item_count = 0; int item_count2 = 0; + bool is_first_item = true; for (int i = 0; i < add_options.size() + 1; i++) { @@ -197,6 +198,7 @@ void VisualShaderEditor::_update_options_menu() { prev_sub_category = ""; category = members->create_item(root); category->set_text(0, add_options[i].category); + category->set_selectable(0, false); if (!use_filter) category->set_collapsed(true); } @@ -212,6 +214,7 @@ void VisualShaderEditor::_update_options_menu() { item_count2 = 0; sub_category = members->create_item(category); sub_category->set_text(0, add_options[i].sub_category); + sub_category->set_selectable(0, false); if (!use_filter) sub_category->set_collapsed(true); } @@ -221,6 +224,10 @@ void VisualShaderEditor::_update_options_menu() { ++item_count2; TreeItem *item = members->create_item(sub_category); item->set_text(0, add_options[i].name); + if (is_first_item) { + item->select(0); + is_first_item = false; + } switch (add_options[i].return_type) { case VisualShaderNode::PORT_TYPE_SCALAR: item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("float", "EditorIcons")); @@ -833,40 +840,56 @@ void VisualShaderEditor::_node_selected(Object *p_node) { //EditorNode::get_singleton()->push_item(vsnode.ptr(), "", true); } -void VisualShaderEditor::_member_gui_input(const Ref<InputEvent> p_event) { +void VisualShaderEditor::_graph_gui_input(const Ref<InputEvent> p_event) { + Ref<InputEventMouseButton> mb = p_event; - Ref<InputEventKey> key = p_event; - if (mb.is_valid()) { - if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT && mb->is_doubleclick()) { - _member_create(); - } - } else if (key.is_valid()) { - if (key->is_pressed() && key->get_scancode() == KEY_ENTER) { - _member_create(); - } - } + if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_RIGHT) + _show_members_dialog(true); } -void VisualShaderEditor::_input(const Ref<InputEvent> p_event) { - if (graph->has_focus()) { - Ref<InputEventMouseButton> mb = p_event; +void VisualShaderEditor::_show_members_dialog(bool at_mouse_pos) { - if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_RIGHT) { - saved_node_pos_dirty = true; - saved_node_pos = graph->get_local_mouse_position(); + members_dialog->popup(); - Point2 gpos = Input::get_singleton()->get_mouse_position(); - members_dialog->popup(); - members_dialog->set_position(gpos); - } + if (at_mouse_pos) { + saved_node_pos_dirty = true; + saved_node_pos = graph->get_local_mouse_position(); + + Point2 gpos = Input::get_singleton()->get_mouse_position(); + members_dialog->popup(); + members_dialog->set_position(gpos); + } else { + saved_node_pos_dirty = false; + members_dialog->set_position(graph->get_global_position() + Point2(5 * EDSCALE, 65 * EDSCALE)); + } + + // keep dialog within window bounds + Size2 window_size = OS::get_singleton()->get_window_size(); + Rect2 dialog_rect = members_dialog->get_global_rect(); + if (dialog_rect.position.y + dialog_rect.size.y > window_size.y) { + int difference = dialog_rect.position.y + dialog_rect.size.y - window_size.y; + members_dialog->set_position(members_dialog->get_position() - Point2(0, difference)); + } + if (dialog_rect.position.x + dialog_rect.size.x > window_size.x) { + int difference = dialog_rect.position.x + dialog_rect.size.x - window_size.x; + members_dialog->set_position(members_dialog->get_position() - Point2(difference, 0)); } + + node_filter->call_deferred("grab_focus"); // still not visible + node_filter->select_all(); } -void VisualShaderEditor::_show_members_dialog() { - saved_node_pos_dirty = false; - members_dialog->popup(); - members_dialog->set_position(graph->get_global_position() + Point2(5 * EDSCALE, 65 * EDSCALE)); +void VisualShaderEditor::_sbox_input(const Ref<InputEvent> &p_ie) { + Ref<InputEventKey> ie = p_ie; + if (ie.is_valid() && (ie->get_scancode() == KEY_UP || + ie->get_scancode() == KEY_DOWN || + ie->get_scancode() == KEY_ENTER || + ie->get_scancode() == KEY_KP_ENTER)) { + + members->call("_gui_input", ie); + node_filter->accept_event(); + } } void VisualShaderEditor::_notification(int p_what) { @@ -1004,6 +1027,58 @@ void VisualShaderEditor::_duplicate_nodes() { } } +void VisualShaderEditor::_on_nodes_delete() { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + List<int> to_erase; + + for (int i = 0; i < graph->get_child_count(); i++) { + GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i)); + if (gn) { + if (gn->is_selected() && gn->is_close_button_visible()) { + to_erase.push_back(gn->get_name().operator String().to_int()); + } + } + } + + if (to_erase.empty()) + return; + + undo_redo->create_action(TTR("Delete Nodes")); + + for (List<int>::Element *F = to_erase.front(); F; F = F->next()) { + undo_redo->add_do_method(visual_shader.ptr(), "remove_node", type, F->get()); + undo_redo->add_undo_method(visual_shader.ptr(), "add_node", type, visual_shader->get_node(type, F->get()), visual_shader->get_node_position(type, F->get()), F->get()); + } + + List<VisualShader::Connection> conns; + visual_shader->get_node_connections(type, &conns); + + List<VisualShader::Connection> used_conns; + for (List<int>::Element *F = to_erase.front(); F; F = F->next()) { + for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) { + if (E->get().from_node == F->get() || E->get().to_node == F->get()) { + + bool cancel = false; + for (List<VisualShader::Connection>::Element *R = used_conns.front(); R; R = R->next()) { + if (R->get().from_node == E->get().from_node && R->get().from_port == E->get().from_port && R->get().to_node == E->get().to_node && R->get().to_port == E->get().to_port) { + cancel = true; // to avoid ERR_ALREADY_EXISTS warning + break; + } + } + if (!cancel) { + undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes", type, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); + used_conns.push_back(E->get()); + } + } + } + } + + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); +} + void VisualShaderEditor::_mode_selected(int p_id) { _update_options_menu(); _update_graph(); @@ -1175,6 +1250,7 @@ void VisualShaderEditor::_bind_methods() { ClassDB::bind_method("_node_selected", &VisualShaderEditor::_node_selected); ClassDB::bind_method("_scroll_changed", &VisualShaderEditor::_scroll_changed); ClassDB::bind_method("_delete_request", &VisualShaderEditor::_delete_request); + ClassDB::bind_method("_on_nodes_delete", &VisualShaderEditor::_on_nodes_delete); ClassDB::bind_method("_node_changed", &VisualShaderEditor::_node_changed); ClassDB::bind_method("_edit_port_default_input", &VisualShaderEditor::_edit_port_default_input); ClassDB::bind_method("_port_edited", &VisualShaderEditor::_port_edited); @@ -1185,7 +1261,7 @@ void VisualShaderEditor::_bind_methods() { ClassDB::bind_method("_mode_selected", &VisualShaderEditor::_mode_selected); ClassDB::bind_method("_input_select_item", &VisualShaderEditor::_input_select_item); ClassDB::bind_method("_preview_select_port", &VisualShaderEditor::_preview_select_port); - ClassDB::bind_method("_input", &VisualShaderEditor::_input); + ClassDB::bind_method("_graph_gui_input", &VisualShaderEditor::_graph_gui_input); ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &VisualShaderEditor::get_drag_data_fw); ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &VisualShaderEditor::can_drop_data_fw); @@ -1194,7 +1270,7 @@ void VisualShaderEditor::_bind_methods() { ClassDB::bind_method("_is_available", &VisualShaderEditor::_is_available); ClassDB::bind_method("_tools_menu_option", &VisualShaderEditor::_tools_menu_option); ClassDB::bind_method("_show_members_dialog", &VisualShaderEditor::_show_members_dialog); - ClassDB::bind_method("_member_gui_input", &VisualShaderEditor::_member_gui_input); + ClassDB::bind_method("_sbox_input", &VisualShaderEditor::_sbox_input); ClassDB::bind_method("_member_filter_changed", &VisualShaderEditor::_member_filter_changed); ClassDB::bind_method("_member_selected", &VisualShaderEditor::_member_selected); ClassDB::bind_method("_member_unselected", &VisualShaderEditor::_member_unselected); @@ -1224,6 +1300,8 @@ VisualShaderEditor::VisualShaderEditor() { graph->connect("node_selected", this, "_node_selected"); graph->connect("scroll_offset_changed", this, "_scroll_changed"); graph->connect("duplicate_nodes_request", this, "_duplicate_nodes"); + graph->connect("delete_nodes_request", this, "_on_nodes_delete"); + graph->connect("gui_input", this, "_graph_gui_input"); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_SCALAR); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_VECTOR); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_BOOLEAN); @@ -1252,7 +1330,7 @@ VisualShaderEditor::VisualShaderEditor() { graph->get_zoom_hbox()->add_child(add_node); add_node->set_text(TTR("Add Node...")); graph->get_zoom_hbox()->move_child(add_node, 0); - add_node->connect("pressed", this, "_show_members_dialog"); + add_node->connect("pressed", this, "_show_members_dialog", varray(false)); /////////////////////////////////////// // SHADER NODES TREE @@ -1267,6 +1345,7 @@ VisualShaderEditor::VisualShaderEditor() { node_filter = memnew(LineEdit); filter_hb->add_child(node_filter); node_filter->connect("text_changed", this, "_member_filter_changed"); + node_filter->connect("gui_input", this, "_sbox_input"); node_filter->set_h_size_flags(SIZE_EXPAND_FILL); node_filter->set_placeholder(TTR("Search")); @@ -1286,9 +1365,9 @@ VisualShaderEditor::VisualShaderEditor() { members->set_allow_reselect(true); members->set_hide_folding(false); members->set_custom_minimum_size(Size2(180 * EDSCALE, 200 * EDSCALE)); + members->connect("item_activated", this, "_member_create"); members->connect("item_selected", this, "_member_selected"); members->connect("nothing_selected", this, "_member_unselected"); - members->connect("gui_input", this, "_member_gui_input"); Label *desc_label = memnew(Label); members_vb->add_child(desc_label); @@ -1346,9 +1425,10 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("ColorUniform", "Color", "Variables", "VisualShaderNodeColorUniform", TTR("Color uniform."), -1, VisualShaderNode::PORT_TYPE_COLOR)); // BOOLEAN - - add_options.push_back(AddOption("BooleanConstant", "Boolean", "Variables", "VisualShaderNodeBooleanConstant", TTR("Boolean constant."), -1, VisualShaderNode::PORT_TYPE_BOOLEAN)); - add_options.push_back(AddOption("BooleanUniform", "Boolean", "Variables", "VisualShaderNodeBooleanUniform", TTR("Boolean uniform."), -1, VisualShaderNode::PORT_TYPE_BOOLEAN)); + add_options.push_back(AddOption("If", "Conditional", "Functions", "VisualShaderNodeIf", TTR("Returns an associated vector if the provided scalars are equal, greater or less."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Switch", "Conditional", "Functions", "VisualShaderNodeSwitch", TTR("Returns an associated vector if the provided boolean value is true or false."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("BooleanConstant", "Conditional", "Variables", "VisualShaderNodeBooleanConstant", TTR("Boolean constant."), -1, VisualShaderNode::PORT_TYPE_BOOLEAN)); + add_options.push_back(AddOption("BooleanUniform", "Conditional", "Variables", "VisualShaderNodeBooleanUniform", TTR("Boolean uniform."), -1, VisualShaderNode::PORT_TYPE_BOOLEAN)); // INPUT diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h index 2709d72931..a00f020ebd 100644 --- a/editor/plugins/visual_shader_editor_plugin.h +++ b/editor/plugins/visual_shader_editor_plugin.h @@ -85,7 +85,7 @@ class VisualShaderEditor : public VBoxContainer { RichTextLabel *node_desc; void _tools_menu_option(int p_idx); - void _show_members_dialog(); + void _show_members_dialog(bool at_mouse_pos); void _update_graph(); @@ -143,6 +143,7 @@ class VisualShaderEditor : public VBoxContainer { void _node_selected(Object *p_node); void _delete_request(int); + void _on_nodes_delete(); void _removed_from_graph(); @@ -165,10 +166,10 @@ class VisualShaderEditor : public VBoxContainer { void _input_select_item(Ref<VisualShaderNodeInput> input, String name); void _preview_select_port(int p_node, int p_port); - void _input(const Ref<InputEvent> p_event); + void _graph_gui_input(const Ref<InputEvent> p_event); - void _member_gui_input(const Ref<InputEvent> p_event); void _member_filter_changed(const String &p_text); + void _sbox_input(const Ref<InputEvent> &p_ie); void _member_selected(); void _member_unselected(); void _member_create(); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 85a56a904d..8473230758 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -986,6 +986,7 @@ void SceneTreeDock::_notification(int p_what) { SpatialEditorPlugin *spatial_editor_plugin = Object::cast_to<SpatialEditorPlugin>(editor_data->get_editor("3D")); spatial_editor_plugin->get_spatial_editor()->connect("item_lock_status_changed", scene_tree, "_update_tree"); + spatial_editor_plugin->get_spatial_editor()->connect("item_group_status_changed", scene_tree, "_update_tree"); button_add->set_icon(get_icon("Add", "EditorIcons")); button_instance->set_icon(get_icon("Instance", "EditorIcons")); @@ -1741,7 +1742,7 @@ void SceneTreeDock::_update_script_button() { button_clear_script->show(); } } else { - button_create_script->show(); + button_create_script->hide(); Array selection = editor_selection->get_selected_nodes(); for (int i = 0; i < selection.size(); i++) { Node *n = Object::cast_to<Node>(selection[i]); @@ -1848,6 +1849,8 @@ void SceneTreeDock::_create() { replace_node(n, newnode); } } + + scene_tree->get_scene_tree()->call_deferred("grab_focus"); } void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node, bool p_keep_properties) { @@ -2206,17 +2209,20 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { if (profile_allow_script_editing) { - if (!existing_script.is_valid()) { - menu->add_icon_shortcut(get_icon("ScriptCreate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/attach_script"), TOOL_ATTACH_SCRIPT); + if (selection.size() == 1) { + if (!existing_script.is_valid()) { + menu->add_icon_shortcut(get_icon("ScriptCreate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/attach_script"), TOOL_ATTACH_SCRIPT); + } else { + menu->add_icon_shortcut(get_icon("ScriptExtend", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/extend_script"), TOOL_ATTACH_SCRIPT); + } } if (selection.size() > 1 || existing_script.is_valid()) { menu->add_icon_shortcut(get_icon("ScriptRemove", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/clear_script"), TOOL_CLEAR_SCRIPT); - menu->add_icon_shortcut(get_icon("ScriptExtend", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/extend_script"), TOOL_ATTACH_SCRIPT); } + menu->add_separator(); } if (profile_allow_editing) { - menu->add_separator(); if (selection.size() == 1) { menu->add_icon_shortcut(get_icon("Rename", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/rename"), TOOL_RENAME); } @@ -2228,12 +2234,12 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { menu->add_icon_shortcut(get_icon("MoveDown", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/move_down"), TOOL_MOVE_DOWN); menu->add_icon_shortcut(get_icon("Duplicate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/duplicate"), TOOL_DUPLICATE); menu->add_icon_shortcut(get_icon("Reparent", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/reparent"), TOOL_REPARENT); + menu->add_icon_shortcut(get_icon("NewRoot", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/make_root"), TOOL_MAKE_ROOT); } } if (selection.size() == 1) { if (profile_allow_editing) { - menu->add_icon_shortcut(get_icon("NewRoot", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/make_root"), TOOL_MAKE_ROOT); menu->add_separator(); menu->add_icon_shortcut(get_icon("Blend", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/merge_from_scene"), TOOL_MERGE_FROM_SCENE); menu->add_icon_shortcut(get_icon("CreateNewSceneFrom", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/save_branch_as_scene"), TOOL_NEW_SCENE_FROM); @@ -2275,7 +2281,7 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { menu->add_icon_shortcut(get_icon("Rename", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/batch_rename"), TOOL_BATCH_RENAME); } menu->add_separator(); - menu->add_icon_item(get_icon("Help", "EditorIcons"), TTR("Open documentation"), TOOL_OPEN_DOCUMENTATION); + menu->add_icon_item(get_icon("Help", "EditorIcons"), TTR("Open Documentation"), TOOL_OPEN_DOCUMENTATION); if (profile_allow_editing) { menu->add_separator(); @@ -2506,7 +2512,6 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel HBoxContainer *filter_hbc = memnew(HBoxContainer); filter_hbc->add_constant_override("separate", 0); - ToolButton *tb; ED_SHORTCUT("scene_tree/rename", TTR("Rename")); ED_SHORTCUT("scene_tree/batch_rename", TTR("Batch Rename"), KEY_MASK_CMD | KEY_F2); @@ -2527,19 +2532,17 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel ED_SHORTCUT("scene_tree/delete_no_confirm", TTR("Delete (No Confirm)"), KEY_MASK_SHIFT | KEY_DELETE); ED_SHORTCUT("scene_tree/delete", TTR("Delete"), KEY_DELETE); - tb = memnew(ToolButton); - tb->connect("pressed", this, "_tool_selected", make_binds(TOOL_NEW, false)); - tb->set_tooltip(TTR("Add/Create a New Node")); - tb->set_shortcut(ED_GET_SHORTCUT("scene_tree/add_child_node")); - filter_hbc->add_child(tb); - button_add = tb; + button_add = memnew(ToolButton); + button_add->connect("pressed", this, "_tool_selected", make_binds(TOOL_NEW, false)); + button_add->set_tooltip(TTR("Add/Create a New Node")); + button_add->set_shortcut(ED_GET_SHORTCUT("scene_tree/add_child_node")); + filter_hbc->add_child(button_add); - tb = memnew(ToolButton); - tb->connect("pressed", this, "_tool_selected", make_binds(TOOL_INSTANCE, false)); - tb->set_tooltip(TTR("Instance a scene file as a Node. Creates an inherited scene if no root node exists.")); - tb->set_shortcut(ED_GET_SHORTCUT("scene_tree/instance_scene")); - filter_hbc->add_child(tb); - button_instance = tb; + button_instance = memnew(ToolButton); + button_instance->connect("pressed", this, "_tool_selected", make_binds(TOOL_INSTANCE, false)); + button_instance->set_tooltip(TTR("Instance a scene file as a Node. Creates an inherited scene if no root node exists.")); + button_instance->set_shortcut(ED_GET_SHORTCUT("scene_tree/instance_scene")); + filter_hbc->add_child(button_instance); vbc->add_child(filter_hbc); filter = memnew(LineEdit); @@ -2549,21 +2552,19 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel filter->add_constant_override("minimum_spaces", 0); filter->connect("text_changed", this, "_filter_changed"); - tb = memnew(ToolButton); - tb->connect("pressed", this, "_tool_selected", make_binds(TOOL_ATTACH_SCRIPT, false)); - tb->set_tooltip(TTR("Attach a new or existing script for the selected node.")); - tb->set_shortcut(ED_GET_SHORTCUT("scene_tree/attach_script")); - filter_hbc->add_child(tb); - tb->hide(); - button_create_script = tb; - - tb = memnew(ToolButton); - tb->connect("pressed", this, "_tool_selected", make_binds(TOOL_CLEAR_SCRIPT, false)); - tb->set_tooltip(TTR("Clear a script for the selected node.")); - tb->set_shortcut(ED_GET_SHORTCUT("scene_tree/clear_script")); - filter_hbc->add_child(tb); - button_clear_script = tb; - tb->hide(); + button_create_script = memnew(ToolButton); + button_create_script->connect("pressed", this, "_tool_selected", make_binds(TOOL_ATTACH_SCRIPT, false)); + button_create_script->set_tooltip(TTR("Attach a new or existing script for the selected node.")); + button_create_script->set_shortcut(ED_GET_SHORTCUT("scene_tree/attach_script")); + filter_hbc->add_child(button_create_script); + button_create_script->hide(); + + button_clear_script = memnew(ToolButton); + button_clear_script->connect("pressed", this, "_tool_selected", make_binds(TOOL_CLEAR_SCRIPT, false)); + button_clear_script->set_tooltip(TTR("Clear a script for the selected node.")); + button_clear_script->set_shortcut(ED_GET_SHORTCUT("scene_tree/clear_script")); + filter_hbc->add_child(button_clear_script); + button_clear_script->hide(); button_hb = memnew(HBoxContainer); vbc->add_child(button_hb); diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index c023c41747..879d072b10 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -48,6 +48,9 @@ Node *SceneTreeEditor::get_scene_node() { void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_id) { + if (connect_to_script_mode) { + return; //dont do anything in this mode + } TreeItem *item = Object::cast_to<TreeItem>(p_item); ERR_FAIL_COND(!item); @@ -99,7 +102,7 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i } } else if (p_id == BUTTON_GROUP) { - if (n->is_class("CanvasItem")) { + if (n->is_class("CanvasItem") || n->is_class("Spatial")) { n->set_meta("_edit_group_", Variant()); _update_tree(); emit_signal("node_changed"); @@ -190,7 +193,25 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { item->set_icon(0, icon); item->set_metadata(0, p_node->get_path()); - if (part_of_subscene) { + if (connect_to_script_mode) { + Color accent = get_color("accent_color", "Editor"); + + if (!p_node->get_script().is_null()) { + //has script + item->add_button(0, get_icon("Script", "EditorIcons"), BUTTON_SCRIPT); + } else { + //has no script + item->set_custom_color(0, get_color("disabled_font_color", "Editor")); + item->set_selectable(0, false); + accent.a *= 0.7; + } + + if (marked.has(p_node)) { + item->set_text(0, String(p_node->get_name()) + " " + TTR("(Connecting From)")); + + item->set_custom_color(0, accent); + } + } else if (part_of_subscene) { //item->set_selectable(0,marked_selectable); if (valid_types.size() == 0) { @@ -199,7 +220,9 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { } else if (marked.has(p_node)) { - item->set_selectable(0, marked_selectable); + if (!connect_to_script_mode) { + item->set_selectable(0, marked_selectable); + } item->set_custom_color(0, get_color("error_color", "Editor")); } else if (!marked_selectable && !marked_children_selectable) { @@ -280,6 +303,10 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { if (is_locked) item->add_button(0, get_icon("Lock", "EditorIcons"), BUTTON_LOCK, false, TTR("Node is locked.\nClick to unlock it.")); + bool is_grouped = p_node->has_meta("_edit_group_"); + if (is_grouped) + item->add_button(0, get_icon("Group", "EditorIcons"), BUTTON_GROUP, false, TTR("Children are not selectable.\nClick to make selectable.")); + bool v = p_node->call("is_visible"); if (v) item->add_button(0, get_icon("GuiVisibilityVisible", "EditorIcons"), BUTTON_VISIBILITY, false, TTR("Toggle Visibility")); @@ -620,6 +647,7 @@ void SceneTreeEditor::set_selected(Node *p_node, bool p_emit_selected) { item->set_as_cursor(0); selected = p_node; tree->ensure_cursor_is_visible(); + } else { if (!p_node) selected = NULL; @@ -974,6 +1002,11 @@ void SceneTreeEditor::_warning_changed(Node *p_for_node) { update_timer->start(); } +void SceneTreeEditor::set_connect_to_script_mode(bool p_enable) { + connect_to_script_mode = p_enable; + update_tree(); +} + void SceneTreeEditor::_bind_methods() { ClassDB::bind_method("_tree_changed", &SceneTreeEditor::_tree_changed); @@ -1016,6 +1049,7 @@ void SceneTreeEditor::_bind_methods() { SceneTreeEditor::SceneTreeEditor(bool p_label, bool p_can_rename, bool p_can_open_instance) { + connect_to_script_mode = false; undo_redo = NULL; tree_dirty = true; selected = NULL; @@ -1091,22 +1125,17 @@ SceneTreeEditor::~SceneTreeEditor() { void SceneTreeDialog::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE) { - connect("confirmed", this, "_select"); - } - - if (p_what == NOTIFICATION_EXIT_TREE) { - disconnect("confirmed", this, "_select"); - } - if (p_what == NOTIFICATION_DRAW) { - - RID ci = get_canvas_item(); - get_stylebox("panel", "PopupMenu")->draw(ci, Rect2(Point2(), get_size())); - } - - if (p_what == NOTIFICATION_VISIBILITY_CHANGED && is_visible_in_tree()) { - - tree->update_tree(); + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + connect("confirmed", this, "_select"); + } break; + case NOTIFICATION_EXIT_TREE: { + disconnect("confirmed", this, "_select"); + } break; + case NOTIFICATION_VISIBILITY_CHANGED: { + if (is_visible_in_tree()) + tree->update_tree(); + } break; } } @@ -1135,8 +1164,6 @@ SceneTreeDialog::SceneTreeDialog() { tree = memnew(SceneTreeEditor(false, false, true)); add_child(tree); - //set_child_rect(tree); - tree->get_scene_tree()->connect("item_activated", this, "_select"); } diff --git a/editor/scene_tree_editor.h b/editor/scene_tree_editor.h index aa4d4dd58a..9158c4aa48 100644 --- a/editor/scene_tree_editor.h +++ b/editor/scene_tree_editor.h @@ -67,6 +67,8 @@ class SceneTreeEditor : public Control { AcceptDialog *error; AcceptDialog *warning; + bool connect_to_script_mode; + int blocked; void _compute_hash(Node *p_node, uint64_t &hash); @@ -151,6 +153,8 @@ public: void update_tree() { _update_tree(); } + void set_connect_to_script_mode(bool p_enable); + Tree *get_scene_tree() { return tree; } SceneTreeEditor(bool p_label = true, bool p_can_rename = false, bool p_can_open_instance = false); |