diff options
Diffstat (limited to 'editor/plugins')
49 files changed, 3440 insertions, 663 deletions
diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp index 28f786e99a..1afd7df049 100644 --- a/editor/plugins/abstract_polygon_2d_editor.cpp +++ b/editor/plugins/abstract_polygon_2d_editor.cpp @@ -481,6 +481,17 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) if (edited_point.valid() && (wip_active || (mm->get_button_mask() & BUTTON_MASK_LEFT))) { Vector2 cpoint = _get_node()->get_global_transform().affine_inverse().xform(canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint))); + + //Move the point in a single axis. Should only work when editing a polygon and while holding shift. + if (mode == MODE_EDIT && mm->get_shift()) { + Vector2 old_point = pre_move_edit.get(selected_point.vertex); + if (ABS(cpoint.x - old_point.x) > ABS(cpoint.y - old_point.y)) { + cpoint.y = old_point.y; + } else { + cpoint.x = old_point.x; + } + } + edited_point = PosVertex(edited_point, cpoint); if (!wip_active) { @@ -638,6 +649,13 @@ void AbstractPolygon2DEditor::forward_canvas_draw_over_viewport(Control *p_overl const Color modulate = vertex == active_point ? Color(0.5, 1, 2) : Color(1, 1, 1); p_overlay->draw_texture(handle, point - handle->get_size() * 0.5, modulate); + + if (vertex == hover_point) { + Ref<Font> font = get_font("font", "Label"); + String num = String::num(vertex.vertex); + Size2 num_size = font->get_string_size(num); + p_overlay->draw_string(font, point - num_size * 0.5, num, Color(1.0, 1.0, 1.0, 0.5)); + } } } diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp index 918f3a2149..5e8fb8e059 100644 --- a/editor/plugins/animation_blend_space_2d_editor.cpp +++ b/editor/plugins/animation_blend_space_2d_editor.cpp @@ -195,7 +195,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven //add triangle! if (blend_space->has_triangle(making_triangle[0], making_triangle[1], making_triangle[2])) { making_triangle.clear(); - EditorNode::get_singleton()->show_warning(TTR("Triangle already exists")); + EditorNode::get_singleton()->show_warning(TTR("Triangle already exists.")); return; } diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index afe2573898..bfee76492b 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -80,6 +80,7 @@ void AnimationNodeBlendTreeEditor::_update_options_menu() { } add_node->get_popup()->add_separator(); add_node->get_popup()->add_item(TTR("Load..."), MENU_LOAD_FILE); + use_popup_menu_position = false; } Size2 AnimationNodeBlendTreeEditor::get_minimum_size() const { @@ -240,7 +241,7 @@ void AnimationNodeBlendTreeEditor::_update_graph() { if (EditorSettings::get_singleton()->get("interface/theme/use_graph_node_headers")) { Ref<StyleBoxFlat> sb = node->get_stylebox("frame", "GraphNode"); - Color c = sb->get_border_color(MARGIN_TOP); + Color c = sb->get_border_color(); Color mono_color = ((c.r + c.g + c.b) / 3) < 0.7 ? Color(1.0, 1.0, 1.0) : Color(0.0, 0.0, 0.0); mono_color.a = 0.85; c = mono_color; @@ -317,7 +318,15 @@ void AnimationNodeBlendTreeEditor::_add_node(int p_idx) { EditorNode::get_singleton()->show_warning(TTR("Output node can't be added to the blend tree.")); return; } - Point2 instance_pos = graph->get_scroll_ofs() + graph->get_size() * 0.5; + + Point2 instance_pos = graph->get_scroll_ofs(); + if (use_popup_menu_position) { + instance_pos += popup_menu_position; + } else { + instance_pos += graph->get_size() * 0.5; + } + + instance_pos /= graph->get_zoom(); int base = 1; String name = base_name; @@ -412,6 +421,40 @@ void AnimationNodeBlendTreeEditor::_delete_request(const String &p_which) { undo_redo->commit_action(); } +void AnimationNodeBlendTreeEditor::_delete_nodes_request() { + + List<StringName> 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()); + } + } + } + + if (to_erase.empty()) + return; + + undo_redo->create_action(TTR("Delete Node(s)")); + + for (List<StringName>::Element *F = to_erase.front(); F; F = F->next()) { + _delete_request(F->get()); + } + + undo_redo->commit_action(); +} + +void AnimationNodeBlendTreeEditor::_popup_request(const Vector2 &p_position) { + + _update_options_menu(); + use_popup_menu_position = true; + popup_menu_position = graph->get_local_mouse_position(); + add_node->get_popup()->set_position(p_position); + add_node->get_popup()->popup(); +} + void AnimationNodeBlendTreeEditor::_node_selected(Object *p_node) { GraphNode *gn = Object::cast_to<GraphNode>(p_node); @@ -730,6 +773,8 @@ void AnimationNodeBlendTreeEditor::_bind_methods() { ClassDB::bind_method("_open_in_editor", &AnimationNodeBlendTreeEditor::_open_in_editor); ClassDB::bind_method("_scroll_changed", &AnimationNodeBlendTreeEditor::_scroll_changed); ClassDB::bind_method("_delete_request", &AnimationNodeBlendTreeEditor::_delete_request); + ClassDB::bind_method("_delete_nodes_request", &AnimationNodeBlendTreeEditor::_delete_nodes_request); + ClassDB::bind_method("_popup_request", &AnimationNodeBlendTreeEditor::_popup_request); ClassDB::bind_method("_edit_filters", &AnimationNodeBlendTreeEditor::_edit_filters); ClassDB::bind_method("_update_filters", &AnimationNodeBlendTreeEditor::_update_filters); ClassDB::bind_method("_filter_edited", &AnimationNodeBlendTreeEditor::_filter_edited); @@ -850,6 +895,7 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { singleton = this; updating = false; + use_popup_menu_position = false; graph = memnew(GraphEdit); add_child(graph); @@ -860,6 +906,8 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { graph->connect("disconnection_request", this, "_disconnection_request", varray(), CONNECT_DEFERRED); graph->connect("node_selected", this, "_node_selected"); graph->connect("scroll_offset_changed", this, "_scroll_changed"); + graph->connect("delete_nodes_request", this, "_delete_nodes_request"); + graph->connect("popup_request", this, "_popup_request"); VSeparator *vs = memnew(VSeparator); graph->get_zoom_hbox()->add_child(vs); @@ -901,7 +949,7 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { filter_dialog->add_child(filter_vbox); filter_enabled = memnew(CheckBox); - filter_enabled->set_text(TTR("Enable filtering")); + filter_enabled->set_text(TTR("Enable Filtering")); filter_enabled->connect("pressed", this, "_filter_toggled"); filter_vbox->add_child(filter_enabled); diff --git a/editor/plugins/animation_blend_tree_editor_plugin.h b/editor/plugins/animation_blend_tree_editor_plugin.h index f2a77cecb4..cb40159a40 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.h +++ b/editor/plugins/animation_blend_tree_editor_plugin.h @@ -51,6 +51,8 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin { Ref<AnimationNodeBlendTree> blend_tree; GraphEdit *graph; MenuButton *add_node; + Vector2 popup_menu_position; + bool use_popup_menu_position; PanelContainer *error_panel; Label *error_label; @@ -97,6 +99,8 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin { void _open_in_editor(const String &p_which); void _anim_selected(int p_index, Array p_options, const String &p_node); void _delete_request(const String &p_which); + void _delete_nodes_request(); + void _popup_request(const Vector2 &p_position); bool _update_filters(const Ref<AnimationNode> &anode); void _edit_filters(const String &p_which); diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index bbaf41e3cc..7c075b5635 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"]); + } } } @@ -841,6 +846,12 @@ void AnimationPlayerEditor::_update_player() { onion_skinning->set_disabled(player == NULL); pin->set_disabled(player == NULL); + if (!player) { + AnimationPlayerEditor::singleton->get_track_editor()->update_keying(); + EditorNode::get_singleton()->update_keying(); + return; + } + int active_idx = -1; for (List<StringName>::Element *E = animlist.front(); E; E = E->next()) { @@ -853,12 +864,6 @@ void AnimationPlayerEditor::_update_player() { active_idx = animation->get_item_count() - 1; } - if (!player) { - AnimationPlayerEditor::singleton->get_track_editor()->update_keying(); - EditorNode::get_singleton()->update_keying(); - return; - } - updating = false; if (active_idx != -1) { animation->select(active_idx); diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp index cb3e5a8129..f06b4b2828 100644 --- a/editor/plugins/animation_state_machine_editor.cpp +++ b/editor/plugins/animation_state_machine_editor.cpp @@ -874,9 +874,9 @@ void AnimationNodeStateMachineEditor::_state_machine_pos_draw() { } to.y = from.y; - float len = MAX(0.0001, playback->get_current_length()); + float len = MAX(0.0001, current_length); - float pos = CLAMP(playback->get_current_play_pos(), 0, len); + float pos = CLAMP(play_pos, 0, len); float c = pos / len; Color fg = get_color("font_color", "Label"); Color bg = fg; @@ -1011,7 +1011,8 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { bool is_playing = false; StringName current_node; StringName blend_from_node; - float play_pos = 0; + play_pos = 0; + current_length = 0; if (playback.is_valid()) { tp = playback->get_travel_path(); @@ -1019,6 +1020,7 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { current_node = playback->get_current_node(); blend_from_node = playback->get_blend_from_node(); play_pos = playback->get_current_play_pos(); + current_length = playback->get_current_length(); } { @@ -1046,6 +1048,27 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { state_machine_play_pos->update(); } + { + if (current_node != StringName() && state_machine->has_node(current_node)) { + + String next = current_node; + Ref<AnimationNodeStateMachine> anodesm = state_machine->get_node(next); + Ref<AnimationNodeStateMachinePlayback> current_node_playback; + + while (anodesm.is_valid()) { + current_node_playback = AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + next + "/playback"); + next += "/" + current_node_playback->get_current_node(); + anodesm = anodesm->get_node(current_node_playback->get_current_node()); + } + + // when current_node is a state machine, use playback of current_node to set play_pos + if (current_node_playback.is_valid()) { + play_pos = current_node_playback->get_current_play_pos(); + current_length = current_node_playback->get_current_length(); + } + } + } + if (last_play_pos != play_pos) { last_play_pos = play_pos; diff --git a/editor/plugins/animation_state_machine_editor.h b/editor/plugins/animation_state_machine_editor.h index 1c4c06090a..8b0a5a0b00 100644 --- a/editor/plugins/animation_state_machine_editor.h +++ b/editor/plugins/animation_state_machine_editor.h @@ -160,6 +160,8 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin { StringName last_current_node; Vector<StringName> last_travel_path; float last_play_pos; + float play_pos; + float current_length; float error_time; String error_text; diff --git a/editor/plugins/animation_tree_player_editor_plugin.cpp b/editor/plugins/animation_tree_player_editor_plugin.cpp index e2a44069d9..f5d21ffb26 100644 --- a/editor/plugins/animation_tree_player_editor_plugin.cpp +++ b/editor/plugins/animation_tree_player_editor_plugin.cpp @@ -200,7 +200,8 @@ void AnimationTreePlayerEditor::_edit_dialog_changed() { if (anim_tree->transition_node_get_current(edited_node) != edit_option->get_selected()) anim_tree->transition_node_set_current(edited_node, edit_option->get_selected()); } break; - default: {} + default: { + } } } @@ -457,7 +458,8 @@ void AnimationTreePlayerEditor::_popup_edit_dialog() { edit_dialog->set_size(Size2(150, 100)); } break; - default: {} + default: { + } } } @@ -555,7 +557,8 @@ void AnimationTreePlayerEditor::_draw_node(const StringName &p_node) { text += "->"; break; - default: {} + default: { + } } font->draw(ci, ofs + ascofs + Point2(3, 0), text, font_color); @@ -740,7 +743,8 @@ void AnimationTreePlayerEditor::_gui_input(Ref<InputEvent> p_event) { //open editor //_node_edit_property(click_node); } break; - default: {} + default: { + } } } if (mb->get_button_index() == 2) { @@ -817,7 +821,8 @@ void AnimationTreePlayerEditor::_gui_input(Ref<InputEvent> p_event) { anim_tree->node_set_position(click_node, new_pos); } break; - default: {} + default: { + } } click_type = CLICK_NONE; @@ -835,7 +840,7 @@ void AnimationTreePlayerEditor::_gui_input(Ref<InputEvent> p_event) { click_motion = Point2(mm->get_position().x, mm->get_position().y); update(); } - if ((mm->get_button_mask() & 4 || Input::get_singleton()->is_key_pressed(KEY_SPACE))) { + if (mm->get_button_mask() & 4 || Input::get_singleton()->is_key_pressed(KEY_SPACE)) { h_scroll->set_value(h_scroll->get_value() - mm->get_relative().x); v_scroll->set_value(v_scroll->get_value() - mm->get_relative().y); diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index a9e9607bc5..0dfb53b34a 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -175,7 +175,7 @@ void EditorAssetLibraryItemDescription::set_image(int p_type, int p_index, const Ref<Image> overlay = get_icon("PlayOverlay", "EditorIcons")->get_data(); Ref<Image> thumbnail = p_image->get_data(); thumbnail = thumbnail->duplicate(); - Point2 overlay_pos = Point2((thumbnail->get_width() - overlay->get_width()) / 2, (thumbnail->get_height() - overlay->get_height()) / 2); + Point2 overlay_pos = Point2((thumbnail->get_width() - overlay->get_width() / 2) / 2, (thumbnail->get_height() - overlay->get_height() / 2) / 2); // Overlay and thumbnail need the same format for `blend_rect` to work. thumbnail->convert(Image::FORMAT_RGBA8); @@ -310,15 +310,20 @@ EditorAssetLibraryItemDescription::EditorAssetLibraryItemDescription() { description = memnew(RichTextLabel); description->connect("meta_clicked", this, "_link_click"); + description->set_custom_minimum_size(Size2(440 * EDSCALE, 300 * EDSCALE)); desc_bg->add_child(description); + VBoxContainer *previews_vbox = memnew(VBoxContainer); + hbox->add_child(previews_vbox); + previews_vbox->add_constant_override("separation", 15 * EDSCALE); + preview = memnew(TextureRect); preview->set_custom_minimum_size(Size2(640 * EDSCALE, 345 * EDSCALE)); - hbox->add_child(preview); + previews_vbox->add_child(preview); previews_bg = memnew(PanelContainer); - vbox->add_child(previews_bg); - previews_bg->set_custom_minimum_size(Size2(0, 101 * EDSCALE)); + previews_vbox->add_child(previews_bg); + previews_bg->set_custom_minimum_size(Size2(640 * EDSCALE, 101 * EDSCALE)); previews = memnew(ScrollContainer); previews_bg->add_child(previews); @@ -454,7 +459,8 @@ void EditorAssetLibraryItemDownload::_notification(int p_what) { progress->set_max(1); progress->set_value(0); } break; - default: {} + default: { + } } prev_status = cstatus; } @@ -612,7 +618,8 @@ void EditorAssetLibrary::_notification(int p_what) { case HTTPClient::STATUS_BODY: { load_status->set_value(0.4); } break; - default: {} + default: { + } } } @@ -818,7 +825,7 @@ void EditorAssetLibrary::_image_request_completed(int p_status, int p_code, cons _image_update(p_code == HTTPClient::RESPONSE_NOT_MODIFIED, true, p_data, p_queue_id); } else { - // WARN_PRINTS("Error getting image file from URL: " + image_queue[p_queue_id].image_url); + WARN_PRINTS("Error getting image file from URL: " + image_queue[p_queue_id].image_url); Object *obj = ObjectDB::get_instance(image_queue[p_queue_id].target); if (obj) { obj->call("set_image", image_queue[p_queue_id].image_type, image_queue[p_queue_id].image_index, get_icon("DefaultProjectIcon", "EditorIcons")); diff --git a/editor/plugins/baked_lightmap_editor_plugin.cpp b/editor/plugins/baked_lightmap_editor_plugin.cpp index d90ff95c1f..d75f06de12 100644 --- a/editor/plugins/baked_lightmap_editor_plugin.cpp +++ b/editor/plugins/baked_lightmap_editor_plugin.cpp @@ -50,7 +50,8 @@ void BakedLightmapEditorPlugin::_bake() { case BakedLightmap::BAKE_ERROR_CANT_CREATE_IMAGE: EditorNode::get_singleton()->show_warning(TTR("Failed creating lightmap images, make sure path is writable.")); break; - default: {} + default: { + } } } } diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index ac05b03ed2..c0f2410636 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -84,7 +84,6 @@ public: container = memnew(VBoxContainer); add_child(container); - //set_child_rect(container); child_container = memnew(GridContainer); child_container->set_columns(3); @@ -99,12 +98,14 @@ public: grid_offset_x->set_min(-SPIN_BOX_GRID_RANGE); grid_offset_x->set_max(SPIN_BOX_GRID_RANGE); grid_offset_x->set_suffix("px"); + grid_offset_x->set_h_size_flags(SIZE_EXPAND_FILL); child_container->add_child(grid_offset_x); grid_offset_y = memnew(SpinBox); grid_offset_y->set_min(-SPIN_BOX_GRID_RANGE); grid_offset_y->set_max(SPIN_BOX_GRID_RANGE); grid_offset_y->set_suffix("px"); + grid_offset_y->set_h_size_flags(SIZE_EXPAND_FILL); child_container->add_child(grid_offset_y); label = memnew(Label); @@ -116,12 +117,14 @@ public: grid_step_x->set_min(0.01); grid_step_x->set_max(SPIN_BOX_GRID_RANGE); grid_step_x->set_suffix("px"); + grid_step_x->set_h_size_flags(SIZE_EXPAND_FILL); child_container->add_child(grid_step_x); grid_step_y = memnew(SpinBox); grid_step_y->set_min(0.01); grid_step_y->set_max(SPIN_BOX_GRID_RANGE); grid_step_y->set_suffix("px"); + grid_step_y->set_h_size_flags(SIZE_EXPAND_FILL); child_container->add_child(grid_step_y); container->add_child(memnew(HSeparator)); @@ -139,6 +142,7 @@ public: rotation_offset->set_min(-SPIN_BOX_ROTATION_RANGE); rotation_offset->set_max(SPIN_BOX_ROTATION_RANGE); rotation_offset->set_suffix("deg"); + rotation_offset->set_h_size_flags(SIZE_EXPAND_FILL); child_container->add_child(rotation_offset); label = memnew(Label); @@ -150,6 +154,7 @@ public: rotation_step->set_min(-SPIN_BOX_ROTATION_RANGE); rotation_step->set_max(SPIN_BOX_ROTATION_RANGE); rotation_step->set_suffix("deg"); + rotation_step->set_h_size_flags(SIZE_EXPAND_FILL); child_container->add_child(rotation_step); } @@ -1072,36 +1077,36 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) { } } - if (drag_type == DRAG_NONE) { + if (!panning) { if (b->is_pressed() && (b->get_button_index() == BUTTON_MIDDLE || (b->get_button_index() == BUTTON_LEFT && tool == TOOL_PAN) || (b->get_button_index() == BUTTON_LEFT && !EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning") && Input::get_singleton()->is_key_pressed(KEY_SPACE)))) { // Pan the viewport - drag_type = DRAG_PAN; + panning = true; } } - if (drag_type == DRAG_PAN) { + if (panning) { if (!b->is_pressed()) { // Stop panning the viewport (for any mouse button press) - drag_type = DRAG_NONE; + panning = false; } } } Ref<InputEventKey> k = p_event; if (k.is_valid()) { - if (k->get_scancode() == KEY_SPACE && EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning")) { - if (drag_type == DRAG_NONE) { + if (k->get_scancode() == KEY_SPACE && (EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning") || drag_type != DRAG_NONE)) { + if (!panning) { if (k->is_pressed() && !k->is_echo()) { //Pan the viewport - drag_type = DRAG_PAN; + panning = true; } - } else if (drag_type == DRAG_PAN) { + } else if (panning) { if (!k->is_pressed()) { // Stop panning the viewport (for any mouse button press) - drag_type = DRAG_NONE; + panning = false; } } } @@ -1109,7 +1114,7 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) { Ref<InputEventMouseMotion> m = p_event; if (m.is_valid()) { - if (drag_type == DRAG_PAN) { + if (panning) { // Pan the viewport Point2i relative; if (bool(EditorSettings::get_singleton()->get("editors/2d/warped_mouse_panning"))) { @@ -1340,6 +1345,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; } @@ -1462,10 +1471,14 @@ bool CanvasItemEditor::_gui_input_anchors(const Ref<InputEvent> &p_event) { if (!use_single_axis || use_y) control->set_anchor(MARGIN_BOTTOM, new_anchor.y, false, false); break; case DRAG_ANCHOR_ALL: - if (!use_single_axis || !use_y) control->set_anchor(MARGIN_LEFT, new_anchor.x, false, true); - if (!use_single_axis || !use_y) control->set_anchor(MARGIN_RIGHT, new_anchor.x, false, true); - if (!use_single_axis || use_y) control->set_anchor(MARGIN_TOP, new_anchor.y, false, true); - if (!use_single_axis || use_y) control->set_anchor(MARGIN_BOTTOM, new_anchor.y, false, true); + if (!use_single_axis || !use_y) { + control->set_anchor(MARGIN_LEFT, new_anchor.x, false, true); + control->set_anchor(MARGIN_RIGHT, new_anchor.x, false, true); + } + if (!use_single_axis || use_y) { + control->set_anchor(MARGIN_TOP, new_anchor.y, false, true); + control->set_anchor(MARGIN_BOTTOM, new_anchor.y, false, true); + } break; default: break; @@ -1641,6 +1654,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 +1763,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; @@ -1847,11 +1867,14 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { } // Confirm the move (only if it was moved) - if (b.is_valid() && !b->is_pressed() && b->get_button_index() == BUTTON_LEFT && (drag_type == DRAG_MOVE)) { + if (b.is_valid() && !b->is_pressed() && b->get_button_index() == BUTTON_LEFT) { if (transform.affine_inverse().xform(b->get_position()) != drag_from) { _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; @@ -2190,32 +2213,34 @@ bool CanvasItemEditor::_gui_input_hover(const Ref<InputEvent> &p_event) { void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) { bool accepted = false; - if ((accepted = _gui_input_rulers_and_guides(p_event))) { - //printf("Rulers and guides\n"); - } else if ((accepted = editor->get_editor_plugins_over()->forward_gui_input(p_event))) { - //printf("Plugin\n"); - } else if ((accepted = _gui_input_open_scene_on_double_click(p_event))) { - //printf("Open scene on double click\n"); - } else if ((accepted = _gui_input_anchors(p_event))) { - //printf("Anchors\n"); - } else if ((accepted = _gui_input_scale(p_event))) { - //printf("Set scale\n"); - } else if ((accepted = _gui_input_pivot(p_event))) { - //printf("Set pivot\n"); - } else if ((accepted = _gui_input_resize(p_event))) { - //printf("Resize\n"); - } else if ((accepted = _gui_input_rotate(p_event))) { - //printf("Rotate\n"); - } else if ((accepted = _gui_input_move(p_event))) { - //printf("Move\n"); - } else if ((accepted = _gui_input_zoom_or_pan(p_event))) { - //printf("Zoom or pan\n"); - } else if ((accepted = _gui_input_select(p_event))) { - //printf("Selection\n"); - } else { - //printf("Not accepted\n"); + if (EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning") || !Input::get_singleton()->is_key_pressed(KEY_SPACE)) { + if ((accepted = _gui_input_rulers_and_guides(p_event))) { + //printf("Rulers and guides\n"); + } else if ((accepted = editor->get_editor_plugins_over()->forward_gui_input(p_event))) { + //printf("Plugin\n"); + } else if ((accepted = _gui_input_open_scene_on_double_click(p_event))) { + //printf("Open scene on double click\n"); + } else if ((accepted = _gui_input_anchors(p_event))) { + //printf("Anchors\n"); + } else if ((accepted = _gui_input_scale(p_event))) { + //printf("Set scale\n"); + } else if ((accepted = _gui_input_pivot(p_event))) { + //printf("Set pivot\n"); + } else if ((accepted = _gui_input_resize(p_event))) { + //printf("Resize\n"); + } else if ((accepted = _gui_input_rotate(p_event))) { + //printf("Rotate\n"); + } else if ((accepted = _gui_input_move(p_event))) { + //printf("Move\n"); + } else if ((accepted = _gui_input_select(p_event))) { + //printf("Selection\n"); + } else { + //printf("Not accepted\n"); + } } + accepted = (_gui_input_zoom_or_pan(p_event) || accepted); + if (accepted) accept_event(); @@ -2259,8 +2284,6 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) { case DRAG_MOVE: c = CURSOR_MOVE; break; - case DRAG_PAN: - c = CURSOR_DRAG; default: break; } @@ -2597,6 +2620,7 @@ void CanvasItemEditor::_draw_control_helpers(Control *control) { case DRAG_TOP_LEFT: case DRAG_BOTTOM_LEFT: _draw_margin_at_position(control->get_size().width, parent_transform.xform(Vector2((node_pos_in_parent[0] + node_pos_in_parent[2]) / 2, node_pos_in_parent[3])) + Vector2(0, 5), MARGIN_BOTTOM); + FALLTHROUGH; case DRAG_MOVE: start = Vector2(node_pos_in_parent[0], Math::lerp(node_pos_in_parent[1], node_pos_in_parent[3], ratio)); end = start - Vector2(control->get_margin(MARGIN_LEFT), 0); @@ -2611,6 +2635,7 @@ void CanvasItemEditor::_draw_control_helpers(Control *control) { case DRAG_TOP_RIGHT: case DRAG_BOTTOM_RIGHT: _draw_margin_at_position(control->get_size().width, parent_transform.xform(Vector2((node_pos_in_parent[0] + node_pos_in_parent[2]) / 2, node_pos_in_parent[3])) + Vector2(0, 5), MARGIN_BOTTOM); + FALLTHROUGH; case DRAG_MOVE: start = Vector2(node_pos_in_parent[2], Math::lerp(node_pos_in_parent[3], node_pos_in_parent[1], ratio)); end = start - Vector2(control->get_margin(MARGIN_RIGHT), 0); @@ -2625,6 +2650,7 @@ void CanvasItemEditor::_draw_control_helpers(Control *control) { case DRAG_TOP_LEFT: case DRAG_TOP_RIGHT: _draw_margin_at_position(control->get_size().height, parent_transform.xform(Vector2(node_pos_in_parent[2], (node_pos_in_parent[1] + node_pos_in_parent[3]) / 2)) + Vector2(5, 0), MARGIN_RIGHT); + FALLTHROUGH; case DRAG_MOVE: start = Vector2(Math::lerp(node_pos_in_parent[0], node_pos_in_parent[2], ratio), node_pos_in_parent[1]); end = start - Vector2(0, control->get_margin(MARGIN_TOP)); @@ -2639,6 +2665,7 @@ void CanvasItemEditor::_draw_control_helpers(Control *control) { case DRAG_BOTTOM_LEFT: case DRAG_BOTTOM_RIGHT: _draw_margin_at_position(control->get_size().height, parent_transform.xform(Vector2(node_pos_in_parent[2], (node_pos_in_parent[1] + node_pos_in_parent[3]) / 2) + Vector2(5, 0)), MARGIN_RIGHT); + FALLTHROUGH; case DRAG_MOVE: start = Vector2(Math::lerp(node_pos_in_parent[2], node_pos_in_parent[0], ratio), node_pos_in_parent[3]); end = start - Vector2(0, control->get_margin(MARGIN_BOTTOM)); @@ -2828,13 +2855,20 @@ void CanvasItemEditor::_draw_selection() { Point2 bsfrom = transform.xform(drag_from); Point2 bsto = transform.xform(box_selecting_to); - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(bsfrom, bsto - bsfrom), Color(0.7, 0.7, 1.0, 0.3)); + VisualServer::get_singleton()->canvas_item_add_rect( + ci, + Rect2(bsfrom, bsto - bsfrom), + get_color("accent_color", "Editor") * Color(1, 1, 1, 0.375)); } - Color rotate_color(0.4, 0.7, 1.0, 0.8); if (drag_type == DRAG_ROTATE) { // Draw the line when rotating a node - viewport->draw_line(transform.xform(drag_rotation_center), transform.xform(drag_to), rotate_color); + viewport->draw_line( + transform.xform(drag_rotation_center), + transform.xform(drag_to), + get_color("accent_color", "Editor") * Color(1, 1, 1, 0.6), + Math::round(2 * EDSCALE), + true); } } @@ -3373,6 +3407,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")); @@ -3402,7 +3437,7 @@ void CanvasItemEditor::_notification(int p_what) { p->add_separator(); p->add_icon_item(get_icon("ControlAlignWide", "EditorIcons"), "Full Rect", ANCHORS_AND_MARGINS_PRESET_WIDE); p->add_separator(); - p->add_submenu_item(TTR("Anchors only"), "Anchors"); + p->add_submenu_item(TTR("Anchors Only"), "Anchors"); p->set_item_icon(20, get_icon("Anchor", "EditorIcons")); anchors_popup->clear(); @@ -3705,6 +3740,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); @@ -3814,6 +3920,8 @@ void CanvasItemEditor::_popup_callback(int p_op) { viewport->update(); } break; case LOCK_SELECTED: { + undo_redo->create_action(TTR("Lock Selected")); + List<Node *> selection = editor_selection->get_selected_node_list(); for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->get()); @@ -3822,12 +3930,18 @@ void CanvasItemEditor::_popup_callback(int p_op) { if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root()) continue; - canvas_item->set_meta("_edit_lock_", true); - emit_signal("item_lock_status_changed"); + undo_redo->add_do_method(canvas_item, "set_meta", "_edit_lock_", true); + undo_redo->add_undo_method(canvas_item, "remove_meta", "_edit_lock_"); + undo_redo->add_do_method(this, "emit_signal", "item_lock_status_changed"); + undo_redo->add_undo_method(this, "emit_signal", "item_lock_status_changed"); } - viewport->update(); + undo_redo->add_do_method(viewport, "update", Variant()); + undo_redo->add_undo_method(viewport, "update", Variant()); + undo_redo->commit_action(); } break; case UNLOCK_SELECTED: { + undo_redo->create_action(TTR("Unlock Selected")); + List<Node *> selection = editor_selection->get_selected_node_list(); for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->get()); @@ -3836,12 +3950,18 @@ void CanvasItemEditor::_popup_callback(int p_op) { if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root()) continue; - canvas_item->set_meta("_edit_lock_", Variant()); - emit_signal("item_lock_status_changed"); + undo_redo->add_do_method(canvas_item, "remove_meta", "_edit_lock_"); + undo_redo->add_undo_method(canvas_item, "set_meta", "_edit_lock_", true); + undo_redo->add_do_method(this, "emit_signal", "item_lock_status_changed"); + undo_redo->add_undo_method(this, "emit_signal", "item_lock_status_changed"); } - viewport->update(); + undo_redo->add_do_method(viewport, "update", Variant()); + undo_redo->add_undo_method(viewport, "update", Variant()); + undo_redo->commit_action(); } break; case GROUP_SELECTED: { + undo_redo->create_action(TTR("Group Selected")); + List<Node *> selection = editor_selection->get_selected_node_list(); for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->get()); @@ -3850,12 +3970,18 @@ void CanvasItemEditor::_popup_callback(int p_op) { if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root()) continue; - canvas_item->set_meta("_edit_group_", true); - emit_signal("item_group_status_changed"); + undo_redo->add_do_method(canvas_item, "set_meta", "_edit_group_", true); + undo_redo->add_undo_method(canvas_item, "remove_meta", "_edit_group_"); + undo_redo->add_do_method(this, "emit_signal", "item_group_status_changed"); + undo_redo->add_undo_method(this, "emit_signal", "item_group_status_changed"); } - viewport->update(); + undo_redo->add_do_method(viewport, "update", Variant()); + undo_redo->add_undo_method(viewport, "update", Variant()); + undo_redo->commit_action(); } break; case UNGROUP_SELECTED: { + undo_redo->create_action(TTR("Ungroup Selected")); + List<Node *> selection = editor_selection->get_selected_node_list(); for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->get()); @@ -3864,10 +3990,14 @@ void CanvasItemEditor::_popup_callback(int p_op) { if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root()) continue; - canvas_item->set_meta("_edit_group_", Variant()); - emit_signal("item_group_status_changed"); + undo_redo->add_do_method(canvas_item, "remove_meta", "_edit_group_"); + undo_redo->add_undo_method(canvas_item, "set_meta", "_edit_group_", true); + undo_redo->add_do_method(this, "emit_signal", "item_group_status_changed"); + undo_redo->add_undo_method(this, "emit_signal", "item_group_status_changed"); } - viewport->update(); + undo_redo->add_do_method(viewport, "update", Variant()); + undo_redo->add_undo_method(viewport, "update", Variant()); + undo_redo->commit_action(); } break; case ANCHORS_AND_MARGINS_PRESET_TOP_LEFT: { _set_anchors_and_margins_preset(PRESET_TOP_LEFT); @@ -3972,73 +4102,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: { @@ -4144,6 +4208,14 @@ void CanvasItemEditor::_popup_callback(int p_op) { _focus_selection(p_op); } break; + case PREVIEW_CANVAS_SCALE: { + + bool preview = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(PREVIEW_CANVAS_SCALE)); + preview = !preview; + VS::get_singleton()->canvas_set_disable_scale(!preview); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(PREVIEW_CANVAS_SCALE), preview); + + } break; case SKELETON_MAKE_BONES: { Map<Node *, Object *> &selection = editor_selection->get_selection(); @@ -4162,6 +4234,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { if (!skeleton_show_bones) skeleton_menu->get_popup()->activate_item(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES)); } + _queue_update_bone_list(); viewport->update(); } break; @@ -4181,6 +4254,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { if (!skeleton_show_bones) skeleton_menu->get_popup()->activate_item(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES)); } + _queue_update_bone_list(); viewport->update(); } break; @@ -4569,6 +4643,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { drag_to = Vector2(); dragged_guide_pos = Point2(); dragged_guide_index = -1; + panning = false; bone_last_frame = 0; @@ -4735,28 +4810,29 @@ 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"); p->set_hide_on_checkable_item_selection(false); - p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_grid", TTR("Snap to grid")), SNAP_USE_GRID); + p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_grid", TTR("Snap to Grid")), SNAP_USE_GRID); p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/use_rotation_snap", TTR("Use Rotation Snap")), SNAP_USE_ROTATION); p->add_shortcut(ED_SHORTCUT("canvas_item_editor/configure_snap", TTR("Configure Snap...")), SNAP_CONFIGURE); p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_relative", TTR("Snap Relative")), SNAP_RELATIVE); p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/use_pixel_snap", TTR("Use Pixel Snap")), SNAP_USE_PIXEL); - p->add_submenu_item(TTR("Smart snapping"), "SmartSnapping"); + p->add_submenu_item(TTR("Smart Snapping"), "SmartSnapping"); smartsnap_config_popup = memnew(PopupMenu); p->add_child(smartsnap_config_popup); smartsnap_config_popup->set_name("SmartSnapping"); smartsnap_config_popup->connect("id_pressed", this, "_popup_callback"); smartsnap_config_popup->set_hide_on_checkable_item_selection(false); - smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_node_parent", TTR("Snap to parent")), SNAP_USE_NODE_PARENT); - smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_node_anchors", TTR("Snap to node anchor")), SNAP_USE_NODE_ANCHORS); - smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_node_sides", TTR("Snap to node sides")), SNAP_USE_NODE_SIDES); - smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_node_center", TTR("Snap to node center")), SNAP_USE_NODE_CENTER); - smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_other_nodes", TTR("Snap to other nodes")), SNAP_USE_OTHER_NODES); - smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_guides", TTR("Snap to guides")), SNAP_USE_GUIDES); + smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_node_parent", TTR("Snap to Parent")), SNAP_USE_NODE_PARENT); + smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_node_anchors", TTR("Snap to Node Anchor")), SNAP_USE_NODE_ANCHORS); + smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_node_sides", TTR("Snap to Node Sides")), SNAP_USE_NODE_SIDES); + smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_node_center", TTR("Snap to Node Center")), SNAP_USE_NODE_CENTER); + smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_other_nodes", TTR("Snap to Other Nodes")), SNAP_USE_OTHER_NODES); + smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_guides", TTR("Snap to Guides")), SNAP_USE_GUIDES); hb->add_child(memnew(VSeparator)); @@ -4786,6 +4862,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); @@ -4804,8 +4881,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); @@ -4817,11 +4896,14 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { p->add_separator(); p->add_shortcut(ED_SHORTCUT("canvas_item_editor/center_selection", TTR("Center Selection"), KEY_F), VIEW_CENTER_TO_SELECTION); p->add_shortcut(ED_SHORTCUT("canvas_item_editor/frame_selection", TTR("Frame Selection"), KEY_MASK_SHIFT | KEY_F), VIEW_FRAME_TO_SELECTION); + p->add_separator(); + p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/preview_canvas_scale", TTR("Preview Canvas Scale"), KEY_MASK_SHIFT | KEY_MASK_CMD | KEY_P), PREVIEW_CANVAS_SCALE); presets_menu = memnew(MenuButton); 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"); @@ -4842,6 +4924,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); @@ -4849,26 +4932,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(); @@ -4922,7 +5015,6 @@ void CanvasItemEditorPlugin::make_visible(bool p_visible) { canvas_item_editor->show(); canvas_item_editor->set_physics_process(true); VisualServer::get_singleton()->viewport_set_hide_canvas(editor->get_scene_root()->get_viewport_rid(), false); - canvas_item_editor->viewport->grab_focus(); } else { @@ -5378,7 +5470,7 @@ CanvasItemEditorViewport::CanvasItemEditorViewport(EditorNode *p_node, CanvasIte selector = memnew(AcceptDialog); editor->get_gui_base()->add_child(selector); - selector->set_title(TTR("Change default type")); + selector->set_title(TTR("Change Default Type")); selector->connect("confirmed", this, "_on_change_type_confirmed"); selector->connect("popup_hide", this, "_on_change_type_closed"); @@ -5415,6 +5507,7 @@ CanvasItemEditorViewport::CanvasItemEditorViewport(EditorNode *p_node, CanvasIte label_desc->add_constant_override("line_spacing", 0); label_desc->hide(); editor->get_gui_base()->add_child(label_desc); + VS::get_singleton()->canvas_set_disable_scale(true); } CanvasItemEditorViewport::~CanvasItemEditorViewport() { diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index 7b6563944e..14ea81f302 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -171,6 +171,7 @@ private: ANIM_CLEAR_POSE, VIEW_CENTER_TO_SELECTION, VIEW_FRAME_TO_SELECTION, + PREVIEW_CANVAS_SCALE, SKELETON_MAKE_BONES, SKELETON_CLEAR_BONES, SKELETON_SHOW_BONES, @@ -204,8 +205,7 @@ private: DRAG_V_GUIDE, DRAG_H_GUIDE, DRAG_DOUBLE_GUIDE, - DRAG_KEY_MOVE, - DRAG_PAN + DRAG_KEY_MOVE }; EditorSelection *editor_selection; @@ -261,6 +261,7 @@ private: bool key_pos; bool key_rot; bool key_scale; + bool panning; MenuOption last_option; @@ -350,6 +351,7 @@ private: Button *key_rot_button; Button *key_scale_button; Button *key_insert_button; + Button *key_auto_insert_button; PopupMenu *selection_menu; @@ -421,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/collision_shape_2d_editor_plugin.cpp b/editor/plugins/collision_shape_2d_editor_plugin.cpp index 10023d88bf..c8561d22a4 100644 --- a/editor/plugins/collision_shape_2d_editor_plugin.cpp +++ b/editor/plugins/collision_shape_2d_editor_plugin.cpp @@ -93,7 +93,7 @@ Variant CollisionShape2DEditor::get_handle_value(int idx) const { case RECTANGLE_SHAPE: { Ref<RectangleShape2D> rect = node->get_shape(); - if (idx < 2) { + if (idx < 3) { return rect->get_extents().abs(); } @@ -175,12 +175,15 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) { } break; case RECTANGLE_SHAPE: { - if (idx < 2) { + if (idx < 3) { Ref<RectangleShape2D> rect = node->get_shape(); Vector2 extents = rect->get_extents(); - extents[idx] = p_point[idx]; - + if (idx == 2) { + extents = p_point; + } else { + extents[idx] = p_point[idx]; + } rect->set_extents(extents.abs()); canvas_item_editor->update_viewport(); @@ -496,13 +499,15 @@ void CollisionShape2DEditor::forward_canvas_draw_over_viewport(Control *p_overla case RECTANGLE_SHAPE: { Ref<RectangleShape2D> shape = node->get_shape(); - handles.resize(2); + handles.resize(3); Vector2 ext = shape->get_extents(); handles.write[0] = Point2(ext.x, 0); handles.write[1] = Point2(0, -ext.y); + handles.write[2] = Point2(ext.x, -ext.y); p_overlay->draw_texture(h, gt.xform(handles[0]) - size); p_overlay->draw_texture(h, gt.xform(handles[1]) - size); + p_overlay->draw_texture(h, gt.xform(handles[2]) - size); } break; diff --git a/editor/plugins/cpu_particles_2d_editor_plugin.cpp b/editor/plugins/cpu_particles_2d_editor_plugin.cpp new file mode 100644 index 0000000000..1622ce17b2 --- /dev/null +++ b/editor/plugins/cpu_particles_2d_editor_plugin.cpp @@ -0,0 +1,308 @@ +/*************************************************************************/ +/* cpu_particles_2d_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "cpu_particles_2d_editor_plugin.h" + +#include "canvas_item_editor_plugin.h" +#include "core/io/image_loader.h" +#include "scene/2d/cpu_particles_2d.h" +#include "scene/gui/separator.h" +#include "scene/resources/particles_material.h" + +void CPUParticles2DEditorPlugin::edit(Object *p_object) { + + particles = Object::cast_to<CPUParticles2D>(p_object); +} + +bool CPUParticles2DEditorPlugin::handles(Object *p_object) const { + + return p_object->is_class("CPUParticles2D"); +} + +void CPUParticles2DEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + + toolbar->show(); + } else { + + toolbar->hide(); + } +} + +void CPUParticles2DEditorPlugin::_file_selected(const String &p_file) { + + source_emission_file = p_file; + emission_mask->popup_centered_minsize(); +} + +void CPUParticles2DEditorPlugin::_menu_callback(int p_idx) { + + switch (p_idx) { + case MENU_LOAD_EMISSION_MASK: { + + file->popup_centered_ratio(); + + } break; + case MENU_CLEAR_EMISSION_MASK: { + + emission_mask->popup_centered_minsize(); + } break; + } +} + +void CPUParticles2DEditorPlugin::_generate_emission_mask() { + + Ref<Image> img; + img.instance(); + Error err = ImageLoader::load_image(source_emission_file, img); + ERR_EXPLAIN(TTR("Error loading image:") + " " + source_emission_file); + ERR_FAIL_COND(err != OK); + + if (img->is_compressed()) { + img->decompress(); + } + img->convert(Image::FORMAT_RGBA8); + ERR_FAIL_COND(img->get_format() != Image::FORMAT_RGBA8); + Size2i s = Size2(img->get_width(), img->get_height()); + ERR_FAIL_COND(s.width == 0 || s.height == 0); + + Vector<Point2> valid_positions; + Vector<Point2> valid_normals; + Vector<uint8_t> valid_colors; + + valid_positions.resize(s.width * s.height); + + EmissionMode emode = (EmissionMode)emission_mask_mode->get_selected(); + + if (emode == EMISSION_MODE_BORDER_DIRECTED) { + valid_normals.resize(s.width * s.height); + } + + bool capture_colors = emission_colors->is_pressed(); + + if (capture_colors) { + valid_colors.resize(s.width * s.height * 4); + } + + int vpc = 0; + + { + PoolVector<uint8_t> data = img->get_data(); + PoolVector<uint8_t>::Read r = data.read(); + + for (int i = 0; i < s.width; i++) { + for (int j = 0; j < s.height; j++) { + + uint8_t a = r[(j * s.width + i) * 4 + 3]; + + if (a > 128) { + + if (emode == EMISSION_MODE_SOLID) { + + if (capture_colors) { + valid_colors.write[vpc * 4 + 0] = r[(j * s.width + i) * 4 + 0]; + valid_colors.write[vpc * 4 + 1] = r[(j * s.width + i) * 4 + 1]; + valid_colors.write[vpc * 4 + 2] = r[(j * s.width + i) * 4 + 2]; + valid_colors.write[vpc * 4 + 3] = r[(j * s.width + i) * 4 + 3]; + } + valid_positions.write[vpc++] = Point2(i, j); + + } else { + + bool on_border = false; + for (int x = i - 1; x <= i + 1; x++) { + for (int y = j - 1; y <= j + 1; y++) { + + if (x < 0 || y < 0 || x >= s.width || y >= s.height || r[(y * s.width + x) * 4 + 3] <= 128) { + on_border = true; + break; + } + } + + if (on_border) + break; + } + + if (on_border) { + valid_positions.write[vpc] = Point2(i, j); + + if (emode == EMISSION_MODE_BORDER_DIRECTED) { + Vector2 normal; + for (int x = i - 2; x <= i + 2; x++) { + for (int y = j - 2; y <= j + 2; y++) { + + if (x == i && y == j) + continue; + + if (x < 0 || y < 0 || x >= s.width || y >= s.height || r[(y * s.width + x) * 4 + 3] <= 128) { + normal += Vector2(x - i, y - j).normalized(); + } + } + } + + normal.normalize(); + valid_normals.write[vpc] = normal; + } + + if (capture_colors) { + valid_colors.write[vpc * 4 + 0] = r[(j * s.width + i) * 4 + 0]; + valid_colors.write[vpc * 4 + 1] = r[(j * s.width + i) * 4 + 1]; + valid_colors.write[vpc * 4 + 2] = r[(j * s.width + i) * 4 + 2]; + valid_colors.write[vpc * 4 + 3] = r[(j * s.width + i) * 4 + 3]; + } + + vpc++; + } + } + } + } + } + } + + valid_positions.resize(vpc); + if (valid_normals.size()) { + valid_normals.resize(vpc); + } + + ERR_EXPLAIN(TTR("No pixels with transparency > 128 in image...")); + ERR_FAIL_COND(valid_positions.size() == 0); + + if (capture_colors) { + PoolColorArray pca; + pca.resize(vpc); + PoolColorArray::Write pcaw = pca.write(); + for (int i = 0; i < vpc; i += 1) { + Color color; + color.r = valid_colors[i * 4 + 0] / 255.0f; + color.g = valid_colors[i * 4 + 1] / 255.0f; + color.b = valid_colors[i * 4 + 2] / 255.0f; + color.a = valid_colors[i * 4 + 3] / 255.0f; + pcaw[i] = color; + } + particles->set_emission_colors(pca); + } + + if (valid_normals.size()) { + particles->set_emission_shape(CPUParticles2D::EMISSION_SHAPE_DIRECTED_POINTS); + PoolVector2Array norms; + norms.resize(valid_normals.size()); + PoolVector2Array::Write normsw = norms.write(); + for (int i = 0; i < valid_normals.size(); i += 1) { + normsw[i] = valid_normals[i]; + } + particles->set_emission_normals(norms); + } else { + particles->set_emission_shape(CPUParticles2D::EMISSION_SHAPE_POINTS); + } + + { + PoolVector2Array points; + points.resize(valid_positions.size()); + PoolVector2Array::Write pointsw = points.write(); + for (int i = 0; i < valid_positions.size(); i += 1) { + pointsw[i] = valid_positions[i]; + } + particles->set_emission_points(points); + } +} + +void CPUParticles2DEditorPlugin::_notification(int p_what) { + + if (p_what == NOTIFICATION_ENTER_TREE) { + + menu->get_popup()->connect("id_pressed", this, "_menu_callback"); + menu->set_icon(menu->get_popup()->get_icon("Particles2D", "EditorIcons")); + file->connect("file_selected", this, "_file_selected"); + } +} + +void CPUParticles2DEditorPlugin::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_menu_callback"), &CPUParticles2DEditorPlugin::_menu_callback); + ClassDB::bind_method(D_METHOD("_file_selected"), &CPUParticles2DEditorPlugin::_file_selected); + ClassDB::bind_method(D_METHOD("_generate_emission_mask"), &CPUParticles2DEditorPlugin::_generate_emission_mask); +} + +CPUParticles2DEditorPlugin::CPUParticles2DEditorPlugin(EditorNode *p_node) { + + particles = NULL; + editor = p_node; + undo_redo = editor->get_undo_redo(); + + toolbar = memnew(HBoxContainer); + add_control_to_container(CONTAINER_CANVAS_EDITOR_MENU, toolbar); + toolbar->hide(); + + toolbar->add_child(memnew(VSeparator)); + + menu = memnew(MenuButton); + menu->get_popup()->add_item(TTR("Load Emission Mask"), MENU_LOAD_EMISSION_MASK); + // menu->get_popup()->add_item(TTR("Clear Emission Mask"), MENU_CLEAR_EMISSION_MASK); + menu->set_text(TTR("Particles")); + menu->set_switch_on_hover(true); + toolbar->add_child(menu); + + file = memnew(EditorFileDialog); + List<String> ext; + ImageLoader::get_recognized_extensions(&ext); + for (List<String>::Element *E = ext.front(); E; E = E->next()) { + file->add_filter("*." + E->get() + "; " + E->get().to_upper()); + } + file->set_mode(EditorFileDialog::MODE_OPEN_FILE); + toolbar->add_child(file); + + epoints = memnew(SpinBox); + epoints->set_min(1); + epoints->set_max(8192); + epoints->set_step(1); + epoints->set_value(512); + file->get_vbox()->add_margin_child(TTR("Generated Point Count:"), epoints); + + emission_mask = memnew(ConfirmationDialog); + emission_mask->set_title(TTR("Load Emission Mask")); + VBoxContainer *emvb = memnew(VBoxContainer); + emission_mask->add_child(emvb); + emission_mask_mode = memnew(OptionButton); + emvb->add_margin_child(TTR("Emission Mask"), emission_mask_mode); + emission_mask_mode->add_item("Solid Pixels", EMISSION_MODE_SOLID); + emission_mask_mode->add_item("Border Pixels", EMISSION_MODE_BORDER); + emission_mask_mode->add_item("Directed Border Pixels", EMISSION_MODE_BORDER_DIRECTED); + emission_colors = memnew(CheckBox); + emission_colors->set_text(TTR("Capture from Pixel")); + emvb->add_margin_child(TTR("Emission Colors"), emission_colors); + + toolbar->add_child(emission_mask); + + emission_mask->connect("confirmed", this, "_generate_emission_mask"); +} + +CPUParticles2DEditorPlugin::~CPUParticles2DEditorPlugin() { +} diff --git a/editor/plugins/cpu_particles_2d_editor_plugin.h b/editor/plugins/cpu_particles_2d_editor_plugin.h new file mode 100644 index 0000000000..f715abd87b --- /dev/null +++ b/editor/plugins/cpu_particles_2d_editor_plugin.h @@ -0,0 +1,92 @@ +/*************************************************************************/ +/* cpu_particles_2d_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef CPU_PARTICLES_2D_EDITOR_PLUGIN_H +#define CPU_PARTICLES_2D_EDITOR_PLUGIN_H + +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" +#include "scene/2d/collision_polygon_2d.h" +#include "scene/2d/cpu_particles_2d.h" +#include "scene/gui/box_container.h" +#include "scene/gui/file_dialog.h" + +class CPUParticles2DEditorPlugin : public EditorPlugin { + + GDCLASS(CPUParticles2DEditorPlugin, EditorPlugin); + + enum { + MENU_LOAD_EMISSION_MASK, + MENU_CLEAR_EMISSION_MASK + }; + + enum EmissionMode { + EMISSION_MODE_SOLID, + EMISSION_MODE_BORDER, + EMISSION_MODE_BORDER_DIRECTED + }; + + CPUParticles2D *particles; + + EditorFileDialog *file; + EditorNode *editor; + + HBoxContainer *toolbar; + MenuButton *menu; + + SpinBox *epoints; + + ConfirmationDialog *emission_mask; + OptionButton *emission_mask_mode; + CheckBox *emission_colors; + + String source_emission_file; + + UndoRedo *undo_redo; + void _file_selected(const String &p_file); + void _menu_callback(int p_idx); + void _generate_emission_mask(); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + virtual String get_name() const { return "CPUParticles2D"; } + bool has_main_screen() const { return false; } + virtual void edit(Object *p_object); + virtual bool handles(Object *p_object) const; + virtual void make_visible(bool p_visible); + + CPUParticles2DEditorPlugin(EditorNode *p_node); + ~CPUParticles2DEditorPlugin(); +}; + +#endif // CPU_PARTICLES_2D_EDITOR_PLUGIN_H diff --git a/editor/plugins/cpu_particles_editor_plugin.cpp b/editor/plugins/cpu_particles_editor_plugin.cpp index 7222525dd7..70be9b95bb 100644 --- a/editor/plugins/cpu_particles_editor_plugin.cpp +++ b/editor/plugins/cpu_particles_editor_plugin.cpp @@ -101,6 +101,7 @@ CPUParticlesEditor::CPUParticlesEditor() { particles_editor_hb = memnew(HBoxContainer); SpatialEditor::get_singleton()->add_control_to_menu_panel(particles_editor_hb); options = memnew(MenuButton); + options->set_switch_on_hover(true); particles_editor_hb->add_child(options); particles_editor_hb->hide(); diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp index 3d76b5da21..55feb40c2c 100644 --- a/editor/plugins/curve_editor_plugin.cpp +++ b/editor/plugins/curve_editor_plugin.cpp @@ -53,11 +53,11 @@ CurveEditor::CurveEditor() { _presets_menu = memnew(PopupMenu); _presets_menu->set_name("_presets_menu"); - _presets_menu->add_item(TTR("Flat0"), PRESET_FLAT0); - _presets_menu->add_item(TTR("Flat1"), PRESET_FLAT1); + _presets_menu->add_item(TTR("Flat 0"), PRESET_FLAT0); + _presets_menu->add_item(TTR("Flat 1"), PRESET_FLAT1); _presets_menu->add_item(TTR("Linear"), PRESET_LINEAR); - _presets_menu->add_item(TTR("Ease in"), PRESET_EASE_IN); - _presets_menu->add_item(TTR("Ease out"), PRESET_EASE_OUT); + _presets_menu->add_item(TTR("Ease In"), PRESET_EASE_IN); + _presets_menu->add_item(TTR("Ease Out"), PRESET_EASE_OUT); _presets_menu->add_item(TTR("Smoothstep"), PRESET_SMOOTHSTEP); _presets_menu->connect("id_pressed", this, "_on_preset_item_selected"); _context_menu->add_child(_presets_menu); @@ -194,7 +194,7 @@ void CurveEditor::on_gui_input(const Ref<InputEvent> &p_event) { Vector2 dir = (control_pos - point_pos).normalized(); real_t tangent; - if (Math::abs(dir.x) > CMP_EPSILON) + if (!Math::is_zero_approx(dir.x)) tangent = dir.y / dir.x; else tangent = 9999 * (dir.y >= 0 ? 1 : -1); @@ -330,10 +330,10 @@ void CurveEditor::open_context_menu(Vector2 pos) { _context_menu->clear(); if (_curve_ref.is_valid()) { - _context_menu->add_item(TTR("Add point"), CONTEXT_ADD_POINT); + _context_menu->add_item(TTR("Add Point"), CONTEXT_ADD_POINT); if (_selected_point >= 0) { - _context_menu->add_item(TTR("Remove point"), CONTEXT_REMOVE_POINT); + _context_menu->add_item(TTR("Remove Point"), CONTEXT_REMOVE_POINT); if (_selected_tangent != TANGENT_NONE) { _context_menu->add_separator(); @@ -351,12 +351,12 @@ void CurveEditor::open_context_menu(Vector2 pos) { _context_menu->add_separator(); if (_selected_point > 0) { - _context_menu->add_check_item(TTR("Left linear"), CONTEXT_LEFT_LINEAR); + _context_menu->add_check_item(TTR("Left Linear"), CONTEXT_LEFT_LINEAR); _context_menu->set_item_checked(_context_menu->get_item_index(CONTEXT_LEFT_LINEAR), _curve_ref->get_point_left_mode(_selected_point) == Curve::TANGENT_LINEAR); } if (_selected_point + 1 < _curve_ref->get_point_count()) { - _context_menu->add_check_item(TTR("Right linear"), CONTEXT_RIGHT_LINEAR); + _context_menu->add_check_item(TTR("Right Linear"), CONTEXT_RIGHT_LINEAR); _context_menu->set_item_checked(_context_menu->get_item_index(CONTEXT_RIGHT_LINEAR), _curve_ref->get_point_right_mode(_selected_point) == Curve::TANGENT_LINEAR); } @@ -366,7 +366,7 @@ void CurveEditor::open_context_menu(Vector2 pos) { _context_menu->add_separator(); } - _context_menu->add_submenu_item(TTR("Load preset"), _presets_menu->get_name()); + _context_menu->add_submenu_item(TTR("Load Preset"), _presets_menu->get_name()); _context_menu->set_size(Size2(0, 0)); _context_menu->popup(); 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..635b934333 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; @@ -387,16 +412,16 @@ void MeshInstanceEditor::_bind_methods() { MeshInstanceEditor::MeshInstanceEditor() { options = memnew(MenuButton); + options->set_switch_on_hover(true); SpatialEditor::get_singleton()->add_control_to_menu_panel(options); options->set_text(TTR("Mesh")); 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/mesh_library_editor_plugin.cpp b/editor/plugins/mesh_library_editor_plugin.cpp index 89eb253afe..18586b2fe5 100644 --- a/editor/plugins/mesh_library_editor_plugin.cpp +++ b/editor/plugins/mesh_library_editor_plugin.cpp @@ -61,7 +61,8 @@ void MeshLibraryEditor::_menu_confirm() { _import_scene_cbk(existing); } break; - default: {}; + default: { + }; } } diff --git a/editor/plugins/multimesh_editor_plugin.cpp b/editor/plugins/multimesh_editor_plugin.cpp index 8ff6080443..fc0a425bfc 100644 --- a/editor/plugins/multimesh_editor_plugin.cpp +++ b/editor/plugins/multimesh_editor_plugin.cpp @@ -293,6 +293,7 @@ void MultiMeshEditor::_bind_methods() { MultiMeshEditor::MultiMeshEditor() { options = memnew(MenuButton); + options->set_switch_on_hover(true); SpatialEditor::get_singleton()->add_control_to_menu_panel(options); options->set_text("MultiMesh"); diff --git a/editor/plugins/particles_2d_editor_plugin.cpp b/editor/plugins/particles_2d_editor_plugin.cpp index d00687f7ea..50bdf4512b 100644 --- a/editor/plugins/particles_2d_editor_plugin.cpp +++ b/editor/plugins/particles_2d_editor_plugin.cpp @@ -375,6 +375,7 @@ Particles2DEditorPlugin::Particles2DEditorPlugin(EditorNode *p_node) { menu->get_popup()->add_separator(); menu->get_popup()->add_item(TTR("Convert to CPUParticles"), MENU_OPTION_CONVERT_TO_CPU_PARTICLES); menu->set_text(TTR("Particles")); + menu->set_switch_on_hover(true); toolbar->add_child(menu); file = memnew(EditorFileDialog); @@ -408,7 +409,7 @@ Particles2DEditorPlugin::Particles2DEditorPlugin(EditorNode *p_node) { generate_visibility_rect->connect("confirmed", this, "_generate_visibility_rect"); emission_mask = memnew(ConfirmationDialog); - emission_mask->set_title(TTR("Generate Visibility Rect")); + emission_mask->set_title(TTR("Load Emission Mask")); VBoxContainer *emvb = memnew(VBoxContainer); emission_mask->add_child(emvb); emission_mask_mode = memnew(OptionButton); diff --git a/editor/plugins/particles_editor_plugin.cpp b/editor/plugins/particles_editor_plugin.cpp index 0032850535..09180edf2a 100644 --- a/editor/plugins/particles_editor_plugin.cpp +++ b/editor/plugins/particles_editor_plugin.cpp @@ -454,6 +454,7 @@ ParticlesEditor::ParticlesEditor() { particles_editor_hb = memnew(HBoxContainer); SpatialEditor::get_singleton()->add_control_to_menu_panel(particles_editor_hb); options = memnew(MenuButton); + options->set_switch_on_hover(true); particles_editor_hb->add_child(options); particles_editor_hb->hide(); diff --git a/editor/plugins/physical_bone_plugin.cpp b/editor/plugins/physical_bone_plugin.cpp index 9ea37aceb9..96681a105f 100644 --- a/editor/plugins/physical_bone_plugin.cpp +++ b/editor/plugins/physical_bone_plugin.cpp @@ -61,7 +61,7 @@ PhysicalBoneEditor::PhysicalBoneEditor(EditorNode *p_editor) : button_transform_joint = memnew(ToolButton); spatial_editor_hb->add_child(button_transform_joint); - button_transform_joint->set_text(TTR("Move joint")); + button_transform_joint->set_text(TTR("Move Joint")); button_transform_joint->set_icon(SpatialEditor::get_singleton()->get_icon("PhysicalBone", "EditorIcons")); button_transform_joint->set_toggle_mode(true); button_transform_joint->connect("toggled", this, "_on_toggle_button_transform_joint"); diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index 0dbbaf4177..712b1a0ae4 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -905,7 +905,8 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { bone_paint_pos = Vector2(mm->get_position().x, mm->get_position().y); } break; - default: {} + default: { + } } if (bone_painting) { @@ -1059,8 +1060,7 @@ void Polygon2DEditor::_uv_draw() { polygon_fill_color.push_back(pf); } Color prev_color = Color(0.5, 0.5, 0.5); - Rect2 rect(Point2(), mtx.basis_xform(base_tex->get_size())); - rect.expand_to(mtx.basis_xform(uv_edit_draw->get_size())); + Rect2 rect; int uv_draw_max = uvs.size(); @@ -1203,7 +1203,8 @@ void Polygon2DEditor::_uv_draw() { uv_edit_draw->draw_circle(bone_paint_pos, bone_paint_radius->get_value() * EDSCALE, Color(1, 1, 1, 0.1)); } - rect = rect.grow(200); + rect.position -= uv_edit_draw->get_size(); + rect.size += uv_edit_draw->get_size() * 2.0; updating_uv_scroll = true; uv_hscroll->set_min(rect.position.x); uv_hscroll->set_max(rect.position.x + rect.size.x); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 9e6459e41f..0982da784f 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -312,6 +312,38 @@ void ScriptEditor::_goto_script_line(REF p_script, int p_line) { } } +void ScriptEditor::_set_execution(REF p_script, int p_line) { + Ref<Script> script = Object::cast_to<Script>(*p_script); + if (script.is_valid() && (script->has_source_code() || script->get_path().is_resource_file())) { + for (int i = 0; i < tab_container->get_child_count(); i++) { + + ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i)); + if (!se) + continue; + + if ((script != NULL && se->get_edited_resource() == p_script) || se->get_edited_resource()->get_path() == script->get_path()) { + se->set_executing_line(p_line); + } + } + } +} + +void ScriptEditor::_clear_execution(REF p_script) { + Ref<Script> script = Object::cast_to<Script>(*p_script); + if (script.is_valid() && (script->has_source_code() || script->get_path().is_resource_file())) { + for (int i = 0; i < tab_container->get_child_count(); i++) { + + ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i)); + if (!se) + continue; + + if ((script != NULL && se->get_edited_resource() == p_script) || se->get_edited_resource()->get_path() == script->get_path()) { + se->clear_executing_line(); + } + } + } +} + ScriptEditorBase *ScriptEditor::_get_current_editor() const { int selected = tab_container->get_current_tab(); @@ -849,8 +881,7 @@ void ScriptEditor::_file_dialog_action(String p_file) { } file->close(); memdelete(file); - - // fallthrough to open the file. + FALLTHROUGH; } case FILE_OPEN: { @@ -979,6 +1010,10 @@ void ScriptEditor::_menu_option(int p_option) { OS::get_singleton()->shell_open("https://docs.godotengine.org/"); } break; + case REQUEST_DOCS: { + + OS::get_singleton()->shell_open("https://github.com/godotengine/godot-docs/issues/new"); + } break; case WINDOW_NEXT: { @@ -1308,6 +1343,7 @@ void ScriptEditor::_notification(int p_what) { EditorSettings::get_singleton()->connect("settings_changed", this, "_editor_settings_changed"); help_search->set_icon(get_icon("HelpSearch", "EditorIcons")); site_search->set_icon(get_icon("Instance", "EditorIcons")); + request_docs->set_icon(get_icon("Issue", "EditorIcons")); script_forward->set_icon(get_icon("Forward", "EditorIcons")); script_back->set_icon(get_icon("Back", "EditorIcons")); @@ -1731,7 +1767,7 @@ void ScriptEditor::_update_script_names() { String name = eh->get_class(); Ref<Texture> icon = get_icon("Help", "EditorIcons"); - String tooltip = name + TTR(" Class Reference"); + String tooltip = vformat(TTR("%s Class Reference"), name); _ScriptEditorItemData sd; sd.icon = icon; @@ -1797,6 +1833,16 @@ void ScriptEditor::_update_script_names() { _update_script_colors(); } +void ScriptEditor::_update_script_connections() { + for (int i = 0; i < tab_container->get_child_count(); i++) { + ScriptTextEditor *ste = Object::cast_to<ScriptTextEditor>(tab_container->get_child(i)); + if (!ste) { + continue; + } + ste->_update_connected_methods(); + } +} + Ref<TextFile> ScriptEditor::_load_text_file(const String &p_path, Error *r_error) { if (r_error) { *r_error = ERR_FILE_CANT_OPEN; @@ -2199,6 +2245,7 @@ void ScriptEditor::_tree_changed() { waiting_update_names = true; call_deferred("_update_script_names"); + call_deferred("_update_script_connections"); } void ScriptEditor::_script_split_dragged(float) { @@ -2478,22 +2525,39 @@ 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; + } + if (!edit(scr)) { + continue; + } + } else { + Error error; + Ref<TextFile> text_file = _load_text_file(path, &error); + if (error != OK || !text_file.is_valid()) { + continue; + } + if (!edit(text_file)) { continue; } } - 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(tab_container->get_tab_count() - 1)); + if (se) { + se->set_edit_state(script_info["state"]); + } } } @@ -2533,7 +2597,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)); @@ -2842,6 +2910,8 @@ void ScriptEditor::_bind_methods() { ClassDB::bind_method("_res_saved_callback", &ScriptEditor::_res_saved_callback); ClassDB::bind_method("_goto_script_line", &ScriptEditor::_goto_script_line); ClassDB::bind_method("_goto_script_line2", &ScriptEditor::_goto_script_line2); + ClassDB::bind_method("_set_execution", &ScriptEditor::_set_execution); + ClassDB::bind_method("_clear_execution", &ScriptEditor::_clear_execution); ClassDB::bind_method("_help_search", &ScriptEditor::_help_search); ClassDB::bind_method("_save_history", &ScriptEditor::_save_history); ClassDB::bind_method("_copy_script_path", &ScriptEditor::_copy_script_path); @@ -2852,6 +2922,7 @@ void ScriptEditor::_bind_methods() { ClassDB::bind_method("_autosave_scripts", &ScriptEditor::_autosave_scripts); ClassDB::bind_method("_editor_settings_changed", &ScriptEditor::_editor_settings_changed); ClassDB::bind_method("_update_script_names", &ScriptEditor::_update_script_names); + ClassDB::bind_method("_update_script_connections", &ScriptEditor::_update_script_connections); ClassDB::bind_method("_tree_changed", &ScriptEditor::_tree_changed); ClassDB::bind_method("_members_overview_selected", &ScriptEditor::_members_overview_selected); ClassDB::bind_method("_help_overview_selected", &ScriptEditor::_help_overview_selected); @@ -2879,6 +2950,7 @@ void ScriptEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("can_drop_data_fw", "point", "data", "from"), &ScriptEditor::can_drop_data_fw); ClassDB::bind_method(D_METHOD("drop_data_fw", "point", "data", "from"), &ScriptEditor::drop_data_fw); + ClassDB::bind_method(D_METHOD("goto_line", "line_number"), &ScriptEditor::_goto_script_line2); ClassDB::bind_method(D_METHOD("get_current_script"), &ScriptEditor::_get_current_script); ClassDB::bind_method(D_METHOD("get_open_scripts"), &ScriptEditor::_get_open_scripts); ClassDB::bind_method(D_METHOD("open_script_create_dialog", "base_name", "base_path"), &ScriptEditor::open_script_create_dialog); @@ -3079,7 +3151,13 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { site_search->set_text(TTR("Online Docs")); site_search->connect("pressed", this, "_menu_option", varray(SEARCH_WEBSITE)); menu_hb->add_child(site_search); - site_search->set_tooltip(TTR("Open Godot online documentation")); + site_search->set_tooltip(TTR("Open Godot online documentation.")); + + request_docs = memnew(ToolButton); + request_docs->set_text(TTR("Request Docs")); + request_docs->connect("pressed", this, "_menu_option", varray(REQUEST_DOCS)); + menu_hb->add_child(request_docs); + request_docs->set_tooltip(TTR("Help improve the Godot documentation by giving feedback.")); help_search = memnew(ToolButton); help_search->set_text(TTR("Search Help")); @@ -3125,6 +3203,8 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { debugger = memnew(ScriptEditorDebugger(editor)); debugger->connect("goto_script_line", this, "_goto_script_line"); + debugger->connect("set_execution", this, "_set_execution"); + debugger->connect("clear_execution", this, "_clear_execution"); debugger->connect("show_debugger", this, "_show_debugger"); disk_changed = memnew(ConfirmationDialog); diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index 11f4589f00..a17fed1e06 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -96,6 +96,8 @@ public: virtual Variant get_edit_state() = 0; virtual void set_edit_state(const Variant &p_state) = 0; virtual void goto_line(int p_line, bool p_with_error = false) = 0; + virtual void set_executing_line(int p_line) = 0; + virtual void clear_executing_line() = 0; virtual void trim_trailing_whitespace() = 0; virtual void convert_indent_to_spaces() = 0; virtual void convert_indent_to_tabs() = 0; @@ -157,6 +159,7 @@ class ScriptEditor : public PanelContainer { DEBUG_WITH_EXTERNAL_EDITOR, SEARCH_HELP, SEARCH_WEBSITE, + REQUEST_DOCS, HELP_SEARCH_FIND, HELP_SEARCH_FIND_NEXT, WINDOW_MOVE_UP, @@ -200,6 +203,7 @@ class ScriptEditor : public PanelContainer { Button *help_search; Button *site_search; + Button *request_docs; EditorHelpSearch *help_search_dialog; ItemList *script_list; @@ -316,6 +320,8 @@ class ScriptEditor : public PanelContainer { void _goto_script_line2(int p_line); void _goto_script_line(REF p_script, int p_line); + void _set_execution(REF p_script, int p_line); + void _clear_execution(REF p_script); void _breaked(bool p_breaked, bool p_can_debug); void _show_debugger(bool p_show); void _update_window_menu(); @@ -331,6 +337,7 @@ class ScriptEditor : public PanelContainer { void _update_members_overview(); void _toggle_members_overview_alpha_sort(bool p_alphabetic_sort); void _update_script_names(); + void _update_script_connections(); bool _sort_list_on_update; void _members_overview_selected(int p_idx); diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 8c3f8fd3e8..d40e67cc8c 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -35,6 +35,76 @@ #include "editor/editor_settings.h" #include "editor/script_editor_debugger.h" +void ConnectionInfoDialog::ok_pressed() { +} + +void ConnectionInfoDialog::popup_connections(String p_method, Vector<Node *> p_nodes) { + method->set_text(p_method); + + tree->clear(); + TreeItem *root = tree->create_item(); + + for (int i = 0; i < p_nodes.size(); i++) { + List<Connection> all_connections; + p_nodes[i]->get_signals_connected_to_this(&all_connections); + + for (List<Connection>::Element *E = all_connections.front(); E; E = E->next()) { + Connection connection = E->get(); + + if (connection.method != p_method) { + continue; + } + + TreeItem *node_item = tree->create_item(root); + + node_item->set_text(0, Object::cast_to<Node>(connection.source)->get_name()); + node_item->set_icon(0, EditorNode::get_singleton()->get_object_icon(connection.source, "Node")); + node_item->set_selectable(0, false); + node_item->set_editable(0, false); + + node_item->set_text(1, connection.signal); + node_item->set_icon(1, get_parent_control()->get_icon("Slot", "EditorIcons")); + node_item->set_selectable(1, false); + node_item->set_editable(1, false); + + node_item->set_text(2, Object::cast_to<Node>(connection.target)->get_name()); + node_item->set_icon(2, EditorNode::get_singleton()->get_object_icon(connection.target, "Node")); + node_item->set_selectable(2, false); + node_item->set_editable(2, false); + } + } + + popup_centered(Size2(400, 300) * EDSCALE); +} + +ConnectionInfoDialog::ConnectionInfoDialog() { + set_title(TTR("Connections to method:")); + + VBoxContainer *vbc = memnew(VBoxContainer); + vbc->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_BEGIN, 8 * EDSCALE); + vbc->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 8 * EDSCALE); + vbc->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, -8 * EDSCALE); + vbc->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, -8 * EDSCALE); + add_child(vbc); + + method = memnew(Label); + method->set_align(Label::ALIGN_CENTER); + vbc->add_child(method); + + tree = memnew(Tree); + tree->set_columns(3); + tree->set_hide_root(true); + tree->set_column_titles_visible(true); + tree->set_column_title(0, TTR("Source")); + tree->set_column_title(1, TTR("Signal")); + tree->set_column_title(2, TTR("Target")); + vbc->add_child(tree); + tree->set_v_size_flags(SIZE_EXPAND_FILL); + tree->set_allow_rmb_select(true); +} + +//////////////////////////////////////////////////////////////////////////////// + Vector<String> ScriptTextEditor::get_functions() { String errortxt; @@ -81,6 +151,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() { @@ -142,6 +214,7 @@ void ScriptTextEditor::_load_theme_settings() { Color member_variable_color = EDITOR_GET("text_editor/highlighting/member_variable_color"); Color mark_color = EDITOR_GET("text_editor/highlighting/mark_color"); Color breakpoint_color = EDITOR_GET("text_editor/highlighting/breakpoint_color"); + Color executing_line_color = EDITOR_GET("text_editor/highlighting/executing_line_color"); Color code_folding_color = EDITOR_GET("text_editor/highlighting/code_folding_color"); Color search_result_color = EDITOR_GET("text_editor/highlighting/search_result_color"); Color search_result_border_color = EDITOR_GET("text_editor/highlighting/search_result_border_color"); @@ -173,6 +246,7 @@ void ScriptTextEditor::_load_theme_settings() { text_edit->add_color_override("function_color", function_color); text_edit->add_color_override("member_variable_color", member_variable_color); text_edit->add_color_override("breakpoint_color", breakpoint_color); + text_edit->add_color_override("executing_line_color", executing_line_color); text_edit->add_color_override("mark_color", mark_color); text_edit->add_color_override("code_folding_color", code_folding_color); text_edit->add_color_override("search_result_color", search_result_color); @@ -286,7 +360,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 +392,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 +436,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) { @@ -400,6 +481,14 @@ void ScriptTextEditor::goto_line_selection(int p_line, int p_begin, int p_end) { code_editor->goto_line_selection(p_line, p_begin, p_end); } +void ScriptTextEditor::set_executing_line(int p_line) { + code_editor->set_executing_line(p_line); +} + +void ScriptTextEditor::clear_executing_line() { + code_editor->clear_executing_line(); +} + void ScriptTextEditor::ensure_focus() { code_editor->get_text_edit()->grab_focus(); @@ -461,9 +550,32 @@ void ScriptTextEditor::_validate_script() { functions.push_back(E->get()); } } + _update_connected_methods(); - code_editor->set_warning_nb(warnings.size()); + code_editor->set_warning_nb(missing_connections.size() + warnings.size()); warnings_panel->clear(); + + // add missing connections + Node *base = get_tree()->get_edited_scene_root(); + if (base && missing_connections.size() > 0) { + warnings_panel->push_table(1); + for (List<Connection>::Element *E = missing_connections.front(); E; E = E->next()) { + Connection connection = E->get(); + + String base_path = base->get_name(); + String source_path = base == connection.source ? base_path : base_path + "/" + String(base->get_path_to(Object::cast_to<Node>(connection.source))); + String target_path = base == connection.target ? base_path : base_path + "/" + String(base->get_path_to(Object::cast_to<Node>(connection.target))); + + warnings_panel->push_cell(); + warnings_panel->push_color(warnings_panel->get_color("warning_color", "Editor")); + warnings_panel->add_text(vformat(TTR("Missing connected method '%s' for signal '%s' from node '%s' to node '%s'"), connection.method, connection.signal, source_path, target_path)); + warnings_panel->pop(); // Color + warnings_panel->pop(); // Cell + } + warnings_panel->pop(); // Table + } + + // add script warnings warnings_panel->push_table(3); for (List<ScriptLanguage::Warning>::Element *E = warnings.front(); E; E = E->next()) { ScriptLanguage::Warning w = E->get(); @@ -517,6 +629,27 @@ void ScriptTextEditor::_validate_script() { emit_signal("edited_script_changed"); } +static Vector<Node *> _find_all_node_for_script(Node *p_base, Node *p_current, const Ref<Script> &p_script) { + + Vector<Node *> nodes; + + if (p_current->get_owner() != p_base && p_base != p_current) { + return nodes; + } + + Ref<Script> c = p_current->get_script(); + if (c == p_script) { + nodes.push_back(p_current); + } + + for (int i = 0; i < p_current->get_child_count(); i++) { + Vector<Node *> found = _find_all_node_for_script(p_base, p_current->get_child(i), p_script); + nodes.append_array(found); + } + + return nodes; +} + static Node *_find_node_for_script(Node *p_base, Node *p_current, const Ref<Script> &p_script) { if (p_current->get_owner() != p_base && p_base != p_current) @@ -712,6 +845,47 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c } } +void ScriptTextEditor::_update_connected_methods() { + TextEdit *text_edit = code_editor->get_text_edit(); + text_edit->clear_info_icons(); + missing_connections.clear(); + + Node *base = get_tree()->get_edited_scene_root(); + if (!base) { + return; + } + + Vector<Node *> nodes = _find_all_node_for_script(base, base, script); + for (int i = 0; i < nodes.size(); i++) { + List<Connection> connections; + nodes[i]->get_signals_connected_to_this(&connections); + + for (List<Connection>::Element *E = connections.front(); E; E = E->next()) { + Connection connection = E->get(); + if (!(connection.flags & CONNECT_PERSIST)) { + continue; + } + + int line = script->get_language()->find_function(connection.method, text_edit->get_text()); + if (line < 0) { + missing_connections.push_back(connection); + continue; + } + text_edit->set_line_info_icon(line - 1, get_parent_control()->get_icon("Slot", "EditorIcons"), connection.method); + } + } +} + +void ScriptTextEditor::_lookup_connections(int p_row, String p_method) { + Node *base = get_tree()->get_edited_scene_root(); + if (!base) { + return; + } + + Vector<Node *> nodes = _find_all_node_for_script(base, base, script); + connection_info_dialog->popup_connections(p_method, nodes); +} + void ScriptTextEditor::_edit_option(int p_op) { TextEdit *tx = code_editor->get_text_edit(); @@ -910,7 +1084,7 @@ void ScriptTextEditor::_edit_option(int p_op) { tx->set_line_as_breakpoint(line, dobreak); ScriptEditor::get_singleton()->get_debugger()->set_breakpoint(script->get_path(), line + 1, dobreak); } - } + } break; case DEBUG_GOTO_NEXT_BREAKPOINT: { List<int> bpoints; @@ -1024,7 +1198,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() { @@ -1032,6 +1205,8 @@ void ScriptTextEditor::_bind_methods() { ClassDB::bind_method("_validate_script", &ScriptTextEditor::_validate_script); ClassDB::bind_method("_load_theme_settings", &ScriptTextEditor::_load_theme_settings); ClassDB::bind_method("_breakpoint_toggled", &ScriptTextEditor::_breakpoint_toggled); + ClassDB::bind_method("_lookup_connections", &ScriptTextEditor::_lookup_connections); + ClassDB::bind_method("_update_connected_methods", &ScriptTextEditor::_update_connected_methods); ClassDB::bind_method("_change_syntax_highlighter", &ScriptTextEditor::_change_syntax_highlighter); ClassDB::bind_method("_edit_option", &ScriptTextEditor::_edit_option); ClassDB::bind_method("_goto_line", &ScriptTextEditor::_goto_line); @@ -1364,6 +1539,7 @@ ScriptTextEditor::ScriptTextEditor() { code_editor->set_code_complete_func(_code_complete_scripts, this); code_editor->get_text_edit()->connect("breakpoint_toggled", this, "_breakpoint_toggled"); code_editor->get_text_edit()->connect("symbol_lookup", this, "_lookup_symbol"); + code_editor->get_text_edit()->connect("info_clicked", this, "_lookup_connections"); code_editor->set_v_size_flags(SIZE_EXPAND_FILL); warnings_panel = memnew(RichTextLabel); @@ -1484,6 +1660,9 @@ ScriptTextEditor::ScriptTextEditor() { goto_line_dialog = memnew(GotoLineDialog); add_child(goto_line_dialog); + connection_info_dialog = memnew(ConnectionInfoDialog); + add_child(connection_info_dialog); + code_editor->get_text_edit()->set_drag_forwarding(this); } diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h index b081a31c18..0dbc884594 100644 --- a/editor/plugins/script_text_editor.h +++ b/editor/plugins/script_text_editor.h @@ -32,8 +32,25 @@ #define SCRIPT_TEXT_EDITOR_H #include "scene/gui/color_picker.h" +#include "scene/gui/dialogs.h" +#include "scene/gui/tree.h" #include "script_editor_plugin.h" +class ConnectionInfoDialog : public AcceptDialog { + + GDCLASS(ConnectionInfoDialog, AcceptDialog); + + Label *method; + Tree *tree; + + virtual void ok_pressed(); + +public: + void popup_connections(String p_method, Vector<Node *> p_nodes); + + ConnectionInfoDialog(); +}; + class ScriptTextEditor : public ScriptEditorBase { GDCLASS(ScriptTextEditor, ScriptEditorBase); @@ -45,6 +62,8 @@ class ScriptTextEditor : public ScriptEditorBase { Vector<String> functions; + List<Connection> missing_connections; + Vector<String> member_keywords; HBoxContainer *edit_hb; @@ -56,6 +75,7 @@ class ScriptTextEditor : public ScriptEditorBase { GotoLineDialog *goto_line_dialog; ScriptEditorQuickOpen *quick_open; + ConnectionInfoDialog *connection_info_dialog; PopupPanel *color_panel; ColorPicker *color_picker; @@ -144,6 +164,8 @@ protected: void _goto_line(int p_line) { goto_line(p_line); } void _lookup_symbol(const String &p_symbol, int p_row, int p_column); + void _lookup_connections(int p_row, String p_method); + void _convert_case(CodeTextEditor::CaseStyle p_case); Variant get_drag_data_fw(const Point2 &p_point, Control *p_from); @@ -151,6 +173,8 @@ protected: void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); public: + void _update_connected_methods(); + virtual void add_syntax_highlighter(SyntaxHighlighter *p_highlighter); virtual void set_syntax_highlighter(SyntaxHighlighter *p_highlighter); @@ -172,6 +196,8 @@ public: virtual void goto_line(int p_line, bool p_with_error = false); void goto_line_selection(int p_line, int p_begin, int p_end); + virtual void set_executing_line(int p_line); + virtual void clear_executing_line(); virtual void reload(bool p_soft); virtual void get_breakpoints(List<int> *p_breakpoints); diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index d39e521113..31660a9e19 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -85,6 +85,7 @@ void ShaderTextEditor::_load_theme_settings() { Color member_variable_color = EDITOR_GET("text_editor/highlighting/member_variable_color"); Color mark_color = EDITOR_GET("text_editor/highlighting/mark_color"); Color breakpoint_color = EDITOR_GET("text_editor/highlighting/breakpoint_color"); + Color executing_line_color = EDITOR_GET("text_editor/highlighting/executing_line_color"); Color code_folding_color = EDITOR_GET("text_editor/highlighting/code_folding_color"); Color search_result_color = EDITOR_GET("text_editor/highlighting/search_result_color"); Color search_result_border_color = EDITOR_GET("text_editor/highlighting/search_result_border_color"); @@ -113,6 +114,7 @@ void ShaderTextEditor::_load_theme_settings() { get_text_edit()->add_color_override("member_variable_color", member_variable_color); get_text_edit()->add_color_override("mark_color", mark_color); get_text_edit()->add_color_override("breakpoint_color", breakpoint_color); + get_text_edit()->add_color_override("executing_line_color", executing_line_color); get_text_edit()->add_color_override("code_folding_color", code_folding_color); get_text_edit()->add_color_override("search_result_color", search_result_color); get_text_edit()->add_color_override("search_result_border_color", search_result_border_color); 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 765d198f79..5a733f6509 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(); @@ -1637,7 +1660,8 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { surface->update(); } break; - default: {} + default: { + } } } @@ -1704,7 +1728,8 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { } break; - default: {} + default: { + } } } @@ -1760,7 +1785,8 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { } break; - default: {} + default: { + } } } @@ -1824,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.")); @@ -2382,7 +2408,10 @@ void SpatialEditorViewport::_draw() { if (cursor.region_select) { - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor.region_begin, cursor.region_end - cursor.region_begin), Color(0.7, 0.7, 1.0, 0.3)); + VisualServer::get_singleton()->canvas_item_add_rect( + ci, + Rect2(cursor.region_begin, cursor.region_end - cursor.region_begin), + get_color("accent_color", "Editor") * Color(1, 1, 1, 0.375)); } if (message_time > 0) { @@ -2396,7 +2425,13 @@ void SpatialEditorViewport::_draw() { if (_edit.mode == TRANSFORM_ROTATE) { Point2 center = _point_to_screen(_edit.center); - VisualServer::get_singleton()->canvas_item_add_line(ci, _edit.mouse_pos, center, Color(0.4, 0.7, 1.0, 0.8)); + VisualServer::get_singleton()->canvas_item_add_line( + ci, + _edit.mouse_pos, + center, + get_color("accent_color", "Editor") * Color(1, 1, 1, 0.6), + Math::round(2 * EDSCALE), + true); } if (previewing) { @@ -2438,7 +2473,7 @@ void SpatialEditorViewport::_draw() { real_t max_speed = camera->get_zfar(); real_t scale_length = (max_speed - min_speed); - if (Math::abs(scale_length) > CMP_EPSILON) { + if (!Math::is_zero_approx(scale_length)) { real_t logscale_t = 1.0 - Math::log(1 + freelook_speed - min_speed) / Math::log(1 + scale_length); // There is no real maximum speed so that factor can become negative, @@ -2456,7 +2491,7 @@ void SpatialEditorViewport::_draw() { real_t max_distance = camera->get_zfar(); real_t scale_length = (max_distance - min_distance); - if (Math::abs(scale_length) > CMP_EPSILON) { + if (!Math::is_zero_approx(scale_length)) { real_t logscale_t = 1.0 - Math::log(1 + cursor.distance - min_distance) / Math::log(1 + scale_length); // There is no real maximum distance so that factor can become negative, @@ -3001,7 +3036,7 @@ void SpatialEditorViewport::set_state(const Dictionary &p_state) { previewing = Object::cast_to<Camera>(pv); previewing->connect("tree_exiting", this, "_preview_exited_scene"); VS::get_singleton()->viewport_attach_camera(viewport->get_viewport_rid(), previewing->get_camera()); //replace - view_menu->hide(); + view_menu->set_disabled(true); surface->update(); preview_camera->set_pressed(true); preview_camera->show(); @@ -4480,6 +4515,7 @@ void SpatialEditor::_menu_item_pressed(int p_option) { snap_selected_nodes_to_floor(); } break; case MENU_LOCK_SELECTED: { + undo_redo->create_action(TTR("Lock Selected")); List<Node *> &selection = editor_selection->get_selected_node_list(); @@ -4492,14 +4528,66 @@ void SpatialEditor::_menu_item_pressed(int p_option) { if (spatial->get_viewport() != EditorNode::get_singleton()->get_scene_root()) continue; - spatial->set_meta("_edit_lock_", true); - emit_signal("item_lock_status_changed"); + undo_redo->add_do_method(spatial, "set_meta", "_edit_lock_", true); + undo_redo->add_undo_method(spatial, "remove_meta", "_edit_lock_"); + undo_redo->add_do_method(this, "emit_signal", "item_lock_status_changed"); + undo_redo->add_undo_method(this, "emit_signal", "item_lock_status_changed"); } - _refresh_menu_icons(); + undo_redo->add_do_method(this, "_refresh_menu_icons", Variant()); + undo_redo->add_undo_method(this, "_refresh_menu_icons", Variant()); + undo_redo->commit_action(); } break; case MENU_UNLOCK_SELECTED: { + undo_redo->create_action(TTR("Unlock 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; + + undo_redo->add_do_method(spatial, "remove_meta", "_edit_lock_"); + undo_redo->add_undo_method(spatial, "set_meta", "_edit_lock_", true); + undo_redo->add_do_method(this, "emit_signal", "item_lock_status_changed"); + undo_redo->add_undo_method(this, "emit_signal", "item_lock_status_changed"); + } + + undo_redo->add_do_method(this, "_refresh_menu_icons", Variant()); + undo_redo->add_undo_method(this, "_refresh_menu_icons", Variant()); + undo_redo->commit_action(); + } break; + case MENU_GROUP_SELECTED: { + undo_redo->create_action(TTR("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; + + undo_redo->add_do_method(spatial, "set_meta", "_edit_group_", true); + undo_redo->add_undo_method(spatial, "remove_meta", "_edit_group_"); + undo_redo->add_do_method(this, "emit_signal", "item_group_status_changed"); + undo_redo->add_undo_method(this, "emit_signal", "item_group_status_changed"); + } + + undo_redo->add_do_method(this, "_refresh_menu_icons", Variant()); + undo_redo->add_undo_method(this, "_refresh_menu_icons", Variant()); + undo_redo->commit_action(); + } break; + case MENU_UNGROUP_SELECTED: { + undo_redo->create_action(TTR("Ungroup Selected")); List<Node *> &selection = editor_selection->get_selected_node_list(); for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { @@ -4511,11 +4599,15 @@ void SpatialEditor::_menu_item_pressed(int p_option) { if (spatial->get_viewport() != EditorNode::get_singleton()->get_scene_root()) continue; - spatial->set_meta("_edit_lock_", Variant()); - emit_signal("item_lock_status_changed"); + undo_redo->add_do_method(spatial, "remove_meta", "_edit_group_"); + undo_redo->add_undo_method(spatial, "set_meta", "_edit_group_", true); + undo_redo->add_do_method(this, "emit_signal", "item_group_status_changed"); + undo_redo->add_undo_method(this, "emit_signal", "item_group_status_changed"); } - _refresh_menu_icons(); + undo_redo->add_do_method(this, "_refresh_menu_icons", Variant()); + undo_redo->add_undo_method(this, "_refresh_menu_icons", Variant()); + undo_redo->commit_action(); } break; } } @@ -4959,11 +5051,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_")) { @@ -4971,11 +5065,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> @@ -5145,6 +5249,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")); @@ -5339,6 +5445,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() { @@ -5452,6 +5559,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); @@ -5586,11 +5705,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 */ @@ -5748,6 +5867,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); @@ -5802,7 +5954,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 3854d27567..7642bfaf04 100644 --- a/editor/plugins/sprite_editor_plugin.cpp +++ b/editor/plugins/sprite_editor_plugin.cpp @@ -31,7 +31,10 @@ #include "sprite_editor_plugin.h" #include "canvas_item_editor_plugin.h" +#include "scene/2d/collision_polygon_2d.h" +#include "scene/2d/light_occluder_2d.h" #include "scene/2d/mesh_instance_2d.h" +#include "scene/2d/polygon_2d.h" #include "scene/gui/box_container.h" #include "thirdparty/misc/clipper.hpp" @@ -116,8 +119,42 @@ void SpriteEditor::_menu_option(int p_option) { return; } + selected_menu_item = (Menu)p_option; + switch (p_option) { - case MENU_OPTION_CREATE_MESH_2D: { + case MENU_OPTION_CONVERT_TO_MESH_2D: { + + debug_uv_dialog->get_ok()->set_text(TTR("Create Mesh2D")); + debug_uv_dialog->set_title("Mesh2D Preview"); + + _update_mesh_data(); + debug_uv_dialog->popup_centered(); + debug_uv->update(); + + } break; + case MENU_OPTION_CONVERT_TO_POLYGON_2D: { + + debug_uv_dialog->get_ok()->set_text(TTR("Create Polygon2D")); + debug_uv_dialog->set_title("Polygon2D Preview"); + + _update_mesh_data(); + debug_uv_dialog->popup_centered(); + debug_uv->update(); + } break; + case MENU_OPTION_CREATE_COLLISION_POLY_2D: { + + debug_uv_dialog->get_ok()->set_text(TTR("Create CollisionPolygon2D")); + debug_uv_dialog->set_title("CollisionPolygon2D Preview"); + + _update_mesh_data(); + debug_uv_dialog->popup_centered(); + debug_uv->update(); + + } break; + case MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D: { + + debug_uv_dialog->get_ok()->set_text(TTR("Create LightOccluder2D")); + debug_uv_dialog->set_title("LightOccluder2D Preview"); _update_mesh_data(); debug_uv_dialog->popup_centered(); @@ -169,47 +206,107 @@ void SpriteEditor::_update_mesh_data() { computed_indices.clear(); Size2 img_size = Vector2(image->get_width(), image->get_height()); - for (int j = 0; j < lines.size(); j++) { - lines.write[j] = expand(lines[j], rect, epsilon); + for (int i = 0; i < lines.size(); i++) { + lines.write[i] = expand(lines[i], rect, epsilon); + } + + if (selected_menu_item == MENU_OPTION_CONVERT_TO_MESH_2D) { + + for (int j = 0; j < lines.size(); j++) { + int index_ofs = computed_vertices.size(); + + for (int i = 0; i < lines[j].size(); i++) { + Vector2 vtx = lines[j][i]; + computed_uv.push_back(vtx / img_size); - int index_ofs = computed_vertices.size(); + vtx -= rect.position; //offset by rect position - for (int i = 0; i < lines[j].size(); i++) { - Vector2 vtx = lines[j][i]; - computed_uv.push_back(vtx / img_size); + //flip if flipped + if (node->is_flipped_h()) + vtx.x = rect.size.x - vtx.x - 1.0; + if (node->is_flipped_v()) + vtx.y = rect.size.y - vtx.y - 1.0; - vtx -= rect.position; //offset by rect position + if (node->is_centered()) + vtx -= rect.size / 2.0; - //flip if flipped - if (node->is_flipped_h()) - vtx.x = rect.size.x - vtx.x - 1.0; - if (node->is_flipped_v()) - vtx.y = rect.size.y - vtx.y - 1.0; + computed_vertices.push_back(vtx); + } + + Vector<int> poly = Geometry::triangulate_polygon(lines[j]); - if (node->is_centered()) - vtx -= rect.size / 2.0; + for (int i = 0; i < poly.size(); i += 3) { + for (int k = 0; k < 3; k++) { + int idx = i + k; + int idxn = i + (k + 1) % 3; + uv_lines.push_back(lines[j][poly[idx]]); + uv_lines.push_back(lines[j][poly[idxn]]); - computed_vertices.push_back(vtx); + computed_indices.push_back(poly[idx] + index_ofs); + } + } } + } - Vector<int> poly = Geometry::triangulate_polygon(lines[j]); + outline_lines.clear(); + computed_outline_lines.clear(); - for (int i = 0; i < poly.size(); i += 3) { - for (int k = 0; k < 3; k++) { - int idx = i + k; - int idxn = i + (k + 1) % 3; - uv_lines.push_back(lines[j][poly[idx]]); - uv_lines.push_back(lines[j][poly[idxn]]); + if (selected_menu_item == MENU_OPTION_CONVERT_TO_POLYGON_2D || selected_menu_item == MENU_OPTION_CREATE_COLLISION_POLY_2D || selected_menu_item == MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D) { + outline_lines.resize(lines.size()); + computed_outline_lines.resize(lines.size()); + for (int pi = 0; pi < lines.size(); pi++) { + + Vector<Vector2> ol; + Vector<Vector2> col; + + ol.resize(lines[pi].size()); + col.resize(lines[pi].size()); + + for (int i = 0; i < lines[pi].size(); i++) { + Vector2 vtx = lines[pi][i]; + + ol.write[i] = vtx; + + vtx -= rect.position; //offset by rect position + + //flip if flipped + if (node->is_flipped_h()) + vtx.x = rect.size.x - vtx.x - 1.0; + if (node->is_flipped_v()) + vtx.y = rect.size.y - vtx.y - 1.0; + + if (node->is_centered()) + vtx -= rect.size / 2.0; - computed_indices.push_back(poly[idx] + index_ofs); + col.write[i] = vtx; } + + outline_lines.write[pi] = ol; + computed_outline_lines.write[pi] = col; } } debug_uv->update(); } -void SpriteEditor::_create_mesh_node() { +void SpriteEditor::_create_node() { + switch (selected_menu_item) { + case MENU_OPTION_CONVERT_TO_MESH_2D: { + _convert_to_mesh_2d_node(); + } break; + case MENU_OPTION_CONVERT_TO_POLYGON_2D: { + _convert_to_polygon_2d_node(); + } break; + case MENU_OPTION_CREATE_COLLISION_POLY_2D: { + _create_collision_polygon_2d_node(); + } break; + case MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D: { + _create_light_occluder_2d_node(); + } break; + } +} + +void SpriteEditor::_convert_to_mesh_2d_node() { if (computed_vertices.size() < 3) { err_dialog->set_text(TTR("Invalid geometry, can't replace by mesh.")); @@ -233,6 +330,117 @@ void SpriteEditor::_create_mesh_node() { EditorNode::get_singleton()->get_scene_tree_dock()->replace_node(node, mesh_instance); } +void SpriteEditor::_convert_to_polygon_2d_node() { + Polygon2D *polygon_2d_instance = memnew(Polygon2D); + + int total_point_count = 0; + for (int i = 0; i < computed_outline_lines.size(); i++) + total_point_count += computed_outline_lines[i].size(); + + PoolVector2Array polygon; + polygon.resize(total_point_count); + PoolVector2Array::Write polygon_write = polygon.write(); + + PoolVector2Array uvs; + uvs.resize(total_point_count); + PoolVector2Array::Write uvs_write = uvs.write(); + + int current_point_index = 0; + + Array polys; + polys.resize(computed_outline_lines.size()); + + for (int i = 0; i < computed_outline_lines.size(); i++) { + + Vector<Vector2> outline = computed_outline_lines[i]; + Vector<Vector2> uv_outline = outline_lines[i]; + + if (outline.size() < 3) { + err_dialog->set_text(TTR("Invalid geometry, can't create polygon.")); + err_dialog->popup_centered_minsize(); + return; + } + + PoolIntArray pia; + pia.resize(outline.size()); + PoolIntArray::Write pia_write = pia.write(); + + for (int pi = 0; pi < outline.size(); pi++) { + polygon_write[current_point_index] = outline[pi]; + uvs_write[current_point_index] = uv_outline[pi]; + pia_write[pi] = current_point_index; + current_point_index++; + } + + polys[i] = pia; + } + + polygon_2d_instance->set_uv(uvs); + polygon_2d_instance->set_polygon(polygon); + polygon_2d_instance->set_polygons(polys); + + EditorNode::get_singleton()->get_scene_tree_dock()->replace_node(node, polygon_2d_instance); +} + +void SpriteEditor::_create_collision_polygon_2d_node() { + for (int i = 0; i < computed_outline_lines.size(); i++) { + + Vector<Vector2> outline = computed_outline_lines[i]; + + if (outline.size() < 3) { + err_dialog->set_text(TTR("Invalid geometry, can't create collision polygon.")); + err_dialog->popup_centered_minsize(); + continue; + } + + CollisionPolygon2D *collision_polygon_2d_instance = memnew(CollisionPolygon2D); + collision_polygon_2d_instance->set_polygon(outline); + + _add_as_sibling_or_child(node, collision_polygon_2d_instance); + } +} + +void SpriteEditor::_create_light_occluder_2d_node() { + for (int i = 0; i < computed_outline_lines.size(); i++) { + + Vector<Vector2> outline = computed_outline_lines[i]; + + if (outline.size() < 3) { + err_dialog->set_text(TTR("Invalid geometry, can't create light occluder.")); + err_dialog->popup_centered_minsize(); + continue; + } + + Ref<OccluderPolygon2D> polygon; + polygon.instance(); + + PoolVector2Array a; + a.resize(outline.size()); + PoolVector2Array::Write aw = a.write(); + for (int io = 0; io < outline.size(); io++) { + aw[io] = outline[io]; + } + polygon->set_polygon(a); + + LightOccluder2D *light_occluder_2d_instance = memnew(LightOccluder2D); + light_occluder_2d_instance->set_occluder_polygon(polygon); + + _add_as_sibling_or_child(node, light_occluder_2d_instance); + } +} + +void SpriteEditor::_add_as_sibling_or_child(Node2D *p_own_node, Node2D *p_new_node) { + // Can't make sibling if own node is scene root + if (p_own_node != this->get_tree()->get_edited_scene_root()) { + p_own_node->get_parent()->add_child(p_new_node, true); + p_new_node->set_transform(p_own_node->get_transform()); + } else { + p_own_node->add_child(p_new_node, true); + } + + p_new_node->set_owner(this->get_tree()->get_edited_scene_root()); +} + #if 0 void SpriteEditor::_create_uv_lines() { @@ -298,16 +506,26 @@ void SpriteEditor::_create_uv_lines() { #endif void SpriteEditor::_debug_uv_draw() { - if (uv_lines.size() == 0) - return; - Ref<Texture> tex = node->get_texture(); ERR_FAIL_COND(!tex.is_valid()); debug_uv->set_clip_contents(true); debug_uv->draw_texture(tex, Point2()); debug_uv->set_custom_minimum_size(tex->get_size()); //debug_uv->draw_set_transform(Vector2(), 0, debug_uv->get_size()); - debug_uv->draw_multiline(uv_lines, Color(1.0, 0.8, 0.7)); + + Color color = Color(1.0, 0.8, 0.7); + + if (selected_menu_item == MENU_OPTION_CONVERT_TO_MESH_2D && uv_lines.size() > 0) { + debug_uv->draw_multiline(uv_lines, color); + + } else if ((selected_menu_item == MENU_OPTION_CONVERT_TO_POLYGON_2D || selected_menu_item == MENU_OPTION_CREATE_COLLISION_POLY_2D || selected_menu_item == MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D) && outline_lines.size() > 0) { + for (int i = 0; i < outline_lines.size(); i++) { + Vector<Vector2> outline = outline_lines[i]; + + debug_uv->draw_polyline(outline, color); + debug_uv->draw_line(outline[0], outline[outline.size() - 1], color); + } + } } void SpriteEditor::_bind_methods() { @@ -315,7 +533,7 @@ void SpriteEditor::_bind_methods() { ClassDB::bind_method("_menu_option", &SpriteEditor::_menu_option); ClassDB::bind_method("_debug_uv_draw", &SpriteEditor::_debug_uv_draw); ClassDB::bind_method("_update_mesh_data", &SpriteEditor::_update_mesh_data); - ClassDB::bind_method("_create_mesh_node", &SpriteEditor::_create_mesh_node); + ClassDB::bind_method("_create_node", &SpriteEditor::_create_node); } SpriteEditor::SpriteEditor() { @@ -327,7 +545,11 @@ SpriteEditor::SpriteEditor() { options->set_text(TTR("Sprite")); options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Sprite", "EditorIcons")); - options->get_popup()->add_item(TTR("Convert to 2D Mesh"), MENU_OPTION_CREATE_MESH_2D); + options->get_popup()->add_item(TTR("Convert to Mesh2D"), MENU_OPTION_CONVERT_TO_MESH_2D); + 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"); @@ -335,7 +557,7 @@ SpriteEditor::SpriteEditor() { add_child(err_dialog); debug_uv_dialog = memnew(ConfirmationDialog); - debug_uv_dialog->get_ok()->set_text(TTR("Create 2D Mesh")); + debug_uv_dialog->get_ok()->set_text(TTR("Create Mesh2D")); debug_uv_dialog->set_title("Mesh 2D Preview"); VBoxContainer *vb = memnew(VBoxContainer); debug_uv_dialog->add_child(vb); @@ -347,7 +569,7 @@ SpriteEditor::SpriteEditor() { debug_uv = memnew(Control); debug_uv->connect("draw", this, "_debug_uv_draw"); scroll->add_child(debug_uv); - debug_uv_dialog->connect("confirmed", this, "_create_mesh_node"); + debug_uv_dialog->connect("confirmed", this, "_create_node"); HBoxContainer *hb = memnew(HBoxContainer); hb->add_child(memnew(Label(TTR("Simplification: ")))); diff --git a/editor/plugins/sprite_editor_plugin.h b/editor/plugins/sprite_editor_plugin.h index 7250e3df59..460f5a5707 100644 --- a/editor/plugins/sprite_editor_plugin.h +++ b/editor/plugins/sprite_editor_plugin.h @@ -41,9 +41,14 @@ class SpriteEditor : public Control { GDCLASS(SpriteEditor, Control); enum Menu { - MENU_OPTION_CREATE_MESH_2D, + MENU_OPTION_CONVERT_TO_MESH_2D, + MENU_OPTION_CONVERT_TO_POLYGON_2D, + MENU_OPTION_CREATE_COLLISION_POLY_2D, + MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D }; + Menu selected_menu_item; + Sprite *node; MenuButton *options; @@ -55,7 +60,8 @@ class SpriteEditor : public Control { ConfirmationDialog *debug_uv_dialog; Control *debug_uv; Vector<Vector2> uv_lines; - + Vector<Vector<Vector2> > outline_lines; + Vector<Vector<Vector2> > computed_outline_lines; Vector<Vector2> computed_vertices; Vector<Vector2> computed_uv; Vector<int> computed_indices; @@ -71,7 +77,14 @@ class SpriteEditor : public Control { void _debug_uv_draw(); void _update_mesh_data(); - void _create_mesh_node(); + + void _create_node(); + void _convert_to_mesh_2d_node(); + void _convert_to_polygon_2d_node(); + void _create_collision_polygon_2d_node(); + void _create_light_occluder_2d_node(); + + void _add_as_sibling_or_child(Node2D *p_own_node, Node2D *p_new_node); protected: void _node_removed(Node *p_node); diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index 89bb7440fe..6edd19901b 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -38,23 +38,200 @@ void SpriteFramesEditor::_gui_input(Ref<InputEvent> p_event) { } -void SpriteFramesEditor::_notification(int p_what) { +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); - if (p_what == NOTIFICATION_ENTER_TREE) { + 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; - load->set_icon(get_icon("Load", "EditorIcons")); - copy->set_icon(get_icon("ActionCopy", "EditorIcons")); - paste->set_icon(get_icon("ActionPaste", "EditorIcons")); - empty->set_icon(get_icon("InsertBefore", "EditorIcons")); - empty2->set_icon(get_icon("InsertAfter", "EditorIcons")); - move_up->set_icon(get_icon("MoveLeft", "EditorIcons")); - move_down->set_icon(get_icon("MoveRight", "EditorIcons")); - _delete->set_icon(get_icon("Remove", "EditorIcons")); - new_anim->set_icon(get_icon("New", "EditorIcons")); - remove_anim->set_icon(get_icon("Remove", "EditorIcons")); - } else if (p_what == NOTIFICATION_READY) { + Ref<AtlasTexture> at; + at.instance(); + at->set_atlas(split_sheet_preview->get_texture()); + at->set_region(Rect2(x, y, width, height)); - add_constant_override("autohide", 1); // Fixes the dragger always showing up. + 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_select_clear_all_frames() { + + bool should_clear = true; + for (int i = 0; i < split_sheet_h->get_value() * split_sheet_v->get_value(); i++) { + if (!frames_selected.has(i)) { + frames_selected.insert(i); + should_clear = false; + } + } + if (should_clear) { + frames_selected.clear(); + } + + split_sheet_preview->update(); +} + +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) { + + switch (p_what) { + case 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")); + empty2->set_icon(get_icon("InsertAfter", "EditorIcons")); + move_up->set_icon(get_icon("MoveLeft", "EditorIcons")); + move_down->set_icon(get_icon("MoveRight", "EditorIcons")); + _delete->set_icon(get_icon("Remove", "EditorIcons")); + new_anim->set_icon(get_icon("New", "EditorIcons")); + remove_anim->set_icon(get_icon("Remove", "EditorIcons")); + } // Fallthrough. + case NOTIFICATION_THEME_CHANGED: { + splite_sheet_scroll->add_style_override("bg", get_stylebox("bg", "Tree")); + } break; + case NOTIFICATION_READY: { + add_constant_override("autohide", 1); // Fixes the dragger always showing up. + } break; } } @@ -72,6 +249,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(); @@ -347,9 +525,6 @@ void SpriteFramesEditor::_animation_name_edited() { for (List<Node *>::Element *E = nodes.front(); E; E = E->next()) { String current = E->get()->call("get_animation"); - if (current != edited_anim) - continue; - undo_redo->add_do_method(E->get(), "set_animation", name); undo_redo->add_undo_method(E->get(), "set_animation", edited_anim); } @@ -382,9 +557,6 @@ void SpriteFramesEditor::_animation_add() { for (List<Node *>::Element *E = nodes.front(); E; E = E->next()) { String current = E->get()->call("get_animation"); - if (frames->has_animation(current)) - continue; - undo_redo->add_do_method(E->get(), "set_animation", name); undo_redo->add_undo_method(E->get(), "set_animation", current); } @@ -661,6 +833,13 @@ 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); + ClassDB::bind_method(D_METHOD("_sheet_select_clear_all_frames"), &SpriteFramesEditor::_sheet_select_clear_all_frames); } SpriteFramesEditor::SpriteFramesEditor() { @@ -718,9 +897,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); @@ -729,6 +914,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); @@ -737,7 +924,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)")); @@ -772,6 +959,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"); @@ -786,6 +974,66 @@ 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"); + + HBoxContainer *split_sheet_hb = memnew(HBoxContainer); + + 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_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_v->connect("value_changed", this, "_sheet_spin_changed"); + + split_sheet_hb->add_spacer(); + + Button *select_clear_all = memnew(Button); + select_clear_all->set_text(TTR("Select/Clear All Frames")); + select_clear_all->connect("pressed", this, "_sheet_select_clear_all_frames"); + split_sheet_hb->add_child(select_clear_all); + + split_sheet_vb->add_child(split_sheet_hb); + + 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"); + + splite_sheet_scroll = memnew(ScrollContainer); + splite_sheet_scroll->set_enable_h_scroll(true); + splite_sheet_scroll->set_enable_v_scroll(true); + splite_sheet_scroll->set_v_size_flags(SIZE_EXPAND_FILL); + 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); + splite_sheet_scroll->add_child(cc); + + split_sheet_vb->add_child(splite_sheet_scroll); + + 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..d64431cde7 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,15 @@ class SpriteFramesEditor : public HSplitContainer { StringName edited_anim; + ConfirmationDialog *split_sheet_dialog; + ScrollContainer *splite_sheet_scroll; + 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 +110,14 @@ 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(); + void _sheet_select_clear_all_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..becaae3567 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() { @@ -95,6 +94,7 @@ void TextEditor::_load_theme_settings() { Color member_variable_color = EDITOR_GET("text_editor/highlighting/member_variable_color"); Color mark_color = EDITOR_GET("text_editor/highlighting/mark_color"); Color breakpoint_color = EDITOR_GET("text_editor/highlighting/breakpoint_color"); + Color executing_line_color = EDITOR_GET("text_editor/highlighting/executing_line_color"); Color code_folding_color = EDITOR_GET("text_editor/highlighting/code_folding_color"); Color search_result_color = EDITOR_GET("text_editor/highlighting/search_result_color"); Color search_result_border_color = EDITOR_GET("text_editor/highlighting/search_result_border_color"); @@ -125,6 +125,7 @@ void TextEditor::_load_theme_settings() { text_edit->add_color_override("function_color", function_color); text_edit->add_color_override("member_variable_color", member_variable_color); text_edit->add_color_override("breakpoint_color", breakpoint_color); + text_edit->add_color_override("executing_line_color", executing_line_color); text_edit->add_color_override("mark_color", mark_color); text_edit->add_color_override("code_folding_color", code_folding_color); text_edit->add_color_override("search_result_color", search_result_color); @@ -234,6 +235,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() { @@ -261,6 +270,15 @@ void TextEditor::goto_line(int p_line, bool p_with_error) { code_editor->goto_line(p_line); } +void TextEditor::set_executing_line(int p_line) { + + code_editor->set_executing_line(p_line); +} + +void TextEditor::clear_executing_line() { + code_editor->clear_executing_line(); +} + void TextEditor::ensure_focus() { code_editor->get_text_edit()->grab_focus(); @@ -299,7 +317,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/text_editor.h b/editor/plugins/text_editor.h index 3c136277df..767001e2f6 100644 --- a/editor/plugins/text_editor.h +++ b/editor/plugins/text_editor.h @@ -123,6 +123,8 @@ public: virtual Vector<String> get_functions(); virtual void get_breakpoints(List<int> *p_breakpoints); virtual void goto_line(int p_line, bool p_with_error = false); + virtual void set_executing_line(int p_line); + virtual void clear_executing_line(); virtual void trim_trailing_whitespace(); virtual void convert_indent_to_spaces(); virtual void convert_indent_to_tabs(); diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index 3eeb871380..4e15bd5116 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -64,7 +64,7 @@ void TextureRegionEditor::_region_draw() { return; Transform2D mtx; - mtx.elements[2] = -draw_ofs; + mtx.elements[2] = -draw_ofs * draw_zoom; mtx.scale_basis(Vector2(draw_zoom, draw_zoom)); VS::get_singleton()->canvas_item_add_set_transform(edit_draw->get_canvas_item(), mtx); @@ -128,15 +128,14 @@ void TextureRegionEditor::_region_draw() { }; for (int i = 0; i < 4; i++) { int next = (i + 1) % 4; - edit_draw->draw_line(endpoints[i] - draw_ofs, endpoints[next] - draw_ofs, Color(0.3, 0.7, 1, 1), 2); + edit_draw->draw_line(endpoints[i] - draw_ofs * draw_zoom, endpoints[next] - draw_ofs * draw_zoom, Color(0.3, 0.7, 1, 1), 2); } } } Ref<Texture> select_handle = get_icon("EditorHandle", "EditorIcons"); - Rect2 scroll_rect(Point2(), mtx.basis_xform(base_tex->get_size())); - scroll_rect.expand_to(mtx.basis_xform(edit_draw->get_size())); + Rect2 scroll_rect; Vector2 endpoints[4] = { mtx.basis_xform(rect.position), @@ -153,21 +152,23 @@ void TextureRegionEditor::_region_draw() { Vector2 ofs = ((endpoints[i] - endpoints[prev]).normalized() + ((endpoints[i] - endpoints[next]).normalized())).normalized(); ofs *= 1.4144 * (select_handle->get_size().width / 2); - edit_draw->draw_line(endpoints[i] - draw_ofs, endpoints[next] - draw_ofs, color, 2); + edit_draw->draw_line(endpoints[i] - draw_ofs * draw_zoom, endpoints[next] - draw_ofs * draw_zoom, color, 2); if (snap_mode != SNAP_AUTOSLICE) - edit_draw->draw_texture(select_handle, (endpoints[i] + ofs - (select_handle->get_size() / 2)).floor() - draw_ofs); + edit_draw->draw_texture(select_handle, (endpoints[i] + ofs - (select_handle->get_size() / 2)).floor() - draw_ofs * draw_zoom); ofs = (endpoints[next] - endpoints[i]) / 2; ofs += (endpoints[next] - endpoints[i]).tangent().normalized() * (select_handle->get_size().width / 2); if (snap_mode != SNAP_AUTOSLICE) - edit_draw->draw_texture(select_handle, (endpoints[i] + ofs - (select_handle->get_size() / 2)).floor() - draw_ofs); + edit_draw->draw_texture(select_handle, (endpoints[i] + ofs - (select_handle->get_size() / 2)).floor() - draw_ofs * draw_zoom); scroll_rect.expand_to(endpoints[i]); } - scroll_rect = scroll_rect.grow(200); + scroll_rect.position -= edit_draw->get_size(); + scroll_rect.size += edit_draw->get_size() * 2.0; + updating_scroll = true; hscroll->set_min(scroll_rect.position.x); hscroll->set_max(scroll_rect.position.x + scroll_rect.size.x); @@ -205,10 +206,10 @@ void TextureRegionEditor::_region_draw() { margins[3] = obj_styleBox->get_margin_size(MARGIN_RIGHT); } Vector2 pos[4] = { - mtx.basis_xform(Vector2(0, margins[0])) + Vector2(0, endpoints[0].y - draw_ofs.y), - -mtx.basis_xform(Vector2(0, margins[1])) + Vector2(0, endpoints[2].y - draw_ofs.y), - mtx.basis_xform(Vector2(margins[2], 0)) + Vector2(endpoints[0].x - draw_ofs.x, 0), - -mtx.basis_xform(Vector2(margins[3], 0)) + Vector2(endpoints[2].x - draw_ofs.x, 0) + mtx.basis_xform(Vector2(0, margins[0])) + Vector2(0, endpoints[0].y - draw_ofs.y * draw_zoom), + -mtx.basis_xform(Vector2(0, margins[1])) + Vector2(0, endpoints[2].y - draw_ofs.y * draw_zoom), + mtx.basis_xform(Vector2(margins[2], 0)) + Vector2(endpoints[0].x - draw_ofs.x * draw_zoom, 0), + -mtx.basis_xform(Vector2(margins[3], 0)) + Vector2(endpoints[2].x - draw_ofs.x * draw_zoom, 0) }; draw_margin_line(edit_draw, pos[0], pos[0] + Vector2(edit_draw->get_size().x, 0)); @@ -220,7 +221,7 @@ void TextureRegionEditor::_region_draw() { void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { Transform2D mtx; - mtx.elements[2] = -draw_ofs; + mtx.elements[2] = -draw_ofs * draw_zoom; mtx.scale_basis(Vector2(draw_zoom, draw_zoom)); Vector2 endpoints[8] = { @@ -255,10 +256,10 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { margins[3] = obj_styleBox->get_margin_size(MARGIN_RIGHT); } Vector2 pos[4] = { - mtx.basis_xform(rect.position + Vector2(0, margins[0])) - draw_ofs, - mtx.basis_xform(rect.position + rect.size - Vector2(0, margins[1])) - draw_ofs, - mtx.basis_xform(rect.position + Vector2(margins[2], 0)) - draw_ofs, - mtx.basis_xform(rect.position + rect.size - Vector2(margins[3], 0)) - draw_ofs + mtx.basis_xform(rect.position + Vector2(0, margins[0])) - draw_ofs * draw_zoom, + mtx.basis_xform(rect.position + rect.size - Vector2(0, margins[1])) - draw_ofs * draw_zoom, + mtx.basis_xform(rect.position + Vector2(margins[2], 0)) - draw_ofs * draw_zoom, + mtx.basis_xform(rect.position + rect.size - Vector2(margins[3], 0)) - draw_ofs * draw_zoom }; if (Math::abs(mb->get_position().y - pos[0].y) < 8) { edited_margin = 0; @@ -415,9 +416,9 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { } } } else if (mb->get_button_index() == BUTTON_WHEEL_UP && mb->is_pressed()) { - _zoom_in(); + _zoom_on_position(draw_zoom * ((0.95 + (0.05 * mb->get_factor())) / 0.95), mb->get_position()); } else if (mb->get_button_index() == BUTTON_WHEEL_DOWN && mb->is_pressed()) { - _zoom_out(); + _zoom_on_position(draw_zoom * (1 - (0.05 * mb->get_factor())), mb->get_position()); } } @@ -427,7 +428,7 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { if (mm->get_button_mask() & BUTTON_MASK_MIDDLE || Input::get_singleton()->is_key_pressed(KEY_SPACE)) { - Vector2 draged(mm->get_relative().x, mm->get_relative().y); + Vector2 draged(mm->get_relative().x / draw_zoom, mm->get_relative().y / draw_zoom); hscroll->set_value(hscroll->get_value() - draged.x); vscroll->set_value(vscroll->get_value() - draged.y); @@ -578,25 +579,30 @@ void TextureRegionEditor::_set_snap_sep_y(float p_val) { edit_draw->update(); } +void TextureRegionEditor::_zoom_on_position(float p_zoom, Point2 p_position) { + if (p_zoom < 0.25 || p_zoom > 8) + return; + + float prev_zoom = draw_zoom; + draw_zoom = p_zoom; + Point2 ofs = p_position; + ofs = ofs / prev_zoom - ofs / draw_zoom; + draw_ofs.x = Math::round(draw_ofs.x + ofs.x); + draw_ofs.y = Math::round(draw_ofs.y + ofs.y); + + edit_draw->update(); +} + void TextureRegionEditor::_zoom_in() { - if (draw_zoom < 8) { - draw_zoom *= 2; - edit_draw->update(); - } + _zoom_on_position(draw_zoom * 1.5, edit_draw->get_size() / 2.0); } void TextureRegionEditor::_zoom_reset() { - if (draw_zoom == 1) - return; - draw_zoom = 1; - edit_draw->update(); + _zoom_on_position(1.0, edit_draw->get_size() / 2.0); } void TextureRegionEditor::_zoom_out() { - if (draw_zoom > 0.25) { - draw_zoom /= 2; - edit_draw->update(); - } + _zoom_on_position(draw_zoom / 1.5, edit_draw->get_size() / 2.0); } void TextureRegionEditor::apply_rect(const Rect2 &p_rect) { @@ -743,6 +749,7 @@ void TextureRegionEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_snap_step_y"), &TextureRegionEditor::_set_snap_step_y); ClassDB::bind_method(D_METHOD("_set_snap_sep_x"), &TextureRegionEditor::_set_snap_sep_x); ClassDB::bind_method(D_METHOD("_set_snap_sep_y"), &TextureRegionEditor::_set_snap_sep_y); + ClassDB::bind_method(D_METHOD("_zoom_on_position"), &TextureRegionEditor::_zoom_on_position); ClassDB::bind_method(D_METHOD("_zoom_in"), &TextureRegionEditor::_zoom_in); ClassDB::bind_method(D_METHOD("_zoom_reset"), &TextureRegionEditor::_zoom_reset); ClassDB::bind_method(D_METHOD("_zoom_out"), &TextureRegionEditor::_zoom_out); diff --git a/editor/plugins/texture_region_editor_plugin.h b/editor/plugins/texture_region_editor_plugin.h index 19eaef9bc3..a49e0fb96c 100644 --- a/editor/plugins/texture_region_editor_plugin.h +++ b/editor/plugins/texture_region_editor_plugin.h @@ -110,6 +110,7 @@ class TextureRegionEditor : public VBoxContainer { void _set_snap_step_y(float p_val); void _set_snap_sep_x(float p_val); void _set_snap_sep_y(float p_val); + void _zoom_on_position(float p_zoom, Point2 p_position = Point2()); void _zoom_in(); void _zoom_reset(); void _zoom_out(); diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index e9b9c03c1e..80e2e99685 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -36,7 +36,7 @@ void ThemeEditor::edit(const Ref<Theme> &p_theme) { theme = p_theme; - main_vb->set_theme(p_theme); + main_container->set_theme(p_theme); } void ThemeEditor::_propagate_redraw(Control *p_at) { @@ -53,7 +53,7 @@ void ThemeEditor::_propagate_redraw(Control *p_at) { void ThemeEditor::_refresh_interval() { - _propagate_redraw(main_vb); + _propagate_redraw(main_container); } void ThemeEditor::_type_menu_cbk(int p_option) { @@ -86,7 +86,7 @@ void ThemeEditor::_name_menu_about_to_show() { } name_menu->get_popup()->clear(); - + name_menu->get_popup()->set_size(Size2()); for (List<StringName>::Element *E = names.front(); E; E = E->next()) { name_menu->get_popup()->add_item(E->get()); @@ -574,7 +574,6 @@ void ThemeEditor::_theme_menu_cbk(int p_option) { } } - //types.sort(); types.sort_custom<StringName::AlphCompare>(); for (List<StringName>::Element *E = types.front(); E; E = E->next()) { @@ -610,30 +609,14 @@ ThemeEditor::ThemeEditor() { time_left = 0; - scroll = memnew(ScrollContainer); - add_child(scroll); - scroll->set_anchors_and_margins_preset(Control::PRESET_WIDE, Control::PRESET_MODE_MINSIZE, 3); - scroll->set_margin(MARGIN_TOP, 30 * EDSCALE); - //scroll->set_enable_h_scroll(true); - scroll->set_enable_v_scroll(true); - scroll->set_enable_h_scroll(false); - - Panel *panel = memnew(Panel); - scroll->add_child(panel); - panel->set_custom_minimum_size(Size2(500, 800) * EDSCALE); - panel->set_theme(Theme::get_default()); - panel->set_h_size_flags(SIZE_EXPAND_FILL); + HBoxContainer *top_menu = memnew(HBoxContainer); + add_child(top_menu); - main_vb = memnew(VBoxContainer); - panel->add_child(main_vb); - main_vb->set_anchors_and_margins_preset(Control::PRESET_WIDE, Control::PRESET_MODE_MINSIZE, 4 * EDSCALE); - - HBoxContainer *hb_menu = memnew(HBoxContainer); - main_vb->add_child(hb_menu); + top_menu->add_child(memnew(Label(TTR("Preview:")))); + top_menu->add_spacer(false); theme_menu = memnew(MenuButton); - theme_menu->set_text(TTR("Edit theme...")); - theme_menu->set_flat(false); + theme_menu->set_text(TTR("Edit Theme")); theme_menu->set_tooltip(TTR("Theme editing menu.")); theme_menu->get_popup()->add_item(TTR("Add Item"), POPUP_ADD); theme_menu->get_popup()->add_item(TTR("Add Class Items"), POPUP_CLASS_ADD); @@ -643,51 +626,73 @@ ThemeEditor::ThemeEditor() { theme_menu->get_popup()->add_item(TTR("Create Empty Template"), POPUP_CREATE_EMPTY); theme_menu->get_popup()->add_item(TTR("Create Empty Editor Template"), POPUP_CREATE_EDITOR_EMPTY); theme_menu->get_popup()->add_item(TTR("Create From Current Editor Theme"), POPUP_IMPORT_EDITOR_THEME); - add_child(theme_menu); - theme_menu->set_position(Vector2(3, 3) * EDSCALE); + top_menu->add_child(theme_menu); theme_menu->get_popup()->connect("id_pressed", this, "_theme_menu_cbk"); + scroll = memnew(ScrollContainer); + add_child(scroll); + scroll->set_enable_v_scroll(true); + scroll->set_enable_h_scroll(false); + scroll->set_v_size_flags(SIZE_EXPAND_FILL); + + main_container = memnew(MarginContainer); + scroll->add_child(main_container); + main_container->set_clip_contents(true); + main_container->set_custom_minimum_size(Size2(700, 0) * EDSCALE); + main_container->set_v_size_flags(SIZE_EXPAND_FILL); + main_container->set_h_size_flags(SIZE_EXPAND_FILL); + + //// Preview Controls //// + + Panel *panel = memnew(Panel); + main_container->add_child(panel); + panel->set_theme(Theme::get_default()); + + MarginContainer *mc = memnew(MarginContainer); + main_container->add_child(mc); + mc->set_theme(Theme::get_default()); + mc->add_constant_override("margin_right", 4 * EDSCALE); + mc->add_constant_override("margin_top", 4 * EDSCALE); + mc->add_constant_override("margin_left", 4 * EDSCALE); + mc->add_constant_override("margin_bottom", 4 * EDSCALE); + HBoxContainer *main_hb = memnew(HBoxContainer); - main_vb->add_child(main_hb); + mc->add_child(main_hb); VBoxContainer *first_vb = memnew(VBoxContainer); - first_vb->set_h_size_flags(SIZE_EXPAND_FILL); main_hb->add_child(first_vb); - - //main_panel->add_child(panel); - //panel->set_anchors_and_margins_preset(Control::PRESET_WIDE); - //panel->set_margin( MARGIN_TOP,20 ); + first_vb->set_h_size_flags(SIZE_EXPAND_FILL); + first_vb->add_constant_override("separation", 10 * EDSCALE); first_vb->add_child(memnew(Label("Label"))); first_vb->add_child(memnew(Button("Button"))); + Button *bt = memnew(Button); + bt->set_text(TTR("Toggle Button")); + bt->set_toggle_mode(true); + bt->set_pressed(true); + first_vb->add_child(bt); + bt = memnew(Button); + bt->set_text(TTR("Disabled Button")); + bt->set_disabled(true); + first_vb->add_child(bt); ToolButton *tb = memnew(ToolButton); tb->set_text("ToolButton"); first_vb->add_child(tb); + CheckButton *cb = memnew(CheckButton); cb->set_text("CheckButton"); first_vb->add_child(cb); + cb = memnew(CheckButton); CheckBox *cbx = memnew(CheckBox); cbx->set_text("CheckBox"); first_vb->add_child(cbx); - VBoxContainer *bg = memnew(VBoxContainer); - bg->set_v_size_flags(SIZE_EXPAND_FILL); - VBoxContainer *gbvb = memnew(VBoxContainer); - gbvb->set_v_size_flags(SIZE_EXPAND_FILL); - CheckBox *rbx1 = memnew(CheckBox); - rbx1->set_text(TTR("CheckBox Radio1")); - rbx1->set_pressed(true); - gbvb->add_child(rbx1); - CheckBox *rbx2 = memnew(CheckBox); - rbx2->set_text(TTR("CheckBox Radio2")); - gbvb->add_child(rbx2); - bg->add_child(gbvb); - first_vb->add_child(bg); - MenuButton *test_menu_button = memnew(MenuButton); test_menu_button->set_text("MenuButton"); test_menu_button->get_popup()->add_item(TTR("Item")); + test_menu_button->get_popup()->add_item(TTR("Disabled Item")); + test_menu_button->get_popup()->set_item_disabled(1, true); test_menu_button->get_popup()->add_separator(); test_menu_button->get_popup()->add_check_item(TTR("Check Item")); test_menu_button->get_popup()->add_check_item(TTR("Checked Item")); @@ -696,6 +701,14 @@ ThemeEditor::ThemeEditor() { test_menu_button->get_popup()->add_radio_check_item(TTR("Radio Item")); test_menu_button->get_popup()->add_radio_check_item(TTR("Checked Radio Item")); test_menu_button->get_popup()->set_item_checked(6, true); + test_menu_button->get_popup()->add_separator(TTR("Named Sep.")); + + PopupMenu *test_submenu = memnew(PopupMenu); + test_menu_button->get_popup()->add_child(test_submenu); + test_submenu->set_name("submenu"); + test_menu_button->get_popup()->add_submenu_item(TTR("Submenu"), "submenu"); + test_submenu->add_item(TTR("Item 1")); + test_submenu->add_item(TTR("Item 2")); first_vb->add_child(test_menu_button); OptionButton *test_option_button = memnew(OptionButton); @@ -705,21 +718,7 @@ ThemeEditor::ThemeEditor() { test_option_button->add_item(TTR("Many")); test_option_button->add_item(TTR("Options")); first_vb->add_child(test_option_button); - - ColorPickerButton *cpb = memnew(ColorPickerButton); - first_vb->add_child(cpb); - - first_vb->add_child(memnew(HSeparator)); - first_vb->add_child(memnew(HSlider)); - first_vb->add_child(memnew(HScrollBar)); - first_vb->add_child(memnew(SpinBox)); - ProgressBar *pb = memnew(ProgressBar); - pb->set_value(50); - first_vb->add_child(pb); - Panel *pn = memnew(Panel); - pn->set_custom_minimum_size(Size2(40, 40) * EDSCALE); - first_vb->add_child(pn); - first_vb->add_constant_override("separation", 10 * EDSCALE); + first_vb->add_child(memnew(ColorPickerButton)); VBoxContainer *second_vb = memnew(VBoxContainer); second_vb->set_h_size_flags(SIZE_EXPAND_FILL); @@ -728,50 +727,48 @@ ThemeEditor::ThemeEditor() { LineEdit *le = memnew(LineEdit); le->set_text("LineEdit"); second_vb->add_child(le); + le = memnew(LineEdit); + le->set_text(TTR("Disabled LineEdit")); + le->set_editable(false); + second_vb->add_child(le); TextEdit *te = memnew(TextEdit); te->set_text("TextEdit"); - //te->set_v_size_flags(SIZE_EXPAND_FILL); - te->set_custom_minimum_size(Size2(0, 160) * EDSCALE); + te->set_custom_minimum_size(Size2(0, 100) * EDSCALE); second_vb->add_child(te); + second_vb->add_child(memnew(SpinBox)); - Tree *test_tree = memnew(Tree); - second_vb->add_child(test_tree); - test_tree->set_custom_minimum_size(Size2(0, 160) * EDSCALE); - - TreeItem *item = test_tree->create_item(); - item->set_editable(0, true); - item->set_text(0, "Tree"); - item = test_tree->create_item(test_tree->get_root()); - item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); - item->set_editable(0, true); - item->set_text(0, "Check"); - item = test_tree->create_item(test_tree->get_root()); - item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE); - item->set_editable(0, true); - item->set_range_config(0, 0, 20, 0.1); - item->set_range(0, 2); - item = test_tree->create_item(test_tree->get_root()); - item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE); - item->set_editable(0, true); - item->set_text(0, TTR("Has,Many,Options")); - item->set_range(0, 2); + HBoxContainer *vhb = memnew(HBoxContainer); + second_vb->add_child(vhb); + vhb->set_custom_minimum_size(Size2(0, 100) * EDSCALE); + vhb->add_child(memnew(VSlider)); + VScrollBar *vsb = memnew(VScrollBar); + vsb->set_page(25); + vhb->add_child(vsb); + vhb->add_child(memnew(VSeparator)); + VBoxContainer *hvb = memnew(VBoxContainer); + vhb->add_child(hvb); + hvb->set_alignment(ALIGN_CENTER); + hvb->set_h_size_flags(SIZE_EXPAND_FILL); + hvb->add_child(memnew(HSlider)); + HScrollBar *hsb = memnew(HScrollBar); + hsb->set_page(25); + hvb->add_child(hsb); + HSlider *hs = memnew(HSlider); + hs->set_editable(false); + hvb->add_child(hs); + hvb->add_child(memnew(HSeparator)); + ProgressBar *pb = memnew(ProgressBar); + pb->set_value(50); + hvb->add_child(pb); VBoxContainer *third_vb = memnew(VBoxContainer); third_vb->set_h_size_flags(SIZE_EXPAND_FILL); - third_vb->add_constant_override("separation", 10); - + third_vb->add_constant_override("separation", 10 * EDSCALE); main_hb->add_child(third_vb); - HBoxContainer *vhb = memnew(HBoxContainer); - vhb->set_custom_minimum_size(Size2(0, 160) * EDSCALE); - vhb->add_child(memnew(VSeparator)); - vhb->add_child(memnew(VSlider)); - vhb->add_child(memnew(VScrollBar)); - third_vb->add_child(vhb); - TabContainer *tc = memnew(TabContainer); third_vb->add_child(tc); - tc->set_custom_minimum_size(Size2(0, 160) * EDSCALE); + tc->set_custom_minimum_size(Size2(0, 135) * EDSCALE); Control *tcc = memnew(Control); tcc->set_name(TTR("Tab 1")); tc->add_child(tcc); @@ -781,9 +778,41 @@ ThemeEditor::ThemeEditor() { tcc = memnew(Control); tcc->set_name(TTR("Tab 3")); tc->add_child(tcc); + tc->set_tab_disabled(2, true); + + Tree *test_tree = memnew(Tree); + third_vb->add_child(test_tree); + test_tree->set_custom_minimum_size(Size2(0, 175) * EDSCALE); + test_tree->add_constant_override("draw_relationship_lines", 1); + + TreeItem *item = test_tree->create_item(); + item->set_text(0, "Tree"); + item = test_tree->create_item(test_tree->get_root()); + item->set_text(0, "Item"); + item = test_tree->create_item(test_tree->get_root()); + item->set_editable(0, true); + item->set_text(0, TTR("Editable Item")); + TreeItem *sub_tree = test_tree->create_item(test_tree->get_root()); + sub_tree->set_text(0, TTR("Subtree")); + item = test_tree->create_item(sub_tree); + item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + item->set_editable(0, true); + item->set_text(0, "Check Item"); + item = test_tree->create_item(sub_tree); + item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE); + item->set_editable(0, true); + item->set_range_config(0, 0, 20, 0.1); + item->set_range(0, 2); + item = test_tree->create_item(sub_tree); + item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE); + item->set_editable(0, true); + item->set_text(0, TTR("Has,Many,Options")); + item->set_range(0, 2); main_hb->add_constant_override("separation", 20 * EDSCALE); + //////// + add_del_dialog = memnew(ConfirmationDialog); add_del_dialog->hide(); add_child(add_del_dialog); @@ -802,6 +831,7 @@ ThemeEditor::ThemeEditor() { type_edit->set_h_size_flags(SIZE_EXPAND_FILL); type_hbc->add_child(type_edit); type_menu = memnew(MenuButton); + type_menu->set_flat(false); type_menu->set_text(".."); type_hbc->add_child(type_menu); @@ -819,6 +849,7 @@ ThemeEditor::ThemeEditor() { name_edit->set_h_size_flags(SIZE_EXPAND_FILL); name_hbc->add_child(name_edit); name_menu = memnew(MenuButton); + type_menu->set_flat(false); name_menu->set_text(".."); name_hbc->add_child(name_menu); @@ -844,9 +875,6 @@ ThemeEditor::ThemeEditor() { file_dialog->add_filter("*.theme ; Theme File"); add_child(file_dialog); file_dialog->connect("file_selected", this, "_save_template_cbk"); - - //MenuButton *name_menu; - //LineEdit *name_edit; } void ThemeEditorPlugin::edit(Object *p_node) { @@ -886,7 +914,6 @@ ThemeEditorPlugin::ThemeEditorPlugin(EditorNode *p_node) { theme_editor = memnew(ThemeEditor); theme_editor->set_custom_minimum_size(Size2(0, 200)); - //p_node->get_viewport()->add_child(theme_editor); button = editor->add_bottom_panel_item(TTR("Theme"), theme_editor); button->hide(); } diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h index 352988d69e..cc236907a9 100644 --- a/editor/plugins/theme_editor_plugin.h +++ b/editor/plugins/theme_editor_plugin.h @@ -33,6 +33,7 @@ #include "scene/gui/check_box.h" #include "scene/gui/file_dialog.h" +#include "scene/gui/margin_container.h" #include "scene/gui/option_button.h" #include "scene/gui/scroll_container.h" #include "scene/gui/texture_rect.h" @@ -40,12 +41,12 @@ #include "editor/editor_node.h" -class ThemeEditor : public Control { +class ThemeEditor : public VBoxContainer { - GDCLASS(ThemeEditor, Control); + GDCLASS(ThemeEditor, VBoxContainer); ScrollContainer *scroll; - VBoxContainer *main_vb; + MarginContainer *main_container; Ref<Theme> theme; EditorFileDialog *file_dialog; diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp index 1a367b61dd..29a54f815d 100644 --- a/editor/plugins/tile_map_editor_plugin.cpp +++ b/editor/plugins/tile_map_editor_plugin.cpp @@ -66,6 +66,11 @@ void TileMapEditor::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { + paint_button->set_icon(get_icon("Edit", "EditorIcons")); + bucket_fill_button->set_icon(get_icon("Bucket", "EditorIcons")); + picker_button->set_icon(get_icon("ColorPick", "EditorIcons")); + select_button->set_icon(get_icon("ActionCopy", "EditorIcons")); + rotate_left_button->set_icon(get_icon("Rotate270", "EditorIcons")); rotate_right_button->set_icon(get_icon("Rotate90", "EditorIcons")); flip_horizontal_button->set_icon(get_icon("MirrorX", "EditorIcons")); @@ -76,9 +81,6 @@ void TileMapEditor::_notification(int p_what) { search_box->set_clear_button_enabled(true); PopupMenu *p = options->get_popup(); - p->set_item_icon(p->get_item_index(OPTION_PAINTING), get_icon("Edit", "EditorIcons")); - p->set_item_icon(p->get_item_index(OPTION_PICK_TILE), get_icon("ColorPick", "EditorIcons")); - p->set_item_icon(p->get_item_index(OPTION_SELECT), get_icon("ActionCopy", "EditorIcons")); p->set_item_icon(p->get_item_index(OPTION_CUT), get_icon("ActionCut", "EditorIcons")); p->set_item_icon(p->get_item_index(OPTION_COPY), get_icon("Duplicate", "EditorIcons")); p->set_item_icon(p->get_item_index(OPTION_ERASE_SELECTION), get_icon("Remove", "EditorIcons")); @@ -87,37 +89,54 @@ void TileMapEditor::_notification(int p_what) { } } -void TileMapEditor::_menu_option(int p_option) { +void TileMapEditor::_update_button_tool() { - switch (p_option) { - - case OPTION_PAINTING: { - // NOTE: We do not set tool = TOOL_PAINTING as this begins painting - // immediately without pressing the left mouse button first - tool = TOOL_NONE; - - CanvasItemEditor::get_singleton()->update_viewport(); + ToolButton *tb[4] = { paint_button, bucket_fill_button, picker_button, select_button }; + // Unpress all buttons + for (int i = 0; i < 4; i++) { + tb[i]->set_pressed(false); + } + // Press the good button + switch (tool) { + case TOOL_NONE: + case TOOL_PAINTING: { + paint_button->set_pressed(true); } break; - case OPTION_BUCKET: { - - tool = TOOL_BUCKET; - - CanvasItemEditor::get_singleton()->update_viewport(); + case TOOL_BUCKET: { + bucket_fill_button->set_pressed(true); } break; - case OPTION_PICK_TILE: { + case TOOL_PICKING: { + picker_button->set_pressed(true); + } break; + case TOOL_SELECTING: { + select_button->set_pressed(true); + } break; + default: + break; + } - tool = TOOL_PICKING; + if (tool != TOOL_PICKING) + last_tool = tool; +} - CanvasItemEditor::get_singleton()->update_viewport(); - } break; - case OPTION_SELECT: { +void TileMapEditor::_button_tool_select(int p_tool) { + tool = (Tool)p_tool; + _update_button_tool(); + switch (tool) { + case TOOL_SELECTING: { - tool = TOOL_SELECTING; selection_active = false; - - CanvasItemEditor::get_singleton()->update_viewport(); } break; + default: + break; + } + CanvasItemEditor::get_singleton()->update_viewport(); +} + +void TileMapEditor::_menu_option(int p_option) { + + switch (p_option) { case OPTION_COPY: { _update_copydata(); @@ -168,6 +187,7 @@ void TileMapEditor::_menu_option(int p_option) { } } break; } + _update_button_tool(); } void TileMapEditor::_palette_selected(int index) { @@ -220,22 +240,23 @@ void TileMapEditor::set_selected_tiles(Vector<int> p_tiles) { palette->ensure_current_is_visible(); } -void TileMapEditor::_create_set_cell_undo(const Vector2 &p_vec, const CellOp &p_cell_old, const CellOp &p_cell_new) { +Dictionary TileMapEditor::_create_cell_dictionary(int tile, bool flip_x, bool flip_y, bool transpose, Vector2 autotile_coord) { - Dictionary cell_old; - Dictionary cell_new; + Dictionary cell; - cell_old["id"] = p_cell_old.idx; - cell_old["flip_h"] = p_cell_old.xf; - cell_old["flip_y"] = p_cell_old.yf; - cell_old["transpose"] = p_cell_old.tr; - cell_old["auto_coord"] = p_cell_old.ac; + cell["id"] = tile; + cell["flip_h"] = flip_x; + cell["flip_y"] = flip_y; + cell["transpose"] = transpose; + cell["auto_coord"] = autotile_coord; - cell_new["id"] = p_cell_new.idx; - cell_new["flip_h"] = p_cell_new.xf; - cell_new["flip_y"] = p_cell_new.yf; - cell_new["transpose"] = p_cell_new.tr; - cell_new["auto_coord"] = p_cell_new.ac; + return cell; +} + +void TileMapEditor::_create_set_cell_undo_redo(const Vector2 &p_vec, const CellOp &p_cell_old, const CellOp &p_cell_new) { + + Dictionary cell_old = _create_cell_dictionary(p_cell_old.idx, p_cell_old.xf, p_cell_old.yf, p_cell_old.tr, p_cell_old.ac); + Dictionary cell_new = _create_cell_dictionary(p_cell_new.idx, p_cell_new.xf, p_cell_new.yf, p_cell_new.tr, p_cell_new.ac); undo_redo->add_undo_method(node, "_set_celld", p_vec, cell_old); undo_redo->add_do_method(node, "_set_celld", p_vec, cell_new); @@ -251,7 +272,7 @@ void TileMapEditor::_finish_undo() { if (undo_data.size()) { for (Map<Point2i, CellOp>::Element *E = undo_data.front(); E; E = E->next()) { - _create_set_cell_undo(E->key(), E->get(), _get_op_from_cell(E->key())); + _create_set_cell_undo_redo(E->key(), E->get(), _get_op_from_cell(E->key())); } undo_data.clear(); @@ -299,7 +320,8 @@ void TileMapEditor::_set_cell(const Point2i &p_pos, Vector<int> p_values, bool p } } - node->set_cell(p_pos.x, p_pos.y, p_value, p_flip_h, p_flip_v, p_transpose, p_autotile_coord); + node->_set_celld(p_pos, _create_cell_dictionary(p_value, p_flip_h, p_flip_v, p_transpose, p_autotile_coord)); + if (manual_autotile || (p_value != -1 && node->get_tileset()->tile_get_tile_mode(p_value) == TileSet::ATLAS_TILE)) { if (current != -1) { node->set_cell_autotile_coord(p_pos.x, p_pos.y, position); @@ -477,10 +499,10 @@ void TileMapEditor::_update_palette() { if (sel_tile != TileMap::INVALID_CELL) { if ((manual_autotile && tileset->tile_get_tile_mode(sel_tile) == TileSet::AUTO_TILE) || tileset->tile_get_tile_mode(sel_tile) == TileSet::ATLAS_TILE) { - const Map<Vector2, uint16_t> &tiles2 = tileset->autotile_get_bitmask_map(sel_tile); + const Map<Vector2, uint32_t> &tiles2 = tileset->autotile_get_bitmask_map(sel_tile); Vector<Vector2> entries2; - for (const Map<Vector2, uint16_t>::Element *E = tiles2.front(); E; E = E->next()) { + for (const Map<Vector2, uint32_t>::Element *E = tiles2.front(); E; E = E->next()) { entries2.push_back(E->key()); } entries2.sort(); @@ -928,11 +950,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { if (mb->get_shift()) { -#ifdef APPLE_STYLE_KEYS if (mb->get_command()) -#else - if (mb->get_control()) -#endif tool = TOOL_RECTANGLE_PAINT; else tool = TOOL_LINE_PAINT; @@ -940,20 +958,20 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { selection_active = false; rectangle_begin = over_tile; + _update_button_tool(); return true; } -#ifdef APPLE_STYLE_KEYS + if (mb->get_command()) { -#else - if (mb->get_control()) { -#endif tool = TOOL_PICKING; _pick_tile(over_tile); + _update_button_tool(); return true; } tool = TOOL_PAINTING; + _update_button_tool(); } if (tool == TOOL_PAINTING) { @@ -975,6 +993,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { rectangle_begin = over_tile; } + _update_button_tool(); return true; } else { @@ -1071,6 +1090,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { } tool = TOOL_NONE; + _update_button_tool(); return true; } @@ -1086,6 +1106,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); return true; } @@ -1096,6 +1117,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); return true; } @@ -1108,11 +1130,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { _start_undo(TTR("Erase TileMap")); if (mb->get_shift()) { -#ifdef APPLE_STYLE_KEYS if (mb->get_command()) -#else - if (mb->get_control()) -#endif tool = TOOL_RECTANGLE_ERASE; else tool = TOOL_LINE_ERASE; @@ -1126,6 +1144,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { _set_cell(local, invalid_cell); } + _update_button_tool(); return true; } @@ -1140,6 +1159,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { tool = TOOL_NONE; + _update_button_tool(); return true; } else if (tool == TOOL_BUCKET) { @@ -1314,6 +1334,14 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { if (k.is_valid() && k->is_pressed()) { + if (last_tool == TOOL_NONE && tool == TOOL_PICKING && k->get_scancode() == KEY_SHIFT && k->get_command()) { + // trying to draw a rectangle with the painting tool, so change to the correct tool + tool = last_tool; + + CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); + } + if (k->get_scancode() == KEY_ESCAPE) { if (tool == TOOL_PASTING) @@ -1325,6 +1353,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); return true; } @@ -1339,17 +1368,20 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { tool = TOOL_NONE; CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); return true; } if (ED_IS_SHORTCUT("tile_map_editor/bucket_fill", p_event)) { tool = TOOL_BUCKET; CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); return true; } if (ED_IS_SHORTCUT("tile_map_editor/erase_selection", p_event)) { _menu_option(OPTION_ERASE_SELECTION); + _update_button_tool(); return true; } if (ED_IS_SHORTCUT("tile_map_editor/select", p_event)) { @@ -1358,6 +1390,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); return true; } if (ED_IS_SHORTCUT("tile_map_editor/copy_selection", p_event)) { @@ -1368,6 +1401,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); return true; } } @@ -1384,6 +1418,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { tool = TOOL_PASTING; CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); return true; } } @@ -1411,8 +1446,30 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { CanvasItemEditor::get_singleton()->update_viewport(); return true; } - } + } else if (k.is_valid()) { // release event + + if (tool == TOOL_NONE) { + + if (k->get_scancode() == KEY_SHIFT && k->get_command()) { + + tool = TOOL_PICKING; + _update_button_tool(); + } + } else if (tool == TOOL_PICKING) { + +#ifdef APPLE_STYLE_KEYS + if (k->get_scancode() == KEY_META) { +#else + if (k->get_scancode() == KEY_CONTROL) { +#endif + // go back to that last tool if KEY_CONTROL was released + tool = last_tool; + CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); + } + } + } return false; } @@ -1434,9 +1491,9 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { aabb.expand_to(node->world_to_map(xform_inv.xform(screen_size))); Rect2i si = aabb.grow(1.0); - if (node->get_half_offset() != TileMap::HALF_OFFSET_X) { + if (node->get_half_offset() != TileMap::HALF_OFFSET_X && node->get_half_offset() != TileMap::HALF_OFFSET_NEGATIVE_X) { - int max_lines = 2000; //avoid crash if size too smal + int max_lines = 2000; //avoid crash if size too small for (int i = (si.position.x) - 1; i <= (si.position.x + si.size.x); i++) { @@ -1450,7 +1507,7 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { } } else { - int max_lines = 10000; //avoid crash if size too smal + int max_lines = 10000; //avoid crash if size too small for (int i = (si.position.x) - 1; i <= (si.position.x + si.size.x); i++) { @@ -1458,23 +1515,26 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { Vector2 ofs; if (ABS(j) & 1) { - ofs = cell_xf[0] * 0.5; + ofs = cell_xf[0] * (node->get_half_offset() == TileMap::HALF_OFFSET_X ? 0.5 : -0.5); } Vector2 from = xform.xform(node->map_to_world(Vector2(i, j), true) + ofs); Vector2 to = xform.xform(node->map_to_world(Vector2(i, j + 1), true) + ofs); + Color col = i == 0 ? Color(1, 0.8, 0.2, 0.5) : Color(1, 0.3, 0.1, 0.2); p_overlay->draw_line(from, to, col, 1); - if (max_lines-- == 0) + if (--max_lines == 0) break; } + if (max_lines == 0) + break; } } int max_lines = 10000; //avoid crash if size too smal - if (node->get_half_offset() != TileMap::HALF_OFFSET_Y) { + if (node->get_half_offset() != TileMap::HALF_OFFSET_Y && node->get_half_offset() != TileMap::HALF_OFFSET_NEGATIVE_Y) { for (int i = (si.position.y) - 1; i <= (si.position.y + si.size.y); i++) { @@ -1495,17 +1555,20 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { Vector2 ofs; if (ABS(j) & 1) { - ofs = cell_xf[1] * 0.5; + ofs = cell_xf[1] * (node->get_half_offset() == TileMap::HALF_OFFSET_Y ? 0.5 : -0.5); } Vector2 from = xform.xform(node->map_to_world(Vector2(j, i), true) + ofs); Vector2 to = xform.xform(node->map_to_world(Vector2(j + 1, i), true) + ofs); + Color col = i == 0 ? Color(1, 0.8, 0.2, 0.5) : Color(1, 0.3, 0.1, 0.2); p_overlay->draw_line(from, to, col, 1); - if (max_lines-- == 0) + if (--max_lines == 0) break; } + if (max_lines == 0) + break; } } } @@ -1533,8 +1596,12 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { for (int i = 0; i < 4; i++) { if (node->get_half_offset() == TileMap::HALF_OFFSET_X && ABS(over_tile.y) & 1) endpoints[i] += cell_xf[0] * 0.5; + if (node->get_half_offset() == TileMap::HALF_OFFSET_NEGATIVE_X && ABS(over_tile.y) & 1) + endpoints[i] += cell_xf[0] * -0.5; if (node->get_half_offset() == TileMap::HALF_OFFSET_Y && ABS(over_tile.x) & 1) endpoints[i] += cell_xf[1] * 0.5; + if (node->get_half_offset() == TileMap::HALF_OFFSET_NEGATIVE_Y && ABS(over_tile.x) & 1) + endpoints[i] += cell_xf[1] * -0.5; endpoints[i] = xform.xform(endpoints[i]); } Color col; @@ -1686,6 +1753,7 @@ void TileMapEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_text_entered"), &TileMapEditor::_text_entered); ClassDB::bind_method(D_METHOD("_text_changed"), &TileMapEditor::_text_changed); ClassDB::bind_method(D_METHOD("_sbox_input"), &TileMapEditor::_sbox_input); + ClassDB::bind_method(D_METHOD("_button_tool_select"), &TileMapEditor::_button_tool_select); ClassDB::bind_method(D_METHOD("_menu_option"), &TileMapEditor::_menu_option); ClassDB::bind_method(D_METHOD("_canvas_mouse_enter"), &TileMapEditor::_canvas_mouse_enter); ClassDB::bind_method(D_METHOD("_canvas_mouse_exit"), &TileMapEditor::_canvas_mouse_exit); @@ -1866,37 +1934,66 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) { // Add menu items toolbar = memnew(HBoxContainer); - toolbar->set_h_size_flags(SIZE_EXPAND_FILL); - toolbar->set_alignment(BoxContainer::ALIGN_END); toolbar->hide(); CanvasItemEditor::get_singleton()->add_control_to_menu_panel(toolbar); + // Separator + toolbar->add_child(memnew(VSeparator)); + + // Tools + paint_button = memnew(ToolButton); + paint_button->set_shortcut(ED_SHORTCUT("tile_map_editor/paint_tile", TTR("Paint Tile"), KEY_P)); + paint_button->set_tooltip(TTR("Shift+RMB: Line Draw\nShift+Ctrl+RMB: Rectangle Paint")); + paint_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_NONE)); + paint_button->set_toggle_mode(true); + toolbar->add_child(paint_button); + + bucket_fill_button = memnew(ToolButton); + bucket_fill_button->set_shortcut(ED_SHORTCUT("tile_map_editor/bucket_fill", TTR("Bucket Fill"), KEY_G)); + bucket_fill_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_BUCKET)); + bucket_fill_button->set_toggle_mode(true); + toolbar->add_child(bucket_fill_button); + + picker_button = memnew(ToolButton); + picker_button->set_shortcut(ED_SHORTCUT("tile_map_editor/pick_tile", TTR("Pick Tile"), KEY_CONTROL)); + picker_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_PICKING)); + picker_button->set_toggle_mode(true); + toolbar->add_child(picker_button); + + select_button = memnew(ToolButton); + select_button->set_shortcut(ED_SHORTCUT("tile_map_editor/select", TTR("Select"), KEY_MASK_CMD + KEY_B)); + select_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_SELECTING)); + select_button->set_toggle_mode(true); + toolbar->add_child(select_button); + + _update_button_tool(); + + // Container to the right of the toolbar + toolbar_right = memnew(HBoxContainer); + toolbar_right->hide(); + toolbar_right->set_h_size_flags(SIZE_EXPAND_FILL); + toolbar_right->set_alignment(BoxContainer::ALIGN_END); + CanvasItemEditor::get_singleton()->add_control_to_menu_panel(toolbar_right); + // Tile position tile_info = memnew(Label); - toolbar->add_child(tile_info); + toolbar_right->add_child(tile_info); + // Menu options = memnew(MenuButton); options->set_text("TileMap"); options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("TileMap", "EditorIcons")); options->set_process_unhandled_key_input(false); + toolbar_right->add_child(options); PopupMenu *p = options->get_popup(); - - p->add_shortcut(ED_SHORTCUT("tile_map_editor/paint_tile", TTR("Paint Tile"), KEY_P), OPTION_PAINTING); - p->add_shortcut(ED_SHORTCUT("tile_map_editor/bucket_fill", TTR("Bucket Fill"), KEY_G), OPTION_BUCKET); - p->add_separator(); - p->add_item(TTR("Pick Tile"), OPTION_PICK_TILE, KEY_CONTROL); - p->add_separator(); - p->add_shortcut(ED_SHORTCUT("tile_map_editor/select", TTR("Select"), KEY_MASK_CMD + KEY_B), OPTION_SELECT); p->add_shortcut(ED_SHORTCUT("tile_map_editor/cut_selection", TTR("Cut Selection"), KEY_MASK_CMD + KEY_X), OPTION_CUT); p->add_shortcut(ED_SHORTCUT("tile_map_editor/copy_selection", TTR("Copy Selection"), KEY_MASK_CMD + KEY_C), OPTION_COPY); p->add_shortcut(ED_GET_SHORTCUT("tile_map_editor/erase_selection"), OPTION_ERASE_SELECTION); p->add_separator(); p->add_item(TTR("Fix Invalid Tiles"), OPTION_FIX_INVALID); - p->connect("id_pressed", this, "_menu_option"); - toolbar->add_child(options); rotate_left_button = memnew(ToolButton); rotate_left_button->set_tooltip(TTR("Rotate left")); rotate_left_button->set_focus_mode(FOCUS_NONE); @@ -1970,10 +2067,12 @@ void TileMapEditorPlugin::make_visible(bool p_visible) { tile_map_editor->show(); tile_map_editor->get_toolbar()->show(); + tile_map_editor->get_toolbar_right()->show(); } else { tile_map_editor->hide(); tile_map_editor->get_toolbar()->hide(); + tile_map_editor->get_toolbar_right()->hide(); tile_map_editor->edit(NULL); } } diff --git a/editor/plugins/tile_map_editor_plugin.h b/editor/plugins/tile_map_editor_plugin.h index b30426eabe..fcdada1111 100644 --- a/editor/plugins/tile_map_editor_plugin.h +++ b/editor/plugins/tile_map_editor_plugin.h @@ -66,12 +66,8 @@ class TileMapEditor : public VBoxContainer { enum Options { - OPTION_BUCKET, - OPTION_PICK_TILE, - OPTION_SELECT, OPTION_COPY, OPTION_ERASE_SELECTION, - OPTION_PAINTING, OPTION_FIX_INVALID, OPTION_CUT }; @@ -90,10 +86,16 @@ class TileMapEditor : public VBoxContainer { ItemList *manual_palette; HBoxContainer *toolbar; + HBoxContainer *toolbar_right; Label *tile_info; MenuButton *options; + ToolButton *paint_button; + ToolButton *bucket_fill_button; + ToolButton *picker_button; + ToolButton *select_button; + ToolButton *flip_horizontal_button; ToolButton *flip_vertical_button; ToolButton *rotate_left_button; @@ -103,6 +105,7 @@ class TileMapEditor : public VBoxContainer { CheckBox *manual_button; Tool tool; + Tool last_tool; bool selection_active; bool mouse_over; @@ -184,13 +187,16 @@ class TileMapEditor : public VBoxContainer { void _text_changed(const String &p_text); void _sbox_input(const Ref<InputEvent> &p_ie); void _update_palette(); + void _update_button_tool(); + void _button_tool_select(int p_tool); void _menu_option(int p_option); void _palette_selected(int index); void _palette_multi_selected(int index, bool selected); + Dictionary _create_cell_dictionary(int tile, bool flip_x, bool flip_y, bool transpose, Vector2 autotile_coord); void _start_undo(const String &p_action); void _finish_undo(); - void _create_set_cell_undo(const Vector2 &p_vec, const CellOp &p_cell_old, const CellOp &p_cell_new); + void _create_set_cell_undo_redo(const Vector2 &p_vec, const CellOp &p_cell_old, const CellOp &p_cell_new); void _set_cell(const Point2i &p_pos, Vector<int> p_values, bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false, const Point2i p_autotile_coord = Point2()); void _canvas_mouse_enter(); @@ -210,6 +216,7 @@ protected: public: HBoxContainer *get_toolbar() const { return toolbar; } + HBoxContainer *get_toolbar_right() const { return toolbar_right; } bool forward_gui_input(const Ref<InputEvent> &p_event); void forward_canvas_draw_over_viewport(Control *p_overlay); diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp index dc099b84a7..21470d81ed 100644 --- a/editor/plugins/tile_set_editor_plugin.cpp +++ b/editor/plugins/tile_set_editor_plugin.cpp @@ -734,7 +734,7 @@ void TileSetEditor::_on_edit_mode_changed(int p_edit_mode) { tools[SHAPE_KEEP_INSIDE_TILE]->hide(); tools[TOOL_SELECT]->set_pressed(true); - tools[TOOL_SELECT]->set_tooltip(TTR("LMB: Set bit on.\nRMB: Set bit off.\nClick on another Tile to edit it.")); + tools[TOOL_SELECT]->set_tooltip(TTR("LMB: Set bit on.\nRMB: Set bit off.\nShift+LMB: Set wildcard bit.\nClick on another Tile to edit it.")); spin_priority->hide(); } break; case EDITMODE_Z_INDEX: @@ -770,7 +770,8 @@ void TileSetEditor::_on_edit_mode_changed(int p_edit_mode) { spin_z_index->show(); } } break; - default: {} + default: { + } } _update_toggle_shape_button(); workspace->update(); @@ -818,52 +819,92 @@ void TileSetEditor::_on_workspace_draw() { } break; case EDITMODE_BITMASK: { Color c(1, 0, 0, 0.5); + Color ci(0.3, 0.6, 1, 0.5); for (float x = 0; x < region.size.x / (spacing + size.x); x++) { for (float y = 0; y < region.size.y / (spacing + size.y); y++) { Vector2 coord(x, y); Point2 anchor(coord.x * (spacing + size.x), coord.y * (spacing + size.y)); anchor += WORKSPACE_MARGIN; anchor += region.position; - uint16_t mask = tileset->autotile_get_bitmask(get_current_tile(), coord); + uint32_t mask = tileset->autotile_get_bitmask(get_current_tile(), coord); if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) { - if (mask & TileSet::BIND_TOPLEFT) { + if (mask & TileSet::BIND_IGNORE_TOPLEFT) { + workspace->draw_rect(Rect2(anchor, size / 4), ci); + workspace->draw_rect(Rect2(anchor + size / 4, size / 4), ci); + } else if (mask & TileSet::BIND_TOPLEFT) { workspace->draw_rect(Rect2(anchor, size / 2), c); } - if (mask & TileSet::BIND_TOPRIGHT) { + if (mask & TileSet::BIND_IGNORE_TOPRIGHT) { + workspace->draw_rect(Rect2(anchor + Vector2(size.x / 2, 0), size / 4), ci); + workspace->draw_rect(Rect2(anchor + Vector2(size.x * 3 / 4, size.y / 4), size / 4), ci); + } else if (mask & TileSet::BIND_TOPRIGHT) { workspace->draw_rect(Rect2(anchor + Vector2(size.x / 2, 0), size / 2), c); } - if (mask & TileSet::BIND_BOTTOMLEFT) { + if (mask & TileSet::BIND_IGNORE_BOTTOMLEFT) { + workspace->draw_rect(Rect2(anchor + Vector2(0, size.y / 2), size / 4), ci); + workspace->draw_rect(Rect2(anchor + Vector2(size.x / 4, size.y * 3 / 4), size / 4), ci); + } else if (mask & TileSet::BIND_BOTTOMLEFT) { workspace->draw_rect(Rect2(anchor + Vector2(0, size.y / 2), size / 2), c); } - if (mask & TileSet::BIND_BOTTOMRIGHT) { + if (mask & TileSet::BIND_IGNORE_BOTTOMRIGHT) { + workspace->draw_rect(Rect2(anchor + size / 2, size / 4), ci); + workspace->draw_rect(Rect2(anchor + size * 3 / 4, size / 4), ci); + } else if (mask & TileSet::BIND_BOTTOMRIGHT) { workspace->draw_rect(Rect2(anchor + size / 2, size / 2), c); } } else { - if (mask & TileSet::BIND_TOPLEFT) { + if (mask & TileSet::BIND_IGNORE_TOPLEFT) { + workspace->draw_rect(Rect2(anchor, size / 6), ci); + workspace->draw_rect(Rect2(anchor + size / 6, size / 6), ci); + } else if (mask & TileSet::BIND_TOPLEFT) { workspace->draw_rect(Rect2(anchor, size / 3), c); } - if (mask & TileSet::BIND_TOP) { + if (mask & TileSet::BIND_IGNORE_TOP) { + workspace->draw_rect(Rect2(anchor + Vector2(size.x / 3, 0), size / 6), ci); + workspace->draw_rect(Rect2(anchor + Vector2(size.x / 2, size.y / 6), size / 6), ci); + } else if (mask & TileSet::BIND_TOP) { workspace->draw_rect(Rect2(anchor + Vector2(size.x / 3, 0), size / 3), c); } - if (mask & TileSet::BIND_TOPRIGHT) { + if (mask & TileSet::BIND_IGNORE_TOPRIGHT) { + workspace->draw_rect(Rect2(anchor + Vector2(size.x * 4 / 6, 0), size / 6), ci); + workspace->draw_rect(Rect2(anchor + Vector2(size.x * 5 / 6, size.y / 6), size / 6), ci); + } else if (mask & TileSet::BIND_TOPRIGHT) { workspace->draw_rect(Rect2(anchor + Vector2((size.x / 3) * 2, 0), size / 3), c); } - if (mask & TileSet::BIND_LEFT) { + if (mask & TileSet::BIND_IGNORE_LEFT) { + workspace->draw_rect(Rect2(anchor + Vector2(0, size.y / 3), size / 6), ci); + workspace->draw_rect(Rect2(anchor + Vector2(size.x / 6, size.y / 2), size / 6), ci); + } else if (mask & TileSet::BIND_LEFT) { workspace->draw_rect(Rect2(anchor + Vector2(0, size.y / 3), size / 3), c); } - if (mask & TileSet::BIND_CENTER) { + if (mask & TileSet::BIND_IGNORE_CENTER) { + workspace->draw_rect(Rect2(anchor + size / 3, size / 6), ci); + workspace->draw_rect(Rect2(anchor + size / 2, size / 6), ci); + } else if (mask & TileSet::BIND_CENTER) { workspace->draw_rect(Rect2(anchor + Vector2(size.x / 3, size.y / 3), size / 3), c); } - if (mask & TileSet::BIND_RIGHT) { + if (mask & TileSet::BIND_IGNORE_RIGHT) { + workspace->draw_rect(Rect2(anchor + Vector2(size.x * 4 / 6, size.y / 3), size / 6), ci); + workspace->draw_rect(Rect2(anchor + Vector2(size.x * 5 / 6, size.y / 2), size / 6), ci); + } else if (mask & TileSet::BIND_RIGHT) { workspace->draw_rect(Rect2(anchor + Vector2((size.x / 3) * 2, size.y / 3), size / 3), c); } - if (mask & TileSet::BIND_BOTTOMLEFT) { + if (mask & TileSet::BIND_IGNORE_BOTTOMLEFT) { + workspace->draw_rect(Rect2(anchor + Vector2(0, size.y * 4 / 6), size / 6), ci); + workspace->draw_rect(Rect2(anchor + Vector2(size.x / 6, size.y * 5 / 6), size / 6), ci); + } else if (mask & TileSet::BIND_BOTTOMLEFT) { workspace->draw_rect(Rect2(anchor + Vector2(0, (size.y / 3) * 2), size / 3), c); } - if (mask & TileSet::BIND_BOTTOM) { + if (mask & TileSet::BIND_IGNORE_BOTTOM) { + workspace->draw_rect(Rect2(anchor + Vector2(size.x / 3, size.y * 4 / 6), size / 6), ci); + workspace->draw_rect(Rect2(anchor + Vector2(size.x / 2, size.y * 5 / 6), size / 6), ci); + } else if (mask & TileSet::BIND_BOTTOM) { workspace->draw_rect(Rect2(anchor + Vector2(size.x / 3, (size.y / 3) * 2), size / 3), c); } - if (mask & TileSet::BIND_BOTTOMRIGHT) { + if (mask & TileSet::BIND_IGNORE_BOTTOMRIGHT) { + workspace->draw_rect(Rect2(anchor + size * 4 / 6, size / 6), ci); + workspace->draw_rect(Rect2(anchor + size * 5 / 6, size / 6), ci); + } else if (mask & TileSet::BIND_BOTTOMRIGHT) { workspace->draw_rect(Rect2(anchor + (size / 3) * 2, size / 3), c); } } @@ -882,10 +923,10 @@ void TileSetEditor::_on_workspace_draw() { } break; case EDITMODE_PRIORITY: { spin_priority->set_value(tileset->autotile_get_subtile_priority(get_current_tile(), edited_shape_coord)); - uint16_t mask = tileset->autotile_get_bitmask(get_current_tile(), edited_shape_coord); + uint32_t mask = tileset->autotile_get_bitmask(get_current_tile(), edited_shape_coord); Vector<Vector2> queue_others; int total = 0; - for (Map<Vector2, uint16_t>::Element *E = tileset->autotile_get_bitmask_map(get_current_tile()).front(); E; E = E->next()) { + for (Map<Vector2, uint32_t>::Element *E = tileset->autotile_get_bitmask_map(get_current_tile()).front(); E; E = E->next()) { if (E->value() == mask) { total += tileset->autotile_get_subtile_priority(get_current_tile(), E->key()); if (E->key() != edited_shape_coord) { @@ -900,7 +941,8 @@ void TileSetEditor::_on_workspace_draw() { spin_z_index->set_value(tileset->autotile_get_z_index(get_current_tile(), edited_shape_coord)); draw_highlight_subtile(edited_shape_coord); } break; - default: {} + default: { + } } } @@ -996,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); } } @@ -1037,6 +1080,7 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { static bool dragging; static bool erasing; + static bool alternative; draw_edited_region = false; Rect2 current_tile_region = Rect2(); @@ -1220,10 +1264,11 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { if ((mb->get_button_index() == BUTTON_RIGHT || mb->get_button_index() == BUTTON_LEFT) && current_tile_region.has_point(mb->get_position())) { dragging = true; erasing = (mb->get_button_index() == BUTTON_RIGHT); + alternative = Input::get_singleton()->is_key_pressed(KEY_SHIFT); Vector2 coord((int)((mb->get_position().x - current_tile_region.position.x) / (spacing + size.x)), (int)((mb->get_position().y - current_tile_region.position.y) / (spacing + size.y))); Vector2 pos(coord.x * (spacing + size.x), coord.y * (spacing + size.y)); pos = mb->get_position() - (pos + current_tile_region.position); - uint16_t bit = 0; + uint32_t bit = 0; if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) { if (pos.x < size.x / 2) { if (pos.y < size.y / 2) { @@ -1266,13 +1311,19 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { } } - uint16_t old_mask = tileset->autotile_get_bitmask(get_current_tile(), coord); - uint16_t new_mask = old_mask; - if (erasing) { + uint32_t old_mask = tileset->autotile_get_bitmask(get_current_tile(), coord); + uint32_t new_mask = old_mask; + if (alternative) { new_mask &= ~bit; + new_mask |= (bit << 16); + } else if (erasing) { + new_mask &= ~bit; + new_mask &= ~(bit << 16); } else { new_mask |= bit; + new_mask &= ~(bit << 16); } + if (old_mask != new_mask) { undo_redo->create_action(TTR("Edit Tile Bitmask")); undo_redo->add_do_method(tileset.ptr(), "autotile_set_bitmask", get_current_tile(), coord, new_mask); @@ -1286,6 +1337,7 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { if ((erasing && mb->get_button_index() == BUTTON_RIGHT) || (!erasing && mb->get_button_index() == BUTTON_LEFT)) { dragging = false; erasing = false; + alternative = false; } } } @@ -1294,7 +1346,7 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { Vector2 coord((int)((mm->get_position().x - current_tile_region.position.x) / (spacing + size.x)), (int)((mm->get_position().y - current_tile_region.position.y) / (spacing + size.y))); Vector2 pos(coord.x * (spacing + size.x), coord.y * (spacing + size.y)); pos = mm->get_position() - (pos + current_tile_region.position); - uint16_t bit = 0; + uint32_t bit = 0; if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) { if (pos.x < size.x / 2) { if (pos.y < size.y / 2) { @@ -1337,12 +1389,17 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { } } - uint16_t old_mask = tileset->autotile_get_bitmask(get_current_tile(), coord); - uint16_t new_mask = old_mask; - if (erasing) { + uint32_t old_mask = tileset->autotile_get_bitmask(get_current_tile(), coord); + uint32_t new_mask = old_mask; + if (alternative) { + new_mask &= ~bit; + new_mask |= (bit << 16); + } else if (erasing) { new_mask &= ~bit; + new_mask &= ~(bit << 16); } else { new_mask |= bit; + new_mask &= ~(bit << 16); } if (old_mask != new_mask) { undo_redo->create_action(TTR("Edit Tile Bitmask")); @@ -1504,19 +1561,49 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { _set_edited_collision_shape(Ref<ConvexPolygonShape2D>()); current_shape.resize(0); - current_shape.push_back(snap_point(shape_anchor)); - current_shape.push_back(snap_point(shape_anchor + Vector2(current_tile_region.size.x, 0))); - current_shape.push_back(snap_point(shape_anchor + current_tile_region.size)); - current_shape.push_back(snap_point(shape_anchor + Vector2(0, current_tile_region.size.y))); - close_shape(shape_anchor); + Vector2 pos = mb->get_position(); + pos = snap_point(pos); + current_shape.push_back(pos); + current_shape.push_back(pos); + current_shape.push_back(pos); + current_shape.push_back(pos); + creating_shape = true; workspace->update(); + return; } else if (mb->is_pressed() && mb->get_button_index() == BUTTON_RIGHT) { + if (creating_shape) { + creating_shape = false; + _select_edited_shape_coord(); + workspace->update(); + } + } else if (!mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + if (creating_shape) { + if ((current_shape[0] - current_shape[1]).length_squared() <= grab_threshold) { + current_shape.set(0, snap_point(shape_anchor)); + current_shape.set(1, snap_point(shape_anchor + Vector2(current_tile_region.size.x, 0))); + current_shape.set(2, snap_point(shape_anchor + current_tile_region.size)); + current_shape.set(3, snap_point(shape_anchor + Vector2(0, current_tile_region.size.y))); + } + close_shape(shape_anchor); + workspace->update(); + return; + } + } + } else if (mm.is_valid()) { + if (creating_shape) { + Vector2 pos = mm->get_position(); + pos = snap_point(pos); + Vector2 p = current_shape[2]; + current_shape.set(3, snap_point(Vector2(pos.x, p.y))); + current_shape.set(0, snap_point(pos)); + current_shape.set(1, snap_point(Vector2(p.x, pos.y))); workspace->update(); } } } } break; - default: {} + default: { + } } } } @@ -1529,10 +1616,10 @@ void TileSetEditor::_on_tool_clicked(int p_tool) { undo_redo->create_action(TTR("Paste Tile Bitmask")); undo_redo->add_do_method(tileset.ptr(), "autotile_clear_bitmask_map", get_current_tile()); undo_redo->add_undo_method(tileset.ptr(), "autotile_clear_bitmask_map", get_current_tile()); - for (Map<Vector2, uint16_t>::Element *E = bitmask_map_copy.front(); E; E = E->next()) { + for (Map<Vector2, uint32_t>::Element *E = bitmask_map_copy.front(); E; E = E->next()) { undo_redo->add_do_method(tileset.ptr(), "autotile_set_bitmask", get_current_tile(), E->key(), E->value()); } - for (Map<Vector2, uint16_t>::Element *E = tileset->autotile_get_bitmask_map(get_current_tile()).front(); E; E = E->next()) { + for (Map<Vector2, uint32_t>::Element *E = tileset->autotile_get_bitmask_map(get_current_tile()).front(); E; E = E->next()) { undo_redo->add_undo_method(tileset.ptr(), "autotile_set_bitmask", get_current_tile(), E->key(), E->value()); } undo_redo->add_do_method(workspace, "update"); @@ -1541,7 +1628,7 @@ void TileSetEditor::_on_tool_clicked(int p_tool) { } else if (p_tool == BITMASK_CLEAR) { undo_redo->create_action(TTR("Clear Tile Bitmask")); undo_redo->add_do_method(tileset.ptr(), "autotile_clear_bitmask_map", get_current_tile()); - for (Map<Vector2, uint16_t>::Element *E = tileset->autotile_get_bitmask_map(get_current_tile()).front(); E; E = E->next()) { + for (Map<Vector2, uint32_t>::Element *E = tileset->autotile_get_bitmask_map(get_current_tile()).front(); E; E = E->next()) { undo_redo->add_undo_method(tileset.ptr(), "autotile_set_bitmask", get_current_tile(), E->key(), E->value()); } undo_redo->add_do_method(workspace, "update"); @@ -1670,7 +1757,8 @@ void TileSetEditor::_on_tool_clicked(int p_tool) { undo_redo->commit_action(); } } break; - default: {} + default: { + } } } } else if (p_tool == TOOL_SELECT) { @@ -1840,7 +1928,8 @@ void TileSetEditor::_select_next_tile() { edited_shape_coord = Vector2(); _select_edited_shape_coord(); } break; - default: {} + default: { + } } } } @@ -1877,7 +1966,8 @@ void TileSetEditor::_select_previous_tile() { edited_shape_coord = cell_count; _select_edited_shape_coord(); } break; - default: {} + default: { + } } } } @@ -2139,8 +2229,8 @@ void TileSetEditor::_undo_tile_removal(int p_id) { for (Map<Vector2, Ref<NavigationPolygon> >::Element *E = navigation_map.front(); E; E = E->next()) { undo_redo->add_undo_method(tileset.ptr(), "autotile_set_navigation_polygon", p_id, E->value(), E->key()); } - Map<Vector2, uint16_t> bitmask_map = tileset->autotile_get_bitmask_map(p_id); - for (Map<Vector2, uint16_t>::Element *E = bitmask_map.front(); E; E = E->next()) { + Map<Vector2, uint32_t> bitmask_map = tileset->autotile_get_bitmask_map(p_id); + for (Map<Vector2, uint32_t>::Element *E = bitmask_map.front(); E; E = E->next()) { undo_redo->add_undo_method(tileset.ptr(), "autotile_set_bitmask", p_id, E->key(), E->value()); } Map<Vector2, int> priority_map = tileset->autotile_get_priority_map(p_id); @@ -2417,9 +2507,11 @@ void TileSetEditor::draw_polygon_shapes() { colors.push_back(c_bg); } } - if (polygon.size() > 2) { - workspace->draw_polygon(polygon, colors); - } + + if (polygon.size() < 3) + continue; + + workspace->draw_polygon(polygon, colors); if (coord == edited_shape_coord || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) { if (!creating_shape) { @@ -2612,7 +2704,8 @@ void TileSetEditor::draw_polygon_shapes() { } } } break; - default: {} + default: { + } } if (creating_shape) { diff --git a/editor/plugins/tile_set_editor_plugin.h b/editor/plugins/tile_set_editor_plugin.h index 2827964592..1176e1bb92 100644 --- a/editor/plugins/tile_set_editor_plugin.h +++ b/editor/plugins/tile_set_editor_plugin.h @@ -126,7 +126,7 @@ class TileSetEditor : public HSplitContainer { Vector2 edited_shape_coord; PoolVector2Array current_shape; Map<Vector2i, SubtileData> current_tile_data; - Map<Vector2, uint16_t> bitmask_map_copy; + Map<Vector2, uint32_t> bitmask_map_copy; Vector2 snap_step; Vector2 snap_offset; diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 7bd26de092..cd86c3890d 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -39,6 +39,7 @@ #include "scene/gui/menu_button.h" #include "scene/gui/panel.h" #include "scene/main/viewport.h" +#include "scene/resources/visual_shader_nodes.h" Control *VisualShaderNodePlugin::create_editor(const Ref<VisualShaderNode> &p_node) { @@ -57,7 +58,9 @@ void VisualShaderNodePlugin::_bind_methods() { void VisualShaderEditor::edit(VisualShader *p_visual_shader) { + bool first_init = false; if (p_visual_shader) { + first_init = true; visual_shader = Ref<VisualShader>(p_visual_shader); } else { visual_shader.unref(); @@ -66,6 +69,9 @@ void VisualShaderEditor::edit(VisualShader *p_visual_shader) { if (visual_shader.is_null()) { hide(); } else { + if (first_init) { // to avoid tree collapse + _update_options_menu(); + } _update_graph(); } } @@ -107,16 +113,187 @@ void VisualShaderEditor::remove_custom_type(const Ref<Script> &p_script) { _update_options_menu(); } +bool VisualShaderEditor::_is_available(int p_mode) { + + int current_mode = edit_type->get_selected(); + + if (p_mode != -1) { + + switch (current_mode) { + case VisualShader::TYPE_VERTEX: + current_mode = 1; + break; + case VisualShader::TYPE_FRAGMENT: + current_mode = 2; + break; + case VisualShader::TYPE_LIGHT: + current_mode = 4; + break; + default: + break; + } + + int temp_mode = 0; + + if (p_mode & VisualShader::TYPE_FRAGMENT) { + temp_mode |= 2; + } + + if (p_mode & VisualShader::TYPE_LIGHT) { + temp_mode |= 4; + } + + if (temp_mode == 0) { + temp_mode |= 1; + } + + p_mode = temp_mode; + } + + if (p_mode != -1 && ((p_mode & current_mode) == 0)) { + return false; + } + return true; +} + void VisualShaderEditor::_update_options_menu() { + node_desc->set_text(""); + members_dialog->get_ok()->set_disabled(true); + String prev_category; - add_node->get_popup()->clear(); - for (int i = 0; i < add_options.size(); i++) { - if (prev_category != add_options[i].category) { - add_node->get_popup()->add_separator(add_options[i].category); + String prev_sub_category; + + members->clear(); + TreeItem *root = members->create_item(); + TreeItem *category = NULL; + TreeItem *sub_category = NULL; + + String filter = node_filter->get_text().strip_edges(); + bool use_filter = !filter.empty(); + + Vector<String> categories; + Vector<String> sub_categories; + + int item_count = 0; + int item_count2 = 0; + bool is_first_item = true; + + int current_func = -1; + + if (!visual_shader.is_null()) { + current_func = visual_shader->get_mode(); + } + + for (int i = 0; i < add_options.size() + 1; i++) { + + if (i == add_options.size()) { + if (sub_category != NULL && item_count2 == 0) { + memdelete(sub_category); + --item_count; + } + if (category != NULL && item_count == 0) { + memdelete(category); + } + break; + } + + if (!use_filter || add_options[i].name.findn(filter) != -1) { + + if (prev_category != add_options[i].category) { + if (category != NULL && item_count == 0) { + memdelete(category); + } + + item_count = 0; + 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); + } + + if (add_options[i].sub_category != "") { + if (prev_sub_category != add_options[i].sub_category) { + if (category != NULL) { + if (sub_category != NULL && item_count2 == 0) { + memdelete(sub_category); + --item_count; + } + ++item_count; + 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); + } + } + if (sub_category != NULL) { + if ((add_options[i].func == current_func || add_options[i].func == -1) && _is_available(add_options[i].mode)) { + ++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")); + break; + case VisualShaderNode::PORT_TYPE_VECTOR: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("Vector3", "EditorIcons")); + break; + case VisualShaderNode::PORT_TYPE_BOOLEAN: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("bool", "EditorIcons")); + break; + case VisualShaderNode::PORT_TYPE_TRANSFORM: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("Transform", "EditorIcons")); + break; + case VisualShaderNode::PORT_TYPE_COLOR: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("Color", "EditorIcons")); + break; + default: + break; + } + item->set_meta("id", i); + } + } + } else { + if (category != NULL) { + if ((add_options[i].func == current_func || add_options[i].func == -1) && _is_available(add_options[i].mode)) { + ++item_count; + TreeItem *item = members->create_item(category); + item->set_text(0, add_options[i].name); + 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")); + break; + case VisualShaderNode::PORT_TYPE_VECTOR: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("Vector3", "EditorIcons")); + break; + case VisualShaderNode::PORT_TYPE_BOOLEAN: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("bool", "EditorIcons")); + break; + case VisualShaderNode::PORT_TYPE_TRANSFORM: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("Transform", "EditorIcons")); + break; + case VisualShaderNode::PORT_TYPE_COLOR: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("Color", "EditorIcons")); + break; + default: + break; + } + item->set_meta("id", i); + } + } + } + + prev_sub_category = add_options[i].sub_category; + prev_category = add_options[i].category; } - add_node->get_popup()->add_item(add_options[i].name, i); - prev_category = add_options[i].category; } } @@ -144,6 +321,21 @@ static Ref<StyleBoxEmpty> make_empty_stylebox(float p_margin_left = -1, float p_ return style; } +void VisualShaderEditor::_update_created_node(GraphNode *node) { + + if (EditorSettings::get_singleton()->get("interface/theme/use_graph_node_headers")) { + Ref<StyleBoxFlat> sb = node->get_stylebox("frame", "GraphNode"); + Color c = sb->get_border_color(); + Color mono_color = ((c.r + c.g + c.b) / 3) < 0.7 ? Color(1.0, 1.0, 1.0) : Color(0.0, 0.0, 0.0); + mono_color.a = 0.85; + c = mono_color; + + node->add_color_override("title_color", c); + c.a = 0.7; + node->add_color_override("close_color", c); + } +} + void VisualShaderEditor::_update_graph() { if (updating) @@ -165,10 +357,11 @@ void VisualShaderEditor::_update_graph() { } } - static const Color type_color[3] = { - Color::html("#61daf4"), - Color::html("#d67dee"), - Color::html("#f6a86e") + static const Color type_color[4] = { + Color::html("#61daf4"), // scalar + Color::html("#d67dee"), // vector + Color::html("#8da6f0"), // boolean + Color::html("#f6a86e") // transform }; List<VisualShader::Connection> connections; @@ -184,7 +377,6 @@ void VisualShaderEditor::_update_graph() { Ref<VisualShaderNode> vsnode = visual_shader->get_node(type, nodes[n_i]); GraphNode *node = memnew(GraphNode); - graph->add_child(node); /*if (!vsnode->is_connected("changed", this, "_node_changed")) { vsnode->connect("changed", this, "_node_changed", varray(vsnode->get_instance_id()), CONNECT_DEFERRED); @@ -207,6 +399,9 @@ void VisualShaderEditor::_update_graph() { Ref<VisualShaderNodeUniform> uniform = vsnode; if (uniform.is_valid()) { + graph->add_child(node); + _update_created_node(node); + LineEdit *uniform_name = memnew(LineEdit); uniform_name->set_text(uniform->get_uniform_name()); node->add_child(uniform_name); @@ -293,7 +488,8 @@ void VisualShaderEditor::_update_graph() { Vector3 v = default_value; button->set_text(String::num(v.x, 3) + "," + String::num(v.y, 3) + "," + String::num(v.z, 3)); } break; - default: {} + default: { + } } } @@ -323,7 +519,7 @@ void VisualShaderEditor::_update_graph() { } } - if (valid_right && edit_type->get_selected() == VisualShader::TYPE_FRAGMENT) { + if (valid_right && edit_type->get_selected() == VisualShader::TYPE_FRAGMENT && port_right != VisualShaderNode::PORT_TYPE_TRANSFORM) { TextureButton *preview = memnew(TextureButton); preview->set_toggle_mode(true); preview->set_normal_texture(get_icon("GuiVisibilityHidden", "EditorIcons")); @@ -341,21 +537,9 @@ void VisualShaderEditor::_update_graph() { node->add_child(hb); node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], valid_right, port_right, type_color[port_right]); - - if (EditorSettings::get_singleton()->get("interface/theme/use_graph_node_headers")) { - Ref<StyleBoxFlat> sb = node->get_stylebox("frame", "GraphNode"); - Color c = sb->get_border_color(MARGIN_TOP); - Color mono_color = ((c.r + c.g + c.b) / 3) < 0.7 ? Color(1.0, 1.0, 1.0) : Color(0.0, 0.0, 0.0); - mono_color.a = 0.85; - c = mono_color; - - node->add_color_override("title_color", c); - c.a = 0.7; - node->add_color_override("close_color", c); - } } - if (vsnode->get_output_port_for_preview() >= 0) { + if (vsnode->get_output_port_for_preview() >= 0 && vsnode->get_output_port_type(vsnode->get_output_port_for_preview()) != VisualShaderNode::PORT_TYPE_TRANSFORM) { VisualShaderNodePortPreview *port_preview = memnew(VisualShaderNodePortPreview); port_preview->setup(visual_shader, type, nodes[n_i], vsnode->get_output_port_for_preview()); port_preview->set_h_size_flags(SIZE_SHRINK_CENTER); @@ -369,6 +553,11 @@ void VisualShaderEditor::_update_graph() { error_label->set_text(error); node->add_child(error_label); } + + if (!uniform.is_valid()) { + graph->add_child(node); + _update_created_node(node); + } } for (List<VisualShader::Connection>::Element *E = connections.front(); E; E = E->next()) { @@ -462,7 +651,7 @@ void VisualShaderEditor::_edit_port_default_input(Object *p_button, int p_node, editing_port = p_port; } -void VisualShaderEditor::_add_node(int p_idx) { +void VisualShaderEditor::_add_node(int p_idx, int p_op_idx) { ERR_FAIL_INDEX(p_idx, add_options.size()); @@ -471,6 +660,70 @@ void VisualShaderEditor::_add_node(int p_idx) { if (add_options[p_idx].type != String()) { VisualShaderNode *vsn = Object::cast_to<VisualShaderNode>(ClassDB::instance(add_options[p_idx].type)); ERR_FAIL_COND(!vsn); + + if (p_op_idx != -1) { + + VisualShaderNodeInput *input = Object::cast_to<VisualShaderNodeInput>(vsn); + + if (input) { + input->set_input_name(add_options[p_idx].sub_func_str); + } + + VisualShaderNodeColorOp *colorOp = Object::cast_to<VisualShaderNodeColorOp>(vsn); + + if (colorOp) { + colorOp->set_operator((VisualShaderNodeColorOp::Operator)p_op_idx); + } + + VisualShaderNodeColorFunc *colorFunc = Object::cast_to<VisualShaderNodeColorFunc>(vsn); + + if (colorFunc) { + colorFunc->set_function((VisualShaderNodeColorFunc::Function)p_op_idx); + } + + VisualShaderNodeScalarOp *scalarOp = Object::cast_to<VisualShaderNodeScalarOp>(vsn); + + if (scalarOp) { + scalarOp->set_operator((VisualShaderNodeScalarOp::Operator)p_op_idx); + } + + VisualShaderNodeScalarFunc *scalarFunc = Object::cast_to<VisualShaderNodeScalarFunc>(vsn); + + if (scalarFunc) { + scalarFunc->set_function((VisualShaderNodeScalarFunc::Function)p_op_idx); + } + + VisualShaderNodeVectorOp *vecOp = Object::cast_to<VisualShaderNodeVectorOp>(vsn); + + if (vecOp) { + vecOp->set_operator((VisualShaderNodeVectorOp::Operator)p_op_idx); + } + + VisualShaderNodeVectorFunc *vecFunc = Object::cast_to<VisualShaderNodeVectorFunc>(vsn); + + if (vecFunc) { + vecFunc->set_function((VisualShaderNodeVectorFunc::Function)p_op_idx); + } + + VisualShaderNodeTransformFunc *matFunc = Object::cast_to<VisualShaderNodeTransformFunc>(vsn); + + if (matFunc) { + matFunc->set_function((VisualShaderNodeTransformFunc::Function)p_op_idx); + } + + VisualShaderNodeScalarDerivativeFunc *sderFunc = Object::cast_to<VisualShaderNodeScalarDerivativeFunc>(vsn); + + if (sderFunc) { + sderFunc->set_function((VisualShaderNodeScalarDerivativeFunc::Function)p_op_idx); + } + + VisualShaderNodeVectorDerivativeFunc *vderFunc = Object::cast_to<VisualShaderNodeVectorDerivativeFunc>(vsn); + + if (vderFunc) { + vderFunc->set_function((VisualShaderNodeVectorDerivativeFunc::Function)p_op_idx); + } + } + vsnode = Ref<VisualShaderNode>(vsn); } else { ERR_FAIL_COND(add_options[p_idx].script.is_null()); @@ -481,7 +734,15 @@ void VisualShaderEditor::_add_node(int p_idx) { vsnode->set_script(add_options[p_idx].script.get_ref_ptr()); } - Point2 position = (graph->get_scroll_ofs() + graph->get_size() * 0.5) / EDSCALE; + Point2 position = graph->get_scroll_ofs(); + + if (saved_node_pos_dirty) { + position += saved_node_pos; + } else { + position += graph->get_size() * 0.5; + position /= EDSCALE; + } + saved_node_pos_dirty = false; VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); @@ -517,7 +778,6 @@ void VisualShaderEditor::_connection_request(const String &p_from, int p_from_in int to = p_to.to_int(); if (!visual_shader->can_connect_nodes(type, from, p_from_index, to, p_to_index)) { - EditorNode::get_singleton()->show_warning(TTR("Unable to connect, port may be in use or connection may be invalid.")); return; } @@ -600,29 +860,98 @@ void VisualShaderEditor::_node_selected(Object *p_node) { //EditorNode::get_singleton()->push_item(vsnode.ptr(), "", true); } -void VisualShaderEditor::_input(const Ref<InputEvent> p_event) { - if (graph->has_focus()) { - Ref<InputEventMouseButton> mb = p_event; +void VisualShaderEditor::_graph_gui_input(const Ref<InputEvent> p_event) { - if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_RIGHT) { - add_node->get_popup()->set_position(get_viewport()->get_mouse_position()); - add_node->get_popup()->show_modal(); - } + Ref<InputEventMouseButton> mb = p_event; + + if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_RIGHT) + _show_members_dialog(true); +} + +void VisualShaderEditor::_show_members_dialog(bool at_mouse_pos) { + + members_dialog->popup(); + + 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::_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) { + if (p_what == NOTIFICATION_ENTER_TREE) { + + node_filter->set_clear_button_enabled(true); + + // collapse tree by default + + TreeItem *category = members->get_root()->get_children(); + while (category) { + category->set_collapsed(true); + TreeItem *sub_category = category->get_children(); + while (sub_category) { + sub_category->set_collapsed(true); + sub_category = sub_category->get_next(); + } + category = category->get_next(); + } + } + + if (p_what == NOTIFICATION_DRAG_BEGIN) { + Dictionary dd = get_viewport()->gui_get_drag_data(); + if (members->is_visible_in_tree() && dd.has("id")) { + members->set_drop_mode_flags(Tree::DROP_MODE_ON_ITEM); + } + } else if (p_what == NOTIFICATION_DRAG_END) { + members->set_drop_mode_flags(0); + } if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { error_panel->add_style_override("panel", get_stylebox("bg", "Tree")); error_label->add_color_override("font_color", get_color("error_color", "Editor")); + node_filter->set_right_icon(Control::get_icon("Search", "EditorIcons")); + + tools->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Tools", "EditorIcons")); + if (p_what == NOTIFICATION_THEME_CHANGED && is_visible_in_tree()) _update_graph(); - } - - if (p_what == NOTIFICATION_PROCESS) { + } else if (p_what == NOTIFICATION_PROCESS) { } } @@ -718,7 +1047,60 @@ 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(); } @@ -756,9 +1138,131 @@ void VisualShaderEditor::_input_select_item(Ref<VisualShaderNodeInput> input, St undo_redo->commit_action(); } -void VisualShaderEditor::_bind_methods() { +void VisualShaderEditor::_member_filter_changed(const String &p_text) { + _update_options_menu(); +} + +void VisualShaderEditor::_member_selected() { + TreeItem *item = members->get_selected(); + + if (item != NULL && item->has_meta("id")) { + members_dialog->get_ok()->set_disabled(false); + node_desc->set_text(add_options[item->get_meta("id")].description); + } else { + members_dialog->get_ok()->set_disabled(true); + node_desc->set_text(""); + } +} + +void VisualShaderEditor::_member_unselected() { +} + +void VisualShaderEditor::_member_create() { + TreeItem *item = members->get_selected(); + if (item != NULL && item->has_meta("id")) { + int idx = members->get_selected()->get_meta("id"); + _add_node(idx, add_options[idx].sub_func); + members_dialog->hide(); + } +} +void VisualShaderEditor::_tools_menu_option(int p_idx) { + + TreeItem *category = members->get_root()->get_children(); + + switch (p_idx) { + case EXPAND_ALL: + + while (category) { + category->set_collapsed(false); + TreeItem *sub_category = category->get_children(); + while (sub_category) { + sub_category->set_collapsed(false); + sub_category = sub_category->get_next(); + } + category = category->get_next(); + } + + break; + + case COLLAPSE_ALL: + + while (category) { + category->set_collapsed(true); + TreeItem *sub_category = category->get_children(); + while (sub_category) { + sub_category->set_collapsed(true); + sub_category = sub_category->get_next(); + } + category = category->get_next(); + } + + break; + default: + break; + } +} + +Variant VisualShaderEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) { + + if (p_from == members) { + TreeItem *it = members->get_item_at_position(p_point); + if (!it) + return Variant(); + if (!it->has_meta("id")) + return Variant(); + + int id = it->get_meta("id"); + AddOption op = add_options[id]; + + Dictionary d; + d["id"] = id; + if (op.sub_func == -1) { + d["sub_func"] = op.sub_func_str; + } else { + d["sub_func"] = op.sub_func; + } + + Label *label = memnew(Label); + label->set_text(it->get_text(0)); + set_drag_preview(label); + return d; + } + return Variant(); +} + +bool VisualShaderEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { + + if (p_from == graph) { + + Dictionary d = p_data; + + if (d.has("id")) { + return true; + } + } + + return false; +} + +void VisualShaderEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) { + + if (p_from == graph) { + + Dictionary d = p_data; + + if (d.has("id")) { + int idx = d["id"]; + saved_node_pos = p_point; + saved_node_pos_dirty = true; + _add_node(idx, add_options[idx].sub_func); + } + } +} + +void VisualShaderEditor::_bind_methods() { ClassDB::bind_method("_update_graph", &VisualShaderEditor::_update_graph); + ClassDB::bind_method("_update_options_menu", &VisualShaderEditor::_update_options_menu); ClassDB::bind_method("_add_node", &VisualShaderEditor::_add_node); ClassDB::bind_method("_node_dragged", &VisualShaderEditor::_node_dragged); ClassDB::bind_method("_connection_request", &VisualShaderEditor::_connection_request); @@ -766,6 +1270,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); @@ -776,7 +1281,20 @@ 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); + ClassDB::bind_method(D_METHOD("drop_data_fw"), &VisualShaderEditor::drop_data_fw); + + 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("_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); + ClassDB::bind_method("_member_create", &VisualShaderEditor::_member_create); } VisualShaderEditor *VisualShaderEditor::singleton = NULL; @@ -785,10 +1303,14 @@ VisualShaderEditor::VisualShaderEditor() { singleton = this; updating = false; + saved_node_pos_dirty = false; + saved_node_pos = Point2(0, 0); graph = memnew(GraphEdit); add_child(graph); + graph->set_drag_forwarding(this); graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_SCALAR); + graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_BOOLEAN); graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_VECTOR); graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_TRANSFORM); //graph->add_valid_left_disconnect_type(0); @@ -798,10 +1320,17 @@ 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); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_SCALAR); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_VECTOR); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_BOOLEAN); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_BOOLEAN, VisualShaderNode::PORT_TYPE_SCALAR); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_BOOLEAN, VisualShaderNode::PORT_TYPE_VECTOR); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_BOOLEAN, VisualShaderNode::PORT_TYPE_BOOLEAN); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_TRANSFORM, VisualShaderNode::PORT_TYPE_TRANSFORM); VSeparator *vs = memnew(VSeparator); @@ -817,40 +1346,373 @@ VisualShaderEditor::VisualShaderEditor() { graph->get_zoom_hbox()->add_child(edit_type); graph->get_zoom_hbox()->move_child(edit_type, 0); - add_node = memnew(MenuButton); + add_node = memnew(ToolButton); 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->get_popup()->connect("id_pressed", this, "_add_node"); - - add_options.push_back(AddOption("Scalar", "Constants", "VisualShaderNodeScalarConstant")); - add_options.push_back(AddOption("Vector", "Constants", "VisualShaderNodeVec3Constant")); - add_options.push_back(AddOption("Color", "Constants", "VisualShaderNodeColorConstant")); - add_options.push_back(AddOption("Transform", "Constants", "VisualShaderNodeTransformConstant")); - add_options.push_back(AddOption("Texture", "Constants", "VisualShaderNodeTexture")); - add_options.push_back(AddOption("CubeMap", "Constants", "VisualShaderNodeCubeMap")); - add_options.push_back(AddOption("ScalarOp", "Operators", "VisualShaderNodeScalarOp")); - add_options.push_back(AddOption("VectorOp", "Operators", "VisualShaderNodeVectorOp")); - add_options.push_back(AddOption("ColorOp", "Operators", "VisualShaderNodeColorOp")); - add_options.push_back(AddOption("TransformMult", "Operators", "VisualShaderNodeTransformMult")); - add_options.push_back(AddOption("TransformVectorMult", "Operators", "VisualShaderNodeTransformVecMult")); - add_options.push_back(AddOption("ScalarFunc", "Functions", "VisualShaderNodeScalarFunc")); - add_options.push_back(AddOption("VectorFunc", "Functions", "VisualShaderNodeVectorFunc")); - add_options.push_back(AddOption("DotProduct", "Functions", "VisualShaderNodeDotProduct")); - add_options.push_back(AddOption("VectorLen", "Functions", "VisualShaderNodeVectorLen")); - add_options.push_back(AddOption("ScalarInterp", "Interpolation", "VisualShaderNodeScalarInterp")); - add_options.push_back(AddOption("VectorInterp", "Interpolation", "VisualShaderNodeVectorInterp")); - add_options.push_back(AddOption("VectorCompose", "Compose", "VisualShaderNodeVectorCompose")); - add_options.push_back(AddOption("TransformCompose", "Compose", "VisualShaderNodeTransformCompose")); - add_options.push_back(AddOption("VectorDecompose", "Decompose", "VisualShaderNodeVectorDecompose")); - add_options.push_back(AddOption("TransformDecompose", "Decompose", "VisualShaderNodeTransformDecompose")); - add_options.push_back(AddOption("Scalar", "Uniforms", "VisualShaderNodeScalarUniform")); - add_options.push_back(AddOption("Vector", "Uniforms", "VisualShaderNodeVec3Uniform")); - add_options.push_back(AddOption("Color", "Uniforms", "VisualShaderNodeColorUniform")); - add_options.push_back(AddOption("Transform", "Uniforms", "VisualShaderNodeTransformUniform")); - add_options.push_back(AddOption("Texture", "Uniforms", "VisualShaderNodeTextureUniform")); - add_options.push_back(AddOption("CubeMap", "Uniforms", "VisualShaderNodeCubeMapUniform")); - add_options.push_back(AddOption("Input", "Inputs", "VisualShaderNodeInput")); + add_node->connect("pressed", this, "_show_members_dialog", varray(false)); + + /////////////////////////////////////// + // SHADER NODES TREE + /////////////////////////////////////// + + VBoxContainer *members_vb = memnew(VBoxContainer); + members_vb->set_v_size_flags(SIZE_EXPAND_FILL); + + HBoxContainer *filter_hb = memnew(HBoxContainer); + members_vb->add_child(filter_hb); + + 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")); + + tools = memnew(MenuButton); + filter_hb->add_child(tools); + tools->set_tooltip(TTR("Options")); + tools->get_popup()->connect("id_pressed", this, "_tools_menu_option"); + tools->get_popup()->add_item(TTR("Expand All"), EXPAND_ALL); + tools->get_popup()->add_item(TTR("Collapse All"), COLLAPSE_ALL); + + members = memnew(Tree); + members_vb->add_child(members); + members->set_drag_forwarding(this); + members->set_h_size_flags(SIZE_EXPAND_FILL); + members->set_v_size_flags(SIZE_EXPAND_FILL); + members->set_hide_root(true); + 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"); + + Label *desc_label = memnew(Label); + members_vb->add_child(desc_label); + desc_label->set_text(TTR("Description:")); + + node_desc = memnew(RichTextLabel); + members_vb->add_child(node_desc); + node_desc->set_h_size_flags(SIZE_EXPAND_FILL); + node_desc->set_v_size_flags(SIZE_FILL); + node_desc->set_custom_minimum_size(Size2(0, 70 * EDSCALE)); + + members_dialog = memnew(ConfirmationDialog); + members_dialog->set_title(TTR("Create Shader Node")); + members_dialog->add_child(members_vb); + members_dialog->get_ok()->set_text(TTR("Create")); + members_dialog->get_ok()->connect("pressed", this, "_member_create"); + members_dialog->get_ok()->set_disabled(true); + members_dialog->set_resizable(true); + members_dialog->set_as_minsize(); + add_child(members_dialog); + + alert = memnew(AcceptDialog); + alert->set_as_minsize(); + alert->get_label()->set_autowrap(true); + alert->get_label()->set_align(Label::ALIGN_CENTER); + alert->get_label()->set_valign(Label::VALIGN_CENTER); + alert->get_label()->set_custom_minimum_size(Size2(400, 60) * EDSCALE); + add_child(alert); + + /////////////////////////////////////// + // SHADER NODES TREE OPTIONS + /////////////////////////////////////// + + // COLOR + + add_options.push_back(AddOption("ColorFunc", "Color", "Common", "VisualShaderNodeColorFunc", TTR("Color function."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("ColorOp", "Color", "Common", "VisualShaderNodeColorOp", TTR("Color operator."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + + add_options.push_back(AddOption("Grayscale", "Color", "Functions", "VisualShaderNodeColorFunc", TTR("Grayscale function."), VisualShaderNodeColorFunc::FUNC_GRAYSCALE, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("HSV2RGB", "Color", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts HSV vector to RGB equivalent."), VisualShaderNodeVectorFunc::FUNC_HSV2RGB, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("RGB2HSV", "Color", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts RGB vector to HSV equivalent."), VisualShaderNodeVectorFunc::FUNC_RGB2HSV, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Sepia", "Color", "Functions", "VisualShaderNodeColorFunc", TTR("Sepia function."), VisualShaderNodeColorFunc::FUNC_SEPIA, VisualShaderNode::PORT_TYPE_VECTOR)); + + add_options.push_back(AddOption("Burn", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Burn operator."), VisualShaderNodeColorOp::OP_BURN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Darken", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Darken operator."), VisualShaderNodeColorOp::OP_DARKEN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Difference", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Difference operator."), VisualShaderNodeColorOp::OP_DIFFERENCE, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Dodge", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Dodge operator."), VisualShaderNodeColorOp::OP_DODGE, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("HardLight", "Color", "Operators", "VisualShaderNodeColorOp", TTR("HardLight operator"), VisualShaderNodeColorOp::OP_HARD_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Lighten", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Lighten operator."), VisualShaderNodeColorOp::OP_LIGHTEN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Overlay", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Overlay operator."), VisualShaderNodeColorOp::OP_OVERLAY, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Screen", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Screen operator."), VisualShaderNodeColorOp::OP_SCREEN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("SoftLight", "Color", "Operators", "VisualShaderNodeColorOp", TTR("SoftLight operator."), VisualShaderNodeColorOp::OP_SOFT_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR)); + + add_options.push_back(AddOption("ColorConstant", "Color", "Variables", "VisualShaderNodeColorConstant", TTR("Color constant."), -1, VisualShaderNode::PORT_TYPE_COLOR)); + add_options.push_back(AddOption("ColorUniform", "Color", "Variables", "VisualShaderNodeColorUniform", TTR("Color uniform."), -1, VisualShaderNode::PORT_TYPE_COLOR)); + + // 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 + + // SPATIAL-FOR-ALL + + add_options.push_back(AddOption("Camera", "Input", "All", "VisualShaderNodeInput", TTR("'camera' input parameter for all shader modes."), "camera", VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("InvCamera", "Input", "All", "VisualShaderNodeInput", TTR("'inv_camera' input parameter for all shader modes."), "inv_camera", VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("InvProjection", "Input", "All", "VisualShaderNodeInput", TTR("'inv_projection' input parameter for all shader modes."), "inv_projection", VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("Normal", "Input", "All", "VisualShaderNodeInput", TTR("'normal' input parameter for all shader modes."), "normal", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("Projection", "Input", "All", "VisualShaderNodeInput", TTR("'projection' input parameter for all shader modes."), "projection", VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("Time", "Input", "All", "VisualShaderNodeInput", TTR("'time' input parameter for all shader modes."), "time", VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("ViewportSize", "Input", "All", "VisualShaderNodeInput", TTR("'viewport_size' input parameter for all shader modes."), "viewport_size", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("World", "Input", "All", "VisualShaderNodeInput", TTR("'world' input parameter for all shader modes."), "world", VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::Mode::MODE_SPATIAL)); + + // CANVASITEM-FOR-ALL + + add_options.push_back(AddOption("Alpha", "Input", "All", "VisualShaderNodeInput", TTR("'alpha' input parameter for all shader modes."), "alpha", VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::Mode::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("Color", "Input", "All", "VisualShaderNodeInput", TTR("'color' input parameter for all shader modes."), "color", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::Mode::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("TexturePixelSize", "Input", "All", "VisualShaderNodeInput", TTR("'texture_pixel_size' input parameter for all shader modes."), "texture_pixel_size", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::Mode::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("Time", "Input", "All", "VisualShaderNodeInput", TTR("'time' input parameter for all shader modes."), "time", VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::Mode::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("UV", "Input", "All", "VisualShaderNodeInput", TTR("'uv' input parameter for all shader modes."), "uv", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::Mode::MODE_CANVAS_ITEM)); + + ///////////////// + + add_options.push_back(AddOption("Input", "Input", "Common", "VisualShaderNodeInput", TTR("Input parameter."))); + + // SPATIAL INPUTS + + add_options.push_back(AddOption("Alpha", "Input", "Fragment", "VisualShaderNodeInput", TTR("'alpha' input parameter for vertex and fragment shader modes."), "alpha", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_FRAGMENT, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("Binormal", "Input", "Fragment", "VisualShaderNodeInput", TTR("'binormal' input parameter for vertex and fragment shader modes."), "binormal", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("Color", "Input", "Fragment", "VisualShaderNodeInput", TTR("'color' input parameter for vertex and fragment shader modes."), "color", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("FragCoord", "Input", "Fragment", "VisualShaderNodeInput", TTR("'fragcoord' input parameter for fragment and light shader modes."), "fragcoord", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("PointCoord", "Input", "Fragment", "VisualShaderNodeInput", TTR("'point_coord' input parameter for fragment shader mode."), "point_coord", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("ScreenUV", "Input", "Fragment", "VisualShaderNodeInput", TTR("'screen_uv' input parameter for fragment shader mode."), "screen_uv", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("Side", "Input", "Fragment", "VisualShaderNodeInput", TTR("'side' input parameter for fragment shader mode."), "side", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_FRAGMENT, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("Tangent", "Input", "Fragment", "VisualShaderNodeInput", TTR("'tangent' input parameter for vertex and fragment shader modes."), "tangent", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("UV", "Input", "Fragment", "VisualShaderNodeInput", TTR("'uv' input parameter for vertex and fragment shader modes."), "uv", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("UV2", "Input", "Fragment", "VisualShaderNodeInput", TTR("'uv2' input parameter for vertex and fragment shader modes."), "uv2", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("Vertex", "Input", "Fragment", "VisualShaderNodeInput", TTR("'vertex' input parameter for vertex and fragment shader modes."), "vertex", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("View", "Input", "Fragment", "VisualShaderNodeInput", TTR("'view' input parameter for fragment and light shader modes."), "view", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT, Shader::Mode::MODE_SPATIAL)); + + add_options.push_back(AddOption("Albedo", "Input", "Light", "VisualShaderNodeInput", TTR("'albedo' input parameter for light shader mode."), "albedo", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("Attenuation", "Input", "Light", "VisualShaderNodeInput", TTR("'attenuation' input parameter for light shader mode."), "attenuation", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("Diffuse", "Input", "Light", "VisualShaderNodeInput", TTR("'diffuse' input parameter for light shader mode."), "diffuse", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("FragCoord", "Input", "Light", "VisualShaderNodeInput", TTR("'fragcoord' input parameter for fragment and light shader modes."), "fragcoord", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("Light", "Input", "Light", "VisualShaderNodeInput", TTR("'light' input parameter for light shader mode."), "light", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("LightColor", "Input", "Light", "VisualShaderNodeInput", TTR("'light_color' input parameter for light shader mode."), "light_color", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("Roughness", "Input", "Light", "VisualShaderNodeInput", TTR("'roughness' input parameter for light shader mode."), "roughness", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_LIGHT, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("Specular", "Input", "Light", "VisualShaderNodeInput", TTR("'specular' input parameter for light shader mode."), "specular", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("Transmission", "Input", "Light", "VisualShaderNodeInput", TTR("'transmission' input parameter for light shader mode."), "transmission", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("View", "Input", "Light", "VisualShaderNodeInput", TTR("'view' input parameter for fragment and light shader modes."), "view", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::Mode::MODE_SPATIAL)); + + add_options.push_back(AddOption("Alpha", "Input", "Vertex", "VisualShaderNodeInput", TTR("'alpha' input parameter for vertex and fragment shader modes."), "alpha", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("Binormal", "Input", "Vertex", "VisualShaderNodeInput", TTR("'binormal' input parameter for vertex and fragment shader modes."), "binormal", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_VERTEX, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("Color", "Input", "Vertex", "VisualShaderNodeInput", TTR("'color' input parameter for vertex and fragment shader modes."), "color", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_VERTEX, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("ModelView", "Input", "Vertex", "VisualShaderNodeInput", TTR("'modelview' input parameter for vertex shader mode."), "modelview", VisualShaderNode::PORT_TYPE_TRANSFORM, VisualShader::TYPE_VERTEX, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("PointSize", "Input", "Vertex", "VisualShaderNodeInput", TTR("'point_size' input parameter for vertex shader mode."), "point_size", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("Tangent", "Input", "Vertex", "VisualShaderNodeInput", TTR("'tangent' input parameter for vertex and fragment shader mode."), "tangent", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_VERTEX, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("UV", "Input", "Vertex", "VisualShaderNodeInput", TTR("'uv' input parameter for vertex and fragment shader modes."), "uv", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_VERTEX, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("UV2", "Input", "Vertex", "VisualShaderNodeInput", TTR("'uv2' input parameter for vertex and fragment shader modes."), "uv2", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_VERTEX, Shader::Mode::MODE_SPATIAL)); + add_options.push_back(AddOption("Vertex", "Input", "Vertex", "VisualShaderNodeInput", TTR("'vertex' input parameter for vertex and fragment shader modes."), "vertex", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_VERTEX, Shader::Mode::MODE_SPATIAL)); + + // CANVASITEM INPUTS + + add_options.push_back(AddOption("FragCoord", "Input", "Fragment", "VisualShaderNodeInput", TTR("'fragcoord' input parameter for fragment and light shader modes."), "fragcoord", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT, Shader::Mode::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("LightPass", "Input", "Fragment", "VisualShaderNodeInput", TTR("'light_pass' input parameter for vertex and fragment shader modes."), "light_pass", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_FRAGMENT, Shader::Mode::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("PointCoord", "Input", "Fragment", "VisualShaderNodeInput", TTR("'point_coord' input parameter for fragment and light shader modes."), "point_coord", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT, Shader::Mode::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("ScreenPixelSize", "Input", "Fragment", "VisualShaderNodeInput", TTR("'screen_pixel_size' input parameter for fragment shader mode."), "screen_pixel_size", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT, Shader::Mode::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("ScreenUV", "Input", "Fragment", "VisualShaderNodeInput", TTR("'screen_uv' input parameter for fragment and light shader modes."), "screen_uv", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT, Shader::Mode::MODE_CANVAS_ITEM)); + + add_options.push_back(AddOption("FragCoord", "Input", "Light", "VisualShaderNodeInput", TTR("'fragcoord' input parameter for fragment and light shader modes."), "fragcoord", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::Mode::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("LightAlpha", "Input", "Light", "VisualShaderNodeInput", TTR("'light_alpha' input parameter for light shader mode."), "light_alpha", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::Mode::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("LightColor", "Input", "Light", "VisualShaderNodeInput", TTR("'light_color' input parameter for light shader mode."), "light_color", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::Mode::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("LightHeight", "Input", "Light", "VisualShaderNodeInput", TTR("'light_height' input parameter for light shader mode."), "light_height", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_LIGHT, Shader::Mode::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("LightUV", "Input", "Light", "VisualShaderNodeInput", TTR("'light_uv' input parameter for light shader mode."), "light_uv", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::Mode::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("LightVector", "Input", "Light", "VisualShaderNodeInput", TTR("'light_vec' input parameter for light shader mode."), "light_vec", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::Mode::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("Normal", "Input", "Light", "VisualShaderNodeInput", TTR("'normal' input parameter for light shader mode."), "normal", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::Mode::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("PointCoord", "Input", "Light", "VisualShaderNodeInput", TTR("'point_coord' input parameter for fragment and light shader modes."), "point_coord", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::Mode::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("ScreenUV", "Input", "Light", "VisualShaderNodeInput", TTR("'screen_uv' input parameter for fragment and light shader modes."), "screen_uv", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::Mode::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("ShadowColor", "Input", "Light", "VisualShaderNodeInput", TTR("'shadow_color' input parameter for light shader mode."), "shadow_color", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::Mode::MODE_CANVAS_ITEM)); + + add_options.push_back(AddOption("Extra", "Input", "Vertex", "VisualShaderNodeInput", TTR("'extra' input parameter for vertex shader mode."), "extra", VisualShaderNode::PORT_TYPE_TRANSFORM, VisualShader::TYPE_VERTEX, Shader::Mode::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("LightPass", "Input", "Vertex", "VisualShaderNodeInput", TTR("'light_pass' input parameter for vertex and fragment shader modes."), "light_pass", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::Mode::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("PointSize", "Input", "Vertex", "VisualShaderNodeInput", TTR("'point_size' input parameter for vertex shader mode."), "point_size", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::Mode::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("Projection", "Input", "Vertex", "VisualShaderNodeInput", TTR("'projection' input parameter for vertex shader mode."), "projection", VisualShaderNode::PORT_TYPE_TRANSFORM, VisualShader::TYPE_VERTEX, Shader::Mode::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("Vertex", "Input", "Vertex", "VisualShaderNodeInput", TTR("'vertex' input parameter for vertex shader mode."), "vertex", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_VERTEX, Shader::Mode::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("World", "Input", "Vertex", "VisualShaderNodeInput", TTR("'world' input parameter for vertex shader mode."), "world", VisualShaderNode::PORT_TYPE_TRANSFORM, VisualShader::TYPE_VERTEX, Shader::Mode::MODE_CANVAS_ITEM)); + + // PARTICLES INPUTS + + add_options.push_back(AddOption("Active", "Input", "Vertex", "VisualShaderNodeInput", TTR("'active' input parameter for vertex shader mode."), "active", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::Mode::MODE_PARTICLES)); + add_options.push_back(AddOption("Alpha", "Input", "Vertex", "VisualShaderNodeInput", TTR("'alpha' input parameter for vertex shader mode."), "alpha", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::Mode::MODE_PARTICLES)); + add_options.push_back(AddOption("Color", "Input", "Vertex", "VisualShaderNodeInput", TTR("'color' input parameter for vertex shader mode."), "color", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_VERTEX, Shader::Mode::MODE_PARTICLES)); + add_options.push_back(AddOption("Custom", "Input", "Vertex", "VisualShaderNodeInput", TTR("'custom' input parameter for vertex shader mode."), "custom", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_VERTEX, Shader::Mode::MODE_PARTICLES)); + add_options.push_back(AddOption("CustomAlpha", "Input", "Vertex", "VisualShaderNodeInput", TTR("'custom_alpha' input parameter for vertex shader mode."), "custom_alpha", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::Mode::MODE_PARTICLES)); + add_options.push_back(AddOption("Delta", "Input", "Vertex", "VisualShaderNodeInput", TTR("'delta' input parameter for vertex shader mode."), "delta", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::Mode::MODE_PARTICLES)); + add_options.push_back(AddOption("EmissionTransform", "Input", "Vertex", "VisualShaderNodeInput", TTR("'emission_transform' input parameter for vertex shader mode."), "emission_transform", VisualShaderNode::PORT_TYPE_TRANSFORM, VisualShader::TYPE_VERTEX, Shader::Mode::MODE_PARTICLES)); + add_options.push_back(AddOption("Index", "Input", "Vertex", "VisualShaderNodeInput", TTR("'index' input parameter for vertex shader mode."), "index", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::Mode::MODE_PARTICLES)); + add_options.push_back(AddOption("LifeTime", "Input", "Vertex", "VisualShaderNodeInput", TTR("'lifetime' input parameter for vertex shader mode."), "lifetime", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::Mode::MODE_PARTICLES)); + add_options.push_back(AddOption("Restart", "Input", "Vertex", "VisualShaderNodeInput", TTR("'restart' input parameter for vertex shader mode."), "restart", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::Mode::MODE_PARTICLES)); + add_options.push_back(AddOption("Time", "Input", "Vertex", "VisualShaderNodeInput", TTR("'time' input parameter for vertex shader mode."), "time", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::Mode::MODE_PARTICLES)); + add_options.push_back(AddOption("Transform", "Input", "Vertex", "VisualShaderNodeInput", TTR("'transform' input parameter for vertex shader mode."), "transform", VisualShaderNode::PORT_TYPE_TRANSFORM, VisualShader::TYPE_VERTEX, Shader::Mode::MODE_PARTICLES)); + add_options.push_back(AddOption("Velocity", "Input", "Vertex", "VisualShaderNodeInput", TTR("'velocity' input parameter for vertex shader mode."), "velocity", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_VERTEX, Shader::Mode::MODE_PARTICLES)); + + // SCALAR + + add_options.push_back(AddOption("ScalarFunc", "Scalar", "Common", "VisualShaderNodeScalarFunc", TTR("Scalar function."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ScalarOp", "Scalar", "Common", "VisualShaderNodeScalarOp", TTR("Scalar operator."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + + add_options.push_back(AddOption("Abs", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the absolute value of the parameter."), VisualShaderNodeScalarFunc::FUNC_ABS, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ACos", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the arc-cosine of the parameter."), VisualShaderNodeScalarFunc::FUNC_ACOS, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ACosH", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("(GLES3 only) Returns the inverse hyperbolic cosine of the parameter."), VisualShaderNodeScalarFunc::FUNC_ACOSH, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ASin", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the arc-sine of the parameter."), VisualShaderNodeScalarFunc::FUNC_ASIN, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ASinH", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("(GLES3 only) Returns the inverse hyperbolic sine of the parameter."), VisualShaderNodeScalarFunc::FUNC_ASINH, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ATan", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the arc-tangent of the parameter."), VisualShaderNodeScalarFunc::FUNC_ATAN, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ATan2", "Scalar", "Functions", "VisualShaderNodeScalarOp", TTR("Returns the arc-tangent of the parameters."), VisualShaderNodeScalarOp::OP_ATAN2, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ATanH", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("(GLES3 only) Returns the inverse hyperbolic tangent of the parameter."), VisualShaderNodeScalarFunc::FUNC_ATANH, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Ceil", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Finds the nearest integer that is greater than or equal to the parameter."), VisualShaderNodeScalarFunc::FUNC_CEIL, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Clamp", "Scalar", "Functions", "VisualShaderNodeScalarClamp", TTR("Constrains a value to lie between two further values."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Cos", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the cosine of the parameter."), VisualShaderNodeScalarFunc::FUNC_COS, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("CosH", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("(GLES3 only) Returns the hyperbolic cosine of the parameter."), VisualShaderNodeScalarFunc::FUNC_COSH, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Degrees", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Converts a quantity in radians to degrees."), VisualShaderNodeScalarFunc::FUNC_DEGREES, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Exp", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Base-e Exponential."), VisualShaderNodeScalarFunc::FUNC_EXP, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Exp2", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Base-2 Exponential."), VisualShaderNodeScalarFunc::FUNC_EXP2, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Floor", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Finds the nearest integer less than or equal to the parameter."), VisualShaderNodeScalarFunc::FUNC_FLOOR, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Fract", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Computes the fractional part of the argument."), VisualShaderNodeScalarFunc::FUNC_FRAC, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("InverseSqrt", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the inverse of the square root of the parameter."), VisualShaderNodeScalarFunc::FUNC_INVERSE_SQRT, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Log", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Natural logarithm."), VisualShaderNodeScalarFunc::FUNC_LOG, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Log2", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Base-2 logarithm."), VisualShaderNodeScalarFunc::FUNC_LOG2, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Max", "Scalar", "Functions", "VisualShaderNodeScalarOp", TTR("Returns the greater of two values."), VisualShaderNodeScalarOp::OP_MAX, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Min", "Scalar", "Functions", "VisualShaderNodeScalarOp", TTR("Returns the lesser of two values."), VisualShaderNodeScalarOp::OP_MIN, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Mix", "Scalar", "Functions", "VisualShaderNodeScalarInterp", TTR("Linear interpolation between two scalars."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Negate", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the opposite value of the parameter."), VisualShaderNodeScalarFunc::FUNC_NEGATE, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Pow", "Scalar", "Functions", "VisualShaderNodeScalarOp", TTR("Returns the value of the first parameter raised to the power of the second."), VisualShaderNodeScalarOp::OP_POW, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Radians", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Converts a quantity in degrees to radians."), VisualShaderNodeScalarFunc::FUNC_RADIANS, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Reciprocal", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("1.0 / scalar"), VisualShaderNodeScalarFunc::FUNC_RECIPROCAL, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Round", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("(GLES3 only) Finds the nearest integer to the parameter."), VisualShaderNodeScalarFunc::FUNC_ROUND, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("RoundEven", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("(GLES3 only) Finds the nearest even integer to the parameter."), VisualShaderNodeScalarFunc::FUNC_ROUNDEVEN, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Saturate", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Clamps the value between 0.0 and 1.0."), VisualShaderNodeScalarFunc::FUNC_SATURATE, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Sign", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Extracts the sign of the parameter."), VisualShaderNodeScalarFunc::FUNC_SIGN, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Sin", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the sine of the parameter."), VisualShaderNodeScalarFunc::FUNC_SIN, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("SinH", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("(GLES3 only) Returns the hyperbolic sine of the parameter."), VisualShaderNodeScalarFunc::FUNC_SINH, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Sqrt", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the square root of the parameter."), VisualShaderNodeScalarFunc::FUNC_SQRT, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("SmoothStep", "Scalar", "Functions", "VisualShaderNodeScalarSmoothStep", TTR("SmoothStep function( scalar(edge0), scalar(edge1), scalar(x) ).\n\nReturns 0.0 if 'x' is smaller then 'edge0' and 1.0 if x is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Step", "Scalar", "Functions", "VisualShaderNodeScalarOp", TTR("Step function( scalar(edge), scalar(x) ).\n\nReturns 0.0 if 'x' is smaller then 'edge' and otherwise 1.0."), VisualShaderNodeScalarOp::OP_STEP, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Tan", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the tangent of the parameter."), VisualShaderNodeScalarFunc::FUNC_TAN, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("TanH", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("(GLES3 only) Returns the hyperbolic tangent of the parameter."), VisualShaderNodeScalarFunc::FUNC_TANH, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Trunc", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("(GLES3 only) Finds the truncated value of the parameter."), VisualShaderNodeScalarFunc::FUNC_TRUNC, VisualShaderNode::PORT_TYPE_SCALAR)); + + add_options.push_back(AddOption("Add", "Scalar", "Operators", "VisualShaderNodeScalarOp", TTR("Adds scalar to scalar."), VisualShaderNodeScalarOp::OP_ADD, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Divide", "Scalar", "Operators", "VisualShaderNodeScalarOp", TTR("Divides scalar by scalar."), VisualShaderNodeScalarOp::OP_DIV, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Multiply", "Scalar", "Operators", "VisualShaderNodeScalarOp", TTR("Multiplies scalar by scalar."), VisualShaderNodeScalarOp::OP_MUL, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Remainder", "Scalar", "Operators", "VisualShaderNodeScalarOp", TTR("Returns the remainder of the two scalars."), VisualShaderNodeScalarOp::OP_MOD, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Subtract", "Scalar", "Operators", "VisualShaderNodeScalarOp", TTR("Subtracts scalar from scalar."), VisualShaderNodeScalarOp::OP_SUB, VisualShaderNode::PORT_TYPE_SCALAR)); + + add_options.push_back(AddOption("ScalarConstant", "Scalar", "Variables", "VisualShaderNodeScalarConstant", TTR("Scalar constant."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ScalarUniform", "Scalar", "Variables", "VisualShaderNodeScalarUniform", TTR("Scalar uniform."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + + // TEXTURES + + add_options.push_back(AddOption("CubeMap", "Textures", "Functions", "VisualShaderNodeCubeMap", TTR("Perform the cubic texture lookup."), -1, VisualShaderNode::PORT_TYPE_COLOR)); + add_options.push_back(AddOption("Texture", "Textures", "Functions", "VisualShaderNodeTexture", TTR("Perform the texture lookup."), -1, VisualShaderNode::PORT_TYPE_COLOR)); + + add_options.push_back(AddOption("CubeMapUniform", "Textures", "Variables", "VisualShaderNodeCubeMapUniform", TTR("Cubic texture uniform."), -1, VisualShaderNode::PORT_TYPE_COLOR)); + add_options.push_back(AddOption("TextureUniform", "Textures", "Variables", "VisualShaderNodeTextureUniform", TTR("2D texture uniform."), -1, VisualShaderNode::PORT_TYPE_COLOR)); + + // TRANSFORM + + add_options.push_back(AddOption("TransformFunc", "Transform", "Common", "VisualShaderNodeTransformFunc", TTR("Transform function."), -1, VisualShaderNode::PORT_TYPE_TRANSFORM)); + + add_options.push_back(AddOption("OuterProduct", "Transform", "Composition", "VisualShaderNodeOuterProduct", TTR("(GLES3 only) Calculate the outer product of a pair of vectors.\n\nOuterProduct treats the first parameter 'c' as a column vector (matrix with one column) and the second parameter 'r' as a row vector (matrix with one row) and does a linear algebraic matrix multiply 'c * r', yielding a matrix whose number of rows is the number of components in 'c' and whose number of columns is the number of components in 'r'."), -1, VisualShaderNode::PORT_TYPE_TRANSFORM)); + add_options.push_back(AddOption("TransformCompose", "Transform", "Composition", "VisualShaderNodeTransformCompose", TTR("Composes transform from four vectors."), -1, VisualShaderNode::PORT_TYPE_TRANSFORM)); + add_options.push_back(AddOption("TransformDecompose", "Transform", "Composition", "VisualShaderNodeTransformDecompose", TTR("Decomposes transform to four vectors."))); + + add_options.push_back(AddOption("Determinant", "Transform", "Functions", "VisualShaderNodeDeterminant", TTR("(GLES3 only) Calculates the determinant of a transform."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Inverse", "Transform", "Functions", "VisualShaderNodeTransformFunc", TTR("(GLES3 only) Calculates the inverse of a transform."), VisualShaderNodeTransformFunc::FUNC_INVERSE, VisualShaderNode::PORT_TYPE_TRANSFORM)); + add_options.push_back(AddOption("Transpose", "Transform", "Functions", "VisualShaderNodeTransformFunc", TTR("(GLES3 only) Calculates the transpose of a transform."), VisualShaderNodeTransformFunc::FUNC_TRANSPOSE, VisualShaderNode::PORT_TYPE_TRANSFORM)); + + add_options.push_back(AddOption("TransformMult", "Transform", "Operators", "VisualShaderNodeTransformMult", TTR("Multiplies transform by transform."), -1, VisualShaderNode::PORT_TYPE_TRANSFORM)); + add_options.push_back(AddOption("TransformVectorMult", "Transform", "Operators", "VisualShaderNodeTransformVecMult", TTR("Multiplies vector by transform."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + + add_options.push_back(AddOption("TransformConstant", "Transform", "Variables", "VisualShaderNodeTransformConstant", TTR("Transform constant."), -1, VisualShaderNode::PORT_TYPE_TRANSFORM)); + add_options.push_back(AddOption("TransformUniform", "Transform", "Variables", "VisualShaderNodeTransformUniform", TTR("Transform uniform."), -1, VisualShaderNode::PORT_TYPE_TRANSFORM)); + + // VECTOR + + add_options.push_back(AddOption("VectorFunc", "Vector", "Common", "VisualShaderNodeVectorFunc", TTR("Vector function."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("VectorOp", "Vector", "Common", "VisualShaderNodeVectorOp", TTR("Vector operator."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + + add_options.push_back(AddOption("VectorCompose", "Vector", "Composition", "VisualShaderNodeVectorCompose", TTR("Composes vector from three scalars."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("VectorDecompose", "Vector", "Composition", "VisualShaderNodeVectorDecompose", TTR("Decomposes vector to three scalars."))); + + add_options.push_back(AddOption("Abs", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the absolute value of the parameter."), VisualShaderNodeVectorFunc::FUNC_ABS, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("ACos", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-cosine of the parameter."), VisualShaderNodeVectorFunc::FUNC_ACOS, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("ACosH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("(GLES3 only) Returns the inverse hyperbolic cosine of the parameter."), VisualShaderNodeVectorFunc::FUNC_ACOSH, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("ASin", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-sine of the parameter."), VisualShaderNodeVectorFunc::FUNC_ASIN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("ASinH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("(GLES3 only) Returns the inverse hyperbolic sine of the parameter."), VisualShaderNodeVectorFunc::FUNC_ASINH, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("ATan", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-tangent of the parameter."), VisualShaderNodeVectorFunc::FUNC_ATAN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("ATan2", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the arc-tangent of the parameters."), VisualShaderNodeVectorOp::OP_ATAN2, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("ATanH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("(GLES3 only) Returns the inverse hyperbolic tangent of the parameter."), VisualShaderNodeVectorFunc::FUNC_ATANH, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Ceil", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer that is greater than or equal to the parameter."), VisualShaderNodeVectorFunc::FUNC_CEIL, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Clamp", "Vector", "Functions", "VisualShaderNodeVectorClamp", TTR("Constrains a value to lie between two further values."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Cos", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the cosine of the parameter."), VisualShaderNodeVectorFunc::FUNC_COS, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("CosH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("(GLES3 only) Returns the hyperbolic cosine of the parameter."), VisualShaderNodeVectorFunc::FUNC_COSH, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Cross", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Calculates the cross product of two vectors."), VisualShaderNodeVectorOp::OP_CROSS, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Degrees", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in radians to degrees."), VisualShaderNodeVectorFunc::FUNC_DEGREES, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Distance", "Vector", "Functions", "VisualShaderNodeVectorDistance", TTR("Returns the distance between two points."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Dot", "Vector", "Functions", "VisualShaderNodeDotProduct", TTR("Calculates the dot product of two vectors."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Exp", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-e Exponential."), VisualShaderNodeVectorFunc::FUNC_EXP, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Exp2", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-2 Exponential."), VisualShaderNodeVectorFunc::FUNC_EXP2, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("FaceForward", "Vector", "Functions", "VisualShaderNodeFaceForward", TTR("Returns a vector that points in the same direction as a reference vector. The function has three vector parameters : N, the vector to orient, I, the incident vector, and Nref, the reference vector. If the dot product of I and Nref is smaller than zero the return value is N. Otherwise -N is returned."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Floor", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer less than or equal to the parameter."), VisualShaderNodeVectorFunc::FUNC_FLOOR, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Fract", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Computes the fractional part of the argument."), VisualShaderNodeVectorFunc::FUNC_FRAC, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("InverseSqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse of the square root of the parameter."), VisualShaderNodeVectorFunc::FUNC_INVERSE_SQRT, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Length", "Vector", "Functions", "VisualShaderNodeVectorLen", TTR("Calculates the length of a vector."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Log", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Natural logarithm."), VisualShaderNodeVectorFunc::FUNC_LOG, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Log2", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-2 logarithm."), VisualShaderNodeVectorFunc::FUNC_LOG2, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Max", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the greater of two values."), VisualShaderNodeVectorOp::OP_MAX, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Min", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the lesser of two values."), VisualShaderNodeVectorOp::OP_MIN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Mix", "Vector", "Functions", "VisualShaderNodeVectorInterp", TTR("Linear interpolation between two vectors."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Negate", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the opposite value of the parameter."), VisualShaderNodeVectorFunc::FUNC_NEGATE, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Normalize", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Calculates the normalize product of vector."), VisualShaderNodeVectorFunc::FUNC_NORMALIZE, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Pow", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the value of the first parameter raised to the power of the second."), VisualShaderNodeVectorOp::OP_POW, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Radians", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in degrees to radians."), VisualShaderNodeVectorFunc::FUNC_RADIANS, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Reciprocal", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 / vector"), VisualShaderNodeVectorFunc::FUNC_RECIPROCAL, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Reflect", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns a vector that points in the direction of reflection ( a : incident vector, b : normal vector )."), VisualShaderNodeVectorOp::OP_REFLECT, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Refract", "Vector", "Functions", "VisualShaderNodeVectorRefract", TTR("Returns a vector that points in the direction of refraction."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Round", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("(GLES3 only) Finds the nearest integer to the parameter."), VisualShaderNodeVectorFunc::FUNC_ROUND, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("RoundEven", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("(GLES3 only) Finds the nearest even integer to the parameter."), VisualShaderNodeVectorFunc::FUNC_ROUNDEVEN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Saturate", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Clamps the value between 0.0 and 1.0."), VisualShaderNodeVectorFunc::FUNC_SATURATE, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Sign", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Extracts the sign of the parameter."), VisualShaderNodeVectorFunc::FUNC_SIGN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Sin", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the sine of the parameter."), VisualShaderNodeVectorFunc::FUNC_SIN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("SinH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("(GLES3 only) Returns the hyperbolic sine of the parameter."), VisualShaderNodeVectorFunc::FUNC_SINH, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Sqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the square root of the parameter."), VisualShaderNodeVectorFunc::FUNC_SQRT, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("SmoothStep", "Vector", "Functions", "VisualShaderNodeVectorSmoothStep", TTR("SmoothStep function( vector(edge0), vector(edge1), vector(x) ).\n\nReturns 0.0 if 'x' is smaller then 'edge0' and 1.0 if 'x' is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("SmoothStepS", "Vector", "Functions", "VisualShaderNodeVectorScalarSmoothStep", TTR("SmoothStep function( scalar(edge0), scalar(edge1), vector(x) ).\n\nReturns 0.0 if 'x' is smaller then 'edge0' and 1.0 if 'x' is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Step", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Step function( vector(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller then 'edge' and otherwise 1.0."), VisualShaderNodeVectorOp::OP_STEP, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("StepS", "Vector", "Functions", "VisualShaderNodeVectorScalarStep", TTR("Step function( scalar(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller then 'edge' and otherwise 1.0."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Tan", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the tangent of the parameter."), VisualShaderNodeVectorFunc::FUNC_TAN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("TanH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("(GLES3 only) Returns the hyperbolic tangent of the parameter."), VisualShaderNodeVectorFunc::FUNC_TANH, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Trunc", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("(GLES3 only) Finds the truncated value of the parameter."), VisualShaderNodeVectorFunc::FUNC_TRUNC, VisualShaderNode::PORT_TYPE_VECTOR)); + + add_options.push_back(AddOption("Add", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Adds vector to vector."), VisualShaderNodeVectorOp::OP_ADD, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Divide", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Divides vector by vector."), VisualShaderNodeVectorOp::OP_DIV, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Multiply", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Multiplies vector by vector."), VisualShaderNodeVectorOp::OP_MUL, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Remainder", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Returns the remainder of the two vectors."), VisualShaderNodeVectorOp::OP_MOD, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Subtract", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Subtracts vector from vector."), VisualShaderNodeVectorOp::OP_SUB, VisualShaderNode::PORT_TYPE_VECTOR)); + + add_options.push_back(AddOption("VectorConstant", "Vector", "Variables", "VisualShaderNodeVec3Constant", TTR("Vector constant."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("VectorUniform", "Vector", "Variables", "VisualShaderNodeVec3Uniform", TTR("Vector uniform."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + + // SPECIAL + + add_options.push_back(AddOption("ScalarDerivativeFunc", "Special", "Common", "VisualShaderNodeScalarDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) Scalar derivative function."), -1, VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT)); + add_options.push_back(AddOption("VectorDerivativeFunc", "Special", "Common", "VisualShaderNodeVectorDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) Vector derivative function."), -1, VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT)); + + add_options.push_back(AddOption("DdX", "Special", "Derivative", "VisualShaderNodeVectorDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) (Vector) Derivative in 'x' using local differencing."), VisualShaderNodeVectorDerivativeFunc::FUNC_X, VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT)); + add_options.push_back(AddOption("DdXS", "Special", "Derivative", "VisualShaderNodeScalarDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) (Scalar) Derivative in 'x' using local differencing."), VisualShaderNodeScalarDerivativeFunc::FUNC_X, VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT)); + add_options.push_back(AddOption("DdY", "Special", "Derivative", "VisualShaderNodeVectorDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) (Vector) Derivative in 'y' using local differencing."), VisualShaderNodeVectorDerivativeFunc::FUNC_Y, VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT)); + add_options.push_back(AddOption("DdYS", "Special", "Derivative", "VisualShaderNodeScalarDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) (Scalar) Derivative in 'y' using local differencing."), VisualShaderNodeScalarDerivativeFunc::FUNC_Y, VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT)); + add_options.push_back(AddOption("Sum", "Special", "Derivative", "VisualShaderNodeVectorDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) (Vector) Sum of absolute derivative in 'x' and 'y'."), VisualShaderNodeVectorDerivativeFunc::FUNC_SUM, VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT)); + add_options.push_back(AddOption("SumS", "Special", "Derivative", "VisualShaderNodeScalarDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) (Scalar) Sum of absolute derivative in 'x' and 'y'."), VisualShaderNodeScalarDerivativeFunc::FUNC_SUM, VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT)); + + ///////////////////////////////////////////////////////////////////// _update_options_menu(); @@ -940,9 +1802,10 @@ public: void setup(const Ref<VisualShaderNodeInput> &p_input) { input = p_input; - Ref<Texture> type_icon[3] = { + Ref<Texture> type_icon[4] = { EditorNode::get_singleton()->get_gui_base()->get_icon("float", "EditorIcons"), EditorNode::get_singleton()->get_gui_base()->get_icon("Vector3", "EditorIcons"), + EditorNode::get_singleton()->get_gui_base()->get_icon("bool", "EditorIcons"), EditorNode::get_singleton()->get_gui_base()->get_icon("Transform", "EditorIcons"), }; @@ -1065,9 +1928,9 @@ Control *VisualShaderNodePluginDefault::create_editor(const Ref<VisualShaderNode if (Object::cast_to<EditorPropertyResource>(prop)) { Object::cast_to<EditorPropertyResource>(prop)->set_use_sub_inspector(false); prop->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); - } else if (Object::cast_to<EditorPropertyTransform>(prop)) { + } else if (Object::cast_to<EditorPropertyTransform>(prop) || Object::cast_to<EditorPropertyVector3>(prop)) { prop->set_custom_minimum_size(Size2(250 * EDSCALE, 0)); - } else if (Object::cast_to<EditorPropertyFloat>(prop) || Object::cast_to<EditorPropertyVector3>(prop)) { + } else if (Object::cast_to<EditorPropertyFloat>(prop)) { prop->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); } else if (Object::cast_to<EditorPropertyEnum>(prop)) { prop->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); @@ -1137,6 +2000,9 @@ void EditorPropertyShaderMode::_option_selected(int p_which) { } } + undo_redo->add_do_method(VisualShaderEditor::get_singleton(), "_update_options_menu"); + undo_redo->add_undo_method(VisualShaderEditor::get_singleton(), "_update_options_menu"); + //update graph undo_redo->add_do_method(VisualShaderEditor::get_singleton(), "_update_graph"); undo_redo->add_undo_method(VisualShaderEditor::get_singleton(), "_update_graph"); diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h index 2fb8a85d5b..eb0dee7594 100644 --- a/editor/plugins/visual_shader_editor_plugin.h +++ b/editor/plugins/visual_shader_editor_plugin.h @@ -60,7 +60,7 @@ class VisualShaderEditor : public VBoxContainer { Ref<VisualShader> visual_shader; GraphEdit *graph; - MenuButton *add_node; + ToolButton *add_node; OptionButton *edit_type; @@ -68,18 +68,62 @@ class VisualShaderEditor : public VBoxContainer { Label *error_label; UndoRedo *undo_redo; + Point2 saved_node_pos; + bool saved_node_pos_dirty; + + ConfirmationDialog *members_dialog; + MenuButton *tools; + + enum ToolsMenuOptions { + EXPAND_ALL, + COLLAPSE_ALL + }; + + Tree *members; + AcceptDialog *alert; + LineEdit *node_filter; + RichTextLabel *node_desc; + + void _tools_menu_option(int p_idx); + void _show_members_dialog(bool at_mouse_pos); void _update_graph(); struct AddOption { String name; String category; + String sub_category; String type; + String description; + int sub_func; + String sub_func_str; Ref<Script> script; - AddOption(const String &p_name = String(), const String &p_category = String(), const String &p_type = String()) { + int mode; + int return_type; + int func; + + AddOption(const String &p_name = String(), const String &p_category = String(), const String &p_sub_category = String(), const String &p_type = String(), const String &p_description = String(), int p_sub_func = -1, int p_return_type = -1, int p_mode = -1, int p_func = -1) { + name = p_name; + type = p_type; + category = p_category; + sub_category = p_sub_category; + description = p_description; + sub_func = p_sub_func; + return_type = p_return_type; + mode = p_mode; + func = p_func; + } + + AddOption(const String &p_name, const String &p_category, const String &p_sub_category, const String &p_type, const String &p_description, const String &p_sub_func, int p_return_type = -1, int p_mode = -1, int p_func = -1) { name = p_name; type = p_type; category = p_category; + sub_category = p_sub_category; + description = p_description; + sub_func_str = p_sub_func; + return_type = p_return_type; + mode = p_mode; + func = p_func; } }; @@ -87,7 +131,7 @@ class VisualShaderEditor : public VBoxContainer { void _draw_color_over_button(Object *obj, Color p_color); - void _add_node(int p_idx); + void _add_node(int p_idx, int p_op_idx = -1); void _update_options_menu(); static VisualShaderEditor *singleton; @@ -102,6 +146,7 @@ class VisualShaderEditor : public VBoxContainer { void _node_selected(Object *p_node); void _delete_request(int); + void _on_nodes_delete(); void _removed_from_graph(); @@ -124,7 +169,20 @@ 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_filter_changed(const String &p_text); + void _sbox_input(const Ref<InputEvent> &p_ie); + void _member_selected(); + void _member_unselected(); + void _member_create(); + + Variant get_drag_data_fw(const Point2 &p_point, Control *p_from); + 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); + + bool _is_available(int p_flags); + void _update_created_node(GraphNode *node); protected: void _notification(int p_what); |