diff options
author | Juan Linietsky <reduzio@gmail.com> | 2015-03-03 14:41:36 -0300 |
---|---|---|
committer | Juan Linietsky <reduzio@gmail.com> | 2015-03-03 14:41:36 -0300 |
commit | 2c2894ceb674927a35d2798b3e63adabdb020077 (patch) | |
tree | 9e8950e0acc8fb7531fa60ce8c0321a5b60c335a /tools/editor | |
parent | 4d2198110b4af7f203eeef95697255569e49bce7 (diff) | |
parent | a0ee5cc3531786a652ee43d3a57cb69dff34bd70 (diff) |
Merge branch 'master' of https://github.com/okamstudio/godot
Conflicts:
modules/gdscript/gd_tokenizer.cpp
scene/resources/shader_graph.h
Diffstat (limited to 'tools/editor')
34 files changed, 2480 insertions, 58 deletions
diff --git a/tools/editor/editor_import_export.cpp b/tools/editor/editor_import_export.cpp index 0881739cbe..e6ec11e9d3 100644 --- a/tools/editor/editor_import_export.cpp +++ b/tools/editor/editor_import_export.cpp @@ -1167,10 +1167,36 @@ EditorImportExport* EditorImportExport::singleton=NULL; void EditorImportExport::add_import_plugin(const Ref<EditorImportPlugin>& p_plugin) { + // Need to make sure the name is unique if we are going to lookup by it + ERR_FAIL_COND(by_idx.has(p_plugin->get_name())); + by_idx[ p_plugin->get_name() ]=plugins.size(); plugins.push_back(p_plugin); } +void EditorImportExport::remove_import_plugin(const Ref<EditorImportPlugin>& p_plugin) { + + String plugin_name = p_plugin->get_name(); + + // Keep the indices the same + // Find the index of the target plugin + ERR_FAIL_COND(!by_idx.has(plugin_name)); + int idx = by_idx[plugin_name]; + int last_idx = plugins.size() - 1; + + // Swap the last plugin and the target one + SWAP(plugins[idx], plugins[last_idx]); + + // Update the index of the old last one + by_idx[plugins[idx]->get_name()] = idx; + + // Remove the target plugin's by_idx entry + by_idx.erase(plugin_name); + + // Erase the plugin + plugins.remove(last_idx); +} + int EditorImportExport::get_import_plugin_count() const{ return plugins.size(); diff --git a/tools/editor/editor_import_export.h b/tools/editor/editor_import_export.h index b97dde12f2..a4723f41d0 100644 --- a/tools/editor/editor_import_export.h +++ b/tools/editor/editor_import_export.h @@ -271,6 +271,7 @@ public: static EditorImportExport* get_singleton() { return singleton; } void add_import_plugin(const Ref<EditorImportPlugin>& p_plugin); + void remove_import_plugin(const Ref<EditorImportPlugin>& p_plugin); int get_import_plugin_count() const; Ref<EditorImportPlugin> get_import_plugin(int p_idx) const; Ref<EditorImportPlugin> get_import_plugin_by_name(const String& p_string) const; diff --git a/tools/editor/editor_node.cpp b/tools/editor/editor_node.cpp index 005bb0958f..3d42867d9a 100644 --- a/tools/editor/editor_node.cpp +++ b/tools/editor/editor_node.cpp @@ -89,6 +89,8 @@ #include "plugins/animation_player_editor_plugin.h" #include "plugins/baked_light_editor_plugin.h" #include "plugins/polygon_2d_editor_plugin.h" +#include "plugins/navigation_polygon_editor_plugin.h" +#include "plugins/light_occluder_2d_editor_plugin.h" // end #include "tools/editor/io_plugins/editor_texture_import_plugin.h" #include "tools/editor/io_plugins/editor_scene_import_plugin.h" @@ -336,6 +338,19 @@ void EditorNode::_vp_resized() { } +void EditorNode::_rebuild_import_menu() +{ + PopupMenu* p = import_menu->get_popup(); + p->clear(); + p->add_item("Sub-Scene", FILE_IMPORT_SUBSCENE); + p->add_separator(); + for (int i = 0; i < editor_import_export->get_import_plugin_count(); i++) { + p->add_item(editor_import_export->get_import_plugin(i)->get_visible_name(), IMPORT_PLUGIN_BASE + i); + } + p->add_separator(); + p->add_item("Re-Import..", SETTINGS_IMPORT); +} + void EditorNode::_node_renamed() { if (property_editor) @@ -1967,6 +1982,25 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { log->add_message("REDO: "+action); } break; + + case EDIT_REVERT: { + + Node *scene = get_edited_scene(); + + if (!scene) + break; + + if (unsaved_cache && !p_confirmed) { + confirmation->get_ok()->set_text("Revert"); + confirmation->set_text("This action cannot be undone. Revert anyway?"); + confirmation->popup_centered(Size2(300,70)); + break; + } + + Error err = load_scene(scene->get_filename()); + + } break; + #if 0 case NODE_EXTERNAL_INSTANCE: { @@ -2388,6 +2422,19 @@ void EditorNode::remove_editor_plugin(EditorPlugin *p_editor) { } +void EditorNode::add_editor_import_plugin(const Ref<EditorImportPlugin>& p_editor_import) { + + editor_import_export->add_import_plugin(p_editor_import); + _rebuild_import_menu(); +} + +void EditorNode::remove_editor_import_plugin(const Ref<EditorImportPlugin>& p_editor_import) { + + editor_import_export->remove_import_plugin(p_editor_import); + _rebuild_import_menu(); +} + + void EditorNode::set_edited_scene(Node *p_scene) { if (edited_scene) { @@ -3152,6 +3199,9 @@ void EditorNode::_bind_methods() { ObjectTypeDB::bind_method("_sources_changed",&EditorNode::_sources_changed); ObjectTypeDB::bind_method("_fs_changed",&EditorNode::_fs_changed); + ObjectTypeDB::bind_method(_MD("add_editor_import_plugin", "plugin"), &EditorNode::add_editor_import_plugin); + ObjectTypeDB::bind_method(_MD("remove_editor_import_plugin", "plugin"), &EditorNode::remove_editor_import_plugin); + ObjectTypeDB::bind_method(_MD("get_gui_base"), &EditorNode::get_gui_base); ADD_SIGNAL( MethodInfo("play_pressed") ); ADD_SIGNAL( MethodInfo("pause_pressed") ); @@ -3212,6 +3262,11 @@ Error EditorNode::export_platform(const String& p_platform, const String& p_path return OK; } +void EditorNode::show_warning(const String& p_text) { + + warning->set_text(p_text); + warning->popup_centered_minsize(); +} EditorNode::EditorNode() { @@ -3469,6 +3524,8 @@ EditorNode::EditorNode() { p->add_separator(); p->add_item("Project Settings",RUN_SETTINGS); p->add_separator(); + p->add_item("Revert Scene",EDIT_REVERT); + p->add_separator(); p->add_item("Quit to Project List",RUN_PROJECT_MANAGER,KEY_MASK_SHIFT+KEY_MASK_CMD+KEY_Q); p->add_item("Quit",FILE_QUIT,KEY_MASK_CMD+KEY_Q); @@ -3513,8 +3570,6 @@ EditorNode::EditorNode() { left_menu_hb->add_child( import_menu ); p=import_menu->get_popup(); - p->add_item("Sub-Scene",FILE_IMPORT_SUBSCENE); - p->add_separator(); p->connect("item_pressed",this,"_menu_option"); export_button = memnew( ToolButton ); @@ -3552,7 +3607,7 @@ EditorNode::EditorNode() { play_button->set_icon(gui_base->get_icon("MainPlay","EditorIcons")); play_button->set_focus_mode(Control::FOCUS_NONE); play_button->connect("pressed", this,"_menu_option",make_binds(RUN_PLAY)); - play_button->set_tooltip("Start the scene (F5)."); + play_button->set_tooltip("Play the project (F5)."); @@ -3922,6 +3977,8 @@ EditorNode::EditorNode() { logo->set_pos(Point2(20,20)); logo->set_texture(gui_base->get_icon("Logo","EditorIcons") ); + warning = memnew( AcceptDialog ); + add_child(warning); @@ -4023,11 +4080,6 @@ EditorNode::EditorNode() { editor_import_export->add_import_plugin( Ref<EditorSampleImportPlugin>( memnew(EditorSampleImportPlugin(this)))); editor_import_export->add_import_plugin( Ref<EditorTranslationImportPlugin>( memnew(EditorTranslationImportPlugin(this)))); - - for(int i=0;i<editor_import_export->get_import_plugin_count();i++) { - import_menu->get_popup()->add_item(editor_import_export->get_import_plugin(i)->get_visible_name(),IMPORT_PLUGIN_BASE+i); - } - editor_import_export->add_export_plugin( Ref<EditorTextureExportPlugin>( memnew(EditorTextureExportPlugin))); add_editor_plugin( memnew( CanvasItemEditorPlugin(this) ) ); @@ -4035,7 +4087,8 @@ EditorNode::EditorNode() { add_editor_plugin( memnew( ScriptEditorPlugin(this) ) ); add_editor_plugin( memnew( EditorHelpPlugin(this) ) ); add_editor_plugin( memnew( AnimationPlayerEditorPlugin(this) ) ); - add_editor_plugin( memnew( ShaderGraphEditorPlugin(this) ) ); + add_editor_plugin( memnew( ShaderGraphEditorPlugin(this,true) ) ); + add_editor_plugin( memnew( ShaderGraphEditorPlugin(this,false) ) ); add_editor_plugin( memnew( ShaderEditorPlugin(this,true) ) ); add_editor_plugin( memnew( ShaderEditorPlugin(this,false) ) ); add_editor_plugin( memnew( CameraEditorPlugin(this) ) ); @@ -4063,6 +4116,8 @@ EditorNode::EditorNode() { add_editor_plugin( memnew( PathEditorPlugin(this) ) ); add_editor_plugin( memnew( BakedLightEditorPlugin(this) ) ); add_editor_plugin( memnew( Polygon2DEditorPlugin(this) ) ); + add_editor_plugin( memnew( LightOccluder2DEditorPlugin(this) ) ); + add_editor_plugin( memnew( NavigationPolygonEditorPlugin(this) ) ); for(int i=0;i<EditorPlugins::get_plugin_count();i++) add_editor_plugin( EditorPlugins::create(i,this) ); @@ -4071,9 +4126,7 @@ EditorNode::EditorNode() { circle_step_frame=OS::get_singleton()->get_frames_drawn();; circle_step=0; - - import_menu->get_popup()->add_separator(); - import_menu->get_popup()->add_item("Re-Import..",SETTINGS_IMPORT); + _rebuild_import_menu(); editor_plugin_screen=NULL; editor_plugin_over=NULL; diff --git a/tools/editor/editor_node.h b/tools/editor/editor_node.h index 7560c2b149..531eccb546 100644 --- a/tools/editor/editor_node.h +++ b/tools/editor/editor_node.h @@ -127,6 +127,7 @@ class EditorNode : public Node { FILE_EXTERNAL_OPEN_SCENE, EDIT_UNDO, EDIT_REDO, + EDIT_REVERT, RESOURCE_NEW, RESOURCE_LOAD, RESOURCE_SAVE, @@ -231,6 +232,7 @@ class EditorNode : public Node { ConfirmationDialog *open_recent_confirmation; AcceptDialog *accept; AcceptDialog *about; + AcceptDialog *warning; //OptimizedPresetsDialog *optimized_presets; EditorSettingsDialog *settings_config_dialog; @@ -339,6 +341,8 @@ class EditorNode : public Node { void _show_messages(); void _vp_resized(); + void _rebuild_import_menu(); + void _save_scene(String p_file); @@ -420,6 +424,9 @@ public: static void add_editor_plugin(EditorPlugin *p_editor); static void remove_editor_plugin(EditorPlugin *p_editor); + void add_editor_import_plugin(const Ref<EditorImportPlugin>& p_editor_import); + void remove_editor_import_plugin(const Ref<EditorImportPlugin>& p_editor_import); + void edit_node(Node *p_node); void edit_resource(const Ref<Resource>& p_resource); @@ -478,6 +485,9 @@ public: Ref<Theme> get_editor_theme() const { return theme; } + void show_warning(const String& p_text); + + Error export_platform(const String& p_platform, const String& p_path, bool p_debug,const String& p_password,bool p_quit_after=false); static void register_editor_types(); diff --git a/tools/editor/icons/icon_canvas_item_material.png b/tools/editor/icons/icon_canvas_item_material.png Binary files differnew file mode 100644 index 0000000000..2fe8921653 --- /dev/null +++ b/tools/editor/icons/icon_canvas_item_material.png diff --git a/tools/editor/icons/icon_canvas_modulate.png b/tools/editor/icons/icon_canvas_modulate.png Binary files differnew file mode 100644 index 0000000000..2a34df7793 --- /dev/null +++ b/tools/editor/icons/icon_canvas_modulate.png diff --git a/tools/editor/icons/icon_graph_color_ramp.png b/tools/editor/icons/icon_graph_color_ramp.png Binary files differnew file mode 100644 index 0000000000..9031b5ec53 --- /dev/null +++ b/tools/editor/icons/icon_graph_color_ramp.png diff --git a/tools/editor/icons/icon_graph_curve_map.png b/tools/editor/icons/icon_graph_curve_map.png Binary files differnew file mode 100644 index 0000000000..de5c32f09e --- /dev/null +++ b/tools/editor/icons/icon_graph_curve_map.png diff --git a/tools/editor/icons/icon_graph_default_texture.png b/tools/editor/icons/icon_graph_default_texture.png Binary files differnew file mode 100644 index 0000000000..da77ec9364 --- /dev/null +++ b/tools/editor/icons/icon_graph_default_texture.png diff --git a/tools/editor/icons/icon_light_2d.png b/tools/editor/icons/icon_light_2d.png Binary files differnew file mode 100644 index 0000000000..9162b33090 --- /dev/null +++ b/tools/editor/icons/icon_light_2d.png diff --git a/tools/editor/icons/icon_light_occluder_2d.png b/tools/editor/icons/icon_light_occluder_2d.png Binary files differnew file mode 100644 index 0000000000..c66dd536d3 --- /dev/null +++ b/tools/editor/icons/icon_light_occluder_2d.png diff --git a/tools/editor/icons/icon_navigation_2d.png b/tools/editor/icons/icon_navigation_2d.png Binary files differnew file mode 100644 index 0000000000..8170ecf68c --- /dev/null +++ b/tools/editor/icons/icon_navigation_2d.png diff --git a/tools/editor/icons/icon_navigation_polygon_instance.png b/tools/editor/icons/icon_navigation_polygon_instance.png Binary files differnew file mode 100644 index 0000000000..9f9c318906 --- /dev/null +++ b/tools/editor/icons/icon_navigation_polygon_instance.png diff --git a/tools/editor/icons/icon_occluder_polygon_2d.png b/tools/editor/icons/icon_occluder_polygon_2d.png Binary files differnew file mode 100644 index 0000000000..705794b729 --- /dev/null +++ b/tools/editor/icons/icon_occluder_polygon_2d.png diff --git a/tools/editor/icons/icon_rotate_0.png b/tools/editor/icons/icon_rotate_0.png Binary files differnew file mode 100644 index 0000000000..85a4b4c420 --- /dev/null +++ b/tools/editor/icons/icon_rotate_0.png diff --git a/tools/editor/icons/icon_rotate_180.png b/tools/editor/icons/icon_rotate_180.png Binary files differnew file mode 100644 index 0000000000..c4c516cff5 --- /dev/null +++ b/tools/editor/icons/icon_rotate_180.png diff --git a/tools/editor/icons/icon_rotate_270.png b/tools/editor/icons/icon_rotate_270.png Binary files differnew file mode 100644 index 0000000000..6e0f2e62b8 --- /dev/null +++ b/tools/editor/icons/icon_rotate_270.png diff --git a/tools/editor/icons/icon_rotate_90.png b/tools/editor/icons/icon_rotate_90.png Binary files differnew file mode 100644 index 0000000000..f25b0e99a3 --- /dev/null +++ b/tools/editor/icons/icon_rotate_90.png diff --git a/tools/editor/icons/icon_transpose.png b/tools/editor/icons/icon_transpose.png Binary files differnew file mode 100644 index 0000000000..f9b78bc0fd --- /dev/null +++ b/tools/editor/icons/icon_transpose.png diff --git a/tools/editor/plugins/collision_polygon_2d_editor_plugin.cpp b/tools/editor/plugins/collision_polygon_2d_editor_plugin.cpp index ed228e9a1c..dab6df9a39 100644 --- a/tools/editor/plugins/collision_polygon_2d_editor_plugin.cpp +++ b/tools/editor/plugins/collision_polygon_2d_editor_plugin.cpp @@ -4,6 +4,7 @@ #include "os/file_access.h" #include "tools/editor/editor_settings.h" + void CollisionPolygon2DEditor::_notification(int p_what) { switch(p_what) { diff --git a/tools/editor/plugins/collision_polygon_editor_plugin.cpp b/tools/editor/plugins/collision_polygon_editor_plugin.cpp index b92acb60f9..a6f2085a19 100644 --- a/tools/editor/plugins/collision_polygon_editor_plugin.cpp +++ b/tools/editor/plugins/collision_polygon_editor_plugin.cpp @@ -31,6 +31,8 @@ #include "os/file_access.h" #include "tools/editor/editor_settings.h" #include "scene/3d/camera.h" +#include "canvas_item_editor_plugin.h" + void CollisionPolygonEditor::_notification(int p_what) { switch(p_what) { @@ -71,14 +73,14 @@ void CollisionPolygonEditor::_node_removed(Node *p_node) { Vector2 CollisionPolygonEditor::snap_point(const Vector2& p_point) const { return p_point; - /* - if (canvas_item_editor->is_snap_active()) { + + if (CanvasItemEditor::get_singleton()->is_snap_active()) { - return p_point.snapped(Vector2(1,1)*canvas_item_editor->get_snap()); + return p_point.snapped(Vector2(1,1)*CanvasItemEditor::get_singleton()->get_snap()); } else { return p_point; - } ??? */ + } } void CollisionPolygonEditor::_menu_option(int p_option) { @@ -148,7 +150,7 @@ bool CollisionPolygonEditor::forward_spatial_input_event(Camera* p_camera,const Vector2 cpoint(spoint.x,spoint.y); - //cpoint=snap_point(cpoint); snap? + cpoint=snap_point(cpoint); Vector<Vector2> poly = node->get_polygon(); @@ -362,7 +364,7 @@ bool CollisionPolygonEditor::forward_spatial_input_event(Camera* p_camera,const Vector2 cpoint(spoint.x,spoint.y); - //cpoint=snap_point(cpoint); + cpoint=snap_point(cpoint); edited_point_pos = cpoint; _polygon_draw(); diff --git a/tools/editor/plugins/light_occluder_2d_editor_plugin.cpp b/tools/editor/plugins/light_occluder_2d_editor_plugin.cpp new file mode 100644 index 0000000000..c1fb81b66a --- /dev/null +++ b/tools/editor/plugins/light_occluder_2d_editor_plugin.cpp @@ -0,0 +1,500 @@ +#include "light_occluder_2d_editor_plugin.h" + +#include "canvas_item_editor_plugin.h" +#include "os/file_access.h" +#include "tools/editor/editor_settings.h" + +void LightOccluder2DEditor::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_READY: { + + button_create->set_icon( get_icon("Edit","EditorIcons")); + button_edit->set_icon( get_icon("MovePoint","EditorIcons")); + button_edit->set_pressed(true); + get_tree()->connect("node_removed",this,"_node_removed"); + create_poly->connect("confirmed",this,"_create_poly"); + + } break; + case NOTIFICATION_FIXED_PROCESS: { + + + } break; + } + +} +void LightOccluder2DEditor::_node_removed(Node *p_node) { + + if(p_node==node) { + node=NULL; + hide(); + canvas_item_editor->get_viewport_control()->update(); + } + +} + + +Vector2 LightOccluder2DEditor::snap_point(const Vector2& p_point) const { + + if (canvas_item_editor->is_snap_active()) { + + return p_point.snapped(Vector2(1,1)*canvas_item_editor->get_snap()); + + } else { + return p_point; + } +} + +void LightOccluder2DEditor::_menu_option(int p_option) { + + switch(p_option) { + + case MODE_CREATE: { + + mode=MODE_CREATE; + button_create->set_pressed(true); + button_edit->set_pressed(false); + } break; + case MODE_EDIT: { + + mode=MODE_EDIT; + button_create->set_pressed(false); + button_edit->set_pressed(true); + } break; + + } +} + +void LightOccluder2DEditor::_wip_close(bool p_closed) { + + undo_redo->create_action("Create Poly"); + undo_redo->add_undo_method(node->get_occluder_polygon().ptr(),"set_polygon",node->get_occluder_polygon()->get_polygon()); + undo_redo->add_do_method(node->get_occluder_polygon().ptr(),"set_polygon",wip); + undo_redo->add_undo_method(node->get_occluder_polygon().ptr(),"set_closed",node->get_occluder_polygon()->is_closed()); + undo_redo->add_do_method(node->get_occluder_polygon().ptr(),"set_closed",p_closed); + + undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->commit_action(); + wip.clear(); + wip_active=false; + mode=MODE_EDIT; + button_edit->set_pressed(true); + button_create->set_pressed(false); + edited_point=-1; +} + +bool LightOccluder2DEditor::forward_input_event(const InputEvent& p_event) { + + + if (!node) + return false; + + if (node->get_occluder_polygon().is_null()) { + if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1 && p_event.mouse_button.pressed) { + create_poly->set_text("No OccluderPolygon2D resource on this node.\nCreate and assign one?"); + create_poly->popup_centered_minsize(); + } + return false; + } + switch(p_event.type) { + + case InputEvent::MOUSE_BUTTON: { + + const InputEventMouseButton &mb=p_event.mouse_button; + + Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); + + + Vector2 gpoint = Point2(mb.x,mb.y); + Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint); + cpoint=snap_point(cpoint); + cpoint = node->get_global_transform().affine_inverse().xform(cpoint); + + Vector<Vector2> poly = Variant(node->get_occluder_polygon()->get_polygon()); + + //first check if a point is to be added (segment split) + real_t grab_treshold=EDITOR_DEF("poly_editor/point_grab_radius",8); + + switch(mode) { + + + case MODE_CREATE: { + + if (mb.button_index==BUTTON_LEFT && mb.pressed) { + + + if (!wip_active) { + + wip.clear(); + wip.push_back( cpoint ); + wip_active=true; + edited_point_pos=cpoint; + canvas_item_editor->get_viewport_control()->update(); + edited_point=1; + return true; + } else { + + + if (wip.size()>1 && xform.xform(wip[0]).distance_to(gpoint)<grab_treshold) { + //wip closed + _wip_close(true); + + return true; + } else if (wip.size()>1 && xform.xform(wip[wip.size()-1]).distance_to(gpoint)<grab_treshold) { + //wip closed + _wip_close(false); + return true; + + } else { + + wip.push_back( cpoint ); + edited_point=wip.size(); + canvas_item_editor->get_viewport_control()->update(); + return true; + + //add wip point + } + } + } else if (mb.button_index==BUTTON_RIGHT && mb.pressed && wip_active) { + _wip_close(true); + } + + + + } break; + + case MODE_EDIT: { + + if (mb.button_index==BUTTON_LEFT) { + if (mb.pressed) { + + if (mb.mod.control) { + + + if (poly.size() < 3) { + + undo_redo->create_action("Edit Poly"); + undo_redo->add_undo_method(node->get_occluder_polygon().ptr(),"set_polygon",poly); + poly.push_back(cpoint); + undo_redo->add_do_method(node->get_occluder_polygon().ptr(),"set_polygon",poly); + undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->commit_action(); + return true; + } + + //search edges + int closest_idx=-1; + Vector2 closest_pos; + real_t closest_dist=1e10; + for(int i=0;i<poly.size();i++) { + + Vector2 points[2] ={ xform.xform(poly[i]), + xform.xform(poly[(i+1)%poly.size()]) }; + + Vector2 cp = Geometry::get_closest_point_to_segment_2d(gpoint,points); + if (cp.distance_squared_to(points[0])<CMP_EPSILON2 || cp.distance_squared_to(points[1])<CMP_EPSILON2) + continue; //not valid to reuse point + + real_t d = cp.distance_to(gpoint); + if (d<closest_dist && d<grab_treshold) { + closest_dist=d; + closest_pos=cp; + closest_idx=i; + } + + + } + + if (closest_idx>=0) { + + pre_move_edit=poly; + poly.insert(closest_idx+1,xform.affine_inverse().xform(closest_pos)); + edited_point=closest_idx+1; + edited_point_pos=xform.affine_inverse().xform(closest_pos); + node->get_occluder_polygon()->set_polygon(Variant(poly)); + canvas_item_editor->get_viewport_control()->update(); + return true; + } + } else { + + //look for points to move + + int closest_idx=-1; + Vector2 closest_pos; + real_t closest_dist=1e10; + for(int i=0;i<poly.size();i++) { + + Vector2 cp =xform.xform(poly[i]); + + real_t d = cp.distance_to(gpoint); + if (d<closest_dist && d<grab_treshold) { + closest_dist=d; + closest_pos=cp; + closest_idx=i; + } + + } + + if (closest_idx>=0) { + + pre_move_edit=poly; + edited_point=closest_idx; + edited_point_pos=xform.affine_inverse().xform(closest_pos); + canvas_item_editor->get_viewport_control()->update(); + return true; + } + } + } else { + + if (edited_point!=-1) { + + //apply + + ERR_FAIL_INDEX_V(edited_point,poly.size(),false); + poly[edited_point]=edited_point_pos; + undo_redo->create_action("Edit Poly"); + undo_redo->add_do_method(node->get_occluder_polygon().ptr(),"set_polygon",poly); + undo_redo->add_undo_method(node->get_occluder_polygon().ptr(),"set_polygon",pre_move_edit); + undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->commit_action(); + + edited_point=-1; + return true; + } + } + } if (mb.button_index==BUTTON_RIGHT && mb.pressed && edited_point==-1) { + + + + int closest_idx=-1; + Vector2 closest_pos; + real_t closest_dist=1e10; + for(int i=0;i<poly.size();i++) { + + Vector2 cp =xform.xform(poly[i]); + + real_t d = cp.distance_to(gpoint); + if (d<closest_dist && d<grab_treshold) { + closest_dist=d; + closest_pos=cp; + closest_idx=i; + } + + } + + if (closest_idx>=0) { + + + undo_redo->create_action("Edit Poly (Remove Point)"); + undo_redo->add_undo_method(node->get_occluder_polygon().ptr(),"set_polygon",poly); + poly.remove(closest_idx); + undo_redo->add_do_method(node->get_occluder_polygon().ptr(),"set_polygon",poly); + undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->commit_action(); + return true; + } + + } + + + + } break; + } + + + + } break; + case InputEvent::MOUSE_MOTION: { + + const InputEventMouseMotion &mm=p_event.mouse_motion; + + if (edited_point!=-1 && (wip_active || mm.button_mask&BUTTON_MASK_LEFT)) { + + Vector2 gpoint = Point2(mm.x,mm.y); + Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint); + cpoint=snap_point(cpoint); + edited_point_pos = node->get_global_transform().affine_inverse().xform(cpoint); + + canvas_item_editor->get_viewport_control()->update(); + + } + + } break; + } + + return false; +} +void LightOccluder2DEditor::_canvas_draw() { + + if (!node || !node->get_occluder_polygon().is_valid()) + return; + + Control *vpc = canvas_item_editor->get_viewport_control(); + + Vector<Vector2> poly; + + if (wip_active) + poly=wip; + else + poly=Variant(node->get_occluder_polygon()->get_polygon()); + + + Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); + Ref<Texture> handle= get_icon("EditorHandle","EditorIcons"); + + int len = poly.size(); + + for(int i=0;i<poly.size();i++) { + + + Vector2 p,p2; + p = i==edited_point ? edited_point_pos : poly[i]; + if ((wip_active && i==poly.size()-1) || (((i+1)%poly.size())==edited_point)) + p2=edited_point_pos; + else + p2 = poly[(i+1)%poly.size()]; + + Vector2 point = xform.xform(p); + Vector2 next_point = xform.xform(p2); + + Color col=Color(1,0.3,0.1,0.8); + + if (i==poly.size()-1 && (!node->get_occluder_polygon()->is_closed() || wip_active)) { + + } else { + vpc->draw_line(point,next_point,col,2); + } + vpc->draw_texture(handle,point-handle->get_size()*0.5); + } +} + + + +void LightOccluder2DEditor::edit(Node *p_collision_polygon) { + + if (!canvas_item_editor) { + canvas_item_editor=CanvasItemEditor::get_singleton(); + } + + if (p_collision_polygon) { + + node=p_collision_polygon->cast_to<LightOccluder2D>(); + if (!canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw")) + canvas_item_editor->get_viewport_control()->connect("draw",this,"_canvas_draw"); + wip.clear(); + wip_active=false; + edited_point=-1; + + } else { + node=NULL; + + if (canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw")) + canvas_item_editor->get_viewport_control()->disconnect("draw",this,"_canvas_draw"); + + } + +} + +void LightOccluder2DEditor::_create_poly() { + + undo_redo->create_action("Create Occluder Polygon"); + undo_redo->add_do_method(node,"set_occluder_polygon",Ref<OccluderPolygon2D>(memnew( OccluderPolygon2D))); + undo_redo->add_undo_method(node,"set_occluder_polygon",Variant(REF())); + undo_redo->commit_action(); +} + +void LightOccluder2DEditor::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_menu_option"),&LightOccluder2DEditor::_menu_option); + ObjectTypeDB::bind_method(_MD("_canvas_draw"),&LightOccluder2DEditor::_canvas_draw); + ObjectTypeDB::bind_method(_MD("_node_removed"),&LightOccluder2DEditor::_node_removed); + ObjectTypeDB::bind_method(_MD("_create_poly"),&LightOccluder2DEditor::_create_poly); + +} + + +LightOccluder2DEditor::LightOccluder2DEditor(EditorNode *p_editor) { + + canvas_item_editor=NULL; + editor=p_editor; + undo_redo = editor->get_undo_redo(); + + add_child( memnew( VSeparator )); + button_create = memnew( ToolButton ); + add_child(button_create); + button_create->connect("pressed",this,"_menu_option",varray(MODE_CREATE)); + button_create->set_toggle_mode(true); + button_create->set_tooltip("Create a new polygon from scratch"); + + 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("Edit existing polygon:\nLMB: Move Point.\nCtrl+LMB: Split Segment.\nRMB: Erase Point."); + + create_poly = memnew( ConfirmationDialog ); + add_child(create_poly); + create_poly->get_ok()->set_text("Create"); + + + //add_constant_override("separation",0); + +#if 0 + options = memnew( MenuButton ); + add_child(options); + options->set_area_as_parent_rect(); + options->set_text("Polygon"); + //options->get_popup()->add_item("Parse BBCODE",PARSE_BBCODE); + options->get_popup()->connect("item_pressed", this,"_menu_option"); +#endif + + mode = MODE_EDIT; + wip_active=false; + +} + + +void LightOccluder2DEditorPlugin::edit(Object *p_object) { + + collision_polygon_editor->edit(p_object->cast_to<Node>()); +} + +bool LightOccluder2DEditorPlugin::handles(Object *p_object) const { + + return p_object->is_type("LightOccluder2D"); +} + +void LightOccluder2DEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + collision_polygon_editor->show(); + } else { + + collision_polygon_editor->hide(); + collision_polygon_editor->edit(NULL); + } + +} + +LightOccluder2DEditorPlugin::LightOccluder2DEditorPlugin(EditorNode *p_node) { + + editor=p_node; + collision_polygon_editor = memnew( LightOccluder2DEditor(p_node) ); + CanvasItemEditor::get_singleton()->add_control_to_menu_panel(collision_polygon_editor); + + collision_polygon_editor->hide(); + + + +} + + +LightOccluder2DEditorPlugin::~LightOccluder2DEditorPlugin() +{ +} + diff --git a/tools/editor/plugins/light_occluder_2d_editor_plugin.h b/tools/editor/plugins/light_occluder_2d_editor_plugin.h new file mode 100644 index 0000000000..5fb5631d05 --- /dev/null +++ b/tools/editor/plugins/light_occluder_2d_editor_plugin.h @@ -0,0 +1,87 @@ +#ifndef LIGHT_OCCLUDER_2D_EDITOR_PLUGIN_H +#define LIGHT_OCCLUDER_2D_EDITOR_PLUGIN_H + + + +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" +#include "scene/2d/light_occluder_2d.h" +#include "scene/gui/tool_button.h" +#include "scene/gui/button_group.h" + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ +class CanvasItemEditor; + +class LightOccluder2DEditor : public HBoxContainer { + + OBJ_TYPE(LightOccluder2DEditor, HBoxContainer ); + + UndoRedo *undo_redo; + enum Mode { + + MODE_CREATE, + MODE_EDIT, + + }; + + Mode mode; + + ToolButton *button_create; + ToolButton *button_edit; + + CanvasItemEditor *canvas_item_editor; + EditorNode *editor; + Panel *panel; + LightOccluder2D *node; + MenuButton *options; + + int edited_point; + Vector2 edited_point_pos; + Vector<Vector2> pre_move_edit; + Vector<Vector2> wip; + bool wip_active; + + ConfirmationDialog *create_poly; + + void _wip_close(bool p_closed); + void _canvas_draw(); + void _menu_option(int p_option); + void _create_poly(); + +protected: + void _notification(int p_what); + void _node_removed(Node *p_node); + static void _bind_methods(); +public: + + Vector2 snap_point(const Vector2& p_point) const; + bool forward_input_event(const InputEvent& p_event); + void edit(Node *p_collision_polygon); + LightOccluder2DEditor(EditorNode *p_editor); +}; + +class LightOccluder2DEditorPlugin : public EditorPlugin { + + OBJ_TYPE( LightOccluder2DEditorPlugin, EditorPlugin ); + + LightOccluder2DEditor *collision_polygon_editor; + EditorNode *editor; + +public: + + virtual bool forward_input_event(const InputEvent& p_event) { return collision_polygon_editor->forward_input_event(p_event); } + + virtual String get_name() const { return "LightOccluder2D"; } + bool has_main_screen() const { return false; } + virtual void edit(Object *p_node); + virtual bool handles(Object *p_node) const; + virtual void make_visible(bool p_visible); + + LightOccluder2DEditorPlugin(EditorNode *p_node); + ~LightOccluder2DEditorPlugin(); + +}; + +#endif // LIGHT_OCCLUDER_2D_EDITOR_PLUGIN_H diff --git a/tools/editor/plugins/navigation_polygon_editor_plugin.cpp b/tools/editor/plugins/navigation_polygon_editor_plugin.cpp new file mode 100644 index 0000000000..599d18c8bb --- /dev/null +++ b/tools/editor/plugins/navigation_polygon_editor_plugin.cpp @@ -0,0 +1,547 @@ +#include "navigation_polygon_editor_plugin.h" + +#include "canvas_item_editor_plugin.h" +#include "os/file_access.h" +#include "tools/editor/editor_settings.h" + +void NavigationPolygonEditor::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_READY: { + + button_create->set_icon( get_icon("Edit","EditorIcons")); + button_edit->set_icon( get_icon("MovePoint","EditorIcons")); + button_edit->set_pressed(true); + get_tree()->connect("node_removed",this,"_node_removed"); + create_nav->connect("confirmed",this,"_create_nav"); + + } break; + case NOTIFICATION_FIXED_PROCESS: { + + + } break; + } + +} +void NavigationPolygonEditor::_node_removed(Node *p_node) { + + if(p_node==node) { + node=NULL; + hide(); + canvas_item_editor->get_viewport_control()->update(); + } + +} + +void NavigationPolygonEditor::_create_nav() { + + undo_redo->create_action("Create Navigation Polygon"); + undo_redo->add_do_method(node,"set_navigation_polygon",Ref<NavigationPolygon>(memnew( NavigationPolygon))); + undo_redo->add_undo_method(node,"set_navigation_polygon",Variant(REF())); + undo_redo->commit_action(); +} + +Vector2 NavigationPolygonEditor::snap_point(const Vector2& p_point) const { + + if (canvas_item_editor->is_snap_active()) { + + return p_point.snapped(Vector2(1,1)*canvas_item_editor->get_snap()); + + } else { + return p_point; + } +} + +void NavigationPolygonEditor::_menu_option(int p_option) { + + switch(p_option) { + + case MODE_CREATE: { + + mode=MODE_CREATE; + button_create->set_pressed(true); + button_edit->set_pressed(false); + } break; + case MODE_EDIT: { + + mode=MODE_EDIT; + button_create->set_pressed(false); + button_edit->set_pressed(true); + } break; + + } +} + +void NavigationPolygonEditor::_wip_close() { + + + if (wip.size()>=3) { + + undo_redo->create_action("Create Poly"); + undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"remove_outline",node->get_navigation_polygon()->get_outline_count()); + undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"add_outline",wip); + undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines"); + undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines"); + undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->commit_action(); + mode=MODE_EDIT; + button_edit->set_pressed(true); + button_create->set_pressed(false); + } + + wip.clear(); + wip_active=false; + edited_point=-1; +} + +bool NavigationPolygonEditor::forward_input_event(const InputEvent& p_event) { + + + if (!node) + return false; + + if (node->get_navigation_polygon().is_null()) { + if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1 && p_event.mouse_button.pressed) { + create_nav->set_text("No NavigationPolygon resource on this node.\nCreate and assign one?"); + create_nav->popup_centered_minsize(); + } + return false; + } + + + switch(p_event.type) { + + case InputEvent::MOUSE_BUTTON: { + + const InputEventMouseButton &mb=p_event.mouse_button; + + Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); + + + Vector2 gpoint = Point2(mb.x,mb.y); + Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint); + cpoint=snap_point(cpoint); + cpoint = node->get_global_transform().affine_inverse().xform(cpoint); + + + + //first check if a point is to be added (segment split) + real_t grab_treshold=EDITOR_DEF("poly_editor/point_grab_radius",8); + + switch(mode) { + + + case MODE_CREATE: { + + if (mb.button_index==BUTTON_LEFT && mb.pressed) { + + + if (!wip_active) { + + wip.clear(); + wip.push_back( cpoint ); + wip_active=true; + edited_point_pos=cpoint; + edited_outline=-1; + canvas_item_editor->get_viewport_control()->update(); + edited_point=1; + return true; + } else { + + + if (wip.size()>1 && xform.xform(wip[0]).distance_to(gpoint)<grab_treshold) { + //wip closed + _wip_close(); + + return true; + } else { + + wip.push_back( cpoint ); + edited_point=wip.size(); + canvas_item_editor->get_viewport_control()->update(); + return true; + + //add wip point + } + } + } else if (mb.button_index==BUTTON_RIGHT && mb.pressed && wip_active) { + _wip_close(); + } + + + + } break; + + case MODE_EDIT: { + + if (mb.button_index==BUTTON_LEFT) { + if (mb.pressed) { + + if (mb.mod.control) { + + + //search edges + int closest_outline=-1; + int closest_idx=-1; + Vector2 closest_pos; + real_t closest_dist=1e10; + + for(int j=0;j<node->get_navigation_polygon()->get_outline_count();j++) { + + + DVector<Vector2> points=node->get_navigation_polygon()->get_outline(j); + + int pc=points.size(); + DVector<Vector2>::Read poly=points.read(); + + for(int i=0;i<pc;i++) { + + Vector2 points[2] ={ xform.xform(poly[i]), + xform.xform(poly[(i+1)%pc]) }; + + Vector2 cp = Geometry::get_closest_point_to_segment_2d(gpoint,points); + if (cp.distance_squared_to(points[0])<CMP_EPSILON2 || cp.distance_squared_to(points[1])<CMP_EPSILON2) + continue; //not valid to reuse point + + real_t d = cp.distance_to(gpoint); + if (d<closest_dist && d<grab_treshold) { + closest_dist=d; + closest_outline=j; + closest_pos=cp; + closest_idx=i; + } + + + } + } + + if (closest_idx>=0) { + + pre_move_edit=node->get_navigation_polygon()->get_outline(closest_outline); + DVector<Point2> poly = pre_move_edit; + poly.insert(closest_idx+1,xform.affine_inverse().xform(closest_pos)); + edited_point=closest_idx+1; + edited_outline=closest_outline; + edited_point_pos=xform.affine_inverse().xform(closest_pos); + node->get_navigation_polygon()->set_outline(closest_outline,poly); + canvas_item_editor->get_viewport_control()->update(); + return true; + } + } else { + + //look for points to move + int closest_outline=-1; + int closest_idx=-1; + Vector2 closest_pos; + real_t closest_dist=1e10; + + for(int j=0;j<node->get_navigation_polygon()->get_outline_count();j++) { + + + DVector<Vector2> points=node->get_navigation_polygon()->get_outline(j); + + int pc=points.size(); + DVector<Vector2>::Read poly=points.read(); + + for(int i=0;i<pc;i++) { + + + Vector2 cp =xform.xform(poly[i]); + + real_t d = cp.distance_to(gpoint); + if (d<closest_dist && d<grab_treshold) { + closest_dist=d; + closest_pos=cp; + closest_outline=j; + closest_idx=i; + } + } + } + + if (closest_idx>=0) { + + pre_move_edit=node->get_navigation_polygon()->get_outline(closest_outline); + edited_point=closest_idx; + edited_outline=closest_outline; + edited_point_pos=xform.affine_inverse().xform(closest_pos); + canvas_item_editor->get_viewport_control()->update(); + return true; + } + } + } else { + + if (edited_point!=-1) { + + //apply + + DVector<Vector2> poly = node->get_navigation_polygon()->get_outline(edited_outline); + ERR_FAIL_INDEX_V(edited_point,poly.size(),false); + poly.set(edited_point,edited_point_pos); + undo_redo->create_action("Edit Poly"); + undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"set_outline",edited_outline,poly); + undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"set_outline",edited_outline,pre_move_edit); + undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines"); + undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines"); + undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->commit_action(); + + edited_point=-1; + return true; + } + } + } if (mb.button_index==BUTTON_RIGHT && mb.pressed && edited_point==-1) { + + int closest_outline=-1; + int closest_idx=-1; + Vector2 closest_pos; + real_t closest_dist=1e10; + + for(int j=0;j<node->get_navigation_polygon()->get_outline_count();j++) { + + + DVector<Vector2> points=node->get_navigation_polygon()->get_outline(j); + + int pc=points.size(); + DVector<Vector2>::Read poly=points.read(); + + for(int i=0;i<pc;i++) { + + + Vector2 cp =xform.xform(poly[i]); + + real_t d = cp.distance_to(gpoint); + if (d<closest_dist && d<grab_treshold) { + closest_dist=d; + closest_pos=cp; + closest_outline=j; + closest_idx=i; + } + } + } + + if (closest_idx>=0) { + + + DVector<Vector2> poly = node->get_navigation_polygon()->get_outline(closest_outline); + + if (poly.size()>3) { + undo_redo->create_action("Edit Poly (Remove Point)"); + undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"set_outline",closest_outline,poly); + poly.remove(closest_idx); + undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"set_outline",closest_outline,poly); + undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines"); + undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines"); + undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->commit_action(); + } else { + + undo_redo->create_action("Remove Poly And Point"); + undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"add_outline_at_index",poly,closest_outline); + poly.remove(closest_idx); + undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"remove_outline",closest_outline); + undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines"); + undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines"); + undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update"); + undo_redo->commit_action(); + + } + return true; + } + } + + + + } break; + } + + + + } break; + case InputEvent::MOUSE_MOTION: { + + const InputEventMouseMotion &mm=p_event.mouse_motion; + + if (edited_point!=-1 && (wip_active || mm.button_mask&BUTTON_MASK_LEFT)) { + + Vector2 gpoint = Point2(mm.x,mm.y); + Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint); + cpoint=snap_point(cpoint); + edited_point_pos = node->get_global_transform().affine_inverse().xform(cpoint); + + canvas_item_editor->get_viewport_control()->update(); + + } + + } break; + } + + return false; +} +void NavigationPolygonEditor::_canvas_draw() { + + if (!node) + return; + + Control *vpc = canvas_item_editor->get_viewport_control(); + if (node->get_navigation_polygon().is_null()) + return; + + Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); + Ref<Texture> handle= get_icon("EditorHandle","EditorIcons"); + + + + for(int j=-1;j<node->get_navigation_polygon()->get_outline_count();j++) { + Vector<Vector2> poly; + + if (wip_active && j==edited_outline) { + poly=wip; + } else { + if (j==-1) + continue; + poly = Variant(node->get_navigation_polygon()->get_outline(j)); + } + + int len = poly.size(); + + for(int i=0;i<poly.size();i++) { + + + Vector2 p,p2; + p = (j==edited_outline && i==edited_point) ? edited_point_pos : poly[i]; + if (j==edited_outline && ((wip_active && i==poly.size()-1) || (((i+1)%poly.size())==edited_point))) + p2=edited_point_pos; + else + p2 = poly[(i+1)%poly.size()]; + + Vector2 point = xform.xform(p); + Vector2 next_point = xform.xform(p2); + + Color col=Color(1,0.3,0.1,0.8); + vpc->draw_line(point,next_point,col,2); + vpc->draw_texture(handle,point-handle->get_size()*0.5); + } + } +} + + + +void NavigationPolygonEditor::edit(Node *p_collision_polygon) { + + if (!canvas_item_editor) { + canvas_item_editor=CanvasItemEditor::get_singleton(); + } + + if (p_collision_polygon) { + + node=p_collision_polygon->cast_to<NavigationPolygonInstance>(); + if (!canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw")) + canvas_item_editor->get_viewport_control()->connect("draw",this,"_canvas_draw"); + wip.clear(); + wip_active=false; + edited_point=-1; + + } else { + node=NULL; + + if (canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw")) + canvas_item_editor->get_viewport_control()->disconnect("draw",this,"_canvas_draw"); + + } + +} + +void NavigationPolygonEditor::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_menu_option"),&NavigationPolygonEditor::_menu_option); + ObjectTypeDB::bind_method(_MD("_canvas_draw"),&NavigationPolygonEditor::_canvas_draw); + ObjectTypeDB::bind_method(_MD("_node_removed"),&NavigationPolygonEditor::_node_removed); + ObjectTypeDB::bind_method(_MD("_create_nav"),&NavigationPolygonEditor::_create_nav); + +} + +NavigationPolygonEditor::NavigationPolygonEditor(EditorNode *p_editor) { + + canvas_item_editor=NULL; + editor=p_editor; + undo_redo = editor->get_undo_redo(); + + add_child( memnew( VSeparator )); + button_create = memnew( ToolButton ); + add_child(button_create); + button_create->connect("pressed",this,"_menu_option",varray(MODE_CREATE)); + button_create->set_toggle_mode(true); + button_create->set_tooltip("Create a new polygon from scratch"); + + 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("Edit existing polygon:\nLMB: Move Point.\nCtrl+LMB: Split Segment.\nRMB: Erase Point."); + create_nav = memnew( ConfirmationDialog ); + add_child(create_nav); + create_nav->get_ok()->set_text("Create"); + + + //add_constant_override("separation",0); + +#if 0 + options = memnew( MenuButton ); + add_child(options); + options->set_area_as_parent_rect(); + options->set_text("Polygon"); + //options->get_popup()->add_item("Parse BBCODE",PARSE_BBCODE); + options->get_popup()->connect("item_pressed", this,"_menu_option"); +#endif + + mode = MODE_EDIT; + wip_active=false; + edited_outline=-1; + +} + + +void NavigationPolygonEditorPlugin::edit(Object *p_object) { + + collision_polygon_editor->edit(p_object->cast_to<Node>()); +} + +bool NavigationPolygonEditorPlugin::handles(Object *p_object) const { + + return p_object->is_type("NavigationPolygonInstance"); +} + +void NavigationPolygonEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + collision_polygon_editor->show(); + } else { + + collision_polygon_editor->hide(); + collision_polygon_editor->edit(NULL); + } + +} + +NavigationPolygonEditorPlugin::NavigationPolygonEditorPlugin(EditorNode *p_node) { + + editor=p_node; + collision_polygon_editor = memnew( NavigationPolygonEditor(p_node) ); + CanvasItemEditor::get_singleton()->add_control_to_menu_panel(collision_polygon_editor); + + collision_polygon_editor->hide(); + + + +} + + +NavigationPolygonEditorPlugin::~NavigationPolygonEditorPlugin() +{ +} + diff --git a/tools/editor/plugins/navigation_polygon_editor_plugin.h b/tools/editor/plugins/navigation_polygon_editor_plugin.h new file mode 100644 index 0000000000..a86d28c8a8 --- /dev/null +++ b/tools/editor/plugins/navigation_polygon_editor_plugin.h @@ -0,0 +1,91 @@ +#ifndef NAVIGATIONPOLYGONEDITORPLUGIN_H +#define NAVIGATIONPOLYGONEDITORPLUGIN_H + + + +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" +#include "scene/2d/navigation_polygon.h" +#include "scene/gui/tool_button.h" +#include "scene/gui/button_group.h" + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ +class CanvasItemEditor; + +class NavigationPolygonEditor : public HBoxContainer { + + OBJ_TYPE(NavigationPolygonEditor, HBoxContainer ); + + UndoRedo *undo_redo; + enum Mode { + + MODE_CREATE, + MODE_EDIT, + + }; + + Mode mode; + + ToolButton *button_create; + ToolButton *button_edit; + + ConfirmationDialog *create_nav; + + CanvasItemEditor *canvas_item_editor; + EditorNode *editor; + Panel *panel; + NavigationPolygonInstance *node; + MenuButton *options; + + int edited_outline; + int edited_point; + Vector2 edited_point_pos; + DVector<Vector2> pre_move_edit; + Vector<Vector2> wip; + bool wip_active; + + + void _wip_close(); + void _canvas_draw(); + void _create_nav(); + + void _menu_option(int p_option); + +protected: + void _notification(int p_what); + void _node_removed(Node *p_node); + static void _bind_methods(); +public: + + Vector2 snap_point(const Vector2& p_point) const; + bool forward_input_event(const InputEvent& p_event); + void edit(Node *p_collision_polygon); + NavigationPolygonEditor(EditorNode *p_editor); +}; + +class NavigationPolygonEditorPlugin : public EditorPlugin { + + OBJ_TYPE( NavigationPolygonEditorPlugin, EditorPlugin ); + + NavigationPolygonEditor *collision_polygon_editor; + EditorNode *editor; + +public: + + virtual bool forward_input_event(const InputEvent& p_event) { return collision_polygon_editor->forward_input_event(p_event); } + + virtual String get_name() const { return "NavigationPolygonInstance"; } + bool has_main_screen() const { return false; } + virtual void edit(Object *p_node); + virtual bool handles(Object *p_node) const; + virtual void make_visible(bool p_visible); + + NavigationPolygonEditorPlugin(EditorNode *p_node); + ~NavigationPolygonEditorPlugin(); + +}; + + +#endif // NAVIGATIONPOLYGONEDITORPLUGIN_H diff --git a/tools/editor/plugins/script_editor_plugin.cpp b/tools/editor/plugins/script_editor_plugin.cpp index d90597ddfb..1349d5ccab 100644 --- a/tools/editor/plugins/script_editor_plugin.cpp +++ b/tools/editor/plugins/script_editor_plugin.cpp @@ -631,7 +631,10 @@ bool ScriptEditor::_test_script_times_on_disk() { if (!all_ok) - disk_changed->call_deferred("popup_centered_ratio",0.5); + if (bool(EDITOR_DEF("text_editor/auto_reload_changed_scripts",false))) + script_editor->_reload_scripts(); + else + disk_changed->call_deferred("popup_centered_ratio",0.5); return all_ok; } @@ -1607,7 +1610,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { edit_menu->get_popup()->add_item("Clone Down",EDIT_CLONE_DOWN,KEY_MASK_CMD|KEY_B); edit_menu->get_popup()->add_separator(); #ifdef OSX_ENABLED - edit_menu->get_popup()->add_item("Complete Symbol",EDIT_COMPLETE,KEY_MASK_META|KEY_SPACE); + edit_menu->get_popup()->add_item("Complete Symbol",EDIT_COMPLETE,KEY_MASK_CTRL|KEY_SPACE); #else edit_menu->get_popup()->add_item("Complete Symbol",EDIT_COMPLETE,KEY_MASK_CMD|KEY_SPACE); #endif @@ -1806,6 +1809,7 @@ ScriptEditorPlugin::ScriptEditorPlugin(EditorNode *p_node) { script_editor->hide(); + EDITOR_DEF("text_editor/auto_reload_changed_scripts",false); EDITOR_DEF("external_editor/use_external_editor",false); EDITOR_DEF("external_editor/exec_path",""); EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,"external_editor/exec_path",PROPERTY_HINT_GLOBAL_FILE)); diff --git a/tools/editor/plugins/shader_editor_plugin.cpp b/tools/editor/plugins/shader_editor_plugin.cpp index 81b5cd8f78..2fcd4e8cd1 100644 --- a/tools/editor/plugins/shader_editor_plugin.cpp +++ b/tools/editor/plugins/shader_editor_plugin.cpp @@ -396,7 +396,7 @@ void ShaderEditor::edit(const Ref<Shader>& p_shader) { light_editor->set_edited_shader(shader,ShaderLanguage::SHADER_CANVAS_ITEM_LIGHT); } - vertex_editor->set_edited_shader(shader,ShaderLanguage::SHADER_MATERIAL_VERTEX); + //vertex_editor->set_edited_shader(shader,ShaderLanguage::SHADER_MATERIAL_VERTEX); // see if already has it diff --git a/tools/editor/plugins/shader_graph_editor_plugin.cpp b/tools/editor/plugins/shader_graph_editor_plugin.cpp index 0a206c4a45..508e8b4cba 100644 --- a/tools/editor/plugins/shader_graph_editor_plugin.cpp +++ b/tools/editor/plugins/shader_graph_editor_plugin.cpp @@ -32,6 +32,642 @@ #include "scene/gui/menu_button.h" #include "scene/gui/panel.h" #include "spatial_editor_plugin.h" +#include "os/keyboard.h" +#include "canvas_item_editor_plugin.h" + +void GraphColorRampEdit::_input_event(const InputEvent& p_event) { + + if (p_event.type==InputEvent::KEY && p_event.key.pressed && p_event.key.scancode==KEY_DELETE && grabbed!=-1) { + + points.remove(grabbed); + grabbed=-1; + update(); + emit_signal("ramp_changed"); + accept_event(); + } + + if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1 && p_event.mouse_button.pressed) { + + update(); + int x = p_event.mouse_button.x; + int total_w = get_size().width-get_size().height-3; + if (x>total_w+3) { + + if (grabbed==-1) + return; + Size2 ms = Size2(350, picker->get_combined_minimum_size().height+10); + picker->set_color(points[grabbed].color); + popup->set_pos(get_global_pos()-Size2(0,ms.height)); + popup->set_size(ms); + popup->popup(); + return; + } + + + float ofs = CLAMP(x/float(total_w),0,1); + + grabbed=-1; + grabbing=true; + int pos=-1; + for(int i=0;i<points.size();i++) { + + if (ABS(x-points[i].offset*total_w)<4) { + grabbed=i; + } + if (points[i].offset<ofs) + pos=i; + } + + grabbed_at=ofs; + //grab or select + if (grabbed!=-1) { + return; + } + //insert + + + Point p; + p.offset=ofs; + + Point prev; + Point next; + + if (pos==-1) { + + prev.color=Color(0,0,0); + prev.offset=0; + if (points.size()) { + next=points[0]; + } else { + next.color=Color(1,1,1); + next.offset=1.0; + } + } else { + + if (pos==points.size()-1) { + next.color=Color(1,1,1); + next.offset=1.0; + } else { + next=points[pos+1]; + } + prev=points[pos]; + + } + + p.color=prev.color.linear_interpolate(next.color,(p.offset-prev.offset)/(next.offset-prev.offset)); + + points.push_back(p); + points.sort(); + for(int i=0;i<points.size();i++) { + if (points[i].offset==ofs) { + grabbed=i; + break; + } + } + + emit_signal("ramp_changed"); + + } + + if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1 && !p_event.mouse_button.pressed) { + + if (grabbing) { + grabbing=false; + emit_signal("ramp_changed"); + } + update(); + } + + if (p_event.type==InputEvent::MOUSE_MOTION && grabbing) { + + int total_w = get_size().width-get_size().height-3; + + int x = p_event.mouse_motion.x; + float newofs = CLAMP(x/float(total_w),0,1); + + bool valid=true; + for(int i=0;i<points.size();i++) { + + if (points[i].offset==newofs && i!=grabbed) { + valid=false; + } + } + + if (!valid) + return; + + points[grabbed].offset=newofs; + + points.sort(); + for(int i=0;i<points.size();i++) { + if (points[i].offset==newofs) { + grabbed=i; + break; + } + } + + emit_signal("ramp_changed"); + + update(); + } +} + +void GraphColorRampEdit::_notification(int p_what){ + + if (p_what==NOTIFICATION_ENTER_TREE) { + picker->connect("color_changed",this,"_color_changed"); + } + if (p_what==NOTIFICATION_DRAW) { + + + Point prev; + prev.offset=0; + prev.color=Color(0,0,0); + int w = get_size().x; + int h = get_size().y; + + int total_w = get_size().width-get_size().height-3; + + for(int i=-1;i<points.size();i++) { + + Point next; + if (i+1==points.size()) { + next.color=Color(1,1,1); + next.offset=1; + } else { + next=points[i+1]; + } + + if (prev.offset==next.offset) { + prev=next; + continue; + } + + Vector<Vector2> points; + Vector<Color> colors; + points.push_back(Vector2(prev.offset*total_w,h)); + points.push_back(Vector2(prev.offset*total_w,0)); + points.push_back(Vector2(next.offset*total_w,0)); + points.push_back(Vector2(next.offset*total_w,h)); + colors.push_back(prev.color); + colors.push_back(prev.color); + colors.push_back(next.color); + colors.push_back(next.color); + draw_primitive(points,colors,Vector<Point2>()); + prev=next; + } + + for(int i=0;i<points.size();i++) { + + Color col=i==grabbed?Color(1,0.0,0.0,0.9):Color(1,1,1,0.8); + + draw_line(Vector2(points[i].offset*total_w,0),Vector2(points[i].offset*total_w,h-1),Color(0,0,0,0.7)); + draw_line(Vector2(points[i].offset*total_w-1,h/2),Vector2(points[i].offset*total_w-1,h-1),col); + draw_line(Vector2(points[i].offset*total_w+1,h/2),Vector2(points[i].offset*total_w+1,h-1),col); + draw_line(Vector2(points[i].offset*total_w-1,h/2),Vector2(points[i].offset*total_w+1,h/2),col); + draw_line(Vector2(points[i].offset*total_w-1,h-1),Vector2(points[i].offset*total_w+1,h-1),col); + + } + + if (grabbed!=-1) { + + draw_rect(Rect2(total_w+3,0,h,h),points[grabbed].color); + } + + if (has_focus()) { + + draw_line(Vector2(-1,-1),Vector2(total_w+1,-1),Color(1,1,1,0.6)); + draw_line(Vector2(total_w+1,-1),Vector2(total_w+1,h+1),Color(1,1,1,0.6)); + draw_line(Vector2(total_w+1,h+1),Vector2(-1,h+1),Color(1,1,1,0.6)); + draw_line(Vector2(-1,-1),Vector2(-1,h+1),Color(1,1,1,0.6)); + } + + } +} + +Size2 GraphColorRampEdit::get_minimum_size() const { + + return Vector2(0,16); +} + + +void GraphColorRampEdit::_color_changed(const Color& p_color) { + + if (grabbed==-1) + return; + points[grabbed].color=p_color; + update(); + emit_signal("ramp_changed"); + +} + +void GraphColorRampEdit::set_ramp(const Vector<float>& p_offsets,const Vector<Color>& p_colors) { + + ERR_FAIL_COND(p_offsets.size()!=p_colors.size()); + points.clear(); + for(int i=0;i<p_offsets.size();i++) { + Point p; + p.offset=p_offsets[i]; + p.color=p_colors[i]; + points.push_back(p); + } + + points.sort(); + update(); +} + +Vector<float> GraphColorRampEdit::get_offsets() const{ + Vector<float> ret; + for(int i=0;i<points.size();i++) + ret.push_back(points[i].offset); + return ret; +} +Vector<Color> GraphColorRampEdit::get_colors() const{ + + Vector<Color> ret; + for(int i=0;i<points.size();i++) + ret.push_back(points[i].color); + return ret; +} + + +void GraphColorRampEdit::_bind_methods(){ + + ObjectTypeDB::bind_method(_MD("_input_event"),&GraphColorRampEdit::_input_event); + ObjectTypeDB::bind_method(_MD("_color_changed"),&GraphColorRampEdit::_color_changed); + ADD_SIGNAL(MethodInfo("ramp_changed")); +} + +GraphColorRampEdit::GraphColorRampEdit(){ + + grabbed=-1; + grabbing=false; + set_focus_mode(FOCUS_ALL); + + popup = memnew( PopupPanel ); + picker = memnew( ColorPicker ); + popup->add_child(picker); + popup->set_child_rect(picker); + add_child(popup); + +} +//////////// + +void GraphCurveMapEdit::_input_event(const InputEvent& p_event) { + + if (p_event.type==InputEvent::KEY && p_event.key.pressed && p_event.key.scancode==KEY_DELETE && grabbed!=-1) { + + points.remove(grabbed); + grabbed=-1; + update(); + emit_signal("curve_changed"); + accept_event(); + } + + if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1 && p_event.mouse_button.pressed) { + + update(); + Point2 p = Vector2(p_event.mouse_button.x,p_event.mouse_button.y)/get_size(); + p.y=1.0-p.y; + grabbed=-1; + grabbing=true; + + for(int i=0;i<points.size();i++) { + + Vector2 ps = p*get_size(); + Vector2 pt = Vector2(points[i].offset,points[i].height)*get_size(); + if (ps.distance_to(pt)<4) { + grabbed=i; + } + + } + + + //grab or select + if (grabbed!=-1) { + return; + } + //insert + + + Point np; + np.offset=p.x; + np.height=p.y; + + points.push_back(np); + points.sort(); + for(int i=0;i<points.size();i++) { + if (points[i].offset==p.x && points[i].height==p.y) { + grabbed=i; + break; + } + } + + emit_signal("curve_changed"); + + } + + if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1 && !p_event.mouse_button.pressed) { + + if (grabbing) { + grabbing=false; + emit_signal("curve_changed"); + } + update(); + } + + if (p_event.type==InputEvent::MOUSE_MOTION && grabbing) { + + Point2 p = Vector2(p_event.mouse_button.x,p_event.mouse_button.y)/get_size(); + p.y=1.0-p.y; + + p.x = CLAMP(p.x,0.0,1.0); + p.y = CLAMP(p.y,0.0,1.0); + + bool valid=true; + + for(int i=0;i<points.size();i++) { + + if (points[i].offset==p.x && points[i].height==p.y && i!=grabbed) { + valid=false; + } + } + + if (!valid) + return; + + points[grabbed].offset=p.x; + points[grabbed].height=p.y; + + points.sort(); + for(int i=0;i<points.size();i++) { + if (points[i].offset==p.x && points[i].height==p.y) { + grabbed=i; + break; + } + } + + emit_signal("curve_changed"); + + update(); + } +} + +void GraphCurveMapEdit::_plot_curve(const Vector2& p_a,const Vector2& p_b,const Vector2& p_c,const Vector2& p_d) { + + float geometry[4][4]; + float tmp1[4][4]; + float tmp2[4][4]; + float deltas[4][4]; + double x, dx, dx2, dx3; + double y, dy, dy2, dy3; + double d, d2, d3; + int lastx, lasty; + int newx, newy; + int ntimes; + int i,j; + + int xmax=get_size().x; + int ymax=get_size().y; + + /* construct the geometry matrix from the segment */ + for (i = 0; i < 4; i++) { + geometry[i][2] = 0; + geometry[i][3] = 0; + } + + geometry[0][0] = (p_a[0] * xmax); + geometry[1][0] = (p_b[0] * xmax); + geometry[2][0] = (p_c[0] * xmax); + geometry[3][0] = (p_d[0] * xmax); + + geometry[0][1] = (p_a[1] * ymax); + geometry[1][1] = (p_b[1] * ymax); + geometry[2][1] = (p_c[1] * ymax); + geometry[3][1] = (p_d[1] * ymax); + + /* subdivide the curve ntimes (1000) times */ + ntimes = 4 * xmax; + /* ntimes can be adjusted to give a finer or coarser curve */ + d = 1.0 / ntimes; + d2 = d * d; + d3 = d * d * d; + + /* construct a temporary matrix for determining the forward differencing deltas */ + tmp2[0][0] = 0; tmp2[0][1] = 0; tmp2[0][2] = 0; tmp2[0][3] = 1; + tmp2[1][0] = d3; tmp2[1][1] = d2; tmp2[1][2] = d; tmp2[1][3] = 0; + tmp2[2][0] = 6*d3; tmp2[2][1] = 2*d2; tmp2[2][2] = 0; tmp2[2][3] = 0; + tmp2[3][0] = 6*d3; tmp2[3][1] = 0; tmp2[3][2] = 0; tmp2[3][3] = 0; + + /* compose the basis and geometry matrices */ + + static const float CR_basis[4][4] = + { + { -0.5, 1.5, -1.5, 0.5 }, + { 1.0, -2.5, 2.0, -0.5 }, + { -0.5, 0.0, 0.5, 0.0 }, + { 0.0, 1.0, 0.0, 0.0 }, + }; + + for (i = 0; i < 4; i++) + { + for (j = 0; j < 4; j++) + { + tmp1[i][j] = (CR_basis[i][0] * geometry[0][j] + + CR_basis[i][1] * geometry[1][j] + + CR_basis[i][2] * geometry[2][j] + + CR_basis[i][3] * geometry[3][j]); + } + } + /* compose the above results to get the deltas matrix */ + + for (i = 0; i < 4; i++) + { + for (j = 0; j < 4; j++) + { + deltas[i][j] = (tmp2[i][0] * tmp1[0][j] + + tmp2[i][1] * tmp1[1][j] + + tmp2[i][2] * tmp1[2][j] + + tmp2[i][3] * tmp1[3][j]); + } + } + + + /* extract the x deltas */ + x = deltas[0][0]; + dx = deltas[1][0]; + dx2 = deltas[2][0]; + dx3 = deltas[3][0]; + + /* extract the y deltas */ + y = deltas[0][1]; + dy = deltas[1][1]; + dy2 = deltas[2][1]; + dy3 = deltas[3][1]; + + + lastx = CLAMP (x, 0, xmax); + lasty = CLAMP (y, 0, ymax); + +/* if (fix255) + { + cd->curve[cd->outline][lastx] = lasty; + } + else + { + cd->curve_ptr[cd->outline][lastx] = lasty; + if(gb_debug) printf("bender_plot_curve xmax:%d ymax:%d\n", (int)xmax, (int)ymax); + } +*/ + /* loop over the curve */ + for (i = 0; i < ntimes; i++) + { + /* increment the x values */ + x += dx; + dx += dx2; + dx2 += dx3; + + /* increment the y values */ + y += dy; + dy += dy2; + dy2 += dy3; + + newx = CLAMP ((Math::round (x)), 0, xmax); + newy = CLAMP ((Math::round (y)), 0, ymax); + + /* if this point is different than the last one...then draw it */ + if ((lastx != newx) || (lasty != newy)) + { +#if 0 + /* + if(fix255) + { + /* use fixed array size (for the curve graph) */ + cd->curve[cd->outline][newx] = newy; + } + else + { + /* use dynamic allocated curve_ptr (for the real curve) */ + cd->curve_ptr[cd->outline][newx] = newy; + + if(gb_debug) printf("outline: %d cX: %d cY: %d\n", (int)cd->outline, (int)newx, (int)newy); + } +#endif + draw_line(Vector2(lastx,ymax-lasty),Vector2(newx,ymax-newy),Color(0.8,0.8,0.8,0.8),2.0); + } + + lastx = newx; + lasty = newy; + } +} + + +void GraphCurveMapEdit::_notification(int p_what){ + + if (p_what==NOTIFICATION_DRAW) { + + draw_style_box(get_stylebox("bg","Tree"),Rect2(Point2(),get_size())); + + int w = get_size().x; + int h = get_size().y; + + Vector2 prev=Vector2(0,0); + Vector2 prev2=Vector2(0,0); + + for(int i=-1;i<points.size();i++) { + + Vector2 next; + Vector2 next2; + if (i+1>=points.size()) { + next=Vector2(1,1); + } else { + next=Vector2(points[i+1].offset,points[i+1].height); + } + + if (i+2>=points.size()) { + next2=Vector2(1,1); + } else { + next2=Vector2(points[i+2].offset,points[i+2].height); + } + + /*if (i==-1 && prev.offset==next.offset) { + prev=next; + continue; + }*/ + + _plot_curve(prev2,prev,next,next2); + + prev2=prev; + prev=next; + } + + for(int i=0;i<points.size();i++) { + + Color col=i==grabbed?Color(1,0.0,0.0,0.9):Color(1,1,1,0.8); + + + draw_rect(Rect2( Vector2(points[i].offset,1.0-points[i].height)*get_size()-Vector2(2,2),Vector2(5,5)),col); + } + +/* if (grabbed!=-1) { + + draw_rect(Rect2(total_w+3,0,h,h),points[grabbed].color); + } +*/ + if (has_focus()) { + + draw_line(Vector2(-1,-1),Vector2(w+1,-1),Color(1,1,1,0.6)); + draw_line(Vector2(w+1,-1),Vector2(w+1,h+1),Color(1,1,1,0.6)); + draw_line(Vector2(w+1,h+1),Vector2(-1,h+1),Color(1,1,1,0.6)); + draw_line(Vector2(-1,-1),Vector2(-1,h+1),Color(1,1,1,0.6)); + } + + } +} + +Size2 GraphCurveMapEdit::get_minimum_size() const { + + return Vector2(64,64); +} + + + +void GraphCurveMapEdit::set_points(const Vector<Vector2>& p_points) { + + + points.clear(); + for(int i=0;i<p_points.size();i++) { + Point p; + p.offset=p_points[i].x; + p.height=p_points[i].y; + points.push_back(p); + } + + points.sort(); + update(); +} + +Vector<Vector2> GraphCurveMapEdit::get_points() const { + Vector<Vector2> ret; + for(int i=0;i<points.size();i++) + ret.push_back(Vector2(points[i].offset,points[i].height)); + return ret; +} + +void GraphCurveMapEdit::_bind_methods(){ + + ObjectTypeDB::bind_method(_MD("_input_event"),&GraphCurveMapEdit::_input_event); + ADD_SIGNAL(MethodInfo("curve_changed")); +} + +GraphCurveMapEdit::GraphCurveMapEdit(){ + + grabbed=-1; + grabbing=false; + set_focus_mode(FOCUS_ALL); + +} + ////cbacks /// @@ -306,6 +942,84 @@ void ShaderGraphView::_comment_edited(int p_id,Node* p_button) { } +void ShaderGraphView::_color_ramp_changed(int p_id,Node* p_ramp) { + + GraphColorRampEdit *cr=p_ramp->cast_to<GraphColorRampEdit>(); + + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + + + Vector<float> offsets=cr->get_offsets(); + Vector<Color> colors=cr->get_colors(); + + DVector<float> new_offsets; + DVector<Color> new_colors; + { + new_offsets.resize(offsets.size()); + new_colors.resize(colors.size()); + DVector<float>::Write ow=new_offsets.write(); + DVector<Color>::Write cw=new_colors.write(); + for(int i=0;i<new_offsets.size();i++) { + ow[i]=offsets[i]; + cw[i]=colors[i]; + } + + } + + + DVector<float> old_offsets=graph->color_ramp_node_get_offsets(type,p_id); + DVector<Color> old_colors=graph->color_ramp_node_get_colors(type,p_id); + + if (old_offsets.size()!=new_offsets.size()) + ur->create_action("Add/Remove to Color Ramp"); + else + ur->create_action("Modify Color Ramp",true); + + ur->add_do_method(graph.ptr(),"color_ramp_node_set_ramp",type,p_id,new_colors,new_offsets); + ur->add_undo_method(graph.ptr(),"color_ramp_node_set_ramp",type,p_id,old_colors,old_offsets); + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + block_update=true; + ur->commit_action(); + block_update=false; +} + +void ShaderGraphView::_curve_changed(int p_id,Node* p_curve) { + + GraphCurveMapEdit *cr=p_curve->cast_to<GraphCurveMapEdit>(); + + UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo(); + + + Vector<Point2> points=cr->get_points(); + + DVector<Vector2> new_points; + { + new_points.resize(points.size()); + DVector<Vector2>::Write ow=new_points.write(); + for(int i=0;i<new_points.size();i++) { + ow[i]=points[i]; + } + + } + + + DVector<Vector2> old_points=graph->curve_map_node_get_points(type,p_id); + + if (old_points.size()!=new_points.size()) + ur->create_action("Add/Remove to Curve Map"); + else + ur->create_action("Modify Curve Map",true); + + ur->add_do_method(graph.ptr(),"curve_map_node_set_points",type,p_id,new_points); + ur->add_undo_method(graph.ptr(),"curve_map_node_set_points",type,p_id,old_points); + ur->add_do_method(this,"_update_graph"); + ur->add_undo_method(this,"_update_graph"); + block_update=true; + ur->commit_action(); + block_update=false; +} + void ShaderGraphView::_input_name_changed(const String& p_name, int p_id, Node *p_line_edit) { @@ -1026,6 +1740,96 @@ void ShaderGraphView::_create_node(int p_id) { gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],false,0,Color()); } break; // vec3 interpolation (with optional curve) + case ShaderGraph::NODE_COLOR_RAMP: { + + gn->set_title("ColorRamp"); + GraphColorRampEdit * ramp = memnew( GraphColorRampEdit ); + + DVector<real_t> offsets = graph->color_ramp_node_get_offsets(type,p_id); + DVector<Color> colors = graph->color_ramp_node_get_colors(type,p_id); + + int oc = offsets.size(); + + if (oc) { + DVector<real_t>::Read rofs = offsets.read(); + DVector<Color>::Read rcol = colors.read(); + + Vector<float> ofsv; + Vector<Color> colorv; + for(int i=0;i<oc;i++) { + ofsv.push_back(rofs[i]); + colorv.push_back(rcol[i]); + } + + ramp->set_ramp(ofsv,colorv); + + } + + ramp->connect("ramp_changed",this,"_color_ramp_changed",varray(p_id,ramp)); + ramp->set_custom_minimum_size(Size2(128,1)); + gn->add_child(ramp); + + + HBoxContainer *hbc = memnew( HBoxContainer ); + hbc->add_constant_override("separation",0); + hbc->add_child( memnew(Label("c"))); + hbc->add_spacer(); + Label *l=memnew(Label("rgb")); + l->set_align(Label::ALIGN_RIGHT); + hbc->add_child( l); + gn->add_child(hbc); + l=memnew(Label("alpha")); + l->set_align(Label::ALIGN_RIGHT); + gn->add_child( l); + + + gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]); + gn->set_slot(2,false,ShaderGraph::SLOT_MAX,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]); + + + } break; // scalar interpolation (with optional curve) + case ShaderGraph::NODE_CURVE_MAP: { + + gn->set_title("CurveMap"); + GraphCurveMapEdit * map = memnew( GraphCurveMapEdit ); + + DVector<Vector2> points = graph->curve_map_node_get_points(type,p_id); + + int oc = points.size(); + + if (oc) { + DVector<Vector2>::Read rofs = points.read(); + + + Vector<Vector2> ofsv; + for(int i=0;i<oc;i++) { + ofsv.push_back(rofs[i]); + } + + map->set_points(ofsv); + + } + map->connect("curve_changed",this,"_curve_changed",varray(p_id,map)); + + //map->connect("map_changed",this,"_curve_map_changed",varray(p_id,map)); + map->set_custom_minimum_size(Size2(128,64)); + gn->add_child(map); + + HBoxContainer *hbc = memnew( HBoxContainer ); + hbc->add_constant_override("separation",0); + hbc->add_child( memnew(Label("c"))); + hbc->add_spacer(); + Label *l=memnew(Label("cmap")); + l->set_align(Label::ALIGN_RIGHT); + hbc->add_child( l); + gn->add_child(hbc); + + + gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]); + + + } break; // scalar interpolation (with optional curve) + case ShaderGraph::NODE_SCALAR_INPUT: { gn->set_title("ScalarUniform"); @@ -1173,6 +1977,28 @@ void ShaderGraphView::_create_node(int p_id) { gn->set_slot(3,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]); } break; // cubemap input (assignable in material) + case ShaderGraph::NODE_DEFAULT_TEXTURE: { + + gn->set_title("CanvasItemTex"); + HBoxContainer *hbc = memnew( HBoxContainer ); + hbc->add_constant_override("separation",0); + hbc->add_child( memnew(Label("UV"))); + hbc->add_spacer(); + Label *l=memnew(Label("RGB")); + l->set_align(Label::ALIGN_RIGHT); + hbc->add_child(l); + gn->add_child(hbc); + l = memnew( Label ); + l->set_text("Alpha"); + l->set_align(Label::ALIGN_RIGHT); + gn->add_child(l); + + gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]); + gn->set_slot(1,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]); + + + } break; // screen texture sampler (takes UV) (only usable in fragment case Shader) + case ShaderGraph::NODE_OUTPUT: { gn->set_title("Output"); @@ -1360,6 +2186,8 @@ void ShaderGraphView::_bind_methods() { ObjectTypeDB::bind_method("_variant_edited",&ShaderGraphView::_variant_edited); ObjectTypeDB::bind_method("_cube_edited",&ShaderGraphView::_cube_edited); ObjectTypeDB::bind_method("_comment_edited",&ShaderGraphView::_comment_edited); + ObjectTypeDB::bind_method("_color_ramp_changed",&ShaderGraphView::_color_ramp_changed); + ObjectTypeDB::bind_method("_curve_changed",&ShaderGraphView::_curve_changed); ObjectTypeDB::bind_method("_sg_updated",&ShaderGraphView::_sg_updated); } @@ -1406,6 +2234,9 @@ void ShaderGraphEditor::_notification(int p_what) { if (i==ShaderGraph::NODE_OUTPUT) continue; + if (!_2d && i==ShaderGraph::NODE_DEFAULT_TEXTURE) + continue; + String nn = node_names[i]; String ic = nn.get_slice(":",0); String v = nn.get_slice(":",1); @@ -1455,18 +2286,22 @@ const char* ShaderGraphEditor::node_names[ShaderGraph::NODE_TYPE_MAX]={ "GraphVecsToXform:Vectors -> XForm:", // 3 vec input", 1 xform output "GraphScalarInterp:Scalar Interpolate", // scalar interpolation (with optional curve) "GraphVecInterp:Vector Interpolate:", // vec3 interpolation (with optional curve) - "GraphScalarUniform:Scalar Uniform", // scalar uniform (assignable in material) + "GraphColorRamp:Color Ramp", // vec3 interpolation (with optional curve) + "GraphCurveMap:Curve Remap:", // vec3 interpolation (with optional curve) + "GraphScalarUniform:Scalar Uniform", // scalar uniform (assignable in material) "GraphVectorUniform:Vector Uniform", // vec3 uniform (assignable in material) "GraphRgbUniform:RGB Uniform", // color uniform (assignable in material) "GraphXformUniform:XForm Uniform", // mat4 uniform (assignable in material) "GraphTextureUniform:Texture Uniform", // texture input (assignable in material) "GraphCubeUniform:CubeMap Uniform:", // cubemap input (assignable in material) - "Output", // output (shader type dependent) + "GraphDefaultTexture:CanvasItem Texture:", // cubemap input (assignable in material) + "Output", // output (shader type dependent) "GraphComment:Comment", // comment }; -ShaderGraphEditor::ShaderGraphEditor() { +ShaderGraphEditor::ShaderGraphEditor(bool p_2d) { + _2d=p_2d; HBoxContainer *hbc = memnew( HBoxContainer ); menu = memnew( MenuButton ); @@ -1508,7 +2343,13 @@ void ShaderGraphEditorPlugin::edit(Object *p_object) { bool ShaderGraphEditorPlugin::handles(Object *p_object) const { - return p_object->is_type("ShaderGraph"); + ShaderGraph *shader=p_object->cast_to<ShaderGraph>(); + if (!shader) + return false; + if (_2d) + return shader->get_mode()==Shader::MODE_CANVAS_ITEM; + else + return shader->get_mode()==Shader::MODE_MATERIAL; } void ShaderGraphEditorPlugin::make_visible(bool p_visible) { @@ -1522,12 +2363,16 @@ void ShaderGraphEditorPlugin::make_visible(bool p_visible) { } -ShaderGraphEditorPlugin::ShaderGraphEditorPlugin(EditorNode *p_node) { +ShaderGraphEditorPlugin::ShaderGraphEditorPlugin(EditorNode *p_node, bool p_2d) { + _2d=p_2d; editor=p_node; - shader_editor = memnew( ShaderGraphEditor ); + shader_editor = memnew( ShaderGraphEditor(p_2d) ); shader_editor->hide(); - SpatialEditor::get_singleton()->get_shader_split()->add_child(shader_editor); + if (p_2d) + CanvasItemEditor::get_singleton()->get_bottom_split()->add_child(shader_editor); + else + SpatialEditor::get_singleton()->get_shader_split()->add_child(shader_editor); // editor->get_viewport()->add_child(shader_editor); diff --git a/tools/editor/plugins/shader_graph_editor_plugin.h b/tools/editor/plugins/shader_graph_editor_plugin.h index bd983c59be..1726302e90 100644 --- a/tools/editor/plugins/shader_graph_editor_plugin.h +++ b/tools/editor/plugins/shader_graph_editor_plugin.h @@ -45,6 +45,77 @@ */ +class GraphColorRampEdit : public Control { + + OBJ_TYPE(GraphColorRampEdit,Control); + + + struct Point { + + float offset; + Color color; + bool operator<(const Point& p_ponit) const { + return offset<p_ponit.offset; + } + }; + + PopupPanel *popup; + ColorPicker *picker; + + + bool grabbing; + int grabbed; + float grabbed_at; + Vector<Point> points; + + void _color_changed(const Color& p_color); + +protected: + void _input_event(const InputEvent& p_event); + void _notification(int p_what); + static void _bind_methods(); +public: + + void set_ramp(const Vector<float>& p_offsets,const Vector<Color>& p_colors); + Vector<float> get_offsets() const; + Vector<Color> get_colors() const; + virtual Size2 get_minimum_size() const; + GraphColorRampEdit(); +}; + + +class GraphCurveMapEdit : public Control { + + OBJ_TYPE(GraphCurveMapEdit,Control); + + + struct Point { + + float offset; + float height; + bool operator<(const Point& p_ponit) const { + return offset<p_ponit.offset; + } + }; + + + bool grabbing; + int grabbed; + Vector<Point> points; + + void _plot_curve(const Vector2& p_a,const Vector2& p_b,const Vector2& p_c,const Vector2& p_d); +protected: + void _input_event(const InputEvent& p_event); + void _notification(int p_what); + static void _bind_methods(); +public: + + void set_points(const Vector<Vector2>& p_points); + Vector<Vector2> get_points() const; + virtual Size2 get_minimum_size() const; + GraphCurveMapEdit(); +}; + class ShaderGraphView : public Node { OBJ_TYPE(ShaderGraphView,Node); @@ -95,7 +166,8 @@ class ShaderGraphView : public Node { void _cube_edited(int p_id,Node* p_button); void _variant_edited(); void _comment_edited(int p_id,Node* p_button); - + void _color_ramp_changed(int p_id,Node* p_ramp); + void _curve_changed(int p_id,Node* p_curve); void _sg_updated(); Map<int,GraphNode*> node_map; protected: @@ -119,6 +191,7 @@ class ShaderGraphEditor : public VBoxContainer { ShaderGraphView *graph_edits[ShaderGraph::SHADER_TYPE_MAX]; static const char* node_names[ShaderGraph::NODE_TYPE_MAX]; + bool _2d; void _add_node(int p_type); protected: void _notification(int p_what); @@ -126,13 +199,14 @@ protected: public: void edit(Ref<ShaderGraph> p_shader); - ShaderGraphEditor(); + ShaderGraphEditor(bool p_2d); }; class ShaderGraphEditorPlugin : public EditorPlugin { OBJ_TYPE( ShaderGraphEditorPlugin, EditorPlugin ); + bool _2d; ShaderGraphEditor *shader_editor; EditorNode *editor; @@ -144,7 +218,7 @@ public: virtual bool handles(Object *p_node) const; virtual void make_visible(bool p_visible); - ShaderGraphEditorPlugin(EditorNode *p_node); + ShaderGraphEditorPlugin(EditorNode *p_node,bool p_2d); ~ShaderGraphEditorPlugin(); }; diff --git a/tools/editor/plugins/tile_map_editor_plugin.cpp b/tools/editor/plugins/tile_map_editor_plugin.cpp index ce5ea58124..47727a00c2 100644 --- a/tools/editor/plugins/tile_map_editor_plugin.cpp +++ b/tools/editor/plugins/tile_map_editor_plugin.cpp @@ -34,7 +34,7 @@ #include "os/file_access.h" #include "tools/editor/editor_settings.h" #include "os/input.h" - +#include "method_bind_ext.inc" void TileMapEditor::_notification(int p_what) { @@ -42,8 +42,13 @@ void TileMapEditor::_notification(int p_what) { case NOTIFICATION_READY: { + transpose->set_icon( get_icon("Transpose","EditorIcons")); mirror_x->set_icon( get_icon("MirrorX","EditorIcons")); mirror_y->set_icon( get_icon("MirrorY","EditorIcons")); + rotate_0->set_icon( get_icon("Rotate0","EditorIcons")); + rotate_90->set_icon( get_icon("Rotate90","EditorIcons")); + rotate_180->set_icon( get_icon("Rotate180","EditorIcons")); + rotate_270->set_icon( get_icon("Rotate270","EditorIcons")); } break; } @@ -85,24 +90,31 @@ void TileMapEditor::set_selected_tile(int p_tile) { } } -void TileMapEditor::_set_cell(const Point2i& p_pos,int p_value,bool p_flip_h, bool p_flip_v,bool p_with_undo) { +// Wrapper to workaround five arg limit of undo/redo methods +void TileMapEditor::_set_cell_shortened(const Point2& p_pos,int p_value,bool p_flip_h, bool p_flip_v, bool p_transpose) { + ERR_FAIL_COND(!node); + node->set_cell(floor(p_pos.x), floor(p_pos.y), p_value, p_flip_h, p_flip_v, p_transpose); +} + +void TileMapEditor::_set_cell(const Point2i& p_pos,int p_value,bool p_flip_h, bool p_flip_v, bool p_transpose,bool p_with_undo) { ERR_FAIL_COND(!node); bool prev_flip_h=node->is_cell_x_flipped(p_pos.x,p_pos.y); bool prev_flip_v=node->is_cell_y_flipped(p_pos.x,p_pos.y); + bool prev_transpose=node->is_cell_transposed(p_pos.x,p_pos.y); int prev_val=node->get_cell(p_pos.x,p_pos.y); - if (p_value==prev_val && p_flip_h==prev_flip_h && p_flip_v==prev_flip_v) + if (p_value==prev_val && p_flip_h==prev_flip_h && p_flip_v==prev_flip_v && p_transpose==prev_transpose) return; //check that it's actually different if (p_with_undo) { - undo_redo->add_do_method(node,"set_cell",p_pos.x,p_pos.y,p_value,p_flip_h,p_flip_v); - undo_redo->add_undo_method(node,"set_cell",p_pos.x,p_pos.y,prev_val,prev_flip_h,prev_flip_v); + undo_redo->add_do_method(this,"_set_cell_shortened",Point2(p_pos),p_value,p_flip_h,p_flip_v,p_transpose); + undo_redo->add_undo_method(this,"_set_cell_shortened",Point2(p_pos),prev_val,prev_flip_h,prev_flip_v,prev_transpose); } else { - node->set_cell(p_pos.x,p_pos.y,p_value,p_flip_h,p_flip_v); + node->set_cell(p_pos.x,p_pos.y,p_value,p_flip_h,p_flip_v,p_transpose); } @@ -168,6 +180,7 @@ struct _TileMapEditorCopyData { int cell; bool flip_h; bool flip_v; + bool transpose; }; bool TileMapEditor::forward_input_event(const InputEvent& p_event) { @@ -204,6 +217,7 @@ bool TileMapEditor::forward_input_event(const InputEvent& p_event) { tcd.cell=node->get_cell(j,i); 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); dupdata.push_back(tcd); @@ -214,7 +228,7 @@ bool TileMapEditor::forward_input_event(const InputEvent& p_event) { for (List<_TileMapEditorCopyData>::Element *E=dupdata.front();E;E=E->next()) { - _set_cell(E->get().pos+ofs,E->get().cell,E->get().flip_h,E->get().flip_v,true); + _set_cell(E->get().pos+ofs,E->get().cell,E->get().flip_h,E->get().flip_v,E->get().transpose,true); } undo_redo->commit_action(); @@ -239,6 +253,10 @@ bool TileMapEditor::forward_input_event(const InputEvent& p_event) { } else if (mb.mod.control) { tool=TOOL_PICKING; set_selected_tile(node->get_cell(over_tile.x, over_tile.y)); + mirror_x->set_pressed(node->is_cell_x_flipped(over_tile.x, over_tile.y)); + mirror_y->set_pressed(node->is_cell_y_flipped(over_tile.x, over_tile.y)); + transpose->set_pressed(node->is_cell_transposed(over_tile.x, over_tile.y)); + _update_transform_buttons(); canvas_item_editor->update(); return true; } else { @@ -248,7 +266,7 @@ bool TileMapEditor::forward_input_event(const InputEvent& p_event) { Point2i local =node->world_to_map((xform_inv.xform(Point2(mb.x,mb.y)))); paint_undo.clear(); paint_undo[local]=_get_op_from_cell(local); - node->set_cell(local.x,local.y,id,mirror_x->is_pressed(),mirror_y->is_pressed()); + node->set_cell(local.x,local.y,id,mirror_x->is_pressed(),mirror_y->is_pressed(),transpose->is_pressed()); return true; } } @@ -263,8 +281,8 @@ bool TileMapEditor::forward_input_event(const InputEvent& p_event) { for(Map<Point2i,CellOp>::Element *E=paint_undo.front();E;E=E->next()) { Point2i p=E->key(); - undo_redo->add_do_method(node,"set_cell",p.x,p.y,node->get_cell(p.x,p.y),node->is_cell_x_flipped(p.x,p.y),node->is_cell_y_flipped(p.x,p.y)); - undo_redo->add_undo_method(node,"set_cell",p.x,p.y,E->get().idx,E->get().xf,E->get().yf); + undo_redo->add_do_method(this,"_set_cell_shortened",Point2(p),node->get_cell(p.x,p.y),node->is_cell_x_flipped(p.x,p.y),node->is_cell_y_flipped(p.x,p.y),node->is_cell_transposed(p.x,p.y)); + undo_redo->add_undo_method(this,"_set_cell_shortened",Point2(p),E->get().idx,E->get().xf,E->get().yf,E->get().tr); } undo_redo->commit_action(); @@ -289,7 +307,7 @@ bool TileMapEditor::forward_input_event(const InputEvent& p_event) { Point2i local =node->world_to_map(xform_inv.xform(Point2(mb.x,mb.y))); paint_undo.clear(); paint_undo[local]=_get_op_from_cell(local); - //node->set_cell(local.x,local.y,id,mirror_x->is_pressed(),mirror_y->is_pressed()); + //node->set_cell(local.x,local.y,id,mirror_x->is_pressed(),mirror_y->is_pressed(),transpose->is_pressed()); //return true; _set_cell(local,TileMap::INVALID_CELL); return true; @@ -302,9 +320,9 @@ bool TileMapEditor::forward_input_event(const InputEvent& p_event) { for(Map<Point2i,CellOp>::Element *E=paint_undo.front();E;E=E->next()) { Point2i p=E->key(); - //undo_redo->add_do_method(node,"set_cell",p.x,p.y,node->get_cell(p.x,p.y),node->is_cell_x_flipped(p.x,p.y),node->is_cell_y_flipped(p.x,p.y)); - _set_cell(p,TileMap::INVALID_CELL,false,false,true); - undo_redo->add_undo_method(node,"set_cell",p.x,p.y,E->get().idx,E->get().xf,E->get().yf); + //undo_redo->add_do_method(node,"set_cell",p.x,p.y,node->get_cell(p.x,p.y),node->is_cell_x_flipped(p.x,p.y),node->is_cell_y_flipped(p.x,p.y),node->is_cell_transposed(p.x,p.y)); + _set_cell(p,TileMap::INVALID_CELL,false,false,false,true); + undo_redo->add_undo_method(this,"_set_cell_shortened",Point2(p),E->get().idx,E->get().xf,E->get().yf,E->get().tr); } undo_redo->commit_action(); @@ -340,7 +358,7 @@ bool TileMapEditor::forward_input_event(const InputEvent& p_event) { paint_undo[over_tile]=_get_op_from_cell(over_tile); } - node->set_cell(over_tile.x,over_tile.y,id,mirror_x->is_pressed(),mirror_y->is_pressed()); + node->set_cell(over_tile.x,over_tile.y,id,mirror_x->is_pressed(),mirror_y->is_pressed(),transpose->is_pressed()); return true; } @@ -373,13 +391,17 @@ bool TileMapEditor::forward_input_event(const InputEvent& p_event) { if (!paint_undo.has(over_tile)) { paint_undo[over_tile]=_get_op_from_cell(over_tile); } - //node->set_cell(over_tile.x,over_tile.y,id,mirror_x->is_pressed(),mirror_y->is_pressed()); + //node->set_cell(over_tile.x,over_tile.y,id,mirror_x->is_pressed(),mirror_y->is_pressed(),transpose->is_pressed()); _set_cell(local,TileMap::INVALID_CELL); return true; } if (tool==TOOL_PICKING) { set_selected_tile(node->get_cell(over_tile.x, over_tile.y)); + mirror_x->set_pressed(node->is_cell_x_flipped(over_tile.x, over_tile.y)); + mirror_y->set_pressed(node->is_cell_y_flipped(over_tile.x, over_tile.y)); + transpose->set_pressed(node->is_cell_transposed(over_tile.x, over_tile.y)); + _update_transform_buttons(); canvas_item_editor->update(); return true; } @@ -627,10 +649,10 @@ void TileMapEditor::_canvas_draw() { sc.y*=-1.0; if (r==Rect2()) { - canvas_item_editor->draw_texture_rect(t,Rect2(from,t->get_size()*sc),false,Color(1,1,1,0.5)); + canvas_item_editor->draw_texture_rect(t,Rect2(from,t->get_size()*sc),false,Color(1,1,1,0.5),transpose->is_pressed()); } else { - canvas_item_editor->draw_texture_rect_region(t,Rect2(from,r.get_size()*sc),r,Color(1,1,1,0.5)); + canvas_item_editor->draw_texture_rect_region(t,Rect2(from,r.get_size()*sc),r,Color(1,1,1,0.5),transpose->is_pressed()); } } } @@ -697,6 +719,8 @@ void TileMapEditor::_bind_methods() { ObjectTypeDB::bind_method(_MD("_canvas_mouse_enter"),&TileMapEditor::_canvas_mouse_enter); ObjectTypeDB::bind_method(_MD("_canvas_mouse_exit"),&TileMapEditor::_canvas_mouse_exit); ObjectTypeDB::bind_method(_MD("_tileset_settings_changed"),&TileMapEditor::_tileset_settings_changed); + ObjectTypeDB::bind_method(_MD("_update_transform_buttons"),&TileMapEditor::_update_transform_buttons); + ObjectTypeDB::bind_method(_MD("_set_cell_shortened","pos","tile","flip_x","flip_y","transpose"),&TileMapEditor::_set_cell_shortened,DEFVAL(false),DEFVAL(false),DEFVAL(false)); } @@ -709,10 +733,60 @@ TileMapEditor::CellOp TileMapEditor::_get_op_from_cell(const Point2i& p_pos) op.xf=true; if (node->is_cell_y_flipped(p_pos.x,p_pos.y)) op.yf=true; + if (node->is_cell_transposed(p_pos.x,p_pos.y)) + op.tr=true; } return op; } +void TileMapEditor::_update_transform_buttons(Object *p_button) { + //ERR_FAIL_NULL(p_button); + ToolButton *b=p_button->cast_to<ToolButton>(); + //ERR_FAIL_COND(!b); + + mirror_x->set_block_signals(true); + mirror_y->set_block_signals(true); + transpose->set_block_signals(true); + rotate_0->set_block_signals(true); + rotate_90->set_block_signals(true); + rotate_180->set_block_signals(true); + rotate_270->set_block_signals(true); + + if (b == rotate_0) { + mirror_x->set_pressed(false); + mirror_y->set_pressed(false); + transpose->set_pressed(false); + } + else if (b == rotate_90) { + mirror_x->set_pressed(true); + mirror_y->set_pressed(false); + transpose->set_pressed(true); + } + else if (b == rotate_180) { + mirror_x->set_pressed(true); + mirror_y->set_pressed(true); + transpose->set_pressed(false); + } + else if (b == rotate_270) { + mirror_x->set_pressed(false); + mirror_y->set_pressed(true); + transpose->set_pressed(true); + } + + rotate_0->set_pressed(!mirror_x->is_pressed() && !mirror_y->is_pressed() && !transpose->is_pressed()); + rotate_90->set_pressed(mirror_x->is_pressed() && !mirror_y->is_pressed() && transpose->is_pressed()); + rotate_180->set_pressed(mirror_x->is_pressed() && mirror_y->is_pressed() && !transpose->is_pressed()); + rotate_270->set_pressed(!mirror_x->is_pressed() && mirror_y->is_pressed() && transpose->is_pressed()); + + mirror_x->set_block_signals(false); + mirror_y->set_block_signals(false); + transpose->set_block_signals(false); + rotate_0->set_block_signals(false); + rotate_90->set_block_signals(false); + rotate_180->set_block_signals(false); + rotate_270->set_block_signals(false); +} + TileMapEditor::TileMapEditor(EditorNode *p_editor) { node=NULL; @@ -734,18 +808,52 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) { canvas_item_editor_hb = memnew( HBoxContainer ); CanvasItemEditor::get_singleton()->add_control_to_menu_panel(canvas_item_editor_hb); canvas_item_editor_hb->add_child( memnew( VSeparator )); + transpose = memnew( ToolButton ); + transpose->set_toggle_mode(true); + transpose->set_tooltip("Transpose"); + transpose->set_focus_mode(FOCUS_NONE); + transpose->connect("pressed", this, "_update_transform_buttons", make_binds(transpose)); + canvas_item_editor_hb->add_child(transpose); mirror_x = memnew( ToolButton ); mirror_x->set_toggle_mode(true); mirror_x->set_tooltip("Mirror X (A)"); mirror_x->set_focus_mode(FOCUS_NONE); + mirror_x->connect("pressed", this, "_update_transform_buttons", make_binds(mirror_x)); canvas_item_editor_hb->add_child(mirror_x); mirror_y = memnew( ToolButton ); mirror_y->set_toggle_mode(true); mirror_y->set_tooltip("Mirror Y (S)"); mirror_y->set_focus_mode(FOCUS_NONE); + mirror_y->connect("pressed", this, "_update_transform_buttons", make_binds(mirror_y)); canvas_item_editor_hb->add_child(mirror_y); + canvas_item_editor_hb->add_child(memnew(VSeparator)); + rotate_0 = memnew( ToolButton ); + rotate_0->set_toggle_mode(true); + rotate_0->set_tooltip("Rotate 0 degrees"); + rotate_0->set_focus_mode(FOCUS_NONE); + rotate_0->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_0)); + canvas_item_editor_hb->add_child(rotate_0); + rotate_90 = memnew( ToolButton ); + rotate_90->set_toggle_mode(true); + rotate_90->set_tooltip("Rotate 90 degrees"); + rotate_90->set_focus_mode(FOCUS_NONE); + rotate_90->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_90)); + canvas_item_editor_hb->add_child(rotate_90); + rotate_180 = memnew( ToolButton ); + rotate_180->set_toggle_mode(true); + rotate_180->set_tooltip("Rotate 180 degrees"); + rotate_180->set_focus_mode(FOCUS_NONE); + rotate_180->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_180)); + canvas_item_editor_hb->add_child(rotate_180); + rotate_270 = memnew( ToolButton ); + rotate_270->set_toggle_mode(true); + rotate_270->set_tooltip("Rotate 270 degrees"); + rotate_270->set_focus_mode(FOCUS_NONE); + rotate_270->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_270)); + canvas_item_editor_hb->add_child(rotate_270); canvas_item_editor_hb->hide(); - + + rotate_0->set_pressed(true); tool=TOOL_NONE; selection_active=false; mouse_over=false; diff --git a/tools/editor/plugins/tile_map_editor_plugin.h b/tools/editor/plugins/tile_map_editor_plugin.h index f3c590e228..367e687d77 100644 --- a/tools/editor/plugins/tile_map_editor_plugin.h +++ b/tools/editor/plugins/tile_map_editor_plugin.h @@ -71,8 +71,13 @@ class TileMapEditor : public VBoxContainer { bool mouse_over; Label *mirror_label; + ToolButton *transpose; ToolButton *mirror_x; ToolButton *mirror_y; + ToolButton *rotate_0; + ToolButton *rotate_90; + ToolButton *rotate_180; + ToolButton *rotate_270; HBoxContainer *canvas_item_editor_hb; @@ -81,8 +86,8 @@ class TileMapEditor : public VBoxContainer { int idx; bool xf; bool yf; - CellOp() { idx=-1; xf=false; yf=false; } - CellOp(const CellOp& p_other) : idx(p_other.idx), xf(p_other.xf), yf(p_other.yf) {} + bool tr; + CellOp() { idx=-1; xf=false; yf=false; tr=false; } }; Map<Point2i,CellOp> paint_undo; @@ -94,7 +99,8 @@ class TileMapEditor : public VBoxContainer { void _canvas_draw(); void _menu_option(int p_option); - void _set_cell(const Point2i& p_pos, int p_value, bool p_flip_h=false, bool p_flip_v=false, bool p_with_undo=false); + void _set_cell(const Point2i& p_pos, int p_value, bool p_flip_h=false, bool p_flip_v=false, bool p_transpose=false, bool p_with_undo=false); + void _set_cell_shortened(const Point2& p_pos, int p_value, bool p_flip_h=false, bool p_flip_v=false, bool p_transpose=false); void _canvas_mouse_enter(); void _canvas_mouse_exit(); @@ -106,6 +112,7 @@ protected: void _node_removed(Node *p_node); static void _bind_methods(); CellOp _get_op_from_cell(const Point2i& p_pos); + void _update_transform_buttons(Object *p_button=0); public: HBoxContainer *get_canvas_item_editor_hb() const { return canvas_item_editor_hb; } diff --git a/tools/editor/property_editor.cpp b/tools/editor/property_editor.cpp index a600683097..25c39b3173 100644 --- a/tools/editor/property_editor.cpp +++ b/tools/editor/property_editor.cpp @@ -1857,8 +1857,33 @@ void PropertyEditor::set_item_text(TreeItem *p_item, int p_type, const String& p } else { p_item->set_text(1,"<"+res->get_type()+">"); }; + + if (has_icon(res->get_type(),"EditorIcons")) { + + p_item->set_icon(1,get_icon(res->get_type(),"EditorIcons")); + } else { + + Dictionary d = p_item->get_metadata(0); + int hint=d.has("hint")?d["hint"].operator int():-1; + String hint_text=d.has("hint_text")?d["hint_text"]:""; + if (hint==PROPERTY_HINT_RESOURCE_TYPE) { + + if (has_icon(hint_text,"EditorIcons")) { + + p_item->set_icon(1,get_icon(hint_text,"EditorIcons")); + + } else { + p_item->set_icon(1,get_icon("Object","EditorIcons")); + + } + } + } + + + } + } break; default: {}; } @@ -2529,7 +2554,10 @@ void PropertyEditor::update_tree() { item->set_editable( 1, !read_only ); item->add_button(1,get_icon("EditResource","EditorIcons")); String type; + if (p.hint==PROPERTY_HINT_RESOURCE_TYPE) + type=p.hint_string; bool notnil=false; + if (obj->get( p.name ).get_type() == Variant::NIL || obj->get( p.name ).operator RefPtr().is_null()) { item->set_text(1,"<null>"); @@ -2553,12 +2581,18 @@ void PropertyEditor::update_tree() { }; notnil=true; + if (has_icon(res->get_type(),"EditorIcons")) { + type=res->get_type(); + } } - if (p.hint==PROPERTY_HINT_RESOURCE_TYPE) { + + if (type!=String()) { + if (type.find(",")!=-1) + type=type.get_slice(",",0); //printf("prop %s , type %s\n",p.name.ascii().get_data(),p.hint_string.ascii().get_data()); - if (has_icon(p.hint_string,"EditorIcons")) - item->set_icon( 0, get_icon(p.hint_string,"EditorIcons") ); + if (has_icon(type,"EditorIcons")) + item->set_icon( 0, get_icon(type,"EditorIcons") ); else item->set_icon( 0, get_icon("Object","EditorIcons") ); } diff --git a/tools/editor/scene_tree_dock.cpp b/tools/editor/scene_tree_dock.cpp index cff3913579..2012d96664 100644 --- a/tools/editor/scene_tree_dock.cpp +++ b/tools/editor/scene_tree_dock.cpp @@ -79,10 +79,22 @@ Node* SceneTreeDock::instance(const String& p_file) { //accept->get_cancel()->hide(); accept->get_ok()->set_text("Ugh"); accept->set_text(String("Error loading scene from ")+p_file); - accept->popup_centered(Size2(300,70));; + accept->popup_centered(Size2(300,70)); return NULL; } + // If the scene hasn't been saved yet a cyclical dependency cannot exist. + if (edited_scene->get_filename()!="") { + + if (_cyclical_dependency_exists(edited_scene->get_filename(), instanced_scene)) { + + accept->get_ok()->set_text("Ok"); + accept->set_text(String("Cannot instance the scene '")+p_file+String("' because the current scene exists within one of its' nodes.")); + accept->popup_centered(Size2(300,90)); + return NULL; + } + } + instanced_scene->generate_instance_state(); instanced_scene->set_filename( Globals::get_singleton()->localize_path(p_file) ); @@ -100,6 +112,25 @@ Node* SceneTreeDock::instance(const String& p_file) { } +bool SceneTreeDock::_cyclical_dependency_exists(const String& p_target_scene_path, Node* p_desired_node) { + int childCount = p_desired_node->get_child_count(); + + if (p_desired_node->get_filename()==p_target_scene_path) { + return true; + } + + for (int i=0;i<childCount;i++) { + Node* child=p_desired_node->get_child(i); + + if(_cyclical_dependency_exists(p_target_scene_path,child)) { + return true; + } + } + + return false; +} + + static String _get_name_num_separator() { switch(EditorSettings::get_singleton()->get("scenetree_editor/duplicate_node_name_num_separator").operator int()) { case 0: return ""; @@ -1223,7 +1254,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor,Node *p_scene_root,EditorSelec tb = memnew( ToolButton ); tb->connect("pressed",this,"_tool_selected",make_binds(TOOL_INSTANCE, false)); - tb->set_tooltip("Instance a Node from scene file."); + tb->set_tooltip("Instance a scene file as a Node."); hbc_top->add_child(tb); tool_buttons[TOOL_INSTANCE]=tb; diff --git a/tools/editor/scene_tree_dock.h b/tools/editor/scene_tree_dock.h index ac5391f3b9..92ebfc5bee 100644 --- a/tools/editor/scene_tree_dock.h +++ b/tools/editor/scene_tree_dock.h @@ -102,6 +102,7 @@ class SceneTreeDock : public VBoxContainer { void _load_request(const String& p_path); void _script_open_request(const Ref<Script>& p_script); + bool _cyclical_dependency_exists(const String& p_target_scene_path, Node* p_desired_node); void _node_selected(); void _node_renamed(); |