diff options
Diffstat (limited to 'editor/plugins')
112 files changed, 7433 insertions, 2198 deletions
diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp index a85f4456f7..1afd7df049 100644 --- a/editor/plugins/abstract_polygon_2d_editor.cpp +++ b/editor/plugins/abstract_polygon_2d_editor.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -201,6 +201,8 @@ void AbstractPolygon2DEditor::_notification(int p_what) { case NOTIFICATION_READY: { + disable_polygon_editing(false, String()); + button_create->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("CurveCreate", "EditorIcons")); button_edit->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("CurveEdit", "EditorIcons")); button_delete->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("CurveDelete", "EditorIcons")); @@ -275,9 +277,30 @@ void AbstractPolygon2DEditor::_wip_close() { selected_point = Vertex(); } +void AbstractPolygon2DEditor::disable_polygon_editing(bool p_disable, String p_reason) { + + _polygon_editing_enabled = !p_disable; + + button_create->set_disabled(p_disable); + button_edit->set_disabled(p_disable); + button_delete->set_disabled(p_disable); + + if (p_disable) { + + button_create->set_tooltip(p_reason); + button_edit->set_tooltip(p_reason); + button_delete->set_tooltip(p_reason); + } else { + + button_create->set_tooltip(TTR("Create points.")); + button_edit->set_tooltip(TTR("Edit points.\nLMB: Move Point\nRMB: Erase Point")); + button_delete->set_tooltip(TTR("Erase points.")); + } +} + bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { - if (!_get_node()) + if (!_get_node() || !_polygon_editing_enabled) return false; Ref<InputEventMouseButton> mb = p_event; @@ -324,16 +347,16 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) return true; } else { - Vector<Vector2> vertices = _get_polygon(insert.polygon); - pre_move_edit = vertices; + Vector<Vector2> vertices2 = _get_polygon(insert.polygon); + pre_move_edit = vertices2; printf("setting pre_move_edit\n"); edited_point = PosVertex(insert.polygon, insert.vertex + 1, xform.affine_inverse().xform(insert.pos)); - vertices.insert(edited_point.vertex, edited_point.pos); + vertices2.insert(edited_point.vertex, edited_point.pos); selected_point = edited_point; edge_point = PosVertex(); undo_redo->create_action(TTR("Insert Point")); - _action_set_polygon(insert.polygon, vertices); + _action_set_polygon(insert.polygon, vertices2); _commit_action(); return true; } @@ -425,7 +448,7 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) return true; } else { - const real_t grab_threshold = EDITOR_DEF("editors/poly_editor/point_grab_radius", 8); + const real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius"); if (!_is_line() && wip.size() > 1 && xform.xform(wip[0]).distance_to(gpoint) < grab_threshold) { //wip closed @@ -458,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) { @@ -565,7 +599,7 @@ void AbstractPolygon2DEditor::forward_canvas_draw_over_viewport(Control *p_overl offset = _get_offset(j); } - if (!wip_active && j == edited_point.polygon && EDITOR_DEF("editors/poly_editor/show_previous_outline", true)) { + if (!wip_active && j == edited_point.polygon && EDITOR_GET("editors/poly_editor/show_previous_outline")) { const Color col = Color(0.5, 0.5, 0.5); // FIXME polygon->get_outline_color(); const int n = pre_move_edit.size(); @@ -615,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)); + } } } @@ -627,9 +668,8 @@ void AbstractPolygon2DEditor::forward_canvas_draw_over_viewport(Control *p_overl void AbstractPolygon2DEditor::edit(Node *p_polygon) { - if (!canvas_item_editor) { + if (!canvas_item_editor) canvas_item_editor = CanvasItemEditor::get_singleton(); - } if (p_polygon) { @@ -648,7 +688,6 @@ void AbstractPolygon2DEditor::edit(Node *p_polygon) { selected_point = Vertex(); canvas_item_editor->update_viewport(); - } else { _set_node(NULL); @@ -695,7 +734,7 @@ AbstractPolygon2DEditor::Vertex AbstractPolygon2DEditor::get_active_point() cons AbstractPolygon2DEditor::PosVertex AbstractPolygon2DEditor::closest_point(const Vector2 &p_pos) const { - const real_t grab_threshold = EDITOR_DEF("editors/poly_editor/point_grab_radius", 8); + const real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius"); const int n_polygons = _get_polygon_count(); const Transform2D xform = canvas_item_editor->get_canvas_transform() * _get_node()->get_global_transform(); @@ -726,7 +765,7 @@ AbstractPolygon2DEditor::PosVertex AbstractPolygon2DEditor::closest_point(const AbstractPolygon2DEditor::PosVertex AbstractPolygon2DEditor::closest_edge_point(const Vector2 &p_pos) const { - const real_t grab_threshold = EDITOR_DEF("editors/poly_editor/point_grab_radius", 8); + const real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius"); const real_t eps = grab_threshold * 2; const real_t eps2 = eps * eps; @@ -783,19 +822,16 @@ AbstractPolygon2DEditor::AbstractPolygon2DEditor(EditorNode *p_editor, bool p_wi add_child(button_create); button_create->connect("pressed", this, "_menu_option", varray(MODE_CREATE)); button_create->set_toggle_mode(true); - button_create->set_tooltip(TTR("Create points.")); button_edit = memnew(ToolButton); add_child(button_edit); button_edit->connect("pressed", this, "_menu_option", varray(MODE_EDIT)); button_edit->set_toggle_mode(true); - button_edit->set_tooltip(TTR("Edit points.\nLMB: Move Point\nRMB: Erase Point")); button_delete = memnew(ToolButton); add_child(button_delete); button_delete->connect("pressed", this, "_menu_option", varray(MODE_DELETE)); button_delete->set_toggle_mode(true); - button_delete->set_tooltip(TTR("Erase points.")); create_resource = memnew(ConfirmationDialog); add_child(create_resource); @@ -826,13 +862,11 @@ void AbstractPolygon2DEditorPlugin::make_visible(bool p_visible) { } } -AbstractPolygon2DEditorPlugin::AbstractPolygon2DEditorPlugin(EditorNode *p_node, AbstractPolygon2DEditor *p_polygon_editor, String p_class) { - - editor = p_node; - polygon_editor = p_polygon_editor; - klass = p_class; +AbstractPolygon2DEditorPlugin::AbstractPolygon2DEditorPlugin(EditorNode *p_node, AbstractPolygon2DEditor *p_polygon_editor, String p_class) : + polygon_editor(p_polygon_editor), + editor(p_node), + klass(p_class) { CanvasItemEditor::get_singleton()->add_control_to_menu_panel(polygon_editor); - polygon_editor->hide(); } diff --git a/editor/plugins/abstract_polygon_2d_editor.h b/editor/plugins/abstract_polygon_2d_editor.h index 046e8540e7..97244fa4e9 100644 --- a/editor/plugins/abstract_polygon_2d_editor.h +++ b/editor/plugins/abstract_polygon_2d_editor.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -81,6 +81,8 @@ class AbstractPolygon2DEditor : public HBoxContainer { bool wip_active; bool wip_destructive; + bool _polygon_editing_enabled; + CanvasItemEditor *canvas_item_editor; EditorNode *editor; Panel *panel; @@ -135,6 +137,8 @@ protected: virtual void _create_resource(); public: + void disable_polygon_editing(bool p_disable, String p_reason); + bool forward_gui_input(const Ref<InputEvent> &p_event); void forward_canvas_draw_over_viewport(Control *p_overlay); diff --git a/editor/plugins/animation_blend_space_1d_editor.cpp b/editor/plugins/animation_blend_space_1d_editor.cpp index 4b1e710705..2ae39d90de 100644 --- a/editor/plugins/animation_blend_space_1d_editor.cpp +++ b/editor/plugins/animation_blend_space_1d_editor.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -72,7 +72,7 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven ap->get_animation_list(&names); for (List<StringName>::Element *E = names.front(); E; E = E->next()) { - animations_menu->add_icon_item(get_icon("Animation", "Editoricons"), E->get()); + animations_menu->add_icon_item(get_icon("Animation", "EditorIcons"), E->get()); animations_to_add.push_back(E->get()); } } @@ -94,7 +94,7 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven menu->add_item(TTR("Paste"), MENU_PASTE); } menu->add_separator(); - menu->add_item(TTR("Load.."), MENU_LOAD_FILE); + menu->add_item(TTR("Load..."), MENU_LOAD_FILE); menu->set_global_position(blend_space_draw->get_global_transform().xform(mb->get_position())); menu->popup(); @@ -142,7 +142,7 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven } updating = true; - undo_redo->create_action("Move Node Point"); + undo_redo->create_action(TTR("Move Node Point")); undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, point); undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point)); undo_redo->add_do_method(this, "_update_space"); @@ -325,7 +325,7 @@ void AnimationNodeBlendSpace1DEditor::_config_changed(double) { return; updating = true; - undo_redo->create_action("Change BlendSpace1D Limits"); + undo_redo->create_action(TTR("Change BlendSpace1D Limits")); undo_redo->add_do_method(blend_space.ptr(), "set_max_space", max_value->get_value()); undo_redo->add_undo_method(blend_space.ptr(), "set_max_space", blend_space->get_max_space()); undo_redo->add_do_method(blend_space.ptr(), "set_min_space", min_value->get_value()); @@ -345,7 +345,7 @@ void AnimationNodeBlendSpace1DEditor::_labels_changed(String) { return; updating = true; - undo_redo->create_action("Change BlendSpace1D Labels", UndoRedo::MERGE_ENDS); + undo_redo->create_action(TTR("Change BlendSpace1D Labels"), UndoRedo::MERGE_ENDS); undo_redo->add_do_method(blend_space.ptr(), "set_value_label", label_value->get_text()); undo_redo->add_undo_method(blend_space.ptr(), "set_value_label", blend_space->get_value_label()); undo_redo->add_do_method(this, "_update_space"); @@ -401,7 +401,7 @@ void AnimationNodeBlendSpace1DEditor::_add_menu_type(int p_index) { } updating = true; - undo_redo->create_action("Add Node Point"); + undo_redo->create_action(TTR("Add Node Point")); undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", node, add_point_pos); undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count()); undo_redo->add_do_method(this, "_update_space"); @@ -419,7 +419,7 @@ void AnimationNodeBlendSpace1DEditor::_add_animation_type(int p_index) { anim->set_animation(animations_to_add[p_index]); updating = true; - undo_redo->create_action("Add Animation Point"); + undo_redo->create_action(TTR("Add Animation Point")); undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", anim, add_point_pos); undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count()); undo_redo->add_do_method(this, "_update_space"); @@ -489,7 +489,7 @@ void AnimationNodeBlendSpace1DEditor::_erase_selected() { if (selected_point != -1) { updating = true; - undo_redo->create_action("Remove BlendSpace1D Point"); + undo_redo->create_action(TTR("Remove BlendSpace1D Point")); undo_redo->add_do_method(blend_space.ptr(), "remove_blend_point", selected_point); undo_redo->add_undo_method(blend_space.ptr(), "add_blend_point", blend_space->get_blend_point_node(selected_point), blend_space->get_blend_point_position(selected_point), selected_point); undo_redo->add_do_method(this, "_update_space"); @@ -507,7 +507,7 @@ void AnimationNodeBlendSpace1DEditor::_edit_point_pos(double) { return; updating = true; - undo_redo->create_action("Move BlendSpace1D Node Point"); + undo_redo->create_action(TTR("Move BlendSpace1D Node Point")); undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, edit_value->get_value()); undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point)); undo_redo->add_do_method(this, "_update_space"); @@ -650,6 +650,7 @@ AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() { snap->set_toggle_mode(true); top_hb->add_child(snap); snap->set_pressed(true); + snap->set_tooltip(TTR("Enable snap and show grid.")); snap->connect("pressed", this, "_snap_toggled"); snap_value = memnew(SpinBox); diff --git a/editor/plugins/animation_blend_space_1d_editor.h b/editor/plugins/animation_blend_space_1d_editor.h index ca6135406c..ae4db184e4 100644 --- a/editor/plugins/animation_blend_space_1d_editor.h +++ b/editor/plugins/animation_blend_space_1d_editor.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp index e2fe9a91d8..5e8fb8e059 100644 --- a/editor/plugins/animation_blend_space_2d_editor.cpp +++ b/editor/plugins/animation_blend_space_2d_editor.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -47,11 +47,19 @@ bool AnimationNodeBlendSpace2DEditor::can_edit(const Ref<AnimationNode> &p_node) return bs2d.is_valid(); } +void AnimationNodeBlendSpace2DEditor::_blend_space_changed() { + blend_space_draw->update(); +} + void AnimationNodeBlendSpace2DEditor::edit(const Ref<AnimationNode> &p_node) { + if (blend_space.is_valid()) { + blend_space->disconnect("triangles_updated", this, "_blend_space_changed"); + } blend_space = p_node; if (!blend_space.is_null()) { + blend_space->connect("triangles_updated", this, "_blend_space_changed"); _update_space(); } } @@ -113,7 +121,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven menu->add_item(TTR("Paste"), MENU_PASTE); } menu->add_separator(); - menu->add_item(TTR("Load.."), MENU_LOAD_FILE); + menu->add_item(TTR("Load..."), MENU_LOAD_FILE); menu->set_global_position(blend_space_draw->get_global_transform().xform(mb->get_position())); menu->popup(); @@ -187,12 +195,12 @@ 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; } updating = true; - undo_redo->create_action("Add Triangle"); + undo_redo->create_action(TTR("Add Triangle")); undo_redo->add_do_method(blend_space.ptr(), "add_triangle", making_triangle[0], making_triangle[1], making_triangle[2]); undo_redo->add_undo_method(blend_space.ptr(), "remove_triangle", blend_space->get_triangle_count()); undo_redo->add_do_method(this, "_update_space"); @@ -217,7 +225,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven } updating = true; - undo_redo->create_action("Move Node Point"); + undo_redo->create_action(TTR("Move Node Point")); undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, point); undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point)); undo_redo->add_do_method(this, "_update_space"); @@ -325,7 +333,7 @@ void AnimationNodeBlendSpace2DEditor::_add_menu_type(int p_index) { } updating = true; - undo_redo->create_action("Add Node Point"); + undo_redo->create_action(TTR("Add Node Point")); undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", node, add_point_pos); undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count()); undo_redo->add_do_method(this, "_update_space"); @@ -344,7 +352,7 @@ void AnimationNodeBlendSpace2DEditor::_add_animation_type(int p_index) { anim->set_animation(animations_to_add[p_index]); updating = true; - undo_redo->create_action("Add Animation Point"); + undo_redo->create_action(TTR("Add Animation Point")); undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", anim, add_point_pos); undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count()); undo_redo->add_do_method(this, "_update_space"); @@ -631,7 +639,7 @@ void AnimationNodeBlendSpace2DEditor::_config_changed(double) { return; updating = true; - undo_redo->create_action("Change BlendSpace2D Limits"); + undo_redo->create_action(TTR("Change BlendSpace2D Limits")); undo_redo->add_do_method(blend_space.ptr(), "set_max_space", Vector2(max_x_value->get_value(), max_y_value->get_value())); undo_redo->add_undo_method(blend_space.ptr(), "set_max_space", blend_space->get_max_space()); undo_redo->add_do_method(blend_space.ptr(), "set_min_space", Vector2(min_x_value->get_value(), min_y_value->get_value())); @@ -653,7 +661,7 @@ void AnimationNodeBlendSpace2DEditor::_labels_changed(String) { return; updating = true; - undo_redo->create_action("Change BlendSpace2D Labels", UndoRedo::MERGE_ENDS); + undo_redo->create_action(TTR("Change BlendSpace2D Labels"), UndoRedo::MERGE_ENDS); undo_redo->add_do_method(blend_space.ptr(), "set_x_label", label_x->get_text()); undo_redo->add_undo_method(blend_space.ptr(), "set_x_label", blend_space->get_x_label()); undo_redo->add_do_method(blend_space.ptr(), "set_y_label", label_y->get_text()); @@ -669,7 +677,7 @@ void AnimationNodeBlendSpace2DEditor::_erase_selected() { if (selected_point != -1) { updating = true; - undo_redo->create_action("Remove BlendSpace2D Point"); + undo_redo->create_action(TTR("Remove BlendSpace2D Point")); undo_redo->add_do_method(blend_space.ptr(), "remove_blend_point", selected_point); undo_redo->add_undo_method(blend_space.ptr(), "add_blend_point", blend_space->get_blend_point_node(selected_point), blend_space->get_blend_point_position(selected_point), selected_point); @@ -692,7 +700,7 @@ void AnimationNodeBlendSpace2DEditor::_erase_selected() { } else if (selected_triangle != -1) { updating = true; - undo_redo->create_action("Remove BlendSpace2D Triangle"); + undo_redo->create_action(TTR("Remove BlendSpace2D Triangle")); undo_redo->add_do_method(blend_space.ptr(), "remove_triangle", selected_triangle); undo_redo->add_undo_method(blend_space.ptr(), "add_triangle", blend_space->get_triangle_point(selected_triangle, 0), blend_space->get_triangle_point(selected_triangle, 1), blend_space->get_triangle_point(selected_triangle, 2), selected_triangle); @@ -729,7 +737,7 @@ void AnimationNodeBlendSpace2DEditor::_edit_point_pos(double) { if (updating) return; updating = true; - undo_redo->create_action("Move Node Point"); + undo_redo->create_action(TTR("Move Node Point")); undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, Vector2(edit_x->get_value(), edit_y->get_value())); undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point)); undo_redo->add_do_method(this, "_update_space"); @@ -806,7 +814,7 @@ void AnimationNodeBlendSpace2DEditor::_removed_from_graph() { void AnimationNodeBlendSpace2DEditor::_auto_triangles_toggled() { - undo_redo->create_action("Toggle Auto Triangles"); + undo_redo->create_action(TTR("Toggle Auto Triangles")); undo_redo->add_do_method(blend_space.ptr(), "set_auto_triangles", auto_triangles->is_pressed()); undo_redo->add_undo_method(blend_space.ptr(), "set_auto_triangles", blend_space->get_auto_triangles()); undo_redo->add_do_method(this, "_update_space"); @@ -837,6 +845,7 @@ void AnimationNodeBlendSpace2DEditor::_bind_methods() { ClassDB::bind_method("_removed_from_graph", &AnimationNodeBlendSpace2DEditor::_removed_from_graph); ClassDB::bind_method("_auto_triangles_toggled", &AnimationNodeBlendSpace2DEditor::_auto_triangles_toggled); + ClassDB::bind_method("_blend_space_changed", &AnimationNodeBlendSpace2DEditor::_blend_space_changed); ClassDB::bind_method("_file_opened", &AnimationNodeBlendSpace2DEditor::_file_opened); } @@ -904,8 +913,8 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() { snap = memnew(ToolButton); snap->set_toggle_mode(true); top_hb->add_child(snap); - //snap->set_text(TTR("Snap")); snap->set_pressed(true); + snap->set_tooltip(TTR("Enable snap and show grid.")); snap->connect("pressed", this, "_snap_toggled"); snap_x = memnew(SpinBox); diff --git a/editor/plugins/animation_blend_space_2d_editor.h b/editor/plugins/animation_blend_space_2d_editor.h index 603fa1cd19..74186791e1 100644 --- a/editor/plugins/animation_blend_space_2d_editor.h +++ b/editor/plugins/animation_blend_space_2d_editor.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -138,6 +138,8 @@ class AnimationNodeBlendSpace2DEditor : public AnimationTreeNodeEditorPlugin { MENU_LOAD_FILE_CONFIRM = 1002 }; + void _blend_space_changed(); + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index eb3c432ee7..bfee76492b 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -79,7 +79,8 @@ void AnimationNodeBlendTreeEditor::_update_options_menu() { add_node->get_popup()->add_item(TTR("Paste"), MENU_PASTE); } add_node->get_popup()->add_separator(); - add_node->get_popup()->add_item(TTR("Load.."), MENU_LOAD_FILE); + add_node->get_popup()->add_item(TTR("Load..."), MENU_LOAD_FILE); + use_popup_menu_position = false; } Size2 AnimationNodeBlendTreeEditor::get_minimum_size() const { @@ -87,11 +88,11 @@ Size2 AnimationNodeBlendTreeEditor::get_minimum_size() const { return Size2(10, 200); } -void AnimationNodeBlendTreeEditor::_property_changed(const StringName &p_property, const Variant &p_value) { +void AnimationNodeBlendTreeEditor::_property_changed(const StringName &p_property, const Variant &p_value, const String &p_field, bool p_changing) { AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_tree(); updating = true; - undo_redo->create_action("Parameter Changed: " + String(p_property), UndoRedo::MERGE_ENDS); + undo_redo->create_action(TTR("Parameter Changed") + ": " + String(p_property), UndoRedo::MERGE_ENDS); undo_redo->add_do_property(tree, p_property, p_value); undo_redo->add_undo_property(tree, p_property, tree->get(p_property)); undo_redo->add_do_method(this, "_update_graph"); @@ -231,29 +232,24 @@ void AnimationNodeBlendTreeEditor::_update_graph() { } pb->set_percent_visible(false); + pb->set_custom_minimum_size(Vector2(0, 14) * EDSCALE); animations[E->get()] = pb; node->add_child(pb); mb->get_popup()->connect("index_pressed", this, "_anim_selected", varray(options, E->get()), CONNECT_DEFERRED); } - /* should be no longer necessary, as the boolean works - Ref<AnimationNodeOneShot> oneshot = agnode; - if (oneshot.is_valid()) { - - HBoxContainer *play_stop = memnew(HBoxContainer); - play_stop->add_spacer(); - Button *play = memnew(Button); - play->set_icon(get_icon("Play", "EditorIcons")); - play->connect("pressed", this, "_oneshot_start", varray(E->get()), CONNECT_DEFERRED); - play_stop->add_child(play); - Button *stop = memnew(Button); - stop->set_icon(get_icon("Stop", "EditorIcons")); - stop->connect("pressed", this, "_oneshot_stop", varray(E->get()), CONNECT_DEFERRED); - play_stop->add_child(stop); - play_stop->add_spacer(); - node->add_child(play_stop); - } */ + 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); + } } List<AnimationNodeBlendTree::NodeConnection> connections; @@ -322,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; @@ -331,7 +335,7 @@ void AnimationNodeBlendTreeEditor::_add_node(int p_idx) { name = base_name + " " + itos(base); } - undo_redo->create_action("Add Node to BlendTree"); + undo_redo->create_action(TTR("Add Node to BlendTree")); undo_redo->add_do_method(blend_tree.ptr(), "add_node", name, anode, instance_pos / EDSCALE); undo_redo->add_undo_method(blend_tree.ptr(), "remove_node", name); undo_redo->add_do_method(this, "_update_graph"); @@ -342,7 +346,7 @@ void AnimationNodeBlendTreeEditor::_add_node(int p_idx) { void AnimationNodeBlendTreeEditor::_node_dragged(const Vector2 &p_from, const Vector2 &p_to, const StringName &p_which) { updating = true; - undo_redo->create_action("Node Moved"); + undo_redo->create_action(TTR("Node Moved")); undo_redo->add_do_method(blend_tree.ptr(), "set_node_position", p_which, p_to / EDSCALE); undo_redo->add_undo_method(blend_tree.ptr(), "set_node_position", p_which, p_from / EDSCALE); undo_redo->add_do_method(this, "_update_graph"); @@ -360,9 +364,9 @@ void AnimationNodeBlendTreeEditor::_connection_request(const String &p_from, int return; } - undo_redo->create_action("Nodes Connected"); + undo_redo->create_action(TTR("Nodes Connected")); undo_redo->add_do_method(blend_tree.ptr(), "connect_node", p_to, p_to_index, p_from); - undo_redo->add_undo_method(blend_tree.ptr(), "disconnect_node", p_to, p_to_index, p_from); + undo_redo->add_undo_method(blend_tree.ptr(), "disconnect_node", p_to, p_to_index); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); @@ -373,7 +377,7 @@ void AnimationNodeBlendTreeEditor::_disconnection_request(const String &p_from, graph->disconnect_node(p_from, p_from_index, p_to, p_to_index); updating = true; - undo_redo->create_action("Nodes Disconnected"); + undo_redo->create_action(TTR("Nodes Disconnected")); undo_redo->add_do_method(blend_tree.ptr(), "disconnect_node", p_to, p_to_index); undo_redo->add_undo_method(blend_tree.ptr(), "connect_node", p_to, p_to_index, p_from); undo_redo->add_do_method(this, "_update_graph"); @@ -389,7 +393,7 @@ void AnimationNodeBlendTreeEditor::_anim_selected(int p_index, Array p_options, Ref<AnimationNodeAnimation> anim = blend_tree->get_node(p_node); ERR_FAIL_COND(!anim.is_valid()); - undo_redo->create_action("Set Animation"); + undo_redo->create_action(TTR("Set Animation")); undo_redo->add_do_method(anim.ptr(), "set_animation", option); undo_redo->add_undo_method(anim.ptr(), "set_animation", anim->get_animation()); undo_redo->add_do_method(this, "_update_graph"); @@ -399,9 +403,9 @@ void AnimationNodeBlendTreeEditor::_anim_selected(int p_index, Array p_options, void AnimationNodeBlendTreeEditor::_delete_request(const String &p_which) { - undo_redo->create_action("Delete Node"); + undo_redo->create_action(TTR("Delete Node")); undo_redo->add_do_method(blend_tree.ptr(), "remove_node", p_which); - undo_redo->add_undo_method(blend_tree.ptr(), "add_node", p_which, blend_tree->get_node(p_which)); + undo_redo->add_undo_method(blend_tree.ptr(), "add_node", p_which, blend_tree->get_node(p_which), blend_tree.ptr()->get_node_position(p_which)); List<AnimationNodeBlendTree::NodeConnection> conns; blend_tree->get_node_connections(&conns); @@ -417,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); @@ -440,7 +478,7 @@ void AnimationNodeBlendTreeEditor::_open_in_editor(const String &p_which) { void AnimationNodeBlendTreeEditor::_filter_toggled() { updating = true; - undo_redo->create_action("Toggle filter on/off"); + undo_redo->create_action(TTR("Toggle Filter On/Off")); undo_redo->add_do_method(_filter_edit.ptr(), "set_filter_enabled", filter_enabled->is_pressed()); undo_redo->add_undo_method(_filter_edit.ptr(), "set_filter_enabled", _filter_edit->is_filter_enabled()); undo_redo->add_do_method(this, "_update_filters", _filter_edit); @@ -458,7 +496,7 @@ void AnimationNodeBlendTreeEditor::_filter_edited() { bool filtered = edited->is_checked(0); updating = true; - undo_redo->create_action("Change filter"); + undo_redo->create_action(TTR("Change Filter")); undo_redo->add_do_method(_filter_edit.ptr(), "set_filter_path", edited_path, filtered); undo_redo->add_undo_method(_filter_edit.ptr(), "set_filter_path", edited_path, _filter_edit->is_path_filtered(edited_path)); undo_redo->add_do_method(this, "_update_filters", _filter_edit); @@ -646,6 +684,9 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) { error_panel->add_style_override("panel", get_stylebox("bg", "Tree")); error_label->add_color_override("font_color", get_color("error_color", "Editor")); + + if (p_what == NOTIFICATION_THEME_CHANGED && is_visible_in_tree()) + _update_graph(); } if (p_what == NOTIFICATION_PROCESS) { @@ -732,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); @@ -772,7 +815,7 @@ void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<Anima String base_path = AnimationTreeEditor::get_singleton()->get_base_path(); updating = true; - undo_redo->create_action("Node Renamed"); + undo_redo->create_action(TTR("Node Renamed")); undo_redo->add_do_method(blend_tree.ptr(), "rename_node", prev_name, name); undo_redo->add_undo_method(blend_tree.ptr(), "rename_node", name, prev_name); undo_redo->add_do_method(AnimationTreeEditor::get_singleton()->get_tree(), "rename_parameter", base_path + prev_name, base_path + name); @@ -788,8 +831,8 @@ void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<Anima for (int i = 0; i < visible_properties.size(); i++) { String pname = visible_properties[i]->get_edited_property().operator String(); if (pname.begins_with(base_path + prev_name)) { - String new_name = pname.replace_first(base_path + prev_name, base_path + name); - visible_properties[i]->set_object_and_property(visible_properties[i]->get_edited_object(), new_name); + String new_name2 = pname.replace_first(base_path + prev_name, base_path + name); + visible_properties[i]->set_object_and_property(visible_properties[i]->get_edited_object(), new_name2); } } @@ -816,6 +859,8 @@ void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<Anima break; } } + + _update_graph(); // Needed to update the signal connections with the new name. } void AnimationNodeBlendTreeEditor::_node_renamed_focus_out(Node *le, Ref<AnimationNode> p_node) { @@ -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); @@ -867,7 +915,7 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { add_node = memnew(MenuButton); graph->get_zoom_hbox()->add_child(add_node); - add_node->set_text(TTR("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_node->connect("about_to_show", this, "_update_options_menu"); @@ -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 e2daefdec6..cb40159a40 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.h +++ b/editor/plugins/animation_blend_tree_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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; @@ -70,9 +72,9 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin { String name; String type; Ref<Script> script; - AddOption(const String &p_name = String(), const String &p_type = String()) { - name = p_name; - type = p_type; + AddOption(const String &p_name = String(), const String &p_type = String()) : + name(p_name), + type(p_type) { } }; @@ -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); @@ -104,7 +108,7 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin { void _filter_toggled(); Ref<AnimationNode> _filter_edit; - void _property_changed(const StringName &p_property, const Variant &p_value); + void _property_changed(const StringName &p_property, const Variant &p_value, const String &p_field, bool p_changing); void _removed_from_graph(); EditorFileDialog *open_file; diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 445664f8dd..951e971615 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -54,13 +54,9 @@ void AnimationPlayerEditor::_node_removed(Node *p_node) { track_editor->set_root(NULL); track_editor->show_select_node_warning(true); _update_player(); - //editor->animation_editor_make_visible(false); } } -void AnimationPlayerEditor::_gui_input(Ref<InputEvent> p_event) { -} - void AnimationPlayerEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_PROCESS: { @@ -129,8 +125,10 @@ void AnimationPlayerEditor::_notification(int p_what) { autoplay_icon = get_icon("AutoPlay", "EditorIcons"); stop->set_icon(get_icon("Stop", "EditorIcons")); + onion_toggle->set_icon(get_icon("Onion", "EditorIcons")); + onion_skinning->set_icon(get_icon("GuiMiniTabMenu", "EditorIcons")); + pin->set_icon(get_icon("Pin", "EditorIcons")); - onion_skinning->set_icon(get_icon("Onion", "EditorIcons")); tool_anim->add_style_override("normal", get_stylebox("normal", "Button")); track_editor->get_edit_menu()->add_style_override("normal", get_stylebox("normal", "Button")); @@ -291,25 +289,40 @@ void AnimationPlayerEditor::_pause_pressed() { //player->set_pause( pause->is_pressed() ); } -void AnimationPlayerEditor::_animation_selected(int p_which) { - if (updating) - return; +String AnimationPlayerEditor::_get_current_animation() const { + // when selecting an animation, the idea is that the only interesting behavior // ui-wise is that it should play/blend the next one if currently playing - String current; if (animation->get_selected() >= 0 && animation->get_selected() < animation->get_item_count()) { - current = animation->get_item_text(animation->get_selected()); + return animation->get_item_text(animation->get_selected()); } - if (current != "") { + return ""; +} - player->set_assigned_animation(current); +void AnimationPlayerEditor::_animation_selected(int p_which) { + + if (updating) + return; + + _current_animation_updated(); +} + +void AnimationPlayerEditor::_current_animation_updated() { + String current = _get_current_animation(); + + if (current != "") { Ref<Animation> anim = player->get_animation(current); + + player->set_assigned_animation(current); { + if (!anim->is_connected("changed", this, "_current_animation_updated")) + anim->connect("changed", this, "_current_animation_updated"); + track_editor->set_animation(anim); Node *root = player->get_node(player->get_root()); if (root) { @@ -670,25 +683,29 @@ 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; } void AnimationPlayerEditor::set_state(const Dictionary &p_state) { - if (p_state.has("visible") && p_state["visible"]) { + if (!p_state.has("visible") || !p_state["visible"]) { + return; + } + if (!EditorNode::get_singleton()->get_edited_scene()) { + return; + } - if (!EditorNode::get_singleton()->get_edited_scene()) - return; + if (p_state.has("player")) { Node *n = EditorNode::get_singleton()->get_edited_scene()->get_node(p_state["player"]); if (Object::cast_to<AnimationPlayer>(n) && EditorNode::get_singleton()->get_editor_selection()->is_selected(n)) { player = Object::cast_to<AnimationPlayer>(n); _update_player(); - show(); + editor->make_bottom_panel_item_visible(this); set_process(true); ensure_visibility(); - //EditorNode::get_singleton()->animation_panel_make_visible(true); if (p_state.has("animation")) { String anim = p_state["animation"]; @@ -697,6 +714,10 @@ void AnimationPlayerEditor::set_state(const Dictionary &p_state) { } } } + + if (p_state.has("track_editor_state")) { + track_editor->set_state(p_state["track_editor_state"]); + } } void AnimationPlayerEditor::_animation_resource_edit() { @@ -714,17 +735,17 @@ void AnimationPlayerEditor::_animation_edit() { String current = animation->get_item_text(animation->get_selected()); Ref<Animation> anim = player->get_animation(current); track_editor->set_animation(anim); + Node *root = player->get_node(player->get_root()); if (root) { track_editor->set_root(root); } - } else { - track_editor->set_animation(Ref<Animation>()); track_editor->set_root(NULL); } } + void AnimationPlayerEditor::_dialog_action(String p_file) { switch (current_option) { @@ -841,6 +862,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 +880,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); @@ -874,8 +895,6 @@ void AnimationPlayerEditor::_update_player() { _animation_selected(0); } - //pause->set_pressed(player->is_paused()); - if (animation->get_item_count()) { String current = animation->get_item_text(animation->get_selected()); Ref<Animation> anim = player->get_animation(current); @@ -903,8 +922,6 @@ void AnimationPlayerEditor::edit(AnimationPlayer *p_player) { track_editor->show_select_node_warning(false); } else { track_editor->show_select_node_warning(true); - - //hide(); } } @@ -1061,17 +1078,19 @@ void AnimationPlayerEditor::_list_changed() { _update_player(); } -void AnimationPlayerEditor::_animation_key_editor_anim_len_changed(float p_len) { - - frame->set_max(p_len); -} - void AnimationPlayerEditor::_animation_key_editor_anim_step_changed(float p_len) { if (p_len) frame->set_step(p_len); else frame->set_step(0.00001); + + String current = _get_current_animation(); + + if (current != "") { + Ref<Animation> anim = player->get_animation(current); + anim->_change_notify("step"); + } } void AnimationPlayerEditor::_animation_key_editor_seek(float p_pos, bool p_drag) { @@ -1103,7 +1122,6 @@ void AnimationPlayerEditor::_hide_anim_editors() { track_editor->set_animation(Ref<Animation>()); track_editor->set_root(NULL); track_editor->show_select_node_warning(true); - //editor->animation_editor_make_visible(false); } void AnimationPlayerEditor::_animation_about_to_show_menu() { @@ -1160,22 +1178,22 @@ void AnimationPlayerEditor::_animation_tool_menu(int p_option) { return; } - String current = animation->get_item_text(animation->get_selected()); - Ref<Animation> anim = player->get_animation(current); - //editor->edit_resource(anim); - EditorSettings::get_singleton()->set_resource_clipboard(anim); + String current2 = animation->get_item_text(animation->get_selected()); + Ref<Animation> anim2 = player->get_animation(current2); + //editor->edit_resource(anim2); + EditorSettings::get_singleton()->set_resource_clipboard(anim2); } break; case TOOL_PASTE_ANIM: { - Ref<Animation> anim = EditorSettings::get_singleton()->get_resource_clipboard(); - if (!anim.is_valid()) { + Ref<Animation> anim2 = EditorSettings::get_singleton()->get_resource_clipboard(); + if (!anim2.is_valid()) { error_dialog->set_text(TTR("No animation resource on clipboard!")); error_dialog->popup_centered_minsize(); return; } - String name = anim->get_name(); + String name = anim2->get_name(); if (name == "") { name = TTR("Pasted Animation"); } @@ -1189,7 +1207,7 @@ void AnimationPlayerEditor::_animation_tool_menu(int p_option) { } undo_redo->create_action(TTR("Paste Animation")); - undo_redo->add_do_method(player, "add_animation", name, anim); + undo_redo->add_do_method(player, "add_animation", name, anim2); undo_redo->add_undo_method(player, "remove_animation", name); undo_redo->add_do_method(this, "_animation_player_changed", player); undo_redo->add_undo_method(this, "_animation_player_changed", player); @@ -1206,9 +1224,9 @@ void AnimationPlayerEditor::_animation_tool_menu(int p_option) { return; } - String current = animation->get_item_text(animation->get_selected()); - Ref<Animation> anim = player->get_animation(current); - editor->edit_resource(anim); + String current2 = animation->get_item_text(animation->get_selected()); + Ref<Animation> anim2 = player->get_animation(current2); + editor->edit_resource(anim2); } break; } @@ -1224,7 +1242,6 @@ void AnimationPlayerEditor::_onion_skinning_menu(int p_option) { case ONION_SKINNING_ENABLE: { onion.enabled = !onion.enabled; - menu->set_item_checked(idx, onion.enabled); if (onion.enabled) _start_onion_skinning(); @@ -1425,6 +1442,7 @@ void AnimationPlayerEditor::_prepare_onion_layers_2() { new_state["show_rulers"] = false; new_state["show_guides"] = false; new_state["show_helpers"] = false; + new_state["show_zoom_control"] = false; // TODO: Save/restore only affected entries CanvasItemEditor::get_singleton()->set_state(new_state); } @@ -1477,7 +1495,7 @@ void AnimationPlayerEditor::_prepare_onion_layers_2() { if (valid) { player->seek(pos, true); get_tree()->flush_transform_notifications(); // Needed for transforms of Spatials - values_backup.update_skeletons(); // Needed for Skeletons + values_backup.update_skeletons(); // Needed for Skeletons (2D & 3D) VS::get_singleton()->viewport_set_active(onion.captures[cidx], true); VS::get_singleton()->viewport_set_parent_viewport(root_vp, onion.captures[cidx]); @@ -1541,7 +1559,6 @@ void AnimationPlayerEditor::_pin_pressed() { void AnimationPlayerEditor::_bind_methods() { - ClassDB::bind_method(D_METHOD("_gui_input"), &AnimationPlayerEditor::_gui_input); ClassDB::bind_method(D_METHOD("_node_removed"), &AnimationPlayerEditor::_node_removed); ClassDB::bind_method(D_METHOD("_play_pressed"), &AnimationPlayerEditor::_play_pressed); ClassDB::bind_method(D_METHOD("_play_from_pressed"), &AnimationPlayerEditor::_play_from_pressed); @@ -1551,6 +1568,7 @@ void AnimationPlayerEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_autoplay_pressed"), &AnimationPlayerEditor::_autoplay_pressed); ClassDB::bind_method(D_METHOD("_pause_pressed"), &AnimationPlayerEditor::_pause_pressed); ClassDB::bind_method(D_METHOD("_animation_selected"), &AnimationPlayerEditor::_animation_selected); + ClassDB::bind_method(D_METHOD("_current_animation_updated"), &AnimationPlayerEditor::_current_animation_updated); ClassDB::bind_method(D_METHOD("_animation_name_edited"), &AnimationPlayerEditor::_animation_name_edited); ClassDB::bind_method(D_METHOD("_animation_new"), &AnimationPlayerEditor::_animation_new); ClassDB::bind_method(D_METHOD("_animation_rename"), &AnimationPlayerEditor::_animation_rename); @@ -1570,7 +1588,6 @@ void AnimationPlayerEditor::_bind_methods() { //ClassDB::bind_method(D_METHOD("_editor_load_all"),&AnimationPlayerEditor::_editor_load_all); ClassDB::bind_method(D_METHOD("_list_changed"), &AnimationPlayerEditor::_list_changed); ClassDB::bind_method(D_METHOD("_animation_key_editor_seek"), &AnimationPlayerEditor::_animation_key_editor_seek); - ClassDB::bind_method(D_METHOD("_animation_key_editor_anim_len_changed"), &AnimationPlayerEditor::_animation_key_editor_anim_len_changed); ClassDB::bind_method(D_METHOD("_animation_key_editor_anim_step_changed"), &AnimationPlayerEditor::_animation_key_editor_anim_step_changed); ClassDB::bind_method(D_METHOD("_hide_anim_editors"), &AnimationPlayerEditor::_hide_anim_editors); ClassDB::bind_method(D_METHOD("_animation_duplicate"), &AnimationPlayerEditor::_animation_duplicate); @@ -1604,12 +1621,6 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay player = NULL; - Label *l; - - /*l= memnew( Label ); - l->set_text("Animation Player:"); - add_child(l);*/ - HBoxContainer *hb = memnew(HBoxContainer); add_child(hb); @@ -1634,10 +1645,6 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay play_from->set_tooltip(TTR("Play selected animation from current pos. (D)")); hb->add_child(play_from); - //pause = memnew( Button ); - //pause->set_toggle_mode(true); - //hb->add_child(pause); - frame = memnew(SpinBox); hb->add_child(frame); frame->set_custom_minimum_size(Size2(60, 0)); @@ -1663,7 +1670,6 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay tool_anim = memnew(MenuButton); tool_anim->set_flat(false); - //tool_anim->set_flat(false); tool_anim->set_tooltip(TTR("Animation Tools")); tool_anim->set_text(TTR("Animation")); tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/new_animation", TTR("New")), TOOL_NEW_ANIM); @@ -1694,28 +1700,27 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay hb->add_child(autoplay); autoplay->set_tooltip(TTR("Autoplay on Load")); - //tool_anim->get_popup()->add_separator(); - //tool_anim->get_popup()->add_item("Edit Anim Resource",TOOL_PASTE_ANIM); - hb->add_child(memnew(VSeparator)); track_editor = memnew(AnimationTrackEditor); hb->add_child(track_editor->get_edit_menu()); + hb->add_child(memnew(VSeparator)); + + onion_toggle = memnew(ToolButton); + onion_toggle->set_toggle_mode(true); + onion_toggle->set_tooltip(TTR("Enable Onion Skinning")); + onion_toggle->connect("pressed", this, "_onion_skinning_menu", varray(ONION_SKINNING_ENABLE)); + hb->add_child(onion_toggle); + onion_skinning = memnew(MenuButton); - //onion_skinning->set_flat(false); - onion_skinning->set_tooltip(TTR("Onion Skinning")); - onion_skinning->get_popup()->add_check_shortcut(ED_SHORTCUT("animation_player_editor/onion_skinning", TTR("Enable Onion Skinning")), ONION_SKINNING_ENABLE); - onion_skinning->get_popup()->add_separator(); - onion_skinning->get_popup()->add_item(TTR("Directions"), -1); - onion_skinning->get_popup()->set_item_disabled(onion_skinning->get_popup()->get_item_count() - 1, true); + onion_skinning->set_tooltip(TTR("Onion Skinning Options")); + onion_skinning->get_popup()->add_separator(TTR("Directions")); onion_skinning->get_popup()->add_check_item(TTR("Past"), ONION_SKINNING_PAST); onion_skinning->get_popup()->set_item_checked(onion_skinning->get_popup()->get_item_count() - 1, true); onion_skinning->get_popup()->add_check_item(TTR("Future"), ONION_SKINNING_FUTURE); - onion_skinning->get_popup()->add_separator(); - onion_skinning->get_popup()->add_item(TTR("Depth"), -1); - onion_skinning->get_popup()->set_item_disabled(onion_skinning->get_popup()->get_item_count() - 1, true); + onion_skinning->get_popup()->add_separator(TTR("Depth")); onion_skinning->get_popup()->add_radio_check_item(TTR("1 step"), ONION_SKINNING_1_STEP); onion_skinning->get_popup()->set_item_checked(onion_skinning->get_popup()->get_item_count() - 1, true); onion_skinning->get_popup()->add_radio_check_item(TTR("2 steps"), ONION_SKINNING_2_STEPS); @@ -1726,6 +1731,8 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay onion_skinning->get_popup()->add_check_item(TTR("Include Gizmos (3D)"), ONION_SKINNING_INCLUDE_GIZMOS); hb->add_child(onion_skinning); + hb->add_child(memnew(VSeparator)); + pin = memnew(ToolButton); pin->set_toggle_mode(true); pin->set_tooltip(TTR("Pin AnimationPlayer")); @@ -1742,10 +1749,8 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay VBoxContainer *vb = memnew(VBoxContainer); name_dialog->add_child(vb); - l = memnew(Label); - l->set_text(TTR("Animation Name:")); - vb->add_child(l); - name_title = l; + name_title = memnew(Label(TTR("Animation Name:"))); + vb->add_child(name_title); name = memnew(LineEdit); vb->add_child(name); @@ -1764,7 +1769,6 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay blend_editor.dialog->set_hide_on_ok(true); VBoxContainer *blend_vb = memnew(VBoxContainer); blend_editor.dialog->add_child(blend_vb); - //blend_editor.dialog->set_child_rect(blend_vb); blend_editor.tree = memnew(Tree); blend_editor.tree->set_columns(2); blend_vb->add_margin_child(TTR("Blend Times:"), blend_editor.tree, true); @@ -1782,8 +1786,6 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay play_bw->connect("pressed", this, "_play_bw_pressed"); play_bw_from->connect("pressed", this, "_play_bw_from_pressed"); stop->connect("pressed", this, "_stop_pressed"); - //pause->connect("pressed", this,"_pause_pressed"); - //frame->connect("text_entered", this,"_seek_frame_changed"); animation->connect("item_selected", this, "_animation_selected", Vector<Variant>(), true); @@ -1799,7 +1801,6 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay add_child(track_editor); track_editor->set_v_size_flags(SIZE_EXPAND_FILL); track_editor->connect("timeline_changed", this, "_animation_key_editor_seek"); - track_editor->connect("animation_len_changed", this, "_animation_key_editor_anim_len_changed"); track_editor->connect("animation_step_changed", this, "_animation_key_editor_anim_step_changed"); _update_player(); @@ -1887,11 +1888,6 @@ void AnimationPlayerEditorPlugin::make_visible(bool p_visible) { editor->make_bottom_panel_item_visible(anim_editor); anim_editor->set_process(true); anim_editor->ensure_visibility(); - //editor->animation_panel_make_visible(true); - } else { - - //anim_editor->hide(); - //anim_editor->set_idle_process(false); } } @@ -1900,16 +1896,7 @@ AnimationPlayerEditorPlugin::AnimationPlayerEditorPlugin(EditorNode *p_node) { editor = p_node; anim_editor = memnew(AnimationPlayerEditor(editor, this)); anim_editor->set_undo_redo(editor->get_undo_redo()); - editor->add_bottom_panel_item(TTR("Animation"), anim_editor); - /* - editor->get_viewport()->add_child(anim_editor); - anim_editor->set_anchors_and_margins_preset(Control::PRESET_WIDE); - anim_editor->set_anchor( MARGIN_TOP, Control::ANCHOR_END); - anim_editor->set_margin( MARGIN_TOP, 75 ); - anim_editor->set_anchor( MARGIN_RIGHT, Control::ANCHOR_END); - anim_editor->set_margin( MARGIN_RIGHT, 0 );*/ - anim_editor->hide(); } AnimationPlayerEditorPlugin::~AnimationPlayerEditorPlugin() { diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h index 55f082aadb..b1026b5b9f 100644 --- a/editor/plugins/animation_player_editor_plugin.h +++ b/editor/plugins/animation_player_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -97,11 +97,10 @@ class AnimationPlayerEditor : public VBoxContainer { Button *play_from; Button *play_bw; Button *play_bw_from; - - //Button *pause; Button *autoplay; MenuButton *tool_anim; + ToolButton *onion_toggle; MenuButton *onion_skinning; ToolButton *pin; SpinBox *frame; @@ -172,7 +171,9 @@ class AnimationPlayerEditor : public VBoxContainer { void _autoplay_pressed(); void _stop_pressed(); void _pause_pressed(); + String _get_current_animation() const; void _animation_selected(int p_which); + void _current_animation_updated(); void _animation_new(); void _animation_rename(); void _animation_name_edited(); @@ -228,7 +229,6 @@ class AnimationPlayerEditor : public VBoxContainer { protected: void _notification(int p_what); - void _gui_input(Ref<InputEvent> p_event); void _node_removed(Node *p_node); static void _bind_methods(); diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp index 990c77430f..f06b4b2828 100644 --- a/editor/plugins/animation_state_machine_editor.cpp +++ b/editor/plugins/animation_state_machine_editor.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -118,7 +118,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv menu->add_item(TTR("Paste"), MENU_PASTE); } menu->add_separator(); - menu->add_item(TTR("Load.."), MENU_LOAD_FILE); + menu->add_item(TTR("Load..."), MENU_LOAD_FILE); menu->set_global_position(state_machine_draw->get_global_transform().xform(mb->get_position())); menu->popup(); @@ -226,7 +226,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv Ref<AnimationNode> an = state_machine->get_node(selected_node); updating = true; - undo_redo->create_action("Move Node"); + undo_redo->create_action(TTR("Move Node")); undo_redo->add_do_method(state_machine.ptr(), "set_node_position", selected_node, state_machine->get_node_position(selected_node) + drag_ofs / EDSCALE); undo_redo->add_undo_method(state_machine.ptr(), "set_node_position", selected_node, state_machine->get_node_position(selected_node)); undo_redo->add_do_method(this, "_update_graph"); @@ -271,7 +271,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv tr->set_switch_mode(AnimationNodeStateMachineTransition::SwitchMode(transition_mode->get_selected())); updating = true; - undo_redo->create_action("Add Transition"); + undo_redo->create_action(TTR("Add Transition")); undo_redo->add_do_method(state_machine.ptr(), "add_transition", connecting_from, connecting_to_node, tr); undo_redo->add_undo_method(state_machine.ptr(), "remove_transition", connecting_from, connecting_to_node); undo_redo->add_do_method(this, "_update_graph"); @@ -463,7 +463,7 @@ void AnimationNodeStateMachineEditor::_add_menu_type(int p_index) { } updating = true; - undo_redo->create_action("Add Node"); + undo_redo->create_action(TTR("Add Node")); undo_redo->add_do_method(state_machine.ptr(), "add_node", name, node, add_node_pos); undo_redo->add_undo_method(state_machine.ptr(), "remove_node", name); undo_redo->add_do_method(this, "_update_graph"); @@ -490,7 +490,7 @@ void AnimationNodeStateMachineEditor::_add_animation_type(int p_index) { } updating = true; - undo_redo->create_action("Add Node"); + undo_redo->create_action(TTR("Add Node")); undo_redo->add_do_method(state_machine.ptr(), "add_node", name, anim, add_node_pos); undo_redo->add_undo_method(state_machine.ptr(), "remove_node", name); undo_redo->add_do_method(this, "_update_graph"); @@ -572,8 +572,8 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { Ref<AnimationNodeStateMachinePlayback> playback = AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback"); - Ref<StyleBox> style = get_stylebox("frame", "GraphNode"); - Ref<StyleBox> style_selected = get_stylebox("selectedframe", "GraphNode"); + Ref<StyleBox> style = get_stylebox("state_machine_frame", "GraphNode"); + Ref<StyleBox> style_selected = get_stylebox("state_machine_selectedframe", "GraphNode"); Ref<Font> font = get_font("title_font", "GraphNode"); Color font_color = get_color("title_color", "GraphNode"); @@ -606,7 +606,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { state_machine->get_node_list(&nodes); node_rects.clear(); - Rect2 scroll_range(Point2(), state_machine_draw->get_size()); + Rect2 scroll_range; //snap lines if (dragging_selected) { @@ -714,12 +714,12 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { tl.to += offset; } - for (int i = 0; i < node_rects.size(); i++) { - if (node_rects[i].node_name == tl.from_node) { - _clip_src_line_to_rect(tl.from, tl.to, node_rects[i].node); + for (int j = 0; j < node_rects.size(); j++) { + if (node_rects[j].node_name == tl.from_node) { + _clip_src_line_to_rect(tl.from, tl.to, node_rects[j].node); } - if (node_rects[i].node_name == tl.to_node) { - _clip_dst_line_to_rect(tl.from, tl.to, node_rects[i].node); + if (node_rects[j].node_name == tl.to_node) { + _clip_dst_line_to_rect(tl.from, tl.to, node_rects[j].node); } } @@ -823,7 +823,8 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { } } - scroll_range = scroll_range.grow(200 * EDSCALE); + scroll_range.position -= state_machine_draw->get_size(); + scroll_range.size += state_machine_draw->get_size() * 2.0; //adjust scrollbars updating = true; @@ -873,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; @@ -1010,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(); @@ -1018,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(); } { @@ -1045,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; @@ -1064,6 +1088,7 @@ void AnimationNodeStateMachineEditor::_open_editor(const String &p_name) { } void AnimationNodeStateMachineEditor::_removed_from_graph() { + EditorNode::get_singleton()->edit_item(NULL); } @@ -1073,7 +1098,9 @@ void AnimationNodeStateMachineEditor::_name_edited(const String &p_text) { ERR_FAIL_COND(new_name == "" || new_name.find(".") != -1 || new_name.find("/") != -1) - ERR_FAIL_COND(new_name == prev_name); + if (new_name == prev_name) { + return; // Nothing to do. + } String base_name = new_name; int base = 1; @@ -1084,7 +1111,7 @@ void AnimationNodeStateMachineEditor::_name_edited(const String &p_text) { } updating = true; - undo_redo->create_action("Node Renamed"); + undo_redo->create_action(TTR("Node Renamed")); undo_redo->add_do_method(state_machine.ptr(), "rename_node", prev_name, name); undo_redo->add_undo_method(state_machine.ptr(), "rename_node", name, prev_name); undo_redo->add_do_method(this, "_update_graph"); @@ -1097,7 +1124,13 @@ void AnimationNodeStateMachineEditor::_name_edited(const String &p_text) { name_edit->hide(); } +void AnimationNodeStateMachineEditor::_name_edited_focus_out() { + + _name_edited(name_edit->get_text()); +} + void AnimationNodeStateMachineEditor::_scroll_changed(double) { + if (updating) return; @@ -1109,9 +1142,9 @@ void AnimationNodeStateMachineEditor::_erase_selected() { if (selected_node != StringName() && state_machine->has_node(selected_node)) { updating = true; - undo_redo->create_action("Node Removed"); + undo_redo->create_action(TTR("Node Removed")); undo_redo->add_do_method(state_machine.ptr(), "remove_node", selected_node); - undo_redo->add_undo_method(state_machine.ptr(), "add_node", selected_node, state_machine->get_node(selected_node)); + undo_redo->add_undo_method(state_machine.ptr(), "add_node", selected_node, state_machine->get_node(selected_node), state_machine->get_node_position(selected_node)); for (int i = 0; i < state_machine->get_transition_count(); i++) { String from = state_machine->get_transition_from(i); String to = state_machine->get_transition_to(i); @@ -1133,7 +1166,7 @@ void AnimationNodeStateMachineEditor::_erase_selected() { Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(state_machine->find_transition(selected_transition_from, selected_transition_to)); updating = true; - undo_redo->create_action("Transition Removed"); + undo_redo->create_action(TTR("Transition Removed")); undo_redo->add_do_method(state_machine.ptr(), "remove_transition", selected_transition_from, selected_transition_to); undo_redo->add_undo_method(state_machine.ptr(), "add_transition", selected_transition_from, selected_transition_to, tr); undo_redo->add_do_method(this, "_update_graph"); @@ -1159,7 +1192,7 @@ void AnimationNodeStateMachineEditor::_autoplay_selected() { } updating = true; - undo_redo->create_action("Set Start Node (Autoplay)"); + undo_redo->create_action(TTR("Set Start Node (Autoplay)")); undo_redo->add_do_method(state_machine.ptr(), "set_start_node", new_start_node); undo_redo->add_undo_method(state_machine.ptr(), "set_start_node", state_machine->get_start_node()); undo_redo->add_do_method(this, "_update_graph"); @@ -1182,7 +1215,7 @@ void AnimationNodeStateMachineEditor::_end_selected() { } updating = true; - undo_redo->create_action("Set Start Node (Autoplay)"); + undo_redo->create_action(TTR("Set Start Node (Autoplay)")); undo_redo->add_do_method(state_machine.ptr(), "set_end_node", new_end_node); undo_redo->add_undo_method(state_machine.ptr(), "set_end_node", state_machine->get_end_node()); undo_redo->add_do_method(this, "_update_graph"); @@ -1215,6 +1248,7 @@ void AnimationNodeStateMachineEditor::_bind_methods() { ClassDB::bind_method("_add_animation_type", &AnimationNodeStateMachineEditor::_add_animation_type); ClassDB::bind_method("_name_edited", &AnimationNodeStateMachineEditor::_name_edited); + ClassDB::bind_method("_name_edited_focus_out", &AnimationNodeStateMachineEditor::_name_edited_focus_out); ClassDB::bind_method("_removed_from_graph", &AnimationNodeStateMachineEditor::_removed_from_graph); @@ -1267,7 +1301,7 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { top_hb->add_child(tool_erase_hb); tool_erase_hb->add_child(memnew(VSeparator)); tool_erase = memnew(ToolButton); - tool_erase->set_tooltip(TTR("Remove selected node or transition")); + tool_erase->set_tooltip(TTR("Remove selected node or transition.")); tool_erase_hb->add_child(tool_erase); tool_erase->connect("pressed", this, "_erase_selected"); tool_erase->set_disabled(true); @@ -1297,17 +1331,13 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { play_mode = memnew(OptionButton); top_hb->add_child(play_mode); - GridContainer *main_grid = memnew(GridContainer); - main_grid->set_columns(2); - add_child(main_grid); - main_grid->set_v_size_flags(SIZE_EXPAND_FILL); - panel = memnew(PanelContainer); panel->set_clip_contents(true); - main_grid->add_child(panel); - panel->set_h_size_flags(SIZE_EXPAND_FILL); + add_child(panel); + panel->set_v_size_flags(SIZE_EXPAND_FILL); state_machine_draw = memnew(Control); + panel->add_child(state_machine_draw); state_machine_draw->connect("gui_input", this, "_state_machine_gui_input"); state_machine_draw->connect("draw", this, "_state_machine_draw"); state_machine_draw->set_focus_mode(FOCUS_ALL); @@ -1318,24 +1348,22 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { state_machine_play_pos->set_anchors_and_margins_preset(PRESET_WIDE); state_machine_play_pos->connect("draw", this, "_state_machine_pos_draw"); - panel->add_child(state_machine_draw); - panel->set_v_size_flags(SIZE_EXPAND_FILL); - v_scroll = memnew(VScrollBar); - main_grid->add_child(v_scroll); + state_machine_draw->add_child(v_scroll); + v_scroll->set_anchors_and_margins_preset(PRESET_RIGHT_WIDE); v_scroll->connect("value_changed", this, "_scroll_changed"); h_scroll = memnew(HScrollBar); - main_grid->add_child(h_scroll); + state_machine_draw->add_child(h_scroll); + h_scroll->set_anchors_and_margins_preset(PRESET_BOTTOM_WIDE); + h_scroll->set_margin(MARGIN_RIGHT, -v_scroll->get_size().x * EDSCALE); h_scroll->connect("value_changed", this, "_scroll_changed"); - main_grid->add_child(memnew(Control)); //empty bottom right - error_panel = memnew(PanelContainer); add_child(error_panel); error_label = memnew(Label); error_panel->add_child(error_label); - error_label->set_text("eh"); + error_panel->hide(); undo_redo = EditorNode::get_singleton()->get_undo_redo(); @@ -1354,6 +1382,7 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { state_machine_draw->add_child(name_edit); name_edit->hide(); name_edit->connect("text_entered", this, "_name_edited"); + name_edit->connect("focus_exited", this, "_name_edited_focus_out"); name_edit->set_as_toplevel(true); open_file = memnew(EditorFileDialog); diff --git a/editor/plugins/animation_state_machine_editor.h b/editor/plugins/animation_state_machine_editor.h index 7b8a4a0e94..8b0a5a0b00 100644 --- a/editor/plugins/animation_state_machine_editor.h +++ b/editor/plugins/animation_state_machine_editor.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -143,6 +143,7 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin { String prev_name; void _name_edited(const String &p_text); + void _name_edited_focus_out(); void _open_editor(const String &p_name); void _scroll_changed(double); @@ -159,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_editor_plugin.cpp b/editor/plugins/animation_tree_editor_plugin.cpp index 24787a78e9..2c1da3c10b 100644 --- a/editor/plugins/animation_tree_editor_plugin.cpp +++ b/editor/plugins/animation_tree_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -64,34 +64,15 @@ void AnimationTreeEditor::edit(AnimationTree *p_tree) { void AnimationTreeEditor::_path_button_pressed(int p_path) { - Ref<AnimationNode> node = tree->get_tree_root(); - if (node.is_null()) - return; - edited_path.clear(); - if (p_path >= 0) { - for (int i = 0; i <= p_path; i++) { - Ref<AnimationNode> child = node->get_child_by_name(button_path[i]); - ERR_BREAK(child.is_null()); - node = child; - edited_path.push_back(button_path[i]); - } - } - - for (int i = 0; i < editors.size(); i++) { - if (editors[i]->can_edit(node)) { - editors[i]->edit(node); - editors[i]->show(); - } else { - editors[i]->edit(Ref<AnimationNode>()); - editors[i]->hide(); - } + for (int i = 0; i <= p_path; i++) { + edited_path.push_back(button_path[i]); } } void AnimationTreeEditor::_update_path() { - while (path_hb->get_child_count()) { - memdelete(path_hb->get_child(0)); + while (path_hb->get_child_count() > 1) { + memdelete(path_hb->get_child(1)); } Ref<ButtonGroup> group; @@ -176,6 +157,10 @@ void AnimationTreeEditor::_notification(int p_what) { if (root != current_root) { edit_path(Vector<String>()); } + + if (button_path.size() != edited_path.size()) { + edit_path(edited_path); + } } } @@ -251,6 +236,9 @@ AnimationTreeEditor::AnimationTreeEditor() { path_edit->set_enable_v_scroll(false); path_hb = memnew(HBoxContainer); path_edit->add_child(path_hb); + path_hb->add_child(memnew(Label(TTR("Path:")))); + + add_child(memnew(HSeparator)); current_root = 0; singleton = this; diff --git a/editor/plugins/animation_tree_editor_plugin.h b/editor/plugins/animation_tree_editor_plugin.h index be8848d600..4a7f933bbf 100644 --- a/editor/plugins/animation_tree_editor_plugin.h +++ b/editor/plugins/animation_tree_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/animation_tree_player_editor_plugin.cpp b/editor/plugins/animation_tree_player_editor_plugin.cpp index c79e3a436d..f5d21ffb26 100644 --- a/editor/plugins/animation_tree_player_editor_plugin.cpp +++ b/editor/plugins/animation_tree_player_editor_plugin.cpp @@ -1,12 +1,12 @@ /*************************************************************************/ -/* animation_tree_editor_plugin.cpp */ +/* animation_tree_player_editor_plugin.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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/animation_tree_player_editor_plugin.h b/editor/plugins/animation_tree_player_editor_plugin.h index d1c5f395e4..f4bfe58909 100644 --- a/editor/plugins/animation_tree_player_editor_plugin.h +++ b/editor/plugins/animation_tree_player_editor_plugin.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* animation_tree_editor_plugin.h */ +/* animation_tree_player_editor_plugin.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index 138b8a491c..0dfb53b34a 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -174,8 +174,11 @@ void EditorAssetLibraryItemDescription::set_image(int p_type, int p_index, const if (preview_images[i].is_video) { Ref<Image> overlay = get_icon("PlayOverlay", "EditorIcons")->get_data(); Ref<Image> thumbnail = p_image->get_data(); - Point2 overlay_pos = Point2((thumbnail->get_width() - overlay->get_width()) / 2, (thumbnail->get_height() - overlay->get_height()) / 2); + thumbnail = thumbnail->duplicate(); + 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); thumbnail->lock(); thumbnail->blend_rect(overlay, overlay->get_used_rect(), overlay_pos); thumbnail->unlock(); @@ -307,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); @@ -451,7 +459,8 @@ void EditorAssetLibraryItemDownload::_notification(int p_what) { progress->set_max(1); progress->set_value(0); } break; - default: {} + default: { + } } prev_status = cstatus; } @@ -609,7 +618,8 @@ void EditorAssetLibrary::_notification(int p_what) { case HTTPClient::STATUS_BODY: { load_status->set_value(0.4); } break; - default: {} + default: { + } } } @@ -815,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")); @@ -1337,6 +1347,7 @@ void EditorAssetLibrary::_bind_methods() { EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) { + requesting = REQUESTING_NONE; templates_only = p_templates_only; VBoxContainer *library_main = memnew(VBoxContainer); diff --git a/editor/plugins/asset_library_editor_plugin.h b/editor/plugins/asset_library_editor_plugin.h index 69a65dd3dc..dd5f3c2077 100644 --- a/editor/plugins/asset_library_editor_plugin.h +++ b/editor/plugins/asset_library_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/audio_stream_editor_plugin.cpp b/editor/plugins/audio_stream_editor_plugin.cpp index 06ca5833e2..172096b1a7 100644 --- a/editor/plugins/audio_stream_editor_plugin.cpp +++ b/editor/plugins/audio_stream_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/audio_stream_editor_plugin.h b/editor/plugins/audio_stream_editor_plugin.h index 1887874b74..12e4faef94 100644 --- a/editor/plugins/audio_stream_editor_plugin.h +++ b/editor/plugins/audio_stream_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -33,7 +33,7 @@ #include "editor/editor_node.h" #include "editor/editor_plugin.h" -#include "scene/audio/audio_player.h" +#include "scene/audio/audio_stream_player.h" #include "scene/gui/color_rect.h" #include "scene/resources/texture.h" diff --git a/editor/plugins/baked_lightmap_editor_plugin.cpp b/editor/plugins/baked_lightmap_editor_plugin.cpp index 40e3bb5be2..d75f06de12 100644 --- a/editor/plugins/baked_lightmap_editor_plugin.cpp +++ b/editor/plugins/baked_lightmap_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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/baked_lightmap_editor_plugin.h b/editor/plugins/baked_lightmap_editor_plugin.h index 8d3b5b1dd6..ff4b59244f 100644 --- a/editor/plugins/baked_lightmap_editor_plugin.h +++ b/editor/plugins/baked_lightmap_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/camera_editor_plugin.cpp b/editor/plugins/camera_editor_plugin.cpp index 3d8b24ccc7..2531d59d1b 100644 --- a/editor/plugins/camera_editor_plugin.cpp +++ b/editor/plugins/camera_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/camera_editor_plugin.h b/editor/plugins/camera_editor_plugin.h index 0340808c9a..eac9acab93 100644 --- a/editor/plugins/camera_editor_plugin.h +++ b/editor/plugins/camera_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 8d9872236c..92cc12d931 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -42,9 +42,9 @@ #include "scene/2d/light_2d.h" #include "scene/2d/particles_2d.h" #include "scene/2d/polygon_2d.h" -#include "scene/2d/screen_button.h" #include "scene/2d/skeleton_2d.h" #include "scene/2d/sprite.h" +#include "scene/2d/touch_screen_button.h" #include "scene/gui/grid_container.h" #include "scene/gui/nine_patch_rect.h" #include "scene/main/canvas_layer.h" @@ -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); } @@ -398,10 +403,10 @@ Rect2 CanvasItemEditor::_get_encompassing_rect_from_list(List<CanvasItem *> p_li // Expand with the other ones for (List<CanvasItem *>::Element *E = p_list.front(); E; E = E->next()) { - CanvasItem *canvas_item = E->get(); - Transform2D xform = canvas_item->get_global_transform_with_canvas(); + CanvasItem *canvas_item2 = E->get(); + Transform2D xform = canvas_item2->get_global_transform_with_canvas(); - Rect2 current_rect = canvas_item->_edit_get_rect(); + Rect2 current_rect = canvas_item2->_edit_get_rect(); rect.expand_to(xform.xform(current_rect.position)); rect.expand_to(xform.xform(current_rect.position + Vector2(current_rect.size.x, 0))); rect.expand_to(xform.xform(current_rect.position + current_rect.size)); @@ -456,7 +461,7 @@ void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_no if (Object::cast_to<Viewport>(p_node)) return; - const real_t grab_distance = EDITOR_DEF("editors/poly_editor/point_grab_radius", 8); + const real_t grab_distance = EDITOR_GET("editors/poly_editor/point_grab_radius"); CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node); for (int i = p_node->get_child_count() - 1; i >= 0; i--) { @@ -474,7 +479,7 @@ void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_no if (canvas_item && canvas_item->is_visible_in_tree()) { Transform2D xform = (p_parent_xform * p_canvas_xform * canvas_item->get_transform()).affine_inverse(); - const real_t local_grab_distance = xform.basis_xform(Vector2(grab_distance, 0)).length(); + const real_t local_grab_distance = xform.basis_xform(Vector2(grab_distance, 0)).length() / zoom; if (canvas_item->_edit_is_selected_on_click(xform.xform(p_pos), local_grab_distance)) { Node2D *node = Object::cast_to<Node2D>(canvas_item); @@ -573,10 +578,10 @@ bool CanvasItemEditor::_get_bone_shape(Vector<Vector2> *shape, Vector<Vector2> * Node2D *from_node = Object::cast_to<Node2D>(ObjectDB::get_instance(bone->key().from)); Node2D *to_node = Object::cast_to<Node2D>(ObjectDB::get_instance(bone->key().to)); - if (!from_node->is_inside_tree()) - return false; //may have been removed if (!from_node) return false; + if (!from_node->is_inside_tree()) + return false; //may have been removed if (!to_node && bone->get().length == 0) return false; @@ -816,10 +821,10 @@ void CanvasItemEditor::_commit_canvas_item_state(List<CanvasItem *> p_canvas_ite undo_redo->add_do_method(canvas_item, "_edit_set_state", canvas_item->_edit_get_state()); undo_redo->add_undo_method(canvas_item, "_edit_set_state", se->undo_state); if (commit_bones) { - for (List<Dictionary>::Element *E = se->pre_drag_bones_undo_state.front(); E; E = E->next()) { + for (List<Dictionary>::Element *F = se->pre_drag_bones_undo_state.front(); F; F = F->next()) { canvas_item = Object::cast_to<CanvasItem>(canvas_item->get_parent()); undo_redo->add_do_method(canvas_item, "_edit_set_state", canvas_item->_edit_get_state()); - undo_redo->add_undo_method(canvas_item, "_edit_set_state", E->get()); + undo_redo->add_undo_method(canvas_item, "_edit_set_state", F->get()); } } } @@ -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; } @@ -1378,7 +1387,7 @@ bool CanvasItemEditor::_gui_input_anchors(const Ref<InputEvent> &p_event) { // Starts anchor dragging if needed if (drag_type == DRAG_NONE) { - if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && b->is_pressed() && tool == TOOL_SELECT && show_helpers) { + if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && b->is_pressed() && tool == TOOL_SELECT) { List<CanvasItem *> selection = _get_edited_canvas_items(); if (selection.size() == 1) { Control *control = Object::cast_to<Control>(selection[0]); @@ -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; @@ -1921,9 +1944,9 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { if (drag_selection.size() == 1) { Node2D *node_2d = Object::cast_to<Node2D>(drag_selection[0]); if (node_2d && move_local_base_rotated) { - Transform2D m; - m.rotate(node_2d->get_rotation()); - new_pos += m.xform(drag_to); + Transform2D m2; + m2.rotate(node_2d->get_rotation()); + new_pos += m2.xform(drag_to); } else if (move_local_base) { new_pos += drag_to; } else { @@ -2033,16 +2056,19 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) { // Find the item to select CanvasItem *canvas_item = NULL; - Vector<_SelectResult> selection; - - // Retrieve the items - _get_canvas_items_at_pos(click, selection); // Retrieve the bones + Vector<_SelectResult> selection = Vector<_SelectResult>(); _get_bones_at_pos(click, selection); - if (!selection.empty()) { canvas_item = selection[0].item; + } else { + // Retrieve the canvas items + selection = Vector<_SelectResult>(); + _get_canvas_items_at_pos(click, selection); + if (!selection.empty()) { + canvas_item = selection[0].item; + } } if (!canvas_item) { @@ -2062,18 +2088,18 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) { // Start dragging if (still_selected) { // Drag the node(s) if requested - List<CanvasItem *> selection = _get_edited_canvas_items(); + List<CanvasItem *> selection2 = _get_edited_canvas_items(); // Remove not movable nodes - for (int i = 0; i < selection.size(); i++) { - if (!_is_node_movable(selection[i], true)) { - selection.erase(selection[i]); + for (int i = 0; i < selection2.size(); i++) { + if (!_is_node_movable(selection2[i], true)) { + selection2.erase(selection2[i]); } } - if (selection.size() > 0) { + if (selection2.size() > 0) { drag_type = DRAG_MOVE; - drag_selection = selection; + drag_selection = selection2; drag_from = click; _save_canvas_item_state(drag_selection); } @@ -2187,32 +2213,35 @@ 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(); @@ -2256,8 +2285,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; } @@ -2486,20 +2513,50 @@ void CanvasItemEditor::_draw_grid() { } } -void CanvasItemEditor::_draw_control_helpers(Control *control) { +void CanvasItemEditor::_draw_control_anchors(Control *control) { Transform2D xform = transform * control->get_global_transform_with_canvas(); RID ci = viewport->get_canvas_item(); + if (tool == TOOL_SELECT && !Object::cast_to<Container>(control->get_parent())) { + + // Compute the anchors + float anchors_values[4]; + anchors_values[0] = control->get_anchor(MARGIN_LEFT); + anchors_values[1] = control->get_anchor(MARGIN_TOP); + anchors_values[2] = control->get_anchor(MARGIN_RIGHT); + anchors_values[3] = control->get_anchor(MARGIN_BOTTOM); + + Vector2 anchors_pos[4]; + for (int i = 0; i < 4; i++) { + Vector2 value = Vector2((i % 2 == 0) ? anchors_values[i] : anchors_values[(i + 1) % 4], (i % 2 == 1) ? anchors_values[i] : anchors_values[(i + 1) % 4]); + anchors_pos[i] = xform.xform(_anchor_to_position(control, value)); + } + + // Draw the anchors handles + Rect2 anchor_rects[4]; + anchor_rects[0] = Rect2(anchors_pos[0] - anchor_handle->get_size(), anchor_handle->get_size()); + anchor_rects[1] = Rect2(anchors_pos[1] - Vector2(0.0, anchor_handle->get_size().y), Point2(-anchor_handle->get_size().x, anchor_handle->get_size().y)); + anchor_rects[2] = Rect2(anchors_pos[2], -anchor_handle->get_size()); + anchor_rects[3] = Rect2(anchors_pos[3] - Vector2(anchor_handle->get_size().x, 0.0), Point2(anchor_handle->get_size().x, -anchor_handle->get_size().y)); + + for (int i = 0; i < 4; i++) { + anchor_handle->draw_rect(ci, anchor_rects[i]); + } + } +} + +void CanvasItemEditor::_draw_control_helpers(Control *control) { + Transform2D xform = transform * control->get_global_transform_with_canvas(); if (tool == TOOL_SELECT && show_helpers && !Object::cast_to<Container>(control->get_parent())) { // Draw the helpers Color color_base = Color(0.8, 0.8, 0.8, 0.5); + // Compute the anchors float anchors_values[4]; anchors_values[0] = control->get_anchor(MARGIN_LEFT); anchors_values[1] = control->get_anchor(MARGIN_TOP); anchors_values[2] = control->get_anchor(MARGIN_RIGHT); anchors_values[3] = control->get_anchor(MARGIN_BOTTOM); - // Draw the anchors Vector2 anchors[4]; Vector2 anchors_pos[4]; for (int i = 0; i < 4; i++) { @@ -2566,16 +2623,6 @@ void CanvasItemEditor::_draw_control_helpers(Control *control) { _draw_percentage_at_position(percent_val, (line_ends[(dragged_anchor + 1) % 4] + anchors_pos[dragged_anchor]) / 2, (Margin)((dragged_anchor + 1) % 4)); } - Rect2 anchor_rects[4]; - anchor_rects[0] = Rect2(anchors_pos[0] - anchor_handle->get_size(), anchor_handle->get_size()); - anchor_rects[1] = Rect2(anchors_pos[1] - Vector2(0.0, anchor_handle->get_size().y), Point2(-anchor_handle->get_size().x, anchor_handle->get_size().y)); - anchor_rects[2] = Rect2(anchors_pos[2], -anchor_handle->get_size()); - anchor_rects[3] = Rect2(anchors_pos[3] - Vector2(anchor_handle->get_size().x, 0.0), Point2(anchor_handle->get_size().x, -anchor_handle->get_size().y)); - - for (int i = 0; i < 4; i++) { - anchor_handle->draw_rect(ci, anchor_rects[i]); - } - // Draw the margin values and the node width/height when dragging control side float ratio = 0.33; Transform2D parent_transform = xform * control->get_transform().affine_inverse(); @@ -2594,6 +2641,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); @@ -2608,6 +2656,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); @@ -2622,6 +2671,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)); @@ -2636,6 +2686,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)); @@ -2735,13 +2786,21 @@ void CanvasItemEditor::_draw_selection() { if (single && (tool == TOOL_SELECT || tool == TOOL_MOVE || tool == TOOL_SCALE || tool == TOOL_ROTATE || tool == TOOL_EDIT_PIVOT)) { //kind of sucks // Draw the pivot - if (canvas_item->_edit_get_pivot() != Vector2() || drag_type == DRAG_PIVOT || tool == TOOL_EDIT_PIVOT) { // This is not really clean :/ - viewport->draw_texture(pivot_icon, (xform.xform(canvas_item->_edit_get_pivot()) - (pivot_icon->get_size() / 2)).floor()); + if (canvas_item->_edit_use_pivot()) { + + // Draw the node's pivot + Transform2D unscaled_transform = (xform * canvas_item->get_transform().affine_inverse() * Transform2D(canvas_item->_edit_get_rotation(), canvas_item->_edit_get_position() + canvas_item->_edit_get_pivot())).orthonormalized(); + Transform2D simple_xform = viewport->get_transform() * unscaled_transform; + + viewport->draw_set_transform_matrix(simple_xform); + viewport->draw_texture(pivot_icon, -(pivot_icon->get_size() / 2).floor()); + viewport->draw_set_transform_matrix(viewport->get_transform()); } // Draw control-related helpers Control *control = Object::cast_to<Control>(canvas_item); if (control && _is_node_movable(control)) { + _draw_control_anchors(control); _draw_control_helpers(control); } @@ -2818,13 +2877,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); } } @@ -3267,8 +3333,33 @@ void CanvasItemEditor::_notification(int p_what) { // Activate / Deactivate the pivot tool pivot_button->set_disabled(nb_having_pivot == 0); - // Show / Hide the layout button - presets_menu->set_visible(nb_control > 0 && nb_control == selection.size()); + // Show / Hide the layout and anchors mode buttons + if (nb_control > 0 && nb_control == selection.size()) { + presets_menu->set_visible(true); + presets_menu->set_tooltip(TTR("Presets for the anchors and margins values of a Control node.")); + anchor_mode_button->set_visible(true); + anchor_mode_button->set_tooltip(TTR("When active, moving Control nodes changes their anchors instead of their margins.")); + + // Set the pressed state of the node + anchor_mode_button->set_pressed(anchors_mode); + + // Disable if the selected node is child of a container + presets_menu->set_disabled(false); + anchor_mode_button->set_disabled(false); + for (List<CanvasItem *>::Element *E = selection.front(); E; E = E->next()) { + Control *control = Object::cast_to<Control>(E->get()); + if (!control || Object::cast_to<Container>(control->get_parent())) { + presets_menu->set_disabled(true); + presets_menu->set_tooltip(TTR("Children of containers have their anchors and margins values overridden by their parent.")); + anchor_mode_button->set_disabled(true); + anchor_mode_button->set_tooltip(TTR("Children of containers have their anchors and margins values overridden by their parent.")); + break; + } + } + } else { + presets_menu->set_visible(false); + anchor_mode_button->set_visible(false); + } // Update the viewport if bones changes for (Map<BoneKey, BoneList>::Element *E = bone_list.front(); E; E = E->next()) { @@ -3346,6 +3437,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")); @@ -3374,9 +3466,10 @@ void CanvasItemEditor::_notification(int p_what) { p->add_icon_item(get_icon("ControlHcenterWide", "EditorIcons"), "HCenter Wide ", ANCHORS_AND_MARGINS_PRESET_HCENTER_WIDE); p->add_separator(); p->add_icon_item(get_icon("ControlAlignWide", "EditorIcons"), "Full Rect", ANCHORS_AND_MARGINS_PRESET_WIDE); + p->add_icon_item(get_icon("Anchor", "EditorIcons"), "Keep Ratio", ANCHORS_AND_MARGINS_PRESET_KEEP_RATIO); p->add_separator(); p->add_submenu_item(TTR("Anchors only"), "Anchors"); - p->set_item_icon(20, get_icon("Anchor", "EditorIcons")); + p->set_item_icon(21, get_icon("Anchor", "EditorIcons")); anchors_popup->clear(); anchors_popup->add_icon_item(get_icon("ControlAlignTopLeft", "EditorIcons"), "Top Left", ANCHORS_PRESET_TOP_LEFT); @@ -3398,7 +3491,29 @@ void CanvasItemEditor::_notification(int p_what) { anchors_popup->add_icon_item(get_icon("ControlHcenterWide", "EditorIcons"), "HCenter Wide ", ANCHORS_PRESET_HCENTER_WIDE); anchors_popup->add_separator(); anchors_popup->add_icon_item(get_icon("ControlAlignWide", "EditorIcons"), "Full Rect", ANCHORS_PRESET_WIDE); + + anchor_mode_button->set_icon(get_icon("Anchor", "EditorIcons")); + } +} + +void CanvasItemEditor::_selection_changed() { + // Update the anchors_mode + int nbValidControls = 0; + int nbAnchorsMode = 0; + List<Node *> selection = editor_selection->get_selected_node_list(); + for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { + Control *control = Object::cast_to<Control>(E->get()); + if (!control) + continue; + if (Object::cast_to<Container>(control->get_parent())) + continue; + + nbValidControls++; + if (control->has_meta("_edit_use_anchors_") && control->get_meta("_edit_use_anchors_")) { + nbAnchorsMode++; + } } + anchors_mode = (nbValidControls == nbAnchorsMode); } void CanvasItemEditor::edit(CanvasItem *p_canvas_item) { @@ -3618,6 +3733,36 @@ void CanvasItemEditor::_set_anchors_and_margins_preset(Control::LayoutPreset p_p } undo_redo->commit_action(); + + anchors_mode = false; +} + +void CanvasItemEditor::_set_anchors_and_margins_to_keep_ratio() { + List<Node *> selection = editor_selection->get_selected_node_list(); + + undo_redo->create_action(TTR("Change Anchors and Margins")); + + for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { + + Control *control = Object::cast_to<Control>(E->get()); + if (control) { + Point2 top_left_anchor = _position_to_anchor(control, Point2()); + Point2 bottom_right_anchor = _position_to_anchor(control, control->get_size()); + undo_redo->add_do_method(control, "set_anchor", MARGIN_LEFT, top_left_anchor.x, false, true); + undo_redo->add_do_method(control, "set_anchor", MARGIN_RIGHT, bottom_right_anchor.x, false, true); + undo_redo->add_do_method(control, "set_anchor", MARGIN_TOP, top_left_anchor.y, false, true); + undo_redo->add_do_method(control, "set_anchor", MARGIN_BOTTOM, bottom_right_anchor.y, false, true); + undo_redo->add_do_method(control, "set_meta", "_edit_use_anchors_", true); + + bool use_anchors = control->has_meta("_edit_use_anchors_") && control->get_meta("_edit_use_anchors_"); + undo_redo->add_undo_method(control, "_edit_set_state", control->_edit_get_state()); + undo_redo->add_undo_method(control, "set_meta", "_edit_use_anchors_", use_anchors); + + anchors_mode = true; + } + } + + undo_redo->commit_action(); } void CanvasItemEditor::_set_anchors_preset(Control::LayoutPreset p_preset) { @@ -3678,6 +3823,92 @@ 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::_button_toggle_anchor_mode(bool p_status) { + List<CanvasItem *> selection = _get_edited_canvas_items(false, false); + for (List<CanvasItem *>::Element *E = selection.front(); E; E = E->next()) { + Control *control = Object::cast_to<Control>(E->get()); + if (!control || Object::cast_to<Container>(control->get_parent())) + continue; + + control->set_meta("_edit_use_anchors_", p_status); + } + + anchors_mode = p_status; + + viewport->update(); +} + void CanvasItemEditor::_popup_callback(int p_op) { last_option = MenuOption(p_op); @@ -3778,6 +4009,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { show_rulers = !show_rulers; int idx = view_menu->get_popup()->get_item_index(SHOW_RULERS); view_menu->get_popup()->set_item_checked(idx, show_rulers); + _update_scrollbars(); viewport->update(); } break; case SHOW_GUIDES: { @@ -3787,6 +4019,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()); @@ -3795,12 +4029,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()); @@ -3809,12 +4049,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()); @@ -3823,12 +4069,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()); @@ -3837,10 +4089,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); @@ -3890,6 +4146,9 @@ void CanvasItemEditor::_popup_callback(int p_op) { case ANCHORS_AND_MARGINS_PRESET_WIDE: { _set_anchors_and_margins_preset(Control::PRESET_WIDE); } break; + case ANCHORS_AND_MARGINS_PRESET_KEEP_RATIO: { + _set_anchors_and_margins_to_keep_ratio(); + } break; case ANCHORS_PRESET_TOP_LEFT: { _set_anchors_preset(PRESET_TOP_LEFT); @@ -3945,73 +4204,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: { @@ -4117,10 +4310,19 @@ 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(); + undo_redo->create_action(TTR("Create Custom Bone(s) from Node(s)")); for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) { Node2D *n2d = Object::cast_to<Node2D>(E->key()); @@ -4130,18 +4332,24 @@ void CanvasItemEditor::_popup_callback(int p_op) { continue; if (!n2d->get_parent_item()) continue; + if (n2d->has_meta("_edit_bone_") && (bool)n2d->get_meta("_edit_bone_") == true) + continue; - n2d->set_meta("_edit_bone_", true); - if (!skeleton_show_bones) - skeleton_menu->get_popup()->activate_item(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES)); + undo_redo->add_do_method(n2d, "set_meta", "_edit_bone_", true); + undo_redo->add_undo_method(n2d, "remove_meta", "_edit_bone_"); } - viewport->update(); + undo_redo->add_do_method(this, "_queue_update_bone_list"); + undo_redo->add_undo_method(this, "_queue_update_bone_list"); + undo_redo->add_do_method(viewport, "update"); + undo_redo->add_undo_method(viewport, "update"); + undo_redo->commit_action(); } break; case SKELETON_CLEAR_BONES: { Map<Node *, Object *> &selection = editor_selection->get_selection(); + undo_redo->create_action(TTR("Clear Bones")); for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) { Node2D *n2d = Object::cast_to<Node2D>(E->key()); @@ -4149,39 +4357,47 @@ void CanvasItemEditor::_popup_callback(int p_op) { continue; if (!n2d->is_visible_in_tree()) continue; + if (!n2d->has_meta("_edit_bone_")) + continue; - n2d->set_meta("_edit_bone_", Variant()); - if (!skeleton_show_bones) - skeleton_menu->get_popup()->activate_item(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES)); + undo_redo->add_do_method(n2d, "remove_meta", "_edit_bone_"); + undo_redo->add_undo_method(n2d, "set_meta", "_edit_bone_", n2d->get_meta("_edit_bone_")); } - viewport->update(); + undo_redo->add_do_method(this, "_queue_update_bone_list"); + undo_redo->add_undo_method(this, "_queue_update_bone_list"); + undo_redo->add_do_method(viewport, "update"); + undo_redo->add_undo_method(viewport, "update"); + undo_redo->commit_action(); } break; case SKELETON_SET_IK_CHAIN: { List<Node *> selection = editor_selection->get_selected_node_list(); + undo_redo->create_action(TTR("Make IK Chain")); for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->get()); if (!canvas_item || !canvas_item->is_visible_in_tree()) continue; - if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root()) continue; + if (canvas_item->has_meta("_edit_ik_") && (bool)canvas_item->get_meta("_edit_ik_") == true) + continue; - canvas_item->set_meta("_edit_ik_", true); - if (!skeleton_show_bones) - skeleton_menu->get_popup()->activate_item(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES)); + undo_redo->add_do_method(canvas_item, "set_meta", "_edit_ik_", true); + undo_redo->add_undo_method(canvas_item, "remove_meta", "_edit_ik_"); } - - viewport->update(); + undo_redo->add_do_method(viewport, "update"); + undo_redo->add_undo_method(viewport, "update"); + undo_redo->commit_action(); } break; case SKELETON_CLEAR_IK_CHAIN: { Map<Node *, Object *> &selection = editor_selection->get_selection(); + undo_redo->create_action(TTR("Clear IK Chain")); for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) { CanvasItem *n2d = Object::cast_to<CanvasItem>(E->key()); @@ -4189,12 +4405,15 @@ void CanvasItemEditor::_popup_callback(int p_op) { continue; if (!n2d->is_visible_in_tree()) continue; + if (!n2d->has_meta("_edit_ik_")) + continue; - n2d->set_meta("_edit_ik_", Variant()); - if (!skeleton_show_bones) - skeleton_menu->get_popup()->activate_item(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES)); + undo_redo->add_do_method(n2d, "remove_meta", "_edit_ik_"); + undo_redo->add_undo_method(n2d, "set_meta", "_edit_ik_", n2d->get_meta("_edit_ik_")); } - viewport->update(); + undo_redo->add_do_method(viewport, "update"); + undo_redo->add_undo_method(viewport, "update"); + undo_redo->commit_action(); } break; } @@ -4265,6 +4484,7 @@ void CanvasItemEditor::_bind_methods() { ClassDB::bind_method("_button_zoom_reset", &CanvasItemEditor::_button_zoom_reset); ClassDB::bind_method("_button_zoom_plus", &CanvasItemEditor::_button_zoom_plus); ClassDB::bind_method("_button_toggle_snap", &CanvasItemEditor::_button_toggle_snap); + ClassDB::bind_method("_button_toggle_anchor_mode", &CanvasItemEditor::_button_toggle_anchor_mode); ClassDB::bind_method("_update_scroll", &CanvasItemEditor::_update_scroll); ClassDB::bind_method("_update_scrollbars", &CanvasItemEditor::_update_scrollbars); ClassDB::bind_method("_popup_callback", &CanvasItemEditor::_popup_callback); @@ -4275,8 +4495,10 @@ void CanvasItemEditor::_bind_methods() { ClassDB::bind_method("_draw_viewport", &CanvasItemEditor::_draw_viewport); ClassDB::bind_method("_gui_input_viewport", &CanvasItemEditor::_gui_input_viewport); ClassDB::bind_method("_snap_changed", &CanvasItemEditor::_snap_changed); + ClassDB::bind_method("_queue_update_bone_list", &CanvasItemEditor::_update_bone_list); ClassDB::bind_method("_update_bone_list", &CanvasItemEditor::_update_bone_list); ClassDB::bind_method("_tree_changed", &CanvasItemEditor::_tree_changed); + ClassDB::bind_method("_selection_changed", &CanvasItemEditor::_selection_changed); ClassDB::bind_method("_popup_warning_depop", &CanvasItemEditor::_popup_warning_depop); ClassDB::bind_method(D_METHOD("_selection_result_pressed"), &CanvasItemEditor::_selection_result_pressed); ClassDB::bind_method(D_METHOD("_selection_menu_hide"), &CanvasItemEditor::_selection_menu_hide); @@ -4310,6 +4532,7 @@ Dictionary CanvasItemEditor::get_state() const { state["show_rulers"] = show_rulers; state["show_guides"] = show_guides; state["show_helpers"] = show_helpers; + state["show_zoom_control"] = zoom_hb->is_visible(); state["show_edit_locks"] = show_edit_locks; state["snap_rotation"] = snap_rotation; state["snap_relative"] = snap_relative; @@ -4320,6 +4543,7 @@ Dictionary CanvasItemEditor::get_state() const { void CanvasItemEditor::set_state(const Dictionary &p_state) { + bool update_scrollbars = false; Dictionary state = p_state; if (state.has("zoom")) { zoom = p_state["zoom"]; @@ -4328,7 +4552,7 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) { if (state.has("ofs")) { view_offset = p_state["ofs"]; previous_update_view_offset = view_offset; - _update_scrollbars(); + update_scrollbars = true; } if (state.has("grid_offset")) { @@ -4416,6 +4640,7 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) { show_rulers = state["show_rulers"]; int idx = view_menu->get_popup()->get_item_index(SHOW_RULERS); view_menu->get_popup()->set_item_checked(idx, show_rulers); + update_scrollbars = true; } if (state.has("show_guides")) { @@ -4436,6 +4661,11 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) { view_menu->get_popup()->set_item_checked(idx, show_edit_locks); } + if (state.has("show_zoom_control")) { + // This one is not user-controllable, but instrumentable + zoom_hb->set_visible(state["show_zoom_control"]); + } + if (state.has("snap_rotation")) { snap_rotation = state["snap_rotation"]; int idx = snap_config_menu->get_popup()->get_item_index(SNAP_USE_ROTATION); @@ -4460,6 +4690,9 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) { skeleton_menu->get_popup()->set_item_checked(idx, skeleton_show_bones); } + if (update_scrollbars) { + _update_scrollbars(); + } viewport->update(); } @@ -4532,8 +4765,11 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { snap_grid = true; snap_guides = true; snap_rotation = false; + snap_relative = false; snap_pixel = false; + anchors_mode = false; + skeleton_show_bones = true; drag_type = DRAG_NONE; @@ -4541,6 +4777,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { drag_to = Vector2(); dragged_guide_pos = Point2(); dragged_guide_index = -1; + panning = false; bone_last_frame = 0; @@ -4551,6 +4788,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { editor_selection = p_editor->get_editor_selection(); editor_selection->add_editor_plugin(this); editor_selection->connect("selection_changed", this, "update"); + editor_selection->connect("selection_changed", this, "_selection_changed"); hb = memnew(HBoxContainer); add_child(hb); @@ -4606,7 +4844,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { warning_child_of_container = memnew(Label); warning_child_of_container->hide(); - warning_child_of_container->set_text(TTR("Warning: Children of a container get their position and size determined only by their parent")); + warning_child_of_container->set_text(TTR("Warning: Children of a container get their position and size determined only by their parent.")); add_control_to_info_overlay(warning_child_of_container); h_scroll = memnew(HScrollBar); @@ -4707,28 +4945,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)); @@ -4758,6 +4997,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); @@ -4776,8 +5016,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); @@ -4789,11 +5031,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"); @@ -4803,6 +5048,12 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { anchors_popup->set_name("Anchors"); anchors_popup->connect("id_pressed", this, "_popup_callback"); + anchor_mode_button = memnew(ToolButton); + hb->add_child(anchor_mode_button); + anchor_mode_button->set_toggle_mode(true); + anchor_mode_button->hide(); + anchor_mode_button->connect("toggled", this, "_button_toggle_anchor_mode"); + animation_hb = memnew(HBoxContainer); hb->add_child(animation_hb); animation_hb->add_child(memnew(VSeparator)); @@ -4814,6 +5065,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); @@ -4821,26 +5073,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(); @@ -4894,7 +5156,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 { @@ -5350,7 +5611,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"); @@ -5387,6 +5648,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 207e57dbe2..e098d261c0 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -129,6 +129,7 @@ private: ANCHORS_AND_MARGINS_PRESET_VCENTER_WIDE, ANCHORS_AND_MARGINS_PRESET_HCENTER_WIDE, ANCHORS_AND_MARGINS_PRESET_WIDE, + ANCHORS_AND_MARGINS_PRESET_KEEP_RATIO, ANCHORS_PRESET_TOP_LEFT, ANCHORS_PRESET_TOP_RIGHT, ANCHORS_PRESET_BOTTOM_LEFT, @@ -171,6 +172,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 +206,7 @@ private: DRAG_V_GUIDE, DRAG_H_GUIDE, DRAG_DOUBLE_GUIDE, - DRAG_KEY_MOVE, - DRAG_PAN + DRAG_KEY_MOVE }; EditorSelection *editor_selection; @@ -240,6 +241,8 @@ private: Point2 view_offset; Point2 previous_update_view_offset; + bool anchors_mode; + Point2 grid_offset; Point2 grid_step; int grid_step_multiplier; @@ -261,6 +264,7 @@ private: bool key_pos; bool key_rot; bool key_scale; + bool panning; MenuOption last_option; @@ -346,10 +350,13 @@ private: PopupMenu *anchors_and_margins_popup; PopupMenu *anchors_popup; + ToolButton *anchor_mode_button; + Button *key_loc_button; Button *key_rot_button; Button *key_scale_button; Button *key_insert_button; + Button *key_auto_insert_button; PopupMenu *selection_menu; @@ -421,6 +428,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); @@ -434,6 +443,7 @@ private: void _draw_guides(); void _draw_focus(); void _draw_grid(); + void _draw_control_anchors(Control *control); void _draw_control_helpers(Control *control); void _draw_selection(); void _draw_axis(); @@ -458,6 +468,8 @@ private: void _gui_input_viewport(const Ref<InputEvent> &p_event); + void _selection_changed(); + void _focus_selection(int p_op); void _solve_IK(Node2D *leaf_node, Point2 target_position); @@ -469,6 +481,9 @@ private: void _set_anchors_preset(Control::LayoutPreset p_preset); void _set_margins_preset(Control::LayoutPreset p_preset); void _set_anchors_and_margins_preset(Control::LayoutPreset p_preset); + void _set_anchors_and_margins_to_keep_ratio(); + + void _button_toggle_anchor_mode(bool p_status); HBoxContainer *zoom_hb; void _zoom_on_position(float p_zoom, Point2 p_position = Point2()); @@ -571,6 +586,8 @@ public: void focus_selection(); + bool is_anchors_mode_enabled() { return anchors_mode; }; + CanvasItemEditor(EditorNode *p_editor); }; diff --git a/editor/plugins/collision_polygon_2d_editor_plugin.cpp b/editor/plugins/collision_polygon_2d_editor_plugin.cpp index 672337ba2f..0179adc148 100644 --- a/editor/plugins/collision_polygon_2d_editor_plugin.cpp +++ b/editor/plugins/collision_polygon_2d_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/collision_polygon_2d_editor_plugin.h b/editor/plugins/collision_polygon_2d_editor_plugin.h index ba691ab76a..e15360d4e5 100644 --- a/editor/plugins/collision_polygon_2d_editor_plugin.h +++ b/editor/plugins/collision_polygon_2d_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/collision_polygon_editor_plugin.cpp b/editor/plugins/collision_polygon_editor_plugin.cpp index e92a91db7c..6ab554cb05 100644 --- a/editor/plugins/collision_polygon_editor_plugin.cpp +++ b/editor/plugins/collision_polygon_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -144,7 +144,7 @@ bool Polygon3DEditor::forward_spatial_gui_input(Camera *p_camera, const Ref<Inpu Vector<Vector2> poly = node->call("get_polygon"); //first check if a point is to be added (segment split) - real_t grab_threshold = EDITOR_DEF("editors/poly_editor/point_grab_radius", 8); + real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius"); switch (mode) { diff --git a/editor/plugins/collision_polygon_editor_plugin.h b/editor/plugins/collision_polygon_editor_plugin.h index 4229808e2f..a699641aba 100644 --- a/editor/plugins/collision_polygon_editor_plugin.h +++ b/editor/plugins/collision_polygon_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/collision_shape_2d_editor_plugin.cpp b/editor/plugins/collision_shape_2d_editor_plugin.cpp index 313ba1ee6b..c8561d22a4 100644 --- a/editor/plugins/collision_shape_2d_editor_plugin.cpp +++ b/editor/plugins/collision_shape_2d_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -35,9 +35,9 @@ #include "scene/resources/circle_shape_2d.h" #include "scene/resources/concave_polygon_shape_2d.h" #include "scene/resources/convex_polygon_shape_2d.h" +#include "scene/resources/line_shape_2d.h" #include "scene/resources/rectangle_shape_2d.h" #include "scene/resources/segment_shape_2d.h" -#include "scene/resources/shape_line_2d.h" Variant CollisionShape2DEditor::get_handle_value(int idx) const { @@ -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/collision_shape_2d_editor_plugin.h b/editor/plugins/collision_shape_2d_editor_plugin.h index fb7b2acb0f..965e6c8827 100644 --- a/editor/plugins/collision_shape_2d_editor_plugin.h +++ b/editor/plugins/collision_shape_2d_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ 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 8d3ebc5052..70be9b95bb 100644 --- a/editor/plugins/cpu_particles_editor_plugin.cpp +++ b/editor/plugins/cpu_particles_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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/cpu_particles_editor_plugin.h b/editor/plugins/cpu_particles_editor_plugin.h index 16fb0bab0c..09b0adbe5d 100644 --- a/editor/plugins/cpu_particles_editor_plugin.h +++ b/editor/plugins/cpu_particles_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp index ace3012c10..55feb40c2c 100644 --- a/editor/plugins/curve_editor_plugin.cpp +++ b/editor/plugins/curve_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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/curve_editor_plugin.h b/editor/plugins/curve_editor_plugin.h index fa0b92e353..b034368b6a 100644 --- a/editor/plugins/curve_editor_plugin.h +++ b/editor/plugins/curve_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index 7f83865777..28e57ac48a 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -36,7 +36,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" -#include "scene/resources/bit_mask.h" +#include "scene/resources/bit_map.h" #include "scene/resources/dynamic_font.h" #include "scene/resources/material.h" #include "scene/resources/mesh.h" @@ -78,7 +78,7 @@ bool EditorTexturePreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "Texture"); } -bool EditorTexturePreviewPlugin::should_generate_small_preview() const { +bool EditorTexturePreviewPlugin::generate_small_preview_automatically() const { return true; } @@ -86,22 +86,32 @@ Ref<Texture> EditorTexturePreviewPlugin::generate(const RES &p_from, const Size2 Ref<Image> img; Ref<AtlasTexture> atex = p_from; + Ref<LargeTexture> ltex = p_from; if (atex.is_valid()) { Ref<Texture> tex = atex->get_atlas(); if (!tex.is_valid()) { return Ref<Texture>(); } + Ref<Image> atlas = tex->get_data(); + if (!atlas.is_valid()) { + return Ref<Texture>(); + } + img = atlas->get_rect(atex->get_region()); + } else if (ltex.is_valid()) { + img = ltex->to_image(); } else { Ref<Texture> tex = p_from; img = tex->get_data(); + if (img.is_valid()) { + img = img->duplicate(); + } } if (img.is_null() || img->empty()) return Ref<Texture>(); - img = img->duplicate(); img->clear_mipmaps(); if (img->is_compressed()) { @@ -176,7 +186,7 @@ Ref<Texture> EditorImagePreviewPlugin::generate(const RES &p_from, const Size2 p EditorImagePreviewPlugin::EditorImagePreviewPlugin() { } -bool EditorImagePreviewPlugin::should_generate_small_preview() const { +bool EditorImagePreviewPlugin::generate_small_preview_automatically() const { return true; } //////////////////////////////////////////////////////////////////////////// @@ -240,7 +250,7 @@ Ref<Texture> EditorBitmapPreviewPlugin::generate(const RES &p_from, const Size2 return ptex; } -bool EditorBitmapPreviewPlugin::should_generate_small_preview() const { +bool EditorBitmapPreviewPlugin::generate_small_preview_automatically() const { return true; } @@ -307,7 +317,7 @@ bool EditorMaterialPreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "Material"); //any material } -bool EditorMaterialPreviewPlugin::should_generate_small_preview() const { +bool EditorMaterialPreviewPlugin::generate_small_preview_automatically() const { return true; } @@ -329,7 +339,7 @@ Ref<Texture> EditorMaterialPreviewPlugin::generate(const RES &p_from, const Size OS::get_singleton()->delay_usec(10); } - Ref<Image> img = VS::get_singleton()->VS::get_singleton()->texture_get_data(viewport_texture); + Ref<Image> img = VS::get_singleton()->texture_get_data(viewport_texture); VS::get_singleton()->mesh_surface_set_material(sphere, 0, RID()); ERR_FAIL_COND_V(!img.is_valid(), Ref<ImageTexture>()); @@ -729,7 +739,7 @@ Ref<Texture> EditorMeshPreviewPlugin::generate(const RES &p_from, const Size2 p_ OS::get_singleton()->delay_usec(10); } - Ref<Image> img = VS::get_singleton()->VS::get_singleton()->texture_get_data(viewport_texture); + Ref<Image> img = VS::get_singleton()->texture_get_data(viewport_texture); ERR_FAIL_COND_V(img.is_null(), Ref<ImageTexture>()); VS::get_singleton()->instance_set_base(mesh_instance, RID()); @@ -840,16 +850,17 @@ Ref<Texture> EditorFontPreviewPlugin::generate_from_path(const String &p_path, c font->draw(canvas_item, pos, sampled_text); - VS::get_singleton()->viewport_set_update_mode(viewport, VS::VIEWPORT_UPDATE_ONCE); //once used for capture - preview_done = false; + VS::get_singleton()->viewport_set_update_mode(viewport, VS::VIEWPORT_UPDATE_ONCE); //once used for capture VS::get_singleton()->request_frame_drawn_callback(const_cast<EditorFontPreviewPlugin *>(this), "_preview_done", Variant()); while (!preview_done) { OS::get_singleton()->delay_usec(10); } - Ref<Image> img = VS::get_singleton()->VS::get_singleton()->texture_get_data(viewport_texture); + VS::get_singleton()->canvas_item_clear(canvas_item); + + Ref<Image> img = VS::get_singleton()->texture_get_data(viewport_texture); ERR_FAIL_COND_V(img.is_null(), Ref<ImageTexture>()); img->convert(Image::FORMAT_RGBA8); @@ -873,7 +884,11 @@ Ref<Texture> EditorFontPreviewPlugin::generate_from_path(const String &p_path, c Ref<Texture> EditorFontPreviewPlugin::generate(const RES &p_from, const Size2 p_size) const { - return generate_from_path(p_from->get_path(), p_size); + String path = p_from->get_path(); + if (!FileAccess::exists(path)) { + return Ref<Texture>(); + } + return generate_from_path(path, p_size); } EditorFontPreviewPlugin::EditorFontPreviewPlugin() { diff --git a/editor/plugins/editor_preview_plugins.h b/editor/plugins/editor_preview_plugins.h index ed5d2a3ecd..16b1f3082b 100644 --- a/editor/plugins/editor_preview_plugins.h +++ b/editor/plugins/editor_preview_plugins.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -39,7 +39,7 @@ class EditorTexturePreviewPlugin : public EditorResourcePreviewGenerator { GDCLASS(EditorTexturePreviewPlugin, EditorResourcePreviewGenerator) public: virtual bool handles(const String &p_type) const; - virtual bool should_generate_small_preview() const; + virtual bool generate_small_preview_automatically() const; virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; EditorTexturePreviewPlugin(); @@ -49,7 +49,7 @@ class EditorImagePreviewPlugin : public EditorResourcePreviewGenerator { GDCLASS(EditorImagePreviewPlugin, EditorResourcePreviewGenerator) public: virtual bool handles(const String &p_type) const; - virtual bool should_generate_small_preview() const; + virtual bool generate_small_preview_automatically() const; virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; EditorImagePreviewPlugin(); @@ -59,7 +59,7 @@ class EditorBitmapPreviewPlugin : public EditorResourcePreviewGenerator { GDCLASS(EditorBitmapPreviewPlugin, EditorResourcePreviewGenerator) public: virtual bool handles(const String &p_type) const; - virtual bool should_generate_small_preview() const; + virtual bool generate_small_preview_automatically() const; virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; EditorBitmapPreviewPlugin(); @@ -98,7 +98,7 @@ protected: public: virtual bool handles(const String &p_type) const; - virtual bool should_generate_small_preview() const; + virtual bool generate_small_preview_automatically() const; virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; EditorMaterialPreviewPlugin(); diff --git a/editor/plugins/gi_probe_editor_plugin.cpp b/editor/plugins/gi_probe_editor_plugin.cpp index 5fc5cad1ef..a6125af1f7 100644 --- a/editor/plugins/gi_probe_editor_plugin.cpp +++ b/editor/plugins/gi_probe_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/gi_probe_editor_plugin.h b/editor/plugins/gi_probe_editor_plugin.h index 1b3b63f227..c33668ae19 100644 --- a/editor/plugins/gi_probe_editor_plugin.h +++ b/editor/plugins/gi_probe_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/gradient_editor_plugin.cpp b/editor/plugins/gradient_editor_plugin.cpp index 442bd52ea7..e85c475ad7 100644 --- a/editor/plugins/gradient_editor_plugin.cpp +++ b/editor/plugins/gradient_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -51,7 +51,7 @@ void GradientEditor::_ramp_changed() { editing = true; UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); - undo_redo->create_action("Gradient Edited"); + undo_redo->create_action(TTR("Gradient Edited")); undo_redo->add_do_method(gradient.ptr(), "set_offsets", get_offsets()); undo_redo->add_do_method(gradient.ptr(), "set_colors", get_colors()); undo_redo->add_undo_method(gradient.ptr(), "set_offsets", gradient->get_offsets()); diff --git a/editor/plugins/gradient_editor_plugin.h b/editor/plugins/gradient_editor_plugin.h index 0c878b168f..91790abee8 100644 --- a/editor/plugins/gradient_editor_plugin.h +++ b/editor/plugins/gradient_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/item_list_editor_plugin.cpp b/editor/plugins/item_list_editor_plugin.cpp index a32f42cc56..b3f3ccf501 100644 --- a/editor/plugins/item_list_editor_plugin.cpp +++ b/editor/plugins/item_list_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -109,7 +109,7 @@ void ItemListPlugin::_get_property_list(List<PropertyInfo> *p_list) const { int flags = get_flags(); if (flags & FLAG_CHECKABLE) { - p_list->push_back(PropertyInfo(Variant::BOOL, base + "checkable", PROPERTY_HINT_ENUM, "No,As checkbox,As radio button")); + p_list->push_back(PropertyInfo(Variant::INT, base + "checkable", PROPERTY_HINT_ENUM, "No,As checkbox,As radio button")); p_list->push_back(PropertyInfo(Variant::BOOL, base + "checked")); } @@ -353,6 +353,7 @@ void ItemListEditor::_bind_methods() { ItemListEditor::ItemListEditor() { selected_idx = -1; + item_list = NULL; toolbar_button = memnew(ToolButton); toolbar_button->set_text(TTR("Items")); diff --git a/editor/plugins/item_list_editor_plugin.h b/editor/plugins/item_list_editor_plugin.h index 3dc3775f83..701632e576 100644 --- a/editor/plugins/item_list_editor_plugin.h +++ b/editor/plugins/item_list_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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/light_occluder_2d_editor_plugin.cpp b/editor/plugins/light_occluder_2d_editor_plugin.cpp index 646883fbda..d8f6259598 100644 --- a/editor/plugins/light_occluder_2d_editor_plugin.cpp +++ b/editor/plugins/light_occluder_2d_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/light_occluder_2d_editor_plugin.h b/editor/plugins/light_occluder_2d_editor_plugin.h index 6117d50e89..633fda7091 100644 --- a/editor/plugins/light_occluder_2d_editor_plugin.h +++ b/editor/plugins/light_occluder_2d_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/line_2d_editor_plugin.cpp b/editor/plugins/line_2d_editor_plugin.cpp index ba6452c1d1..368cf719c6 100644 --- a/editor/plugins/line_2d_editor_plugin.cpp +++ b/editor/plugins/line_2d_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/line_2d_editor_plugin.h b/editor/plugins/line_2d_editor_plugin.h index e2e42c2087..a7a9813cb5 100644 --- a/editor/plugins/line_2d_editor_plugin.h +++ b/editor/plugins/line_2d_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/material_editor_plugin.cpp b/editor/plugins/material_editor_plugin.cpp index 5e59a73061..ebacccb03c 100644 --- a/editor/plugins/material_editor_plugin.cpp +++ b/editor/plugins/material_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -32,6 +32,210 @@ #include "scene/resources/particles_material.h" +void MaterialEditor::_notification(int p_what) { + + if (p_what == NOTIFICATION_PHYSICS_PROCESS) { + } + + if (p_what == NOTIFICATION_READY) { + + //get_scene()->connect("node_removed",this,"_node_removed"); + + if (first_enter) { + //it's in propertyeditor so.. could be moved around + + light_1_switch->set_normal_texture(get_icon("MaterialPreviewLight1", "EditorIcons")); + light_1_switch->set_pressed_texture(get_icon("MaterialPreviewLight1Off", "EditorIcons")); + light_2_switch->set_normal_texture(get_icon("MaterialPreviewLight2", "EditorIcons")); + light_2_switch->set_pressed_texture(get_icon("MaterialPreviewLight2Off", "EditorIcons")); + + sphere_switch->set_normal_texture(get_icon("MaterialPreviewSphereOff", "EditorIcons")); + sphere_switch->set_pressed_texture(get_icon("MaterialPreviewSphere", "EditorIcons")); + box_switch->set_normal_texture(get_icon("MaterialPreviewCubeOff", "EditorIcons")); + box_switch->set_pressed_texture(get_icon("MaterialPreviewCube", "EditorIcons")); + + first_enter = false; + } + } + + if (p_what == NOTIFICATION_DRAW) { + + Ref<Texture> checkerboard = get_icon("Checkerboard", "EditorIcons"); + Size2 size = get_size(); + + draw_texture_rect(checkerboard, Rect2(Point2(), size), true); + } +} + +void MaterialEditor::edit(Ref<Material> p_material, const Ref<Environment> &p_env) { + + material = p_material; + camera->set_environment(p_env); + if (!material.is_null()) { + sphere_instance->set_material_override(material); + box_instance->set_material_override(material); + } else { + + hide(); + } +} + +void MaterialEditor::_button_pressed(Node *p_button) { + + if (p_button == light_1_switch) { + light1->set_visible(!light_1_switch->is_pressed()); + } + + if (p_button == light_2_switch) { + light2->set_visible(!light_2_switch->is_pressed()); + } + + if (p_button == box_switch) { + box_instance->show(); + sphere_instance->hide(); + box_switch->set_pressed(true); + sphere_switch->set_pressed(false); + } + + if (p_button == sphere_switch) { + box_instance->hide(); + sphere_instance->show(); + box_switch->set_pressed(false); + sphere_switch->set_pressed(true); + } +} + +void MaterialEditor::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_button_pressed"), &MaterialEditor::_button_pressed); +} + +MaterialEditor::MaterialEditor() { + + vc = memnew(ViewportContainer); + vc->set_stretch(true); + add_child(vc); + vc->set_anchors_and_margins_preset(PRESET_WIDE); + viewport = memnew(Viewport); + Ref<World> world; + world.instance(); + viewport->set_world(world); //use own world + vc->add_child(viewport); + viewport->set_disable_input(true); + viewport->set_transparent_background(true); + viewport->set_msaa(Viewport::MSAA_4X); + + camera = memnew(Camera); + camera->set_transform(Transform(Basis(), Vector3(0, 0, 3))); + camera->set_perspective(45, 0.1, 10); + camera->make_current(); + viewport->add_child(camera); + + light1 = memnew(DirectionalLight); + light1->set_transform(Transform().looking_at(Vector3(-1, -1, -1), Vector3(0, 1, 0))); + viewport->add_child(light1); + + light2 = memnew(DirectionalLight); + light2->set_transform(Transform().looking_at(Vector3(0, 1, 0), Vector3(0, 0, 1))); + light2->set_color(Color(0.7, 0.7, 0.7)); + viewport->add_child(light2); + + sphere_instance = memnew(MeshInstance); + viewport->add_child(sphere_instance); + + box_instance = memnew(MeshInstance); + viewport->add_child(box_instance); + + Transform box_xform; + box_xform.basis.rotate(Vector3(1, 0, 0), Math::deg2rad(25.0)); + box_xform.basis = box_xform.basis * Basis().rotated(Vector3(0, 1, 0), Math::deg2rad(-25.0)); + box_xform.basis.scale(Vector3(0.8, 0.8, 0.8)); + box_xform.origin.y = 0.2; + box_instance->set_transform(box_xform); + + sphere_mesh.instance(); + sphere_instance->set_mesh(sphere_mesh); + box_mesh.instance(); + box_instance->set_mesh(box_mesh); + box_instance->hide(); + + set_custom_minimum_size(Size2(1, 150) * EDSCALE); + + HBoxContainer *hb = memnew(HBoxContainer); + add_child(hb); + hb->set_anchors_and_margins_preset(Control::PRESET_WIDE, Control::PRESET_MODE_MINSIZE, 2); + + VBoxContainer *vb_shape = memnew(VBoxContainer); + hb->add_child(vb_shape); + + sphere_switch = memnew(TextureButton); + sphere_switch->set_toggle_mode(true); + sphere_switch->set_pressed(true); + vb_shape->add_child(sphere_switch); + sphere_switch->connect("pressed", this, "_button_pressed", varray(sphere_switch)); + + box_switch = memnew(TextureButton); + box_switch->set_toggle_mode(true); + box_switch->set_pressed(false); + vb_shape->add_child(box_switch); + box_switch->connect("pressed", this, "_button_pressed", varray(box_switch)); + + hb->add_spacer(); + + VBoxContainer *vb_light = memnew(VBoxContainer); + hb->add_child(vb_light); + + light_1_switch = memnew(TextureButton); + light_1_switch->set_toggle_mode(true); + vb_light->add_child(light_1_switch); + light_1_switch->connect("pressed", this, "_button_pressed", varray(light_1_switch)); + + light_2_switch = memnew(TextureButton); + light_2_switch->set_toggle_mode(true); + vb_light->add_child(light_2_switch); + light_2_switch->connect("pressed", this, "_button_pressed", varray(light_2_switch)); + + first_enter = true; +} + +/////////////////////// + +bool EditorInspectorPluginMaterial::can_handle(Object *p_object) { + + Material *material = Object::cast_to<Material>(p_object); + if (!material) + return false; + + return material->get_shader_mode() == Shader::MODE_SPATIAL; +} + +void EditorInspectorPluginMaterial::parse_begin(Object *p_object) { + + Material *material = Object::cast_to<Material>(p_object); + if (!material) { + return; + } + Ref<Material> m(material); + + MaterialEditor *editor = memnew(MaterialEditor); + editor->edit(m, env); + add_custom_control(editor); +} + +EditorInspectorPluginMaterial::EditorInspectorPluginMaterial() { + env.instance(); + Ref<ProceduralSky> proc_sky = memnew(ProceduralSky(true)); + env->set_sky(proc_sky); + env->set_background(Environment::BG_COLOR_SKY); +} + +MaterialEditorPlugin::MaterialEditorPlugin(EditorNode *p_node) { + + Ref<EditorInspectorPluginMaterial> plugin; + plugin.instance(); + add_inspector_plugin(plugin); +} + String SpatialMaterialConversionPlugin::converts_to() const { return "ShaderMaterial"; diff --git a/editor/plugins/material_editor_plugin.h b/editor/plugins/material_editor_plugin.h index c06d95e700..c3f14c27e5 100644 --- a/editor/plugins/material_editor_plugin.h +++ b/editor/plugins/material_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -32,6 +32,71 @@ #define MATERIAL_EDITOR_PLUGIN_H #include "editor/property_editor.h" +#include "scene/resources/primitive_meshes.h" + +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" +#include "scene/3d/camera.h" +#include "scene/3d/light.h" +#include "scene/3d/mesh_instance.h" +#include "scene/resources/material.h" + +class MaterialEditor : public Control { + + GDCLASS(MaterialEditor, Control); + + ViewportContainer *vc; + Viewport *viewport; + MeshInstance *sphere_instance; + MeshInstance *box_instance; + DirectionalLight *light1; + DirectionalLight *light2; + Camera *camera; + + Ref<SphereMesh> sphere_mesh; + Ref<CubeMesh> box_mesh; + + TextureButton *sphere_switch; + TextureButton *box_switch; + + TextureButton *light_1_switch; + TextureButton *light_2_switch; + + Ref<Material> material; + + void _button_pressed(Node *p_button); + bool first_enter; + +protected: + void _notification(int p_what); + + static void _bind_methods(); + +public: + void edit(Ref<Material> p_material, const Ref<Environment> &p_env); + MaterialEditor(); +}; + +class EditorInspectorPluginMaterial : public EditorInspectorPlugin { + GDCLASS(EditorInspectorPluginMaterial, EditorInspectorPlugin) + Ref<Environment> env; + +public: + virtual bool can_handle(Object *p_object); + virtual void parse_begin(Object *p_object); + + EditorInspectorPluginMaterial(); +}; + +class MaterialEditorPlugin : public EditorPlugin { + + GDCLASS(MaterialEditorPlugin, EditorPlugin); + +public: + virtual String get_name() const { return "Material"; } + + MaterialEditorPlugin(EditorNode *p_node); +}; class SpatialMaterialConversionPlugin : public EditorResourceConversionPlugin { GDCLASS(SpatialMaterialConversionPlugin, EditorResourceConversionPlugin) diff --git a/editor/plugins/mesh_editor_plugin.cpp b/editor/plugins/mesh_editor_plugin.cpp index 73a216e96f..6203035e25 100644 --- a/editor/plugins/mesh_editor_plugin.cpp +++ b/editor/plugins/mesh_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -80,26 +80,21 @@ void MeshEditor::edit(Ref<Mesh> p_mesh) { mesh = p_mesh; mesh_instance->set_mesh(mesh); - if (mesh.is_null()) { - - hide(); - } else { - rot_x = 0; - rot_y = 0; - _update_rotation(); - - AABB aabb = mesh->get_aabb(); - Vector3 ofs = aabb.position + aabb.size * 0.5; - float m = aabb.get_longest_axis_size(); - if (m != 0) { - m = 1.0 / m; - m *= 0.5; - Transform xform; - xform.basis.scale(Vector3(m, m, m)); - xform.origin = -xform.basis.xform(ofs); //-ofs*m; - //xform.origin.z -= aabb.get_longest_axis_size() * 2; - mesh_instance->set_transform(xform); - } + rot_x = Math::deg2rad(-15.0); + rot_y = Math::deg2rad(30.0); + _update_rotation(); + + AABB aabb = mesh->get_aabb(); + Vector3 ofs = aabb.position + aabb.size * 0.5; + float m = aabb.get_longest_axis_size(); + if (m != 0) { + m = 1.0 / m; + m *= 0.5; + Transform xform; + xform.basis.scale(Vector3(m, m, m)); + xform.origin = -xform.basis.xform(ofs); //-ofs*m; + //xform.origin.z -= aabb.get_longest_axis_size() * 2; + mesh_instance->set_transform(xform); } } @@ -128,8 +123,8 @@ MeshEditor::MeshEditor() { viewport->set_world(world); //use own world add_child(viewport); viewport->set_disable_input(true); + viewport->set_msaa(Viewport::MSAA_2X); set_stretch(true); - camera = memnew(Camera); camera->set_transform(Transform(Basis(), Vector3(0, 0, 1.1))); camera->set_perspective(45, 0.1, 10); @@ -176,39 +171,29 @@ MeshEditor::MeshEditor() { rot_y = 0; } -void MeshEditorPlugin::edit(Object *p_object) { - - Mesh *s = Object::cast_to<Mesh>(p_object); - if (!s) - return; - - mesh_editor->edit(Ref<Mesh>(s)); -} +/////////////////////// -bool MeshEditorPlugin::handles(Object *p_object) const { +bool EditorInspectorPluginMesh::can_handle(Object *p_object) { - return p_object->is_class("Mesh"); + return Object::cast_to<Mesh>(p_object) != NULL; } -void MeshEditorPlugin::make_visible(bool p_visible) { +void EditorInspectorPluginMesh::parse_begin(Object *p_object) { - if (p_visible) { - mesh_editor->show(); - //mesh_editor->set_process(true); - } else { - - mesh_editor->hide(); - //mesh_editor->set_process(false); + Mesh *mesh = Object::cast_to<Mesh>(p_object); + if (!mesh) { + return; } + Ref<Mesh> m(mesh); + + MeshEditor *editor = memnew(MeshEditor); + editor->edit(m); + add_custom_control(editor); } MeshEditorPlugin::MeshEditorPlugin(EditorNode *p_node) { - editor = p_node; - mesh_editor = memnew(MeshEditor); - add_control_to_container(CONTAINER_PROPERTY_EDITOR_BOTTOM, mesh_editor); - mesh_editor->hide(); -} - -MeshEditorPlugin::~MeshEditorPlugin() { + Ref<EditorInspectorPluginMesh> plugin; + plugin.instance(); + add_inspector_plugin(plugin); } diff --git a/editor/plugins/mesh_editor_plugin.h b/editor/plugins/mesh_editor_plugin.h index cc20bfcbf3..8ada2dac90 100644 --- a/editor/plugins/mesh_editor_plugin.h +++ b/editor/plugins/mesh_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -72,22 +72,21 @@ public: MeshEditor(); }; +class EditorInspectorPluginMesh : public EditorInspectorPlugin { + GDCLASS(EditorInspectorPluginMesh, EditorInspectorPlugin) +public: + virtual bool can_handle(Object *p_object); + virtual void parse_begin(Object *p_object); +}; + class MeshEditorPlugin : public EditorPlugin { GDCLASS(MeshEditorPlugin, EditorPlugin); - MeshEditor *mesh_editor; - EditorNode *editor; - public: virtual String get_name() const { return "Mesh"; } - 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); MeshEditorPlugin(EditorNode *p_node); - ~MeshEditorPlugin(); }; #endif diff --git a/editor/plugins/mesh_instance_editor_plugin.cpp b/editor/plugins/mesh_instance_editor_plugin.cpp index 74b6f14c2e..635b934333 100644 --- a/editor/plugins/mesh_instance_editor_plugin.cpp +++ b/editor/plugins/mesh_instance_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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; @@ -198,14 +223,14 @@ void MeshInstanceEditor::_menu_option(int p_option) { } break; case MENU_OPTION_CREATE_UV2: { - Ref<ArrayMesh> mesh = node->get_mesh(); - if (!mesh.is_valid()) { + Ref<ArrayMesh> mesh2 = node->get_mesh(); + if (!mesh2.is_valid()) { err_dialog->set_text(TTR("Contained Mesh is not of type ArrayMesh.")); err_dialog->popup_centered_minsize(); return; } - Error err = mesh->lightmap_unwrap(node->get_global_transform()); + Error err = mesh2->lightmap_unwrap(node->get_global_transform()); if (err != OK) { err_dialog->set_text(TTR("UV Unwrap failed, mesh may not be manifold?")); err_dialog->popup_centered_minsize(); @@ -214,8 +239,8 @@ void MeshInstanceEditor::_menu_option(int p_option) { } break; case MENU_OPTION_DEBUG_UV1: { - Ref<Mesh> mesh = node->get_mesh(); - if (!mesh.is_valid()) { + Ref<Mesh> mesh2 = node->get_mesh(); + if (!mesh2.is_valid()) { err_dialog->set_text(TTR("No mesh to debug.")); err_dialog->popup_centered_minsize(); return; @@ -223,8 +248,8 @@ void MeshInstanceEditor::_menu_option(int p_option) { _create_uv_lines(0); } break; case MENU_OPTION_DEBUG_UV2: { - Ref<Mesh> mesh = node->get_mesh(); - if (!mesh.is_valid()) { + Ref<Mesh> mesh2 = node->get_mesh(); + if (!mesh2.is_valid()) { err_dialog->set_text(TTR("No mesh to debug.")); err_dialog->popup_centered_minsize(); return; @@ -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_instance_editor_plugin.h b/editor/plugins/mesh_instance_editor_plugin.h index 30caeae7ec..d82af621df 100644 --- a/editor/plugins/mesh_instance_editor_plugin.h +++ b/editor/plugins/mesh_instance_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/mesh_library_editor_plugin.cpp b/editor/plugins/mesh_library_editor_plugin.cpp index c24c96bdc5..18586b2fe5 100644 --- a/editor/plugins/mesh_library_editor_plugin.cpp +++ b/editor/plugins/mesh_library_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -61,7 +61,8 @@ void MeshLibraryEditor::_menu_confirm() { _import_scene_cbk(existing); } break; - default: {}; + default: { + }; } } @@ -127,7 +128,7 @@ void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library, continue; MeshLibrary::ShapeData shape_data; shape_data.shape = collision; - shape_data.local_transform = sb->shape_owner_get_transform(E->get()); + shape_data.local_transform = sb->get_transform() * sb->shape_owner_get_transform(E->get()); collisions.push_back(shape_data); } } @@ -136,17 +137,20 @@ void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library, p_library->set_item_shapes(id, collisions); Ref<NavigationMesh> navmesh; + Transform navmesh_transform; for (int j = 0; j < mi->get_child_count(); j++) { Node *child2 = mi->get_child(j); if (!Object::cast_to<NavigationMeshInstance>(child2)) continue; NavigationMeshInstance *sb = Object::cast_to<NavigationMeshInstance>(child2); navmesh = sb->get_navigation_mesh(); + navmesh_transform = sb->get_transform(); if (!navmesh.is_null()) break; } if (!navmesh.is_null()) { p_library->set_item_navmesh(id, navmesh); + p_library->set_item_navmesh_transform(id, navmesh_transform); } } diff --git a/editor/plugins/mesh_library_editor_plugin.h b/editor/plugins/mesh_library_editor_plugin.h index be33b5324d..8121ff52bd 100644 --- a/editor/plugins/mesh_library_editor_plugin.h +++ b/editor/plugins/mesh_library_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/multimesh_editor_plugin.cpp b/editor/plugins/multimesh_editor_plugin.cpp index 45f8e25959..fc0a425bfc 100644 --- a/editor/plugins/multimesh_editor_plugin.cpp +++ b/editor/plugins/multimesh_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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/multimesh_editor_plugin.h b/editor/plugins/multimesh_editor_plugin.h index 201e735893..fe87a2b9cb 100644 --- a/editor/plugins/multimesh_editor_plugin.h +++ b/editor/plugins/multimesh_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/navigation_polygon_editor_plugin.cpp b/editor/plugins/navigation_polygon_editor_plugin.cpp index 0332e15b0e..0d6b649526 100644 --- a/editor/plugins/navigation_polygon_editor_plugin.cpp +++ b/editor/plugins/navigation_polygon_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/navigation_polygon_editor_plugin.h b/editor/plugins/navigation_polygon_editor_plugin.h index 738aa9fa41..336c28d642 100644 --- a/editor/plugins/navigation_polygon_editor_plugin.h +++ b/editor/plugins/navigation_polygon_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/particles_2d_editor_plugin.cpp b/editor/plugins/particles_2d_editor_plugin.cpp index ab94258c44..4c354b8002 100644 --- a/editor/plugins/particles_2d_editor_plugin.cpp +++ b/editor/plugins/particles_2d_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -85,21 +85,21 @@ void Particles2DEditorPlugin::_menu_callback(int p_idx) { } break; case MENU_OPTION_CONVERT_TO_CPU_PARTICLES: { - UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); - CPUParticles2D *cpu_particles = memnew(CPUParticles2D); cpu_particles->convert_from_particles(particles); cpu_particles->set_name(particles->get_name()); cpu_particles->set_transform(particles->get_transform()); cpu_particles->set_visible(particles->is_visible()); cpu_particles->set_pause_mode(particles->get_pause_mode()); + cpu_particles->set_z_index(particles->get_z_index()); - undo_redo->create_action("Replace Particles by CPUParticles"); - undo_redo->add_do_method(particles, "replace_by", cpu_particles); - undo_redo->add_undo_method(cpu_particles, "replace_by", particles); - undo_redo->add_do_reference(cpu_particles); - undo_redo->add_undo_reference(particles); - undo_redo->commit_action(); + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Convert from Particles2D")); + ur->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", particles, cpu_particles, true, false); + ur->add_do_reference(particles); + ur->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", cpu_particles, particles, false, false); + ur->add_undo_reference(this); + ur->commit_action(); } break; } @@ -139,7 +139,10 @@ void Particles2DEditorPlugin::_generate_visibility_rect() { particles->set_emitting(false); } - particles->set_visibility_rect(rect); + undo_redo->create_action(TTR("Generate Visibility Rect")); + undo_redo->add_do_method(particles, "set_visibility_rect", rect); + undo_redo->add_undo_method(particles, "set_visibility_rect", particles->get_visibility_rect()); + undo_redo->commit_action(); } void Particles2DEditorPlugin::_generate_emission_mask() { @@ -378,6 +381,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); @@ -411,7 +415,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_2d_editor_plugin.h b/editor/plugins/particles_2d_editor_plugin.h index eaa96d84e9..35b874fb25 100644 --- a/editor/plugins/particles_2d_editor_plugin.h +++ b/editor/plugins/particles_2d_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/particles_editor_plugin.cpp b/editor/plugins/particles_editor_plugin.cpp index f2dfae7a9f..09180edf2a 100644 --- a/editor/plugins/particles_editor_plugin.cpp +++ b/editor/plugins/particles_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -305,8 +305,6 @@ void ParticlesEditor::_menu_option(int p_option) { } break; case MENU_OPTION_CONVERT_TO_CPU_PARTICLES: { - UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); - CPUParticles *cpu_particles = memnew(CPUParticles); cpu_particles->convert_from_particles(node); cpu_particles->set_name(node->get_name()); @@ -314,12 +312,7 @@ void ParticlesEditor::_menu_option(int p_option) { cpu_particles->set_visible(node->is_visible()); cpu_particles->set_pause_mode(node->get_pause_mode()); - undo_redo->create_action("Replace Particles by CPUParticles"); - undo_redo->add_do_method(node, "replace_by", cpu_particles); - undo_redo->add_undo_method(cpu_particles, "replace_by", node); - undo_redo->add_do_reference(cpu_particles); - undo_redo->add_undo_reference(node); - undo_redo->commit_action(); + EditorNode::get_singleton()->get_scene_tree_dock()->replace_node(node, cpu_particles, false); } break; } @@ -360,7 +353,11 @@ void ParticlesEditor::_generate_aabb() { node->set_emitting(false); } - node->set_visibility_aabb(rect); + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Generate Visibility AABB")); + ur->add_do_method(node, "set_visibility_aabb", rect); + ur->add_undo_method(node, "set_visibility_aabb", node->get_visibility_aabb()); + ur->commit_action(); } void ParticlesEditor::edit(Particles *p_particles) { @@ -457,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/particles_editor_plugin.h b/editor/plugins/particles_editor_plugin.h index 830d30d98f..b1b3e3c1c0 100644 --- a/editor/plugins/particles_editor_plugin.h +++ b/editor/plugins/particles_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/path_2d_editor_plugin.cpp b/editor/plugins/path_2d_editor_plugin.cpp index c67c96798a..a10eddb131 100644 --- a/editor/plugins/path_2d_editor_plugin.cpp +++ b/editor/plugins/path_2d_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -70,8 +70,9 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { if (!node->get_curve().is_valid()) return false; - Ref<InputEventMouseButton> mb = p_event; + real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius"); + Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid()) { Transform2D xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); @@ -79,8 +80,6 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { Vector2 gpoint = mb->get_position(); Vector2 cpoint = node->get_global_transform().affine_inverse().xform(canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(mb->get_position()))); - real_t grab_threshold = EDITOR_DEF("editors/poly_editor/point_grab_radius", 8); - if (mb->is_pressed() && action == ACTION_NONE) { Ref<Curve2D> curve = node->get_curve(); @@ -179,6 +178,41 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { return true; } + // Check for segment split. + if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT && mode == MODE_EDIT && on_edge == true) { + Vector2 gpoint2 = mb->get_position(); + Ref<Curve2D> curve = node->get_curve(); + + int insertion_point = -1; + float mbLength = curve->get_closest_offset(xform.affine_inverse().xform(gpoint2)); + int len = curve->get_point_count(); + for (int i = 0; i < len - 1; i++) { + float compareLength = curve->get_closest_offset(curve->get_point_position(i + 1)); + if (mbLength >= curve->get_closest_offset(curve->get_point_position(i)) && mbLength <= compareLength) + insertion_point = i; + } + if (insertion_point == -1) + insertion_point = curve->get_point_count() - 2; + + undo_redo->create_action(TTR("Split Curve")); + undo_redo->add_do_method(curve.ptr(), "add_point", xform.affine_inverse().xform(gpoint2), Vector2(0, 0), Vector2(0, 0), insertion_point + 1); + undo_redo->add_undo_method(curve.ptr(), "remove_point", insertion_point + 1); + undo_redo->add_do_method(canvas_item_editor, "update_viewport"); + undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); + undo_redo->commit_action(); + + action = ACTION_MOVING_POINT; + action_point = insertion_point + 1; + moving_from = curve->get_point_position(action_point); + moving_screen_from = gpoint2; + + canvas_item_editor->update_viewport(); + + on_edge = false; + + return true; + } + // Check for point movement completion. if (!mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT && action != ACTION_NONE) { @@ -245,6 +279,49 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { if (mm.is_valid()) { + if (action == ACTION_NONE && mode == MODE_EDIT) { + // Handle Edge Follow + bool old_edge = on_edge; + + Transform2D xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); + Vector2 gpoint = mm->get_position(); + + Ref<Curve2D> curve = node->get_curve(); + if (curve == NULL) return true; + if (curve->get_point_count() < 2) return true; + + // Find edge + edge_point = xform.xform(curve->get_closest_point(xform.affine_inverse().xform(mm->get_position()))); + on_edge = false; + if (edge_point.distance_to(gpoint) <= grab_threshold) { + on_edge = true; + } + // However, if near a control point or its in-out handles then not on edge + int len = curve->get_point_count(); + for (int i = 0; i < len; i++) { + Vector2 pp = curve->get_point_position(i); + Vector2 p = xform.xform(pp); + if (p.distance_to(gpoint) <= grab_threshold) { + on_edge = false; + break; + } + p = xform.xform(pp + curve->get_point_in(i)); + if (p.distance_to(gpoint) <= grab_threshold) { + on_edge = false; + break; + } + p = xform.xform(pp + curve->get_point_out(i)); + if (p.distance_to(gpoint) <= grab_threshold) { + on_edge = false; + break; + } + } + if (on_edge || old_edge != on_edge) { + canvas_item_editor->update_viewport(); + return true; + } + } + if (action != ACTION_NONE) { // Handle point/control movement. Transform2D xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); @@ -309,7 +386,6 @@ void Path2DEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { Control *vpc = canvas_item_editor->get_viewport_control(); for (int i = 0; i < len; i++) { - Vector2 point = xform.xform(curve->get_point_position(i)); vpc->draw_texture_rect(handle, Rect2(point - handle_size * 0.5, handle_size), false, Color(1, 1, 1, 1)); @@ -325,6 +401,11 @@ void Path2DEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { vpc->draw_texture_rect(handle, Rect2(pointin - handle_size * 0.5, handle_size), false, Color(1, 0.5, 1, 0.3)); } } + + if (on_edge) { + Ref<Texture> add_handle = get_icon("EditorHandleAdd", "EditorIcons"); + p_overlay->draw_texture(add_handle, edge_point - add_handle->get_size() * 0.5); + } } void Path2DEditor::_node_visibility_changed() { @@ -442,6 +523,7 @@ Path2DEditor::Path2DEditor(EditorNode *p_editor) { undo_redo = editor->get_undo_redo(); mirror_handle_angle = true; mirror_handle_length = true; + on_edge = false; mode = MODE_EDIT; action = ACTION_NONE; @@ -455,7 +537,7 @@ Path2DEditor::Path2DEditor(EditorNode *p_editor) { curve_edit->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("CurveEdit", "EditorIcons")); curve_edit->set_toggle_mode(true); curve_edit->set_focus_mode(Control::FOCUS_NONE); - curve_edit->set_tooltip(TTR("Select Points") + "\n" + TTR("Shift+Drag: Select Control Points") + "\n" + keycode_get_string(KEY_MASK_CMD) + TTR("Click: Add Point") + "\n" + TTR("Right Click: Delete Point")); + curve_edit->set_tooltip(TTR("Select Points") + "\n" + TTR("Shift+Drag: Select Control Points") + "\n" + keycode_get_string(KEY_MASK_CMD) + TTR("Click: Add Point") + "\n" + TTR("Left Click: Split Segment (in curve)") + "\n" + TTR("Right Click: Delete Point")); curve_edit->connect("pressed", this, "_mode_selected", varray(MODE_EDIT)); base_hb->add_child(curve_edit); curve_edit_curve = memnew(ToolButton); @@ -469,7 +551,7 @@ Path2DEditor::Path2DEditor(EditorNode *p_editor) { curve_create->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("CurveCreate", "EditorIcons")); curve_create->set_toggle_mode(true); curve_create->set_focus_mode(Control::FOCUS_NONE); - curve_create->set_tooltip(TTR("Add Point (in empty space)") + "\n" + TTR("Split Segment (in curve)")); + curve_create->set_tooltip(TTR("Add Point (in empty space)")); curve_create->connect("pressed", this, "_mode_selected", varray(MODE_CREATE)); base_hb->add_child(curve_create); curve_del = memnew(ToolButton); diff --git a/editor/plugins/path_2d_editor_plugin.h b/editor/plugins/path_2d_editor_plugin.h index 3a78657746..4edd17d146 100644 --- a/editor/plugins/path_2d_editor_plugin.h +++ b/editor/plugins/path_2d_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -73,6 +73,7 @@ class Path2DEditor : public HBoxContainer { bool mirror_handle_angle; bool mirror_handle_length; + bool on_edge; enum HandleOption { HANDLE_OPTION_ANGLE, @@ -93,6 +94,7 @@ class Path2DEditor : public HBoxContainer { Point2 moving_screen_from; float orig_in_length; float orig_out_length; + Vector2 edge_point; void _mode_selected(int p_mode); void _handle_option_pressed(int p_option); diff --git a/editor/plugins/path_editor_plugin.cpp b/editor/plugins/path_editor_plugin.cpp index df6c40ed02..88dc258c5f 100644 --- a/editor/plugins/path_editor_plugin.cpp +++ b/editor/plugins/path_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -563,7 +563,7 @@ PathEditorPlugin::PathEditorPlugin(EditorNode *p_node) { Ref<PathSpatialGizmoPlugin> gizmo_plugin; gizmo_plugin.instance(); - SpatialEditor::get_singleton()->register_gizmo_plugin(gizmo_plugin); + SpatialEditor::get_singleton()->add_gizmo_plugin(gizmo_plugin); sep = memnew(VSeparator); sep->hide(); @@ -638,6 +638,10 @@ String PathSpatialGizmoPlugin::get_name() const { return "Path"; } +int PathSpatialGizmoPlugin::get_priority() const { + return -1; +} + PathSpatialGizmoPlugin::PathSpatialGizmoPlugin() { Color path_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/path", Color(0.5, 0.5, 1.0, 0.8)); diff --git a/editor/plugins/path_editor_plugin.h b/editor/plugins/path_editor_plugin.h index c77b2a41cc..5482d09377 100644 --- a/editor/plugins/path_editor_plugin.h +++ b/editor/plugins/path_editor_plugin.h @@ -1,12 +1,12 @@ -/*************************************************************************/ +/*************************************************************************/ /* path_editor_plugin.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -62,6 +62,7 @@ protected: public: String get_name() const; + int get_priority() const; PathSpatialGizmoPlugin(); }; diff --git a/editor/plugins/physical_bone_plugin.cpp b/editor/plugins/physical_bone_plugin.cpp index 6341d7f2ef..96681a105f 100644 --- a/editor/plugins/physical_bone_plugin.cpp +++ b/editor/plugins/physical_bone_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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/physical_bone_plugin.h b/editor/plugins/physical_bone_plugin.h index e1f8c9ec47..78c1c331a0 100644 --- a/editor/plugins/physical_bone_plugin.h +++ b/editor/plugins/physical_bone_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index 97448f3f88..712b1a0ae4 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -45,6 +45,7 @@ Node2D *Polygon2DEditor::_get_node() const { void Polygon2DEditor::_set_node(Node *p_polygon) { node = Object::cast_to<Polygon2D>(p_polygon); + _update_polygon_editing_state(); } Vector2 Polygon2DEditor::_get_offset(int p_idx) const { @@ -52,6 +53,15 @@ Vector2 Polygon2DEditor::_get_offset(int p_idx) const { return node->get_offset(); } +int Polygon2DEditor::_get_polygon_count() const { + + if (node->get_internal_vertex_count() > 0) { + return 0; //do not edit if internal vertices exist + } else { + return 1; + } +} + void Polygon2DEditor::_notification(int p_what) { switch (p_what) { @@ -66,13 +76,15 @@ void Polygon2DEditor::_notification(int p_what) { button_uv->set_icon(get_icon("Uv", "EditorIcons")); - uv_button[UV_MODE_CREATE]->set_icon(get_icon("Add", "EditorIcons")); + uv_button[UV_MODE_CREATE]->set_icon(get_icon("Edit", "EditorIcons")); + uv_button[UV_MODE_CREATE_INTERNAL]->set_icon(get_icon("EditInternal", "EditorIcons")); + uv_button[UV_MODE_REMOVE_INTERNAL]->set_icon(get_icon("RemoveInternal", "EditorIcons")); uv_button[UV_MODE_EDIT_POINT]->set_icon(get_icon("ToolSelect", "EditorIcons")); uv_button[UV_MODE_MOVE]->set_icon(get_icon("ToolMove", "EditorIcons")); uv_button[UV_MODE_ROTATE]->set_icon(get_icon("ToolRotate", "EditorIcons")); uv_button[UV_MODE_SCALE]->set_icon(get_icon("ToolScale", "EditorIcons")); - uv_button[UV_MODE_ADD_SPLIT]->set_icon(get_icon("AddSplit", "EditorIcons")); - uv_button[UV_MODE_REMOVE_SPLIT]->set_icon(get_icon("DeleteSplit", "EditorIcons")); + uv_button[UV_MODE_ADD_POLYGON]->set_icon(get_icon("Edit", "EditorIcons")); + uv_button[UV_MODE_REMOVE_POLYGON]->set_icon(get_icon("Close", "EditorIcons")); uv_button[UV_MODE_PAINT_WEIGHT]->set_icon(get_icon("PaintVertex", "EditorIcons")); uv_button[UV_MODE_CLEAR_WEIGHT]->set_icon(get_icon("UnpaintVertex", "EditorIcons")); @@ -91,7 +103,7 @@ void Polygon2DEditor::_notification(int p_what) { void Polygon2DEditor::_sync_bones() { - Skeleton2D *skeleton; + Skeleton2D *skeleton = NULL; if (!node->has_node(node->get_skeleton())) { error->set_text(TTR("The skeleton property of the Polygon2D does not point to a Skeleton2D node")); error->popup_centered_minsize(); @@ -191,11 +203,13 @@ void Polygon2DEditor::_uv_edit_mode_select(int p_mode) { if (p_mode == 0) { //uv uv_button[UV_MODE_CREATE]->hide(); + uv_button[UV_MODE_CREATE_INTERNAL]->hide(); + uv_button[UV_MODE_REMOVE_INTERNAL]->hide(); for (int i = UV_MODE_MOVE; i <= UV_MODE_SCALE; i++) { uv_button[i]->show(); } - uv_button[UV_MODE_ADD_SPLIT]->hide(); - uv_button[UV_MODE_REMOVE_SPLIT]->hide(); + uv_button[UV_MODE_ADD_POLYGON]->hide(); + uv_button[UV_MODE_REMOVE_POLYGON]->hide(); uv_button[UV_MODE_PAINT_WEIGHT]->hide(); uv_button[UV_MODE_CLEAR_WEIGHT]->hide(); _uv_mode(UV_MODE_EDIT_POINT); @@ -209,8 +223,8 @@ void Polygon2DEditor::_uv_edit_mode_select(int p_mode) { for (int i = 0; i <= UV_MODE_SCALE; i++) { uv_button[i]->show(); } - uv_button[UV_MODE_ADD_SPLIT]->hide(); - uv_button[UV_MODE_REMOVE_SPLIT]->hide(); + uv_button[UV_MODE_ADD_POLYGON]->hide(); + uv_button[UV_MODE_REMOVE_POLYGON]->hide(); uv_button[UV_MODE_PAINT_WEIGHT]->hide(); uv_button[UV_MODE_CLEAR_WEIGHT]->hide(); _uv_mode(UV_MODE_EDIT_POINT); @@ -224,11 +238,11 @@ void Polygon2DEditor::_uv_edit_mode_select(int p_mode) { for (int i = 0; i <= UV_MODE_SCALE; i++) { uv_button[i]->hide(); } - uv_button[UV_MODE_ADD_SPLIT]->show(); - uv_button[UV_MODE_REMOVE_SPLIT]->show(); + uv_button[UV_MODE_ADD_POLYGON]->show(); + uv_button[UV_MODE_REMOVE_POLYGON]->show(); uv_button[UV_MODE_PAINT_WEIGHT]->hide(); uv_button[UV_MODE_CLEAR_WEIGHT]->hide(); - _uv_mode(UV_MODE_ADD_SPLIT); + _uv_mode(UV_MODE_ADD_POLYGON); bone_scroll_main_vb->hide(); bone_paint_strength->hide(); @@ -236,7 +250,7 @@ void Polygon2DEditor::_uv_edit_mode_select(int p_mode) { bone_paint_radius_label->hide(); } else if (p_mode == 3) { //bones´ - for (int i = 0; i <= UV_MODE_REMOVE_SPLIT; i++) { + for (int i = 0; i <= UV_MODE_REMOVE_POLYGON; i++) { uv_button[i]->hide(); } uv_button[UV_MODE_PAINT_WEIGHT]->show(); @@ -290,6 +304,7 @@ void Polygon2DEditor::_menu_option(int p_option) { uv_edit->popup(EditorSettings::get_singleton()->get("interface/dialogs/uv_editor_bounds")); else uv_edit->popup_centered_ratio(0.85); + _update_bone_list(); } break; case UVEDIT_POLYGON_TO_UV: { @@ -348,8 +363,12 @@ void Polygon2DEditor::_cancel_editing() { uv_create = false; node->set_uv(uv_create_uv_prev); node->set_polygon(uv_create_poly_prev); + node->set_internal_vertex_count(uv_create_prev_internal_vertices); + node->set_vertex_colors(uv_create_colors_prev); node->call("_set_bones", uv_create_bones_prev); - node->set_splits(splits_prev); + node->set_polygons(polygons_prev); + + _update_polygon_editing_state(); } else if (uv_drag) { uv_drag = false; if (uv_edit_mode[0]->is_pressed()) { // Edit UV. @@ -357,14 +376,25 @@ void Polygon2DEditor::_cancel_editing() { } else if (uv_edit_mode[1]->is_pressed()) { // Edit polygon. node->set_polygon(points_prev); } - } else if (split_create) { - split_create = false; } + + polygon_create.clear(); +} + +void Polygon2DEditor::_update_polygon_editing_state() { + + if (!_get_node()) + return; + + if (node->get_internal_vertex_count() > 0) + disable_polygon_editing(true, TTR("Polygon 2D has internal vertices, so it can no longer be edited in the viewport.")); + else + disable_polygon_editing(false, String()); } void Polygon2DEditor::_commit_action() { - // Makes that undo/redoing actions made outside of the UV editor still affects its polygon. + // Makes that undo/redoing actions made outside of the UV editor still affect its polygon. undo_redo->add_do_method(uv_edit_draw, "update"); undo_redo->add_undo_method(uv_edit_draw, "update"); undo_redo->add_do_method(CanvasItemEditor::get_singleton(), "update_viewport"); @@ -409,7 +439,7 @@ void Polygon2DEditor::_set_snap_step_y(float p_val) { void Polygon2DEditor::_uv_mode(int p_mode) { - split_create = false; + polygon_create.clear(); uv_drag = false; uv_create = false; @@ -461,10 +491,14 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { uv_create = true; uv_create_uv_prev = node->get_uv(); uv_create_poly_prev = node->get_polygon(); + uv_create_prev_internal_vertices = node->get_internal_vertex_count(); + uv_create_colors_prev = node->get_vertex_colors(); uv_create_bones_prev = node->call("_get_bones"); - splits_prev = node->get_splits(); + polygons_prev = node->get_polygons(); + disable_polygon_editing(false, String()); node->set_polygon(points_prev); node->set_uv(points_prev); + node->set_internal_vertex_count(0); uv_edit_draw->update(); } else { @@ -477,8 +511,14 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { undo_redo->add_undo_method(node, "set_uv", uv_create_uv_prev); undo_redo->add_do_method(node, "set_polygon", node->get_polygon()); undo_redo->add_undo_method(node, "set_polygon", uv_create_poly_prev); + undo_redo->add_do_method(node, "set_internal_vertex_count", 0); + undo_redo->add_undo_method(node, "set_internal_vertex_count", uv_create_prev_internal_vertices); + undo_redo->add_do_method(node, "set_vertex_colors", Vector<Color>()); + undo_redo->add_undo_method(node, "set_vertex_colors", uv_create_colors_prev); undo_redo->add_do_method(node, "clear_bones"); undo_redo->add_undo_method(node, "_set_bones", uv_create_bones_prev); + undo_redo->add_do_method(this, "_update_polygon_editing_state"); + undo_redo->add_undo_method(this, "_update_polygon_editing_state"); undo_redo->add_do_method(uv_edit_draw, "update"); undo_redo->add_undo_method(uv_edit_draw, "update"); undo_redo->commit_action(); @@ -499,6 +539,99 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { CanvasItemEditor::get_singleton()->update_viewport(); } + if (uv_move_current == UV_MODE_CREATE_INTERNAL) { + + uv_create_uv_prev = node->get_uv(); + uv_create_poly_prev = node->get_polygon(); + uv_create_colors_prev = node->get_vertex_colors(); + uv_create_bones_prev = node->call("_get_bones"); + int internal_vertices = node->get_internal_vertex_count(); + + Vector2 pos = mtx.affine_inverse().xform(snap_point(Vector2(mb->get_position().x, mb->get_position().y))); + + uv_create_poly_prev.push_back(pos); + uv_create_uv_prev.push_back(pos); + if (uv_create_colors_prev.size()) { + uv_create_colors_prev.push_back(Color(1, 1, 1)); + } + + undo_redo->create_action(TTR("Create Internal Vertex")); + undo_redo->add_do_method(node, "set_uv", uv_create_uv_prev); + undo_redo->add_undo_method(node, "set_uv", node->get_uv()); + undo_redo->add_do_method(node, "set_polygon", uv_create_poly_prev); + undo_redo->add_undo_method(node, "set_polygon", node->get_polygon()); + undo_redo->add_do_method(node, "set_vertex_colors", uv_create_colors_prev); + undo_redo->add_undo_method(node, "set_vertex_colors", node->get_vertex_colors()); + for (int i = 0; i < node->get_bone_count(); i++) { + PoolVector<float> bonew = node->get_bone_weights(i); + bonew.push_back(0); + undo_redo->add_do_method(node, "set_bone_weights", i, bonew); + undo_redo->add_undo_method(node, "set_bone_weights", i, node->get_bone_weights(i)); + } + undo_redo->add_do_method(node, "set_internal_vertex_count", internal_vertices + 1); + undo_redo->add_undo_method(node, "set_internal_vertex_count", internal_vertices); + undo_redo->add_do_method(this, "_update_polygon_editing_state"); + undo_redo->add_undo_method(this, "_update_polygon_editing_state"); + undo_redo->add_do_method(uv_edit_draw, "update"); + undo_redo->add_undo_method(uv_edit_draw, "update"); + undo_redo->commit_action(); + } + + if (uv_move_current == UV_MODE_REMOVE_INTERNAL) { + + uv_create_uv_prev = node->get_uv(); + uv_create_poly_prev = node->get_polygon(); + uv_create_colors_prev = node->get_vertex_colors(); + uv_create_bones_prev = node->call("_get_bones"); + int internal_vertices = node->get_internal_vertex_count(); + + if (internal_vertices <= 0) + return; + + int closest = -1; + float closest_dist = 1e20; + + for (int i = points_prev.size() - internal_vertices; i < points_prev.size(); i++) { + + Vector2 tuv = mtx.xform(uv_create_poly_prev[i]); + float dist = tuv.distance_to(Vector2(mb->get_position().x, mb->get_position().y)); + if (dist < 8 && dist < closest_dist) { + closest = i; + closest_dist = dist; + } + } + + if (closest == -1) + return; + + uv_create_poly_prev.remove(closest); + uv_create_uv_prev.remove(closest); + if (uv_create_colors_prev.size()) { + uv_create_colors_prev.remove(closest); + } + + undo_redo->create_action(TTR("Remove Internal Vertex")); + undo_redo->add_do_method(node, "set_uv", uv_create_uv_prev); + undo_redo->add_undo_method(node, "set_uv", node->get_uv()); + undo_redo->add_do_method(node, "set_polygon", uv_create_poly_prev); + undo_redo->add_undo_method(node, "set_polygon", node->get_polygon()); + undo_redo->add_do_method(node, "set_vertex_colors", uv_create_colors_prev); + undo_redo->add_undo_method(node, "set_vertex_colors", node->get_vertex_colors()); + for (int i = 0; i < node->get_bone_count(); i++) { + PoolVector<float> bonew = node->get_bone_weights(i); + bonew.remove(closest); + undo_redo->add_do_method(node, "set_bone_weights", i, bonew); + undo_redo->add_undo_method(node, "set_bone_weights", i, node->get_bone_weights(i)); + } + undo_redo->add_do_method(node, "set_internal_vertex_count", internal_vertices - 1); + undo_redo->add_undo_method(node, "set_internal_vertex_count", internal_vertices); + undo_redo->add_do_method(this, "_update_polygon_editing_state"); + undo_redo->add_undo_method(this, "_update_polygon_editing_state"); + undo_redo->add_do_method(uv_edit_draw, "update"); + undo_redo->add_undo_method(uv_edit_draw, "update"); + undo_redo->commit_action(); + } + if (uv_move_current == UV_MODE_EDIT_POINT) { if (mb->get_shift() && mb->get_command()) @@ -526,129 +659,80 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { } } - if (uv_move_current == UV_MODE_ADD_SPLIT) { + if (uv_move_current == UV_MODE_ADD_POLYGON) { + + int closest = -1; + float closest_dist = 1e20; - int split_to_index = -1; - split_to_index = -1; for (int i = 0; i < points_prev.size(); i++) { Vector2 tuv = mtx.xform(points_prev[i]); - if (tuv.distance_to(Vector2(mb->get_position().x, mb->get_position().y)) < 8) { - split_to_index = i; + float dist = tuv.distance_to(Vector2(mb->get_position().x, mb->get_position().y)); + if (dist < 8 && dist < closest_dist) { + closest = i; + closest_dist = dist; } } - if (split_to_index == -1) { - split_create = false; - return; - } - - if (split_create) { - - split_create = false; - if (split_to_index < point_drag_index) { - SWAP(split_to_index, point_drag_index); - } - bool valid = true; - String split_error; - if (split_to_index == point_drag_index) { - split_error = TTR("Split point with itself."); - valid = false; - } - if (split_to_index + 1 == point_drag_index) { - //not a split,goes along the edge - split_error = TTR("Split can't form an existing edge."); - valid = false; - } - if (split_to_index == points_prev.size() - 1 && point_drag_index == 0) { - //not a split,goes along the edge - split_error = TTR("Split can't form an existing edge."); - valid = false; - } - - for (int i = 0; i < splits_prev.size(); i += 2) { - - if (splits_prev[i] == point_drag_index && splits_prev[i + 1] == split_to_index) { - //already exists - split_error = TTR("Split already exists."); - valid = false; - break; - } - - int a_state; //-1, outside split, 0 split point, +1, inside split - if (point_drag_index == splits_prev[i] || point_drag_index == splits_prev[i + 1]) { - a_state = 0; - } else if (point_drag_index < splits_prev[i] || point_drag_index > splits_prev[i + 1]) { - a_state = -1; + if (closest != -1) { + if (polygon_create.size() && closest == polygon_create[0]) { + //close + if (polygon_create.size() < 3) { + error->set_text(TTR("Invalid Polygon (need 3 different vertices)")); + error->popup_centered_minsize(); } else { - a_state = 1; + Array polygons = node->get_polygons(); + polygons = polygons.duplicate(); //copy because its a reference + + //todo, could check whether it already exists? + polygons.push_back(polygon_create); + undo_redo->create_action(TTR("Add Custom Polygon")); + undo_redo->add_do_method(node, "set_polygons", polygons); + undo_redo->add_undo_method(node, "set_polygons", node->get_polygons()); + undo_redo->add_do_method(uv_edit_draw, "update"); + undo_redo->add_undo_method(uv_edit_draw, "update"); + undo_redo->commit_action(); } - int b_state; //-1, outside split, 0 split point, +1, inside split - if (split_to_index == splits_prev[i] || split_to_index == splits_prev[i + 1]) { - b_state = 0; - } else if (split_to_index < splits_prev[i] || split_to_index > splits_prev[i + 1]) { - b_state = -1; - } else { - b_state = 1; - } - - if (b_state * a_state < 0) { - //crossing - split_error = "Split crosses another split."; - valid = false; - break; - } + polygon_create.clear(); + } else if (polygon_create.find(closest) == -1) { + //add temporarily if not exists + polygon_create.push_back(closest); } - - if (valid) { - - splits_prev.push_back(point_drag_index); - splits_prev.push_back(split_to_index); - - undo_redo->create_action(TTR("Add Split")); - undo_redo->add_do_method(node, "set_splits", splits_prev); - undo_redo->add_undo_method(node, "set_splits", node->get_splits()); - undo_redo->add_do_method(uv_edit_draw, "update"); - undo_redo->add_undo_method(uv_edit_draw, "update"); - undo_redo->commit_action(); - } else { - error->set_text(TTR("Invalid Split: ") + split_error); - error->popup_centered_minsize(); - } - - } else { - point_drag_index = split_to_index; - split_create = true; - splits_prev = node->get_splits(); - uv_create_to = mtx.affine_inverse().xform(Vector2(mb->get_position().x, mb->get_position().y)); } } - if (uv_move_current == UV_MODE_REMOVE_SPLIT) { + if (uv_move_current == UV_MODE_REMOVE_POLYGON) { + Array polygons = node->get_polygons(); + polygons = polygons.duplicate(); //copy because its a reference + + int erase_index = -1; + for (int i = polygons.size() - 1; i >= 0; i--) { + PoolVector<int> points = polygons[i]; + Vector<Vector2> polys; + polys.resize(points.size()); + for (int j = 0; j < polys.size(); j++) { + int idx = points[j]; + if (idx < 0 || idx >= points_prev.size()) + continue; + polys.write[j] = mtx.xform(points_prev[idx]); + } - splits_prev = node->get_splits(); - for (int i = 0; i < splits_prev.size(); i += 2) { - if (splits_prev[i] < 0 || splits_prev[i] >= points_prev.size()) - continue; - if (splits_prev[i + 1] < 0 || splits_prev[i] >= points_prev.size()) - continue; - Vector2 e[2] = { mtx.xform(points_prev[splits_prev[i]]), mtx.xform(points_prev[splits_prev[i + 1]]) }; - Vector2 mp = Vector2(mb->get_position().x, mb->get_position().y); - Vector2 cp = Geometry::get_closest_point_to_segment_2d(mp, e); - if (cp.distance_to(mp) < 8) { - splits_prev.remove(i); - splits_prev.remove(i); - - undo_redo->create_action(TTR("Remove Split")); - undo_redo->add_do_method(node, "set_splits", splits_prev); - undo_redo->add_undo_method(node, "set_splits", node->get_splits()); - undo_redo->add_do_method(uv_edit_draw, "update"); - undo_redo->add_undo_method(uv_edit_draw, "update"); - undo_redo->commit_action(); + if (Geometry::is_point_in_polygon(Vector2(mb->get_position().x, mb->get_position().y), polys)) { + erase_index = i; break; } } + + if (erase_index != -1) { + polygons.remove(erase_index); + undo_redo->create_action(TTR("Remove Custom Polygon")); + undo_redo->add_do_method(node, "set_polygons", polygons); + undo_redo->add_undo_method(node, "set_polygons", node->get_polygons()); + undo_redo->add_do_method(uv_edit_draw, "update"); + undo_redo->add_undo_method(uv_edit_draw, "update"); + undo_redo->commit_action(); + } } if (uv_move_current == UV_MODE_PAINT_WEIGHT || uv_move_current == UV_MODE_CLEAR_WEIGHT) { @@ -672,18 +756,21 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { } else if (uv_drag && !uv_create) { - if (uv_edit_mode[0]->is_pressed()) { //edit uv + if (uv_edit_mode[0]->is_pressed()) { // Edit UV. undo_redo->create_action(TTR("Transform UV Map")); undo_redo->add_do_method(node, "set_uv", node->get_uv()); undo_redo->add_undo_method(node, "set_uv", points_prev); - } else if (uv_edit_mode[1]->is_pressed()) { //edit polygon + undo_redo->add_do_method(uv_edit_draw, "update"); + undo_redo->add_undo_method(uv_edit_draw, "update"); + undo_redo->commit_action(); + } else if (uv_edit_mode[1]->is_pressed() && uv_move_current == UV_MODE_EDIT_POINT) { // Edit polygon. undo_redo->create_action(TTR("Transform Polygon")); undo_redo->add_do_method(node, "set_polygon", node->get_polygon()); undo_redo->add_undo_method(node, "set_polygon", points_prev); + undo_redo->add_do_method(uv_edit_draw, "update"); + undo_redo->add_undo_method(uv_edit_draw, "update"); + undo_redo->commit_action(); } - undo_redo->add_do_method(uv_edit_draw, "update"); - undo_redo->add_undo_method(uv_edit_draw, "update"); - undo_redo->commit_action(); uv_drag = false; } else if (bone_painting) { @@ -818,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) { @@ -849,7 +937,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { uv_edit_draw->update(); CanvasItemEditor::get_singleton()->update_viewport(); - } else if (split_create) { + } else if (polygon_create.size()) { uv_create_to = mtx.affine_inverse().xform(Vector2(mm->get_position().x, mm->get_position().y)); uv_edit_draw->update(); } else if (uv_mode == UV_MODE_PAINT_WEIGHT || uv_mode == UV_MODE_CLEAR_WEIGHT) { @@ -930,6 +1018,8 @@ void Polygon2DEditor::_uv_draw() { } } + Array polygons = node->get_polygons(); + PoolVector<Vector2> uvs; if (uv_edit_mode[0]->is_pressed()) { //edit uv uvs = node->get_uv(); @@ -956,17 +1046,34 @@ void Polygon2DEditor::_uv_draw() { } Ref<Texture> handle = get_icon("EditorHandle", "EditorIcons"); + Ref<Texture> internal_handle = get_icon("EditorInternalHandle", "EditorIcons"); Color poly_line_color = Color(0.9, 0.5, 0.5); + if (polygons.size() || polygon_create.size()) { + poly_line_color.a *= 0.25; + } + Color polygon_line_color = Color(0.5, 0.5, 0.9); + Vector<Color> polygon_fill_color; + { + Color pf = polygon_line_color; + pf.a *= 0.5; + 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(); + + uv_draw_max -= node->get_internal_vertex_count(); + if (uv_draw_max < 0) { + uv_draw_max = 0; + } for (int i = 0; i < uvs.size(); i++) { - int next = (i + 1) % uvs.size(); + int next = uv_draw_max > 0 ? (i + 1) % uv_draw_max : 0; - if (uv_drag && uv_move_current == UV_MODE_EDIT_POINT && EDITOR_DEF("editors/poly_editor/show_previous_outline", true)) { + if (i < uv_draw_max && uv_drag && uv_move_current == UV_MODE_EDIT_POINT && EDITOR_DEF("editors/poly_editor/show_previous_outline", true)) { uv_edit_draw->draw_line(mtx.xform(points_prev[i]), mtx.xform(points_prev[next]), prev_color, 2 * EDSCALE); } @@ -974,24 +1081,59 @@ void Polygon2DEditor::_uv_draw() { if (uv_create && i == uvs.size() - 1) { next_point = uv_create_to; } - uv_edit_draw->draw_line(mtx.xform(uvs[i]), mtx.xform(next_point), poly_line_color, 2 * EDSCALE); + if (i < uv_draw_max /*&& polygons.size() == 0 && polygon_create.size() == 0*/) { //if using or creating polygons, do not show outline (will show polygons instead) + uv_edit_draw->draw_line(mtx.xform(uvs[i]), mtx.xform(next_point), poly_line_color, 2 * EDSCALE); + } + + rect.expand_to(mtx.basis_xform(uvs[i])); + } + + for (int i = 0; i < polygons.size(); i++) { + + PoolVector<int> points = polygons[i]; + Vector<Vector2> polypoints; + for (int j = 0; j < points.size(); j++) { + int next = (j + 1) % points.size(); + + int idx = points[j]; + int idx_next = points[next]; + if (idx < 0 || idx >= uvs.size()) + continue; + polypoints.push_back(mtx.xform(uvs[idx])); + + if (idx_next < 0 || idx_next >= uvs.size()) + continue; + uv_edit_draw->draw_line(mtx.xform(uvs[idx]), mtx.xform(uvs[idx_next]), polygon_line_color, 2 * EDSCALE); + } + if (points.size() >= 3) { + uv_edit_draw->draw_polygon(polypoints, polygon_fill_color); + } + } + + for (int i = 0; i < uvs.size(); i++) { if (weight_r.ptr()) { Vector2 draw_pos = mtx.xform(uvs[i]); float weight = weight_r[i]; uv_edit_draw->draw_rect(Rect2(draw_pos - Vector2(2, 2) * EDSCALE, Vector2(5, 5) * EDSCALE), Color(weight, weight, weight, 1.0)); } else { - uv_edit_draw->draw_texture(handle, mtx.xform(uvs[i]) - handle->get_size() * 0.5); + if (i < uv_draw_max) { + uv_edit_draw->draw_texture(handle, mtx.xform(uvs[i]) - handle->get_size() * 0.5); + } else { + uv_edit_draw->draw_texture(internal_handle, mtx.xform(uvs[i]) - internal_handle->get_size() * 0.5); + } } - rect.expand_to(mtx.basis_xform(uvs[i])); } - if (split_create) { - Vector2 from = uvs[point_drag_index]; - Vector2 to = uv_create_to; - uv_edit_draw->draw_line(mtx.xform(from), mtx.xform(to), poly_line_color, 2); + if (polygon_create.size()) { + for (int i = 0; i < polygon_create.size(); i++) { + Vector2 from = uvs[polygon_create[i]]; + Vector2 to = (i + 1) < polygon_create.size() ? uvs[polygon_create[i + 1]] : uv_create_to; + uv_edit_draw->draw_line(mtx.xform(from), mtx.xform(to), polygon_line_color, 2); + } } +#if 0 PoolVector<int> splits = node->get_splits(); for (int i = 0; i < splits.size(); i += 2) { @@ -1001,7 +1143,7 @@ void Polygon2DEditor::_uv_draw() { continue; uv_edit_draw->draw_line(mtx.xform(uvs[idx_from]), mtx.xform(uvs[idx_to]), poly_line_color, 2); } - +#endif if (uv_mode == UV_MODE_PAINT_WEIGHT || uv_mode == UV_MODE_CLEAR_WEIGHT) { NodePath bone_path; @@ -1061,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); @@ -1101,6 +1244,7 @@ void Polygon2DEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_uv_edit_popup_hide"), &Polygon2DEditor::_uv_edit_popup_hide); ClassDB::bind_method(D_METHOD("_sync_bones"), &Polygon2DEditor::_sync_bones); ClassDB::bind_method(D_METHOD("_update_bone_list"), &Polygon2DEditor::_update_bone_list); + ClassDB::bind_method(D_METHOD("_update_polygon_editing_state"), &Polygon2DEditor::_update_polygon_editing_state); ClassDB::bind_method(D_METHOD("_bone_paint_selected"), &Polygon2DEditor::_bone_paint_selected); } @@ -1155,8 +1299,8 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) : uv_edit_mode[0]->set_text(TTR("UV")); uv_edit_mode[0]->set_pressed(true); - uv_edit_mode[1]->set_text(TTR("Poly")); - uv_edit_mode[2]->set_text(TTR("Splits")); + uv_edit_mode[1]->set_text(TTR("Points")); + uv_edit_mode[2]->set_text(TTR("Polygons")); uv_edit_mode[3]->set_text(TTR("Bones")); uv_edit_mode[0]->set_button_group(uv_edit_group); @@ -1181,22 +1325,26 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) : uv_button[i]->set_focus_mode(FOCUS_NONE); } - uv_button[0]->set_tooltip(TTR("Create Polygon")); - uv_button[1]->set_tooltip(TTR("Move Points") + "\n" + TTR("Ctrl: Rotate") + "\n" + TTR("Shift: Move All") + "\n" + TTR("Shift+Ctrl: Scale")); - uv_button[2]->set_tooltip(TTR("Move Polygon")); - uv_button[3]->set_tooltip(TTR("Rotate Polygon")); - uv_button[4]->set_tooltip(TTR("Scale Polygon")); - uv_button[5]->set_tooltip(TTR("Connect two points to make a split.")); - uv_button[6]->set_tooltip(TTR("Select a split to erase it.")); - uv_button[7]->set_tooltip(TTR("Paint weights with specified intensity.")); - uv_button[8]->set_tooltip(TTR("Unpaint weights with specified intensity.")); - - uv_button[0]->hide(); - uv_button[5]->hide(); - uv_button[6]->hide(); - uv_button[7]->hide(); - uv_button[8]->hide(); - uv_button[1]->set_pressed(true); + uv_button[UV_MODE_CREATE]->set_tooltip(TTR("Create Polygon")); + uv_button[UV_MODE_CREATE_INTERNAL]->set_tooltip(TTR("Create Internal Vertex")); + uv_button[UV_MODE_REMOVE_INTERNAL]->set_tooltip(TTR("Remove Internal Vertex")); + uv_button[UV_MODE_EDIT_POINT]->set_tooltip(TTR("Move Points") + "\n" + TTR("Ctrl: Rotate") + "\n" + TTR("Shift: Move All") + "\n" + TTR("Shift+Ctrl: Scale")); + uv_button[UV_MODE_MOVE]->set_tooltip(TTR("Move Polygon")); + uv_button[UV_MODE_ROTATE]->set_tooltip(TTR("Rotate Polygon")); + uv_button[UV_MODE_SCALE]->set_tooltip(TTR("Scale Polygon")); + uv_button[UV_MODE_ADD_POLYGON]->set_tooltip(TTR("Create a custom polygon. Enables custom polygon rendering.")); + uv_button[UV_MODE_REMOVE_POLYGON]->set_tooltip(TTR("Remove a custom polygon. If none remain, custom polygon rendering is disabled.")); + uv_button[UV_MODE_PAINT_WEIGHT]->set_tooltip(TTR("Paint weights with specified intensity.")); + uv_button[UV_MODE_CLEAR_WEIGHT]->set_tooltip(TTR("Unpaint weights with specified intensity.")); + + uv_button[UV_MODE_CREATE]->hide(); + uv_button[UV_MODE_CREATE_INTERNAL]->hide(); + uv_button[UV_MODE_REMOVE_INTERNAL]->hide(); + uv_button[UV_MODE_ADD_POLYGON]->hide(); + uv_button[UV_MODE_REMOVE_POLYGON]->hide(); + uv_button[UV_MODE_PAINT_WEIGHT]->hide(); + uv_button[UV_MODE_CLEAR_WEIGHT]->hide(); + uv_button[UV_MODE_EDIT_POINT]->set_pressed(true); bone_paint_strength = memnew(HSlider); uv_mode_hb->add_child(bone_paint_strength); @@ -1354,7 +1502,6 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) : uv_drag = false; uv_create = false; updating_uv_scroll = false; - split_create = false; bone_painting = false; error = memnew(AcceptDialog); diff --git a/editor/plugins/polygon_2d_editor_plugin.h b/editor/plugins/polygon_2d_editor_plugin.h index 935f1cfff0..24ca2ea3f4 100644 --- a/editor/plugins/polygon_2d_editor_plugin.h +++ b/editor/plugins/polygon_2d_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -50,12 +50,14 @@ class Polygon2DEditor : public AbstractPolygon2DEditor { enum UVMode { UV_MODE_CREATE, + UV_MODE_CREATE_INTERNAL, + UV_MODE_REMOVE_INTERNAL, UV_MODE_EDIT_POINT, UV_MODE_MOVE, UV_MODE_ROTATE, UV_MODE_SCALE, - UV_MODE_ADD_SPLIT, - UV_MODE_REMOVE_SPLIT, + UV_MODE_ADD_POLYGON, + UV_MODE_REMOVE_POLYGON, UV_MODE_PAINT_WEIGHT, UV_MODE_CLEAR_WEIGHT, UV_MODE_MAX @@ -100,14 +102,16 @@ class Polygon2DEditor : public AbstractPolygon2DEditor { PoolVector<Vector2> points_prev; PoolVector<Vector2> uv_create_uv_prev; PoolVector<Vector2> uv_create_poly_prev; + PoolVector<Color> uv_create_colors_prev; + int uv_create_prev_internal_vertices; Array uv_create_bones_prev; - PoolVector<int> splits_prev; + Array polygons_prev; Vector2 uv_create_to; int point_drag_index; bool uv_drag; bool uv_create; - bool split_create; + Vector<int> polygon_create; UVMode uv_move_current; Vector2 uv_drag_from; bool updating_uv_scroll; @@ -124,6 +128,7 @@ class Polygon2DEditor : public AbstractPolygon2DEditor { virtual void _menu_option(int p_option); void _cancel_editing(); + void _update_polygon_editing_state(); void _uv_scroll_changed(float); void _uv_input(const Ref<InputEvent> &p_input); @@ -141,6 +146,8 @@ class Polygon2DEditor : public AbstractPolygon2DEditor { void _uv_edit_popup_hide(); void _bone_paint_selected(int p_index); + int _get_polygon_count() const; + protected: virtual Node2D *_get_node() const; virtual void _set_node(Node *p_polygon); diff --git a/editor/plugins/resource_preloader_editor_plugin.cpp b/editor/plugins/resource_preloader_editor_plugin.cpp index bd4a35c9d8..53300f45ec 100644 --- a/editor/plugins/resource_preloader_editor_plugin.cpp +++ b/editor/plugins/resource_preloader_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/resource_preloader_editor_plugin.h b/editor/plugins/resource_preloader_editor_plugin.h index 0a8238ce18..346249b1c5 100644 --- a/editor/plugins/resource_preloader_editor_plugin.h +++ b/editor/plugins/resource_preloader_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/root_motion_editor_plugin.cpp b/editor/plugins/root_motion_editor_plugin.cpp index b3adf19a64..326e8394a0 100644 --- a/editor/plugins/root_motion_editor_plugin.cpp +++ b/editor/plugins/root_motion_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -39,7 +39,7 @@ void EditorPropertyRootMotion::_confirmed() { return; NodePath path = ti->get_metadata(0); - emit_signal("property_changed", get_edited_property(), path); + emit_changed(get_edited_property(), path); update_property(); filter_dialog->hide(); //may come from activated } @@ -195,7 +195,7 @@ void EditorPropertyRootMotion::_node_assign() { void EditorPropertyRootMotion::_node_clear() { - emit_signal("property_changed", get_edited_property(), NodePath()); + emit_changed(get_edited_property(), NodePath()); update_property(); } @@ -206,7 +206,7 @@ void EditorPropertyRootMotion::update_property() { assign->set_tooltip(p); if (p == NodePath()) { assign->set_icon(Ref<Texture>()); - assign->set_text(TTR("Assign..")); + assign->set_text(TTR("Assign...")); assign->set_flat(false); return; } diff --git a/editor/plugins/root_motion_editor_plugin.h b/editor/plugins/root_motion_editor_plugin.h index 2b5492350b..e7fd597235 100644 --- a/editor/plugins/root_motion_editor_plugin.h +++ b/editor/plugins/root_motion_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 0bbe08821a..839c9483d7 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -40,6 +40,7 @@ #include "editor/editor_settings.h" #include "editor/find_in_files.h" #include "editor/node_dock.h" +#include "editor/plugins/shader_editor_plugin.h" #include "editor/script_editor_debugger.h" #include "scene/main/viewport.h" #include "script_text_editor.h" @@ -300,7 +301,7 @@ void ScriptEditor::_goto_script_line2(int p_line) { void ScriptEditor::_goto_script_line(REF p_script, int p_line) { Ref<Script> script = Object::cast_to<Script>(*p_script); - if (!script.is_null() && script->has_source_code()) { + if (script.is_valid() && (script->has_source_code() || script->get_path().is_resource_file())) { if (edit(p_script, p_line, 0)) { editor->push_item(p_script.ptr()); @@ -311,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(); @@ -570,6 +603,8 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) { idx = history[history_pos].control->get_index(); } tab_container->set_current_tab(idx); + } else { + _update_selected_editor_menu(); } _update_history_arrows(); @@ -848,8 +883,7 @@ void ScriptEditor::_file_dialog_action(String p_file) { } file->close(); memdelete(file); - - // fallthrough to open the file. + FALLTHROUGH; } case FILE_OPEN: { @@ -896,12 +930,12 @@ void ScriptEditor::_file_dialog_action(String p_file) { } break; case THEME_SAVE_AS: { if (!EditorSettings::get_singleton()->save_text_editor_theme_as(p_file)) { - editor->show_warning(TTR("Error while saving theme"), TTR("Error saving")); + editor->show_warning(TTR("Error while saving theme."), TTR("Error Saving")); } } break; case THEME_IMPORT: { if (!EditorSettings::get_singleton()->import_text_editor_theme(p_file)) { - editor->show_warning(TTR("Error importing theme"), TTR("Error importing")); + editor->show_warning(TTR("Error importing theme."), TTR("Error Importing")); } } break; } @@ -970,6 +1004,9 @@ void ScriptEditor::_menu_option(int p_option) { save_all_scripts(); } break; + case SEARCH_IN_FILES: { + _on_find_in_files_requested(""); + } break; case SEARCH_HELP: { help_search_dialog->popup_dialog(); @@ -978,6 +1015,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: { @@ -1089,7 +1130,7 @@ void ScriptEditor::_menu_option(int p_option) { Ref<Script> scr = current->get_edited_resource(); if (scr == NULL || scr.is_null()) { - EditorNode::get_singleton()->show_warning("Can't obtain the script for running"); + EditorNode::get_singleton()->show_warning("Can't obtain the script for running."); break; } @@ -1102,13 +1143,13 @@ void ScriptEditor::_menu_option(int p_option) { } if (!scr->is_tool()) { - EditorNode::get_singleton()->show_warning("Script is not in tool mode, will not be able to run"); + EditorNode::get_singleton()->show_warning("Script is not in tool mode, will not be able to run."); return; } if (!ClassDB::is_parent_class(scr->get_instance_base_type(), "EditorScript")) { - EditorNode::get_singleton()->show_warning("To run this script, it must inherit EditorScript and be set to tool mode"); + EditorNode::get_singleton()->show_warning("To run this script, it must inherit EditorScript and be set to tool mode."); return; } @@ -1307,6 +1348,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")); @@ -1516,7 +1558,15 @@ struct _ScriptEditorItemData { bool operator<(const _ScriptEditorItemData &id) const { - return category == id.category ? sort_key < id.sort_key : category < id.category; + if (category == id.category) { + if (sort_key == id.sort_key) { + return index < id.index; + } else { + return sort_key < id.sort_key; + } + } else { + return category < id.category; + } } }; @@ -1730,7 +1780,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; @@ -1796,6 +1846,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; @@ -1950,8 +2010,9 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra if (is_visible_in_tree()) se->ensure_focus(); - if (p_line >= 0) + if (p_line > 0) { se->goto_line(p_line - 1); + } } return true; } @@ -2011,8 +2072,9 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra _test_script_times_on_disk(p_resource); _update_modified_scripts_for_external_editor(p_resource); - if (p_line >= 0) + if (p_line > 0) { se->goto_line(p_line - 1); + } notify_script_changed(p_resource); _add_recent_script(p_resource->get_path()); @@ -2196,6 +2258,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) { @@ -2205,6 +2268,9 @@ void ScriptEditor::_script_split_dragged(float) { Variant ScriptEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) { + if (tab_container->get_child_count() == 0) + return Variant(); + Node *cur_node = tab_container->get_child(tab_container->get_current_tab()); HBoxContainer *drag_preview = memnew(HBoxContainer); @@ -2472,22 +2538,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"]); + } } } @@ -2527,7 +2610,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)); @@ -2620,10 +2707,22 @@ void ScriptEditor::_update_selected_editor_menu() { } EditorHelp *eh = Object::cast_to<EditorHelp>(tab_container->get_current_tab_control()); + script_search_menu->get_popup()->clear(); if (eh) { + + script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find", TTR("Find..."), KEY_MASK_CMD | KEY_F), HELP_SEARCH_FIND); + script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_next", TTR("Find Next"), KEY_F3), HELP_SEARCH_FIND_NEXT); + script_search_menu->get_popup()->add_separator(); + script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_in_files", TTR("Find in Files"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F), SEARCH_IN_FILES); script_search_menu->show(); } else { - script_search_menu->hide(); + + if (tab_container->get_child_count() == 0) { + script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_in_files", TTR("Find in Files"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F), SEARCH_IN_FILES); + script_search_menu->show(); + } else { + script_search_menu->hide(); + } } } @@ -2778,13 +2877,18 @@ void ScriptEditor::_on_find_in_files_requested(String text) { void ScriptEditor::_on_find_in_files_result_selected(String fpath, int line_number, int begin, int end) { RES res = ResourceLoader::load(fpath); - edit(res); - - ScriptEditorBase *seb = _get_current_editor(); + if (fpath.get_extension() == "shader") { + ShaderEditorPlugin *shader_editor = Object::cast_to<ShaderEditorPlugin>(EditorNode::get_singleton()->get_editor_data().get_editor("Shader")); + shader_editor->edit(res.ptr()); + shader_editor->make_visible(true); + shader_editor->get_shader_editor()->goto_line_selection(line_number - 1, begin, end); + } else { + edit(res); - ScriptTextEditor *ste = Object::cast_to<ScriptTextEditor>(seb); - if (ste) { - ste->goto_line_selection(line_number - 1, begin, end); + ScriptTextEditor *ste = Object::cast_to<ScriptTextEditor>(_get_current_editor()); + if (ste) { + ste->goto_line_selection(line_number - 1, begin, end); + } } } @@ -2794,7 +2898,7 @@ void ScriptEditor::_start_find_in_files(bool with_replace) { f->set_search_text(find_in_files_dialog->get_search_text()); f->set_match_case(find_in_files_dialog->is_match_case()); - f->set_whole_words(find_in_files_dialog->is_match_case()); + f->set_whole_words(find_in_files_dialog->is_whole_words()); f->set_folder(find_in_files_dialog->get_folder()); f->set_filter(find_in_files_dialog->get_filter()); @@ -2831,6 +2935,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); @@ -2841,6 +2947,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); @@ -2868,6 +2975,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); @@ -2947,7 +3055,6 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { members_overview->set_custom_minimum_size(Size2(0, 90) * EDSCALE); //need to give a bit of limit to avoid it from disappearing members_overview->set_v_size_flags(SIZE_EXPAND_FILL); members_overview->set_allow_rmb_select(true); - members_overview->set_drag_forwarding(this); help_overview = memnew(ItemList); overview_vbox->add_child(help_overview); @@ -2971,10 +3078,11 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { file_menu = memnew(MenuButton); menu_hb->add_child(file_menu); file_menu->set_text(TTR("File")); + file_menu->set_switch_on_hover(true); file_menu->get_popup()->set_hide_on_window_lose_focus(true); - file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/new", TTR("New Script")), FILE_NEW); - file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/new_textfile", TTR("New TextFile")), FILE_NEW_TEXTFILE); - file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/open", TTR("Open")), FILE_OPEN); + file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/new", TTR("New Script...")), FILE_NEW); + file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/new_textfile", TTR("New TextFile...")), FILE_NEW_TEXTFILE); + file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/open", TTR("Open...")), FILE_OPEN); file_menu->get_popup()->add_submenu_item(TTR("Open Recent"), "RecentScripts", FILE_OPEN_RECENT); recent_scripts = memnew(PopupMenu); @@ -3003,10 +3111,11 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { theme_submenu->set_name("Theme"); file_menu->get_popup()->add_child(theme_submenu); theme_submenu->connect("id_pressed", this, "_theme_option"); - theme_submenu->add_shortcut(ED_SHORTCUT("script_editor/import_theme", TTR("Import Theme")), THEME_IMPORT); + theme_submenu->add_shortcut(ED_SHORTCUT("script_editor/import_theme", TTR("Import Theme...")), THEME_IMPORT); theme_submenu->add_shortcut(ED_SHORTCUT("script_editor/reload_theme", TTR("Reload Theme")), THEME_RELOAD); + theme_submenu->add_separator(); theme_submenu->add_shortcut(ED_SHORTCUT("script_editor/save_theme", TTR("Save Theme")), THEME_SAVE); - theme_submenu->add_shortcut(ED_SHORTCUT("script_editor/save_theme_as", TTR("Save Theme As")), THEME_SAVE_AS); + theme_submenu->add_shortcut(ED_SHORTCUT("script_editor/save_theme_as", TTR("Save Theme As...")), THEME_SAVE_AS); file_menu->get_popup()->add_separator(); file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/close_docs", TTR("Close Docs")), CLOSE_DOCS); @@ -3022,15 +3131,14 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { script_search_menu = memnew(MenuButton); menu_hb->add_child(script_search_menu); script_search_menu->set_text(TTR("Search")); + script_search_menu->set_switch_on_hover(true); script_search_menu->get_popup()->set_hide_on_window_lose_focus(true); - script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find", TTR("Find..."), KEY_MASK_CMD | KEY_F), HELP_SEARCH_FIND); - script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_next", TTR("Find Next"), KEY_F3), HELP_SEARCH_FIND_NEXT); script_search_menu->get_popup()->connect("id_pressed", this, "_menu_option"); - script_search_menu->hide(); debug_menu = memnew(MenuButton); menu_hb->add_child(debug_menu); debug_menu->set_text(TTR("Debug")); + debug_menu->set_switch_on_hover(true); debug_menu->get_popup()->set_hide_on_window_lose_focus(true); debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/step_over", TTR("Step Over"), KEY_F10), DEBUG_NEXT); debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/step_into", TTR("Step Into"), KEY_F11), DEBUG_STEP); @@ -3064,7 +3172,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")); @@ -3110,6 +3224,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); @@ -3196,9 +3312,7 @@ void ScriptEditorPlugin::edit(Object *p_object) { } else { script_editor->edit(p_script); } - } - - if (Object::cast_to<TextFile>(p_object)) { + } else if (Object::cast_to<TextFile>(p_object)) { script_editor->edit(Object::cast_to<TextFile>(p_object)); } } diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index 4be5345aaa..683fa881f8 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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; @@ -155,8 +157,10 @@ class ScriptEditor : public PanelContainer { DEBUG_SHOW, DEBUG_SHOW_KEEP_OPEN, DEBUG_WITH_EXTERNAL_EDITOR, + SEARCH_IN_FILES, SEARCH_HELP, SEARCH_WEBSITE, + REQUEST_DOCS, HELP_SEARCH_FIND, HELP_SEARCH_FIND_NEXT, WINDOW_MOVE_UP, @@ -200,6 +204,7 @@ class ScriptEditor : public PanelContainer { Button *help_search; Button *site_search; + Button *request_docs; EditorHelpSearch *help_search_dialog; ItemList *script_list; @@ -316,6 +321,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 +338,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 e6bb8b24a9..f66ae0465f 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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() { @@ -141,7 +213,9 @@ void ScriptTextEditor::_load_theme_settings() { Color function_color = EDITOR_GET("text_editor/highlighting/function_color"); Color member_variable_color = EDITOR_GET("text_editor/highlighting/member_variable_color"); Color mark_color = EDITOR_GET("text_editor/highlighting/mark_color"); + Color bookmark_color = EDITOR_GET("text_editor/highlighting/bookmark_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"); @@ -172,7 +246,9 @@ void ScriptTextEditor::_load_theme_settings() { text_edit->add_color_override("number_color", number_color); 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("bookmark_color", bookmark_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); @@ -273,11 +349,12 @@ void ScriptTextEditor::_set_theme_for_script() { } } -void ScriptTextEditor::_toggle_warning_pannel(const Ref<InputEvent> &p_event) { - Ref<InputEventMouseButton> mb = p_event; - if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { - warnings_panel->set_visible(!warnings_panel->is_visible()); - } +void ScriptTextEditor::_show_warnings_panel(bool p_show) { + warnings_panel->set_visible(p_show); +} + +void ScriptTextEditor::_error_pressed() { + code_editor->goto_error(); } void ScriptTextEditor::_warning_clicked(Variant p_line) { @@ -285,7 +362,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(); } } @@ -301,7 +378,6 @@ void ScriptTextEditor::reload_text() { int v = te->get_v_scroll(); te->set_text(script->get_source_code()); - te->clear_undo_history(); te->cursor_set_line(row); te->cursor_set_column(column); te->set_h_scroll(h); @@ -317,7 +393,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; } } @@ -362,6 +437,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) { @@ -399,6 +482,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(); @@ -460,9 +551,32 @@ void ScriptTextEditor::_validate_script() { functions.push_back(E->get()); } } + _update_connected_methods(); - code_editor->get_warning_count_label()->set_text(itos(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(); @@ -516,6 +630,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) @@ -619,7 +754,9 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c } ScriptLanguage::LookupResult result; - if (p_symbol.is_resource_file()) { + if (ScriptServer::is_global_class(p_symbol)) { + EditorNode::get_singleton()->load_resource(ScriptServer::get_global_class_path(p_symbol)); + } else if (p_symbol.is_resource_file()) { List<String> scene_extensions; ResourceLoader::get_recognized_extensions_for_type("PackedScene", &scene_extensions); @@ -709,6 +846,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(); @@ -793,92 +971,7 @@ void ScriptTextEditor::_edit_option(int p_op) { } break; case EDIT_TOGGLE_COMMENT: { - Ref<Script> scr = script; - if (scr.is_null()) - return; - - String delimiter = "#"; - List<String> comment_delimiters; - scr->get_language()->get_comment_delimiters(&comment_delimiters); - - for (List<String>::Element *E = comment_delimiters.front(); E; E = E->next()) { - String script_delimiter = E->get(); - if (script_delimiter.find(" ") == -1) { - delimiter = script_delimiter; - break; - } - } - - tx->begin_complex_operation(); - if (tx->is_selection_active()) { - int begin = tx->get_selection_from_line(); - int end = tx->get_selection_to_line(); - - // End of selection ends on the first column of the last line, ignore it. - if (tx->get_selection_to_column() == 0) - end -= 1; - - int col_to = tx->get_selection_to_column(); - int cursor_pos = tx->cursor_get_column(); - - // Check if all lines in the selected block are commented - bool is_commented = true; - for (int i = begin; i <= end; i++) { - if (!tx->get_line(i).begins_with(delimiter)) { - is_commented = false; - break; - } - } - for (int i = begin; i <= end; i++) { - String line_text = tx->get_line(i); - - if (line_text.strip_edges().empty()) { - line_text = delimiter; - } else { - if (is_commented) { - line_text = line_text.substr(delimiter.length(), line_text.length()); - } else { - line_text = delimiter + line_text; - } - } - tx->set_line(i, line_text); - } - - // Adjust selection & cursor position. - int offset = is_commented ? -1 : 1; - int col_from = tx->get_selection_from_column() > 0 ? tx->get_selection_from_column() + offset : 0; - - if (is_commented && tx->cursor_get_column() == tx->get_line(tx->cursor_get_line()).length() + 1) - cursor_pos += 1; - - if (tx->get_selection_to_column() != 0 && col_to != tx->get_line(tx->get_selection_to_line()).length() + 1) - col_to += offset; - - if (tx->cursor_get_column() != 0) - cursor_pos += offset; - - tx->select(begin, col_from, tx->get_selection_to_line(), col_to); - tx->cursor_set_column(cursor_pos); - - } else { - int begin = tx->cursor_get_line(); - String line_text = tx->get_line(begin); - - int col = tx->cursor_get_column(); - if (line_text.begins_with(delimiter)) { - line_text = line_text.substr(delimiter.length(), line_text.length()); - col -= 1; - } else { - line_text = delimiter + line_text; - col += 1; - } - - tx->set_line(begin, line_text); - tx->cursor_set_column(col); - } - tx->end_complex_operation(); - tx->update(); - + _edit_option_toggle_inline_comment(); } break; case EDIT_COMPLETE: { @@ -974,6 +1067,22 @@ void ScriptTextEditor::_edit_option(int p_op) { goto_line_dialog->popup_find_line(tx); } break; + case BOOKMARK_TOGGLE: { + + code_editor->toggle_bookmark(); + } break; + case BOOKMARK_GOTO_NEXT: { + + code_editor->goto_next_bookmark(); + } break; + case BOOKMARK_GOTO_PREV: { + + code_editor->goto_prev_bookmark(); + } break; + case BOOKMARK_REMOVE_ALL: { + + code_editor->remove_all_bookmarks(); + } break; case DEBUG_TOGGLE_BREAKPOINT: { int line = tx->cursor_get_line(); @@ -992,7 +1101,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; @@ -1065,6 +1174,25 @@ void ScriptTextEditor::_edit_option(int p_op) { } } +void ScriptTextEditor::_edit_option_toggle_inline_comment() { + if (script.is_null()) + return; + + String delimiter = "#"; + List<String> comment_delimiters; + script->get_language()->get_comment_delimiters(&comment_delimiters); + + for (List<String>::Element *E = comment_delimiters.front(); E; E = E->next()) { + String script_delimiter = E->get(); + if (script_delimiter.find(" ") == -1) { + delimiter = script_delimiter; + break; + } + } + + code_editor->toggle_inline_comment(delimiter); +} + void ScriptTextEditor::add_syntax_highlighter(SyntaxHighlighter *p_highlighter) { highlighters[p_highlighter->get_name()] = p_highlighter; highlighter_menu->add_radio_check_item(p_highlighter->get_name()); @@ -1076,7 +1204,7 @@ void ScriptTextEditor::set_syntax_highlighter(SyntaxHighlighter *p_highlighter) if (p_highlighter != NULL) highlighter_menu->set_item_checked(highlighter_menu->get_item_idx_from_text(p_highlighter->get_name()), true); else - highlighter_menu->set_item_checked(highlighter_menu->get_item_idx_from_text("Standard"), true); + highlighter_menu->set_item_checked(highlighter_menu->get_item_idx_from_text(TTR("Standard")), true); } void ScriptTextEditor::_change_syntax_highlighter(int p_idx) { @@ -1087,7 +1215,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() { @@ -1095,12 +1222,15 @@ 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); ClassDB::bind_method("_lookup_symbol", &ScriptTextEditor::_lookup_symbol); ClassDB::bind_method("_text_edit_gui_input", &ScriptTextEditor::_text_edit_gui_input); - ClassDB::bind_method("_toggle_warning_pannel", &ScriptTextEditor::_toggle_warning_pannel); + ClassDB::bind_method("_show_warnings_panel", &ScriptTextEditor::_show_warnings_panel); + ClassDB::bind_method("_error_pressed", &ScriptTextEditor::_error_pressed); ClassDB::bind_method("_warning_clicked", &ScriptTextEditor::_warning_clicked); ClassDB::bind_method("_color_changed", &ScriptTextEditor::_color_changed); @@ -1153,7 +1283,8 @@ bool ScriptTextEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_ Dictionary d = p_data; if (d.has("type") && (String(d["type"]) == "resource" || String(d["type"]) == "files" || - String(d["type"]) == "nodes")) { + String(d["type"]) == "nodes" || + String(d["type"]) == "files_and_dirs")) { return true; } @@ -1215,7 +1346,7 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data te->insert_text_at_cursor(res->get_path()); } - if (d.has("type") && String(d["type"]) == "files") { + if (d.has("type") && (String(d["type"]) == "files" || String(d["type"]) == "files_and_dirs")) { Array files = d["files"]; @@ -1386,6 +1517,7 @@ void ScriptTextEditor::_make_context_menu(bool p_selection, bool p_color, bool p context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_left"), EDIT_INDENT_LEFT); context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_right"), EDIT_INDENT_RIGHT); context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_bookmark"), BOOKMARK_TOGGLE); if (p_selection) { context_menu->add_separator(); @@ -1419,13 +1551,14 @@ ScriptTextEditor::ScriptTextEditor() { code_editor = memnew(CodeTextEditor); editor_box->add_child(code_editor); - code_editor->add_constant_override("separation", 0); + code_editor->add_constant_override("separation", 2); code_editor->set_anchors_and_margins_preset(Control::PRESET_WIDE); code_editor->connect("validate_script", this, "_validate_script"); code_editor->connect("load_theme_settings", this, "_load_theme_settings"); 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); @@ -1437,8 +1570,8 @@ ScriptTextEditor::ScriptTextEditor() { warnings_panel->set_focus_mode(FOCUS_CLICK); warnings_panel->hide(); - code_editor->get_warning_label()->connect("gui_input", this, "_toggle_warning_pannel"); - code_editor->get_warning_count_label()->connect("gui_input", this, "_toggle_warning_pannel"); + code_editor->connect("error_pressed", this, "_error_pressed"); + code_editor->connect("show_warnings_panel", this, "_show_warnings_panel"); warnings_panel->connect("meta_clicked", this, "_warning_clicked"); update_settings(); @@ -1466,6 +1599,7 @@ ScriptTextEditor::ScriptTextEditor() { edit_menu = memnew(MenuButton); edit_menu->set_text(TTR("Edit")); + edit_menu->set_switch_on_hover(true); edit_menu->get_popup()->set_hide_on_window_lose_focus(true); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/undo"), EDIT_UNDO); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/redo"), EDIT_REDO); @@ -1509,7 +1643,7 @@ ScriptTextEditor::ScriptTextEditor() { convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/capitalize", TTR("Capitalize"), KEY_MASK_SHIFT | KEY_F6), EDIT_CAPITALIZE); convert_case->connect("id_pressed", this, "_edit_option"); - highlighters["Standard"] = NULL; + highlighters[TTR("Standard")] = NULL; highlighter_menu = memnew(PopupMenu); highlighter_menu->set_name("highlighter_menu"); edit_menu->get_popup()->add_child(highlighter_menu); @@ -1520,6 +1654,7 @@ ScriptTextEditor::ScriptTextEditor() { search_menu = memnew(MenuButton); edit_hb->add_child(search_menu); search_menu->set_text(TTR("Search")); + search_menu->set_switch_on_hover(true); search_menu->get_popup()->set_hide_on_window_lose_focus(true); search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find"), SEARCH_FIND); search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_next"), SEARCH_FIND_NEXT); @@ -1535,6 +1670,16 @@ ScriptTextEditor::ScriptTextEditor() { search_menu->get_popup()->connect("id_pressed", this, "_edit_option"); + PopupMenu *bookmarks = memnew(PopupMenu); + bookmarks->set_name("bookmarks"); + edit_menu->get_popup()->add_child(bookmarks); + edit_menu->get_popup()->add_submenu_item(TTR("Bookmarks"), "bookmarks"); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_bookmark"), BOOKMARK_TOGGLE); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/remove_all_bookmarks"), BOOKMARK_REMOVE_ALL); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_next_bookmark"), BOOKMARK_GOTO_NEXT); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_previous_bookmark"), BOOKMARK_GOTO_PREV); + bookmarks->connect("id_pressed", this, "_edit_option"); + edit_hb->add_child(edit_menu); quick_open = memnew(ScriptEditorQuickOpen); @@ -1544,6 +1689,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); } @@ -1573,6 +1721,10 @@ void ScriptTextEditor::register_editor() { ED_SHORTCUT("script_text_editor/indent_left", TTR("Indent Left"), 0); ED_SHORTCUT("script_text_editor/indent_right", TTR("Indent Right"), 0); ED_SHORTCUT("script_text_editor/toggle_comment", TTR("Toggle Comment"), KEY_MASK_CMD | KEY_K); + ED_SHORTCUT("script_text_editor/toggle_bookmark", TTR("Toggle Bookmark"), KEY_MASK_CMD | KEY_MASK_ALT | KEY_B); + ED_SHORTCUT("script_text_editor/goto_next_bookmark", TTR("Go to Next Bookmark"), KEY_MASK_CMD | KEY_B); + ED_SHORTCUT("script_text_editor/goto_previous_bookmark", TTR("Go to Previous Bookmark"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_B); + ED_SHORTCUT("script_text_editor/remove_all_bookmarks", TTR("Remove All Bookmarks"), 0); ED_SHORTCUT("script_text_editor/toggle_fold_line", TTR("Fold/Unfold Line"), KEY_MASK_ALT | KEY_F); ED_SHORTCUT("script_text_editor/fold_all_lines", TTR("Fold All Lines"), 0); ED_SHORTCUT("script_text_editor/unfold_all_lines", TTR("Unfold All Lines"), 0); @@ -1580,7 +1732,7 @@ void ScriptTextEditor::register_editor() { ED_SHORTCUT("script_text_editor/clone_down", TTR("Clone Down"), KEY_MASK_SHIFT | KEY_MASK_CMD | KEY_C); ED_SHORTCUT("script_text_editor/complete_symbol", TTR("Complete Symbol"), KEY_MASK_CTRL | KEY_SPACE); #else - ED_SHORTCUT("script_text_editor/clone_down", TTR("Clone Down"), KEY_MASK_CMD | KEY_B); + ED_SHORTCUT("script_text_editor/clone_down", TTR("Clone Down"), KEY_MASK_CMD | KEY_D); ED_SHORTCUT("script_text_editor/complete_symbol", TTR("Complete Symbol"), KEY_MASK_CMD | KEY_SPACE); #endif ED_SHORTCUT("script_text_editor/trim_trailing_whitespace", TTR("Trim Trailing Whitespace"), KEY_MASK_CMD | KEY_MASK_ALT | KEY_T); @@ -1620,7 +1772,7 @@ void ScriptTextEditor::register_editor() { #ifdef OSX_ENABLED ED_SHORTCUT("script_text_editor/contextual_help", TTR("Contextual Help"), KEY_MASK_ALT | KEY_MASK_SHIFT | KEY_SPACE); #else - ED_SHORTCUT("script_text_editor/contextual_help", TTR("Contextual Help"), KEY_MASK_SHIFT | KEY_F1); + ED_SHORTCUT("script_text_editor/contextual_help", TTR("Contextual Help"), KEY_MASK_ALT | KEY_F1); #endif ScriptEditor::register_create_script_editor_function(create_editor); diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h index 837201a947..bdfdf18d45 100644 --- a/editor/plugins/script_text_editor.h +++ b/editor/plugins/script_text_editor.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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; @@ -108,6 +128,10 @@ class ScriptTextEditor : public ScriptEditorBase { SEARCH_LOCATE_FUNCTION, SEARCH_GOTO_LINE, SEARCH_IN_FILES, + BOOKMARK_TOGGLE, + BOOKMARK_GOTO_NEXT, + BOOKMARK_GOTO_PREV, + BOOKMARK_REMOVE_ALL, DEBUG_TOGGLE_BREAKPOINT, DEBUG_REMOVE_ALL_BREAKPOINTS, DEBUG_GOTO_NEXT_BREAKPOINT, @@ -125,7 +149,8 @@ protected: void _code_complete_script(const String &p_code, List<String> *r_options, bool &r_force); void _load_theme_settings(); void _set_theme_for_script(); - void _toggle_warning_pannel(const Ref<InputEvent> &p_event); + void _show_warnings_panel(bool p_show); + void _error_pressed(); void _warning_clicked(Variant p_line); void _notification(int p_what); @@ -135,6 +160,7 @@ protected: void _change_syntax_highlighter(int p_idx); void _edit_option(int p_op); + void _edit_option_toggle_inline_comment(); void _make_context_menu(bool p_selection, bool p_color, bool p_foldable, bool p_open_docs, bool p_goto_definition); void _text_edit_gui_input(const Ref<InputEvent> &ev); void _color_changed(const Color &p_color); @@ -142,6 +168,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); @@ -149,6 +177,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); @@ -170,6 +200,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 6bc5c77df2..a795405dfc 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -47,12 +47,16 @@ Ref<Shader> ShaderTextEditor::get_edited_shader() const { } void ShaderTextEditor::set_edited_shader(const Ref<Shader> &p_shader) { + if (shader == p_shader) { + return; + } shader = p_shader; _load_theme_settings(); get_text_edit()->set_text(p_shader->get_code()); + _validate_script(); _line_col_changed(); } @@ -80,7 +84,9 @@ void ShaderTextEditor::_load_theme_settings() { Color function_color = EDITOR_GET("text_editor/highlighting/function_color"); Color member_variable_color = EDITOR_GET("text_editor/highlighting/member_variable_color"); Color mark_color = EDITOR_GET("text_editor/highlighting/mark_color"); + Color bookmark_color = EDITOR_GET("text_editor/highlighting/bookmark_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"); @@ -108,7 +114,9 @@ void ShaderTextEditor::_load_theme_settings() { get_text_edit()->add_color_override("function_color", function_color); 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("bookmark_color", bookmark_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); @@ -244,19 +252,19 @@ void ShaderEditor::_menu_option(int p_option) { } break; case EDIT_INDENT_LEFT: { - TextEdit *tx = shader_editor->get_text_edit(); if (shader.is_null()) return; + TextEdit *tx = shader_editor->get_text_edit(); tx->indent_left(); } break; case EDIT_INDENT_RIGHT: { - TextEdit *tx = shader_editor->get_text_edit(); if (shader.is_null()) return; + TextEdit *tx = shader_editor->get_text_edit(); tx->indent_right(); } break; @@ -268,54 +276,10 @@ void ShaderEditor::_menu_option(int p_option) { } break; case EDIT_TOGGLE_COMMENT: { - TextEdit *tx = shader_editor->get_text_edit(); if (shader.is_null()) return; - tx->begin_complex_operation(); - if (tx->is_selection_active()) { - int begin = tx->get_selection_from_line(); - int end = tx->get_selection_to_line(); - - // End of selection ends on the first column of the last line, ignore it. - if (tx->get_selection_to_column() == 0) - end -= 1; - - // Check if all lines in the selected block are commented - bool is_commented = true; - for (int i = begin; i <= end; i++) { - if (!tx->get_line(i).begins_with("//")) { - is_commented = false; - break; - } - } - for (int i = begin; i <= end; i++) { - String line_text = tx->get_line(i); - - if (line_text.strip_edges().empty()) { - line_text = "//"; - } else { - if (is_commented) { - line_text = line_text.substr(2, line_text.length()); - } else { - line_text = "//" + line_text; - } - } - tx->set_line(i, line_text); - } - } else { - int begin = tx->cursor_get_line(); - String line_text = tx->get_line(begin); - - if (line_text.begins_with("//")) - line_text = line_text.substr(2, line_text.length()); - else - line_text = "//" + line_text; - tx->set_line(begin, line_text); - } - tx->end_complex_operation(); - tx->update(); - //tx->deselect(); + shader_editor->toggle_inline_comment("//"); } break; case EDIT_COMPLETE: { @@ -342,6 +306,22 @@ void ShaderEditor::_menu_option(int p_option) { goto_line_dialog->popup_find_line(shader_editor->get_text_edit()); } break; + case BOOKMARK_TOGGLE: { + + shader_editor->toggle_bookmark(); + } break; + case BOOKMARK_GOTO_NEXT: { + + shader_editor->goto_next_bookmark(); + } break; + case BOOKMARK_GOTO_PREV: { + + shader_editor->goto_prev_bookmark(); + } break; + case BOOKMARK_REMOVE_ALL: { + + shader_editor->remove_all_bookmarks(); + } break; } if (p_option != SEARCH_FIND && p_option != SEARCH_REPLACE && p_option != SEARCH_GOTO_LINE) { shader_editor->get_text_edit()->call_deferred("grab_focus"); @@ -350,9 +330,9 @@ void ShaderEditor::_menu_option(int p_option) { void ShaderEditor::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE) { - } - if (p_what == NOTIFICATION_DRAW) { + if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { + //if (is_visible_in_tree()) + // shader_editor->get_text_edit()->grab_focus(); } } @@ -389,7 +369,6 @@ void ShaderEditor::_bind_methods() { ClassDB::bind_method("_menu_option", &ShaderEditor::_menu_option); ClassDB::bind_method("_params_changed", &ShaderEditor::_params_changed); ClassDB::bind_method("apply_shaders", &ShaderEditor::apply_shaders); - //ClassDB::bind_method("_close_current_tab",&ShaderEditor::_close_current_tab); } void ShaderEditor::ensure_select_current() { @@ -405,11 +384,19 @@ void ShaderEditor::ensure_select_current() { }*/ } +void ShaderEditor::goto_line_selection(int p_line, int p_begin, int p_end) { + + shader_editor->goto_line_selection(p_line, p_begin, p_end); +} + void ShaderEditor::edit(const Ref<Shader> &p_shader) { if (p_shader.is_null() || !p_shader->is_text_shader()) return; + if (shader == p_shader) + return; + shader = p_shader; shader_editor->set_edited_shader(p_shader); @@ -433,8 +420,12 @@ void ShaderEditor::save_external_data() { void ShaderEditor::apply_shaders() { if (shader.is_valid()) { - shader->set_code(shader_editor->get_text_edit()->get_text()); - shader->set_edited(true); + String shader_code = shader->get_code(); + String editor_code = shader_editor->get_text_edit()->get_text(); + if (shader_code != editor_code) { + shader->set_code(editor_code); + shader->set_edited(true); + } } } @@ -527,9 +518,8 @@ ShaderEditor::ShaderEditor(EditorNode *p_node) { HBoxContainer *hbc = memnew(HBoxContainer); edit_menu = memnew(MenuButton); - //edit_menu->set_position(Point2(5, -1)); edit_menu->set_text(TTR("Edit")); - + edit_menu->set_switch_on_hover(true); edit_menu->get_popup()->set_hide_on_window_lose_focus(true); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/undo"), EDIT_UNDO); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/redo"), EDIT_REDO); @@ -549,12 +539,11 @@ ShaderEditor::ShaderEditor(EditorNode *p_node) { edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/clone_down"), EDIT_CLONE_DOWN); edit_menu->get_popup()->add_separator(); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/complete_symbol"), EDIT_COMPLETE); - edit_menu->get_popup()->connect("id_pressed", this, "_menu_option"); search_menu = memnew(MenuButton); - //search_menu->set_position(Point2(38, -1)); search_menu->set_text(TTR("Search")); + search_menu->set_switch_on_hover(true); search_menu->get_popup()->set_hide_on_window_lose_focus(true); search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find"), SEARCH_FIND); search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_next"), SEARCH_FIND_NEXT); @@ -564,6 +553,16 @@ ShaderEditor::ShaderEditor(EditorNode *p_node) { search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_line"), SEARCH_GOTO_LINE); search_menu->get_popup()->connect("id_pressed", this, "_menu_option"); + PopupMenu *bookmarks = memnew(PopupMenu); + bookmarks->set_name("bookmarks"); + edit_menu->get_popup()->add_child(bookmarks); + edit_menu->get_popup()->add_submenu_item(TTR("Bookmarks"), "bookmarks"); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_bookmark"), BOOKMARK_TOGGLE); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/remove_all_bookmarks"), BOOKMARK_REMOVE_ALL); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_next_bookmark"), BOOKMARK_GOTO_NEXT); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_previous_bookmark"), BOOKMARK_GOTO_PREV); + bookmarks->connect("id_pressed", this, "_edit_option"); + add_child(main_container); main_container->add_child(hbc); hbc->add_child(search_menu); diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h index 2ea1562310..28ac9faaa5 100644 --- a/editor/plugins/shader_editor_plugin.h +++ b/editor/plugins/shader_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -88,6 +88,10 @@ class ShaderEditor : public PanelContainer { SEARCH_FIND_PREV, SEARCH_REPLACE, SEARCH_GOTO_LINE, + BOOKMARK_TOGGLE, + BOOKMARK_GOTO_NEXT, + BOOKMARK_GOTO_PREV, + BOOKMARK_REMOVE_ALL, }; @@ -120,6 +124,8 @@ public: void ensure_select_current(); void edit(const Ref<Shader> &p_shader); + void goto_line_selection(int p_line, int p_begin, int p_end); + virtual Size2 get_minimum_size() const { return Size2(0, 200); } void save_external_data(); @@ -143,6 +149,8 @@ public: virtual void make_visible(bool p_visible); virtual void selected_notify(); + ShaderEditor *get_shader_editor() const { return shader_editor; } + virtual void save_external_data(); virtual void apply_changes(); diff --git a/editor/plugins/skeleton_2d_editor_plugin.cpp b/editor/plugins/skeleton_2d_editor_plugin.cpp index 08bfebefbd..0ccb60e39e 100644 --- a/editor/plugins/skeleton_2d_editor_plugin.cpp +++ b/editor/plugins/skeleton_2d_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -63,7 +63,7 @@ void Skeleton2DEditor::_menu_option(int p_option) { return; } UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); - ur->create_action("Create Rest Pose from Bones"); + ur->create_action(TTR("Create Rest Pose from Bones")); for (int i = 0; i < node->get_bone_count(); i++) { Bone2D *bone = node->get_bone(i); ur->add_do_method(bone, "set_rest", bone->get_transform()); @@ -79,7 +79,7 @@ void Skeleton2DEditor::_menu_option(int p_option) { return; } UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); - ur->create_action("Set Rest Pose to Bones"); + ur->create_action(TTR("Set Rest Pose to Bones")); for (int i = 0; i < node->get_bone_count(); i++) { Bone2D *bone = node->get_bone(i); ur->add_do_method(bone, "set_transform", bone->get_rest()); @@ -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/skeleton_2d_editor_plugin.h b/editor/plugins/skeleton_2d_editor_plugin.h index 26ab4328b0..d8e2d23257 100644 --- a/editor/plugins/skeleton_2d_editor_plugin.h +++ b/editor/plugins/skeleton_2d_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/skeleton_editor_plugin.cpp b/editor/plugins/skeleton_editor_plugin.cpp index 8b0beefb3e..cd360d4caf 100644 --- a/editor/plugins/skeleton_editor_plugin.cpp +++ b/editor/plugins/skeleton_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/skeleton_editor_plugin.h b/editor/plugins/skeleton_editor_plugin.h index aac3e06063..33a9128a11 100644 --- a/editor/plugins/skeleton_editor_plugin.h +++ b/editor/plugins/skeleton_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/skeleton_ik_editor_plugin.cpp b/editor/plugins/skeleton_ik_editor_plugin.cpp index c605548a6b..928171950a 100644 --- a/editor/plugins/skeleton_ik_editor_plugin.cpp +++ b/editor/plugins/skeleton_ik_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/skeleton_ik_editor_plugin.h b/editor/plugins/skeleton_ik_editor_plugin.h index 3e412305c9..d1ef7c99c5 100644 --- a/editor/plugins/skeleton_ik_editor_plugin.h +++ b/editor/plugins/skeleton_ik_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index cc5f50a834..60f1248ace 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -35,7 +35,7 @@ #include "core/os/keyboard.h" #include "core/print_string.h" #include "core/project_settings.h" -#include "core/sort.h" +#include "core/sort_array.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" #include "editor/plugins/animation_player_editor_plugin.h" @@ -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(); @@ -1021,7 +1044,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { } _edit.mouse_pos = b->get_position(); - _edit.snap = false; + _edit.snap = spatial_editor->is_snap_enabled(); _edit.mode = TRANSFORM_NONE; //gizmo has priority over everything @@ -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: { + } } } @@ -1772,7 +1798,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { if (ED_IS_SHORTCUT("spatial_editor/snap", p_event)) { if (_edit.mode != TRANSFORM_NONE) { - _edit.snap = true; + _edit.snap = !_edit.snap; } } if (ED_IS_SHORTCUT("spatial_editor/bottom_view", p_event)) { @@ -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.")); @@ -2108,9 +2134,11 @@ void SpatialEditorViewport::_notification(int p_what) { set_process(visible); - if (visible) + if (visible) { _update_camera(0); - + } else { + set_freelook_active(false); + } call_deferred("update_transform_gizmo_view"); } @@ -2262,7 +2290,7 @@ void SpatialEditorViewport::_notification(int p_what) { if (show_fps) { String text; const float temp_fps = Engine::get_singleton()->get_frames_per_second(); - text += TTR("FPS") + ": " + itos(temp_fps) + " (" + String::num(1000.0f / temp_fps, 2) + " ms)"; + text += TTR(vformat("FPS: %d (%s ms)", temp_fps, String::num(1000.0f / temp_fps, 2))); fps_label->set_text(text); } @@ -2380,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) { @@ -2394,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) { @@ -2436,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, @@ -2454,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, @@ -2546,7 +2583,7 @@ void SpatialEditorViewport::_menu_option(int p_option) { List<Node *> &selection = editor_selection->get_selected_node_list(); - undo_redo->create_action(TTR("Align with view")); + undo_redo->create_action(TTR("Align with View")); for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { Spatial *sp = Object::cast_to<Spatial>(E->get()); @@ -2865,6 +2902,18 @@ void SpatialEditorViewport::update_transform_gizmo_view() { Transform xform = spatial_editor->get_gizmo_transform(); Transform camera_xform = camera->get_transform(); + + if (xform.origin.distance_squared_to(camera_xform.origin) < 0.01) { + for (int i = 0; i < 3; i++) { + VisualServer::get_singleton()->instance_set_visible(move_gizmo_instance[i], false); + VisualServer::get_singleton()->instance_set_visible(move_plane_gizmo_instance[i], false); + VisualServer::get_singleton()->instance_set_visible(rotate_gizmo_instance[i], false); + VisualServer::get_singleton()->instance_set_visible(scale_gizmo_instance[i], false); + VisualServer::get_singleton()->instance_set_visible(scale_plane_gizmo_instance[i], false); + } + return; + } + Vector3 camz = -camera_xform.get_basis().get_axis(2).normalized(); Vector3 camy = -camera_xform.get_basis().get_axis(1).normalized(); Plane p(camera_xform.origin, camz); @@ -2987,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(); @@ -3461,10 +3510,14 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed camera->make_current(); surface->set_focus_mode(FOCUS_ALL); + VBoxContainer *vbox = memnew(VBoxContainer); + surface->add_child(vbox); + vbox->set_position(Point2(10, 10) * EDSCALE); + view_menu = memnew(MenuButton); view_menu->set_flat(false); - surface->add_child(view_menu); - view_menu->set_position(Point2(10, 10) * EDSCALE); + vbox->add_child(view_menu); + view_menu->set_h_size_flags(0); view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/top_view"), VIEW_TOP); view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/bottom_view"), VIEW_BOTTOM); @@ -3517,9 +3570,9 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed ED_SHORTCUT("spatial_editor/freelook_speed_modifier", TTR("Freelook Speed Modifier"), KEY_SHIFT); preview_camera = memnew(CheckBox); - preview_camera->set_position(Point2(10, 38) * EDSCALE); // Below the 'view_menu' MenuButton. preview_camera->set_text(TTR("Preview")); - surface->add_child(preview_camera); + vbox->add_child(preview_camera); + preview_camera->set_h_size_flags(0); preview_camera->hide(); preview_camera->connect("toggled", this, "_toggle_camera_preview"); previewing = NULL; @@ -3542,6 +3595,8 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed fps_label->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 10 * EDSCALE); fps_label->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, -10 * EDSCALE); fps_label->set_h_grow_direction(GROW_DIRECTION_BEGIN); + fps_label->set_tooltip(TTR("Note: The FPS value displayed is the editor's framerate.\nIt cannot be used as a reliable indication of in-game performance.")); + fps_label->set_mouse_filter(MOUSE_FILTER_PASS); // Otherwise tooltip doesn't show. surface->add_child(fps_label); fps_label->hide(); @@ -4101,10 +4156,10 @@ Dictionary SpatialEditor::get_state() const { d["zfar"] = get_zfar(); Dictionary gizmos_status; - for (int i = 0; i < gizmo_plugins.size(); i++) { - if (!gizmo_plugins[i]->can_be_hidden()) continue; + for (int i = 0; i < gizmo_plugins_by_name.size(); i++) { + if (!gizmo_plugins_by_name[i]->can_be_hidden()) continue; int state = gizmos_menu->get_item_state(gizmos_menu->get_item_index(i)); - String name = gizmo_plugins[i]->get_name(); + String name = gizmo_plugins_by_name[i]->get_name(); gizmos_status[name] = state; } @@ -4154,9 +4209,13 @@ void SpatialEditor::set_state(const Dictionary &p_state) { if (d.has("viewports")) { Array vp = d["viewports"]; - ERR_FAIL_COND(vp.size() > 4); + uint32_t vp_size = static_cast<uint32_t>(vp.size()); + if (vp_size > VIEWPORTS_COUNT) { + WARN_PRINT("Ignoring superfluous viewport settings from spatial editor state.") + vp_size = VIEWPORTS_COUNT; + } - for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) { + for (uint32_t i = 0; i < vp_size; i++) { viewports[i]->set_state(vp[i]); } } @@ -4189,32 +4248,19 @@ void SpatialEditor::set_state(const Dictionary &p_state) { List<Variant> keys; gizmos_status.get_key_list(&keys); - for (int j = 0; j < gizmo_plugins.size(); ++j) { - if (!gizmo_plugins[j]->can_be_hidden()) continue; - int state = EditorSpatialGizmoPlugin::ON_TOP; + for (int j = 0; j < gizmo_plugins_by_name.size(); ++j) { + if (!gizmo_plugins_by_name[j]->can_be_hidden()) continue; + int state = EditorSpatialGizmoPlugin::VISIBLE; for (int i = 0; i < keys.size(); i++) { - if (gizmo_plugins.write[j]->get_name() == keys[i]) { + if (gizmo_plugins_by_name.write[j]->get_name() == keys[i]) { state = gizmos_status[keys[i]]; + break; } } - const int idx = gizmos_menu->get_item_index(j); - - gizmos_menu->set_item_multistate(idx, state); - gizmo_plugins.write[j]->set_state(state); - - switch (state) { - case EditorSpatialGizmoPlugin::VISIBLE: - gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_visible")); - break; - case EditorSpatialGizmoPlugin::ON_TOP: - gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_xray")); - break; - case EditorSpatialGizmoPlugin::HIDDEN: - gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_hidden")); - break; - } + gizmo_plugins_by_name.write[j]->set_state(state); } + _update_gizmos_menu(); } } @@ -4328,7 +4374,7 @@ void SpatialEditor::_menu_gizmo_toggled(int p_option) { break; } - gizmo_plugins.write[p_option]->set_state(state); + gizmo_plugins_by_name.write[p_option]->set_state(state); update_all_gizmos(); } @@ -4473,6 +4519,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(); @@ -4485,13 +4532,18 @@ 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(); @@ -4504,11 +4556,62 @@ 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_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"); } - _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_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()) { + + 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_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"); + } + + 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; } } @@ -4681,10 +4784,10 @@ void SpatialEditor::_init_indicators() { plane_mat->set_on_top_of_alpha(); plane_mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); plane_mat->set_cull_mode(SpatialMaterial::CULL_DISABLED); - Color col; - col[i] = 1.0; - col.a = gizmo_alph; - plane_mat->set_albedo(col); + Color col2; + col2[i] = 1.0; + col2.a = gizmo_alph; + plane_mat->set_albedo(col2); plane_gizmo_color[i] = plane_mat; // needed, so we can draw planes from both sides surftool->set_material(plane_mat); surftool->commit(move_plane_gizmo[i]); @@ -4810,10 +4913,10 @@ void SpatialEditor::_init_indicators() { plane_mat->set_on_top_of_alpha(); plane_mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); plane_mat->set_cull_mode(SpatialMaterial::CULL_DISABLED); - Color col; - col[i] = 1.0; - col.a = gizmo_alph; - plane_mat->set_albedo(col); + Color col2; + col2[i] = 1.0; + col2.a = gizmo_alph; + plane_mat->set_albedo(col2); plane_gizmo_color[i] = plane_mat; // needed, so we can draw planes from both sides surftool->set_material(plane_mat); surftool->commit(scale_plane_gizmo[i]); @@ -4824,23 +4927,46 @@ void SpatialEditor::_init_indicators() { _generate_selection_box(); } -struct _GizmoPluginComparator { +void SpatialEditor::_update_gizmos_menu() { - bool operator()(const Ref<EditorSpatialGizmoPlugin> &p_a, const Ref<EditorSpatialGizmoPlugin> &p_b) const { - return p_a->get_name() < p_b->get_name(); - } -}; - -void SpatialEditor::_init_gizmos_menu() { - _register_all_gizmos(); + gizmos_menu->clear(); - gizmo_plugins.sort_custom<_GizmoPluginComparator>(); + for (int i = 0; i < gizmo_plugins_by_name.size(); ++i) { + if (!gizmo_plugins_by_name[i]->can_be_hidden()) continue; + String plugin_name = gizmo_plugins_by_name[i]->get_name(); + const int plugin_state = gizmo_plugins_by_name[i]->get_state(); + gizmos_menu->add_multistate_item(TTR(plugin_name), 3, plugin_state, i); + const int idx = gizmos_menu->get_item_index(i); + switch (plugin_state) { + case EditorSpatialGizmoPlugin::VISIBLE: + gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_visible")); + break; + case EditorSpatialGizmoPlugin::ON_TOP: + gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_xray")); + break; + case EditorSpatialGizmoPlugin::HIDDEN: + gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_hidden")); + break; + } + } +} - for (int i = 0; i < gizmo_plugins.size(); ++i) { - if (!gizmo_plugins[i]->can_be_hidden()) continue; - String plugin_name = gizmo_plugins[i]->get_name(); - gizmos_menu->add_multistate_item(TTR(plugin_name), 3, EditorSpatialGizmoPlugin::VISIBLE, i); - gizmos_menu->set_item_icon(gizmos_menu->get_item_index(i), gizmos_menu->get_icon("visibility_visible")); +void SpatialEditor::_update_gizmos_menu_theme() { + for (int i = 0; i < gizmo_plugins_by_name.size(); ++i) { + if (!gizmo_plugins_by_name[i]->can_be_hidden()) continue; + const int plugin_state = gizmo_plugins_by_name[i]->get_state(); + const int idx = gizmos_menu->get_item_index(i); + switch (plugin_state) { + case EditorSpatialGizmoPlugin::VISIBLE: + gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_visible")); + break; + case EditorSpatialGizmoPlugin::ON_TOP: + gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_xray")); + break; + case EditorSpatialGizmoPlugin::HIDDEN: + gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_hidden")); + break; + } } } @@ -4929,11 +5055,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_")) { @@ -4941,11 +5069,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> @@ -5034,7 +5172,7 @@ void SpatialEditor::snap_selected_nodes_to_floor() { Array keys = snap_data.keys(); if (keys.size()) { - undo_redo->create_action("Snap Nodes To Floor"); + undo_redo->create_action(TTR("Snap Nodes To Floor")); for (int i = 0; i < keys.size(); i++) { Node *node = keys[i]; @@ -5115,6 +5253,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")); @@ -5133,19 +5273,17 @@ void SpatialEditor::_notification(int p_what) { get_tree()->connect("node_removed", this, "_node_removed"); EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor()->connect("node_changed", this, "_refresh_menu_icons"); editor_selection->connect("selection_changed", this, "_refresh_menu_icons"); - } - - if (p_what == NOTIFICATION_ENTER_TREE) { + } else if (p_what == NOTIFICATION_ENTER_TREE) { - _init_gizmos_menu(); + _register_all_gizmos(); + _update_gizmos_menu(); _init_indicators(); - } - - if (p_what == NOTIFICATION_EXIT_TREE) { + } else if (p_what == NOTIFICATION_THEME_CHANGED) { + _update_gizmos_menu_theme(); + } else if (p_what == NOTIFICATION_EXIT_TREE) { _finish_indicators(); - } - if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { + } else if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { tool_button[SpatialEditor::TOOL_MODE_SELECT]->set_icon(get_icon("ToolSelect", "EditorIcons")); tool_button[SpatialEditor::TOOL_MODE_MOVE]->set_icon(get_icon("ToolMove", "EditorIcons")); tool_button[SpatialEditor::TOOL_MODE_ROTATE]->set_icon(get_icon("ToolRotate", "EditorIcons")); @@ -5204,8 +5342,8 @@ void SpatialEditor::_request_gizmo(Object *p_obj) { Ref<EditorSpatialGizmo> seg; - for (int i = 0; i < gizmo_plugins.size(); ++i) { - seg = gizmo_plugins.write[i]->get_gizmo(sp); + for (int i = 0; i < gizmo_plugins_by_priority.size(); ++i) { + seg = gizmo_plugins_by_priority.write[i]->get_gizmo(sp); if (seg.is_valid()) { sp->set_gizmo(seg); @@ -5273,27 +5411,27 @@ void SpatialEditor::_node_removed(Node *p_node) { } void SpatialEditor::_register_all_gizmos() { - register_gizmo_plugin(Ref<CameraSpatialGizmoPlugin>(memnew(CameraSpatialGizmoPlugin))); - register_gizmo_plugin(Ref<LightSpatialGizmoPlugin>(memnew(LightSpatialGizmoPlugin))); - register_gizmo_plugin(Ref<AudioStreamPlayer3DSpatialGizmoPlugin>(memnew(AudioStreamPlayer3DSpatialGizmoPlugin))); - register_gizmo_plugin(Ref<MeshInstanceSpatialGizmoPlugin>(memnew(MeshInstanceSpatialGizmoPlugin))); - register_gizmo_plugin(Ref<SoftBodySpatialGizmoPlugin>(memnew(SoftBodySpatialGizmoPlugin))); - register_gizmo_plugin(Ref<Sprite3DSpatialGizmoPlugin>(memnew(Sprite3DSpatialGizmoPlugin))); - register_gizmo_plugin(Ref<SkeletonSpatialGizmoPlugin>(memnew(SkeletonSpatialGizmoPlugin))); - register_gizmo_plugin(Ref<Position3DSpatialGizmoPlugin>(memnew(Position3DSpatialGizmoPlugin))); - register_gizmo_plugin(Ref<RayCastSpatialGizmoPlugin>(memnew(RayCastSpatialGizmoPlugin))); - register_gizmo_plugin(Ref<SpringArmSpatialGizmoPlugin>(memnew(SpringArmSpatialGizmoPlugin))); - register_gizmo_plugin(Ref<VehicleWheelSpatialGizmoPlugin>(memnew(VehicleWheelSpatialGizmoPlugin))); - register_gizmo_plugin(Ref<VisibilityNotifierGizmoPlugin>(memnew(VisibilityNotifierGizmoPlugin))); - register_gizmo_plugin(Ref<ParticlesGizmoPlugin>(memnew(ParticlesGizmoPlugin))); - register_gizmo_plugin(Ref<ReflectionProbeGizmoPlugin>(memnew(ReflectionProbeGizmoPlugin))); - register_gizmo_plugin(Ref<GIProbeGizmoPlugin>(memnew(GIProbeGizmoPlugin))); - register_gizmo_plugin(Ref<BakedIndirectLightGizmoPlugin>(memnew(BakedIndirectLightGizmoPlugin))); - register_gizmo_plugin(Ref<CollisionShapeSpatialGizmoPlugin>(memnew(CollisionShapeSpatialGizmoPlugin))); - register_gizmo_plugin(Ref<CollisionPolygonSpatialGizmoPlugin>(memnew(CollisionPolygonSpatialGizmoPlugin))); - register_gizmo_plugin(Ref<NavigationMeshSpatialGizmoPlugin>(memnew(NavigationMeshSpatialGizmoPlugin))); - register_gizmo_plugin(Ref<JointSpatialGizmoPlugin>(memnew(JointSpatialGizmoPlugin))); - register_gizmo_plugin(Ref<PhysicalBoneSpatialGizmoPlugin>(memnew(PhysicalBoneSpatialGizmoPlugin))); + add_gizmo_plugin(Ref<CameraSpatialGizmoPlugin>(memnew(CameraSpatialGizmoPlugin))); + add_gizmo_plugin(Ref<LightSpatialGizmoPlugin>(memnew(LightSpatialGizmoPlugin))); + add_gizmo_plugin(Ref<AudioStreamPlayer3DSpatialGizmoPlugin>(memnew(AudioStreamPlayer3DSpatialGizmoPlugin))); + add_gizmo_plugin(Ref<MeshInstanceSpatialGizmoPlugin>(memnew(MeshInstanceSpatialGizmoPlugin))); + add_gizmo_plugin(Ref<SoftBodySpatialGizmoPlugin>(memnew(SoftBodySpatialGizmoPlugin))); + add_gizmo_plugin(Ref<Sprite3DSpatialGizmoPlugin>(memnew(Sprite3DSpatialGizmoPlugin))); + add_gizmo_plugin(Ref<SkeletonSpatialGizmoPlugin>(memnew(SkeletonSpatialGizmoPlugin))); + add_gizmo_plugin(Ref<Position3DSpatialGizmoPlugin>(memnew(Position3DSpatialGizmoPlugin))); + add_gizmo_plugin(Ref<RayCastSpatialGizmoPlugin>(memnew(RayCastSpatialGizmoPlugin))); + add_gizmo_plugin(Ref<SpringArmSpatialGizmoPlugin>(memnew(SpringArmSpatialGizmoPlugin))); + add_gizmo_plugin(Ref<VehicleWheelSpatialGizmoPlugin>(memnew(VehicleWheelSpatialGizmoPlugin))); + add_gizmo_plugin(Ref<VisibilityNotifierGizmoPlugin>(memnew(VisibilityNotifierGizmoPlugin))); + add_gizmo_plugin(Ref<ParticlesGizmoPlugin>(memnew(ParticlesGizmoPlugin))); + add_gizmo_plugin(Ref<ReflectionProbeGizmoPlugin>(memnew(ReflectionProbeGizmoPlugin))); + add_gizmo_plugin(Ref<GIProbeGizmoPlugin>(memnew(GIProbeGizmoPlugin))); + add_gizmo_plugin(Ref<BakedIndirectLightGizmoPlugin>(memnew(BakedIndirectLightGizmoPlugin))); + add_gizmo_plugin(Ref<CollisionShapeSpatialGizmoPlugin>(memnew(CollisionShapeSpatialGizmoPlugin))); + add_gizmo_plugin(Ref<CollisionPolygonSpatialGizmoPlugin>(memnew(CollisionPolygonSpatialGizmoPlugin))); + add_gizmo_plugin(Ref<NavigationMeshSpatialGizmoPlugin>(memnew(NavigationMeshSpatialGizmoPlugin))); + add_gizmo_plugin(Ref<JointSpatialGizmoPlugin>(memnew(JointSpatialGizmoPlugin))); + add_gizmo_plugin(Ref<PhysicalBoneSpatialGizmoPlugin>(memnew(PhysicalBoneSpatialGizmoPlugin))); } void SpatialEditor::_bind_methods() { @@ -5311,6 +5449,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() { @@ -5424,6 +5563,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); @@ -5458,7 +5609,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { ED_SHORTCUT("spatial_editor/front_view", TTR("Front View"), KEY_KP_1); ED_SHORTCUT("spatial_editor/left_view", TTR("Left View"), KEY_MASK_ALT + KEY_KP_3); ED_SHORTCUT("spatial_editor/right_view", TTR("Right View"), KEY_KP_3); - ED_SHORTCUT("spatial_editor/switch_perspective_orthogonal", TTR("Switch Perspective/Orthogonal view"), KEY_KP_5); + ED_SHORTCUT("spatial_editor/switch_perspective_orthogonal", TTR("Switch Perspective/Orthogonal View"), KEY_KP_5); ED_SHORTCUT("spatial_editor/insert_anim_key", TTR("Insert Animation Key"), KEY_K); ED_SHORTCUT("spatial_editor/focus_origin", TTR("Focus Origin"), KEY_O); ED_SHORTCUT("spatial_editor/focus_selection", TTR("Focus Selection"), KEY_F); @@ -5475,10 +5626,11 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { transform_menu = memnew(MenuButton); transform_menu->set_text(TTR("Transform")); + transform_menu->set_switch_on_hover(true); hbc_menu->add_child(transform_menu); p = transform_menu->get_popup(); - p->add_shortcut(ED_SHORTCUT("spatial_editor/snap_to_floor", TTR("Snap object to floor"), KEY_PAGEDOWN), MENU_SNAP_TO_FLOOR); + p->add_shortcut(ED_SHORTCUT("spatial_editor/snap_to_floor", TTR("Snap Object to Floor"), KEY_PAGEDOWN), MENU_SNAP_TO_FLOOR); p->add_shortcut(ED_SHORTCUT("spatial_editor/configure_snap", TTR("Configure Snap...")), MENU_TRANSFORM_CONFIGURE_SNAP); p->add_separator(); p->add_shortcut(ED_SHORTCUT("spatial_editor/transform_dialog", TTR("Transform Dialog...")), MENU_TRANSFORM_DIALOG); @@ -5487,6 +5639,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { view_menu = memnew(MenuButton); view_menu->set_text(TTR("View")); + view_menu->set_switch_on_hover(true); hbc_menu->add_child(view_menu); p = view_menu->get_popup(); @@ -5556,11 +5709,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 */ @@ -5718,6 +5871,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); @@ -5728,8 +5914,40 @@ void SpatialEditorPlugin::snap_cursor_to_plane(const Plane &p_plane) { spatial_editor->snap_cursor_to_plane(p_plane); } -void SpatialEditor::register_gizmo_plugin(Ref<EditorSpatialGizmoPlugin> ref) { - gizmo_plugins.push_back(ref); +struct _GizmoPluginPriorityComparator { + + bool operator()(const Ref<EditorSpatialGizmoPlugin> &p_a, const Ref<EditorSpatialGizmoPlugin> &p_b) const { + if (p_a->get_priority() == p_b->get_priority()) { + return p_a->get_name() < p_b->get_name(); + } + return p_a->get_priority() > p_b->get_priority(); + } +}; + +struct _GizmoPluginNameComparator { + + bool operator()(const Ref<EditorSpatialGizmoPlugin> &p_a, const Ref<EditorSpatialGizmoPlugin> &p_b) const { + return p_a->get_name() < p_b->get_name(); + } +}; + +void SpatialEditor::add_gizmo_plugin(Ref<EditorSpatialGizmoPlugin> p_plugin) { + ERR_FAIL_NULL(p_plugin.ptr()); + + gizmo_plugins_by_priority.push_back(p_plugin); + gizmo_plugins_by_priority.sort_custom<_GizmoPluginPriorityComparator>(); + + gizmo_plugins_by_name.push_back(p_plugin); + gizmo_plugins_by_name.sort_custom<_GizmoPluginNameComparator>(); + + _update_gizmos_menu(); + SpatialEditor::get_singleton()->update_all_gizmos(); +} + +void SpatialEditor::remove_gizmo_plugin(Ref<EditorSpatialGizmoPlugin> p_plugin) { + gizmo_plugins_by_priority.erase(p_plugin); + gizmo_plugins_by_name.erase(p_plugin); + _update_gizmos_menu(); } SpatialEditorPlugin::SpatialEditorPlugin(EditorNode *p_node) { @@ -5740,7 +5958,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() { @@ -5855,11 +6073,11 @@ void EditorSpatialGizmoPlugin::add_material(const String &p_name, Ref<SpatialMat materials[p_name].push_back(p_material); } -Ref<SpatialMaterial> EditorSpatialGizmoPlugin::get_material(const String &p_name, EditorSpatialGizmo *p_gizmo) { +Ref<SpatialMaterial> EditorSpatialGizmoPlugin::get_material(const String &p_name, const Ref<EditorSpatialGizmo> &p_gizmo) { ERR_FAIL_COND_V(!materials.has(p_name), Ref<SpatialMaterial>()); ERR_FAIL_COND_V(materials[p_name].size() == 0, Ref<SpatialMaterial>()); - if (p_gizmo == NULL) return materials[p_name][0]; + if (p_gizmo.is_null() || materials[p_name].size() == 1) return materials[p_name][0]; int index = (p_gizmo->is_selected() ? 1 : 0) + (p_gizmo->is_editable() ? 2 : 0); @@ -5874,8 +6092,26 @@ Ref<SpatialMaterial> EditorSpatialGizmoPlugin::get_material(const String &p_name return mat; } +String EditorSpatialGizmoPlugin::get_name() const { + if (get_script_instance() && get_script_instance()->has_method("get_name")) { + return get_script_instance()->call("get_name"); + } + return TTR("Nameless gizmo"); +} + +int EditorSpatialGizmoPlugin::get_priority() const { + if (get_script_instance() && get_script_instance()->has_method("get_priority")) { + return get_script_instance()->call("get_priority"); + } + return 0; +} + Ref<EditorSpatialGizmo> EditorSpatialGizmoPlugin::get_gizmo(Spatial *p_spatial) { + if (get_script_instance() && get_script_instance()->has_method("get_gizmo")) { + return get_script_instance()->call("get_gizmo", p_spatial); + } + Ref<EditorSpatialGizmo> ref = create_gizmo(p_spatial); if (ref.is_null()) return ref; @@ -5888,22 +6124,110 @@ Ref<EditorSpatialGizmo> EditorSpatialGizmoPlugin::get_gizmo(Spatial *p_spatial) return ref; } +void EditorSpatialGizmoPlugin::_bind_methods() { +#define GIZMO_REF PropertyInfo(Variant::OBJECT, "gizmo", PROPERTY_HINT_RESOURCE_TYPE, "EditorSpatialGizmo") + + BIND_VMETHOD(MethodInfo(Variant::BOOL, "has_gizmo", PropertyInfo(Variant::OBJECT, "spatial", PROPERTY_HINT_RESOURCE_TYPE, "Spatial"))); + BIND_VMETHOD(MethodInfo(GIZMO_REF, "create_gizmo", PropertyInfo(Variant::OBJECT, "spatial", PROPERTY_HINT_RESOURCE_TYPE, "Spatial"))); + + ClassDB::bind_method(D_METHOD("create_material", "name", "color", "billboard", "on_top", "use_vertex_color"), &EditorSpatialGizmoPlugin::create_material, DEFVAL(false), DEFVAL(false), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("create_icon_material", "name", "texture", "on_top", "color"), &EditorSpatialGizmoPlugin::create_icon_material, DEFVAL(false), DEFVAL(Color(1, 1, 1, 1))); + ClassDB::bind_method(D_METHOD("create_handle_material", "name", "billboard"), &EditorSpatialGizmoPlugin::create_handle_material, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("add_material", "name", "material"), &EditorSpatialGizmoPlugin::add_material); + + ClassDB::bind_method(D_METHOD("get_material", "name", "gizmo"), &EditorSpatialGizmoPlugin::get_material); //, DEFVAL(Ref<EditorSpatialGizmo>())); + + BIND_VMETHOD(MethodInfo(Variant::STRING, "get_name")); + BIND_VMETHOD(MethodInfo(Variant::STRING, "get_priority")); + BIND_VMETHOD(MethodInfo(Variant::BOOL, "can_be_hidden")); + BIND_VMETHOD(MethodInfo(Variant::BOOL, "is_selectable_when_hidden")); + + BIND_VMETHOD(MethodInfo("redraw", GIZMO_REF)); + BIND_VMETHOD(MethodInfo(Variant::STRING, "get_handle_name", GIZMO_REF, PropertyInfo(Variant::INT, "index"))); + + MethodInfo hvget(Variant::NIL, "get_handle_value", GIZMO_REF, PropertyInfo(Variant::INT, "index")); + hvget.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + BIND_VMETHOD(hvget); + + BIND_VMETHOD(MethodInfo("set_handle", GIZMO_REF, PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera"), PropertyInfo(Variant::VECTOR2, "point"))); + MethodInfo cm = MethodInfo("commit_handle", GIZMO_REF, PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::NIL, "restore"), PropertyInfo(Variant::BOOL, "cancel")); + cm.default_arguments.push_back(false); + BIND_VMETHOD(cm); + + BIND_VMETHOD(MethodInfo(Variant::BOOL, "is_handle_highlighted", GIZMO_REF, PropertyInfo(Variant::INT, "index"))); + +#undef GIZMO_REF +} + bool EditorSpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) { + if (get_script_instance() && get_script_instance()->has_method("has_gizmo")) { + return get_script_instance()->call("has_gizmo", p_spatial); + } return false; } Ref<EditorSpatialGizmo> EditorSpatialGizmoPlugin::create_gizmo(Spatial *p_spatial) { + if (get_script_instance() && get_script_instance()->has_method("create_gizmo")) { + return get_script_instance()->call("create_gizmo", p_spatial); + } + Ref<EditorSpatialGizmo> ref; if (has_gizmo(p_spatial)) ref.instance(); return ref; } bool EditorSpatialGizmoPlugin::can_be_hidden() const { + if (get_script_instance() && get_script_instance()->has_method("can_be_hidden")) { + return get_script_instance()->call("can_be_hidden"); + } return true; } bool EditorSpatialGizmoPlugin::is_selectable_when_hidden() const { + if (get_script_instance() && get_script_instance()->has_method("is_selectable_when_hidden")) { + return get_script_instance()->call("is_selectable_when_hidden"); + } + return false; +} + +void EditorSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { + if (get_script_instance() && get_script_instance()->has_method("redraw")) { + Ref<EditorSpatialGizmo> ref(p_gizmo); + get_script_instance()->call("redraw", ref); + } +} + +String EditorSpatialGizmoPlugin::get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const { + if (get_script_instance() && get_script_instance()->has_method("get_handle_name")) { + return get_script_instance()->call("get_handle_name", p_gizmo, p_idx); + } + return ""; +} + +Variant EditorSpatialGizmoPlugin::get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const { + if (get_script_instance() && get_script_instance()->has_method("get_handle_value")) { + return get_script_instance()->call("get_handle_value", p_gizmo, p_idx); + } + return Variant(); +} + +void EditorSpatialGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point) { + if (get_script_instance() && get_script_instance()->has_method("set_handle")) { + get_script_instance()->call("set_handle", p_gizmo, p_idx, p_camera, p_point); + } +} + +void EditorSpatialGizmoPlugin::commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { + if (get_script_instance() && get_script_instance()->has_method("commit_handle")) { + get_script_instance()->call("commit_handle", p_gizmo, p_idx, p_restore, p_cancel); + } +} + +bool EditorSpatialGizmoPlugin::is_handle_highlighted(const EditorSpatialGizmo *p_gizmo, int p_idx) const { + if (get_script_instance() && get_script_instance()->has_method("is_handle_highlighted")) { + return get_script_instance()->call("is_handle_highlighted", p_gizmo, p_idx); + } return false; } @@ -5914,6 +6238,10 @@ void EditorSpatialGizmoPlugin::set_state(int p_state) { } } +int EditorSpatialGizmoPlugin::get_state() const { + return current_state; +} + void EditorSpatialGizmoPlugin::unregister_gizmo(EditorSpatialGizmo *p_gizmo) { current_gizmos.erase(p_gizmo); } @@ -5923,4 +6251,9 @@ EditorSpatialGizmoPlugin::EditorSpatialGizmoPlugin() { } EditorSpatialGizmoPlugin::~EditorSpatialGizmoPlugin() { + for (int i = 0; i < current_gizmos.size(); ++i) { + current_gizmos[i]->set_plugin(NULL); + current_gizmos[i]->get_spatial_node()->set_gizmo(NULL); + } + SpatialEditor::get_singleton()->update_all_gizmos(); } diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h index c515a4aaf9..f3a1e657cc 100644 --- a/editor/plugins/spatial_editor_plugin.h +++ b/editor/plugins/spatial_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -60,6 +60,7 @@ public: RID instance; Ref<ArrayMesh> mesh; + Ref<Material> material; RID skeleton; bool billboard; bool unscaled; @@ -103,13 +104,14 @@ protected: public: void add_lines(const Vector<Vector3> &p_lines, const Ref<Material> &p_material, bool p_billboard = false); - void add_mesh(const Ref<ArrayMesh> &p_mesh, bool p_billboard = false, const RID &p_skeleton = RID()); + void add_mesh(const Ref<ArrayMesh> &p_mesh, bool p_billboard = false, const RID &p_skeleton = RID(), const Ref<Material> &p_material = Ref<Material>()); void add_collision_segments(const Vector<Vector3> &p_lines); void add_collision_triangles(const Ref<TriangleMesh> &p_tmesh); void add_unscaled_billboard(const Ref<Material> &p_material, float p_scale = 1); void add_handles(const Vector<Vector3> &p_handles, const Ref<Material> &p_material, bool p_billboard = false, bool p_secondary = false); void add_solid_box(Ref<Material> &p_material, Vector3 p_size, Vector3 p_position = Vector3()); + virtual bool is_handle_highlighted(int p_idx) const; virtual String get_handle_name(int p_idx) const; virtual Variant get_handle_value(int p_idx); virtual void set_handle(int p_idx, Camera *p_camera, const Point2 &p_point); @@ -117,6 +119,7 @@ public: void set_spatial_node(Spatial *p_node); Spatial *get_spatial_node() const { return spatial_node; } + EditorSpatialGizmoPlugin *get_plugin() const { return gizmo_plugin; } Vector3 get_handle_pos(int p_idx) const; bool intersect_frustum(const Camera *p_camera, const Vector<Plane> &p_frustum); bool intersect_ray(Camera *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal, int *r_gizmo_handle = NULL, bool p_sec_first = false); @@ -482,6 +485,8 @@ public: TOOL_MODE_LIST_SELECT, TOOL_LOCK_SELECTED, TOOL_UNLOCK_SELECTED, + TOOL_GROUP_SELECTED, + TOOL_UNGROUP_SELECTED, TOOL_MAX }; @@ -567,6 +572,8 @@ private: MENU_VIEW_CAMERA_SETTINGS, MENU_LOCK_SELECTED, MENU_UNLOCK_SELECTED, + MENU_GROUP_SELECTED, + MENU_UNGROUP_SELECTED, MENU_SNAP_TO_FLOOR }; @@ -615,7 +622,8 @@ private: void _instance_scene(); void _init_indicators(); - void _init_gizmos_menu(); + void _update_gizmos_menu(); + void _update_gizmos_menu_theme(); void _init_grid(); void _finish_indicators(); void _finish_grid(); @@ -635,7 +643,8 @@ private: static SpatialEditor *singleton; void _node_removed(Node *p_node); - Vector<Ref<EditorSpatialGizmoPlugin> > gizmo_plugins; + Vector<Ref<EditorSpatialGizmoPlugin> > gizmo_plugins_by_priority; + Vector<Ref<EditorSpatialGizmoPlugin> > gizmo_plugins_by_name; void _register_all_gizmos(); @@ -668,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]; } @@ -710,7 +719,8 @@ public: return viewports[p_idx]; } - void register_gizmo_plugin(Ref<EditorSpatialGizmoPlugin> ref); + void add_gizmo_plugin(Ref<EditorSpatialGizmoPlugin> p_plugin); + void remove_gizmo_plugin(Ref<EditorSpatialGizmoPlugin> p_plugin); void edit(Spatial *p_spatial); void clear(); @@ -764,6 +774,7 @@ private: HashMap<String, Vector<Ref<SpatialMaterial> > > materials; protected: + static void _bind_methods(); virtual bool has_gizmo(Spatial *p_spatial); virtual Ref<EditorSpatialGizmo> create_gizmo(Spatial *p_spatial); @@ -773,21 +784,23 @@ public: void create_handle_material(const String &p_name, bool p_billboard = false); void add_material(const String &p_name, Ref<SpatialMaterial> p_material); - Ref<SpatialMaterial> get_material(const String &p_name, EditorSpatialGizmo *p_gizmo = NULL); + Ref<SpatialMaterial> get_material(const String &p_name, const Ref<EditorSpatialGizmo> &p_gizmo = Ref<EditorSpatialGizmo>()); - virtual String get_name() const = 0; + virtual String get_name() const; + virtual int get_priority() const; virtual bool can_be_hidden() const; virtual bool is_selectable_when_hidden() const; - virtual void redraw(EditorSpatialGizmo *p_gizmo) {} - virtual String get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const { return ""; } - virtual Variant get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const { return Variant(); } - virtual void set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point) {} - virtual void commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false) {} - virtual bool is_gizmo_handle_highlighted(const EditorSpatialGizmo *p_gizmo, int idx) const { return false; } + virtual void redraw(EditorSpatialGizmo *p_gizmo); + virtual String get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const; + virtual Variant get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const; + virtual void set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point); + virtual void commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); + virtual bool is_handle_highlighted(const EditorSpatialGizmo *p_gizmo, int p_idx) const; Ref<EditorSpatialGizmo> get_gizmo(Spatial *p_spatial); void set_state(int p_state); + int get_state() const; void unregister_gizmo(EditorSpatialGizmo *p_gizmo); EditorSpatialGizmoPlugin(); diff --git a/editor/plugins/sprite_editor_plugin.cpp b/editor/plugins/sprite_editor_plugin.cpp index c574b5e8ba..2deb2090e2 100644 --- a/editor/plugins/sprite_editor_plugin.cpp +++ b/editor/plugins/sprite_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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" @@ -91,6 +94,8 @@ Vector<Vector2> expand(const Vector<Vector2> &points, const Rect2i &rect, float Vector<Vector2> outPoints; ClipperLib::PolyNode *p2 = out.GetFirst(); + ERR_FAIL_COND_V(!p2, points); + while (p2->IsHole()) { p2 = p2->GetNext(); } @@ -114,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(); @@ -167,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(); - 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); - for (int i = 0; i < lines[j].size(); i++) { - Vector2 vtx = lines[j][i]; - computed_uv.push_back(vtx / img_size); + vtx -= rect.position; //offset by rect position - 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; - //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; - if (node->is_centered()) - vtx -= rect.size / 2.0; + computed_vertices.push_back(vtx); + } + + Vector<int> poly = Geometry::triangulate_polygon(lines[j]); - computed_vertices.push_back(vtx); + 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_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; - computed_indices.push_back(poly[idx] + index_ofs); + if (node->is_centered()) + vtx -= rect.size / 2.0; + + 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.")); @@ -228,7 +327,141 @@ void SpriteEditor::_create_mesh_node() { MeshInstance2D *mesh_instance = memnew(MeshInstance2D); mesh_instance->set_mesh(mesh); - EditorNode::get_singleton()->get_scene_tree_dock()->replace_node(node, mesh_instance); + + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Convert to Mesh2D")); + ur->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", node, mesh_instance, true, false); + ur->add_do_reference(mesh_instance); + ur->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", mesh_instance, node, false, false); + ur->add_undo_reference(node); + ur->commit_action(); +} + +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); + + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Convert to Polygon2D")); + ur->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", node, polygon_2d_instance, true, false); + ur->add_do_reference(polygon_2d_instance); + ur->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", polygon_2d_instance, node, false, false); + ur->add_undo_reference(node); + ur->commit_action(); +} + +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); + + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Create CollisionPolygon2D Sibling")); + ur->add_do_method(this, "_add_as_sibling_or_child", node, collision_polygon_2d_instance); + ur->add_do_reference(collision_polygon_2d_instance); + ur->add_undo_method(node != this->get_tree()->get_edited_scene_root() ? node->get_parent() : this->get_tree()->get_edited_scene_root(), "remove_child", collision_polygon_2d_instance); + ur->commit_action(); + } +} + +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); + + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Create LightOccluder2D Sibling")); + ur->add_do_method(this, "_add_as_sibling_or_child", node, light_occluder_2d_instance); + ur->add_do_reference(light_occluder_2d_instance); + ur->add_undo_method(node != this->get_tree()->get_edited_scene_root() ? node->get_parent() : this->get_tree()->get_edited_scene_root(), "remove_child", light_occluder_2d_instance); + ur->commit_action(); + } +} + +void SpriteEditor::_add_as_sibling_or_child(Node *p_own_node, Node *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); + Object::cast_to<Node2D>(p_new_node)->set_transform(Object::cast_to<Node2D>(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 @@ -296,16 +529,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() { @@ -313,7 +556,8 @@ 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); + ClassDB::bind_method("_add_as_sibling_or_child", &SpriteEditor::_add_as_sibling_or_child); } SpriteEditor::SpriteEditor() { @@ -325,7 +569,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"); @@ -333,7 +581,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); @@ -345,7 +593,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 238227e4a0..81be4a19e9 100644 --- a/editor/plugins/sprite_editor_plugin.h +++ b/editor/plugins/sprite_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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(Node *p_own_node, Node *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 82936d63d2..c509202a88 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -38,30 +38,207 @@ 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(); + int width = size.width / h; + int height = size.height / v; + const float a = 0.3; + for (int i = 1; i < h; i++) { + + int x = i * width; + 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)); + + for (int j = 1; j < v; j++) { + + int y = j * height; + + 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)); + } + } + + if (frames_selected.size() == 0) { + split_sheet_dialog->get_ok()->set_disabled(true); + split_sheet_dialog->get_ok()->set_text(TTR("No Frames Selected")); + return; + } + + Color accent = get_color("accent_color", "Editor"); + + for (Set<int>::Element *E = frames_selected.front(); E; E = E->next()) { + int idx = E->get(); + int xp = idx % h; + int yp = (idx - xp) / h; + int x = xp * width; + int y = yp * height; + + 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); + } + + 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(); + } +} - if (p_what == NOTIFICATION_PHYSICS_PROCESS) { +void SpriteFramesEditor::_sheet_add_frames() { + + Size2i size = split_sheet_preview->get_size(); + int h = split_sheet_h->get_value(); + int v = split_sheet_v->get_value(); + + undo_redo->create_action(TTR("Add Frame")); + + int fc = frames->get_frame_count(edited_anim); + + for (Set<int>::Element *E = frames_selected.front(); E; E = E->next()) { + int idx = E->get(); + int width = size.width / h; + int height = size.height / v; + int xp = idx % h; + int yp = (idx - xp) / h; + int x = xp * width; + int y = yp * height; + + Ref<AtlasTexture> at; + at.instance(); + at->set_atlas(split_sheet_preview->get_texture()); + at->set_region(Rect2(x, y, width, height)); + + undo_redo->add_do_method(frames, "add_frame", edited_anim, at, -1); + undo_redo->add_undo_method(frames, "remove_frame", edited_anim, fc); } - if (p_what == NOTIFICATION_ENTER_TREE) { - 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")); + 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(); +} - if (p_what == NOTIFICATION_READY) { +void SpriteFramesEditor::_prepare_sprite_sheet(const String &p_file) { - //NodePath("/root")->connect("node_removed", this,"_node_removed",Vector<Variant>(),true); + Ref<Resource> texture = ResourceLoader::load(p_file); + if (!texture.is_valid()) { + EditorNode::get_singleton()->show_warning("Unable to load images"); + ERR_FAIL_COND(!texture.is_valid()); } + if (texture != split_sheet_preview->get_texture()) { + //different texture, reset to 4x4 + split_sheet_h->set_value(4); + split_sheet_v->set_value(4); + } + frames_selected.clear(); + last_frame_selected = -1; + + split_sheet_preview->set_texture(texture); + split_sheet_dialog->popup_centered_ratio(0.65); +} + +void SpriteFramesEditor::_notification(int p_what) { - if (p_what == NOTIFICATION_DRAW) { + 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; } } @@ -79,6 +256,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(); @@ -285,6 +463,10 @@ void SpriteFramesEditor::_animation_select() { if (updating) return; + double value = anim_speed->get_line_edit()->get_text().to_double(); + if (!Math::is_equal_approx(value, frames->get_animation_speed(edited_anim))) + _animation_fps_changed(value); + TreeItem *selected = animations->get_selected(); ERR_FAIL_COND(!selected); edited_anim = selected->get_text(0); @@ -354,9 +536,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); } @@ -389,9 +568,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); } @@ -649,7 +825,6 @@ void SpriteFramesEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da void SpriteFramesEditor::_bind_methods() { - ClassDB::bind_method(D_METHOD("_gui_input"), &SpriteFramesEditor::_gui_input); ClassDB::bind_method(D_METHOD("_load_pressed"), &SpriteFramesEditor::_load_pressed); ClassDB::bind_method(D_METHOD("_empty_pressed"), &SpriteFramesEditor::_empty_pressed); ClassDB::bind_method(D_METHOD("_empty2_pressed"), &SpriteFramesEditor::_empty2_pressed); @@ -669,35 +844,36 @@ 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() { - //add_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_stylebox("panel","Panel")); - - split = memnew(HSplitContainer); - add_child(split); - VBoxContainer *vbc_animlist = memnew(VBoxContainer); - split->add_child(vbc_animlist); + add_child(vbc_animlist); vbc_animlist->set_custom_minimum_size(Size2(150, 0) * EDSCALE); - //vbc_animlist->set_v_size_flags(SIZE_EXPAND_FILL); VBoxContainer *sub_vb = memnew(VBoxContainer); - vbc_animlist->add_margin_child(TTR("Animations"), sub_vb, true); + vbc_animlist->add_margin_child(TTR("Animations:"), sub_vb, true); sub_vb->set_v_size_flags(SIZE_EXPAND_FILL); HBoxContainer *hbc_animlist = memnew(HBoxContainer); sub_vb->add_child(hbc_animlist); - new_anim = memnew(Button); - new_anim->set_flat(true); + new_anim = memnew(ToolButton); + new_anim->set_tooltip(TTR("New Animation")); hbc_animlist->add_child(new_anim); new_anim->set_h_size_flags(SIZE_EXPAND_FILL); new_anim->connect("pressed", this, "_animation_add"); - remove_anim = memnew(Button); - remove_anim->set_flat(true); + remove_anim = memnew(ToolButton); + remove_anim->set_tooltip(TTR("Remove Animation")); hbc_animlist->add_child(remove_anim); remove_anim->connect("pressed", this, "_animation_remove"); @@ -722,56 +898,55 @@ SpriteFramesEditor::SpriteFramesEditor() { anim_loop->connect("pressed", this, "_animation_loop_changed"); VBoxContainer *vbc = memnew(VBoxContainer); - split->add_child(vbc); + add_child(vbc); vbc->set_h_size_flags(SIZE_EXPAND_FILL); sub_vb = memnew(VBoxContainer); - vbc->add_margin_child(TTR("Animation Frames"), sub_vb, true); + vbc->add_margin_child(TTR("Animation Frames:"), sub_vb, true); HBoxContainer *hbc = memnew(HBoxContainer); sub_vb->add_child(hbc); - //animations = memnew( ItemList ); - - load = memnew(Button); - load->set_flat(true); - load->set_tooltip(TTR("Load Resource")); + load = memnew(ToolButton); + load->set_tooltip(TTR("Add a Texture from File")); hbc->add_child(load); - copy = memnew(Button); - copy->set_flat(true); + 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); - paste = memnew(Button); - paste->set_flat(true); + paste = memnew(ToolButton); paste->set_tooltip(TTR("Paste")); hbc->add_child(paste); - empty = memnew(Button); - empty->set_flat(true); + hbc->add_spacer(false); + + empty = memnew(ToolButton); empty->set_tooltip(TTR("Insert Empty (Before)")); hbc->add_child(empty); - empty2 = memnew(Button); - empty2->set_flat(true); + empty2 = memnew(ToolButton); empty2->set_tooltip(TTR("Insert Empty (After)")); hbc->add_child(empty2); - hbc->add_spacer(false); + hbc->add_child(memnew(VSeparator)); - move_up = memnew(Button); - move_up->set_flat(true); + move_up = memnew(ToolButton); move_up->set_tooltip(TTR("Move (Before)")); hbc->add_child(move_up); - move_down = memnew(Button); - move_down->set_flat(true); + move_down = memnew(ToolButton); move_down->set_tooltip(TTR("Move (After)")); hbc->add_child(move_down); - _delete = memnew(Button); - _delete->set_flat(true); + _delete = memnew(ToolButton); + _delete->set_tooltip(TTR("Delete")); hbc->add_child(_delete); file = memnew(EditorFileDialog); @@ -787,7 +962,6 @@ SpriteFramesEditor::SpriteFramesEditor() { tree->set_fixed_column_width(thumbnail_size * 3 / 2); tree->set_max_text_lines(2); tree->set_fixed_icon_size(Size2(thumbnail_size, thumbnail_size)); - //tree->set_min_icon_size(Size2(thumbnail_size,thumbnail_size)); tree->set_drag_forwarding(this); sub_vb->add_child(tree); @@ -796,6 +970,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"); @@ -810,6 +985,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 1f45b10323..d64431cde7 100644 --- a/editor/plugins/sprite_frames_editor_plugin.h +++ b/editor/plugins/sprite_frames_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -37,27 +37,29 @@ #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 PanelContainer { +class SpriteFramesEditor : public HSplitContainer { - GDCLASS(SpriteFramesEditor, PanelContainer); + GDCLASS(SpriteFramesEditor, HSplitContainer); - Button *load; - Button *_delete; - Button *copy; - Button *paste; - Button *empty; - Button *empty2; - Button *move_up; - Button *move_down; + ToolButton *load; + ToolButton *load_sheet; + ToolButton *_delete; + ToolButton *copy; + ToolButton *paste; + ToolButton *empty; + ToolButton *empty2; + ToolButton *move_up; + ToolButton *move_down; ItemList *tree; bool loading_scene; int sel; HSplitContainer *split; - Button *new_anim; - Button *remove_anim; + ToolButton *new_anim; + ToolButton *remove_anim; Tree *animations; SpinBox *anim_speed; @@ -71,6 +73,15 @@ class SpriteFramesEditor : public PanelContainer { 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 PanelContainer { 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/style_box_editor_plugin.cpp b/editor/plugins/style_box_editor_plugin.cpp index f6d98cb4c7..defc0a40ea 100644 --- a/editor/plugins/style_box_editor_plugin.cpp +++ b/editor/plugins/style_box_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/style_box_editor_plugin.h b/editor/plugins/style_box_editor_plugin.h index 6b0d7e57a8..95d21b2c44 100644 --- a/editor/plugins/style_box_editor_plugin.h +++ b/editor/plugins/style_box_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp index 4a8eae1ba4..a0f3c253d1 100644 --- a/editor/plugins/text_editor.cpp +++ b/editor/plugins/text_editor.cpp @@ -1,12 +1,12 @@ /*************************************************************************/ -/* text_editor.cpp */ +/* text_editor.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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() { @@ -94,7 +93,9 @@ void TextEditor::_load_theme_settings() { Color function_color = EDITOR_GET("text_editor/highlighting/function_color"); Color member_variable_color = EDITOR_GET("text_editor/highlighting/member_variable_color"); Color mark_color = EDITOR_GET("text_editor/highlighting/mark_color"); + Color bookmark_color = EDITOR_GET("text_editor/highlighting/bookmark_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,7 +126,9 @@ 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("bookmark_color", bookmark_color); text_edit->add_color_override("code_folding_color", code_folding_color); text_edit->add_color_override("search_result_color", search_result_color); text_edit->add_color_override("search_result_border_color", search_result_border_color); @@ -201,7 +204,6 @@ void TextEditor::reload_text() { int v = te->get_v_scroll(); te->set_text(text_file->get_text()); - te->clear_undo_history(); te->cursor_set_line(row); te->cursor_set_column(column); te->set_h_scroll(h); @@ -234,6 +236,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 +271,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 +318,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; } } @@ -421,6 +439,22 @@ void TextEditor::_edit_option(int p_op) { goto_line_dialog->popup_find_line(tx); } break; + case BOOKMARK_TOGGLE: { + + code_editor->toggle_bookmark(); + } break; + case BOOKMARK_GOTO_NEXT: { + + code_editor->goto_next_bookmark(); + } break; + case BOOKMARK_GOTO_PREV: { + + code_editor->goto_prev_bookmark(); + } break; + case BOOKMARK_REMOVE_ALL: { + + code_editor->remove_all_bookmarks(); + } break; } } @@ -603,5 +637,15 @@ TextEditor::TextEditor() { highlighter_menu->add_radio_check_item(TTR("Standard")); highlighter_menu->connect("id_pressed", this, "_change_syntax_highlighter"); + PopupMenu *bookmarks = memnew(PopupMenu); + bookmarks->set_name("bookmarks"); + edit_menu->get_popup()->add_child(bookmarks); + edit_menu->get_popup()->add_submenu_item(TTR("Bookmarks"), "bookmarks"); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_bookmark"), BOOKMARK_TOGGLE); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/remove_all_bookmarks"), BOOKMARK_REMOVE_ALL); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_next_bookmark"), BOOKMARK_GOTO_NEXT); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_previous_bookmark"), BOOKMARK_GOTO_PREV); + bookmarks->connect("id_pressed", this, "_edit_option"); + code_editor->get_text_edit()->set_drag_forwarding(this); } diff --git a/editor/plugins/text_editor.h b/editor/plugins/text_editor.h index 8b1983d891..2da7474793 100644 --- a/editor/plugins/text_editor.h +++ b/editor/plugins/text_editor.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -87,6 +87,10 @@ private: SEARCH_FIND_PREV, SEARCH_REPLACE, SEARCH_GOTO_LINE, + BOOKMARK_TOGGLE, + BOOKMARK_GOTO_NEXT, + BOOKMARK_GOTO_PREV, + BOOKMARK_REMOVE_ALL, }; protected: @@ -123,6 +127,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_editor_plugin.cpp b/editor/plugins/texture_editor_plugin.cpp index 140d37fdb5..0aa4a7662c 100644 --- a/editor/plugins/texture_editor_plugin.cpp +++ b/editor/plugins/texture_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -138,39 +138,33 @@ TextureEditor::TextureEditor() { set_custom_minimum_size(Size2(1, 150)); } -void TextureEditorPlugin::edit(Object *p_object) { - - Texture *s = Object::cast_to<Texture>(p_object); - if (!s) - return; - - texture_editor->edit(Ref<Texture>(s)); +TextureEditor::~TextureEditor() { + if (!texture.is_null()) { + texture->remove_change_receptor(this); + } } +// +bool EditorInspectorPluginTexture::can_handle(Object *p_object) { -bool TextureEditorPlugin::handles(Object *p_object) const { - - return p_object->is_class("Texture"); + return Object::cast_to<ImageTexture>(p_object) != NULL || Object::cast_to<AtlasTexture>(p_object) != NULL || Object::cast_to<StreamTexture>(p_object) != NULL || Object::cast_to<LargeTexture>(p_object) != NULL || Object::cast_to<AnimatedTexture>(p_object) != NULL; } -void TextureEditorPlugin::make_visible(bool p_visible) { +void EditorInspectorPluginTexture::parse_begin(Object *p_object) { - if (p_visible) { - texture_editor->show(); - //texture_editor->set_process(true); - } else { - - texture_editor->hide(); - //texture_editor->set_process(false); + Texture *texture = Object::cast_to<Texture>(p_object); + if (!texture) { + return; } + Ref<Texture> m(texture); + + TextureEditor *editor = memnew(TextureEditor); + editor->edit(m); + add_custom_control(editor); } TextureEditorPlugin::TextureEditorPlugin(EditorNode *p_node) { - editor = p_node; - texture_editor = memnew(TextureEditor); - add_control_to_container(CONTAINER_PROPERTY_EDITOR_BOTTOM, texture_editor); - texture_editor->hide(); -} - -TextureEditorPlugin::~TextureEditorPlugin() { + Ref<EditorInspectorPluginTexture> plugin; + plugin.instance(); + add_inspector_plugin(plugin); } diff --git a/editor/plugins/texture_editor_plugin.h b/editor/plugins/texture_editor_plugin.h index 4ca2bc641e..bcbda1fbd7 100644 --- a/editor/plugins/texture_editor_plugin.h +++ b/editor/plugins/texture_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -50,24 +50,24 @@ protected: public: void edit(Ref<Texture> p_texture); TextureEditor(); + ~TextureEditor(); +}; + +class EditorInspectorPluginTexture : public EditorInspectorPlugin { + GDCLASS(EditorInspectorPluginTexture, EditorInspectorPlugin) +public: + virtual bool can_handle(Object *p_object); + virtual void parse_begin(Object *p_object); }; class TextureEditorPlugin : public EditorPlugin { GDCLASS(TextureEditorPlugin, EditorPlugin); - TextureEditor *texture_editor; - EditorNode *editor; - public: virtual String get_name() const { return "Texture"; } - 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); TextureEditorPlugin(EditorNode *p_node); - ~TextureEditorPlugin(); }; #endif // TEXTURE_EDITOR_PLUGIN_H diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index aed3a7d503..4e15bd5116 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -51,6 +51,8 @@ void TextureRegionEditor::_region_draw() { Ref<Texture> base_tex = NULL; if (node_sprite) base_tex = node_sprite->get_texture(); + else if (node_sprite_3d) + base_tex = node_sprite_3d->get_texture(); else if (node_ninepatch) base_tex = node_ninepatch->get_texture(); else if (obj_styleBox.is_valid()) @@ -62,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); @@ -126,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), @@ -151,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); @@ -203,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)); @@ -218,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] = { @@ -253,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; @@ -285,6 +288,8 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { Rect2 r; if (node_sprite) r = node_sprite->get_region_rect(); + else if (node_sprite_3d) + r = node_sprite_3d->get_region_rect(); else if (node_ninepatch) r = node_ninepatch->get_region_rect(); else if (obj_styleBox.is_valid()) @@ -298,6 +303,9 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { if (node_sprite) { undo_redo->add_do_method(node_sprite, "set_region_rect", rect); undo_redo->add_undo_method(node_sprite, "set_region_rect", node_sprite->get_region_rect()); + } else if (node_sprite_3d) { + undo_redo->add_do_method(node_sprite_3d, "set_region_rect", rect); + undo_redo->add_undo_method(node_sprite_3d, "set_region_rect", node_sprite_3d->get_region_rect()); } else if (node_ninepatch) { undo_redo->add_do_method(node_ninepatch, "set_region_rect", rect); undo_redo->add_undo_method(node_ninepatch, "set_region_rect", node_ninepatch->get_region_rect()); @@ -325,6 +333,8 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { drag = true; if (node_sprite) rect_prev = node_sprite->get_region_rect(); + else if (node_sprite_3d) + rect_prev = node_sprite_3d->get_region_rect(); else if (node_ninepatch) rect_prev = node_ninepatch->get_region_rect(); else if (obj_styleBox.is_valid()) @@ -363,6 +373,9 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { if (node_sprite) { undo_redo->add_do_method(node_sprite, "set_region_rect", node_sprite->get_region_rect()); undo_redo->add_undo_method(node_sprite, "set_region_rect", rect_prev); + } else if (node_sprite_3d) { + undo_redo->add_do_method(node_sprite_3d, "set_region_rect", node_sprite_3d->get_region_rect()); + undo_redo->add_undo_method(node_sprite_3d, "set_region_rect", rect_prev); } else if (atlas_tex.is_valid()) { undo_redo->add_do_method(atlas_tex.ptr(), "set_region", atlas_tex->get_region()); undo_redo->add_undo_method(atlas_tex.ptr(), "set_region", rect_prev); @@ -403,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()); } } @@ -415,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); @@ -566,30 +579,37 @@ 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) { if (node_sprite) node_sprite->set_region_rect(p_rect); + else if (node_sprite_3d) + node_sprite_3d->set_region_rect(p_rect); else if (node_ninepatch) node_ninepatch->set_region_rect(p_rect); else if (obj_styleBox.is_valid()) @@ -601,6 +621,8 @@ void TextureRegionEditor::apply_rect(const Rect2 &p_rect) { void TextureRegionEditor::_update_rect() { if (node_sprite) rect = node_sprite->get_region_rect(); + else if (node_sprite_3d) + rect = node_sprite_3d->get_region_rect(); else if (node_ninepatch) rect = node_ninepatch->get_region_rect(); else if (obj_styleBox.is_valid()) @@ -616,6 +638,8 @@ void TextureRegionEditor::_update_autoslice() { Ref<Texture> texture = NULL; if (node_sprite) texture = node_sprite->get_texture(); + else if (node_sprite_3d) + texture = node_sprite_3d->get_texture(); else if (node_ninepatch) texture = node_ninepatch->get_texture(); else if (obj_styleBox.is_valid()) @@ -702,9 +726,10 @@ void TextureRegionEditor::_notification(int p_what) { } void TextureRegionEditor::_node_removed(Object *p_obj) { - if (p_obj == node_sprite || p_obj == node_ninepatch || p_obj == obj_styleBox.ptr() || p_obj == atlas_tex.ptr()) { - node_ninepatch = NULL; + if (p_obj == node_sprite || p_obj == node_sprite_3d || p_obj == node_ninepatch || p_obj == obj_styleBox.ptr() || p_obj == atlas_tex.ptr()) { node_sprite = NULL; + node_sprite_3d = NULL; + node_ninepatch = NULL; obj_styleBox = Ref<StyleBox>(NULL); atlas_tex = Ref<AtlasTexture>(NULL); hide(); @@ -724,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); @@ -743,6 +769,10 @@ bool TextureRegionEditor::is_ninepatch() { return node_ninepatch != NULL; } +Sprite3D *TextureRegionEditor::get_sprite_3d() { + return node_sprite_3d; +} + Sprite *TextureRegionEditor::get_sprite() { return node_sprite; } @@ -750,6 +780,8 @@ Sprite *TextureRegionEditor::get_sprite() { void TextureRegionEditor::edit(Object *p_obj) { if (node_sprite) node_sprite->remove_change_receptor(this); + if (node_sprite_3d) + node_sprite_3d->remove_change_receptor(this); if (node_ninepatch) node_ninepatch->remove_change_receptor(this); if (obj_styleBox.is_valid()) @@ -758,6 +790,7 @@ void TextureRegionEditor::edit(Object *p_obj) { atlas_tex->remove_change_receptor(this); if (p_obj) { node_sprite = Object::cast_to<Sprite>(p_obj); + node_sprite_3d = Object::cast_to<Sprite3D>(p_obj); node_ninepatch = Object::cast_to<NinePatchRect>(p_obj); if (Object::cast_to<StyleBoxTexture>(p_obj)) obj_styleBox = Ref<StyleBoxTexture>(Object::cast_to<StyleBoxTexture>(p_obj)); @@ -767,12 +800,13 @@ void TextureRegionEditor::edit(Object *p_obj) { _edit_region(); } else { node_sprite = NULL; + node_sprite_3d = NULL; node_ninepatch = NULL; obj_styleBox = Ref<StyleBoxTexture>(NULL); atlas_tex = Ref<AtlasTexture>(NULL); } edit_draw->update(); - if (node_sprite && !node_sprite->is_region()) { + if ((node_sprite && !node_sprite->is_region()) || (node_sprite_3d && !node_sprite_3d->is_region())) { set_process(true); } if (!p_obj) { @@ -792,6 +826,8 @@ void TextureRegionEditor::_edit_region() { Ref<Texture> texture = NULL; if (node_sprite) texture = node_sprite->get_texture(); + else if (node_sprite_3d) + texture = node_sprite_3d->get_texture(); else if (node_ninepatch) texture = node_ninepatch->get_texture(); else if (obj_styleBox.is_valid()) @@ -834,6 +870,7 @@ Vector2 TextureRegionEditor::snap_point(Vector2 p_target) const { TextureRegionEditor::TextureRegionEditor(EditorNode *p_editor) { node_sprite = NULL; + node_sprite_3d = NULL; node_ninepatch = NULL; obj_styleBox = Ref<StyleBoxTexture>(NULL); atlas_tex = Ref<AtlasTexture>(NULL); @@ -976,13 +1013,13 @@ void TextureRegionEditorPlugin::edit(Object *p_object) { } bool TextureRegionEditorPlugin::handles(Object *p_object) const { - return p_object->is_class("Sprite") || p_object->is_class("NinePatchRect") || p_object->is_class("StyleBoxTexture") || p_object->is_class("AtlasTexture"); + return p_object->is_class("Sprite") || p_object->is_class("Sprite3D") || p_object->is_class("NinePatchRect") || p_object->is_class("StyleBoxTexture") || p_object->is_class("AtlasTexture"); } void TextureRegionEditorPlugin::make_visible(bool p_visible) { if (p_visible) { texture_region_button->show(); - if (region_editor->is_stylebox() || region_editor->is_atlas_texture() || region_editor->is_ninepatch() || (region_editor->get_sprite() && region_editor->get_sprite()->is_region()) || texture_region_button->is_pressed()) { + if (region_editor->is_stylebox() || region_editor->is_atlas_texture() || region_editor->is_ninepatch() || (region_editor->get_sprite() && region_editor->get_sprite()->is_region()) || (region_editor->get_sprite_3d() && region_editor->get_sprite_3d()->is_region()) || texture_region_button->is_pressed()) { editor->make_bottom_panel_item_visible(region_editor); } } else { diff --git a/editor/plugins/texture_region_editor_plugin.h b/editor/plugins/texture_region_editor_plugin.h index 4f301ea916..a49e0fb96c 100644 --- a/editor/plugins/texture_region_editor_plugin.h +++ b/editor/plugins/texture_region_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -35,6 +35,7 @@ #include "editor/editor_node.h" #include "editor/editor_plugin.h" #include "scene/2d/sprite.h" +#include "scene/3d/sprite_3d.h" #include "scene/gui/nine_patch_rect.h" #include "scene/resources/style_box.h" #include "scene/resources/texture.h" @@ -83,8 +84,9 @@ class TextureRegionEditor : public VBoxContainer { Vector2 snap_step; Vector2 snap_separation; - NinePatchRect *node_ninepatch; Sprite *node_sprite; + Sprite3D *node_sprite_3d; + NinePatchRect *node_ninepatch; Ref<StyleBoxTexture> obj_styleBox; Ref<AtlasTexture> atlas_tex; @@ -108,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(); @@ -132,6 +135,7 @@ public: bool is_stylebox(); bool is_atlas_texture(); bool is_ninepatch(); + Sprite3D *get_sprite_3d(); Sprite *get_sprite(); void edit(Object *p_obj); diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index acee1a6942..5b67d259ba 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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); - - 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 *top_menu = memnew(HBoxContainer); + add_child(top_menu); - 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,27 +626,60 @@ 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_theme(Theme::get_default()); + 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_theme(Theme::get_default()); + 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); + + MarginContainer *mc = memnew(MarginContainer); + main_container->add_child(mc); + 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); @@ -671,23 +687,11 @@ ThemeEditor::ThemeEditor() { 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 +700,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 +717,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 +726,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 +777,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 +830,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 +848,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,19 +874,14 @@ 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) { if (Object::cast_to<Theme>(p_node)) { - theme_editor->show(); theme_editor->edit(Object::cast_to<Theme>(p_node)); } else { theme_editor->edit(Ref<Theme>()); - theme_editor->hide(); } } @@ -871,11 +896,11 @@ void ThemeEditorPlugin::make_visible(bool p_visible) { theme_editor->set_process(true); button->show(); editor->make_bottom_panel_item_visible(theme_editor); - } else { theme_editor->set_process(false); if (theme_editor->is_visible_in_tree()) editor->hide_bottom_panel(); + button->hide(); } } @@ -886,7 +911,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 625b35e2c1..cc236907a9 100644 --- a/editor/plugins/theme_editor_plugin.h +++ b/editor/plugins/theme_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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 1d8a80d3f3..772e47ac3a 100644 --- a/editor/plugins/tile_map_editor_plugin.cpp +++ b/editor/plugins/tile_map_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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) { - - switch (p_option) { +void TileMapEditor::_update_button_tool() { - 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 TOOL_PICKING: { + picker_button->set_pressed(true); + } break; + case TOOL_SELECTING: { + select_button->set_pressed(true); } break; - case OPTION_PICK_TILE: { + 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; + + cell["id"] = tile; + cell["flip_h"] = flip_x; + cell["flip_y"] = flip_y; + cell["transpose"] = transpose; + cell["auto_coord"] = autotile_coord; - Dictionary cell_old; - Dictionary cell_new; + return 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; +void TileMapEditor::_create_set_cell_undo_redo(const Vector2 &p_vec, const CellOp &p_cell_old, const CellOp &p_cell_new) { - 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; + 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(); @@ -260,7 +281,7 @@ void TileMapEditor::_finish_undo() { undo_redo->commit_action(); } -void TileMapEditor::_set_cell(const Point2i &p_pos, Vector<int> p_values, bool p_flip_h, bool p_flip_v, bool p_transpose) { +void TileMapEditor::_set_cell(const Point2i &p_pos, Vector<int> p_values, bool p_flip_h, bool p_flip_v, bool p_transpose, const Point2i p_autotile_coord) { ERR_FAIL_COND(!node); @@ -299,14 +320,17 @@ 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); + 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); } } else { // manually placing tiles should not update bitmasks - node->update_bitmask_area(Point2(p_pos)); + if (tool != TOOL_PASTING) { + node->update_bitmask_area(Point2(p_pos)); + } } } @@ -475,17 +499,17 @@ 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> &tiles = tileset->autotile_get_bitmask_map(sel_tile); + const Map<Vector2, uint32_t> &tiles2 = tileset->autotile_get_bitmask_map(sel_tile); - Vector<Vector2> entries; - for (const Map<Vector2, uint16_t>::Element *E = tiles.front(); E; E = E->next()) { - entries.push_back(E->key()); + Vector<Vector2> entries2; + for (const Map<Vector2, uint32_t>::Element *E = tiles2.front(); E; E = E->next()) { + entries2.push_back(E->key()); } - entries.sort(); + entries2.sort(); Ref<Texture> tex = tileset->tile_get_texture(sel_tile); - for (int i = 0; i < entries.size(); i++) { + for (int i = 0; i < entries2.size(); i++) { manual_palette->add_item(String()); @@ -494,7 +518,7 @@ void TileMapEditor::_update_palette() { Rect2 region = tileset->tile_get_region(sel_tile); int spacing = tileset->autotile_get_spacing(sel_tile); region.size = tileset->autotile_get_size(sel_tile); // !! - region.position += (region.size + Vector2(spacing, spacing)) * entries[i]; + region.position += (region.size + Vector2(spacing, spacing)) * entries2[i]; if (!region.has_no_area()) manual_palette->set_item_icon_region(manual_palette->get_item_count() - 1, region); @@ -502,18 +526,24 @@ void TileMapEditor::_update_palette() { manual_palette->set_item_icon(manual_palette->get_item_count() - 1, tex); } - manual_palette->set_item_metadata(manual_palette->get_item_count() - 1, entries[i]); + manual_palette->set_item_metadata(manual_palette->get_item_count() - 1, entries2[i]); } } } if (manual_palette->get_item_count() > 0) { // Only show the manual palette if at least tile exists in it - int selected = manual_palette->get_current(); - if (selected == -1) selected = 0; - manual_palette->set_current(selected); + int selected2 = manual_palette->get_current(); + if (selected2 == -1) selected2 = 0; + manual_palette->set_current(selected2); manual_palette->show(); } + + if (sel_tile != TileMap::INVALID_CELL && tileset->tile_get_tile_mode(sel_tile) == TileSet::AUTO_TILE) { + manual_button->show(); + } else { + manual_button->hide(); + } } void TileMapEditor::_pick_tile(const Point2 &p_pos) { @@ -537,6 +567,7 @@ void TileMapEditor::_pick_tile(const Point2 &p_pos) { flip_h = node->is_cell_x_flipped(p_pos.x, p_pos.y); flip_v = node->is_cell_y_flipped(p_pos.x, p_pos.y); transpose = node->is_cell_transposed(p_pos.x, p_pos.y); + autotile_coord = node->get_cell_autotile_coord(p_pos.x, p_pos.y); _update_palette(); CanvasItemEditor::get_singleton()->update_viewport(); @@ -701,7 +732,7 @@ void TileMapEditor::_erase_selection() { } } -void TileMapEditor::_draw_cell(Control *p_viewport, int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Transform2D &p_xform) { +void TileMapEditor::_draw_cell(Control *p_viewport, int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Point2i p_autotile_coord, const Transform2D &p_xform) { Ref<Texture> t = node->get_tileset()->tile_get_texture(p_cell); @@ -717,7 +748,11 @@ void TileMapEditor::_draw_cell(Control *p_viewport, int p_cell, const Point2i &p if ((manual_autotile || node->get_tileset()->tile_get_tile_mode(p_cell) == TileSet::ATLAS_TILE) && selected != -1) { offset = manual_palette->get_item_metadata(selected); } else { - offset = node->get_tileset()->autotile_get_icon_coordinate(p_cell); + if (tool != TOOL_PASTING) { + offset = node->get_tileset()->autotile_get_icon_coordinate(p_cell); + } else { + offset = p_autotile_coord; + } } int spacing = node->get_tileset()->autotile_get_spacing(p_cell); @@ -725,7 +760,7 @@ void TileMapEditor::_draw_cell(Control *p_viewport, int p_cell, const Point2i &p r.position += (r.size + Vector2(spacing, spacing)) * offset; } Size2 sc = p_xform.get_scale(); - + Size2 cell_size = node->get_cell_size(); Rect2 rect = Rect2(); rect.position = node->map_to_world(p_point) + node->get_cell_draw_offset(); @@ -735,62 +770,25 @@ void TileMapEditor::_draw_cell(Control *p_viewport, int p_cell, const Point2i &p rect.size = r.size; } - if (rect.size.y > rect.size.x) { - if ((p_flip_h && (p_flip_v || p_transpose)) || (p_flip_v && !p_transpose)) - tile_ofs.y += rect.size.y - rect.size.x; - } else if (rect.size.y < rect.size.x) { - if ((p_flip_v && (p_flip_h || p_transpose)) || (p_flip_h && !p_transpose)) - tile_ofs.x += rect.size.x - rect.size.y; - } - if (p_transpose) { SWAP(tile_ofs.x, tile_ofs.y); + rect.position.x += cell_size.x / 2 - rect.size.y / 2; + rect.position.y += cell_size.y / 2 - rect.size.x / 2; + } else { + rect.position += cell_size / 2 - rect.size / 2; } + if (p_flip_h) { sc.x *= -1.0; tile_ofs.x *= -1.0; } + if (p_flip_v) { sc.y *= -1.0; tile_ofs.y *= -1.0; } - if (node->get_tile_origin() == TileMap::TILE_ORIGIN_TOP_LEFT) { - - rect.position += tile_ofs; - } else if (node->get_tile_origin() == TileMap::TILE_ORIGIN_BOTTOM_LEFT) { - Size2 cell_size = node->get_cell_size(); - - rect.position += tile_ofs; - - if (p_transpose) { - if (p_flip_h) - rect.position.x -= cell_size.x; - else - rect.position.x += cell_size.x; - } else { - if (p_flip_v) - rect.position.y -= cell_size.y; - else - rect.position.y += cell_size.y; - } - - } else if (node->get_tile_origin() == TileMap::TILE_ORIGIN_CENTER) { - Size2 cell_size = node->get_cell_size(); - - rect.position += tile_ofs; - - if (p_flip_h) - rect.position.x -= cell_size.x / 2; - else - rect.position.x += cell_size.x / 2; - - if (p_flip_v) - rect.position.y -= cell_size.y / 2; - else - rect.position.y += cell_size.y / 2; - } - + rect.position += tile_ofs; rect.position = p_xform.xform(rect.position); rect.size *= sc; @@ -803,14 +801,14 @@ void TileMapEditor::_draw_cell(Control *p_viewport, int p_cell, const Point2i &p p_viewport->draw_texture_rect_region(t, rect, r, modulate, p_transpose); } -void TileMapEditor::_draw_fill_preview(Control *p_viewport, int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Transform2D &p_xform) { +void TileMapEditor::_draw_fill_preview(Control *p_viewport, int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Point2i p_autotile_coord, const Transform2D &p_xform) { PoolVector<Vector2> points = _bucket_fill(p_point, false, true); PoolVector<Vector2>::Read pr = points.read(); int len = points.size(); for (int i = 0; i < len; ++i) { - _draw_cell(p_viewport, p_cell, pr[i], p_flip_h, p_flip_v, p_transpose, p_xform); + _draw_cell(p_viewport, p_cell, pr[i], p_flip_h, p_flip_v, p_transpose, p_autotile_coord, p_xform); } } @@ -841,6 +839,7 @@ void TileMapEditor::_update_copydata() { tcd.flip_h = node->is_cell_x_flipped(j, i); tcd.flip_v = node->is_cell_y_flipped(j, i); tcd.transpose = node->is_cell_transposed(j, i); + tcd.autotile_coord = node->get_cell_autotile_coord(j, i); } copydata.push_back(tcd); @@ -914,11 +913,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; @@ -926,20 +921,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) { @@ -961,6 +956,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { rectangle_begin = over_tile; } + _update_button_tool(); return true; } else { @@ -1022,7 +1018,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { for (List<TileData>::Element *E = copydata.front(); E; E = E->next()) { ids.write[0] = E->get().cell; - _set_cell(E->get().pos + ofs, ids, E->get().flip_h, E->get().flip_v, E->get().transpose); + _set_cell(E->get().pos + ofs, ids, E->get().flip_h, E->get().flip_v, E->get().transpose, E->get().autotile_coord); } _finish_undo(); @@ -1057,6 +1053,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { } tool = TOOL_NONE; + _update_button_tool(); return true; } @@ -1072,6 +1069,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); return true; } @@ -1082,6 +1080,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); return true; } @@ -1094,11 +1093,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; @@ -1112,6 +1107,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { _set_cell(local, invalid_cell); } + _update_button_tool(); return true; } @@ -1126,6 +1122,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { tool = TOOL_NONE; + _update_button_tool(); return true; } else if (tool == TOOL_BUCKET) { @@ -1143,7 +1140,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { if (points.size() == 0) return false; - undo_redo->create_action("Bucket Fill"); + undo_redo->create_action(TTR("Bucket Fill")); undo_redo->add_do_method(this, "_erase_points", points); undo_redo->add_undo_method(this, "_fill_points", points, pop); @@ -1300,6 +1297,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) @@ -1311,6 +1316,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); return true; } @@ -1325,17 +1331,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)) { @@ -1344,6 +1353,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)) { @@ -1354,6 +1364,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); return true; } } @@ -1370,6 +1381,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { tool = TOOL_PASTING; CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); return true; } } @@ -1397,8 +1409,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; } @@ -1420,9 +1454,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++) { @@ -1436,7 +1470,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++) { @@ -1444,23 +1478,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++) { @@ -1481,17 +1518,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; } } } @@ -1519,8 +1559,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; @@ -1549,7 +1593,7 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { for (Map<Point2i, CellOp>::Element *E = paint_undo.front(); E; E = E->next()) { - _draw_cell(p_overlay, ids[0], E->key(), flip_h, flip_v, transpose, xform); + _draw_cell(p_overlay, ids[0], E->key(), flip_h, flip_v, transpose, autotile_coord, xform); } } else if (tool == TOOL_RECTANGLE_PAINT) { @@ -1562,7 +1606,7 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { for (int i = rectangle.position.y; i <= rectangle.position.y + rectangle.size.y; i++) { for (int j = rectangle.position.x; j <= rectangle.position.x + rectangle.size.x; j++) { - _draw_cell(p_overlay, ids[0], Point2i(j, i), flip_h, flip_v, transpose, xform); + _draw_cell(p_overlay, ids[0], Point2i(j, i), flip_h, flip_v, transpose, autotile_coord, xform); } } } else if (tool == TOOL_PASTING) { @@ -1584,7 +1628,7 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { TileData tcd = E->get(); - _draw_cell(p_overlay, tcd.cell, tcd.pos + ofs, tcd.flip_h, tcd.flip_v, tcd.transpose, xform); + _draw_cell(p_overlay, tcd.cell, tcd.pos + ofs, tcd.flip_h, tcd.flip_v, tcd.transpose, tcd.autotile_coord, xform); } Rect2i duplicate = rectangle; @@ -1601,7 +1645,7 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { } else if (tool == TOOL_BUCKET) { Vector<int> tiles = get_selected_tiles(); - _draw_fill_preview(p_overlay, tiles[0], over_tile, flip_h, flip_v, transpose, xform); + _draw_fill_preview(p_overlay, tiles[0], over_tile, flip_h, flip_v, transpose, autotile_coord, xform); } else { @@ -1610,7 +1654,7 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { if (st.size() == 1 && st[0] == TileMap::INVALID_CELL) return; - _draw_cell(p_overlay, st[0], over_tile, flip_h, flip_v, transpose, xform); + _draw_cell(p_overlay, st[0], over_tile, flip_h, flip_v, transpose, autotile_coord, xform); } } } @@ -1672,6 +1716,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); @@ -1852,37 +1897,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); @@ -1956,10 +2030,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 68e5806ee5..fcdada1111 100644 --- a/editor/plugins/tile_map_editor_plugin.h +++ b/editor/plugins/tile_map_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -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; @@ -111,6 +114,7 @@ class TileMapEditor : public VBoxContainer { bool flip_h; bool flip_v; bool transpose; + Point2i autotile_coord; Point2i rectangle_begin; Rect2i rectangle; @@ -145,6 +149,7 @@ class TileMapEditor : public VBoxContainer { bool flip_h; bool flip_v; bool transpose; + Point2i autotile_coord; TileData() : cell(TileMap::INVALID_CELL), @@ -168,8 +173,8 @@ class TileMapEditor : public VBoxContainer { void _select(const Point2i &p_from, const Point2i &p_to); void _erase_selection(); - void _draw_cell(Control *p_viewport, int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Transform2D &p_xform); - void _draw_fill_preview(Control *p_viewport, int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Transform2D &p_xform); + void _draw_cell(Control *p_viewport, int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Point2i p_autotile_coord, const Transform2D &p_xform); + void _draw_fill_preview(Control *p_viewport, int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Point2i p_autotile_coord, const Transform2D &p_xform); void _clear_bucket_cache(); void _update_copydata(); @@ -182,14 +187,17 @@ 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 _set_cell(const Point2i &p_pos, Vector<int> p_values, bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false); + 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(); void _canvas_mouse_exit(); @@ -208,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 fe896e12ff..a00be3c0ce 100644 --- a/editor/plugins/tile_set_editor_plugin.cpp +++ b/editor/plugins/tile_set_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -165,6 +165,11 @@ void TileSetEditor::_import_scene(Node *p_scene, Ref<TileSet> p_library, bool p_ _import_node(p_scene, p_library); } +void TileSetEditor::_undo_redo_import_scene(Node *p_scene, bool p_merge) { + + _import_scene(p_scene, tileset, p_merge); +} + Error TileSetEditor::update_library_file(Node *p_base_scene, Ref<TileSet> ml, bool p_merge) { _import_scene(p_base_scene, ml, p_merge); @@ -173,6 +178,7 @@ Error TileSetEditor::update_library_file(Node *p_base_scene, Ref<TileSet> ml, bo void TileSetEditor::_bind_methods() { + ClassDB::bind_method("_undo_redo_import_scene", &TileSetEditor::_undo_redo_import_scene); ClassDB::bind_method("_on_tileset_toolbar_button_pressed", &TileSetEditor::_on_tileset_toolbar_button_pressed); ClassDB::bind_method("_on_textures_added", &TileSetEditor::_on_textures_added); ClassDB::bind_method("_on_tileset_toolbar_confirm", &TileSetEditor::_on_tileset_toolbar_confirm); @@ -185,13 +191,23 @@ void TileSetEditor::_bind_methods() { ClassDB::bind_method("_on_workspace_input", &TileSetEditor::_on_workspace_input); ClassDB::bind_method("_on_tool_clicked", &TileSetEditor::_on_tool_clicked); ClassDB::bind_method("_on_priority_changed", &TileSetEditor::_on_priority_changed); + ClassDB::bind_method("_on_z_index_changed", &TileSetEditor::_on_z_index_changed); ClassDB::bind_method("_on_grid_snap_toggled", &TileSetEditor::_on_grid_snap_toggled); ClassDB::bind_method("_set_snap_step", &TileSetEditor::_set_snap_step); ClassDB::bind_method("_set_snap_off", &TileSetEditor::_set_snap_off); ClassDB::bind_method("_set_snap_sep", &TileSetEditor::_set_snap_sep); + ClassDB::bind_method("_validate_current_tile_id", &TileSetEditor::_validate_current_tile_id); ClassDB::bind_method("_zoom_in", &TileSetEditor::_zoom_in); ClassDB::bind_method("_zoom_out", &TileSetEditor::_zoom_out); ClassDB::bind_method("_zoom_reset", &TileSetEditor::_zoom_reset); + ClassDB::bind_method("_select_edited_shape_coord", &TileSetEditor::_select_edited_shape_coord); + ClassDB::bind_method("_sort_tiles", &TileSetEditor::_sort_tiles); + + ClassDB::bind_method("edit", &TileSetEditor::edit); + ClassDB::bind_method("add_texture", &TileSetEditor::add_texture); + ClassDB::bind_method("remove_texture", &TileSetEditor::remove_texture); + ClassDB::bind_method("update_texture_list_icon", &TileSetEditor::update_texture_list_icon); + ClassDB::bind_method("update_workspace_minsize", &TileSetEditor::update_workspace_minsize); } void TileSetEditor::_notification(int p_what) { @@ -218,6 +234,9 @@ void TileSetEditor::_notification(int p_what) { tools[BITMASK_PASTE]->set_icon(get_icon("Override", "EditorIcons")); tools[BITMASK_CLEAR]->set_icon(get_icon("Clear", "EditorIcons")); tools[SHAPE_NEW_POLYGON]->set_icon(get_icon("CollisionPolygon2D", "EditorIcons")); + tools[SHAPE_NEW_RECTANGLE]->set_icon(get_icon("CollisionShape2D", "EditorIcons")); + tools[SELECT_PREVIOUS]->set_icon(get_icon("ArrowLeft", "EditorIcons")); + tools[SELECT_NEXT]->set_icon(get_icon("ArrowRight", "EditorIcons")); tools[SHAPE_DELETE]->set_icon(get_icon("Remove", "EditorIcons")); tools[SHAPE_KEEP_INSIDE_TILE]->set_icon(get_icon("Snap", "EditorIcons")); tools[TOOL_GRID_SNAP]->set_icon(get_icon("SnapGrid", "EditorIcons")); @@ -225,6 +244,7 @@ void TileSetEditor::_notification(int p_what) { tools[ZOOM_1]->set_icon(get_icon("ZoomReset", "EditorIcons")); tools[ZOOM_IN]->set_icon(get_icon("ZoomMore", "EditorIcons")); tools[VISIBLE_INFO]->set_icon(get_icon("InformationSign", "EditorIcons")); + _update_toggle_shape_button(); tool_editmode[EDITMODE_REGION]->set_icon(get_icon("RegionEdit", "EditorIcons")); tool_editmode[EDITMODE_COLLISION]->set_icon(get_icon("StaticBody2D", "EditorIcons")); @@ -233,6 +253,7 @@ void TileSetEditor::_notification(int p_what) { tool_editmode[EDITMODE_BITMASK]->set_icon(get_icon("PackedDataContainer", "EditorIcons")); tool_editmode[EDITMODE_PRIORITY]->set_icon(get_icon("MaterialPreviewLight1", "EditorIcons")); tool_editmode[EDITMODE_ICON]->set_icon(get_icon("LargeTexture", "EditorIcons")); + tool_editmode[EDITMODE_Z_INDEX]->set_icon(get_icon("Sort", "EditorIcons")); scroll->add_style_override("bg", get_stylebox("bg", "Tree")); } break; @@ -242,6 +263,7 @@ void TileSetEditor::_notification(int p_what) { TileSetEditor::TileSetEditor(EditorNode *p_editor) { editor = p_editor; + undo_redo = editor->get_undo_redo(); current_tile = -1; VBoxContainer *left_container = memnew(VBoxContainer); @@ -306,11 +328,29 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) { tool_workspacemode[i]->connect("pressed", this, "_on_workspace_mode_changed", varray(i)); tool_hb->add_child(tool_workspacemode[i]); } + Control *spacer = memnew(Control); spacer->set_h_size_flags(Control::SIZE_EXPAND_FILL); tool_hb->add_child(spacer); tool_hb->move_child(spacer, WORKSPACE_CREATE_SINGLE); + tools[SELECT_NEXT] = memnew(ToolButton); + tool_hb->add_child(tools[SELECT_NEXT]); + tool_hb->move_child(tools[SELECT_NEXT], WORKSPACE_CREATE_SINGLE); + tools[SELECT_NEXT]->set_shortcut(ED_SHORTCUT("tileset_editor/next_shape", TTR("Next Coordinate"), KEY_PAGEDOWN)); + tools[SELECT_NEXT]->connect("pressed", this, "_on_tool_clicked", varray(SELECT_NEXT)); + tools[SELECT_NEXT]->set_tooltip(TTR("Select the next shape, subtile, or Tile.")); + tools[SELECT_PREVIOUS] = memnew(ToolButton); + tool_hb->add_child(tools[SELECT_PREVIOUS]); + tool_hb->move_child(tools[SELECT_PREVIOUS], WORKSPACE_CREATE_SINGLE); + tools[SELECT_PREVIOUS]->set_shortcut(ED_SHORTCUT("tileset_editor/previous_shape", TTR("Previous Coordinate"), KEY_PAGEUP)); + tools[SELECT_PREVIOUS]->set_tooltip(TTR("Select the previous shape, subtile, or Tile.")); + tools[SELECT_PREVIOUS]->connect("pressed", this, "_on_tool_clicked", varray(SELECT_PREVIOUS)); + + VSeparator *separator_shape_selection = memnew(VSeparator); + tool_hb->add_child(separator_shape_selection); + tool_hb->move_child(separator_shape_selection, WORKSPACE_CREATE_SINGLE); + tool_workspacemode[WORKSPACE_EDIT]->set_pressed(true); workspace_mode = WORKSPACE_EDIT; @@ -320,7 +360,7 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) { tool_hb = memnew(HBoxContainer); g = Ref<ButtonGroup>(memnew(ButtonGroup)); - String label[EDITMODE_MAX] = { "Region", "Collision", "Occlusion", "Navigation", "Bitmask", "Priority", "Icon" }; + String label[EDITMODE_MAX] = { "Region", "Collision", "Occlusion", "Navigation", "Bitmask", "Priority", "Icon", "Z Index" }; for (int i = 0; i < (int)EDITMODE_MAX; i++) { tool_editmode[i] = memnew(Button); tool_editmode[i]->set_text(label[i]); @@ -332,6 +372,15 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) { tool_editmode[EDITMODE_COLLISION]->set_pressed(true); edit_mode = EDITMODE_COLLISION; + tool_editmode[EDITMODE_REGION]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_region", TTR("Region Mode"), KEY_1)); + tool_editmode[EDITMODE_COLLISION]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_collision", TTR("Collision Mode"), KEY_2)); + tool_editmode[EDITMODE_OCCLUSION]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_occlusion", TTR("Occlusion Mode"), KEY_3)); + tool_editmode[EDITMODE_NAVIGATION]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_navigation", TTR("Navigation Mode"), KEY_4)); + tool_editmode[EDITMODE_BITMASK]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_bitmask", TTR("Bitmask Mode"), KEY_5)); + tool_editmode[EDITMODE_PRIORITY]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_priority", TTR("Priority Mode"), KEY_6)); + tool_editmode[EDITMODE_ICON]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_icon", TTR("Icon Mode"), KEY_7)); + tool_editmode[EDITMODE_Z_INDEX]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_z_index", TTR("Z Index Mode"), KEY_8)); + main_vb->add_child(tool_hb); separator_editmode = memnew(HSeparator); main_vb->add_child(separator_editmode); @@ -361,18 +410,48 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) { tools[BITMASK_CLEAR]->connect("pressed", this, "_on_tool_clicked", varray(BITMASK_CLEAR)); toolbar->add_child(tools[BITMASK_CLEAR]); + tools[SHAPE_NEW_RECTANGLE] = memnew(ToolButton); + toolbar->add_child(tools[SHAPE_NEW_RECTANGLE]); + tools[SHAPE_NEW_RECTANGLE]->set_toggle_mode(true); + tools[SHAPE_NEW_RECTANGLE]->set_button_group(tg); + tools[SHAPE_NEW_RECTANGLE]->set_tooltip(TTR("Create a new rectangle.")); + tools[SHAPE_NEW_POLYGON] = memnew(ToolButton); toolbar->add_child(tools[SHAPE_NEW_POLYGON]); tools[SHAPE_NEW_POLYGON]->set_toggle_mode(true); tools[SHAPE_NEW_POLYGON]->set_button_group(tg); tools[SHAPE_NEW_POLYGON]->set_tooltip(TTR("Create a new polygon.")); + separator_shape_toggle = memnew(VSeparator); + toolbar->add_child(separator_shape_toggle); + tools[SHAPE_TOGGLE_TYPE] = memnew(ToolButton); + tools[SHAPE_TOGGLE_TYPE]->connect("pressed", this, "_on_tool_clicked", varray(SHAPE_TOGGLE_TYPE)); + toolbar->add_child(tools[SHAPE_TOGGLE_TYPE]); + separator_delete = memnew(VSeparator); toolbar->add_child(separator_delete); tools[SHAPE_DELETE] = memnew(ToolButton); tools[SHAPE_DELETE]->connect("pressed", this, "_on_tool_clicked", varray(SHAPE_DELETE)); toolbar->add_child(tools[SHAPE_DELETE]); + spin_priority = memnew(SpinBox); + spin_priority->set_min(1); + spin_priority->set_max(255); + spin_priority->set_step(1); + spin_priority->set_custom_minimum_size(Size2(100, 0)); + spin_priority->connect("value_changed", this, "_on_priority_changed"); + spin_priority->hide(); + toolbar->add_child(spin_priority); + + spin_z_index = memnew(SpinBox); + spin_z_index->set_min(VS::CANVAS_ITEM_Z_MIN); + spin_z_index->set_max(VS::CANVAS_ITEM_Z_MAX); + spin_z_index->set_step(1); + spin_z_index->set_custom_minimum_size(Size2(100, 0)); + spin_z_index->connect("value_changed", this, "_on_z_index_changed"); + spin_z_index->hide(); + toolbar->add_child(spin_z_index); + separator_grid = memnew(VSeparator); toolbar->add_child(separator_grid); tools[SHAPE_KEEP_INSIDE_TILE] = memnew(ToolButton); @@ -386,15 +465,6 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) { tools[TOOL_GRID_SNAP]->connect("toggled", this, "_on_grid_snap_toggled"); toolbar->add_child(tools[TOOL_GRID_SNAP]); - spin_priority = memnew(SpinBox); - spin_priority->set_min(1); - spin_priority->set_max(255); - spin_priority->set_step(1); - spin_priority->set_custom_minimum_size(Size2(100, 0)); - spin_priority->connect("value_changed", this, "_on_priority_changed"); - spin_priority->hide(); - toolbar->add_child(spin_priority); - Control *separator = memnew(Control); separator->set_h_size_flags(SIZE_EXPAND_FILL); toolbar->add_child(separator); @@ -470,7 +540,7 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) { //--------------- helper = memnew(TilesetEditorContext(this)); - tile_names_opacity = 0; + tile_names_visible = false; // config scale max_scale = 10.0f; @@ -491,7 +561,7 @@ void TileSetEditor::_on_tileset_toolbar_button_pressed(int p_index) { } break; case TOOL_TILESET_REMOVE_TEXTURE: { if (get_current_texture().is_valid()) { - cd->set_text(TTR("Remove selected texture and ALL TILES which use it?")); + cd->set_text(TTR("Remove selected texture? This will remove all tiles which use it.")); cd->popup_centered(Size2(300, 60)); } else { err_dialog->set_text(TTR("You haven't selected a texture to remove.")); @@ -500,7 +570,7 @@ void TileSetEditor::_on_tileset_toolbar_button_pressed(int p_index) { } break; case TOOL_TILESET_CREATE_SCENE: { - cd->set_text(TTR("Create from scene?")); + cd->set_text(TTR("Create from scene? This will overwrite all current tiles.")); cd->popup_centered(Size2(300, 60)); } break; case TOOL_TILESET_MERGE_SCENE: { @@ -517,14 +587,18 @@ void TileSetEditor::_on_tileset_toolbar_confirm() { RID current_rid = get_current_texture()->get_rid(); List<int> ids; tileset->get_tile_list(&ids); + + undo_redo->create_action(TTR("Remove Texture")); for (List<int>::Element *E = ids.front(); E; E = E->next()) { if (tileset->tile_get_texture(E->get())->get_rid() == current_rid) { - tileset->remove_tile(E->get()); + undo_redo->add_do_method(tileset.ptr(), "remove_tile", E->get()); + _undo_tile_removal(E->get()); } } - texture_list->remove_item(texture_list->find_metadata(current_rid)); - texture_map.erase(current_rid); - _on_texture_list_selected(-1); + undo_redo->add_do_method(this, "remove_texture", get_current_texture()); + undo_redo->add_undo_method(this, "add_texture", get_current_texture()); + undo_redo->add_undo_method(this, "update_texture_list_icon"); + undo_redo->commit_action(); } break; case TOOL_TILESET_MERGE_SCENE: case TOOL_TILESET_CREATE_SCENE: { @@ -533,9 +607,19 @@ void TileSetEditor::_on_tileset_toolbar_confirm() { Node *scene = en->get_edited_scene(); if (!scene) break; - _import_scene(scene, tileset, option == TOOL_TILESET_MERGE_SCENE); - edit(tileset); + List<int> ids; + tileset->get_tile_list(&ids); + + undo_redo->create_action(TTR(option == TOOL_TILESET_MERGE_SCENE ? "Merge Tileset from Scene" : "Create Tileset from Scene")); + undo_redo->add_do_method(this, "_undo_redo_import_scene", scene, option == TOOL_TILESET_MERGE_SCENE); + undo_redo->add_undo_method(tileset.ptr(), "clear"); + for (List<int>::Element *E = ids.front(); E; E = E->next()) { + _undo_tile_removal(E->get()); + } + undo_redo->add_do_method(this, "edit", tileset); + undo_redo->add_undo_method(this, "edit", tileset); + undo_redo->commit_action(); } break; } } @@ -544,16 +628,15 @@ void TileSetEditor::_on_texture_list_selected(int p_index) { if (get_current_texture().is_valid()) { current_item_index = p_index; preview->set_texture(get_current_texture()); - workspace->set_custom_minimum_size(get_current_texture()->get_size() + WORKSPACE_MARGIN * 2); - workspace_container->set_custom_minimum_size(get_current_texture()->get_size() + WORKSPACE_MARGIN * 2); - workspace_overlay->set_custom_minimum_size(get_current_texture()->get_size() + WORKSPACE_MARGIN * 2); update_workspace_tile_mode(); + update_workspace_minsize(); } else { current_item_index = -1; preview->set_texture(NULL); workspace->set_custom_minimum_size(Size2i()); update_workspace_tile_mode(); } + set_current_tile(-1); workspace->update(); } @@ -569,9 +652,7 @@ void TileSetEditor::_on_textures_added(const PoolStringArray &p_paths) { if (texture_map.has(t->get_rid())) { invalid_count++; } else { - texture_list->add_item(t->get_path().get_file()); - texture_map.insert(t->get_rid(), t); - texture_list->set_item_metadata(texture_list->get_item_count() - 1, t->get_rid()); + add_texture(t); } } @@ -600,6 +681,7 @@ void TileSetEditor::_on_edit_mode_changed(int p_edit_mode) { tools[BITMASK_PASTE]->hide(); tools[BITMASK_CLEAR]->hide(); tools[SHAPE_NEW_POLYGON]->hide(); + tools[SHAPE_NEW_RECTANGLE]->hide(); if (workspace_mode == WORKSPACE_EDIT) { separator_delete->show(); @@ -617,10 +699,11 @@ void TileSetEditor::_on_edit_mode_changed(int p_edit_mode) { tools[TOOL_SELECT]->set_tooltip(TTR("Drag handles to edit Rect.\nClick on another Tile to edit it.")); tools[SHAPE_DELETE]->set_tooltip(TTR("Delete selected Rect.")); spin_priority->hide(); + spin_z_index->hide(); } break; case EDITMODE_COLLISION: - case EDITMODE_NAVIGATION: - case EDITMODE_OCCLUSION: { + case EDITMODE_OCCLUSION: + case EDITMODE_NAVIGATION: { tools[TOOL_SELECT]->show(); separator_bitmask->hide(); @@ -628,6 +711,7 @@ void TileSetEditor::_on_edit_mode_changed(int p_edit_mode) { tools[BITMASK_PASTE]->hide(); tools[BITMASK_CLEAR]->hide(); tools[SHAPE_NEW_POLYGON]->show(); + tools[SHAPE_NEW_RECTANGLE]->show(); separator_delete->show(); tools[SHAPE_DELETE]->show(); @@ -639,7 +723,9 @@ void TileSetEditor::_on_edit_mode_changed(int p_edit_mode) { tools[TOOL_SELECT]->set_tooltip(TTR("Select current edited sub-tile.\nClick on another Tile to edit it.")); tools[SHAPE_DELETE]->set_tooltip(TTR("Delete polygon.")); spin_priority->hide(); - select_coord(edited_shape_coord); + spin_z_index->hide(); + + _select_edited_shape_coord(); } break; case EDITMODE_BITMASK: { tools[TOOL_SELECT]->show(); @@ -649,18 +735,18 @@ void TileSetEditor::_on_edit_mode_changed(int p_edit_mode) { tools[BITMASK_PASTE]->show(); tools[BITMASK_CLEAR]->show(); tools[SHAPE_NEW_POLYGON]->hide(); + tools[SHAPE_NEW_RECTANGLE]->hide(); separator_delete->hide(); tools[SHAPE_DELETE]->hide(); - separator_grid->hide(); tools[SHAPE_KEEP_INSIDE_TILE]->hide(); - tools[TOOL_GRID_SNAP]->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: case EDITMODE_PRIORITY: case EDITMODE_ICON: { tools[TOOL_SELECT]->show(); @@ -670,6 +756,7 @@ void TileSetEditor::_on_edit_mode_changed(int p_edit_mode) { tools[BITMASK_PASTE]->hide(); tools[BITMASK_CLEAR]->hide(); tools[SHAPE_NEW_POLYGON]->hide(); + tools[SHAPE_NEW_RECTANGLE]->hide(); separator_delete->hide(); tools[SHAPE_DELETE]->hide(); @@ -681,13 +768,21 @@ void TileSetEditor::_on_edit_mode_changed(int p_edit_mode) { if (edit_mode == EDITMODE_ICON) { tools[TOOL_SELECT]->set_tooltip(TTR("Select sub-tile to use as icon, this will be also used on invalid autotile bindings.\nClick on another Tile to edit it.")); spin_priority->hide(); - } else { + spin_z_index->hide(); + } else if (edit_mode == EDITMODE_PRIORITY) { tools[TOOL_SELECT]->set_tooltip(TTR("Select sub-tile to change its priority.\nClick on another Tile to edit it.")); spin_priority->show(); + spin_z_index->hide(); + } else { + tools[TOOL_SELECT]->set_tooltip(TTR("Select sub-tile to change its z index.\nClick on another Tile to edit it.")); + spin_priority->hide(); + spin_z_index->show(); } } break; - default: {} + default: { + } } + _update_toggle_shape_button(); workspace->update(); } @@ -733,52 +828,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); } } @@ -797,10 +932,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) { @@ -811,7 +946,12 @@ void TileSetEditor::_on_workspace_draw() { spin_priority->set_suffix(" / " + String::num(total, 0)); draw_highlight_subtile(edited_shape_coord, queue_others); } break; - default: {} + case EDITMODE_Z_INDEX: { + spin_z_index->set_value(tileset->autotile_get_z_index(get_current_tile(), edited_shape_coord)); + draw_highlight_subtile(edited_shape_coord); + } break; + default: { + } } } @@ -849,6 +989,9 @@ void TileSetEditor::_on_workspace_draw() { draw_edited_region_subdivision(); } else { int t_id = get_current_tile(); + if (t_id < 0) + return; + Rect2i region; if (draw_edited_region) region = edited_region; @@ -877,17 +1020,16 @@ void TileSetEditor::_on_workspace_draw() { } void TileSetEditor::_on_workspace_process() { - float a = tile_names_opacity; - if (Input::get_singleton()->is_key_pressed(KEY_ALT) || tools[VISIBLE_INFO]->is_pressed()) { - a += get_tree()->get_idle_process_time() * 2; - } else { - a -= get_tree()->get_idle_process_time() * 2; - } - a = CLAMP(a, 0, 1); - if (a != tile_names_opacity) + if (Input::get_singleton()->is_key_pressed(KEY_ALT) || tools[VISIBLE_INFO]->is_pressed()) { + if (!tile_names_visible) { + tile_names_visible = true; + workspace_overlay->update(); + } + } else if (tile_names_visible) { + tile_names_visible = false; workspace_overlay->update(); - tile_names_opacity = a; + } } void TileSetEditor::_on_workspace_overlay_draw() { @@ -899,32 +1041,32 @@ void TileSetEditor::_on_workspace_overlay_draw() { const Color COLOR_SINGLE = Color(0.988281, 0.909323, 0.266373); const Color COLOR_ATLAS = Color(0.78653, 0.812835, 0.832031); - if (tile_names_opacity > 0) { + if (tile_names_visible) { RID current_texture_rid = get_current_texture()->get_rid(); List<int> *tiles = new List<int>(); 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; - c.a = tile_names_opacity; - 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, tile_names_opacity); - 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); } } @@ -940,7 +1082,6 @@ void TileSetEditor::_on_workspace_overlay_draw() { } } -#define MIN_DISTANCE_SQUARED 6 void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { if (tileset.is_null() || !get_current_texture().is_valid()) @@ -948,6 +1089,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(); @@ -1019,29 +1161,78 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { edited_region.position -= WORKSPACE_MARGIN; if (!edited_region.has_no_area()) { if (get_current_tile() >= 0 && workspace_mode == WORKSPACE_EDIT) { - tileset->tile_set_region(get_current_tile(), edited_region); + undo_redo->create_action(TTR("Set Tile Region")); + undo_redo->add_do_method(tileset.ptr(), "tile_set_region", get_current_tile(), edited_region); + undo_redo->add_undo_method(tileset.ptr(), "tile_set_region", get_current_tile(), tileset->tile_get_region(get_current_tile())); + + Size2 tile_workspace_size = edited_region.position + edited_region.size + WORKSPACE_MARGIN * 2; + Size2 workspace_minsize = workspace->get_custom_minimum_size(); + if (tile_workspace_size.x > workspace_minsize.x && tile_workspace_size.y > workspace_minsize.y) { + undo_redo->add_do_method(workspace, "set_custom_minimum_size", tile_workspace_size); + undo_redo->add_undo_method(workspace, "set_custom_minimum_size", workspace_minsize); + undo_redo->add_do_method(workspace_container, "set_custom_minimum_size", tile_workspace_size); + undo_redo->add_undo_method(workspace_container, "set_custom_minimum_size", workspace_minsize); + undo_redo->add_do_method(workspace_overlay, "set_custom_minimum_size", tile_workspace_size); + undo_redo->add_undo_method(workspace_overlay, "set_custom_minimum_size", workspace_minsize); + } else if (workspace_minsize.x > get_current_texture()->get_size().x + WORKSPACE_MARGIN.x * 2 || workspace_minsize.y > get_current_texture()->get_size().y + WORKSPACE_MARGIN.y * 2) { + undo_redo->add_do_method(this, "update_workspace_minsize"); + undo_redo->add_undo_method(this, "update_workspace_minsize"); + } + + edited_region = Rect2(); + + undo_redo->add_do_method(workspace, "update"); + undo_redo->add_undo_method(workspace, "update"); + undo_redo->add_do_method(workspace_overlay, "update"); + undo_redo->add_undo_method(workspace_overlay, "update"); + undo_redo->commit_action(); } else { int t_id = tileset->get_last_unused_tile_id(); - tileset->create_tile(t_id); - tileset->tile_set_texture(t_id, get_current_texture()); - tileset->tile_set_region(t_id, edited_region); - tileset->tile_set_name(t_id, get_current_texture()->get_path().get_file() + " " + String::num(t_id, 0)); + undo_redo->create_action(TTR("Create Tile")); + undo_redo->add_do_method(tileset.ptr(), "create_tile", t_id); + undo_redo->add_undo_method(tileset.ptr(), "remove_tile", t_id); + undo_redo->add_undo_method(this, "_validate_current_tile_id"); + undo_redo->add_do_method(tileset.ptr(), "tile_set_texture", t_id, get_current_texture()); + undo_redo->add_do_method(tileset.ptr(), "tile_set_region", t_id, edited_region); + undo_redo->add_do_method(tileset.ptr(), "tile_set_name", t_id, get_current_texture()->get_path().get_file() + " " + String::num(t_id, 0)); if (workspace_mode != WORKSPACE_CREATE_SINGLE) { - tileset->autotile_set_size(t_id, snap_step); - tileset->autotile_set_spacing(t_id, snap_separation.x); - tileset->tile_set_tile_mode(t_id, workspace_mode == WORKSPACE_CREATE_AUTOTILE ? TileSet::AUTO_TILE : TileSet::ATLAS_TILE); + undo_redo->add_do_method(tileset.ptr(), "autotile_set_size", t_id, snap_step); + undo_redo->add_do_method(tileset.ptr(), "autotile_set_spacing", t_id, snap_separation.x); + undo_redo->add_do_method(tileset.ptr(), "tile_set_tile_mode", t_id, workspace_mode == WORKSPACE_CREATE_AUTOTILE ? TileSet::AUTO_TILE : TileSet::ATLAS_TILE); } - set_current_tile(t_id); tool_workspacemode[WORKSPACE_EDIT]->set_pressed(true); tool_editmode[EDITMODE_COLLISION]->set_pressed(true); edit_mode = EDITMODE_COLLISION; + + Size2 tile_workspace_size = edited_region.position + edited_region.size + WORKSPACE_MARGIN * 2; + Size2 workspace_minsize = workspace->get_custom_minimum_size(); + if (tile_workspace_size.x > workspace_minsize.x || tile_workspace_size.y > workspace_minsize.y) { + Size2 new_workspace_minsize = Size2(MAX(tile_workspace_size.x, workspace_minsize.x), MAX(tile_workspace_size.y, workspace_minsize.y)); + undo_redo->add_do_method(workspace, "set_custom_minimum_size", new_workspace_minsize); + undo_redo->add_undo_method(workspace, "set_custom_minimum_size", workspace_minsize); + undo_redo->add_do_method(workspace_container, "set_custom_minimum_size", new_workspace_minsize); + undo_redo->add_undo_method(workspace_container, "set_custom_minimum_size", workspace_minsize); + undo_redo->add_do_method(workspace_overlay, "set_custom_minimum_size", new_workspace_minsize); + undo_redo->add_undo_method(workspace_overlay, "set_custom_minimum_size", workspace_minsize); + } + + edited_region = Rect2(); + + undo_redo->add_do_method(workspace, "update"); + undo_redo->add_undo_method(workspace, "update"); + undo_redo->add_do_method(workspace_overlay, "update"); + undo_redo->add_undo_method(workspace_overlay, "update"); + undo_redo->commit_action(); + + set_current_tile(t_id); _on_workspace_mode_changed(WORKSPACE_EDIT); } + } else { + edited_region = Rect2(); + workspace->update(); + workspace_overlay->update(); } - edited_region = Rect2(); - workspace->update(); - workspace_overlay->update(); return; } } else if (mm.is_valid()) { @@ -1054,8 +1245,8 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { } } } - if (workspace_mode == WORKSPACE_EDIT) { + if (workspace_mode == WORKSPACE_EDIT) { if (get_current_tile() >= 0) { int spacing = tileset->autotile_get_spacing(get_current_tile()); Vector2 size = tileset->autotile_get_size(get_current_tile()); @@ -1064,13 +1255,12 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { if (mb.is_valid()) { if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT && current_tile_region.has_point(mb->get_position())) { 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))); - tileset->autotile_set_icon_coordinate(get_current_tile(), coord); - Rect2 region = tileset->tile_get_region(get_current_tile()); - region.size = size; - coord.x *= (spacing + size.x); - coord.y *= (spacing + size.y); - region.position += coord; - workspace->update(); + undo_redo->create_action(TTR("Set Tile Icon")); + undo_redo->add_do_method(tileset.ptr(), "autotile_set_icon_coordinate", get_current_tile(), coord); + undo_redo->add_undo_method(tileset.ptr(), "autotile_set_icon_coordinate", get_current_tile(), tileset->autotile_get_icon_coordinate(get_current_tile())); + undo_redo->add_do_method(workspace, "update"); + undo_redo->add_undo_method(workspace, "update"); + undo_redo->commit_action(); } } } break; @@ -1083,10 +1273,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) { @@ -1128,19 +1319,34 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { } } } - uint16_t mask = tileset->autotile_get_bitmask(get_current_tile(), coord); - if (erasing) { - mask &= ~bit; + + 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 { - mask |= bit; + 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); + undo_redo->add_undo_method(tileset.ptr(), "autotile_set_bitmask", get_current_tile(), coord, old_mask); + undo_redo->add_do_method(workspace, "update"); + undo_redo->add_undo_method(workspace, "update"); + undo_redo->commit_action(); } - tileset->autotile_set_bitmask(get_current_tile(), coord, mask); - workspace->update(); } } else { if ((erasing && mb->get_button_index() == BUTTON_RIGHT) || (!erasing && mb->get_button_index() == BUTTON_LEFT)) { dragging = false; erasing = false; + alternative = false; } } } @@ -1149,7 +1355,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) { @@ -1191,34 +1397,49 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { } } } - uint16_t mask = tileset->autotile_get_bitmask(get_current_tile(), coord); - if (erasing) { - mask &= ~bit; + + 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 { - mask |= bit; + 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); + undo_redo->add_undo_method(tileset.ptr(), "autotile_set_bitmask", get_current_tile(), coord, old_mask); + undo_redo->add_do_method(workspace, "update"); + undo_redo->add_undo_method(workspace, "update"); + undo_redo->commit_action(); } - tileset->autotile_set_bitmask(get_current_tile(), coord, mask); - workspace->update(); } } } break; case EDITMODE_COLLISION: case EDITMODE_OCCLUSION: case EDITMODE_NAVIGATION: - case EDITMODE_PRIORITY: { + case EDITMODE_PRIORITY: + case EDITMODE_Z_INDEX: { Vector2 shape_anchor = Vector2(0, 0); if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) { shape_anchor = edited_shape_coord; shape_anchor.x *= (size.x + spacing); shape_anchor.y *= (size.y + spacing); } + const real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius"); shape_anchor += current_tile_region.position; if (tools[TOOL_SELECT]->is_pressed()) { if (mb.is_valid()) { if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { if (edit_mode != EDITMODE_PRIORITY && current_shape.size() > 0) { for (int i = 0; i < current_shape.size(); i++) { - if ((current_shape[i] - mb->get_position()).length_squared() <= MIN_DISTANCE_SQUARED) { + if ((current_shape[i] - mb->get_position()).length_squared() <= grab_threshold) { dragging_point = i; workspace->update(); return; @@ -1229,20 +1450,7 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { 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))); if (edited_shape_coord != coord) { edited_shape_coord = coord; - edited_occlusion_shape = tileset->autotile_get_light_occluder(get_current_tile(), edited_shape_coord); - edited_navigation_shape = tileset->autotile_get_navigation_polygon(get_current_tile(), edited_shape_coord); - Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(get_current_tile()); - bool found_collision_shape = false; - for (int i = 0; i < sd.size(); i++) { - if (sd[i].autotile_coord == coord) { - edited_collision_shape = sd[i].shape; - found_collision_shape = true; - break; - } - } - if (!found_collision_shape) - edited_collision_shape = Ref<ConvexPolygonShape2D>(NULL); - select_coord(edited_shape_coord); + _select_edited_shape_coord(); } } workspace->update(); @@ -1261,9 +1469,11 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { points.push_back(p - shape_anchor); } - edited_collision_shape->set_points(points); - - workspace->update(); + undo_redo->create_action(TTR("Edit Collision Polygon")); + _set_edited_shape_points(points); + undo_redo->add_do_method(this, "_select_edited_shape_coord"); + undo_redo->add_undo_method(this, "_select_edited_shape_coord"); + undo_redo->commit_action(); } } else if (edit_mode == EDITMODE_OCCLUSION) { if (dragging_point >= 0) { @@ -1278,9 +1488,13 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { } w = PoolVector<Vector2>::Write(); - edited_occlusion_shape->set_polygon(polygon); - workspace->update(); + undo_redo->create_action(TTR("Edit Occlusion Polygon")); + undo_redo->add_do_method(edited_occlusion_shape.ptr(), "set_polygon", polygon); + undo_redo->add_undo_method(edited_occlusion_shape.ptr(), "set_polygon", edited_occlusion_shape->get_polygon()); + undo_redo->add_do_method(this, "_select_edited_shape_coord"); + undo_redo->add_undo_method(this, "_select_edited_shape_coord"); + undo_redo->commit_action(); } } else if (edit_mode == EDITMODE_NAVIGATION) { if (dragging_point >= 0) { @@ -1297,10 +1511,15 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { } w = PoolVector<Vector2>::Write(); - edited_navigation_shape->set_vertices(polygon); - edited_navigation_shape->add_polygon(indices); - workspace->update(); + undo_redo->create_action(TTR("Edit Navigation Polygon")); + undo_redo->add_do_method(edited_navigation_shape.ptr(), "set_vertices", polygon); + undo_redo->add_undo_method(edited_navigation_shape.ptr(), "set_vertices", edited_navigation_shape->get_vertices()); + undo_redo->add_do_method(edited_navigation_shape.ptr(), "add_polygon", indices); + undo_redo->add_undo_method(edited_navigation_shape.ptr(), "add_polygon", edited_navigation_shape->get_polygon(0)); + undo_redo->add_do_method(this, "_select_edited_shape_coord"); + undo_redo->add_undo_method(this, "_select_edited_shape_coord"); + undo_redo->commit_action(); } } } @@ -1311,14 +1530,13 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { } } } else if (tools[SHAPE_NEW_POLYGON]->is_pressed()) { - if (mb.is_valid()) { if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { Vector2 pos = mb->get_position(); pos = snap_point(pos); if (creating_shape) { if (current_shape.size() > 0) { - if ((pos - current_shape[0]).length_squared() <= MIN_DISTANCE_SQUARED) { + if ((pos - current_shape[0]).length_squared() <= grab_threshold) { if (current_shape.size() > 2) { close_shape(shape_anchor); workspace->update(); @@ -1329,70 +1547,72 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { current_shape.push_back(pos); workspace->update(); } else { - int t_id = get_current_tile(); - if (t_id >= 0) { - if (edit_mode == EDITMODE_COLLISION) { - Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(t_id); - for (int i = 0; i < sd.size(); i++) { - if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE || sd[i].autotile_coord == edited_shape_coord) { - Ref<ConvexPolygonShape2D> shape = sd[i].shape; - - if (!shape.is_null()) { - sd.remove(i); - tileset->tile_set_shapes(get_current_tile(), sd); - edited_collision_shape = Ref<Shape2D>(); - workspace->update(); - } - break; - } - } - } else if (edit_mode == EDITMODE_OCCLUSION) { - if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) { - Map<Vector2, Ref<OccluderPolygon2D> > map = tileset->autotile_get_light_oclusion_map(t_id); - for (Map<Vector2, Ref<OccluderPolygon2D> >::Element *E = map.front(); E; E = E->next()) { - if (E->key() == edited_shape_coord) { - tileset->autotile_set_light_occluder(get_current_tile(), Ref<OccluderPolygon2D>(), edited_shape_coord); - break; - } - } - } else - tileset->tile_set_light_occluder(t_id, Ref<OccluderPolygon2D>()); - - edited_occlusion_shape = Ref<OccluderPolygon2D>(); - workspace->update(); - } else if (edit_mode == EDITMODE_NAVIGATION) { - if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) { - Map<Vector2, Ref<NavigationPolygon> > map = tileset->autotile_get_navigation_map(t_id); - for (Map<Vector2, Ref<NavigationPolygon> >::Element *E = map.front(); E; E = E->next()) { - if (E->key() == edited_shape_coord) { - tileset->autotile_set_navigation_polygon(t_id, Ref<NavigationPolygon>(), edited_shape_coord); - break; - } - } - } else - tileset->tile_set_navigation_polygon(t_id, Ref<NavigationPolygon>()); - edited_navigation_shape = Ref<NavigationPolygon>(); - workspace->update(); - } - } - creating_shape = true; + _set_edited_collision_shape(Ref<ConvexPolygonShape2D>()); current_shape.resize(0); current_shape.push_back(snap_point(pos)); + workspace->update(); + } + } 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 (mm.is_valid()) { + if (creating_shape) { + workspace->update(); + } + } + } else if (tools[SHAPE_NEW_RECTANGLE]->is_pressed()) { + if (mb.is_valid()) { + if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + _set_edited_collision_shape(Ref<ConvexPolygonShape2D>()); + current_shape.resize(0); + 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_RIGHT && current_shape.size() > 2) { + } 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: { + } } } } @@ -1402,14 +1622,70 @@ void TileSetEditor::_on_tool_clicked(int p_tool) { if (p_tool == BITMASK_COPY) { bitmask_map_copy = tileset->autotile_get_bitmask_map(get_current_tile()); } else if (p_tool == BITMASK_PASTE) { - tileset->autotile_clear_bitmask_map(get_current_tile()); - for (Map<Vector2, uint16_t>::Element *E = bitmask_map_copy.front(); E; E = E->next()) { - tileset->autotile_set_bitmask(get_current_tile(), E->key(), E->value()); + 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, 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()); } - workspace->update(); + 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"); + undo_redo->add_undo_method(workspace, "update"); + undo_redo->commit_action(); } else if (p_tool == BITMASK_CLEAR) { - tileset->autotile_clear_bitmask_map(get_current_tile()); - workspace->update(); + 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, 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"); + undo_redo->add_undo_method(workspace, "update"); + undo_redo->commit_action(); + } else if (p_tool == SHAPE_TOGGLE_TYPE) { + if (edited_collision_shape.is_valid()) { + Ref<ConvexPolygonShape2D> convex = edited_collision_shape; + Ref<ConcavePolygonShape2D> concave = edited_collision_shape; + Ref<Shape2D> previous_shape = edited_collision_shape; + Array sd = tileset->call("tile_get_shapes", get_current_tile()); + + if (convex.is_valid()) { + // Make concave + undo_redo->create_action(TTR("Make Polygon Concave")); + Ref<ConcavePolygonShape2D> _concave = memnew(ConcavePolygonShape2D); + edited_collision_shape = _concave; + _set_edited_shape_points(_get_collision_shape_points(convex)); + } else if (concave.is_valid()) { + // Make convex + undo_redo->create_action(TTR("Make Polygon Convex")); + Ref<ConvexPolygonShape2D> _convex = memnew(ConvexPolygonShape2D); + edited_collision_shape = _convex; + _set_edited_shape_points(_get_collision_shape_points(concave)); + } else { + // Shouldn't happen + } + for (int i = 0; i < sd.size(); i++) { + if (sd[i].get("shape") == previous_shape) { + undo_redo->add_undo_method(tileset.ptr(), "tile_set_shapes", get_current_tile(), sd.duplicate()); + sd.remove(i); + sd.insert(i, edited_collision_shape); + undo_redo->add_do_method(tileset.ptr(), "tile_set_shapes", get_current_tile(), sd); + undo_redo->add_do_method(this, "_select_edited_shape_coord"); + undo_redo->add_undo_method(this, "_select_edited_shape_coord"); + undo_redo->commit_action(); + break; + } + } + _update_toggle_shape_button(); + workspace->update(); + workspace_container->update(); + helper->_change_notify(""); + } + } else if (p_tool == SELECT_NEXT) { + _select_next_shape(); + } else if (p_tool == SELECT_PREVIOUS) { + _select_previous_shape(); } else if (p_tool == SHAPE_DELETE) { if (creating_shape) { creating_shape = false; @@ -1418,11 +1694,25 @@ void TileSetEditor::_on_tool_clicked(int p_tool) { } else { switch (edit_mode) { case EDITMODE_REGION: { - if (workspace_mode == WORKSPACE_EDIT && get_current_tile() >= 0) { - tileset->remove_tile(get_current_tile()); - set_current_tile(-1); - workspace->update(); - workspace_overlay->update(); + int t_id = get_current_tile(); + if (workspace_mode == WORKSPACE_EDIT && t_id >= 0) { + undo_redo->create_action(TTR("Remove Tile")); + undo_redo->add_do_method(tileset.ptr(), "remove_tile", t_id); + _undo_tile_removal(t_id); + undo_redo->add_do_method(this, "_validate_current_tile_id"); + + Rect2 tile_region = tileset->tile_get_region(get_current_tile()); + Size2 tile_workspace_size = tile_region.position + tile_region.size; + if (tile_workspace_size.x > get_current_texture()->get_size().x || tile_workspace_size.y > get_current_texture()->get_size().y) { + undo_redo->add_do_method(this, "update_workspace_minsize"); + undo_redo->add_undo_method(this, "update_workspace_minsize"); + } + + undo_redo->add_do_method(workspace, "update"); + undo_redo->add_undo_method(workspace, "update"); + undo_redo->add_do_method(workspace_overlay, "update"); + undo_redo->add_undo_method(workspace_overlay, "update"); + undo_redo->commit_action(); } tool_workspacemode[WORKSPACE_EDIT]->set_pressed(true); workspace_mode = WORKSPACE_EDIT; @@ -1430,40 +1720,54 @@ void TileSetEditor::_on_tool_clicked(int p_tool) { } break; case EDITMODE_COLLISION: { if (!edited_collision_shape.is_null()) { - Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(get_current_tile()); - int index = -1; + // Necessary to get the version that returns a Array instead of a Vector. + Array sd = tileset->call("tile_get_shapes", get_current_tile()); for (int i = 0; i < sd.size(); i++) { - if (sd[i].shape == edited_collision_shape) { - index = i; + if (sd[i].get("shape") == edited_collision_shape) { + undo_redo->create_action(TTR("Remove Collision Polygon")); + undo_redo->add_undo_method(tileset.ptr(), "tile_set_shapes", get_current_tile(), sd.duplicate()); + sd.remove(i); + undo_redo->add_do_method(tileset.ptr(), "tile_set_shapes", get_current_tile(), sd); + undo_redo->add_do_method(this, "_select_edited_shape_coord"); + undo_redo->add_undo_method(this, "_select_edited_shape_coord"); + undo_redo->commit_action(); break; } } - if (index >= 0) { - sd.remove(index); - tileset->tile_set_shapes(get_current_tile(), sd); - edited_collision_shape = Ref<Shape2D>(); - current_shape.resize(0); - workspace->update(); + } + } break; + case EDITMODE_OCCLUSION: { + if (!edited_occlusion_shape.is_null()) { + undo_redo->create_action(TTR("Remove Occlusion Polygon")); + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) { + undo_redo->add_do_method(tileset.ptr(), "tile_set_light_occluder", get_current_tile(), Ref<OccluderPolygon2D>()); + undo_redo->add_undo_method(tileset.ptr(), "tile_set_light_occluder", get_current_tile(), tileset->tile_get_light_occluder(get_current_tile())); + } else { + undo_redo->add_do_method(tileset.ptr(), "autotile_set_light_occluder", get_current_tile(), Ref<OccluderPolygon2D>(), edited_shape_coord); + undo_redo->add_undo_method(tileset.ptr(), "autotile_set_light_occluder", get_current_tile(), tileset->autotile_get_light_occluder(get_current_tile(), edited_shape_coord), edited_shape_coord); } + undo_redo->add_do_method(this, "_select_edited_shape_coord"); + undo_redo->add_undo_method(this, "_select_edited_shape_coord"); + undo_redo->commit_action(); } } break; case EDITMODE_NAVIGATION: { if (!edited_navigation_shape.is_null()) { - tileset->autotile_set_navigation_polygon(get_current_tile(), Ref<NavigationPolygon>(), edited_shape_coord); - edited_navigation_shape = Ref<NavigationPolygon>(); - current_shape.resize(0); - workspace->update(); - } - } break; - case EDITMODE_OCCLUSION: { - if (!edited_occlusion_shape.is_null()) { - tileset->autotile_set_light_occluder(get_current_tile(), Ref<OccluderPolygon2D>(), edited_shape_coord); - edited_occlusion_shape = Ref<OccluderPolygon2D>(); - current_shape.resize(0); - workspace->update(); + undo_redo->create_action(TTR("Remove Navigation Polygon")); + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) { + undo_redo->add_do_method(tileset.ptr(), "tile_set_navigation_polygon", get_current_tile(), Ref<NavigationPolygon>()); + undo_redo->add_undo_method(tileset.ptr(), "tile_set_navigation_polygon", get_current_tile(), tileset->tile_get_navigation_polygon(get_current_tile())); + } else { + undo_redo->add_do_method(tileset.ptr(), "autotile_set_navigation_polygon", get_current_tile(), Ref<NavigationPolygon>(), edited_shape_coord); + undo_redo->add_undo_method(tileset.ptr(), "autotile_set_navigation_polygon", get_current_tile(), tileset->autotile_get_navigation_polygon(get_current_tile(), edited_shape_coord), edited_shape_coord); + } + undo_redo->add_do_method(this, "_select_edited_shape_coord"); + undo_redo->add_undo_method(this, "_select_edited_shape_coord"); + undo_redo->commit_action(); } } break; - default: {} + default: { + } } } } else if (p_tool == TOOL_SELECT) { @@ -1477,8 +1781,27 @@ void TileSetEditor::_on_tool_clicked(int p_tool) { } void TileSetEditor::_on_priority_changed(float val) { - tileset->autotile_set_subtile_priority(get_current_tile(), edited_shape_coord, (int)val); - workspace->update(); + if ((int)val == tileset->autotile_get_subtile_priority(get_current_tile(), edited_shape_coord)) + return; + + undo_redo->create_action(TTR("Edit Tile Priority")); + undo_redo->add_do_method(tileset.ptr(), "autotile_set_subtile_priority", get_current_tile(), edited_shape_coord, (int)val); + undo_redo->add_undo_method(tileset.ptr(), "autotile_set_subtile_priority", get_current_tile(), edited_shape_coord, tileset->autotile_get_subtile_priority(get_current_tile(), edited_shape_coord)); + undo_redo->add_do_method(workspace, "update"); + undo_redo->add_undo_method(workspace, "update"); + undo_redo->commit_action(); +} + +void TileSetEditor::_on_z_index_changed(float val) { + if ((int)val == tileset->autotile_get_z_index(get_current_tile(), edited_shape_coord)) + return; + + undo_redo->create_action(TTR("Edit Tile Z Index")); + undo_redo->add_do_method(tileset.ptr(), "autotile_set_z_index", get_current_tile(), edited_shape_coord, (int)val); + undo_redo->add_undo_method(tileset.ptr(), "autotile_set_z_index", get_current_tile(), edited_shape_coord, tileset->autotile_get_z_index(get_current_tile(), edited_shape_coord)); + undo_redo->add_do_method(workspace, "update"); + undo_redo->add_undo_method(workspace, "update"); + undo_redo->commit_action(); } void TileSetEditor::_on_grid_snap_toggled(bool p_val) { @@ -1486,6 +1809,380 @@ void TileSetEditor::_on_grid_snap_toggled(bool p_val) { workspace->update(); } +Vector<Vector2> TileSetEditor::_get_collision_shape_points(const Ref<Shape2D> &p_shape) { + Ref<ConvexPolygonShape2D> convex = p_shape; + Ref<ConcavePolygonShape2D> concave = p_shape; + if (convex.is_valid()) { + return convex->get_points(); + } else if (concave.is_valid()) { + Vector<Vector2> points; + for (int i = 0; i < concave->get_segments().size(); i += 2) { + points.push_back(concave->get_segments()[i]); + } + return points; + } else { + return Vector<Vector2>(); + } +} + +Vector<Vector2> TileSetEditor::_get_edited_shape_points() { + return _get_collision_shape_points(edited_collision_shape); +} + +void TileSetEditor::_set_edited_shape_points(const Vector<Vector2> points) { + Ref<ConvexPolygonShape2D> convex = edited_collision_shape; + Ref<ConcavePolygonShape2D> concave = edited_collision_shape; + if (convex.is_valid()) { + undo_redo->add_do_method(convex.ptr(), "set_points", points); + undo_redo->add_undo_method(convex.ptr(), "set_points", _get_edited_shape_points()); + } else if (concave.is_valid()) { + PoolVector2Array segments; + for (int i = 0; i < points.size() - 1; i++) { + segments.push_back(points[i]); + segments.push_back(points[i + 1]); + } + segments.push_back(points[points.size() - 1]); + segments.push_back(points[0]); + concave->set_segments(segments); + undo_redo->add_do_method(concave.ptr(), "set_segments", segments); + undo_redo->add_undo_method(concave.ptr(), "set_segments", concave->get_segments()); + } else { + // Invalid shape + } +} + +void TileSetEditor::_update_tile_data() { + current_tile_data.clear(); + if (get_current_tile() < 0) + return; + + Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(get_current_tile()); + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) { + SubtileData data; + for (int i = 0; i < sd.size(); i++) { + data.collisions.push_back(sd[i].shape); + } + data.navigation_shape = tileset->tile_get_navigation_polygon(get_current_tile()); + data.occlusion_shape = tileset->tile_get_light_occluder(get_current_tile()); + current_tile_data[Vector2i()] = data; + } else { + int spacing = tileset->autotile_get_spacing(get_current_tile()); + Vector2 size = tileset->tile_get_region(get_current_tile()).size; + Vector2i cell_count = size / (tileset->autotile_get_size(get_current_tile()) + Vector2(spacing, spacing)); + for (int y = 0; y < cell_count.y; y++) { + for (int x = 0; x < cell_count.x; x++) { + SubtileData data; + Vector2i coord(x, y); + for (int i = 0; i < sd.size(); i++) { + if (sd[i].autotile_coord == coord) { + data.collisions.push_back(sd[i].shape); + } + } + data.navigation_shape = tileset->autotile_get_navigation_polygon(get_current_tile(), coord); + data.occlusion_shape = tileset->tile_get_light_occluder(get_current_tile()); + current_tile_data[coord] = data; + } + } + } +} + +void TileSetEditor::_update_toggle_shape_button() { + Ref<ConvexPolygonShape2D> convex = edited_collision_shape; + Ref<ConcavePolygonShape2D> concave = edited_collision_shape; + separator_shape_toggle->show(); + tools[SHAPE_TOGGLE_TYPE]->show(); + if (edit_mode != EDITMODE_COLLISION || !edited_collision_shape.is_valid()) { + separator_shape_toggle->hide(); + tools[SHAPE_TOGGLE_TYPE]->hide(); + } else if (concave.is_valid()) { + tools[SHAPE_TOGGLE_TYPE]->set_icon(get_icon("ConvexPolygonShape2D", "EditorIcons")); + tools[SHAPE_TOGGLE_TYPE]->set_text("Make Convex"); + } else if (convex.is_valid()) { + tools[SHAPE_TOGGLE_TYPE]->set_icon(get_icon("ConcavePolygonShape2D", "EditorIcons")); + tools[SHAPE_TOGGLE_TYPE]->set_text("Make Concave"); + } else { + // Shouldn't happen + separator_shape_toggle->hide(); + tools[SHAPE_TOGGLE_TYPE]->hide(); + } +} + +void TileSetEditor::_select_next_tile() { + Array tiles = _get_tiles_in_current_texture(true); + if (tiles.size() == 0) { + set_current_tile(-1); + } else if (get_current_tile() == -1) { + set_current_tile(tiles[0]); + } else { + int index = tiles.find(get_current_tile()); + if (index < 0) { + set_current_tile(tiles[0]); + } else if (index == tiles.size() - 1) { + set_current_tile(tiles[0]); + } else { + set_current_tile(tiles[index + 1]); + } + } + if (get_current_tile() == -1) { + return; + } else if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) { + return; + } else { + switch (edit_mode) { + case EDITMODE_COLLISION: + case EDITMODE_OCCLUSION: + case EDITMODE_NAVIGATION: + case EDITMODE_PRIORITY: + case EDITMODE_Z_INDEX: { + edited_shape_coord = Vector2(); + _select_edited_shape_coord(); + } break; + default: { + } + } + } +} + +void TileSetEditor::_select_previous_tile() { + Array tiles = _get_tiles_in_current_texture(true); + if (tiles.size() == 0) { + set_current_tile(-1); + } else if (get_current_tile() == -1) { + set_current_tile(tiles[tiles.size() - 1]); + } else { + int index = tiles.find(get_current_tile()); + if (index <= 0) { + set_current_tile(tiles[tiles.size() - 1]); + } else { + set_current_tile(tiles[index - 1]); + } + } + if (get_current_tile() == -1) { + return; + } else if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) { + return; + } else { + switch (edit_mode) { + case EDITMODE_COLLISION: + case EDITMODE_OCCLUSION: + case EDITMODE_NAVIGATION: + case EDITMODE_PRIORITY: + case EDITMODE_Z_INDEX: { + int spacing = tileset->autotile_get_spacing(get_current_tile()); + Vector2 size = tileset->tile_get_region(get_current_tile()).size; + Vector2i cell_count = size / (tileset->autotile_get_size(get_current_tile()) + Vector2(spacing, spacing)); + cell_count -= Vector2(1, 1); + edited_shape_coord = cell_count; + _select_edited_shape_coord(); + } break; + default: { + } + } + } +} + +Array TileSetEditor::_get_tiles_in_current_texture(bool sorted) { + Array a; + List<int> all_tiles; + if (!get_current_texture().is_valid()) { + return a; + } + tileset->get_tile_list(&all_tiles); + for (int i = 0; i < all_tiles.size(); i++) { + if (tileset->tile_get_texture(all_tiles[i]) == get_current_texture()) { + a.push_back(all_tiles[i]); + } + } + if (sorted) { + a.sort_custom(this, "_sort_tiles"); + } + return a; +} + +bool TileSetEditor::_sort_tiles(Variant p_a, Variant p_b) { + int a = p_a; + int b = p_b; + + Vector2 pos_a = tileset->tile_get_region(a).position; + Vector2 pos_b = tileset->tile_get_region(b).position; + if (pos_a.y < pos_b.y) { + return true; + + } else if (pos_a.y == pos_b.y) { + if (pos_a.x < pos_b.x) { + return true; + } else { + return false; + } + } else { + return false; + } +} + +void TileSetEditor::_select_next_subtile() { + if (get_current_tile() == -1) { + _select_next_tile(); + return; + } + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) { + _select_next_tile(); + } else if (edit_mode == EDITMODE_REGION || edit_mode == EDITMODE_BITMASK || edit_mode == EDITMODE_ICON) { + _select_next_tile(); + } else { + int spacing = tileset->autotile_get_spacing(get_current_tile()); + Vector2 size = tileset->tile_get_region(get_current_tile()).size; + Vector2i cell_count = size / (tileset->autotile_get_size(get_current_tile()) + Vector2(spacing, spacing)); + if (edited_shape_coord.x >= cell_count.x - 1 && edited_shape_coord.y >= cell_count.y - 1) { + _select_next_tile(); + } else { + edited_shape_coord.x++; + if (edited_shape_coord.x >= cell_count.x) { + edited_shape_coord.x = 0; + edited_shape_coord.y++; + } + _select_edited_shape_coord(); + } + } +} + +void TileSetEditor::_select_previous_subtile() { + if (get_current_tile() == -1) { + _select_previous_tile(); + return; + } + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) { + _select_previous_tile(); + } else if (edit_mode == EDITMODE_REGION || edit_mode == EDITMODE_BITMASK || edit_mode == EDITMODE_ICON) { + _select_previous_tile(); + } else { + int spacing = tileset->autotile_get_spacing(get_current_tile()); + Vector2 size = tileset->tile_get_region(get_current_tile()).size; + Vector2i cell_count = size / (tileset->autotile_get_size(get_current_tile()) + Vector2(spacing, spacing)); + if (edited_shape_coord.x <= 0 && edited_shape_coord.y <= 0) { + _select_previous_tile(); + } else { + edited_shape_coord.x--; + if (edited_shape_coord.x == -1) { + edited_shape_coord.x = cell_count.x - 1; + edited_shape_coord.y--; + } + _select_edited_shape_coord(); + } + } +} + +void TileSetEditor::_select_next_shape() { + if (get_current_tile() == -1) { + _select_next_subtile(); + } else if (edit_mode != EDITMODE_COLLISION) { + _select_next_subtile(); + } else { + Vector2i edited_coord = Vector2(); + if (tileset->tile_get_tile_mode(get_current_tile()) != TileSet::SINGLE_TILE) { + edited_coord = edited_shape_coord; + } + SubtileData data = current_tile_data[edited_coord]; + if (data.collisions.size() == 0) { + _select_next_subtile(); + } else { + int index = data.collisions.find(edited_collision_shape); + if (index < 0) { + _set_edited_collision_shape(data.collisions[0]); + } else if (index == data.collisions.size() - 1) { + _select_next_subtile(); + } else { + _set_edited_collision_shape(data.collisions[index + 1]); + } + } + current_shape.resize(0); + Rect2 current_tile_region = tileset->tile_get_region(get_current_tile()); + current_tile_region.position += WORKSPACE_MARGIN; + + int spacing = tileset->autotile_get_spacing(get_current_tile()); + Vector2 size = tileset->autotile_get_size(get_current_tile()); + Vector2 shape_anchor = edited_shape_coord; + shape_anchor.x *= (size.x + spacing); + shape_anchor.y *= (size.y + spacing); + current_tile_region.position += shape_anchor; + + if (edited_collision_shape.is_valid()) { + for (int i = 0; i < _get_edited_shape_points().size(); i++) { + current_shape.push_back(_get_edited_shape_points()[i] + current_tile_region.position); + } + } + workspace->update(); + workspace_container->update(); + helper->_change_notify(""); + } +} + +void TileSetEditor::_select_previous_shape() { + if (get_current_tile() == -1) { + _select_previous_subtile(); + if (get_current_tile() != -1 && edit_mode == EDITMODE_COLLISION) { + SubtileData data = current_tile_data[Vector2i(edited_shape_coord)]; + if (data.collisions.size() > 1) { + _set_edited_collision_shape(data.collisions[data.collisions.size() - 1]); + } + } else { + return; + } + } else if (edit_mode != EDITMODE_COLLISION) { + _select_previous_subtile(); + } else { + Vector2i edited_coord = Vector2(); + if (tileset->tile_get_tile_mode(get_current_tile()) != TileSet::SINGLE_TILE) { + edited_coord = edited_shape_coord; + } + SubtileData data = current_tile_data[edited_coord]; + if (data.collisions.size() == 0) { + _select_previous_subtile(); + data = current_tile_data[Vector2i(edited_shape_coord)]; + if (data.collisions.size() > 1) { + _set_edited_collision_shape(data.collisions[data.collisions.size() - 1]); + } + } else { + int index = data.collisions.find(edited_collision_shape); + if (index < 0) { + _set_edited_collision_shape(data.collisions[data.collisions.size() - 1]); + } else if (index == 0) { + _select_previous_subtile(); + data = current_tile_data[Vector2i(edited_shape_coord)]; + if (data.collisions.size() > 1) { + _set_edited_collision_shape(data.collisions[data.collisions.size() - 1]); + } + } else { + _set_edited_collision_shape(data.collisions[index - 1]); + } + } + + current_shape.resize(0); + Rect2 current_tile_region = tileset->tile_get_region(get_current_tile()); + current_tile_region.position += WORKSPACE_MARGIN; + + int spacing = tileset->autotile_get_spacing(get_current_tile()); + Vector2 size = tileset->autotile_get_size(get_current_tile()); + Vector2 shape_anchor = edited_shape_coord; + shape_anchor.x *= (size.x + spacing); + shape_anchor.y *= (size.y + spacing); + current_tile_region.position += shape_anchor; + + if (edited_collision_shape.is_valid()) { + for (int i = 0; i < _get_edited_shape_points().size(); i++) { + current_shape.push_back(_get_edited_shape_points()[i] + current_tile_region.position); + } + } + workspace->update(); + workspace_container->update(); + helper->_change_notify(""); + } +} + +void TileSetEditor::_set_edited_collision_shape(const Ref<Shape2D> &p_shape) { + edited_collision_shape = p_shape; + _update_toggle_shape_button(); +} + void TileSetEditor::_set_snap_step(Vector2 p_val) { snap_step.x = CLAMP(p_val.x, 0, 256); snap_step.y = CLAMP(p_val.y, 0, 256); @@ -1504,6 +2201,63 @@ void TileSetEditor::_set_snap_sep(Vector2 p_val) { workspace->update(); } +void TileSetEditor::_validate_current_tile_id() { + if (get_current_tile() >= 0 && !tileset->has_tile(get_current_tile())) + set_current_tile(-1); +} + +void TileSetEditor::_select_edited_shape_coord() { + select_coord(edited_shape_coord); +} + +void TileSetEditor::_undo_tile_removal(int p_id) { + undo_redo->add_undo_method(tileset.ptr(), "create_tile", p_id); + undo_redo->add_undo_method(tileset.ptr(), "tile_set_name", p_id, tileset->tile_get_name(p_id)); + undo_redo->add_undo_method(tileset.ptr(), "tile_set_normal_map", p_id, tileset->tile_get_normal_map(p_id)); + undo_redo->add_undo_method(tileset.ptr(), "tile_set_texture_offset", p_id, tileset->tile_get_texture_offset(p_id)); + undo_redo->add_undo_method(tileset.ptr(), "tile_set_material", p_id, tileset->tile_get_material(p_id)); + undo_redo->add_undo_method(tileset.ptr(), "tile_set_modulate", p_id, tileset->tile_get_modulate(p_id)); + undo_redo->add_undo_method(tileset.ptr(), "tile_set_occluder_offset", p_id, tileset->tile_get_occluder_offset(p_id)); + undo_redo->add_undo_method(tileset.ptr(), "tile_set_navigation_polygon_offset", p_id, tileset->tile_get_navigation_polygon_offset(p_id)); + undo_redo->add_undo_method(tileset.ptr(), "tile_set_shape_offset", p_id, 0, tileset->tile_get_shape_offset(p_id, 0)); + undo_redo->add_undo_method(tileset.ptr(), "tile_set_shape_transform", p_id, 0, tileset->tile_get_shape_transform(p_id, 0)); + undo_redo->add_undo_method(tileset.ptr(), "tile_set_z_index", p_id, tileset->tile_get_z_index(p_id)); + undo_redo->add_undo_method(tileset.ptr(), "tile_set_texture", p_id, tileset->tile_get_texture(p_id)); + undo_redo->add_undo_method(tileset.ptr(), "tile_set_region", p_id, tileset->tile_get_region(p_id)); + // Necessary to get the version that returns a Array instead of a Vector. + undo_redo->add_undo_method(tileset.ptr(), "tile_set_shapes", p_id, tileset->call("tile_get_shapes", p_id)); + if (tileset->tile_get_tile_mode(p_id) == TileSet::SINGLE_TILE) { + undo_redo->add_undo_method(tileset.ptr(), "tile_set_light_occluder", p_id, tileset->tile_get_light_occluder(p_id)); + undo_redo->add_undo_method(tileset.ptr(), "tile_set_navigation_polygon", p_id, tileset->tile_get_navigation_polygon(p_id)); + } else { + Map<Vector2, Ref<OccluderPolygon2D> > oclusion_map = tileset->autotile_get_light_oclusion_map(p_id); + for (Map<Vector2, Ref<OccluderPolygon2D> >::Element *E = oclusion_map.front(); E; E = E->next()) { + undo_redo->add_undo_method(tileset.ptr(), "autotile_set_light_occluder", p_id, E->value(), E->key()); + } + Map<Vector2, Ref<NavigationPolygon> > navigation_map = tileset->autotile_get_navigation_map(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, 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); + for (Map<Vector2, int>::Element *E = priority_map.front(); E; E = E->next()) { + undo_redo->add_undo_method(tileset.ptr(), "autotile_set_subtile_priority", p_id, E->key(), E->value()); + } + undo_redo->add_undo_method(tileset.ptr(), "autotile_set_icon_coordinate", p_id, tileset->autotile_get_icon_coordinate(p_id)); + Map<Vector2, int> z_map = tileset->autotile_get_z_index_map(p_id); + for (Map<Vector2, int>::Element *E = z_map.front(); E; E = E->next()) { + undo_redo->add_undo_method(tileset.ptr(), "autotile_set_z_index", p_id, E->key(), E->value()); + } + undo_redo->add_undo_method(tileset.ptr(), "tile_set_tile_mode", p_id, tileset->tile_get_tile_mode(p_id)); + undo_redo->add_undo_method(tileset.ptr(), "autotile_set_size", p_id, tileset->autotile_get_size(p_id)); + undo_redo->add_undo_method(tileset.ptr(), "autotile_set_spacing", p_id, tileset->autotile_get_spacing(p_id)); + undo_redo->add_undo_method(tileset.ptr(), "autotile_set_bitmask_mode", p_id, tileset->autotile_get_bitmask_mode(p_id)); + } +} + void TileSetEditor::_zoom_in() { float scale = workspace->get_scale().x; if (scale < max_scale) { @@ -1514,7 +2268,6 @@ void TileSetEditor::_zoom_in() { } } void TileSetEditor::_zoom_out() { - float scale = workspace->get_scale().x; if (scale > min_scale) { scale /= scale_ratio; @@ -1726,38 +2479,56 @@ void TileSetEditor::draw_polygon_shapes() { } anchor += WORKSPACE_MARGIN; anchor += tileset->tile_get_region(t_id).position; - Ref<ConvexPolygonShape2D> shape = sd[i].shape; + Ref<Shape2D> shape = sd[i].shape; if (shape.is_valid()) { Color c_bg; Color c_border; + Ref<ConvexPolygonShape2D> convex = shape; + bool is_convex = convex.is_valid(); if ((tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE || coord == edited_shape_coord) && sd[i].shape == edited_collision_shape) { - c_bg = Color(0, 1, 1, 0.5); - c_border = Color(0, 1, 1); + if (is_convex) { + c_bg = Color(0, 1, 1, 0.5); + c_border = Color(0, 1, 1); + } else { + c_bg = Color(0.8, 0, 1, 0.5); + c_border = Color(0.8, 0, 1); + } } else { - c_bg = Color(0.9, 0.7, 0.07, 0.5); - c_border = Color(0.9, 0.7, 0.07, 1); + if (is_convex) { + c_bg = Color(0.9, 0.7, 0.07, 0.5); + c_border = Color(0.9, 0.7, 0.07, 1); + + } else { + c_bg = Color(0.9, 0.45, 0.075, 0.5); + c_border = Color(0.9, 0.45, 0.075); + } } Vector<Vector2> polygon; Vector<Color> colors; - if (shape == edited_collision_shape && current_shape.size() > 2) { + if (!creating_shape && shape == edited_collision_shape && current_shape.size() > 2) { for (int j = 0; j < current_shape.size(); j++) { polygon.push_back(current_shape[j]); colors.push_back(c_bg); } } else { - for (int j = 0; j < shape->get_points().size(); j++) { - polygon.push_back(shape->get_points()[j] + anchor); + for (int j = 0; j < _get_collision_shape_points(shape).size(); j++) { + polygon.push_back(_get_collision_shape_points(shape)[j] + anchor); 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) { - for (int j = 0; j < shape->get_points().size() - 1; j++) { - workspace->draw_line(shape->get_points()[j] + anchor, shape->get_points()[j + 1] + anchor, c_border, 1, true); + if (!creating_shape) { + for (int j = 0; j < polygon.size() - 1; j++) { + workspace->draw_line(polygon[j], polygon[j + 1], c_border, 1, true); + } + workspace->draw_line(polygon[polygon.size() - 1], polygon[0], c_border, 1, true); } - if (shape == edited_collision_shape) { draw_handles = true; } @@ -1776,16 +2547,27 @@ void TileSetEditor::draw_polygon_shapes() { Vector<Color> colors; Vector2 anchor = WORKSPACE_MARGIN; anchor += tileset->tile_get_region(get_current_tile()).position; - for (int j = 0; j < shape->get_polygon().size(); j++) { - polygon.push_back(shape->get_polygon()[j] + anchor); - colors.push_back(c_bg); + if (!creating_shape && shape == edited_occlusion_shape && current_shape.size() > 2) { + for (int j = 0; j < current_shape.size(); j++) { + polygon.push_back(current_shape[j]); + colors.push_back(c_bg); + } + } else { + for (int j = 0; j < shape->get_polygon().size(); j++) { + polygon.push_back(shape->get_polygon()[j] + anchor); + colors.push_back(c_bg); + } } workspace->draw_polygon(polygon, colors); - for (int j = 0; j < shape->get_polygon().size() - 1; j++) { - workspace->draw_line(shape->get_polygon()[j] + anchor, shape->get_polygon()[j + 1] + anchor, c_border, 1, true); + if (!creating_shape) { + if (polygon.size() > 1) { + for (int j = 0; j < polygon.size() - 1; j++) { + workspace->draw_line(polygon[j], polygon[j + 1], c_border, 1, true); + } + workspace->draw_line(polygon[polygon.size() - 1], polygon[0], c_border, 1, true); + } } - workspace->draw_line(shape->get_polygon()[shape->get_polygon().size() - 1] + anchor, shape->get_polygon()[0] + anchor, c_border, 1, true); if (shape == edited_occlusion_shape) { draw_handles = true; } @@ -1814,7 +2596,7 @@ void TileSetEditor::draw_polygon_shapes() { } Vector<Vector2> polygon; Vector<Color> colors; - if (shape == edited_occlusion_shape && current_shape.size() > 2) { + if (!creating_shape && shape == edited_occlusion_shape && current_shape.size() > 2) { for (int j = 0; j < current_shape.size(); j++) { polygon.push_back(current_shape[j]); colors.push_back(c_bg); @@ -1826,11 +2608,14 @@ void TileSetEditor::draw_polygon_shapes() { } } workspace->draw_polygon(polygon, colors); + if (coord == edited_shape_coord) { - for (int j = 0; j < shape->get_polygon().size() - 1; j++) { - workspace->draw_line(shape->get_polygon()[j] + anchor, shape->get_polygon()[j + 1] + anchor, c_border, 1, true); + if (!creating_shape) { + for (int j = 0; j < polygon.size() - 1; j++) { + workspace->draw_line(polygon[j], polygon[j + 1], c_border, 1, true); + } + workspace->draw_line(polygon[polygon.size() - 1], polygon[0], c_border, 1, true); } - workspace->draw_line(shape->get_polygon()[shape->get_polygon().size() - 1] + anchor, shape->get_polygon()[0] + anchor, c_border, 1, true); if (shape == edited_occlusion_shape) { draw_handles = true; } @@ -1851,24 +2636,30 @@ void TileSetEditor::draw_polygon_shapes() { Vector<Color> colors; Vector2 anchor = WORKSPACE_MARGIN; anchor += tileset->tile_get_region(get_current_tile()).position; - PoolVector<Vector2> vertices = shape->get_vertices(); - for (int j = 0; j < shape->get_polygon(0).size(); j++) { - polygon.push_back(vertices[shape->get_polygon(0)[j]] + anchor); - colors.push_back(c_bg); + if (!creating_shape && shape == edited_navigation_shape && current_shape.size() > 2) { + for (int j = 0; j < current_shape.size(); j++) { + polygon.push_back(current_shape[j]); + colors.push_back(c_bg); + } + } else { + PoolVector<Vector2> vertices = shape->get_vertices(); + for (int j = 0; j < shape->get_polygon(0).size(); j++) { + polygon.push_back(vertices[shape->get_polygon(0)[j]] + anchor); + colors.push_back(c_bg); + } } workspace->draw_polygon(polygon, colors); - if (shape->get_polygon_count() > 0) { - PoolVector<Vector2> vertices = shape->get_vertices(); - for (int j = 0; j < shape->get_polygon(0).size() - 1; j++) { - workspace->draw_line(vertices[shape->get_polygon(0)[j]] + anchor, vertices[shape->get_polygon(0)[j + 1]] + anchor, c_border, 1, true); - } - if (shape == edited_navigation_shape) { - draw_handles = true; + if (!creating_shape) { + for (int j = 0; j < polygon.size() - 1; j++) { + workspace->draw_line(polygon[j], polygon[j + 1], c_border, 1, true); } + workspace->draw_line(polygon[polygon.size() - 1], polygon[0], c_border, 1, true); + } + if (shape == edited_navigation_shape) { + draw_handles = true; } } - } else { Map<Vector2, Ref<NavigationPolygon> > map = tileset->autotile_get_navigation_map(t_id); for (Map<Vector2, Ref<NavigationPolygon> >::Element *E = map.front(); E; E = E->next()) { @@ -1893,12 +2684,12 @@ void TileSetEditor::draw_polygon_shapes() { } Vector<Vector2> polygon; Vector<Color> colors; - if (shape == edited_navigation_shape && current_shape.size() > 2) { + if (!creating_shape && shape == edited_navigation_shape && current_shape.size() > 2) { for (int j = 0; j < current_shape.size(); j++) { polygon.push_back(current_shape[j]); colors.push_back(c_bg); } - } else if (shape->get_polygon_count() > 0) { + } else { PoolVector<Vector2> vertices = shape->get_vertices(); for (int j = 0; j < shape->get_polygon(0).size(); j++) { polygon.push_back(vertices[shape->get_polygon(0)[j]] + anchor); @@ -1906,28 +2697,32 @@ void TileSetEditor::draw_polygon_shapes() { } } workspace->draw_polygon(polygon, colors); + if (coord == edited_shape_coord) { - if (shape->get_polygon_count() > 0) { - PoolVector<Vector2> vertices = shape->get_vertices(); - for (int j = 0; j < shape->get_polygon(0).size() - 1; j++) { - workspace->draw_line(vertices[shape->get_polygon(0)[j]] + anchor, vertices[shape->get_polygon(0)[j + 1]] + anchor, c_border, 1, true); - } - if (shape == edited_navigation_shape) { - draw_handles = true; + if (!creating_shape) { + for (int j = 0; j < polygon.size() - 1; j++) { + workspace->draw_line(polygon[j], polygon[j + 1], c_border, 1, true); } + workspace->draw_line(polygon[polygon.size() - 1], polygon[0], c_border, 1, true); + } + if (shape == edited_navigation_shape) { + draw_handles = true; } } } } } } break; - default: {} + default: { + } } + if (creating_shape) { for (int j = 0; j < current_shape.size() - 1; j++) { workspace->draw_line(current_shape[j], current_shape[j + 1], Color(0, 1, 1), 1, true); } workspace->draw_line(current_shape[current_shape.size() - 1], snap_point(workspace->get_local_mouse_position()), Color(0, 1, 1), 1, true); + draw_handles = true; } } @@ -1939,11 +2734,11 @@ void TileSetEditor::close_shape(const Vector2 &shape_anchor) { if (current_shape.size() >= 3) { Ref<ConvexPolygonShape2D> shape = memnew(ConvexPolygonShape2D); - Vector<Vector2> segments; + Vector<Vector2> points; float p_total = 0; for (int i = 0; i < current_shape.size(); i++) { - segments.push_back(current_shape[i] - shape_anchor); + points.push_back(current_shape[i] - shape_anchor); if (i != current_shape.size() - 1) p_total += ((current_shape[i + 1].x - current_shape[i].x) * (-current_shape[i + 1].y + (-current_shape[i].y))); @@ -1952,20 +2747,33 @@ void TileSetEditor::close_shape(const Vector2 &shape_anchor) { } if (p_total < 0) - segments.invert(); + points.invert(); - shape->set_points(segments); + shape->set_points(points); + undo_redo->create_action(TTR("Create Collision Polygon")); + // Necessary to get the version that returns a Array instead of a Vector. + Array sd = tileset->call("tile_get_shapes", get_current_tile()); + undo_redo->add_undo_method(tileset.ptr(), "tile_set_shapes", get_current_tile(), sd.duplicate()); + for (int i = 0; i < sd.size(); i++) { + if (sd[i].get("shape") == edited_collision_shape) { + sd.remove(i); + break; + } + } + undo_redo->add_do_method(tileset.ptr(), "tile_set_shapes", get_current_tile(), sd); if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) - tileset->tile_add_shape(get_current_tile(), shape, Transform2D(), false, edited_shape_coord); + undo_redo->add_do_method(tileset.ptr(), "tile_add_shape", get_current_tile(), shape, Transform2D(), false, edited_shape_coord); else - tileset->tile_add_shape(get_current_tile(), shape, Transform2D()); - - edited_collision_shape = shape; + undo_redo->add_do_method(tileset.ptr(), "tile_add_shape", get_current_tile(), shape, Transform2D()); + tools[TOOL_SELECT]->set_pressed(true); + undo_redo->add_do_method(this, "_select_edited_shape_coord"); + undo_redo->add_undo_method(this, "_select_edited_shape_coord"); + undo_redo->commit_action(); + } else { + tools[TOOL_SELECT]->set_pressed(true); + workspace->update(); } - - tools[TOOL_SELECT]->set_pressed(true); - workspace->update(); } else if (edit_mode == EDITMODE_OCCLUSION) { Ref<OccluderPolygon2D> shape = memnew(OccluderPolygon2D); @@ -1980,13 +2788,18 @@ void TileSetEditor::close_shape(const Vector2 &shape_anchor) { w = PoolVector<Vector2>::Write(); shape->set_polygon(polygon); - if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) - tileset->autotile_set_light_occluder(get_current_tile(), shape, edited_shape_coord); - else - tileset->tile_set_light_occluder(get_current_tile(), shape); - edited_occlusion_shape = shape; + undo_redo->create_action(TTR("Create Occlusion Polygon")); + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) { + undo_redo->add_do_method(tileset.ptr(), "autotile_set_light_occluder", get_current_tile(), shape, edited_shape_coord); + undo_redo->add_undo_method(tileset.ptr(), "autotile_set_light_occluder", get_current_tile(), tileset->autotile_get_light_occluder(get_current_tile(), edited_shape_coord), edited_shape_coord); + } else { + undo_redo->add_do_method(tileset.ptr(), "tile_set_light_occluder", get_current_tile(), shape); + undo_redo->add_undo_method(tileset.ptr(), "tile_set_light_occluder", get_current_tile(), tileset->tile_get_light_occluder(get_current_tile())); + } tools[TOOL_SELECT]->set_pressed(true); - workspace->update(); + undo_redo->add_do_method(this, "_select_edited_shape_coord"); + undo_redo->add_undo_method(this, "_select_edited_shape_coord"); + undo_redo->commit_action(); } else if (edit_mode == EDITMODE_NAVIGATION) { Ref<NavigationPolygon> shape = memnew(NavigationPolygon); @@ -2004,18 +2817,24 @@ void TileSetEditor::close_shape(const Vector2 &shape_anchor) { shape->set_vertices(polygon); shape->add_polygon(indices); - if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) - tileset->autotile_set_navigation_polygon(get_current_tile(), shape, edited_shape_coord); - else - tileset->tile_set_navigation_polygon(get_current_tile(), shape); - edited_navigation_shape = shape; + undo_redo->create_action(TTR("Create Navigation Polygon")); + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) { + undo_redo->add_do_method(tileset.ptr(), "autotile_set_navigation_polygon", get_current_tile(), shape, edited_shape_coord); + undo_redo->add_undo_method(tileset.ptr(), "autotile_set_navigation_polygon", get_current_tile(), tileset->autotile_get_navigation_polygon(get_current_tile(), edited_shape_coord), edited_shape_coord); + } else { + undo_redo->add_do_method(tileset.ptr(), "tile_set_navigation_polygon", get_current_tile(), shape); + undo_redo->add_undo_method(tileset.ptr(), "tile_set_navigation_polygon", get_current_tile(), tileset->tile_get_navigation_polygon(get_current_tile())); + } tools[TOOL_SELECT]->set_pressed(true); - workspace->update(); + undo_redo->add_do_method(this, "_select_edited_shape_coord"); + undo_redo->add_undo_method(this, "_select_edited_shape_coord"); + undo_redo->commit_action(); } tileset->_change_notify(""); } void TileSetEditor::select_coord(const Vector2 &coord) { + _update_tile_data(); current_shape = PoolVector2Array(); if (get_current_tile() == -1) return; @@ -2023,7 +2842,7 @@ void TileSetEditor::select_coord(const Vector2 &coord) { current_tile_region.position += WORKSPACE_MARGIN; if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) { if (edited_collision_shape != tileset->tile_get_shape(get_current_tile(), 0)) - edited_collision_shape = tileset->tile_get_shape(get_current_tile(), 0); + _set_edited_collision_shape(tileset->tile_get_shape(get_current_tile(), 0)); if (edited_occlusion_shape != tileset->tile_get_light_occluder(get_current_tile())) edited_occlusion_shape = tileset->tile_get_light_occluder(get_current_tile()); if (edited_navigation_shape != tileset->tile_get_navigation_polygon(get_current_tile())) @@ -2032,8 +2851,8 @@ void TileSetEditor::select_coord(const Vector2 &coord) { if (edit_mode == EDITMODE_COLLISION) { current_shape.resize(0); if (edited_collision_shape.is_valid()) { - for (int i = 0; i < edited_collision_shape->get_points().size(); i++) { - current_shape.push_back(edited_collision_shape->get_points()[i] + current_tile_region.position); + for (int i = 0; i < _get_edited_shape_points().size(); i++) { + current_shape.push_back(_get_edited_shape_points()[i] + current_tile_region.position); } } } else if (edit_mode == EDITMODE_OCCLUSION) { @@ -2055,6 +2874,23 @@ void TileSetEditor::select_coord(const Vector2 &coord) { } } } else { + Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(get_current_tile()); + bool found_collision_shape = false; + for (int i = 0; i < sd.size(); i++) { + if (sd[i].autotile_coord == coord) { + if (edited_collision_shape != sd[i].shape) + _set_edited_collision_shape(sd[i].shape); + found_collision_shape = true; + break; + } + } + if (!found_collision_shape) + _set_edited_collision_shape(Ref<ConvexPolygonShape2D>(NULL)); + if (edited_occlusion_shape != tileset->autotile_get_light_occluder(get_current_tile(), coord)) + edited_occlusion_shape = tileset->autotile_get_light_occluder(get_current_tile(), coord); + if (edited_navigation_shape != tileset->autotile_get_navigation_polygon(get_current_tile(), coord)) + edited_navigation_shape = tileset->autotile_get_navigation_polygon(get_current_tile(), coord); + int spacing = tileset->autotile_get_spacing(get_current_tile()); Vector2 size = tileset->autotile_get_size(get_current_tile()); Vector2 shape_anchor = coord; @@ -2064,8 +2900,8 @@ void TileSetEditor::select_coord(const Vector2 &coord) { if (edit_mode == EDITMODE_COLLISION) { current_shape.resize(0); if (edited_collision_shape.is_valid()) { - for (int j = 0; j < edited_collision_shape->get_points().size(); j++) { - current_shape.push_back(edited_collision_shape->get_points()[j] + shape_anchor); + for (int j = 0; j < _get_edited_shape_points().size(); j++) { + current_shape.push_back(_get_edited_shape_points()[j] + shape_anchor); } } } else if (edit_mode == EDITMODE_OCCLUSION) { @@ -2125,6 +2961,24 @@ Vector2 TileSetEditor::snap_point(const Vector2 &point) { return p; } +void TileSetEditor::add_texture(Ref<Texture> p_texture) { + texture_list->add_item(p_texture->get_path().get_file()); + texture_map.insert(p_texture->get_rid(), p_texture); + texture_list->set_item_metadata(texture_list->get_item_count() - 1, p_texture->get_rid()); +} + +void TileSetEditor::remove_texture(Ref<Texture> p_texture) { + texture_list->remove_item(texture_list->find_metadata(p_texture->get_rid())); + texture_map.erase(p_texture->get_rid()); + + _validate_current_tile_id(); + + if (!get_current_texture().is_valid()) { + _on_texture_list_selected(-1); + workspace_overlay->update(); + } +} + void TileSetEditor::update_texture_list() { Ref<Texture> selected_texture = get_current_texture(); @@ -2141,9 +2995,7 @@ void TileSetEditor::update_texture_list() { } if (!texture_map.has(tileset->tile_get_texture(E->get())->get_rid())) { - texture_list->add_item(tileset->tile_get_texture(E->get())->get_path().get_file()); - texture_map.insert(tileset->tile_get_texture(E->get())->get_rid(), tileset->tile_get_texture(E->get())); - texture_list->set_item_metadata(texture_list->get_item_count() - 1, tileset->tile_get_texture(E->get())->get_rid()); + add_texture(tileset->tile_get_texture(E->get())); } } for (int i = 0; i < ids_to_remove.size(); i++) { @@ -2157,7 +3009,9 @@ void TileSetEditor::update_texture_list() { } else if (get_current_texture().is_valid()) { _on_texture_list_selected(texture_list->find_metadata(get_current_texture()->get_rid())); } else { + _validate_current_tile_id(); _on_texture_list_selected(-1); + workspace_overlay->update(); } update_texture_list_icon(); helper->_change_notify(""); @@ -2168,7 +3022,8 @@ void TileSetEditor::update_texture_list_icon() { for (int current_idx = 0; current_idx < texture_list->get_item_count(); current_idx++) { RID rid = texture_list->get_item_metadata(current_idx); texture_list->set_item_icon(current_idx, texture_map[rid]); - texture_list->set_item_icon_region(current_idx, Rect2(0, 0, 150, 100)); + Size2 texture_size = texture_map[rid]->get_size(); + texture_list->set_item_icon_region(current_idx, Rect2(0, 0, MIN(texture_size.x, 150), MIN(texture_size.y, 100))); } texture_list->update(); } @@ -2181,10 +3036,14 @@ void TileSetEditor::update_workspace_tile_mode() { for (int i = 1; i < WORKSPACE_MODE_MAX; i++) { tool_workspacemode[i]->set_disabled(true); } + tools[SELECT_NEXT]->set_disabled(true); + tools[SELECT_PREVIOUS]->set_disabled(true); } else { for (int i = 1; i < WORKSPACE_MODE_MAX; i++) { tool_workspacemode[i]->set_disabled(false); } + tools[SELECT_NEXT]->set_disabled(false); + tools[SELECT_PREVIOUS]->set_disabled(false); } if (workspace_mode != WORKSPACE_EDIT) { @@ -2202,7 +3061,7 @@ void TileSetEditor::update_workspace_tile_mode() { for (int i = 0; i < EDITMODE_MAX; i++) { tool_editmode[i]->hide(); } - for (int i = 0; i < ZOOM_OUT; i++) { + for (int i = TOOL_SELECT; i < ZOOM_OUT; i++) { tools[i]->hide(); } @@ -2219,7 +3078,7 @@ void TileSetEditor::update_workspace_tile_mode() { separator_editmode->show(); if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) { - if (tool_editmode[EDITMODE_ICON]->is_pressed() || tool_editmode[EDITMODE_PRIORITY]->is_pressed() || tool_editmode[EDITMODE_BITMASK]->is_pressed()) { + if (tool_editmode[EDITMODE_ICON]->is_pressed() || tool_editmode[EDITMODE_PRIORITY]->is_pressed() || tool_editmode[EDITMODE_BITMASK]->is_pressed() || tool_editmode[EDITMODE_Z_INDEX]->is_pressed()) { tool_editmode[EDITMODE_COLLISION]->set_pressed(true); edit_mode = EDITMODE_COLLISION; } @@ -2228,11 +3087,12 @@ void TileSetEditor::update_workspace_tile_mode() { tool_editmode[EDITMODE_ICON]->hide(); tool_editmode[EDITMODE_BITMASK]->hide(); tool_editmode[EDITMODE_PRIORITY]->hide(); - } else if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) { + tool_editmode[EDITMODE_Z_INDEX]->hide(); + } else if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) { if (edit_mode == EDITMODE_ICON) select_coord(tileset->autotile_get_icon_coordinate(get_current_tile())); else - select_coord(edited_shape_coord); + _select_edited_shape_coord(); } else if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) { if (tool_editmode[EDITMODE_PRIORITY]->is_pressed() || tool_editmode[EDITMODE_BITMASK]->is_pressed()) { tool_editmode[EDITMODE_COLLISION]->set_pressed(true); @@ -2241,7 +3101,7 @@ void TileSetEditor::update_workspace_tile_mode() { if (edit_mode == EDITMODE_ICON) select_coord(tileset->autotile_get_icon_coordinate(get_current_tile())); else - select_coord(edited_shape_coord); + _select_edited_shape_coord(); tool_editmode[EDITMODE_BITMASK]->hide(); tool_editmode[EDITMODE_PRIORITY]->hide(); @@ -2249,6 +3109,26 @@ void TileSetEditor::update_workspace_tile_mode() { _on_edit_mode_changed(edit_mode); } +void TileSetEditor::update_workspace_minsize() { + Size2 workspace_min_size = get_current_texture()->get_size(); + RID current_texture_rid = get_current_texture()->get_rid(); + List<int> *tiles = new List<int>(); + tileset->get_tile_list(tiles); + for (List<int>::Element *E = tiles->front(); E; E = E->next()) { + if (tileset->tile_get_texture(E->get())->get_rid() == current_texture_rid) { + Rect2i region = tileset->tile_get_region(E->get()); + if (region.position.x + region.size.x > workspace_min_size.x) + workspace_min_size.x = region.position.x + region.size.x; + if (region.position.y + region.size.y > workspace_min_size.y) + workspace_min_size.y = region.position.y + region.size.y; + } + } + + workspace->set_custom_minimum_size(workspace_min_size + WORKSPACE_MARGIN * 2); + workspace_container->set_custom_minimum_size(workspace_min_size + WORKSPACE_MARGIN * 2); + workspace_overlay->set_custom_minimum_size(workspace_min_size + WORKSPACE_MARGIN * 2); +} + void TileSetEditor::update_edited_region(const Vector2 &end_point) { edited_region = Rect2(region_from, Size2()); if (tools[TOOL_GRID_SNAP]->is_pressed()) { @@ -2257,14 +3137,14 @@ void TileSetEditor::update_edited_region(const Vector2 &end_point) { grid_coord *= (snap_step + snap_separation); grid_coord += snap_offset; edited_region.expand_to(grid_coord); - grid_coord += snap_step + snap_separation; + grid_coord += snap_step; edited_region.expand_to(grid_coord); grid_coord = ((end_point - snap_offset) / (snap_step + snap_separation)).floor(); grid_coord *= (snap_step + snap_separation); grid_coord += snap_offset; edited_region.expand_to(grid_coord); - grid_coord += snap_step + snap_separation; + grid_coord += snap_step; edited_region.expand_to(grid_coord); } else { edited_region.expand_to(end_point); @@ -2281,6 +3161,11 @@ void TileSetEditor::set_current_tile(int p_id) { helper->_change_notify(""); select_coord(Vector2(0, 0)); update_workspace_tile_mode(); + if (p_id == -1) { + editor->get_inspector()->edit(tileset.ptr()); + } else { + editor->get_inspector()->edit(helper); + } } } @@ -2318,20 +3203,20 @@ bool TilesetEditorContext::_set(const StringName &p_name, const Variant &p_value tileset_editor->_set_snap_sep(snap); return true; } else if (p_name.operator String().left(5) == "tile_") { - String name = p_name.operator String().right(5); + String name2 = p_name.operator String().right(5); bool v = false; if (tileset_editor->get_current_tile() < 0 || tileset.is_null()) return false; - if (name == "autotile_bitmask_mode") { + if (name2 == "autotile_bitmask_mode") { tileset->set(String::num(tileset_editor->get_current_tile(), 0) + "/autotile/bitmask_mode", p_value, &v); - } else if (name == "subtile_size") { + } else if (name2 == "subtile_size") { tileset->set(String::num(tileset_editor->get_current_tile(), 0) + "/autotile/tile_size", p_value, &v); - } else if (name == "subtile_spacing") { + } else if (name2 == "subtile_spacing") { tileset->set(String::num(tileset_editor->get_current_tile(), 0) + "/autotile/spacing", p_value, &v); } else { - tileset->set(String::num(tileset_editor->get_current_tile(), 0) + "/" + name, p_value, &v); + tileset->set(String::num(tileset_editor->get_current_tile(), 0) + "/" + name2, p_value, &v); } if (v) { tileset->_change_notify(""); @@ -2342,6 +3227,24 @@ bool TilesetEditorContext::_set(const StringName &p_name, const Variant &p_value } else if (name == "tileset_script") { tileset->set_script(p_value); return true; + } else if (name == "selected_collision_one_way") { + Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(tileset_editor->get_current_tile()); + for (int index = 0; index < sd.size(); index++) { + if (sd[index].shape == tileset_editor->edited_collision_shape) { + tileset->tile_set_shape_one_way(tileset_editor->get_current_tile(), index, p_value); + return true; + } + } + return false; + } else if (name == "selected_collision_one_way_margin") { + Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(tileset_editor->get_current_tile()); + for (int index = 0; index < sd.size(); index++) { + if (sd[index].shape == tileset_editor->edited_collision_shape) { + tileset->tile_set_shape_one_way_margin(tileset_editor->get_current_tile(), index, p_value); + return true; + } + } + return false; } tileset_editor->err_dialog->set_text(TTR("This property can't be changed.")); @@ -2384,6 +3287,24 @@ bool TilesetEditorContext::_get(const StringName &p_name, Variant &r_ret) const } else if (name == "selected_collision") { r_ret = tileset_editor->edited_collision_shape; v = true; + } else if (name == "selected_collision_one_way") { + Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(tileset_editor->get_current_tile()); + for (int index = 0; index < sd.size(); index++) { + if (sd[index].shape == tileset_editor->edited_collision_shape) { + r_ret = sd[index].one_way_collision; + v = true; + break; + } + } + } else if (name == "selected_collision_one_way_margin") { + Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(tileset_editor->get_current_tile()); + for (int index = 0; index < sd.size(); index++) { + if (sd[index].shape == tileset_editor->edited_collision_shape) { + r_ret = sd[index].one_way_collision_margin; + v = true; + break; + } + } } else if (name == "selected_navigation") { r_ret = tileset_editor->edited_navigation_shape; v = true; @@ -2430,6 +3351,10 @@ void TilesetEditorContext::_get_property_list(List<PropertyInfo> *p_list) const } if (tileset_editor->edit_mode == TileSetEditor::EDITMODE_COLLISION && tileset_editor->edited_collision_shape.is_valid()) { p_list->push_back(PropertyInfo(Variant::OBJECT, "selected_collision", PROPERTY_HINT_RESOURCE_TYPE, tileset_editor->edited_collision_shape->get_class())); + if (tileset_editor->edited_collision_shape.is_valid()) { + p_list->push_back(PropertyInfo(Variant::BOOL, "selected_collision_one_way", PROPERTY_HINT_NONE)); + p_list->push_back(PropertyInfo(Variant::REAL, "selected_collision_one_way_margin", PROPERTY_HINT_NONE)); + } } if (tileset_editor->edit_mode == TileSetEditor::EDITMODE_NAVIGATION && tileset_editor->edited_navigation_shape.is_valid()) { p_list->push_back(PropertyInfo(Variant::OBJECT, "selected_navigation", PROPERTY_HINT_RESOURCE_TYPE, tileset_editor->edited_navigation_shape->get_class())); @@ -2450,13 +3375,13 @@ void TilesetEditorContext::_bind_methods() { TilesetEditorContext::TilesetEditorContext(TileSetEditor *p_tileset_editor) { tileset_editor = p_tileset_editor; + snap_options_visible = false; } void TileSetEditorPlugin::edit(Object *p_node) { if (Object::cast_to<TileSet>(p_node)) { tileset_editor->edit(Object::cast_to<TileSet>(p_node)); - editor->get_inspector()->edit(tileset_editor->helper); } } @@ -2485,6 +3410,7 @@ Dictionary TileSetEditorPlugin::get_state() const { state["snap_separation"] = tileset_editor->snap_separation; state["snap_enabled"] = tileset_editor->tools[TileSetEditor::TOOL_GRID_SNAP]->is_pressed(); state["keep_inside_tile"] = tileset_editor->tools[TileSetEditor::SHAPE_KEEP_INSIDE_TILE]->is_pressed(); + state["show_information"] = tileset_editor->tools[TileSetEditor::VISIBLE_INFO]->is_pressed(); return state; } @@ -2505,11 +3431,18 @@ void TileSetEditorPlugin::set_state(const Dictionary &p_state) { if (state.has("snap_enabled")) { tileset_editor->tools[TileSetEditor::TOOL_GRID_SNAP]->set_pressed(state["snap_enabled"]); + if (tileset_editor->helper) { + tileset_editor->_on_grid_snap_toggled(state["snap_enabled"]); + } } if (state.has("keep_inside_tile")) { tileset_editor->tools[TileSetEditor::SHAPE_KEEP_INSIDE_TILE]->set_pressed(state["keep_inside_tile"]); } + + if (state.has("show_information")) { + tileset_editor->tools[TileSetEditor::VISIBLE_INFO]->set_pressed(state["show_information"]); + } } TileSetEditorPlugin::TileSetEditorPlugin(EditorNode *p_node) { diff --git a/editor/plugins/tile_set_editor_plugin.h b/editor/plugins/tile_set_editor_plugin.h index e713818c70..1176e1bb92 100644 --- a/editor/plugins/tile_set_editor_plugin.h +++ b/editor/plugins/tile_set_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -34,6 +34,7 @@ #include "editor/editor_name_dialog.h" #include "editor/editor_node.h" #include "scene/2d/sprite.h" +#include "scene/resources/concave_polygon_shape_2d.h" #include "scene/resources/convex_polygon_shape_2d.h" #include "scene/resources/tile_set.h" @@ -71,15 +72,20 @@ class TileSetEditor : public HSplitContainer { EDITMODE_BITMASK, EDITMODE_PRIORITY, EDITMODE_ICON, + EDITMODE_Z_INDEX, EDITMODE_MAX }; enum TileSetTools { + SELECT_PREVIOUS, + SELECT_NEXT, TOOL_SELECT, BITMASK_COPY, BITMASK_PASTE, BITMASK_CLEAR, SHAPE_NEW_POLYGON, + SHAPE_NEW_RECTANGLE, + SHAPE_TOGGLE_TYPE, SHAPE_DELETE, SHAPE_KEEP_INSIDE_TILE, TOOL_GRID_SNAP, @@ -90,9 +96,16 @@ class TileSetEditor : public HSplitContainer { TOOL_MAX }; + struct SubtileData { + Array collisions; + Ref<OccluderPolygon2D> occlusion_shape; + Ref<NavigationPolygon> navigation_shape; + }; + Ref<TileSet> tileset; TilesetEditorContext *helper; EditorNode *editor; + UndoRedo *undo_redo; ConfirmationDialog *cd; AcceptDialog *err_dialog; @@ -106,19 +119,20 @@ class TileSetEditor : public HSplitContainer { bool creating_shape; int dragging_point; - float tile_names_opacity; + bool tile_names_visible; Vector2 region_from; Rect2 edited_region; bool draw_edited_region; Vector2 edited_shape_coord; PoolVector2Array current_shape; - Map<Vector2, uint16_t> bitmask_map_copy; + Map<Vector2i, SubtileData> current_tile_data; + Map<Vector2, uint32_t> bitmask_map_copy; Vector2 snap_step; Vector2 snap_offset; Vector2 snap_separation; - Ref<ConvexPolygonShape2D> edited_collision_shape; + Ref<Shape2D> edited_collision_shape; Ref<OccluderPolygon2D> edited_occlusion_shape; Ref<NavigationPolygon> edited_navigation_shape; @@ -134,10 +148,12 @@ class TileSetEditor : public HSplitContainer { HSeparator *separator_editmode; HBoxContainer *toolbar; ToolButton *tools[TOOL_MAX]; + VSeparator *separator_shape_toggle; VSeparator *separator_bitmask; VSeparator *separator_delete; VSeparator *separator_grid; SpinBox *spin_priority; + SpinBox *spin_z_index; WorkspaceMode workspace_mode; EditMode edit_mode; int current_tile; @@ -149,10 +165,14 @@ class TileSetEditor : public HSplitContainer { void update_texture_list(); void update_texture_list_icon(); + void add_texture(Ref<Texture> p_texture); + void remove_texture(Ref<Texture> p_texture); + Ref<Texture> get_current_texture(); static void _import_node(Node *p_node, Ref<TileSet> p_library); static void _import_scene(Node *p_scene, Ref<TileSet> p_library, bool p_merge); + void _undo_redo_import_scene(Node *p_scene, bool p_merge); protected: static void _bind_methods(); @@ -178,11 +198,30 @@ private: void _on_workspace_input(const Ref<InputEvent> &p_ie); void _on_tool_clicked(int p_tool); void _on_priority_changed(float val); + void _on_z_index_changed(float val); void _on_grid_snap_toggled(bool p_val); + Vector<Vector2> _get_collision_shape_points(const Ref<Shape2D> &p_shape); + Vector<Vector2> _get_edited_shape_points(); + void _set_edited_shape_points(const Vector<Vector2> points); + void _update_tile_data(); + void _update_toggle_shape_button(); + void _select_next_tile(); + void _select_previous_tile(); + Array _get_tiles_in_current_texture(bool sorted = false); + bool _sort_tiles(Variant p_a, Variant p_b); + void _select_next_subtile(); + void _select_previous_subtile(); + void _select_next_shape(); + void _select_previous_shape(); + void _set_edited_collision_shape(const Ref<Shape2D> &p_shape); void _set_snap_step(Vector2 p_val); void _set_snap_off(Vector2 p_val); void _set_snap_sep(Vector2 p_val); + void _validate_current_tile_id(); + void _select_edited_shape_coord(); + void _undo_tile_removal(int p_id); + void _zoom_in(); void _zoom_out(); void _zoom_reset(); @@ -197,6 +236,7 @@ private: void select_coord(const Vector2 &coord); Vector2 snap_point(const Vector2 &point); void update_workspace_tile_mode(); + void update_workspace_minsize(); void update_edited_region(const Vector2 &end_point); int get_current_tile() const; diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index b084fe1915..a1b903576e 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -31,6 +31,7 @@ #include "visual_shader_editor_plugin.h" #include "core/io/resource_loader.h" +#include "core/math/math_defs.h" #include "core/os/input.h" #include "core/os/keyboard.h" #include "core/project_settings.h" @@ -39,6 +40,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,8 +59,17 @@ void VisualShaderNodePlugin::_bind_methods() { void VisualShaderEditor::edit(VisualShader *p_visual_shader) { + bool changed = false; if (p_visual_shader) { + if (visual_shader.is_null()) { + changed = true; + } else { + if (visual_shader.ptr() != p_visual_shader) { + changed = true; + } + } visual_shader = Ref<VisualShader>(p_visual_shader); + visual_shader->set_graph_offset(graph->get_scroll_ofs() / EDSCALE); } else { visual_shader.unref(); } @@ -66,6 +77,9 @@ void VisualShaderEditor::edit(VisualShader *p_visual_shader) { if (visual_shader.is_null()) { hide(); } else { + if (changed) { // to avoid tree collapse + _update_options_menu(); + } _update_graph(); } } @@ -107,16 +121,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 +329,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) @@ -160,15 +360,18 @@ void VisualShaderEditor::_update_graph() { for (int i = 0; i < graph->get_child_count(); i++) { if (Object::cast_to<GraphNode>(graph->get_child(i))) { - memdelete(graph->get_child(i)); + Node *node = graph->get_child(i); + graph->remove_child(node); + memdelete(node); i--; } } - 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; @@ -178,13 +381,32 @@ void VisualShaderEditor::_update_graph() { Vector<int> nodes = visual_shader->get_node_list(type); + Control *offset; + for (int n_i = 0; n_i < nodes.size(); n_i++) { Vector2 position = visual_shader->get_node_position(type, nodes[n_i]); Ref<VisualShaderNode> vsnode = visual_shader->get_node(type, nodes[n_i]); + Ref<VisualShaderNodeGroupBase> group_node = Object::cast_to<VisualShaderNodeGroupBase>(vsnode.ptr()); + bool is_group = !group_node.is_null(); + Size2 size = Size2(0, 0); + + Ref<VisualShaderNodeExpression> expression_node = Object::cast_to<VisualShaderNodeExpression>(group_node.ptr()); + bool is_expression = !expression_node.is_null(); + String expression = ""; + GraphNode *node = memnew(GraphNode); - graph->add_child(node); + + if (is_group) { + size = group_node->get_size(); + + node->set_resizable(true); + node->connect("resize_request", this, "_node_resized", varray((int)type, nodes[n_i])); + } + if (is_expression) { + expression = expression_node->get_expression(); + } /*if (!vsnode->is_connected("changed", this, "_node_changed")) { vsnode->connect("changed", this, "_node_changed", varray(vsnode->get_instance_id()), CONNECT_DEFERRED); @@ -205,8 +427,15 @@ void VisualShaderEditor::_update_graph() { Control *custom_editor = NULL; int port_offset = 0; + if (is_group) { + port_offset++; + } + 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); @@ -237,6 +466,24 @@ void VisualShaderEditor::_update_graph() { custom_editor = NULL; } + if (is_group) { + HBoxContainer *hb2 = memnew(HBoxContainer); + + Button *add_input_btn = memnew(Button); + add_input_btn->set_text(TTR("Add input +")); + add_input_btn->connect("pressed", this, "_add_input_port", varray(nodes[n_i], group_node->get_free_input_port_id(), VisualShaderNode::PORT_TYPE_VECTOR, "input" + itos(group_node->get_free_input_port_id())), CONNECT_DEFERRED); + hb2->add_child(add_input_btn); + + hb2->add_spacer(); + + Button *add_output_btn = memnew(Button); + add_output_btn->set_text(TTR("Add output +")); + add_output_btn->connect("pressed", this, "_add_output_port", varray(nodes[n_i], group_node->get_free_output_port_id(), VisualShaderNode::PORT_TYPE_VECTOR, "output" + itos(group_node->get_free_output_port_id())), CONNECT_DEFERRED); + hb2->add_child(add_output_btn); + + node->add_child(hb2); + } + for (int i = 0; i < MAX(vsnode->get_input_port_count(), vsnode->get_output_port_count()); i++) { if (vsnode->is_port_separator(i)) { @@ -293,7 +540,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: { + } } } @@ -305,25 +553,79 @@ void VisualShaderEditor::_update_graph() { if (valid_left) { - Label *label = memnew(Label); - label->set_text(name_left); - label->add_style_override("normal", label_style); //more compact - hb->add_child(label); + if (is_group) { + + OptionButton *type_box = memnew(OptionButton); + hb->add_child(type_box); + type_box->add_item(TTR("Scalar")); + type_box->add_item(TTR("Vector")); + type_box->add_item(TTR("Boolean")); + type_box->add_item(TTR("Transform")); + type_box->select(group_node->get_input_port_type(i)); + type_box->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); + type_box->connect("item_selected", this, "_change_input_port_type", varray(nodes[n_i], i), CONNECT_DEFERRED); + + LineEdit *name_box = memnew(LineEdit); + hb->add_child(name_box); + name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0)); + name_box->set_text(name_left); + name_box->set_expand_to_text_length(true); + name_box->connect("text_entered", this, "_change_input_port_name", varray(name_box, nodes[n_i], i)); + name_box->connect("focus_exited", this, "_port_name_focus_out", varray(name_box, nodes[n_i], i, false)); + + if (is_group) { + Button *remove_btn = memnew(Button); + remove_btn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Remove", "EditorIcons")); + remove_btn->set_tooltip(TTR("Remove") + " " + name_left); + remove_btn->connect("pressed", this, "_remove_input_port", varray(nodes[n_i], i), CONNECT_DEFERRED); + hb->add_child(remove_btn); + } + } else { + + Label *label = memnew(Label); + label->set_text(name_left); + label->add_style_override("normal", label_style); //more compact + hb->add_child(label); + } } hb->add_spacer(); if (valid_right) { - - Label *label = memnew(Label); - label->set_text(name_right); - label->set_align(Label::ALIGN_RIGHT); - label->add_style_override("normal", label_style); //more compact - hb->add_child(label); + if (is_group) { + Button *remove_btn = memnew(Button); + remove_btn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Remove", "EditorIcons")); + remove_btn->set_tooltip(TTR("Remove") + " " + name_left); + remove_btn->connect("pressed", this, "_remove_output_port", varray(nodes[n_i], i), CONNECT_DEFERRED); + hb->add_child(remove_btn); + + LineEdit *name_box = memnew(LineEdit); + hb->add_child(name_box); + name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0)); + name_box->set_text(name_right); + name_box->set_expand_to_text_length(true); + name_box->connect("text_entered", this, "_change_output_port_name", varray(name_box, nodes[n_i], i)); + name_box->connect("focus_exited", this, "_port_name_focus_out", varray(name_box, nodes[n_i], i, true)); + + OptionButton *type_box = memnew(OptionButton); + hb->add_child(type_box); + type_box->add_item(TTR("Scalar")); + type_box->add_item(TTR("Vector")); + type_box->add_item(TTR("Boolean")); + type_box->add_item(TTR("Transform")); + type_box->select(group_node->get_output_port_type(i)); + type_box->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); + type_box->connect("item_selected", this, "_change_output_port_type", varray(nodes[n_i], i), CONNECT_DEFERRED); + } else { + Label *label = memnew(Label); + label->set_text(name_right); + label->add_style_override("normal", label_style); //more compact + hb->add_child(label); + } } } - 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")); @@ -338,18 +640,33 @@ void VisualShaderEditor::_update_graph() { hb->add_child(preview); } + if (is_group) { + offset = memnew(Control); + offset->set_custom_minimum_size(Size2(0, 5 * EDSCALE)); + node->add_child(offset); + port_offset++; + } + 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 (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) { + offset = memnew(Control); + offset->set_custom_minimum_size(Size2(0, 5 * EDSCALE)); + node->add_child(offset); + 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); node->add_child(port_preview); } + offset = memnew(Control); + offset->set_custom_minimum_size(Size2(0, 5 * EDSCALE)); + node->add_child(offset); + String error = vsnode->get_warning(visual_shader->get_mode(), type); if (error != String()) { Label *error_label = memnew(Label); @@ -357,6 +674,44 @@ void VisualShaderEditor::_update_graph() { error_label->set_text(error); node->add_child(error_label); } + + if (is_expression) { + + TextEdit *expression_box = memnew(TextEdit); + expression_node->set_control(expression_box, 0); + node->add_child(expression_box); + + Color text_color = EDITOR_GET("text_editor/highlighting/text_color"); + Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color"); + Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color"); + Color symbol_color = EDITOR_GET("text_editor/highlighting/symbol_color"); + + expression_box->set_syntax_coloring(true); + + for (List<String>::Element *E = keyword_list.front(); E; E = E->next()) { + + expression_box->add_keyword_color(E->get(), keyword_color); + } + + expression_box->add_font_override("font", get_font("expression", "EditorFonts")); + expression_box->add_color_override("font_color", text_color); + expression_box->add_color_override("symbol_color", symbol_color); + expression_box->add_color_region("/*", "*/", comment_color, false); + expression_box->add_color_region("//", "", comment_color, false); + + expression_box->set_text(expression); + expression_box->set_context_menu_enabled(false); + expression_box->set_show_line_numbers(true); + + expression_box->connect("focus_exited", this, "_expression_focus_out", varray(expression_box, nodes[n_i])); + } + + if (!uniform.is_valid()) { + graph->add_child(node); + _update_created_node(node); + if (is_group) + call_deferred("_set_node_size", (int)type, nodes[n_i], size); + } } for (List<VisualShader::Connection>::Element *E = connections.front(); E; E = E->next()) { @@ -370,6 +725,285 @@ void VisualShaderEditor::_update_graph() { } } +void VisualShaderEditor::_add_input_port(int p_node, int p_port, int p_port_type, const String &p_name) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + Ref<VisualShaderNodeExpression> node = visual_shader->get_node(type, p_node); + if (node.is_null()) { + return; + } + + undo_redo->create_action(TTR("Add input port")); + undo_redo->add_do_method(node.ptr(), "add_input_port", p_port, p_port_type, p_name); + undo_redo->add_undo_method(node.ptr(), "remove_input_port", p_port); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->add_do_method(this, "_rebuild"); + undo_redo->add_undo_method(this, "_rebuild"); + undo_redo->commit_action(); +} + +void VisualShaderEditor::_add_output_port(int p_node, int p_port, int p_port_type, const String &p_name) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node); + if (node.is_null()) { + return; + } + + undo_redo->create_action(TTR("Add output port")); + undo_redo->add_do_method(node.ptr(), "add_output_port", p_port, p_port_type, p_name); + undo_redo->add_undo_method(node.ptr(), "remove_output_port", p_port); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->add_do_method(this, "_rebuild"); + undo_redo->add_undo_method(this, "_rebuild"); + undo_redo->commit_action(); +} + +void VisualShaderEditor::_change_input_port_type(int p_type, int p_node, int p_port) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node); + if (node.is_null()) { + return; + } + + undo_redo->create_action(TTR("Change input port type")); + undo_redo->add_do_method(node.ptr(), "set_input_port_type", p_port, p_type); + undo_redo->add_undo_method(node.ptr(), "set_input_port_type", p_port, node->get_input_port_type(p_port)); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->add_do_method(this, "_rebuild"); + undo_redo->add_undo_method(this, "_rebuild"); + undo_redo->commit_action(); +} + +void VisualShaderEditor::_change_output_port_type(int p_type, int p_node, int p_port) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node); + if (node.is_null()) { + return; + } + + undo_redo->create_action(TTR("Change output port type")); + undo_redo->add_do_method(node.ptr(), "set_output_port_type", p_port, p_type); + undo_redo->add_undo_method(node.ptr(), "set_output_port_type", p_port, node->get_output_port_type(p_port)); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->add_do_method(this, "_rebuild"); + undo_redo->add_undo_method(this, "_rebuild"); + undo_redo->commit_action(); +} + +void VisualShaderEditor::_change_input_port_name(const String &p_text, Object *line_edit, int p_node_id, int p_port_id) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + + Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node_id); + ERR_FAIL_COND(!node.is_valid()); + + undo_redo->create_action(TTR("Change input port name")); + undo_redo->add_do_method(node.ptr(), "set_input_port_name", p_port_id, p_text); + undo_redo->add_undo_method(node.ptr(), "set_input_port_name", p_port_id, node->get_input_port_name(p_port_id)); + undo_redo->add_do_method(this, "_rebuild"); + undo_redo->add_undo_method(this, "_rebuild"); + undo_redo->commit_action(); +} + +void VisualShaderEditor::_change_output_port_name(const String &p_text, Object *line_edit, int p_node_id, int p_port_id) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + + Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node_id); + ERR_FAIL_COND(!node.is_valid()); + + undo_redo->create_action(TTR("Change output port name")); + undo_redo->add_do_method(node.ptr(), "set_output_port_name", p_port_id, p_text); + undo_redo->add_undo_method(node.ptr(), "set_output_port_name", p_port_id, node->get_output_port_name(p_port_id)); + undo_redo->add_do_method(this, "_rebuild"); + undo_redo->add_undo_method(this, "_rebuild"); + undo_redo->commit_action(); +} + +void VisualShaderEditor::_remove_input_port(int p_node, int p_port) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node); + if (node.is_null()) { + return; + } + + undo_redo->create_action(TTR("Remove input port")); + + List<VisualShader::Connection> conns; + visual_shader->get_node_connections(type, &conns); + for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) { + + int from_node = E->get().from_node; + int from_port = E->get().from_port; + int to_node = E->get().to_node; + int to_port = E->get().to_port; + + if (to_node == p_node) { + if (to_port == p_port) { + undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port); + undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port); + } else if (to_port > p_port) { + undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port); + undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port); + + undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port - 1); + undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port - 1); + } + } + } + + undo_redo->add_do_method(node.ptr(), "remove_input_port", p_port); + undo_redo->add_undo_method(node.ptr(), "add_input_port", p_port, (int)node->get_input_port_type(p_port), node->get_input_port_name(p_port)); + + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + + undo_redo->add_do_method(this, "_rebuild"); + undo_redo->add_undo_method(this, "_rebuild"); + + undo_redo->commit_action(); +} + +void VisualShaderEditor::_remove_output_port(int p_node, int p_port) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node); + if (node.is_null()) { + return; + } + + undo_redo->create_action(TTR("Remove output port")); + + List<VisualShader::Connection> conns; + visual_shader->get_node_connections(type, &conns); + for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) { + + int from_node = E->get().from_node; + int from_port = E->get().from_port; + int to_node = E->get().to_node; + int to_port = E->get().to_port; + + if (from_node == p_node) { + if (from_port == p_port) { + undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port); + undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port); + } else if (from_port > p_port) { + undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port); + undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port); + + undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port - 1, to_node, to_port); + undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port - 1, to_node, to_port); + } + } + } + + undo_redo->add_do_method(node.ptr(), "remove_output_port", p_port); + undo_redo->add_undo_method(node.ptr(), "add_output_port", p_port, (int)node->get_output_port_type(p_port), node->get_output_port_name(p_port)); + + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + + undo_redo->add_do_method(this, "_rebuild"); + undo_redo->add_undo_method(this, "_rebuild"); + + undo_redo->commit_action(); +} + +void VisualShaderEditor::_expression_focus_out(Object *text_edit, int p_node) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + Ref<VisualShaderNodeExpression> node = visual_shader->get_node(type, p_node); + if (node.is_null()) { + return; + } + + TextEdit *expression_box = Object::cast_to<TextEdit>(text_edit); + + if (node->get_expression() == expression_box->get_text()) + return; + + undo_redo->create_action(TTR("Set expression")); + undo_redo->add_do_method(node.ptr(), "set_expression", expression_box->get_text()); + undo_redo->add_undo_method(node.ptr(), "set_expression", node->get_expression()); + undo_redo->add_do_method(this, "_rebuild"); + undo_redo->add_undo_method(this, "_rebuild"); + undo_redo->commit_action(); +} + +void VisualShaderEditor::_rebuild() { + EditorNode::get_singleton()->get_log()->clear(); + visual_shader->rebuild(); +} + +void VisualShaderEditor::_set_node_size(int p_type, int p_node, const Vector2 &p_size) { + + VisualShader::Type type = VisualShader::Type(p_type); + Ref<VisualShaderNode> node = visual_shader->get_node(type, p_node); + if (node.is_null()) { + return; + } + + Ref<VisualShaderNodeGroupBase> group_node = Object::cast_to<VisualShaderNodeGroupBase>(node.ptr()); + + if (group_node.is_null()) { + return; + } + + Vector2 size = p_size; + + group_node->set_size(size); + + GraphNode *gn = NULL; + if (edit_type->get_selected() == p_type) { // check - otherwise the error will be emitted + Node *node2 = graph->get_node(itos(p_node)); + gn = Object::cast_to<GraphNode>(node2); + if (!gn) + return; + + gn->set_custom_minimum_size(size); + gn->set_size(Size2(1, 1)); + } + + Ref<VisualShaderNodeExpression> expression_node = Object::cast_to<VisualShaderNodeExpression>(node.ptr()); + if (!expression_node.is_null()) { + Control *text_box = expression_node->get_control(0); + Size2 box_size = size; + if (gn != NULL) { + if (box_size.x < 150 * EDSCALE || box_size.y < 0) { + box_size.x = gn->get_size().x; + } + } + box_size.x -= text_box->get_margin(MARGIN_LEFT); + box_size.x -= 28 * EDSCALE; + box_size.y -= text_box->get_margin(MARGIN_TOP); + box_size.y -= 28 * EDSCALE; + text_box->set_custom_minimum_size(Size2(box_size.x, box_size.y)); + text_box->set_size(Size2(1, 1)); + } +} + +void VisualShaderEditor::_node_resized(const Vector2 &p_new_size, int p_type, int p_node) { + + VisualShader::Type type = VisualShader::Type(p_type); + Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node); + if (node.is_null()) { + return; + } + + undo_redo->create_action(TTR("Resize VisualShader node"), UndoRedo::MERGE_ENDS); + undo_redo->add_do_method(this, "_set_node_size", p_type, p_node, p_new_size / EDSCALE); + undo_redo->add_undo_method(this, "_set_node_size", p_type, p_node, node->get_size()); + undo_redo->commit_action(); +} + void VisualShaderEditor::_preview_select_port(int p_node, int p_port) { VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); @@ -381,7 +1015,7 @@ void VisualShaderEditor::_preview_select_port(int p_node, int p_port) { if (node->get_output_port_for_preview() == p_port) { p_port = -1; //toggle it } - undo_redo->create_action("Set Uniform Name"); + undo_redo->create_action(TTR("Set Uniform Name")); undo_redo->add_do_method(node.ptr(), "set_output_port_for_preview", p_port); undo_redo->add_undo_method(node.ptr(), "set_output_port_for_preview", node->get_output_port_for_preview()); undo_redo->add_do_method(this, "_update_graph"); @@ -399,7 +1033,7 @@ void VisualShaderEditor::_line_edit_changed(const String &p_text, Object *line_e String validated_name = visual_shader->validate_uniform_name(p_text, node); updating = true; - undo_redo->create_action("Set Uniform Name"); + undo_redo->create_action(TTR("Set Uniform Name")); undo_redo->add_do_method(node.ptr(), "set_uniform_name", validated_name); undo_redo->add_undo_method(node.ptr(), "set_uniform_name", node->get_uniform_name()); undo_redo->add_do_method(this, "_update_graph"); @@ -416,6 +1050,52 @@ void VisualShaderEditor::_line_edit_focus_out(Object *line_edit, int p_node_id) _line_edit_changed(text, line_edit, p_node_id); } +void VisualShaderEditor::_port_name_focus_out(Object *line_edit, int p_node_id, int p_port_id, bool p_output) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + + Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node_id); + ERR_FAIL_COND(!node.is_valid()); + + String text = Object::cast_to<LineEdit>(line_edit)->get_text(); + + if (!p_output) { + if (node->get_input_port_name(p_port_id) == text) + return; + } else { + if (node->get_output_port_name(p_port_id) == text) + return; + } + + List<String> input_names; + List<String> output_names; + + for (int i = 0; i < node->get_input_port_count(); i++) { + if (!p_output && i == p_port_id) continue; + input_names.push_back(node->get_input_port_name(i)); + } + for (int i = 0; i < node->get_output_port_count(); i++) { + if (p_output && i == p_port_id) continue; + output_names.push_back(node->get_output_port_name(i)); + } + + String validated_name = visual_shader->validate_port_name(text, input_names, output_names); + if (validated_name == "") { + if (!p_output) { + Object::cast_to<LineEdit>(line_edit)->set_text(node->get_input_port_name(p_port_id)); + } else { + Object::cast_to<LineEdit>(line_edit)->set_text(node->get_output_port_name(p_port_id)); + } + return; + } + + if (!p_output) { + _change_input_port_name(validated_name, line_edit, p_node_id, p_port_id); + } else { + _change_output_port_name(validated_name, line_edit, p_node_id, p_port_id); + } +} + void VisualShaderEditor::_port_edited() { VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); @@ -424,7 +1104,7 @@ void VisualShaderEditor::_port_edited() { Ref<VisualShaderNode> vsn = visual_shader->get_node(type, editing_node); ERR_FAIL_COND(!vsn.is_valid()); - undo_redo->create_action("Set Input Default Port"); + undo_redo->create_action(TTR("Set Input Default Port")); undo_redo->add_do_method(vsn.ptr(), "set_input_port_default_value", editing_port, value); undo_redo->add_undo_method(vsn.ptr(), "set_input_port_default_value", editing_port, vsn->get_input_port_default_value(editing_port)); undo_redo->add_do_method(this, "_update_graph"); @@ -450,7 +1130,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()); @@ -459,6 +1139,77 @@ 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); + + VisualShaderNodeScalarConstant *constant = Object::cast_to<VisualShaderNodeScalarConstant>(vsn); + + if (constant) { + if ((int)add_options[p_idx].value != -1) + constant->set_constant(add_options[p_idx].value); + } + + 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()); @@ -469,15 +1220,29 @@ 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()); int id_to_use = visual_shader->get_valid_node_id(type); - undo_redo->create_action("Add Node to Visual Shader"); + undo_redo->create_action(TTR("Add Node to Visual Shader")); undo_redo->add_do_method(visual_shader.ptr(), "add_node", type, vsnode, position, id_to_use); undo_redo->add_undo_method(visual_shader.ptr(), "remove_node", type, id_to_use); + + VisualShaderNodeExpression *expr = Object::cast_to<VisualShaderNodeExpression>(vsnode.ptr()); + if (expr) { + undo_redo->add_do_method(expr, "set_size", Size2(250 * EDSCALE, 150 * EDSCALE)); + } + undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); @@ -488,7 +1253,7 @@ void VisualShaderEditor::_node_dragged(const Vector2 &p_from, const Vector2 &p_t VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); updating = true; - undo_redo->create_action("Node Moved"); + undo_redo->create_action(TTR("Node Moved")); undo_redo->add_do_method(visual_shader.ptr(), "set_node_position", type, p_node, p_to); undo_redo->add_undo_method(visual_shader.ptr(), "set_node_position", type, p_node, p_from); undo_redo->add_do_method(this, "_update_graph"); @@ -505,11 +1270,10 @@ 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; } - undo_redo->create_action("Nodes Connected"); + undo_redo->create_action(TTR("Nodes Connected")); List<VisualShader::Connection> conns; visual_shader->get_node_connections(type, &conns); @@ -538,7 +1302,7 @@ void VisualShaderEditor::_disconnection_request(const String &p_from, int p_from int to = p_to.to_int(); //updating = true; seems graph edit can handle this, no need to protect - undo_redo->create_action("Nodes Disconnected"); + undo_redo->create_action(TTR("Nodes Disconnected")); undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from, p_from_index, to, p_to_index); undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes", type, from, p_from_index, to, p_to_index); undo_redo->add_do_method(this, "_update_graph"); @@ -553,10 +1317,25 @@ void VisualShaderEditor::_connection_to_empty(const String &p_from, int p_from_s void VisualShaderEditor::_delete_request(int which) { VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + Ref<VisualShaderNode> node = Ref<VisualShaderNode>(visual_shader->get_node(type, which)); - undo_redo->create_action("Delete Node"); + undo_redo->create_action(TTR("Delete Node")); undo_redo->add_do_method(visual_shader.ptr(), "remove_node", type, which); - undo_redo->add_undo_method(visual_shader.ptr(), "add_node", type, visual_shader->get_node(type, which), visual_shader->get_node_position(type, which), which); + undo_redo->add_undo_method(visual_shader.ptr(), "add_node", type, node, visual_shader->get_node_position(type, which), which); + + // restore size, inputs and outputs if node is group + VisualShaderNodeGroupBase *group = Object::cast_to<VisualShaderNodeGroupBase>(node.ptr()); + if (group) { + undo_redo->add_undo_method(group, "set_size", group->get_size()); + undo_redo->add_undo_method(group, "set_inputs", group->get_inputs()); + undo_redo->add_undo_method(group, "set_outputs", group->get_outputs()); + } + + // restore expression text if node is expression + VisualShaderNodeExpression *expression = Object::cast_to<VisualShaderNodeExpression>(node.ptr()); + if (expression) { + undo_redo->add_undo_method(expression, "set_expression", expression->get_expression()); + } List<VisualShader::Connection> conns; visual_shader->get_node_connections(type, &conns); @@ -588,26 +1367,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")); - } - if (p_what == NOTIFICATION_PROCESS) { + 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(); + } else if (p_what == NOTIFICATION_PROCESS) { } } @@ -633,25 +1484,30 @@ void VisualShaderEditor::_duplicate_nodes() { VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); List<int> nodes; + Set<int> excluded; for (int i = 0; i < graph->get_child_count(); i++) { - if (Object::cast_to<GraphNode>(graph->get_child(i))) { - int id = String(graph->get_child(i)->get_name()).to_int(); + GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i)); + if (gn) { + int id = String(gn->get_name()).to_int(); Ref<VisualShaderNode> node = visual_shader->get_node(type, id); Ref<VisualShaderNodeOutput> output = node; - if (output.is_valid()) //can't duplicate output + if (output.is_valid()) { // can't duplicate output + excluded.insert(id); continue; - if (node.is_valid()) { + } + if (node.is_valid() && gn->is_selected()) { nodes.push_back(id); } + excluded.insert(id); } } if (nodes.empty()) return; - undo_redo->create_action("Duplicate Nodes"); + undo_redo->create_action(TTR("Duplicate Nodes")); int base_id = visual_shader->get_valid_node_id(type); int id_from = base_id; @@ -667,6 +1523,19 @@ void VisualShaderEditor::_duplicate_nodes() { undo_redo->add_do_method(visual_shader.ptr(), "add_node", type, dupli, visual_shader->get_node_position(type, E->get()) + Vector2(10, 10) * EDSCALE, id_from); undo_redo->add_undo_method(visual_shader.ptr(), "remove_node", type, id_from); + // duplicate size, inputs and outputs if node is group + Ref<VisualShaderNodeGroupBase> group = Object::cast_to<VisualShaderNodeGroupBase>(node.ptr()); + if (!group.is_null()) { + undo_redo->add_do_method(dupli.ptr(), "set_size", group->get_size()); + undo_redo->add_do_method(dupli.ptr(), "set_inputs", group->get_inputs()); + undo_redo->add_do_method(dupli.ptr(), "set_outputs", group->get_outputs()); + } + // duplicate expression text if node is expression + Ref<VisualShaderNodeExpression> expression = Object::cast_to<VisualShaderNodeExpression>(node.ptr()); + if (!expression.is_null()) { + undo_redo->add_do_method(dupli.ptr(), "set_expression", expression->get_expression()); + } + id_from++; } @@ -675,7 +1544,7 @@ void VisualShaderEditor::_duplicate_nodes() { for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) { if (connection_remap.has(E->get().from_node) && connection_remap.has(E->get().to_node)) { - undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, connection_remap[E->get().from_node], E->get().from_port, connection_remap[E->get().to_node], E->get().to_port); + undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes_forced", type, connection_remap[E->get().from_node], E->get().from_port, connection_remap[E->get().to_node], E->get().to_port); } } @@ -683,21 +1552,92 @@ void VisualShaderEditor::_duplicate_nodes() { undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); - //reselect + // reselect duplicated nodes by excluding the other ones for (int i = 0; i < graph->get_child_count(); i++) { - if (Object::cast_to<GraphNode>(graph->get_child(i))) { - int id = String(graph->get_child(i)->get_name()).to_int(); - if (nodes.find(id)) { - Object::cast_to<GraphNode>(graph->get_child(i))->set_selected(true); + GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i)); + if (gn) { + int id = String(gn->get_name()).to_int(); + if (!excluded.has(id)) { + gn->set_selected(true); } else { - Object::cast_to<GraphNode>(graph->get_child(i))->set_selected(false); + gn->set_selected(false); } } } } +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()) { + + Ref<VisualShaderNode> node = visual_shader->get_node(type, F->get()); + + undo_redo->add_do_method(visual_shader.ptr(), "remove_node", type, F->get()); + undo_redo->add_undo_method(visual_shader.ptr(), "add_node", type, node, visual_shader->get_node_position(type, F->get()), F->get()); + + // restore size, inputs and outputs if node is group + VisualShaderNodeGroupBase *group = Object::cast_to<VisualShaderNodeGroupBase>(node.ptr()); + if (group) { + undo_redo->add_undo_method(group, "set_size", group->get_size()); + undo_redo->add_undo_method(group, "set_inputs", group->get_inputs()); + undo_redo->add_undo_method(group, "set_outputs", group->get_outputs()); + } + + // restore expression text if node is expression + VisualShaderNodeExpression *expression = Object::cast_to<VisualShaderNodeExpression>(node.ptr()); + if (expression) { + undo_redo->add_undo_method(expression, "set_expression", expression->get_expression()); + } + } + + 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(); } @@ -711,7 +1651,7 @@ void VisualShaderEditor::_input_select_item(Ref<VisualShaderNodeInput> input, St bool type_changed = input->get_input_type_by_name(name) != input->get_input_type_by_name(prev_name); UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); - undo_redo->create_action("Visual Shader Input Type Changed"); + undo_redo->create_action(TTR("Visual Shader Input Type Changed")); undo_redo->add_do_method(input.ptr(), "set_input_name", name); undo_redo->add_undo_method(input.ptr(), "set_input_name", prev_name); @@ -735,9 +1675,133 @@ 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("_rebuild", &VisualShaderEditor::_rebuild); ClassDB::bind_method("_update_graph", &VisualShaderEditor::_update_graph); + ClassDB::bind_method("_update_options_menu", &VisualShaderEditor::_update_options_menu); + ClassDB::bind_method("_expression_focus_out", &VisualShaderEditor::_expression_focus_out); ClassDB::bind_method("_add_node", &VisualShaderEditor::_add_node); ClassDB::bind_method("_node_dragged", &VisualShaderEditor::_node_dragged); ClassDB::bind_method("_connection_request", &VisualShaderEditor::_connection_request); @@ -745,17 +1809,42 @@ 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); ClassDB::bind_method("_connection_to_empty", &VisualShaderEditor::_connection_to_empty); ClassDB::bind_method("_line_edit_focus_out", &VisualShaderEditor::_line_edit_focus_out); ClassDB::bind_method("_line_edit_changed", &VisualShaderEditor::_line_edit_changed); + ClassDB::bind_method("_port_name_focus_out", &VisualShaderEditor::_port_name_focus_out); ClassDB::bind_method("_duplicate_nodes", &VisualShaderEditor::_duplicate_nodes); 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("_add_input_port", &VisualShaderEditor::_add_input_port); + ClassDB::bind_method("_change_input_port_type", &VisualShaderEditor::_change_input_port_type); + ClassDB::bind_method("_change_input_port_name", &VisualShaderEditor::_change_input_port_name); + ClassDB::bind_method("_remove_input_port", &VisualShaderEditor::_remove_input_port); + ClassDB::bind_method("_add_output_port", &VisualShaderEditor::_add_output_port); + ClassDB::bind_method("_change_output_port_type", &VisualShaderEditor::_change_output_port_type); + ClassDB::bind_method("_change_output_port_name", &VisualShaderEditor::_change_output_port_name); + ClassDB::bind_method("_remove_output_port", &VisualShaderEditor::_remove_output_port); + ClassDB::bind_method("_node_resized", &VisualShaderEditor::_node_resized); + ClassDB::bind_method("_set_node_size", &VisualShaderEditor::_set_node_size); + + 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; @@ -764,10 +1853,15 @@ VisualShaderEditor::VisualShaderEditor() { singleton = this; updating = false; + saved_node_pos_dirty = false; + saved_node_pos = Point2(0, 0); + ShaderLanguage::get_keyword_list(&keyword_list); 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); @@ -777,10 +1871,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); @@ -796,40 +1897,392 @@ 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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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)); + + //CONSTANTS + + add_options.push_back(AddOption("E", "Scalar", "Constants", "VisualShaderNodeScalarConstant", TTR("E constant (2.718282). Represents the base of the natural logarithm."), -1, VisualShaderNode::PORT_TYPE_SCALAR, -1, -1, Math_E)); + add_options.push_back(AddOption("Epsilon", "Scalar", "Constants", "VisualShaderNodeScalarConstant", TTR("Epsilon constant (0.00001). Smallest possible scalar number."), -1, VisualShaderNode::PORT_TYPE_SCALAR, -1, -1, CMP_EPSILON)); + add_options.push_back(AddOption("Phi", "Scalar", "Constants", "VisualShaderNodeScalarConstant", TTR("Phi constant (1.618034). Golden ratio."), -1, VisualShaderNode::PORT_TYPE_SCALAR, -1, -1, 1.618034f)); + add_options.push_back(AddOption("Pi/4", "Scalar", "Constants", "VisualShaderNodeScalarConstant", TTR("Pi/4 constant (0.785398) or 45 degrees."), -1, VisualShaderNode::PORT_TYPE_SCALAR, -1, -1, Math_PI / 4)); + add_options.push_back(AddOption("Pi/2", "Scalar", "Constants", "VisualShaderNodeScalarConstant", TTR("Pi/2 constant (1.570796) or 90 degrees."), -1, VisualShaderNode::PORT_TYPE_SCALAR, -1, -1, Math_PI / 2)); + add_options.push_back(AddOption("Pi", "Scalar", "Constants", "VisualShaderNodeScalarConstant", TTR("Pi constant (3.141593) or 180 degrees."), -1, VisualShaderNode::PORT_TYPE_SCALAR, -1, -1, Math_PI)); + add_options.push_back(AddOption("Tau", "Scalar", "Constants", "VisualShaderNodeScalarConstant", TTR("Tau constant (6.283185) or 360 degrees."), -1, VisualShaderNode::PORT_TYPE_SCALAR, -1, -1, Math_TAU)); + add_options.push_back(AddOption("Sqrt2", "Scalar", "Constants", "VisualShaderNodeScalarConstant", TTR("Sqrt2 constant (1.414214). Square root of 2."), -1, VisualShaderNode::PORT_TYPE_SCALAR, -1, -1, Math_SQRT2)); + + // FUNCTIONS + + 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("OneMinus", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("1.0 - scalar"), VisualShaderNodeScalarFunc::FUNC_ONEMINUS, 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("OneMinus", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 - vector"), VisualShaderNodeVectorFunc::FUNC_ONEMINUS, 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("Expression", "Special", "", "VisualShaderNodeExpression", TTR("Custom Godot Shader Language expression, with custom amount of input and output ports. This is a direct injection of code into the vertex/fragment/light function, do not use it to write the function declarations inside."))); + add_options.push_back(AddOption("Fresnel", "Special", "", "VisualShaderNodeFresnel", TTR("Returns falloff based on the dot product of surface normal and view direction of camera (pass associated inputs to it)."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + + 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(); @@ -919,9 +2372,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"), }; @@ -943,7 +2397,7 @@ public: class VisualShaderNodePluginDefaultEditor : public VBoxContainer { GDCLASS(VisualShaderNodePluginDefaultEditor, VBoxContainer) public: - void _property_changed(const String &prop, const Variant &p_value, bool p_changing = false) { + void _property_changed(const String &prop, const Variant &p_value, const String &p_field, bool p_changing = false) { if (p_changing) return; @@ -951,7 +2405,7 @@ public: UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); updating = true; - undo_redo->create_action("Edit Visual Property: " + prop, UndoRedo::MERGE_ENDS); + undo_redo->create_action(TTR("Edit Visual Property") + ": " + prop, UndoRedo::MERGE_ENDS); undo_redo->add_do_property(node.ptr(), prop, p_value); undo_redo->add_undo_property(node.ptr(), prop, node->get(prop)); undo_redo->commit_action(); @@ -993,7 +2447,7 @@ public: } static void _bind_methods() { - ClassDB::bind_method("_property_changed", &VisualShaderNodePluginDefaultEditor::_property_changed, DEFVAL(false)); + ClassDB::bind_method("_property_changed", &VisualShaderNodePluginDefaultEditor::_property_changed, DEFVAL(String()), DEFVAL(false)); ClassDB::bind_method("_node_changed", &VisualShaderNodePluginDefaultEditor::_node_changed); ClassDB::bind_method("_refresh_request", &VisualShaderNodePluginDefaultEditor::_refresh_request); } @@ -1044,9 +2498,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)); @@ -1072,7 +2526,7 @@ void EditorPropertyShaderMode::_option_selected(int p_which) { return; UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); - undo_redo->create_action("Visual Shader Mode Changed"); + undo_redo->create_action(TTR("Visual Shader Mode Changed")); //do is easy undo_redo->add_do_method(visual_shader.ptr(), "set_mode", p_which); undo_redo->add_undo_method(visual_shader.ptr(), "set_mode", visual_shader->get_mode()); @@ -1095,8 +2549,8 @@ void EditorPropertyShaderMode::_option_selected(int p_which) { VisualShader::Type type = VisualShader::Type(i); Vector<int> nodes = visual_shader->get_node_list(type); - for (int i = 0; i < nodes.size(); i++) { - Ref<VisualShaderNodeInput> input = visual_shader->get_node(type, nodes[i]); + for (int j = 0; j < nodes.size(); j++) { + Ref<VisualShaderNodeInput> input = visual_shader->get_node(type, nodes[j]); if (!input.is_valid()) { continue; } @@ -1116,6 +2570,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 49a51ede8f..1b009b61d5 100644 --- a/editor/plugins/visual_shader_editor_plugin.h +++ b/editor/plugins/visual_shader_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -60,7 +60,7 @@ class VisualShaderEditor : public VBoxContainer { Ref<VisualShader> visual_shader; GraphEdit *graph; - MenuButton *add_node; + ToolButton *add_node; OptionButton *edit_type; @@ -68,26 +68,74 @@ 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; + float value; + + 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, float p_value = -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; + value = p_value; + } + + 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, float p_value = -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; + value = p_value; } }; Vector<AddOption> add_options; + List<String> keyword_list; 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 +150,7 @@ class VisualShaderEditor : public VBoxContainer { void _node_selected(Object *p_node); void _delete_request(int); + void _on_nodes_delete(); void _removed_from_graph(); @@ -115,16 +164,47 @@ class VisualShaderEditor : public VBoxContainer { void _line_edit_changed(const String &p_text, Object *line_edit, int p_node_id); void _line_edit_focus_out(Object *line_edit, int p_node_id); + void _port_name_focus_out(Object *line_edit, int p_node_id, int p_port_id, bool p_output); + void _duplicate_nodes(); Vector<Ref<VisualShaderNodePlugin> > plugins; void _mode_selected(int p_id); + void _rebuild(); void _input_select_item(Ref<VisualShaderNodeInput> input, String name); + void _add_input_port(int p_node, int p_port, int p_type, const String &p_name); + void _remove_input_port(int p_node, int p_port); + void _change_input_port_type(int p_type, int p_node, int p_port); + void _change_input_port_name(const String &p_text, Object *line_edit, int p_node, int p_port); + + void _add_output_port(int p_node, int p_port, int p_type, const String &p_name); + void _remove_output_port(int p_node, int p_port); + void _change_output_port_type(int p_type, int p_node, int p_port); + void _change_output_port_name(const String &p_text, Object *line_edit, int p_node, int p_port); + + void _expression_focus_out(Object *text_edit, int p_node); + + void _set_node_size(int p_type, int p_node, const Size2 &p_size); + void _node_resized(const Vector2 &p_new_size, int p_type, int p_node); + 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); |