diff options
| author | Juan Linietsky | 2014-02-09 22:10:30 -0300 |
|---|---|---|
| committer | Juan Linietsky | 2014-02-09 22:10:30 -0300 |
| commit | 0b806ee0fc9097fa7bda7ac0109191c9c5e0a1ac (patch) | |
| tree | 276c4d099e178eb67fbd14f61d77b05e3808e9e3 /tools/editor/plugins | |
| parent | 0e49da1687bc8192ed210947da52c9e5c5f301bb (diff) | |
| download | godot-0b806ee.tar.gz godot-0b806ee.tar.zst godot-0b806ee.zip | |
GODOT IS OPEN SOURCE
Diffstat (limited to '')
63 files changed, 25894 insertions, 0 deletions
diff --git a/tools/editor/plugins/SCsub b/tools/editor/plugins/SCsub new file mode 100644 index 000000000..b525fb3f7 --- /dev/null +++ b/tools/editor/plugins/SCsub @@ -0,0 +1,7 @@ +Import('env') +Export('env') +env.add_source_files(env.tool_sources,"*.cpp") + + + + diff --git a/tools/editor/plugins/animation_data_editor_plugin.cpp b/tools/editor/plugins/animation_data_editor_plugin.cpp new file mode 100644 index 000000000..17f17bba7 --- /dev/null +++ b/tools/editor/plugins/animation_data_editor_plugin.cpp @@ -0,0 +1,33 @@ +/*************************************************************************/ +/* animation_data_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "animation_data_editor_plugin.h"
+
+AnimationDataEditorPlugin::AnimationDataEditorPlugin()
+{
+}
diff --git a/tools/editor/plugins/animation_data_editor_plugin.h b/tools/editor/plugins/animation_data_editor_plugin.h new file mode 100644 index 000000000..2fd3d5b32 --- /dev/null +++ b/tools/editor/plugins/animation_data_editor_plugin.h @@ -0,0 +1,38 @@ +/*************************************************************************/ +/* animation_data_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef ANIMATION_DATA_EDITOR_PLUGIN_H
+#define ANIMATION_DATA_EDITOR_PLUGIN_H
+
+class AnimationDataEditorPlugin
+{
+public:
+ AnimationDataEditorPlugin();
+};
+
+#endif // ANIMATION_DATA_EDITOR_PLUGIN_H
diff --git a/tools/editor/plugins/animation_editor_plugin.cpp b/tools/editor/plugins/animation_editor_plugin.cpp new file mode 100644 index 000000000..bd27fa7da --- /dev/null +++ b/tools/editor/plugins/animation_editor_plugin.cpp @@ -0,0 +1,806 @@ +/*************************************************************************/ +/* animation_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "animation_editor_plugin.h" +#include "io/resource_loader.h" + + +class AnimationEditor_TrackEditor : public Object { + + OBJ_TYPE(AnimationEditor_TrackEditor,Object); + +protected: + + bool _set(const StringName& p_name, const Variant& p_value) { + + if (anim.is_null()) + return false; + String name=p_name; + + if (name=="track/interpolation") { + + anim_editor->_internal_set_interpolation_type(track,Animation::InterpolationType(p_value.operator int())); + + } else if (name.begins_with("keys/")) { + + int key = name.get_slice("/",1).to_int(); + ERR_FAIL_INDEX_V( key,anim->track_get_key_count(track), false ); + String what = name.get_slice("/",2); + float time = anim->track_get_key_time(track,key); + float transition = anim->track_get_key_transition(track,key); + + if (what=="time") { + Variant v = anim->track_get_key_value(track,key); + anim_editor->_internal_set_key(track,time,transition,v); + return true; + } + if (what=="transition") { + transition=p_value; + Variant v = anim->track_get_key_value(track,key); + anim_editor->_internal_set_key(track,time,transition,v); + return true; + } + + switch(anim->track_get_type(track)) { + + case Animation::TYPE_TRANSFORM: { + + Vector3 scale,loc; + Quat rot; + anim->transform_track_get_key(track,key,&loc,&rot,&scale); + + + if (what=="loc") { + loc=p_value; + } else if (what=="scale") { + scale=p_value; + } else if (what=="rot") { + rot=p_value; + } else { + return false; //meh + } + + Dictionary k; + k["rot"]=rot; + k["loc"]=loc; + k["scale"]=scale; + anim_editor->_internal_set_key(track,time,transition,k); + + } break; + case Animation::TYPE_METHOD: { + + } break; + case Animation::TYPE_VALUE: { + + if (what=="value") + anim_editor->_internal_set_key(track,time,transition,p_value); + } break; + default: { return false; } + + } + + } else + return false; + + return true; + } + + bool _get(const StringName& p_name,Variant &r_ret) const { + + if (anim.is_null()) + return false; + String name=p_name; + + if (name=="track/interpolation") { + + r_ret=anim->track_get_interpolation_type(track); + + } else if (name.begins_with("keys/")) { + + int key = name.get_slice("/",1).to_int(); + ERR_FAIL_INDEX_V( key,anim->track_get_key_count(track), Variant() ); + String what = name.get_slice("/",2); + + if (what=="time") { + r_ret=anim->track_get_key_time(track,key); + return true; + } + + if (what=="transition") { + r_ret=anim->track_get_key_transition(track,key); + return true; + } + + switch(anim->track_get_type(track)) { + + case Animation::TYPE_TRANSFORM: { + + Vector3 scale,loc; + Quat rot; + anim->transform_track_get_key(track,key,&loc,&rot,&scale); + + + if (what=="loc") { + r_ret= loc; + } else if (what=="scale") { + r_ret= scale; + } else if (what=="rot") { + r_ret= rot; + } + + } break; + case Animation::TYPE_METHOD: { + + } break; + case Animation::TYPE_VALUE: { + + if (what=="value") + r_ret= anim->track_get_key_value(track,key); + } break; + default: { return false; } + + } + } else + return false; + + return true; + } + void _get_property_list( List<PropertyInfo> *p_list) const { + + p_list->push_back(PropertyInfo(Variant::INT,"track/interpolation",PROPERTY_HINT_ENUM,"Nearest,Linear,Cubic") ); + + if (anim.is_null()) + return; + + int keycount = anim->track_get_key_count(track); + + for(int i=0;i<keycount;i++) { + + p_list->push_back(PropertyInfo(Variant::REAL,"keys/"+itos(i)+"/time",PROPERTY_HINT_RANGE,"0,3600,0.001") ); + p_list->push_back(PropertyInfo(Variant::REAL,"keys/"+itos(i)+"/transition",PROPERTY_HINT_EXP_EASING) ); + switch(anim->track_get_type(track)) { + + case Animation::TYPE_TRANSFORM: { + + p_list->push_back(PropertyInfo(Variant::VECTOR3,"keys/"+itos(i)+"/loc" ) ); + p_list->push_back(PropertyInfo(Variant::QUAT,"keys/"+itos(i)+"/rot" ) ); + p_list->push_back(PropertyInfo(Variant::VECTOR3,"keys/"+itos(i)+"/scale" ) ); + + } break; + case Animation::TYPE_METHOD: { + + } break; + case Animation::TYPE_VALUE: { + + + Variant v = anim->track_get_key_value(track,i); + + PropertyHint hint=PROPERTY_HINT_NONE; + String hint_string; + if (v.get_type()==Variant::INT) { + hint=PROPERTY_HINT_RANGE; + hint_string="-16384,16384,1"; + } else if (v.get_type()==Variant::REAL) { + hint=PROPERTY_HINT_RANGE; + hint_string="-16384,16384,0.001"; + } else if (v.get_type()==Variant::OBJECT) { + hint=PROPERTY_HINT_RESOURCE_TYPE; + hint_string="Resource"; + } + + + + p_list->push_back(PropertyInfo(v.get_type(),"keys/"+itos(i)+"/value",hint,hint_string ) ); + + + + } break; + + } + } + + } + +public: + + AnimationEditor *anim_editor; + + Ref<Animation> anim; + int track; + AnimationEditor_TrackEditor() { } + +}; + + +void AnimationEditor::update_anim() { + + tracks->clear(); + key_editor->edit(NULL); + TreeItem *root = tracks->create_item(NULL); + + TreeItem *sel=NULL; + int selected_track=-1; + if (animation->has_meta("_anim_editor_selected_track_")) + selected_track=animation->get_meta("_anim_editor_selected_track_"); + + for(int i=0;i<animation->get_track_count();i++) { + + String path = animation->track_get_path(i); + TreeItem *track = tracks->create_item(root); + track->set_text(0,itos(i)); + track->set_editable(0,false); + track->set_text(1,path); + track->set_editable(1,true); + track->set_metadata(0,i); + if (selected_track==i) + sel=track; + + switch(animation->track_get_type(i)) { + + case Animation::TYPE_TRANSFORM: { + + track->set_icon(0,get_icon("Matrix","EditorIcons")); + + } break; + case Animation::TYPE_METHOD: { + track->set_icon(0,get_icon("TrackMethod","EditorIcons")); + } break; + case Animation::TYPE_VALUE: { + + track->set_icon(0,get_icon("TrackValue","EditorIcons")); + } break; + + + } + + } + + if (sel) { + sel->select(1); + _update_track_keys(); + } else { + selected_track=-1; + } +} + +void AnimationEditor::_update_track_keys() { + if (selected_track<0 || selected_track>=animation->get_track_count()) + return; + track_editor->anim=animation; + track_editor->track=selected_track; + key_editor->edit(NULL); + key_editor->edit(track_editor); + + if (animation->track_get_type(selected_track)==Animation::TYPE_VALUE) { + key_time->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,160); + key_type->show(); + } else { + key_time->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,60); + key_type->hide(); + } + + +} + +void AnimationEditor::_track_path_changed() { + + TreeItem *ti=tracks->get_edited(); + int track=ti->get_metadata(0); + String path=ti->get_text(1); + if (track<0 || track>=animation->get_track_count()) + return; + undo_redo->create_action("Create Anim Track"); + undo_redo->add_do_method(animation.ptr(),"track_set_path",track,path); + undo_redo->add_undo_method(animation.ptr(),"track_set_path",track,animation->track_get_path(track)); + undo_redo->add_do_method(this,"_internal_check_update",animation); + undo_redo->add_undo_method(this,"_internal_check_update",animation); + undo_redo->commit_action(); + +} + +void AnimationEditor::_track_selected() { + + TreeItem *ti=tracks->get_selected(); + if(!ti) + return; + selected_track=ti->get_metadata(0); + animation->set_meta("_anim_editor_selected_track_",selected_track); + _update_track_keys(); +} + +void AnimationEditor::_track_added() { + + undo_redo->create_action("Create Anim Track"); + undo_redo->add_do_method(animation.ptr(),"add_track",track_type->get_selected(),-1); + undo_redo->add_undo_method(animation.ptr(),"remove_track",animation->get_track_count()); + undo_redo->add_do_method(this,"_internal_set_selected_track",animation->get_track_count(),animation); + undo_redo->add_undo_method(this,"_internal_set_selected_track",selected_track,animation); + undo_redo->add_do_method(this,"_internal_check_update",animation); + undo_redo->add_undo_method(this,"_internal_check_update",animation); + undo_redo->commit_action(); + update_anim(); +} + +void AnimationEditor::_track_removed() { + + if (selected_track<0 || selected_track>=animation->get_track_count()) + return; + + undo_redo->create_action("Remove Anim Track"); + undo_redo->add_do_method(animation.ptr(),"remove_track",selected_track); + undo_redo->add_undo_method(animation.ptr(),"add_track",animation->track_get_type(selected_track),selected_track); + undo_redo->add_undo_method(animation.ptr(),"track_set_path",selected_track,animation->track_get_path(selected_track)); + //todo interpolation + for(int i=0;i<animation->track_get_key_count(selected_track);i++) { + + Variant v = animation->track_get_key_value(selected_track,i); + float time = animation->track_get_key_time(selected_track,i); + + undo_redo->add_undo_method(animation.ptr(),"track_insert_key",selected_track,time,v); + + } + + int old_track=selected_track; + if (selected_track>0) + selected_track--; + if (selected_track<0 || selected_track>=(animation->get_track_count()-1)) + selected_track=-1; + + undo_redo->add_do_method(this,"_internal_set_selected_track",selected_track,animation); + undo_redo->add_undo_method(this,"_internal_set_selected_track",old_track,animation); + + undo_redo->add_do_method(this,"_internal_check_update",animation); + undo_redo->add_undo_method(this,"_internal_check_update",animation); + undo_redo->commit_action(); + +} + +void AnimationEditor::_internal_set_interpolation_type(int p_track,Animation::InterpolationType p_type) { + + undo_redo->create_action("Set Interpolation"); + undo_redo->add_do_method(animation.ptr(),"track_set_interpolation_type",p_track,p_type); + undo_redo->add_undo_method(animation.ptr(),"track_set_interpolation_type",p_track,animation->track_get_interpolation_type(p_track)); + undo_redo->add_do_method(this,"_internal_set_selected_track",selected_track,animation); + undo_redo->add_undo_method(this,"_internal_set_selected_track",selected_track,animation); + undo_redo->add_do_method(this,"_internal_check_update",animation); + undo_redo->add_undo_method(this,"_internal_check_update",animation); + undo_redo->commit_action(); + +} + +void AnimationEditor::_internal_set_selected_track(int p_which,const Ref<Animation>& p_anim) { + + if (is_visible() && animation==p_anim) { + selected_track=p_which; + animation->set_meta("_anim_editor_selected_track_",selected_track); + } +} + +void AnimationEditor::_track_moved_up() { + + + if (selected_track<0 || selected_track>=animation->get_track_count()) + return; + + if (selected_track<(animation->get_track_count()-1)) { + undo_redo->create_action("Move Up Track"); + undo_redo->add_do_method(animation.ptr(),"track_move_up",selected_track); + undo_redo->add_undo_method(animation.ptr(),"track_move_down",selected_track+1); + undo_redo->add_do_method(this,"_internal_set_selected_track",selected_track+1,animation); + undo_redo->add_undo_method(this,"_internal_set_selected_track",selected_track,animation); + undo_redo->add_do_method(this,"_internal_check_update",animation); + undo_redo->add_undo_method(this,"_internal_check_update",animation); + undo_redo->commit_action(); + } +} + +void AnimationEditor::_track_moved_down() { + + + + if (selected_track<0 || selected_track>=animation->get_track_count()) + return; + if (selected_track>0) { + undo_redo->create_action("Move Down Track"); + undo_redo->add_do_method(animation.ptr(),"track_move_down",selected_track); + undo_redo->add_undo_method(animation.ptr(),"track_move_up",selected_track-1); + undo_redo->add_do_method(this,"_internal_set_selected_track",selected_track-1,animation); + undo_redo->add_undo_method(this,"_internal_set_selected_track",selected_track,animation); + undo_redo->add_do_method(this,"_internal_check_update",animation); + undo_redo->add_undo_method(this,"_internal_check_update",animation); + undo_redo->commit_action(); + } + + +} + +void AnimationEditor::_key_added() { + + if (selected_track<0 || selected_track>=animation->get_track_count()) + return; + + bool need_variant= animation->track_get_type(selected_track)==Animation::TYPE_VALUE; + + Variant v; + + if (need_variant) { + + switch(key_type->get_selected()) { + + case Variant::NIL: v=Variant(); break; + case Variant::BOOL: v=false; break; + case Variant::INT: v=0; break; + case Variant::REAL: v=0.0; break; + case Variant::STRING: v=""; break; + case Variant::VECTOR2: v=Vector2(); break; // 5 + case Variant::RECT2: v=Rect2(); break; + case Variant::VECTOR3: v=Vector3(); break; + case Variant::PLANE: v=Plane(); break; + case Variant::QUAT: v=Quat(); break; + case Variant::_AABB: v=AABB(); break; //sorry naming convention fail :( not like it's used often // 10 + case Variant::MATRIX3: v=Matrix3(); break; + case Variant::TRANSFORM: v=Transform(); break; + case Variant::COLOR: v=Color(); break; + case Variant::IMAGE: v=Image(); break; + case Variant::NODE_PATH: v=NodePath(); break; // 15 + case Variant::_RID: v=RID(); break; + case Variant::OBJECT: v=Variant(); break; + case Variant::INPUT_EVENT: v=InputEvent(); break; + case Variant::DICTIONARY: v=Dictionary(); break; // 20 + case Variant::ARRAY: v=Array(); break; + case Variant::RAW_ARRAY: v=DVector<uint8_t>(); break; + case Variant::INT_ARRAY: v=DVector<int>(); break; + case Variant::REAL_ARRAY: v=DVector<real_t>(); break; + case Variant::STRING_ARRAY: v=DVector<String>(); break; //25 + case Variant::VECTOR3_ARRAY: v=DVector<Vector3>(); break; + case Variant::COLOR_ARRAY: v=DVector<Color>(); break; + default: v=Variant(); break; + } + } + + float time = key_time->get_text().to_double(); + + switch(animation->track_get_type(selected_track)) { + case Animation::TYPE_TRANSFORM: { + + Dictionary d; + d["loc"]=Vector3(); + d["rot"]=Quat(); + d["scale"]=Vector3(1,1,1); + v=d; + + } break; + case Animation::TYPE_VALUE: { + //v=v + } break; + case Animation::TYPE_METHOD: { + + return; //not do anything yet + } break; + } + + _internal_set_key(selected_track,time,1.0,v); + + _update_track_keys(); +} + + + +void AnimationEditor::_internal_check_update(Ref<Animation> p_anim) { + + if (is_visible() && p_anim==animation) { + update_anim(); + } +} + +void AnimationEditor::_internal_set_key(int p_track, float p_time, float p_transition,const Variant& p_value) { + + int prev = animation->track_find_key(p_track,p_time); + bool existing = (prev>=0) && (animation->track_get_key_time(p_track,prev)==p_time); + + undo_redo->create_action("Insert Key"); + + undo_redo->add_do_method(animation.ptr(),"track_insert_key",p_track,p_time,p_value,p_transition); + if (existing) + undo_redo->add_undo_method(animation.ptr(),"track_insert_key",p_track,p_time,animation->track_get_key_value(p_track,existing),animation->track_get_key_transition(p_track,existing)); + else + undo_redo->add_undo_method(animation.ptr(),"track_remove_key",p_track,prev+1); + undo_redo->add_do_method(this,"_internal_check_update",animation); + undo_redo->add_undo_method(this,"_internal_check_update",animation); + + undo_redo->commit_action(); +} + +void AnimationEditor::_key_removed() { + + if (selected_track<0 || selected_track>=animation->get_track_count()) + return; + + String sel=key_editor->get_selected_path(); + if (sel.get_slice_count("/")<2) + return; + if (sel.get_slice("/",0)!="keys") + return; + int key = sel.get_slice("/",1).to_int(); + if (key<0 || key>=animation->track_get_key_count(selected_track)) + return; + + + undo_redo->create_action("Remove Key"); + + Variant data = animation->track_get_key_value(selected_track,key); + float time = animation->track_get_key_time(selected_track,key); + undo_redo->add_do_method(animation.ptr(),"track_remove_key",selected_track,key); + undo_redo->add_undo_method(animation.ptr(),"track_insert_key",selected_track,time,data); + undo_redo->add_do_method(this,"_internal_check_update",animation); + undo_redo->add_undo_method(this,"_internal_check_update",animation); + undo_redo->commit_action(); + + _update_track_keys(); +} + + + +void AnimationEditor::edit(const Ref<Animation>& p_animation) { + + + animation=p_animation; + if (!animation.is_null()) + update_anim(); + + +} + + + +void AnimationEditor::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_track_selected"),&AnimationEditor::_track_selected); + ObjectTypeDB::bind_method(_MD("_track_added"),&AnimationEditor::_track_added); + ObjectTypeDB::bind_method(_MD("_track_removed"),&AnimationEditor::_track_removed); + ObjectTypeDB::bind_method(_MD("_track_moved_up"),&AnimationEditor::_track_moved_up); + ObjectTypeDB::bind_method(_MD("_track_moved_down"),&AnimationEditor::_track_moved_down); + ObjectTypeDB::bind_method(_MD("_track_path_changed"),&AnimationEditor::_track_path_changed); + ObjectTypeDB::bind_method(_MD("_key_added"),&AnimationEditor::_key_added); + ObjectTypeDB::bind_method(_MD("_key_removed"),&AnimationEditor::_key_removed); + ObjectTypeDB::bind_method(_MD("_internal_check_update"),&AnimationEditor::_internal_check_update); + ObjectTypeDB::bind_method(_MD("_internal_set_selected_track"),&AnimationEditor::_internal_set_selected_track); + +} + +void AnimationEditor::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_ENTER_SCENE: { + + add_track->set_icon( get_icon("Add","EditorIcons") ); + remove_track->set_icon( get_icon("Del","EditorIcons") ); + move_up_track->set_icon( get_icon("Up","EditorIcons") ); + move_down_track->set_icon( get_icon("Down","EditorIcons") ); + time_icon->set_texture( get_icon("Time","EditorIcons") ); + + add_key->set_icon( get_icon("Add","EditorIcons") ); + remove_key->set_icon( get_icon("Del","EditorIcons") ); + + } break; + } +} + +AnimationEditor::AnimationEditor() { + + panel = memnew( Panel ); + add_child(panel); + panel->set_area_as_parent_rect(); + + Control *left_pane = memnew( Control ); + panel->add_child(left_pane); + left_pane->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_RATIO,0.5); + left_pane->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_END,0); + + Label *l = memnew( Label ); + l->set_text("Track List:"); + l->set_pos(Point2(5,5)); + left_pane->add_child(l); + + /* + track_name = memnew( LineEdit ); + track_name->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_BEGIN,10); + track_name->set_anchor_and_margin(MARGIN_TOP,ANCHOR_BEGIN,25); + track_name->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,80); + track_name->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_BEGIN,45); + left_pane->add_child(track_name); +*/ + + track_type = memnew( OptionButton ); + track_type->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_BEGIN,10); + track_type->set_anchor_and_margin(MARGIN_TOP,ANCHOR_BEGIN,25); + track_type->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,115); + track_type->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_BEGIN,45); + left_pane->add_child(track_type); + track_type->add_item("Transform",Animation::TYPE_TRANSFORM); + track_type->add_item("Value",Animation::TYPE_VALUE); + track_type->add_item("Method",Animation::TYPE_METHOD); + + + add_track = memnew( Button ); + add_track->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_END,110); + add_track->set_anchor_and_margin(MARGIN_TOP,ANCHOR_BEGIN,25); + add_track->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,90); + add_track->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_BEGIN,45); + left_pane->add_child(add_track); + + remove_track = memnew( Button ); + remove_track->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_END,85); + remove_track->set_anchor_and_margin(MARGIN_TOP,ANCHOR_BEGIN,25); + remove_track->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,60); + remove_track->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_BEGIN,45); + left_pane->add_child(remove_track); + + move_up_track = memnew( Button ); + move_up_track->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_END,55); + move_up_track->set_anchor_and_margin(MARGIN_TOP,ANCHOR_BEGIN,25); + move_up_track->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,30); + move_up_track->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_BEGIN,45); + left_pane->add_child(move_up_track); + + move_down_track = memnew( Button ); + move_down_track->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_END,25); + move_down_track->set_anchor_and_margin(MARGIN_TOP,ANCHOR_BEGIN,25); + move_down_track->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,0); + move_down_track->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_BEGIN,45); + left_pane->add_child(move_down_track); + + tracks = memnew(Tree); + tracks->set_columns(2); + tracks->set_column_expand(0,false); + tracks->set_column_min_width(0,55); + tracks->set_column_expand(1,true); + tracks->set_column_min_width(1,100); + tracks->set_anchor_and_margin(MARGIN_TOP,ANCHOR_BEGIN,55); + tracks->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,0); + tracks->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_BEGIN,10); + tracks->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_END,5); + tracks->set_hide_root(true); + left_pane->add_child(tracks); + + + Control *right_pane = memnew( Control ); + panel->add_child(right_pane); + right_pane->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_RATIO,0.5); + right_pane->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,5); + right_pane->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_END,0); + + l = memnew( Label ); + l->set_text("Track Keys:"); + l->set_pos(Point2(5,5)); + right_pane->add_child(l); + + time_icon = memnew( TextureFrame ); + time_icon->set_pos(Point2(8,28)); + right_pane->add_child(time_icon); + + key_time = memnew( LineEdit ); + key_time->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_BEGIN,24); + key_time->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,160); + key_time->set_anchor_and_margin(MARGIN_TOP,ANCHOR_BEGIN,25); + key_time->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_BEGIN,45); + key_time->set_text("0.0"); + right_pane->add_child(key_time); + + key_type = memnew( OptionButton ); + key_type->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_END,160); + key_type->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,60); + key_type->set_anchor_and_margin(MARGIN_TOP,ANCHOR_BEGIN,25); + key_type->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_BEGIN,45); + right_pane->add_child(key_type); + + for(int i=0;i<Variant::VARIANT_MAX;i++) { + + key_type->add_item(Variant::get_type_name(Variant::Type(i))); + } + + add_key = memnew( Button ); + add_key->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_END,55); + add_key->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,30); + add_key->set_anchor_and_margin(MARGIN_TOP,ANCHOR_BEGIN,25); + add_key->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_BEGIN,45); + right_pane->add_child(add_key); + + remove_key = memnew( Button ); + remove_key->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_END,25); + remove_key->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,0); + remove_key->set_anchor_and_margin(MARGIN_TOP,ANCHOR_BEGIN,25); + remove_key->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_BEGIN,45); + right_pane->add_child(remove_key); + + key_editor = memnew(PropertyEditor); + key_editor->set_anchor_and_margin(MARGIN_TOP,ANCHOR_BEGIN,55); + key_editor->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,0); + key_editor->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_BEGIN,10); + key_editor->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_END,5); + key_editor->hide_top_label(); + + right_pane->add_child(key_editor); + + track_editor = memnew( AnimationEditor_TrackEditor ); + track_editor->anim_editor=this; + selected_track=-1; + + add_track->connect("pressed", this,"_track_added"); + remove_track->connect("pressed", this,"_track_removed"); + move_up_track->connect("pressed", this,"_track_moved_up"); + move_down_track->connect("pressed", this,"_track_moved_down"); + tracks->connect("cell_selected", this,"_track_selected"); + tracks->connect("item_edited", this,"_track_path_changed"); + add_key->connect("pressed", this,"_key_added"); + remove_key->connect("pressed", this,"_key_removed"); +} + +AnimationEditor::~AnimationEditor() { + + memdelete(track_editor); +} + +void AnimationEditorPlugin::edit(Object *p_node) { + + animation_editor->set_undo_redo(&get_undo_redo()); + if (p_node && p_node->cast_to<Animation>()) { + animation_editor->edit( p_node->cast_to<Animation>() ); + animation_editor->show(); + } else + animation_editor->hide(); +} + +bool AnimationEditorPlugin::handles(Object *p_node) const{ + + return p_node->is_type("Animation"); +} + +void AnimationEditorPlugin::make_visible(bool p_visible){ + + if (p_visible) + animation_editor->show(); + else + animation_editor->hide(); +} + +AnimationEditorPlugin::AnimationEditorPlugin(EditorNode *p_node) { + + animation_editor = memnew( AnimationEditor ); + + p_node->get_viewport()->add_child(animation_editor); + animation_editor->set_area_as_parent_rect(); + animation_editor->hide(); + + + + +} + + diff --git a/tools/editor/plugins/animation_editor_plugin.h b/tools/editor/plugins/animation_editor_plugin.h new file mode 100644 index 000000000..82edef756 --- /dev/null +++ b/tools/editor/plugins/animation_editor_plugin.h @@ -0,0 +1,123 @@ +/*************************************************************************/ +/* animation_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef ANIMATION_EDITOR_PLUGIN_H +#define ANIMATION_EDITOR_PLUGIN_H + +#include "scene/resources/animation.h" +#include "scene/gui/texture_frame.h" +#include "scene/gui/option_button.h" +#include "tools/editor/editor_node.h" +#include "tools/editor/property_editor.h" +#include "undo_redo.h" +class AnimationEditor_TrackEditor; + +class AnimationEditor : public Control { + + OBJ_TYPE( AnimationEditor, Control ); + + Panel *panel; + Ref<Animation> animation; + + Button *add_track; + Button *remove_track; + Button *move_up_track; + Button *move_down_track; + + Button *add_key; + Button *remove_key; + + LineEdit *key_time; + OptionButton *track_type; + OptionButton *key_type; + TextureFrame *time_icon; + + Tree *tracks; + PropertyEditor *key_editor; + AnimationEditor_TrackEditor *track_editor; + int selected_track; + + void _track_selected(); + void _track_added(); + void _track_removed(); + void _track_moved_up(); + void _track_moved_down(); + void _track_path_changed(); + + void _key_added(); + void _key_removed(); + + + void _update_track_keys(); + void update_anim(); + + UndoRedo *undo_redo; + + void _internal_set_selected_track(int p_which,const Ref<Animation>& p_anim); + void _internal_check_update(Ref<Animation> p_anim); + +friend class AnimationEditor_TrackEditor; + void _internal_set_key(int p_track, float p_time, float p_transition,const Variant& p_value); + void _internal_set_interpolation_type(int p_track,Animation::InterpolationType p_type); + +protected: + void _notification(int p_what); + static void _bind_methods(); +public: + + void set_undo_redo(UndoRedo *p_undo_redo) { undo_redo=p_undo_redo; } + void edit(const Ref<Animation>& p_animation); + + AnimationEditor(); + ~AnimationEditor(); +}; + + + +class AnimationEditorPlugin : public EditorPlugin { + + OBJ_TYPE( AnimationEditorPlugin, EditorPlugin ); + + AnimationEditor *animation_editor; + EditorNode *editor; + +public: + + + virtual String get_name() const { return "Animation"; } + 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); + + AnimationEditorPlugin(EditorNode *p_node); + +}; + + +#endif diff --git a/tools/editor/plugins/animation_player_editor_plugin.cpp b/tools/editor/plugins/animation_player_editor_plugin.cpp new file mode 100644 index 000000000..0d709b9a7 --- /dev/null +++ b/tools/editor/plugins/animation_player_editor_plugin.cpp @@ -0,0 +1,1157 @@ +/*************************************************************************/ +/* animation_player_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "animation_player_editor_plugin.h" +#include "io/resource_loader.h" + + +void AnimationPlayerEditor::_node_removed(Node *p_node) { + + if (player && player == p_node) { + player=NULL; + hide(); + set_process(false); + if (edit_anim->is_pressed()) { + + editor->get_animation_editor()->set_animation(Ref<Animation>()); + editor->get_animation_editor()->set_root(NULL); + editor->animation_editor_make_visible(false); + edit_anim->set_pressed(false); + } + } +} + +void AnimationPlayerEditor::_input_event(InputEvent p_event) { + + +} + +void AnimationPlayerEditor::_notification(int p_what) { + + if (p_what==NOTIFICATION_PROCESS) { + + if (!player) + return; + + updating = true; + + if (player->is_playing()) { + + { + String animname=player->get_current_animation(); + + if (player->has_animation(animname)) { + Ref<Animation> anim = player->get_animation(animname); + if (!anim.is_null()) { + + seek->set_max(anim->get_length()); + } + } + } + seek->set_val(player->get_current_animation_pos()); + if (edit_anim->is_pressed()) + editor->get_animation_editor()->set_anim_pos(player->get_current_animation_pos()); + } else if (last_active) { + //need the last frame after it stopped + + seek->set_val(player->get_current_animation_pos()); + } + + last_active=player->is_playing(); + //seek->set_val(player->get_pos()); + updating = false; + } + + if (p_what==NOTIFICATION_ENTER_SCENE) { + + editor->connect("hide_animation_player_editors",this,"_hide_anim_editors"); + add_anim->set_icon( get_icon("New","EditorIcons") ); + rename_anim->set_icon( get_icon("Rename","EditorIcons") ); + duplicate_anim->set_icon( get_icon("Duplicate","EditorIcons") ); + autoplay->set_icon( get_icon("AutoPlay","EditorIcons") ); + load_anim->set_icon( get_icon("Folder","EditorIcons") ); + remove_anim->set_icon( get_icon("Del","EditorIcons") ); + edit_anim->set_icon( get_icon("Edit","EditorIcons") ); + blend_anim->set_icon( get_icon("Blend","EditorIcons") ); + play->set_icon( get_icon("Play","EditorIcons") ); + autoplay_icon=get_icon("AutoPlay","EditorIcons"); + stop->set_icon( get_icon("Stop","EditorIcons") ); + resource_edit_anim->set_icon( get_icon("EditResource","EditorIcons") ); + pin->set_normal_texture(get_icon("Pin","EditorIcons") ); + pin->set_pressed_texture( get_icon("PinPressed","EditorIcons") ); + + blend_editor.next->connect("text_changed",this,"_blend_editor_next_changed"); + +/* + anim_editor_load->set_normal_texture( get_icon("AnimGet","EditorIcons")); + anim_editor_store->set_normal_texture( get_icon("AnimSet","EditorIcons")); + anim_editor_load->set_pressed_texture( get_icon("AnimGet","EditorIcons")); + anim_editor_store->set_pressed_texture( get_icon("AnimSet","EditorIcons")); + anim_editor_load->set_hover_texture( get_icon("AnimGetHl","EditorIcons")); + anim_editor_store->set_hover_texture( get_icon("AnimSetHl","EditorIcons")); +*/ + } + + if (p_what==NOTIFICATION_READY) { + + get_scene()->connect("node_removed",this,"_node_removed"); + } + + if (p_what==NOTIFICATION_DRAW) { + + } +} + +void AnimationPlayerEditor::_autoplay_pressed() { + + if (updating) + return; + if (animation->get_item_count()==0) { + return; + } + + String current = animation->get_item_text( animation->get_selected() ); + if (player->get_autoplay()==current) { + //unset + undo_redo->create_action("Toggle Autoplay"); + undo_redo->add_do_method(player,"set_autoplay",""); + undo_redo->add_undo_method(player,"set_autoplay",player->get_autoplay()); + undo_redo->add_do_method(this,"_animation_player_changed",player); + undo_redo->add_undo_method(this,"_animation_player_changed",player); + undo_redo->commit_action(); + + + } else { + //set + undo_redo->create_action("Toggle Autoplay"); + undo_redo->add_do_method(player,"set_autoplay",current); + undo_redo->add_undo_method(player,"set_autoplay",player->get_autoplay()); + undo_redo->add_do_method(this,"_animation_player_changed",player); + undo_redo->add_undo_method(this,"_animation_player_changed",player); + undo_redo->commit_action(); + } + +} + +void AnimationPlayerEditor::_play_pressed() { + + String current; + if (animation->get_selected()>=0 && animation->get_selected()<animation->get_item_count()) { + + current = animation->get_item_text( animation->get_selected() ); + } + + if (current!="") { + + if (current==player->get_current_animation()) + player->stop(); //so it wont blend with itself + player->play(current ); + } + + //unstop + stop->set_pressed(false); + //unpause + //pause->set_pressed(false); +} +void AnimationPlayerEditor::_stop_pressed() { + + player->stop(); + play->set_pressed(false); + stop->set_pressed(true); + //pause->set_pressed(false); + //player->set_pause(false); +} + +void AnimationPlayerEditor::_pause_pressed() { + + //player->set_pause( pause->is_pressed() ); +} +void AnimationPlayerEditor::_animation_selected(int p_which) { + + if (updating) + return; + // when selecting an animation, the idea is that the only interesting behavior + // ui-wise is that it should play/blend the next one if currently playing + String current; + if (animation->get_selected()>=0 && animation->get_selected()<animation->get_item_count()) { + + current = animation->get_item_text( animation->get_selected() ); + } + + if (current!="") { + + + player->set_current_animation( current ); + + Ref<Animation> anim = player->get_animation(current); + if (edit_anim->is_pressed()) { + Ref<Animation> anim = player->get_animation(current); + editor->get_animation_editor()->set_animation(anim); + Node *root = player->get_node(player->get_root()); + if (root) { + editor->get_animation_editor()->set_root(root); + } + } + seek->set_max(anim->get_length()); + + + } else { + if (edit_anim->is_pressed()) { + editor->get_animation_editor()->set_animation(Ref<Animation>()); + editor->get_animation_editor()->set_root(NULL); + } + + } + + + autoplay->set_pressed(current==player->get_autoplay()); +} + +void AnimationPlayerEditor::_animation_new() { + + renaming=false; + name_title->set_text("New Animation Name:"); + + int count=1; + String base="New Anim"; + while(true) { + String attempt = base; + if (count>1) + attempt+=" ("+itos(count)+")"; + if (player->has_animation(attempt)) { + count++; + continue; + } + base=attempt; + break; + } + + name->set_text(base); + name_dialog->popup_centered(Size2(300,90)); + name->select_all(); + name->grab_focus(); +} +void AnimationPlayerEditor::_animation_rename() { + + if (animation->get_item_count()==0) + return; + int selected = animation->get_selected(); + String selected_name = animation->get_item_text(selected); + + name_title->set_text("Change Animation Name:"); + name->set_text(selected_name); + renaming=true; + name_dialog->popup_centered(Size2(300,90)); + name->select_all(); + name->grab_focus(); + +} +void AnimationPlayerEditor::_animation_load() { + ERR_FAIL_COND(!player); + file->set_mode( FileDialog::MODE_OPEN_FILE ); + file->clear_filters(); + List<String> extensions; + + ResourceLoader::get_recognized_extensions_for_type("Animation",&extensions); + for (List<String>::Element *E=extensions.front();E;E=E->next()) { + + file->add_filter("*."+E->get()+" ; "+E->get().to_upper() ); + + } + + file->popup_centered_ratio(); + + +} +void AnimationPlayerEditor::_animation_remove() { + + if (animation->get_item_count()==0) + return; + + String current = animation->get_item_text(animation->get_selected()); + Ref<Animation> anim = player->get_animation(current); + + + undo_redo->create_action("Remove Animation"); + undo_redo->add_do_method(player,"remove_animation",current); + undo_redo->add_undo_method(player,"add_animation",current,anim); + undo_redo->add_do_method(this,"_animation_player_changed",player); + undo_redo->add_undo_method(this,"_animation_player_changed",player); + undo_redo->commit_action(); + +} + +void AnimationPlayerEditor::_select_anim_by_name(const String& p_anim) { + + int idx=-1; + for(int i=0;i<animation->get_item_count();i++) { + + if (animation->get_item_text(i)==p_anim) { + + idx=i; + break; + } + } + + ERR_FAIL_COND(idx==-1); + + + animation->select(idx); + + _animation_selected(idx); + +} + +void AnimationPlayerEditor::_animation_name_edited() { + + player->stop(); + + String new_name = name->get_text(); + if (new_name=="" || new_name.find(":")!=-1 || new_name.find("/")!=-1) { + error_dialog->set_text("ERROR: Invalid animation name!"); + error_dialog->popup_centered(Size2(300,70)); + return; + } + + if (renaming && animation->get_item_count()>0 && animation->get_item_text(animation->get_selected())==new_name) { + name_dialog->hide(); + return; + } + + if (player->has_animation(new_name)) { + error_dialog->set_text("ERROR: Animation Name Already Exists!"); + error_dialog->popup_centered(Size2(300,70)); + return; + } + + if (renaming) { + String current = animation->get_item_text(animation->get_selected()); + Ref<Animation> anim = player->get_animation(current); + + undo_redo->create_action("Rename Animation"); + undo_redo->add_do_method(player,"rename_animation",current,new_name); + undo_redo->add_do_method(anim.ptr(),"set_name",new_name); + undo_redo->add_undo_method(player,"rename_animation",new_name,current); + undo_redo->add_undo_method(anim.ptr(),"set_name",current); + undo_redo->add_do_method(this,"_animation_player_changed",player); + undo_redo->add_undo_method(this,"_animation_player_changed",player); + undo_redo->commit_action(); + + _select_anim_by_name(new_name); + + } else { + + Ref<Animation> new_anim = Ref<Animation>(memnew( Animation )); + new_anim->set_name(new_name); + + undo_redo->create_action("Add Animation"); + undo_redo->add_do_method(player,"add_animation",new_name,new_anim); + undo_redo->add_undo_method(player,"remove_animation",new_name); + undo_redo->add_do_method(this,"_animation_player_changed",player); + undo_redo->add_undo_method(this,"_animation_player_changed",player); + undo_redo->commit_action(); + + _select_anim_by_name(new_name); + + } + + name_dialog->hide(); +} + + +void AnimationPlayerEditor::_blend_editor_next_changed(const String& p_string) { + + if (animation->get_item_count()==0) + return; + + String current = animation->get_item_text(animation->get_selected()); + player->animation_set_next(current,p_string); + +} + +void AnimationPlayerEditor::_animation_blend() { + + if (updating_blends) + return; + + blend_editor.tree->clear(); + + if (animation->get_item_count()==0) + return; + + String current = animation->get_item_text(animation->get_selected()); + + blend_editor.dialog->popup_centered(Size2(400,400)); + + blend_editor.tree->set_hide_root(true); + blend_editor.tree->set_column_min_width(0,10); + blend_editor.tree->set_column_min_width(1,3); + + List<StringName> anims; + player->get_animation_list(&anims); + TreeItem *root = blend_editor.tree->create_item(); + updating_blends=true; + + for(List<StringName>::Element *E=anims.front();E;E=E->next()) { + + String to=E->get(); + TreeItem *blend=blend_editor.tree->create_item(root); + blend->set_editable(0,false); + blend->set_editable(1,true); + blend->set_text(0,to); + blend->set_cell_mode(1,TreeItem::CELL_MODE_RANGE); + blend->set_range_config(1,0,3600,0.001); + blend->set_range(1,player->get_blend_time(current,to)); + } + + blend_editor.next->set_text( player->animation_get_next(current) ); + + updating_blends=false; +} + +void AnimationPlayerEditor::_blend_edited() { + + if (updating_blends) + return; + + if (animation->get_item_count()==0) + return; + + String current = animation->get_item_text(animation->get_selected()); + + TreeItem *selected = blend_editor.tree->get_edited(); + if (!selected) + return; + + updating_blends=true; + String to=selected->get_text(0); + float blend_time = selected->get_range(1); + float prev_blend_time = player->get_blend_time(current,to); + + undo_redo->create_action("Change Blend Time"); + undo_redo->add_do_method(player,"set_blend_time",current,to,blend_time); + undo_redo->add_undo_method(player,"set_blend_time",current,to,prev_blend_time); + undo_redo->add_do_method(this,"_animation_player_changed",player); + undo_redo->add_undo_method(this,"_animation_player_changed",player); + undo_redo->commit_action(); + updating_blends=false; +} + +void AnimationPlayerEditor::ensure_visibility() { + + _animation_edit(); +} + +void AnimationPlayerEditor::_animation_resource_edit() { + + if (animation->get_item_count()) { + String current = animation->get_item_text(animation->get_selected()); + Ref<Animation> anim = player->get_animation(current); + editor->edit_resource(anim); + } + +} + +void AnimationPlayerEditor::_animation_edit() { + +// if (animation->get_item_count()==0) +// return; + + if (edit_anim->is_pressed()) { + editor->animation_editor_make_visible(true); + + //editor->get_animation_editor()->set_root(player->get_roo); - get root pending + if (animation->get_item_count()) { + String current = animation->get_item_text(animation->get_selected()); + Ref<Animation> anim = player->get_animation(current); + editor->get_animation_editor()->set_animation(anim); + Node *root = player->get_node(player->get_root()); + if (root) { + editor->get_animation_editor()->set_root(root); + } + + } else { + + editor->get_animation_editor()->set_animation(Ref<Animation>()); + editor->get_animation_editor()->set_root(NULL); + + } + } else { + editor->animation_editor_make_visible(false); + editor->get_animation_editor()->set_animation(Ref<Animation>()); + editor->get_animation_editor()->set_root(NULL); + } + + //get_scene()->get_root_node()->call("_resource_selected",anim,""); + +} +void AnimationPlayerEditor::_file_selected(String p_file) { + + ERR_FAIL_COND(!player); + + Ref<Resource> res = ResourceLoader::load(p_file,"Animation"); + ERR_FAIL_COND(res.is_null()); + ERR_FAIL_COND( !res->is_type("Animation") ); + if (p_file.find_last("/")!=-1) { + + p_file=p_file.substr( p_file.find_last("/")+1, p_file.length() ); + + } + if (p_file.find_last("\\")!=-1) { + + p_file=p_file.substr( p_file.find_last("\\")+1, p_file.length() ); + + } + + if (p_file.find(".")!=-1) + p_file=p_file.substr(0,p_file.find(".")); + + undo_redo->create_action("Load Animation"); + undo_redo->add_do_method(player,"add_animation",p_file,res); + undo_redo->add_undo_method(player,"remove_animation",p_file); + if (player->has_animation(p_file)) { + undo_redo->add_undo_method(player,"add_animation",p_file,player->get_animation(p_file)); + + } + undo_redo->add_do_method(this,"_animation_player_changed",player); + undo_redo->add_undo_method(this,"_animation_player_changed",player); + undo_redo->commit_action(); + +} + +void AnimationPlayerEditor::_scale_changed(const String& p_scale) { + + player->set_speed(p_scale.to_double()); +} + +void AnimationPlayerEditor::_update_animation() { + + // the purpose of _update_animation is to reflect the current state + // of the animation player in the current editor.. + + updating=true; + + + if (player->is_playing()) { + + play->set_pressed(true); + stop->set_pressed(false); + + } else { + + play->set_pressed(false); + stop->set_pressed(true); + } + + scale->set_text( String::num(player->get_speed(),2) ); + String current=player->get_current_animation(); + + for (int i=0;i<animation->get_item_count();i++) { + + if (animation->get_item_text(i)==current) { + animation->select(i); + break; + } + } + + updating=false; +} + +void AnimationPlayerEditor::_update_player() { + + if (!player) + return; + + updating=true; + List<StringName> animlist; + player->get_animation_list(&animlist); + + animation->clear(); + nodename->set_text(player->get_name()); + + stop->set_disabled(animlist.size()==0); + play->set_disabled(animlist.size()==0); + autoplay->set_disabled(animlist.size()==0); + duplicate_anim->set_disabled(animlist.size()==0); + rename_anim->set_disabled(animlist.size()==0); + blend_anim->set_disabled(animlist.size()==0); + remove_anim->set_disabled(animlist.size()==0); + resource_edit_anim->set_disabled(animlist.size()==0); + + int active_idx=-1; + for (List<StringName>::Element *E=animlist.front();E;E=E->next()) { + + if (player->get_autoplay()==E->get()) + animation->add_icon_item(autoplay_icon,E->get()); + else + animation->add_item(E->get()); + + if (player->get_current_animation()==E->get()) + active_idx=animation->get_item_count()-1; + + } + + updating=false; + if (active_idx!=-1) { + animation->select(active_idx); + autoplay->set_pressed(animation->get_item_text(active_idx)==player->get_autoplay()); + _animation_selected(active_idx); + + } else if (animation->get_item_count()>0){ + + animation->select(0); + autoplay->set_pressed(animation->get_item_text(0)==player->get_autoplay()); + _animation_selected(0); + } + + //pause->set_pressed(player->is_paused()); + + if (edit_anim->is_pressed()) { + + if (animation->get_item_count()) { + String current = animation->get_item_text(animation->get_selected()); + Ref<Animation> anim = player->get_animation(current); + editor->get_animation_editor()->set_animation(anim); + Node *root = player->get_node(player->get_root()); + if (root) { + editor->get_animation_editor()->set_root(root); + } + + } + + } + + _update_animation(); +} + + + +void AnimationPlayerEditor::edit(AnimationPlayer *p_player) { + + + if (player && pin->is_pressed()) + return; //ignore, pinned + player=p_player; + + if (player) + _update_player(); + else { + +// hide(); + + } + +} + + +void AnimationPlayerEditor::_animation_duplicate() { + + + if (!animation->get_item_count()) + return; + + String current = animation->get_item_text(animation->get_selected()); + Ref<Animation> anim = player->get_animation(current); + if (!anim.is_valid()) + return; + + Ref<Animation> new_anim = memnew( Animation ); + List<PropertyInfo> plist; + anim->get_property_list(&plist); + for (List<PropertyInfo>::Element *E=plist.front();E;E=E->next()) { + + if (E->get().usage&PROPERTY_USAGE_STORAGE) { + + new_anim->set(E->get().name, anim->get(E->get().name)); + } + } + new_anim->set_path(""); + + String new_name = current; + while(player->has_animation(new_name)) { + + new_name=new_name+" (copy)"; + } + + + undo_redo->create_action("Duplicate Animation"); + undo_redo->add_do_method(player,"add_animation",new_name,new_anim); + undo_redo->add_undo_method(player,"remove_animation",new_name); + undo_redo->add_do_method(this,"_animation_player_changed",player); + undo_redo->add_undo_method(this,"_animation_player_changed",player); + undo_redo->commit_action(); + + + for(int i=0;i<animation->get_item_count();i++) { + + if (animation->get_item_text(i)==new_name) { + + animation->select(i); + _animation_selected(i); + return; + } + } + +} + +void AnimationPlayerEditor::_seek_value_changed(float p_value) { + + if (updating || !player || player->is_playing()) { + return; + }; + + + updating=true; + String current=player->get_current_animation(); //animation->get_item_text( animation->get_selected() ); + if (current == "" || !player->has_animation(current)) { + updating=false; + current=""; + return; + }; + + Ref<Animation> anim; + anim=player->get_animation(current); + + float pos = anim->get_length() * (p_value / seek->get_max()); + + if (player->is_valid()) { + float cpos = player->get_current_animation_pos(); + + player->seek_delta(pos,pos-cpos); + } else { + player->seek(pos,true); + } + + if (edit_anim->is_pressed()) + editor->get_animation_editor()->set_anim_pos(pos); + + updating=true; +}; + +void AnimationPlayerEditor::_animation_player_changed(Object *p_pl) { + + if (player==p_pl && is_visible()) { + + _update_player(); + if (blend_editor.dialog->is_visible()) + _animation_blend(); //update + } +} + + + +void AnimationPlayerEditor::_list_changed() { + + if(is_visible()) + _update_player(); +} +#if 0 +void AnimationPlayerEditor::_editor_store() { + + if (animation->get_item_count()==0) + return; + String current = animation->get_item_text(animation->get_selected()); + Ref<Animation> anim = player->get_animation(current); + + if (editor->get_animation_editor()->get_current_animation()==anim) + return; //already there + + + undo_redo->create_action("Store anim in editor"); + undo_redo->add_do_method(editor->get_animation_editor(),"set_animation",anim); + undo_redo->add_undo_method(editor->get_animation_editor(),"remove_animation",anim); + undo_redo->commit_action(); +} + +void AnimationPlayerEditor::_editor_load(){ + + Ref<Animation> anim = editor->get_animation_editor()->get_current_animation(); + if (anim.is_null()) + return; + + String existing = player->find_animation(anim); + if (existing!="") { + _select_anim_by_name(existing); + return; //already has + } + + int count=1; + String base=anim->get_name(); + bool noname=false; + if (base=="") { + base="New Anim"; + noname=true; + } + + while(true) { + String attempt = base; + if (count>1) + attempt+=" ("+itos(count)+")"; + if (player->has_animation(attempt)) { + count++; + continue; + } + base=attempt; + break; + } + + if (noname) + anim->set_name(base); + + undo_redo->create_action("Add Animation From Editor"); + undo_redo->add_do_method(player,"add_animation",base,anim); + undo_redo->add_undo_method(player,"remove_animation",base); + undo_redo->add_do_method(this,"_animation_player_changed",player); + undo_redo->add_undo_method(this,"_animation_player_changed",player); + undo_redo->commit_action(); + + _select_anim_by_name(base); + + +} +#endif + +void AnimationPlayerEditor::_animation_key_editor_anim_len_changed(float p_len) { + + seek->set_max(p_len); + +} + + +void AnimationPlayerEditor::_animation_key_editor_seek(float p_pos) { + + if (!is_visible()) + return; + if (!player) + return; + + if (player->is_playing() ) + return; + + seek->set_val(p_pos); + + + //seekit +} + +void AnimationPlayerEditor::_hide_anim_editors() { + + player=NULL; + hide(); + set_process(false); + if (edit_anim->is_pressed()) { + + editor->get_animation_editor()->set_animation(Ref<Animation>()); + editor->get_animation_editor()->set_root(NULL); + editor->animation_editor_make_visible(false); + edit_anim->set_pressed(false); + } +} + +void AnimationPlayerEditor::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_input_event"),&AnimationPlayerEditor::_input_event); + ObjectTypeDB::bind_method(_MD("_node_removed"),&AnimationPlayerEditor::_node_removed); + ObjectTypeDB::bind_method(_MD("_play_pressed"),&AnimationPlayerEditor::_play_pressed); + ObjectTypeDB::bind_method(_MD("_stop_pressed"),&AnimationPlayerEditor::_stop_pressed); + ObjectTypeDB::bind_method(_MD("_autoplay_pressed"),&AnimationPlayerEditor::_autoplay_pressed); + ObjectTypeDB::bind_method(_MD("_pause_pressed"),&AnimationPlayerEditor::_pause_pressed); + ObjectTypeDB::bind_method(_MD("_animation_selected"),&AnimationPlayerEditor::_animation_selected); + ObjectTypeDB::bind_method(_MD("_animation_name_edited"),&AnimationPlayerEditor::_animation_name_edited); + ObjectTypeDB::bind_method(_MD("_animation_new"),&AnimationPlayerEditor::_animation_new); + ObjectTypeDB::bind_method(_MD("_animation_rename"),&AnimationPlayerEditor::_animation_rename); + ObjectTypeDB::bind_method(_MD("_animation_load"),&AnimationPlayerEditor::_animation_load); + ObjectTypeDB::bind_method(_MD("_animation_remove"),&AnimationPlayerEditor::_animation_remove); + ObjectTypeDB::bind_method(_MD("_animation_blend"),&AnimationPlayerEditor::_animation_blend); + ObjectTypeDB::bind_method(_MD("_animation_edit"),&AnimationPlayerEditor::_animation_edit); + ObjectTypeDB::bind_method(_MD("_animation_resource_edit"),&AnimationPlayerEditor::_animation_resource_edit); + ObjectTypeDB::bind_method(_MD("_file_selected"),&AnimationPlayerEditor::_file_selected); + ObjectTypeDB::bind_method(_MD("_seek_value_changed"),&AnimationPlayerEditor::_seek_value_changed); + ObjectTypeDB::bind_method(_MD("_animation_player_changed"),&AnimationPlayerEditor::_animation_player_changed); + ObjectTypeDB::bind_method(_MD("_blend_edited"),&AnimationPlayerEditor::_blend_edited); +// ObjectTypeDB::bind_method(_MD("_seek_frame_changed"),&AnimationPlayerEditor::_seek_frame_changed); + ObjectTypeDB::bind_method(_MD("_scale_changed"),&AnimationPlayerEditor::_scale_changed); + //ObjectTypeDB::bind_method(_MD("_editor_store_all"),&AnimationPlayerEditor::_editor_store_all); + ///jectTypeDB::bind_method(_MD("_editor_load_all"),&AnimationPlayerEditor::_editor_load_all); + ObjectTypeDB::bind_method(_MD("_list_changed"),&AnimationPlayerEditor::_list_changed); + ObjectTypeDB::bind_method(_MD("_animation_key_editor_seek"),&AnimationPlayerEditor::_animation_key_editor_seek); + ObjectTypeDB::bind_method(_MD("_animation_key_editor_anim_len_changed"),&AnimationPlayerEditor::_animation_key_editor_anim_len_changed); + ObjectTypeDB::bind_method(_MD("_hide_anim_editors"),&AnimationPlayerEditor::_hide_anim_editors); + ObjectTypeDB::bind_method(_MD("_animation_duplicate"),&AnimationPlayerEditor::_animation_duplicate); + ObjectTypeDB::bind_method(_MD("_blend_editor_next_changed"),&AnimationPlayerEditor::_blend_editor_next_changed); + + + + +} + +AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) { + editor=p_editor; + + updating=false; + + set_focus_mode(FOCUS_ALL); + + player=NULL; + add_style_override("panel", get_stylebox("panel","Panel")); + + + Label * l; + + /*l= memnew( Label ); + l->set_text("Animation Player:"); + add_child(l);*/ + + HBoxContainer *hb = memnew( HBoxContainer ); + add_child(hb); + + + add_anim = memnew( Button ); + add_anim->set_tooltip("Create new animation in player."); + + hb->add_child(add_anim); + + + + load_anim = memnew( Button ); + load_anim->set_tooltip("Load an animation from disk."); + hb->add_child(load_anim); + + duplicate_anim = memnew( Button ); + hb->add_child(duplicate_anim); + duplicate_anim->set_tooltip("Duplicate Animation"); + + animation = memnew( OptionButton ); + hb->add_child(animation); + animation->set_h_size_flags(SIZE_EXPAND_FILL); + animation->set_tooltip("Display list of animations in player."); + + autoplay = memnew( Button ); + hb->add_child(autoplay); + autoplay->set_tooltip("Autoplay On Load"); + + + rename_anim = memnew( Button ); + hb->add_child(rename_anim); + rename_anim->set_tooltip("Rename Animation"); + + remove_anim = memnew( Button ); + + hb->add_child(remove_anim); + remove_anim->set_tooltip("Remove Animation"); + + blend_anim = memnew( Button ); + hb->add_child(blend_anim); + blend_anim->set_tooltip("Edit Target Blend Times"); + + + + edit_anim = memnew( Button ); + edit_anim->set_toggle_mode(true); + hb->add_child(edit_anim); + edit_anim->set_tooltip("Open animation editor.\nProperty editor will displays all editable keys too."); + + + hb = memnew (HBoxContainer); + add_child(hb); + + play = memnew( Button ); + play->set_tooltip("Play selected animation."); + + hb->add_child(play); + + stop = memnew( Button ); + stop->set_toggle_mode(true); + hb->add_child(stop); + play->set_tooltip("Stop animation playback."); + + //pause = memnew( Button ); + //pause->set_toggle_mode(true); + //hb->add_child(pause); + + seek = memnew( HSlider ); + seek->set_val(0); + seek->set_step(0.01); + hb->add_child(seek); + seek->set_h_size_flags(SIZE_EXPAND_FILL); + seek->set_stretch_ratio(8); + seek->set_tooltip("Seek animation (when stopped)."); + + frame = memnew( SpinBox ); + hb->add_child(frame); + frame->set_h_size_flags(SIZE_EXPAND_FILL); + frame->set_stretch_ratio(2); + frame->set_tooltip("Animation position (in seconds)."); + seek->share(frame); + + + + scale = memnew( LineEdit ); + hb->add_child(scale); + scale->set_h_size_flags(SIZE_EXPAND_FILL); + scale->set_stretch_ratio(1); + scale->set_tooltip("Scale animation playback globally for the node."); + scale->hide(); + + resource_edit_anim= memnew( Button ); + hb->add_child(resource_edit_anim); + + + file = memnew(FileDialog); + add_child(file); + + name_dialog = memnew( ConfirmationDialog ); + name_dialog->set_hide_on_ok(false); + add_child(name_dialog); + name = memnew( LineEdit ); + name_dialog->add_child(name); + name->set_pos(Point2(18,30)); + name->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,10); + name_dialog->register_text_enter(name); + + + l = memnew( Label ); + l->set_text("Animation Name:"); + l->set_pos( Point2(10,10) ); + + name_dialog->add_child(l); + name_title=l; + + error_dialog = memnew( ConfirmationDialog ); + error_dialog->get_ok()->set_text("Close"); + //error_dialog->get_cancel()->set_text("Close"); + error_dialog->set_text("Error!"); + add_child(error_dialog); + + name_dialog->connect("confirmed", this,"_animation_name_edited"); + + blend_editor.dialog = memnew( AcceptDialog ); + add_child(blend_editor.dialog); + blend_editor.dialog->get_ok()->set_text("Close"); + blend_editor.dialog->set_hide_on_ok(true); + VBoxContainer *blend_vb = memnew( VBoxContainer); + blend_editor.dialog->add_child(blend_vb); + blend_editor.dialog->set_child_rect(blend_vb); + blend_editor.tree = memnew( Tree ); + blend_editor.tree->set_columns(2); + blend_vb->add_margin_child("Blend Times: ",blend_editor.tree,true); + blend_editor.next = memnew( LineEdit ); + blend_vb->add_margin_child("Next (Auto Queue):",blend_editor.next); + blend_editor.dialog->set_title("Cross-Animation Blend Times"); + updating_blends=false; + + blend_editor.tree->connect("item_edited",this,"_blend_edited"); + + + autoplay->connect("pressed", this,"_autoplay_pressed"); + autoplay->set_toggle_mode(true); + play->connect("pressed", this,"_play_pressed"); + stop->connect("pressed", this,"_stop_pressed"); + //pause->connect("pressed", this,"_pause_pressed"); + add_anim->connect("pressed", this,"_animation_new"); + rename_anim->connect("pressed", this,"_animation_rename"); + load_anim->connect("pressed", this,"_animation_load"); + duplicate_anim->connect("pressed", this,"_animation_duplicate"); + //frame->connect("text_entered", this,"_seek_frame_changed"); + edit_anim->connect("pressed", this,"_animation_edit"); + blend_anim->connect("pressed", this,"_animation_blend"); + remove_anim->connect("pressed", this,"_animation_remove"); + animation->connect("item_selected", this,"_animation_selected",Vector<Variant>(),true); + resource_edit_anim->connect("pressed", this,"_animation_resource_edit"); + file->connect("file_selected", this,"_file_selected"); + seek->connect("value_changed", this, "_seek_value_changed",Vector<Variant>(),true); + scale->connect("text_entered", this, "_scale_changed",Vector<Variant>(),true); + editor->get_animation_editor()->connect("timeline_changed",this,"_animation_key_editor_seek"); + editor->get_animation_editor()->connect("animation_len_changed",this,"_animation_key_editor_anim_len_changed"); + + HBoxContainer *ahb = editor->get_animation_panel_hb(); + nodename = memnew( Label ); + ahb->add_child(nodename); + nodename->set_h_size_flags(SIZE_EXPAND_FILL); + nodename->set_opacity(0.5); + pin = memnew( TextureButton ); + pin->set_toggle_mode(true); + ahb->add_child(pin); + + renaming=false; + last_active=false; +} + + +void AnimationPlayerEditorPlugin::edit(Object *p_object) { + + anim_editor->set_undo_redo(&get_undo_redo()); + if (!p_object) + return; + anim_editor->edit(p_object->cast_to<AnimationPlayer>()); +} + +bool AnimationPlayerEditorPlugin::handles(Object *p_object) const { + + return p_object->is_type("AnimationPlayer"); +} + +void AnimationPlayerEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + anim_editor->show(); + anim_editor->set_process(true); + anim_editor->ensure_visibility(); + editor->animation_panel_make_visible(true); + } else { + +// anim_editor->hide(); +// anim_editor->set_idle_process(false); + } + +} + +AnimationPlayerEditorPlugin::AnimationPlayerEditorPlugin(EditorNode *p_node) { + + editor=p_node; + anim_editor = memnew( AnimationPlayerEditor(editor) ); + editor->get_animation_panel()->add_child(anim_editor); + /* + editor->get_viewport()->add_child(anim_editor); + anim_editor->set_area_as_parent_rect(); + anim_editor->set_anchor( MARGIN_TOP, Control::ANCHOR_END); + anim_editor->set_margin( MARGIN_TOP, 75 ); + anim_editor->set_anchor( MARGIN_RIGHT, Control::ANCHOR_END); + anim_editor->set_margin( MARGIN_RIGHT, 0 );*/ + anim_editor->hide(); + + + +} + + +AnimationPlayerEditorPlugin::~AnimationPlayerEditorPlugin() +{ +} + + diff --git a/tools/editor/plugins/animation_player_editor_plugin.h b/tools/editor/plugins/animation_player_editor_plugin.h new file mode 100644 index 000000000..2c6bcae97 --- /dev/null +++ b/tools/editor/plugins/animation_player_editor_plugin.h @@ -0,0 +1,166 @@ +/*************************************************************************/ +/* animation_player_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef ANIMATION_PLAYER_EDITOR_PLUGIN_H +#define ANIMATION_PLAYER_EDITOR_PLUGIN_H + +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" +#include "scene/animation/animation_player.h" +#include "scene/gui/dialogs.h" +#include "scene/gui/texture_button.h" +#include "scene/gui/slider.h" +#include "scene/gui/spin_box.h" + + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + +class AnimationPlayerEditor : public VBoxContainer { + + OBJ_TYPE(AnimationPlayerEditor, VBoxContainer ); + + EditorNode *editor; + AnimationPlayer *player; + + + OptionButton *animation; + Button *stop; + Button *play; +// Button *pause; + Button *add_anim; + Button *autoplay; + Button *rename_anim; + Button *duplicate_anim; + Button *edit_anim; + Button *resource_edit_anim; + Button *load_anim; + Button *blend_anim; + Button *remove_anim; + TextureButton *pin; + Label *nodename; + SpinBox *frame; + HSlider *seek; + LineEdit *scale; + LineEdit *name; + Label *name_title; + UndoRedo *undo_redo; + Ref<Texture> autoplay_icon; + bool last_active; + + FileDialog *file; + + struct BlendEditor { + + AcceptDialog * dialog; + Tree *tree; + LineEdit *next; + + } blend_editor; + + + ConfirmationDialog *name_dialog; + ConfirmationDialog *error_dialog; + bool renaming; + + + bool updating; + bool updating_blends; + + void _select_anim_by_name(const String& p_anim); + void _play_pressed(); + void _autoplay_pressed(); + void _stop_pressed(); + void _pause_pressed(); + void _animation_selected(int p_which); + void _animation_new(); + void _animation_rename(); + void _animation_name_edited(); + void _animation_load(); + void _animation_remove(); + void _animation_blend(); + void _animation_edit(); + void _animation_duplicate(); + void _animation_resource_edit(); + void _scale_changed(const String& p_scale); + void _file_selected(String p_file); + void _seek_frame_changed(const String& p_frame); + void _seek_value_changed(float p_value); + void _blend_editor_next_changed(const String& p_string); + + void _list_changed(); + void _update_animation(); + void _update_player(); + void _blend_edited(); + + + void _hide_anim_editors(); + + void _animation_player_changed(Object *p_pl); + + void _animation_key_editor_seek(float p_pos); + void _animation_key_editor_anim_len_changed(float p_new); + + AnimationPlayerEditor(); +protected: + + void _notification(int p_what); + void _input_event(InputEvent p_event); + void _node_removed(Node *p_node); + static void _bind_methods(); +public: + + void ensure_visibility(); + + void set_undo_redo(UndoRedo *p_undo_redo) { undo_redo=p_undo_redo; } + void edit(AnimationPlayer *p_player); + AnimationPlayerEditor(EditorNode *p_editor); +}; + +class AnimationPlayerEditorPlugin : public EditorPlugin { + + OBJ_TYPE( AnimationPlayerEditorPlugin, EditorPlugin ); + + AnimationPlayerEditor *anim_editor; + EditorNode *editor; + +public: + + virtual String get_name() const { return "Anim"; } + 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); + + AnimationPlayerEditorPlugin(EditorNode *p_node); + ~AnimationPlayerEditorPlugin(); + +}; + +#endif // ANIMATION_PLAYER_EDITOR_PLUGIN_H diff --git a/tools/editor/plugins/animation_tree_editor_plugin.cpp b/tools/editor/plugins/animation_tree_editor_plugin.cpp new file mode 100644 index 000000000..aeef59309 --- /dev/null +++ b/tools/editor/plugins/animation_tree_editor_plugin.cpp @@ -0,0 +1,1535 @@ +/*************************************************************************/ +/* animation_tree_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "animation_tree_editor_plugin.h" + +#include "scene/gui/menu_button.h" +#include "scene/gui/panel.h" +#include "scene/main/viewport.h" +#include "core/io/resource_loader.h" +#include "core/globals.h" +#include "os/input.h" +#include "os/keyboard.h" + +void AnimationTreeEditor::edit(AnimationTreePlayer* p_anim_tree) { + + + anim_tree=p_anim_tree; + + if (!anim_tree) { + hide(); + } else { + order.clear(); + p_anim_tree->get_node_list(&order); + /* + for(List<StringName>::Element* E=order.front();E;E=E->next()) { + + if (E->get() >= (int)last_id) + last_id=E->get()+1; + }*/ + play_button->set_pressed(p_anim_tree->is_active()); + //read the orders + } + +} + +Size2 AnimationTreeEditor::_get_maximum_size() { + + Size2 max; + + for(List<StringName>::Element *E=order.front();E;E=E->next()) { + + Point2 pos = anim_tree->node_get_pos(E->get()); + + if (click_type==CLICK_NODE && click_node==E->get()) { + + pos+=click_motion-click_pos; + } + pos+=get_node_size(E->get()); + if (pos.x>max.x) + max.x=pos.x; + if (pos.y>max.y) + max.y=pos.y; + + } + + return max; +} + + +const char* AnimationTreeEditor::_node_type_names[]={"Output","Animation","OneShot","Mix","Blend2","Blend3","Blend4","TimeScale","TimeSeek","Transition"}; + +Size2 AnimationTreeEditor::get_node_size(const StringName& p_node) const { + + AnimationTreePlayer::NodeType type=anim_tree->node_get_type(p_node); + + Ref<StyleBox> style = get_stylebox("panel","PopupMenu"); + Ref<Font> font = get_font("font","PopupMenu"); + Color font_color = get_color("font_color","PopupMenu"); + + Size2 size = style->get_minimum_size(); + + int count=2; // title and name + int inputs = anim_tree->node_get_input_count(p_node); + count += inputs?inputs:1; + String name = p_node; + + + float name_w = font->get_string_size( name ).width; + float type_w = font->get_string_size( String(_node_type_names[type]) ).width; + float max_w=MAX(name_w,type_w); + + + + switch(type) { + case AnimationTreePlayer::NODE_TIMESEEK: + case AnimationTreePlayer::NODE_OUTPUT: {} break; + case AnimationTreePlayer::NODE_ANIMATION: + case AnimationTreePlayer::NODE_ONESHOT: + case AnimationTreePlayer::NODE_MIX: + case AnimationTreePlayer::NODE_BLEND2: + case AnimationTreePlayer::NODE_BLEND3: + case AnimationTreePlayer::NODE_BLEND4: + case AnimationTreePlayer::NODE_TIMESCALE: + case AnimationTreePlayer::NODE_TRANSITION: { + + + size.height+=font->get_height(); + } break; + case AnimationTreePlayer::NODE_MAX: {} + } + + size.x+=max_w+20; + size.y+=count*(font->get_height()+get_constant("vseparation","PopupMenu")); + + return size; +} + + +void AnimationTreeEditor::_edit_dialog_changede(String) { + + edit_dialog->hide(); +} + +void AnimationTreeEditor::_edit_dialog_changeds(String s) { + + _edit_dialog_changed(); +} + +void AnimationTreeEditor::_edit_dialog_changedf(float) { + + _edit_dialog_changed(); +} + +void AnimationTreeEditor::_edit_dialog_changed() { + + if (updating_edit) + return; + + if (renaming_edit) { + + if (anim_tree->node_rename(edited_node,edit_line[0]->get_text())==OK) { + for(List<StringName>::Element* E=order.front();E;E=E->next()) { + + if (E->get() == edited_node) + E->get()=edit_line[0]->get_text(); + } + edited_node=edit_line[0]->get_text(); + } + update(); + return; + } + + AnimationTreePlayer::NodeType type=anim_tree->node_get_type(edited_node); + + switch(type) { + + case AnimationTreePlayer::NODE_TIMESCALE: + anim_tree->timescale_node_set_scale(edited_node,edit_line[0]->get_text().to_double()); + break; + case AnimationTreePlayer::NODE_ONESHOT: + anim_tree->oneshot_node_set_fadein_time(edited_node,edit_line[0]->get_text().to_double()); + anim_tree->oneshot_node_set_fadeout_time(edited_node,edit_line[1]->get_text().to_double()); + anim_tree->oneshot_node_set_autorestart_delay(edited_node,edit_line[2]->get_text().to_double()); + anim_tree->oneshot_node_set_autorestart_random_delay(edited_node,edit_line[3]->get_text().to_double()); + anim_tree->oneshot_node_set_autorestart(edited_node,edit_check->is_pressed()); + anim_tree->oneshot_node_set_mix_mode(edited_node,edit_option->get_selected()); + + break; + + case AnimationTreePlayer::NODE_MIX: + + anim_tree->mix_node_set_amount(edited_node,edit_scroll[0]->get_val()); + break; + case AnimationTreePlayer::NODE_BLEND2: + anim_tree->blend2_node_set_amount(edited_node,edit_scroll[0]->get_val()); + + break; + + case AnimationTreePlayer::NODE_BLEND3: + anim_tree->blend3_node_set_amount(edited_node,edit_scroll[0]->get_val()); + + break; + case AnimationTreePlayer::NODE_BLEND4: + + anim_tree->blend4_node_set_amount(edited_node,Point2(edit_scroll[0]->get_val(),edit_scroll[1]->get_val())); + + break; + + case AnimationTreePlayer::NODE_TRANSITION: { + anim_tree->transition_node_set_xfade_time(edited_node,edit_line[0]->get_text().to_double()); + if (anim_tree->transition_node_get_current(edited_node)!=edit_option->get_selected()) + anim_tree->transition_node_set_current(edited_node,edit_option->get_selected()); + } break; + default: {} + } + +} + +void AnimationTreeEditor::_edit_dialog_animation_changed() { + + + Ref<Animation> anim = property_editor->get_variant().operator RefPtr(); + anim_tree->animation_node_set_animation(edited_node,anim); + update(); +} + +void AnimationTreeEditor::_edit_dialog_edit_animation() { + + if (get_scene()->is_editor_hint()) { + get_scene()->get_root()->get_child(0)->call("_resource_selected", property_editor->get_variant().operator RefPtr()); + }; +}; + +void AnimationTreeEditor::_edit_oneshot_start() { + + anim_tree->oneshot_node_start(edited_node); +} + +void AnimationTreeEditor::_play_toggled() { + + anim_tree->set_active(play_button->is_pressed()); +} + + +void AnimationTreeEditor::_master_anim_menu_item(int p_item) { + + String str = master_anim_popup->get_item_text(p_item); + anim_tree->animation_node_set_master_animation(edited_node,str); + update(); +} + +void AnimationTreeEditor::_popup_edit_dialog() { + + updating_edit=true; + + for(int i=0;i<2;i++) + edit_scroll[i]->hide(); + + for(int i=0;i<4;i++) { + + edit_line[i]->hide(); + edit_label[i]->hide(); + } + + edit_option->hide(); + edit_button->hide();; + filter_button->hide(); + edit_check->hide();; + + Point2 pos = anim_tree->node_get_pos(edited_node)-Point2(h_scroll->get_val(),v_scroll->get_val()); + Ref<StyleBox> style = get_stylebox("panel","PopupMenu"); + Size2 size = get_node_size(edited_node); + Point2 popup_pos( pos.x+style->get_margin(MARGIN_LEFT), pos.y+size.y-style->get_margin(MARGIN_BOTTOM)); + popup_pos+=get_global_pos(); + + if (renaming_edit) { + + edit_label[0]->set_text("New name:"); + edit_label[0]->set_pos(Point2(5,5)); + edit_label[0]->show(); + edit_line[0]->set_begin(Point2(15,25)); + edit_line[0]->set_text(edited_node); + edit_line[0]->show(); + edit_dialog->set_size(Size2(150,50)); + + } else { + + AnimationTreePlayer::NodeType type=anim_tree->node_get_type(edited_node); + + + switch(type) { + + case AnimationTreePlayer::NODE_ANIMATION: + + if (anim_tree->get_master_player()!=NodePath() && anim_tree->has_node(anim_tree->get_master_player()) && anim_tree->get_node(anim_tree->get_master_player())->cast_to<AnimationPlayer>()) { + + AnimationPlayer *ap = anim_tree->get_node(anim_tree->get_master_player())->cast_to<AnimationPlayer>(); + master_anim_popup->clear(); + List<StringName> sn; + ap->get_animation_list(&sn); + sn.sort_custom<StringName::AlphCompare>(); + for (List<StringName>::Element *E=sn.front();E;E=E->next()) { + master_anim_popup->add_item(E->get()); + } + + master_anim_popup->set_pos(popup_pos); + master_anim_popup->popup(); + } else { + property_editor->edit(this,"",Variant::OBJECT,anim_tree->animation_node_get_animation(edited_node),PROPERTY_HINT_RESOURCE_TYPE,"Animation"); + property_editor->set_pos(popup_pos); + property_editor->popup(); + updating_edit=false; + } + return; + case AnimationTreePlayer::NODE_TIMESCALE: + edit_label[0]->set_text("Scale:"); + edit_label[0]->set_pos(Point2(5,5)); + edit_label[0]->show(); + edit_line[0]->set_begin(Point2(15,25)); + edit_line[0]->set_text(rtos(anim_tree->timescale_node_get_scale(edited_node))); + edit_line[0]->show(); + edit_dialog->set_size(Size2(150,50)); + break; + case AnimationTreePlayer::NODE_ONESHOT: + edit_label[0]->set_text("Fade In (s):"); + edit_label[0]->set_pos(Point2(5,5)); + edit_label[0]->show(); + edit_line[0]->set_begin(Point2(15,25)); + edit_line[0]->set_text(rtos(anim_tree->oneshot_node_get_fadein_time(edited_node))); + edit_line[0]->show(); + edit_label[1]->set_text("Fade Out (s):"); + edit_label[1]->set_pos(Point2(5,55)); + edit_label[1]->show(); + edit_line[1]->set_begin(Point2(15,75)); + edit_line[1]->set_text(rtos(anim_tree->oneshot_node_get_fadeout_time(edited_node))); + edit_line[1]->show(); + + edit_option->clear(); + edit_option->add_item("Blend",0); + edit_option->add_item("Mix",1); + edit_option->set_begin(Point2(15,105)); + + edit_option->select( anim_tree->oneshot_node_get_mix_mode(edited_node)); + edit_option->show(); + + edit_check->set_text("Auto Restart:"); + edit_check->set_begin(Point2(15,125)); + edit_check->set_pressed(anim_tree->oneshot_node_has_autorestart(edited_node)); + edit_check->show(); + + edit_label[2]->set_text("Restart (s):"); + edit_label[2]->set_pos(Point2(5,145)); + edit_label[2]->show(); + edit_line[2]->set_begin(Point2(15,165)); + edit_line[2]->set_text(rtos(anim_tree->oneshot_node_get_autorestart_delay(edited_node))); + edit_line[2]->show(); + edit_label[3]->set_text("Random Restart (s):"); + edit_label[3]->set_pos(Point2(5,195)); + edit_label[3]->show(); + edit_line[3]->set_begin(Point2(15,215)); + edit_line[3]->set_text(rtos(anim_tree->oneshot_node_get_autorestart_random_delay(edited_node))); + edit_line[3]->show(); + + filter_button->set_begin(Point2(10,245)); + filter_button->show(); + + edit_button->set_begin(Point2(10,268)); + edit_button->set_text("Start!"); + + edit_button->show(); + + edit_dialog->set_size(Size2(180,293)); + + break; + + case AnimationTreePlayer::NODE_MIX: + + edit_label[0]->set_text("Amount:"); + edit_label[0]->set_pos(Point2(5,5)); + edit_label[0]->show(); + edit_scroll[0]->set_min(0); + edit_scroll[0]->set_max(1); + edit_scroll[0]->set_val(anim_tree->mix_node_get_amount(edited_node)); + edit_scroll[0]->set_begin(Point2(15,25)); + edit_scroll[0]->show(); + edit_dialog->set_size(Size2(150,50)); + + break; + case AnimationTreePlayer::NODE_BLEND2: + edit_label[0]->set_text("Blend:"); + edit_label[0]->set_pos(Point2(5,5)); + edit_label[0]->show(); + edit_scroll[0]->set_min(0); + edit_scroll[0]->set_max(1); + edit_scroll[0]->set_val(anim_tree->blend2_node_get_amount(edited_node)); + edit_scroll[0]->set_begin(Point2(15,25)); + edit_scroll[0]->show(); + filter_button->set_begin(Point2(10,47)); + filter_button->show(); + edit_dialog->set_size(Size2(150,74)); + + break; + + case AnimationTreePlayer::NODE_BLEND3: + edit_label[0]->set_text("Blend:"); + edit_label[0]->set_pos(Point2(5,5)); + edit_label[0]->show(); + edit_scroll[0]->set_min(-1); + edit_scroll[0]->set_max(1); + edit_scroll[0]->set_val(anim_tree->blend3_node_get_amount(edited_node)); + edit_scroll[0]->set_begin(Point2(15,25)); + edit_scroll[0]->show(); + edit_dialog->set_size(Size2(150,50)); + + break; + case AnimationTreePlayer::NODE_BLEND4: + + edit_label[0]->set_text("Blend 0:"); + edit_label[0]->set_pos(Point2(5,5)); + edit_label[0]->show(); + edit_scroll[0]->set_min(0); + edit_scroll[0]->set_max(1); + edit_scroll[0]->set_val(anim_tree->blend4_node_get_amount(edited_node).x); + edit_scroll[0]->set_begin(Point2(15,25)); + edit_scroll[0]->show(); + edit_label[1]->set_text("Blend 1:"); + edit_label[1]->set_pos(Point2(5,55)); + edit_label[1]->show(); + edit_scroll[1]->set_min(0); + edit_scroll[1]->set_max(1); + edit_scroll[1]->set_val(anim_tree->blend4_node_get_amount(edited_node).y); + edit_scroll[1]->set_begin(Point2(15,75)); + edit_scroll[1]->show(); + edit_dialog->set_size(Size2(150,100)); + + break; + + case AnimationTreePlayer::NODE_TRANSITION: { + + + edit_label[0]->set_text("X-Fade Time (s):"); + edit_label[0]->set_pos(Point2(5,5)); + edit_label[0]->show(); + edit_line[0]->set_begin(Point2(15,25)); + edit_line[0]->set_text(rtos(anim_tree->transition_node_get_xfade_time(edited_node))); + edit_line[0]->show(); + + edit_label[1]->set_text("Current:"); + edit_label[1]->set_pos(Point2(5,55)); + edit_label[1]->show(); + edit_option->set_begin(Point2(15,75)); + + edit_option->clear();; + + for(int i=0;i<anim_tree->transition_node_get_input_count(edited_node);i++) { + edit_option->add_item(itos(i),i); + } + + edit_option->select(anim_tree->transition_node_get_current(edited_node)); + edit_option->show(); + edit_dialog->set_size(Size2(150,100)); + + } break; + default: {} + + } + + } + + + + edit_dialog->set_pos(popup_pos); + edit_dialog->popup(); + + updating_edit=false; +} + +void AnimationTreeEditor::_draw_node(const StringName& p_node) { + + RID ci = get_canvas_item(); + AnimationTreePlayer::NodeType type=anim_tree->node_get_type(p_node); + + Ref<StyleBox> style = get_stylebox("panel","PopupMenu"); + Ref<Font> font = get_font("font","PopupMenu"); + Color font_color = get_color("font_color","PopupMenu"); + Color font_color_title = get_color("font_color_hover","PopupMenu"); + font_color_title.a*=0.8; + Ref<Texture> slot_icon = get_icon("NodeRealSlot","EditorIcons"); + + + Size2 size=get_node_size(p_node); + Point2 pos = anim_tree->node_get_pos(p_node); + if (click_type==CLICK_NODE && click_node==p_node) { + + pos+=click_motion-click_pos; + if (pos.x<5) + pos.x=5; + if (pos.y<5) + pos.y=5; + + } + + pos-=Point2(h_scroll->get_val(),v_scroll->get_val()); + + style->draw(ci,Rect2(pos,size)); + + float w = size.width-style->get_minimum_size().width; + float h = font->get_height()+get_constant("vseparation","PopupMenu"); + + Point2 ofs=style->get_offset()+pos; + Point2 ascofs(0,font->get_ascent()); + + Color bx = font_color_title; + bx.a*=0.1; + draw_rect(Rect2(ofs,Size2(size.width-style->get_minimum_size().width,font->get_height())),bx); + font->draw_halign(ci,ofs+ascofs,HALIGN_CENTER,w,String(_node_type_names[type]),font_color_title); + + ofs.y+=h; + font->draw_halign(ci,ofs+ascofs,HALIGN_CENTER,w,p_node,font_color); + ofs.y+=h; + + int count=2; // title and name + int inputs = anim_tree->node_get_input_count(p_node); + count += inputs?inputs:1; + + float icon_h_ofs = Math::floor(( font->get_height()-slot_icon->get_height())/2.0 )+1; + + if (type!=AnimationTreePlayer::NODE_OUTPUT) + slot_icon->draw(ci,ofs+Point2(w,icon_h_ofs)); //output + + if (inputs) { + for(int i=0;i<inputs;i++) { + + slot_icon->draw(ci,ofs+Point2(-slot_icon->get_width(),icon_h_ofs)); + String text; + switch(type) { + + case AnimationTreePlayer::NODE_TIMESCALE: + case AnimationTreePlayer::NODE_TIMESEEK: text="in"; break; + case AnimationTreePlayer::NODE_OUTPUT: text="out"; break; + case AnimationTreePlayer::NODE_ANIMATION: break; + case AnimationTreePlayer::NODE_ONESHOT: text=(i==0?"in":"add"); break; + case AnimationTreePlayer::NODE_BLEND2: + case AnimationTreePlayer::NODE_MIX: text=(i==0?"a":"b"); break; + case AnimationTreePlayer::NODE_BLEND3: + switch(i) { + case 0: text="b-"; break; + case 1: text="a"; break; + case 2: text="b+"; break; + + } + break; + + + case AnimationTreePlayer::NODE_BLEND4: + switch(i) { + case 0: text="a0"; break; + case 1: text="b0"; break; + case 2: text="a1"; break; + case 3: text="b1"; break; + } + break; + + case AnimationTreePlayer::NODE_TRANSITION: + text=itos(i); + if (anim_tree->transition_node_has_input_auto_advance(p_node,i)) + text+="->"; + + break; + default: {} + } + font->draw(ci,ofs+ascofs+Point2(3,0),text,font_color); + + ofs.y+=h; + } + } else { + ofs.y+=h; + } + + Ref<StyleBox> pg_bg=get_stylebox("bg","ProgressBar"); + Ref<StyleBox> pg_fill=get_stylebox("fill","ProgressBar"); + Rect2 pg_rect(ofs,Size2(w,h)); + + bool editable=true; + switch(type) { + case AnimationTreePlayer::NODE_ANIMATION: { + + Ref<Animation> anim = anim_tree->animation_node_get_animation(p_node); + String text; + if (anim_tree->animation_node_get_master_animation(p_node)!="") + text=anim_tree->animation_node_get_master_animation(p_node); + else if (anim.is_null()) + text="load.."; + else + text=anim->get_name(); + + font->draw_halign(ci,ofs+ascofs,HALIGN_CENTER,w,text,font_color_title); + + } break; + case AnimationTreePlayer::NODE_ONESHOT: + case AnimationTreePlayer::NODE_MIX: + case AnimationTreePlayer::NODE_BLEND2: + case AnimationTreePlayer::NODE_BLEND3: + case AnimationTreePlayer::NODE_BLEND4: + case AnimationTreePlayer::NODE_TIMESCALE: + case AnimationTreePlayer::NODE_TRANSITION: { + + font->draw_halign(ci,ofs+ascofs,HALIGN_CENTER,w,"edit..",font_color_title); + } break; + default: editable=false; + } + + if (editable) { + + Ref<Texture> arrow = get_icon("arrow","Tree"); + Point2 arrow_ofs( w-arrow->get_width(),Math::floor( (h-arrow->get_height())/2) ); + arrow->draw(ci,ofs+arrow_ofs); + } +} + +#if 0 +void AnimationTreeEditor::_node_param_changed() { + +// anim_tree->node_set_param( click_node,property_editor->get_variant() ); +// update(); +// _write_anim_tree_graph(); +} +#endif + +AnimationTreeEditor::ClickType AnimationTreeEditor::_locate_click(const Point2& p_click,StringName *p_node_id,int *p_slot_index) const { + + + Ref<StyleBox> style = get_stylebox("panel","PopupMenu"); + Ref<Font> font = get_font("font","PopupMenu"); + Color font_color = get_color("font_color","PopupMenu"); + + float h = (font->get_height()+get_constant("vseparation","PopupMenu")); + + for(const List<StringName>::Element *E=order.back();E;E=E->prev()) { + + StringName node = E->get(); + + AnimationTreePlayer::NodeType type=anim_tree->node_get_type(node); + + Point2 pos = anim_tree->node_get_pos(node); + Size2 size = get_node_size(node); + + pos-=Point2(h_scroll->get_val(),v_scroll->get_val()); + + if (!Rect2(pos,size).has_point(p_click)) + continue; + + if (p_node_id) + *p_node_id=node; + + pos=p_click-pos; + + float y = pos.y-style->get_offset().height; + + if (y<h) + return CLICK_NODE; + y-=h; + + if (y<h) + return CLICK_NODE; + + y-=h; + + int count=0; // title and name + int inputs = anim_tree->node_get_input_count(node); + count += inputs?inputs:1; + + for(int i=0;i<count;i++) { + + if (y<h) { + + if (inputs==0 || ( type!=AnimationTreePlayer::NODE_OUTPUT && pos.x > size.width/2)) { + + if (p_slot_index) + *p_slot_index=0; + return CLICK_OUTPUT_SLOT; + } else { + + if (p_slot_index) + *p_slot_index=i; + return CLICK_INPUT_SLOT; + } + } + y-=h; + } + + return (type!=AnimationTreePlayer::NODE_OUTPUT && type!=AnimationTreePlayer::NODE_TIMESEEK)?CLICK_PARAMETER:CLICK_NODE; + } + + return CLICK_NONE; +} + +Point2 AnimationTreeEditor::_get_slot_pos(const StringName& p_node,bool p_input,int p_slot) { + + Ref<StyleBox> style = get_stylebox("panel","PopupMenu"); + Ref<Font> font = get_font("font","PopupMenu"); + Ref<Texture> slot_icon = get_icon("NodeRealSlot","EditorIcons"); + + Size2 size=get_node_size(p_node); + Point2 pos = anim_tree->node_get_pos(p_node); + + if (click_type==CLICK_NODE && click_node==p_node) { + + pos+=click_motion-click_pos; + if (pos.x<5) + pos.x=5; + if (pos.y<5) + pos.y=5; + + } + + pos-=Point2(h_scroll->get_val(),v_scroll->get_val()); + + + float w = size.width-style->get_minimum_size().width; + float h = font->get_height()+get_constant("vseparation","PopupMenu"); + + + pos+=style->get_offset(); + + pos.y+=h*2; + + pos.y+=h*p_slot; + + pos+=Point2( -slot_icon->get_width()/2.0, h/2.0).floor(); + + if(!p_input) { + pos.x+=w+slot_icon->get_width(); + + } + + return pos; + +} + +#if 0 +void AnimationTreeEditor::_node_edit_property(const StringName& p_node) { + + Ref<StyleBox> style = get_stylebox("panel","PopupMenu"); + Size2 size = get_node_size(p_node); + Point2 pos = Point2( anim_tree->node_get_pos_x(p_node), anim_tree->node_get_pos_y(p_node) )-offset; + + VisualServer::AnimationTreeNodeType type=anim_tree->node_get_type(p_node); + + PropertyInfo ph = VisualServer::get_singleton()->anim_tree_node_get_type_info(type); + if (ph.type==Variant::NIL) + return; + if (ph.type==Variant::_RID) + ph.type=Variant::RESOURCE; + + property_editor->edit(NULL,ph.name,ph.type,anim_tree->node_get_param(p_node),ph.hint,ph.hint_string); + + Point2 popup_pos=Point2( pos.x+(size.width-property_editor->get_size().width)/2.0,pos.y+(size.y-style->get_margin(MARGIN_BOTTOM))).floor(); + popup_pos+=get_global_pos(); + property_editor->set_pos(popup_pos); + + property_editor->popup(); + +} +#endif + +void AnimationTreeEditor::_input_event(InputEvent p_event) { + + switch(p_event.type) { + + case InputEvent::MOUSE_BUTTON: { + + if (p_event.mouse_button.pressed) { + + + if (p_event.mouse_button.button_index==1) { + click_pos=Point2(p_event.mouse_button.x,p_event.mouse_button.y); + click_motion=click_pos; + click_type = _locate_click(click_pos,&click_node,&click_slot); + if( click_type!=CLICK_NONE) { + + order.erase(click_node); + order.push_back(click_node); + update(); + } + + switch(click_type) { + case CLICK_INPUT_SLOT: { + click_pos=_get_slot_pos(click_node,true,click_slot); + } break; + case CLICK_OUTPUT_SLOT: { + click_pos=_get_slot_pos(click_node,false,click_slot); + } break; + case CLICK_PARAMETER: { + + edited_node=click_node; + renaming_edit=false; + _popup_edit_dialog(); + //open editor + // _node_edit_property(click_node); + } break; + default:{} + } + } + if (p_event.mouse_button.button_index==2) { + + if (click_type!=CLICK_NONE) { + click_type=CLICK_NONE; + update(); + } else { + // try to disconnect/remove + + Point2 rclick_pos=Point2(p_event.mouse_button.x,p_event.mouse_button.y); + rclick_type = _locate_click(rclick_pos,&rclick_node,&rclick_slot); + if (rclick_type==CLICK_INPUT_SLOT || rclick_type==CLICK_OUTPUT_SLOT) { + + node_popup->clear(); + node_popup->add_item("Disconnect",NODE_DISCONNECT); + if (anim_tree->node_get_type(rclick_node)==AnimationTreePlayer::NODE_TRANSITION) { + node_popup->add_item("Add Input",NODE_ADD_INPUT); + if (rclick_type==CLICK_INPUT_SLOT) { + if (anim_tree->transition_node_has_input_auto_advance(rclick_node,rclick_slot)) + node_popup->add_item("Clear Auto-Advance",NODE_CLEAR_AUTOADVANCE); + else + node_popup->add_item("Set Auto-Advance",NODE_SET_AUTOADVANCE); + node_popup->add_item("Delete Input",NODE_DELETE_INPUT); + + } + } + + node_popup->set_pos(rclick_pos+get_global_pos()); + node_popup->popup(); + + } + + if (rclick_type==CLICK_NODE) { + node_popup->clear(); + node_popup->add_item("Rename",NODE_RENAME); + node_popup->add_item("Remove",NODE_ERASE); + if (anim_tree->node_get_type(rclick_node)==AnimationTreePlayer::NODE_TRANSITION) + node_popup->add_item("Add Input",NODE_ADD_INPUT); + node_popup->set_pos(rclick_pos+get_global_pos()); + node_popup->popup(); + } + + + } + } + } else { + + if (p_event.mouse_button.button_index==1 && click_type!=CLICK_NONE) { + + switch(click_type) { + case CLICK_INPUT_SLOT: + case CLICK_OUTPUT_SLOT: { + + Point2 dst_click_pos=Point2(p_event.mouse_button.x,p_event.mouse_button.y); + StringName id; + int slot; + ClickType dst_click_type = _locate_click(dst_click_pos,&id,&slot); + + if (dst_click_type==CLICK_INPUT_SLOT && click_type==CLICK_OUTPUT_SLOT) { + + anim_tree->connect(click_node,id,slot); + + } + if (click_type==CLICK_INPUT_SLOT && dst_click_type==CLICK_OUTPUT_SLOT) { + + anim_tree->connect(id,click_node,click_slot); + } + + } break; + case CLICK_NODE: { + Point2 new_pos = anim_tree->node_get_pos(click_node)+(click_motion-click_pos); + if (new_pos.x<5) + new_pos.x=5; + if (new_pos.y<5) + new_pos.y=5; + anim_tree->node_set_pos(click_node,new_pos); + + } break; + default: {} + } + + click_type=CLICK_NONE; + update(); + } + } + } + + case InputEvent::MOUSE_MOTION: { + + if (p_event.mouse_motion.button_mask&1 && click_type!=CLICK_NONE) { + + click_motion=Point2(p_event.mouse_button.x,p_event.mouse_button.y); + update(); + } + if ((p_event.mouse_motion.button_mask&4 || Input::get_singleton()->is_key_pressed(KEY_SPACE))) { + + h_scroll->set_val( h_scroll->get_val() - p_event.mouse_motion.relative_x ); + v_scroll->set_val( v_scroll->get_val() - p_event.mouse_motion.relative_y ); + update(); + } + + } break; + } + +} + + +void AnimationTreeEditor::_draw_cos_line(const Vector2& p_from, const Vector2& p_to,const Color& p_color) { + + static const int steps = 20; + + Rect2 r; + r.pos=p_from; + r.expand_to(p_to); + Vector2 sign=Vector2((p_from.x < p_to.x) ? 1 : -1,(p_from.y < p_to.y) ? 1 : -1); + bool flip = sign.x * sign.y < 0; + + Vector2 prev; + for(int i=0;i<=steps;i++) { + + float d = i/float(steps); + float c=-Math::cos(d*Math_PI) * 0.5+0.5; + if (flip) + c=1.0-c; + Vector2 p = r.pos+Vector2(d*r.size.width,c*r.size.height); + + if (i>0) { + + draw_line(prev,p,p_color,2); + } + + prev=p; + } +} + +void AnimationTreeEditor::_notification(int p_what) { + + + switch(p_what) { + + case NOTIFICATION_ENTER_SCENE: { + + play_button->set_icon( get_icon("Play","EditorIcons") ); + add_menu->set_icon( get_icon("Add","EditorIcons") ); + } break; + case NOTIFICATION_DRAW: { + + + _update_scrollbars(); + //VisualServer::get_singleton()->canvas_item_add_rect(get_canvas_item(),Rect2(Point2(),get_size()),Color(0,0,0,1)); + get_stylebox("bg","Tree")->draw(get_canvas_item(),Rect2(Point2(),get_size())); + VisualServer::get_singleton()->canvas_item_set_clip(get_canvas_item(),true); + + for(List<StringName>::Element *E=order.front();E;E=E->next()) { + + _draw_node(E->get()); + } + + if (click_type==CLICK_INPUT_SLOT || click_type==CLICK_OUTPUT_SLOT) { + + _draw_cos_line(click_pos,click_motion,Color(0.5,1,0.5,0.8)); + } + + List<AnimationTreePlayer::Connection> connections; + anim_tree->get_connection_list(&connections); + + for(List<AnimationTreePlayer::Connection>::Element *E=connections.front();E;E=E->next()) { + + const AnimationTreePlayer::Connection &c=E->get(); + Point2 source = _get_slot_pos(c.src_node,false,0); + Point2 dest = _get_slot_pos(c.dst_node,true,c.dst_input); + Color col = Color(1,1,0.5,0.8); +/* + if (click_type==CLICK_NODE && click_node==c.src_node) { + + source+=click_motion-click_pos; + } + + if (click_type==CLICK_NODE && click_node==c.dst_node) { + + dest+=click_motion-click_pos; + }*/ + + _draw_cos_line(source,dest,col); + + } + + switch(anim_tree->get_last_error()) { + + case AnimationTreePlayer::CONNECT_OK: { + + Ref<Font> f = get_font("font","Label"); + f->draw(get_canvas_item(),Point2(5,25+f->get_ascent()),"Animation Tree is Valid.",Color(0,1,0.6,0.8)); + } break; + default: { + + Ref<Font> f = get_font("font","Label"); + f->draw(get_canvas_item(),Point2(5,25+f->get_ascent()),"Animation Tree is Invalid.",Color(1,0.6,0.0,0.8)); + } break; + } + + } break; + } + +} + +void AnimationTreeEditor::_update_scrollbars() { + + Size2 size = get_size(); + Size2 hmin = h_scroll->get_combined_minimum_size(); + Size2 vmin = v_scroll->get_combined_minimum_size(); + + v_scroll->set_begin( Point2(size.width - vmin.width, 0) ); + v_scroll->set_end( Point2(size.width, size.height) ); + + h_scroll->set_begin( Point2( 0, size.height - hmin.height) ); + h_scroll->set_end( Point2(size.width-vmin.width, size.height) ); + + + Size2 min = _get_maximum_size(); + + if (min.height < size.height - hmin.height) { + + v_scroll->hide(); + offset.y=0; + } else { + + v_scroll->show(); + v_scroll->set_max(min.height); + v_scroll->set_page(size.height - hmin.height); + offset.y=v_scroll->get_val(); + } + + if (min.width < size.width - vmin.width) { + + h_scroll->hide(); + offset.x=0; + } else { + + h_scroll->show(); + h_scroll->set_max(min.width); + h_scroll->set_page(size.width - vmin.width); + offset.x=h_scroll->get_val(); + } +} + +void AnimationTreeEditor::_scroll_moved(float) { + + offset.x=h_scroll->get_val(); + offset.y=v_scroll->get_val(); + update(); +} + + +void AnimationTreeEditor::_node_menu_item(int p_item) { + + switch(p_item) { + + case NODE_DISCONNECT: { + + if (rclick_type==CLICK_INPUT_SLOT) { + + anim_tree->disconnect(rclick_node,rclick_slot); + update(); + } + + if (rclick_type==CLICK_OUTPUT_SLOT) { + + + List<AnimationTreePlayer::Connection> connections; + anim_tree->get_connection_list(&connections); + + for(List<AnimationTreePlayer::Connection>::Element *E=connections.front();E;E=E->next()) { + + const AnimationTreePlayer::Connection &c=E->get(); + if( c.dst_node==rclick_node) { + + anim_tree->disconnect(c.dst_node,c.dst_input); + } + } + update(); + } + + } break; + case NODE_RENAME: { + + renaming_edit=true; + edited_node=rclick_node; + _popup_edit_dialog(); + + } break; + case NODE_ADD_INPUT: { + + anim_tree->transition_node_set_input_count(rclick_node, anim_tree->transition_node_get_input_count(rclick_node)+1); + update(); + } break; + case NODE_DELETE_INPUT: { + + anim_tree->transition_node_delete_input(rclick_node, rclick_slot); + update(); + } break; + case NODE_SET_AUTOADVANCE: { + + anim_tree->transition_node_set_input_auto_advance(rclick_node,rclick_slot,true); + update(); + + } break; + case NODE_CLEAR_AUTOADVANCE: { + + anim_tree->transition_node_set_input_auto_advance(rclick_node,rclick_slot,false); + update(); + + } break; + + case NODE_ERASE: { + + if (rclick_node=="out") + break; + order.erase(rclick_node); + anim_tree->remove_node(rclick_node); + update(); + } break; + } +} + +StringName AnimationTreeEditor::_add_node(int p_item) { + + static const char* bname[] = { + "out", + "anim", + "oneshot", + "mix", + "blend2", + "blend3", + "blend4", + "scale", + "seek", + "transition" + }; + + String name; + int idx=1; + + while(true) { + + name = bname[p_item]; + if (idx>1) + name+=" "+itos(idx); + if (anim_tree->node_exists(name)) + idx++; + else + break; + } + + + anim_tree->add_node((AnimationTreePlayer::NodeType)p_item,name); + anim_tree->node_set_pos(name,Point2(last_x,last_y)); + order.push_back(name); + last_x+=10; + last_y+=10; + last_x=last_x % (int)get_size().width; + last_y=last_y % (int)get_size().height; + update(); + + return name; +}; + +void AnimationTreeEditor::_file_dialog_selected(String p_path) { + + switch (file_op) { + + case MENU_IMPORT_ANIMATIONS: { + Vector<String> files = file_dialog->get_selected_files(); + + for (int i=0; i<files.size(); i++) { + + StringName node = _add_node(AnimationTreePlayer::NODE_ANIMATION); + + RES anim = ResourceLoader::load(files[i]); + anim_tree->animation_node_set_animation(node,anim); + //anim_tree->node_set_name(node, files[i].get_file()); + }; + } break; + + default: + break; + }; +}; + +void AnimationTreeEditor::_add_menu_item(int p_item) { + + if (p_item==MENU_GRAPH_CLEAR) { + + //clear + } else if (p_item == MENU_IMPORT_ANIMATIONS) { + + file_op = MENU_IMPORT_ANIMATIONS; + file_dialog->set_mode(FileDialog::MODE_OPEN_FILE); + file_dialog->popup_centered_ratio(); + + } else { + + _add_node(p_item); + } +} + +Size2 AnimationTreeEditor::get_minimum_size() const { + + return Size2(10,200); +} + +void AnimationTreeEditor::_find_paths_for_filter(const StringName& p_node,Set<String>& paths) { + + ERR_FAIL_COND( !anim_tree->node_exists(p_node) ); + + for(int i=0;i<anim_tree->node_get_input_count(p_node);i++) { + + StringName port = anim_tree->node_get_input_source(p_node,i); + if (port==StringName()) + continue; + _find_paths_for_filter(port,paths); + } + + if (anim_tree->node_get_type(p_node)==AnimationTreePlayer::NODE_ANIMATION) { + + Ref<Animation> anim = anim_tree->animation_node_get_animation(p_node); + if (anim.is_valid()) { + + for(int i=0;i<anim->get_track_count();i++) { + paths.insert(anim->track_get_path(i)); + } + } + } +} + + +void AnimationTreeEditor::_filter_edited() { + + + TreeItem *ed = filter->get_edited(); + if (!ed) + return; + + if (anim_tree->node_get_type(edited_node)==AnimationTreePlayer::NODE_ONESHOT) { + anim_tree->oneshot_node_set_filter_path(edited_node,ed->get_metadata(0),ed->is_checked(0)); + } else if (anim_tree->node_get_type(edited_node)==AnimationTreePlayer::NODE_BLEND2) { + anim_tree->blend2_node_set_filter_path(edited_node,ed->get_metadata(0),ed->is_checked(0)); + } + +} + +void AnimationTreeEditor::_edit_filters() { + + filter_dialog->popup_centered_ratio(); + filter->clear(); + + Set<String> npb; + _find_paths_for_filter(edited_node,npb); + + TreeItem *root = filter->create_item(); + filter->set_hide_root(true); + Map<String,TreeItem*> pm; + + Node *base = anim_tree->get_node( anim_tree->get_base_path() ); + + for(Set<String>::Element *E=npb.front();E;E=E->next()) { + + TreeItem *parent=root; + String descr=E->get(); + if (base) { + NodePath np = E->get(); + + if (np.get_property()!=StringName()) { + Node *n = base->get_node(np); + Skeleton *s = n->cast_to<Skeleton>(); + if (s) { + + String skelbase = E->get().substr(0,E->get().find(":")); + + + int bidx = s->find_bone(np.get_property()); + + if (bidx!=-1) { + int bparent = s->get_bone_parent(bidx); + // + if (bparent!=-1) { + + + String bpn = skelbase+":"+s->get_bone_name(bparent); + if (pm.has(bpn)) { + parent=pm[bpn]; + descr=np.get_property(); + } + } else { + + if (pm.has(skelbase)) { + parent=pm[skelbase]; + + } + } + } + } + } + } + + TreeItem *it = filter->create_item(parent); + it->set_cell_mode(0,TreeItem::CELL_MODE_CHECK); + it->set_text(0,descr); + it->set_metadata(0,NodePath(E->get())); + it->set_editable(0,true); + if (anim_tree->node_get_type(edited_node)==AnimationTreePlayer::NODE_ONESHOT) { + it->set_checked(0, anim_tree->oneshot_node_is_path_filtered(edited_node,E->get())); + } else if (anim_tree->node_get_type(edited_node)==AnimationTreePlayer::NODE_BLEND2) { + it->set_checked(0, anim_tree->blend2_node_is_path_filtered(edited_node,E->get())); + } + pm[E->get()]=it; + } + + +} + +void AnimationTreeEditor::_bind_methods() { + + ObjectTypeDB::bind_method( "_add_menu_item", &AnimationTreeEditor::_add_menu_item ); + ObjectTypeDB::bind_method( "_node_menu_item", &AnimationTreeEditor::_node_menu_item ); + ObjectTypeDB::bind_method( "_input_event", &AnimationTreeEditor::_input_event ); +// ObjectTypeDB::bind_method( "_node_param_changed", &AnimationTreeEditor::_node_param_changed ); + ObjectTypeDB::bind_method( "_scroll_moved", &AnimationTreeEditor::_scroll_moved ); + ObjectTypeDB::bind_method( "_edit_dialog_changeds", &AnimationTreeEditor::_edit_dialog_changeds ); + ObjectTypeDB::bind_method( "_edit_dialog_changede", &AnimationTreeEditor::_edit_dialog_changede ); + ObjectTypeDB::bind_method( "_edit_dialog_changedf", &AnimationTreeEditor::_edit_dialog_changedf ); + ObjectTypeDB::bind_method( "_edit_dialog_changed", &AnimationTreeEditor::_edit_dialog_changed ); + ObjectTypeDB::bind_method( "_edit_dialog_animation_changed", &AnimationTreeEditor::_edit_dialog_animation_changed ); + ObjectTypeDB::bind_method( "_edit_dialog_edit_animation", &AnimationTreeEditor::_edit_dialog_edit_animation ); + ObjectTypeDB::bind_method( "_play_toggled", &AnimationTreeEditor::_play_toggled ); + ObjectTypeDB::bind_method( "_edit_oneshot_start", &AnimationTreeEditor::_edit_oneshot_start ); + ObjectTypeDB::bind_method( "_file_dialog_selected", &AnimationTreeEditor::_file_dialog_selected); + ObjectTypeDB::bind_method( "_master_anim_menu_item", &AnimationTreeEditor::_master_anim_menu_item); + ObjectTypeDB::bind_method( "_edit_filters", &AnimationTreeEditor::_edit_filters); + ObjectTypeDB::bind_method( "_filter_edited", &AnimationTreeEditor::_filter_edited); + +} + +AnimationTreeEditor::AnimationTreeEditor() { + + set_focus_mode(FOCUS_ALL); + + PopupMenu *p; + List<PropertyInfo> defaults; + + add_menu = memnew( MenuButton ); + //add_menu->set_ + add_menu->set_pos( Point2( 0,0) ); + add_menu->set_size( Point2( 25,15) ); + add_child( add_menu ); + + p=add_menu->get_popup(); + p->add_item("Animation Node",AnimationTreePlayer::NODE_ANIMATION); + p->add_item("OneShot Node",AnimationTreePlayer::NODE_ONESHOT); + p->add_item("Mix Node",AnimationTreePlayer::NODE_MIX); + p->add_item("Blend2 Node",AnimationTreePlayer::NODE_BLEND2); + p->add_item("Blend3 Node",AnimationTreePlayer::NODE_BLEND3); + p->add_item("Blend4 Node",AnimationTreePlayer::NODE_BLEND4); + p->add_item("TimeScale Node",AnimationTreePlayer::NODE_TIMESCALE); + p->add_item("TimeSeek Node",AnimationTreePlayer::NODE_TIMESEEK); + p->add_item("Transition Node",AnimationTreePlayer::NODE_TRANSITION); + p->add_separator(); + p->add_item("Import Animations...", MENU_IMPORT_ANIMATIONS); // wtf + p->add_separator(); + p->add_item("Clear",MENU_GRAPH_CLEAR); + + p->connect("item_pressed", this,"_add_menu_item"); + + play_button = memnew(Button); + play_button->set_pos(Point2(25,0)); + play_button->set_size(Point2(25,15)); + add_child(play_button); + play_button->set_toggle_mode(true); + play_button->connect("pressed", this,"_play_toggled"); + + + + + + last_x=50; + last_y=50; + + property_editor = memnew( CustomPropertyEditor ); + add_child(property_editor); + property_editor->connect("variant_changed", this,"_edit_dialog_animation_changed"); + property_editor->connect("resource_edit_request", this, "_edit_dialog_edit_animation"); + + h_scroll = memnew( HScrollBar ); + v_scroll = memnew( VScrollBar ); + + add_child(h_scroll); + add_child(v_scroll); + + h_scroll->connect("value_changed", this,"_scroll_moved"); + v_scroll->connect("value_changed", this,"_scroll_moved"); + + node_popup= memnew(PopupMenu ); + add_child(node_popup); + node_popup->set_as_toplevel(true); + + master_anim_popup = memnew( PopupMenu ); + add_child(master_anim_popup); + master_anim_popup->connect("item_pressed",this,"_master_anim_menu_item"); + + + node_popup->connect("item_pressed", this,"_node_menu_item"); + + updating_edit=false; + + edit_dialog = memnew( PopupPanel ); +// edit_dialog->get_ok()->hide(); +// edit_dialog->get_cancel()->hide(); + add_child(edit_dialog); + + edit_option = memnew( OptionButton ); + edit_option->set_anchor( MARGIN_RIGHT, ANCHOR_END ); + edit_option->set_margin(MARGIN_RIGHT, 10); + edit_dialog->add_child(edit_option); + edit_option->connect("item_selected", this,"_edit_dialog_changedf"); + edit_option->hide(); + + + for(int i=0;i<2;i++) { + edit_scroll[i] = memnew ( HSlider ); + edit_scroll[i]->set_anchor( MARGIN_RIGHT, ANCHOR_END ); + edit_scroll[i]->set_margin(MARGIN_RIGHT, 10); + edit_dialog->add_child(edit_scroll[i]); + edit_scroll[i]->hide(); + edit_scroll[i]->connect("value_changed", this,"_edit_dialog_changedf"); + } + for(int i=0;i<4;i++) { + edit_line[i] = memnew ( LineEdit ); + edit_line[i]->set_anchor( MARGIN_RIGHT, ANCHOR_END ); + edit_line[i]->set_margin(MARGIN_RIGHT, 10); + edit_dialog->add_child(edit_line[i]); + edit_line[i]->hide(); + edit_line[i]->connect("text_changed", this,"_edit_dialog_changeds"); + edit_line[i]->connect("text_entered", this,"_edit_dialog_changede"); + edit_label[i] = memnew ( Label ); + edit_dialog->add_child(edit_label[i]); + edit_label[i]->hide(); + } + + edit_button = memnew( Button ); + edit_button->set_anchor( MARGIN_RIGHT, ANCHOR_END ); + edit_button->set_margin(MARGIN_RIGHT, 10); + edit_dialog->add_child(edit_button); + edit_button->hide();; + edit_button->connect("pressed", this,"_edit_oneshot_start"); + + edit_check = memnew( CheckButton ); + edit_check->set_anchor( MARGIN_RIGHT, ANCHOR_END ); + edit_check->set_margin(MARGIN_RIGHT, 10); + edit_dialog->add_child(edit_check); + edit_check->hide();; + edit_check->connect("pressed", this,"_edit_dialog_changed"); + + file_dialog = memnew( FileDialog ); + file_dialog->set_enable_multiple_selection(true); + file_dialog->set_current_dir(Globals::get_singleton()->get_resource_path()); + add_child(file_dialog); + file_dialog->connect("file_selected", this, "_file_dialog_selected"); + + filter_dialog = memnew( AcceptDialog ); + filter_dialog->set_title("Edit Node Filters"); + add_child(filter_dialog); + + filter = memnew( Tree ); + filter_dialog->add_child(filter); + filter_dialog->set_child_rect(filter); + filter->connect("item_edited",this,"_filter_edited"); + + filter_button = memnew( Button ); + filter_button->set_anchor( MARGIN_RIGHT, ANCHOR_END ); + filter_button->set_margin(MARGIN_RIGHT, 10); + edit_dialog->add_child(filter_button); + filter_button->hide();; + filter_button->set_text("Filters.."); + filter_button->connect("pressed", this,"_edit_filters"); + +} + + +void AnimationTreeEditorPlugin::edit(Object *p_object) { + + anim_tree_editor->edit(p_object->cast_to<AnimationTreePlayer>()); + +} + +bool AnimationTreeEditorPlugin::handles(Object *p_object) const { + + return p_object->is_type("AnimationTreePlayer"); +} + +void AnimationTreeEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + editor->hide_animation_player_editors(); + editor->animation_panel_make_visible(true); + anim_tree_editor->show(); + anim_tree_editor->set_fixed_process(true); + EditorNode::get_top_split()->set_collapsed(false); + + } else { + + anim_tree_editor->hide(); + anim_tree_editor->set_fixed_process(false); + editor->animation_panel_make_visible(false); + EditorNode::get_top_split()->set_collapsed(true); + } +} + +AnimationTreeEditorPlugin::AnimationTreeEditorPlugin(EditorNode *p_node) { + + editor=p_node; + anim_tree_editor = memnew( AnimationTreeEditor ); + //editor->get_viewport()->add_child(anim_tree_editor); + //anim_tree_editor->set_area_as_parent_rect(); + editor->get_animation_panel()->add_child(anim_tree_editor); + anim_tree_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL); + anim_tree_editor->hide(); + + + + +} + + +AnimationTreeEditorPlugin::~AnimationTreeEditorPlugin() +{ +} + diff --git a/tools/editor/plugins/animation_tree_editor_plugin.h b/tools/editor/plugins/animation_tree_editor_plugin.h new file mode 100644 index 000000000..21b31863b --- /dev/null +++ b/tools/editor/plugins/animation_tree_editor_plugin.h @@ -0,0 +1,193 @@ +/*************************************************************************/ +/* animation_tree_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef ANIMATION_TREE_EDITOR_PLUGIN_H +#define ANIMATION_TREE_EDITOR_PLUGIN_H + +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" +#include "scene/animation/animation_tree_player.h" +#include "scene/gui/tree.h" +#include "scene/gui/button.h" +#include "scene/gui/popup.h" +#include "tools/editor/property_editor.h" +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + +class AnimationTreeEditor : public Control { + + OBJ_TYPE(AnimationTreeEditor, Control ); + + static const char* _node_type_names[]; + + enum ClickType { + CLICK_NONE, + CLICK_NAME, + CLICK_NODE, + CLICK_INPUT_SLOT, + CLICK_OUTPUT_SLOT, + CLICK_PARAMETER + }; + + enum { + + MENU_GRAPH_CLEAR=100, + MENU_IMPORT_ANIMATIONS=101, + NODE_DISCONNECT, + NODE_RENAME, + NODE_ERASE, + NODE_ADD_INPUT, + NODE_DELETE_INPUT, + NODE_SET_AUTOADVANCE, + NODE_CLEAR_AUTOADVANCE + }; + + bool renaming_edit; + StringName edited_node; + bool updating_edit; + Popup *edit_dialog; + HSlider *edit_scroll[2]; + LineEdit *edit_line[4]; + OptionButton *edit_option; + Label *edit_label[4]; + Button *edit_button; + Button *filter_button; + CheckButton *edit_check; + FileDialog* file_dialog; + int file_op; + + void _popup_edit_dialog(); + + + void _setup_edit_dialog(const StringName& p_node); + PopupMenu *master_anim_popup; + PopupMenu *node_popup; + PopupMenu *add_popup; + HScrollBar *h_scroll; + VScrollBar *v_scroll; + MenuButton* add_menu; + + CustomPropertyEditor *property_editor; + + AnimationTreePlayer* anim_tree; + List<StringName> order; + Set<StringName> active_nodes; + + int last_x,last_y; + + Point2 offset; + ClickType click_type; + Point2 click_pos; + StringName click_node; + int click_slot; + Point2 click_motion; + ClickType rclick_type; + StringName rclick_node; + int rclick_slot; + + Button *play_button; + + Size2 _get_maximum_size(); + Size2 get_node_size(const StringName &p_node) const; + void _draw_node(const StringName& p_node); + + AcceptDialog *filter_dialog; + Tree *filter; + + + + void _draw_cos_line(const Vector2& p_from, const Vector2& p_to,const Color& p_color); + void _update_scrollbars(); + void _scroll_moved(float); + void _play_toggled(); +/* + void _node_param_changed(); + void _node_add_callback(); + void _node_add(VisualServer::AnimationTreeNodeType p_type); + void _node_edit_property(const StringName& p_node); +*/ + + void _master_anim_menu_item(int p_item); + void _node_menu_item(int p_item); + void _add_menu_item(int p_item); + + + void _filter_edited(); + void _find_paths_for_filter(const StringName& p_node,Set<String>& paths); + void _edit_filters(); + + + void _edit_oneshot_start(); + void _edit_dialog_animation_changed(); + void _edit_dialog_edit_animation(); + void _edit_dialog_changeds(String); + void _edit_dialog_changede(String); + void _edit_dialog_changedf(float); + void _edit_dialog_changed(); + void _dialog_changed() const; + ClickType _locate_click(const Point2& p_click,StringName *p_node_id,int *p_slot_index) const; + Point2 _get_slot_pos(const StringName& p_node_id,bool p_input,int p_slot); + + StringName _add_node(int p_item); + void _file_dialog_selected(String p_path); + + +protected: + void _notification(int p_what); + void _input_event(InputEvent p_event); + static void _bind_methods(); +public: + + + virtual Size2 get_minimum_size() const; + void edit(AnimationTreePlayer *p_player); + AnimationTreeEditor(); +}; + +class AnimationTreeEditorPlugin : public EditorPlugin { + + OBJ_TYPE( AnimationTreeEditorPlugin, EditorPlugin ); + + AnimationTreeEditor *anim_tree_editor; + EditorNode *editor; + +public: + + virtual String get_name() const { return "AnimTree"; } + 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); + + AnimationTreeEditorPlugin(EditorNode *p_node); + ~AnimationTreeEditorPlugin(); + +}; + +#endif // ANIMATION_TREE_EDITOR_PLUGIN_H diff --git a/tools/editor/plugins/camera_editor_plugin.cpp b/tools/editor/plugins/camera_editor_plugin.cpp new file mode 100644 index 000000000..aa7562b17 --- /dev/null +++ b/tools/editor/plugins/camera_editor_plugin.cpp @@ -0,0 +1,151 @@ +/*************************************************************************/ +/* camera_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "camera_editor_plugin.h" +#include "spatial_editor_plugin.h" + + +void CameraEditor::_notification(int p_what) { + + switch(p_what) { + +/* case NOTIFICATION_PROCESS: { + + if (preview->is_pressed() && node) + node->call("make_current"); + + } break;*/ + } + +} +void CameraEditor::_node_removed(Node *p_node) { + + if(p_node==node) { + node=NULL; + SpatialEditor::get_singleton()->set_custom_camera(NULL); + hide(); + } + +} + +void CameraEditor::_pressed() { + + Node *sn = (node && preview->is_pressed())?node:NULL; + SpatialEditor::get_singleton()->set_custom_camera(sn); +} + +void CameraEditor::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_pressed"),&CameraEditor::_pressed); + +} + +void CameraEditor::edit(Node *p_camera) { + + node=p_camera; + + if (!node) { + preview->set_pressed(false); + SpatialEditor::get_singleton()->set_custom_camera(NULL); + } else { + + if (preview->is_pressed()) + SpatialEditor::get_singleton()->set_custom_camera(p_camera); + else + SpatialEditor::get_singleton()->set_custom_camera(NULL); + } +} + + +CameraEditor::CameraEditor() { + + preview = memnew( Button ); + add_child(preview); + + preview->set_text("Preview"); + preview->set_toggle_mode(true); + preview->set_anchor(MARGIN_LEFT,Control::ANCHOR_END); + preview->set_anchor(MARGIN_RIGHT,Control::ANCHOR_END); + preview->set_margin(MARGIN_LEFT,60); + preview->set_margin(MARGIN_RIGHT,0); + preview->set_margin(MARGIN_TOP,0); + preview->set_margin(MARGIN_BOTTOM,10); + preview->connect("pressed",this,"_pressed"); + +} + + +void CameraEditorPlugin::edit(Object *p_object) { + + SpatialEditor::get_singleton()->set_can_preview(p_object->cast_to<Camera>()); + //camera_editor->edit(p_object->cast_to<Node>()); +} + +bool CameraEditorPlugin::handles(Object *p_object) const { + + return p_object->is_type("Camera"); +} + +void CameraEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { +// SpatialEditor::get_singleton()->set_can_preview(p_object->cast_to<Camera>()); + + } else { + + SpatialEditor::get_singleton()->set_can_preview(NULL); + } + +} + +CameraEditorPlugin::CameraEditorPlugin(EditorNode *p_node) { + + editor=p_node; +/* camera_editor = memnew( CameraEditor ); + editor->get_viewport()->add_child(camera_editor); + + camera_editor->set_anchor(MARGIN_LEFT,Control::ANCHOR_END); + camera_editor->set_anchor(MARGIN_RIGHT,Control::ANCHOR_END); + camera_editor->set_margin(MARGIN_LEFT,60); + camera_editor->set_margin(MARGIN_RIGHT,0); + camera_editor->set_margin(MARGIN_TOP,0); + camera_editor->set_margin(MARGIN_BOTTOM,10); + + + camera_editor->hide(); +*/ + + +} + + +CameraEditorPlugin::~CameraEditorPlugin() +{ +} + + diff --git a/tools/editor/plugins/camera_editor_plugin.h b/tools/editor/plugins/camera_editor_plugin.h new file mode 100644 index 000000000..5529b32e5 --- /dev/null +++ b/tools/editor/plugins/camera_editor_plugin.h @@ -0,0 +1,79 @@ +/*************************************************************************/ +/* camera_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef CAMERA_EDITOR_PLUGIN_H +#define CAMERA_EDITOR_PLUGIN_H + +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" +#include "scene/3d/camera.h" + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + +class CameraEditor : public Control { + + OBJ_TYPE(CameraEditor, Control ); + + Panel *panel; + Button * preview; + Node *node; + + void _pressed(); +protected: + void _notification(int p_what); + void _node_removed(Node *p_node); + static void _bind_methods(); +public: + + void edit(Node *p_camera); + CameraEditor(); +}; + +class CameraEditorPlugin : public EditorPlugin { + + OBJ_TYPE( CameraEditorPlugin, EditorPlugin ); + +// CameraEditor *camera_editor; + EditorNode *editor; + +public: + + virtual String get_name() const { return "Camera"; } + 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); + + CameraEditorPlugin(EditorNode *p_node); + ~CameraEditorPlugin(); + +}; + +#endif // CAMERA_EDITOR_PLUGIN_H diff --git a/tools/editor/plugins/canvas_item_editor_plugin.cpp b/tools/editor/plugins/canvas_item_editor_plugin.cpp new file mode 100644 index 000000000..514409240 --- /dev/null +++ b/tools/editor/plugins/canvas_item_editor_plugin.cpp @@ -0,0 +1,2308 @@ +/*************************************************************************/ +/* canvas_item_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "canvas_item_editor_plugin.h" +#include "print_string.h" +#include "tools/editor/editor_node.h" +#include "os/keyboard.h" +#include "scene/main/viewport.h" +#include "scene/main/canvas_layer.h" +#include "scene/2d/node_2d.h" +#include "globals.h" +#include "os/input.h" + +void CanvasItemEditor::_unhandled_key_input(const InputEvent& p_ev) { + + if (!is_visible()) + return; + if (p_ev.key.pressed && !p_ev.key.echo && p_ev.key.scancode==KEY_Q) + _tool_select(TOOL_SELECT); + if (p_ev.key.pressed && !p_ev.key.echo && p_ev.key.scancode==KEY_W) + _tool_select(TOOL_MOVE); + if (p_ev.key.pressed && !p_ev.key.echo && p_ev.key.scancode==KEY_E) + _tool_select(TOOL_ROTATE); + if (p_ev.key.pressed && !p_ev.key.echo && p_ev.key.scancode==KEY_V && drag==DRAG_ALL && can_move_pivot) + drag=DRAG_PIVOT; + +} + +void CanvasItemEditor::_tool_select(int p_index) { + + + ToolButton *tb[TOOL_MAX]={select_button,move_button,rotate_button}; + for(int i=0;i<TOOL_MAX;i++) { + + tb[i]->set_pressed(i==p_index); + } + + + viewport->update(); + tool=(Tool)p_index; + +} + +Object *CanvasItemEditor::_get_editor_data(Object *p_what) { + + CanvasItem *ci = p_what->cast_to<CanvasItem>(); + if (!ci) + return NULL; + + return memnew( CanvasItemEditorSelectedItem ); +} + +bool CanvasItemEditor::is_snap_active() const { + + return edit_menu->get_popup()->is_item_checked(edit_menu->get_popup()->get_item_index(SNAP_USE)); +} + +Dictionary CanvasItemEditor::get_state() const { + + Dictionary state; + state["zoom"]=zoom; + state["ofs"]=Point2(h_scroll->get_val(),v_scroll->get_val()); + state["pixel_snap"]=pixel_snap; +// state["ofs"]=-transform.get_origin(); + return state; +} +void CanvasItemEditor::set_state(const Dictionary& p_state){ + + Dictionary state=p_state; + if (state.has("zoom")) { + zoom=p_state["zoom"]; + } + + if (state.has("ofs")) { + _update_scrollbars(); // i wonder how safe is calling this here.. + Point2 ofs=p_state["ofs"]; + h_scroll->set_val(ofs.x); + v_scroll->set_val(ofs.y); + } + + if (state.has("pixel_snap")) { + pixel_snap=state["pixel_snap"]; + int idx = edit_menu->get_popup()->get_item_index(SNAP_USE_PIXEL); + edit_menu->get_popup()->set_item_checked(idx,pixel_snap); + + + } +} + + +void CanvasItemEditor::_add_canvas_item(CanvasItem *p_canvas_item) { + + editor_selection->add_node(p_canvas_item); +#if 0 + if (canvas_items.has(p_canvas_item)) + return; + + canvas_items.insert(p_canvas_item,p_info); + p_canvas_item->connect("hide",this,"_visibility_changed",varray(p_canvas_item->get_instance_ID()),CONNECT_ONESHOT); +#endif +} + +void CanvasItemEditor::_remove_canvas_item(CanvasItem *p_canvas_item) { + + editor_selection->remove_node(p_canvas_item); +#if 0 + p_canvas_item->disconnect("hide",this,"_visibility_changed"); + canvas_items.erase(p_canvas_item); +#endif + +} +void CanvasItemEditor::_clear_canvas_items() { + + editor_selection->clear();; +#if 0 + while(canvas_items.size()) + _remove_canvas_item(canvas_items.front()->key()); +#endif +} + +void CanvasItemEditor::_visibility_changed(ObjectID p_canvas_item) { +#if 0 + Object *c = ObjectDB::get_instance(p_canvas_item); + if (!c) + return; + CanvasItem *ct = c->cast_to<CanvasItem>(); + if (!ct) + return; + canvas_items.erase(ct); + //_remove_canvas_item(ct); + update(); +#endif +} + + +void CanvasItemEditor::_node_removed(Node *p_node) { +#if 0 + CanvasItem *canvas_item = (CanvasItem*)p_node; //not a good cast, but safe + if (canvas_items.has(canvas_item)) + _remove_canvas_item(canvas_item); + + update(); +#endif +} + +void CanvasItemEditor::_keying_changed(bool p_changed) { + + if (p_changed) + animation_menu->show(); + else + animation_menu->hide(); +} + +// slow but modern computers should have no problem +CanvasItem* CanvasItemEditor::_select_canvas_item_at_pos(const Point2& p_pos,Node* p_node,const Matrix32& p_parent_xform,const Matrix32& p_canvas_xform) { + + if (!p_node) + return NULL; + if (p_node->cast_to<Viewport>()) + return NULL; + + CanvasItem *c=p_node->cast_to<CanvasItem>(); + + + for (int i=p_node->get_child_count()-1;i>=0;i--) { + + CanvasItem *r=NULL; + + if (c && !c->is_set_as_toplevel()) + r=_select_canvas_item_at_pos(p_pos,p_node->get_child(i),p_parent_xform * c->get_transform(),p_canvas_xform); + else { + CanvasLayer *cl = p_node->cast_to<CanvasLayer>(); + if (cl) + return NULL; + r=_select_canvas_item_at_pos(p_pos,p_node->get_child(i),transform ,cl ? cl->get_transform() : p_canvas_xform); //use base transform + } + + if (r) + return r; + } + + + if (c && c->is_visible() && !c->has_meta("_edit_lock_")) { + + Rect2 rect = c->get_item_rect(); + Point2 local_pos = (p_parent_xform * p_canvas_xform * c->get_transform()).affine_inverse().xform(p_pos); + + + if (rect.has_point(local_pos)) + return c; + + } + + return NULL; +} + + +void CanvasItemEditor::_find_canvas_items_at_rect(const Rect2& p_rect,Node* p_node,const Matrix32& p_parent_xform,const Matrix32& p_canvas_xform,List<CanvasItem*> *r_items) { + + if (!p_node) + return; + if (p_node->cast_to<Viewport>()) + return; + + CanvasItem *c=p_node->cast_to<CanvasItem>(); + + + for (int i=p_node->get_child_count()-1;i>=0;i--) { + + if (c && !c->is_set_as_toplevel()) + _find_canvas_items_at_rect(p_rect,p_node->get_child(i),p_parent_xform * c->get_transform(),p_canvas_xform,r_items); + else { + CanvasLayer *cl = p_node->cast_to<CanvasLayer>(); + if (cl) + return; + _find_canvas_items_at_rect(p_rect,p_node->get_child(i),transform,cl?cl->get_transform():p_canvas_xform,r_items); + } + } + + + if (c && c->is_visible() && !c->has_meta("_edit_lock_")) { + + Rect2 rect = c->get_item_rect(); + Matrix32 xform = p_parent_xform * p_canvas_xform * c->get_transform(); + + if ( p_rect.has_point( xform.xform( rect.pos ) ) && + p_rect.has_point( xform.xform( rect.pos+Vector2(rect.size.x,0) ) ) && + p_rect.has_point( xform.xform( rect.pos+Vector2(rect.size.x,rect.size.y) ) ) && + p_rect.has_point( xform.xform( rect.pos+Vector2(0,rect.size.y) ) ) ) { + + r_items->push_back(c); + + } + } + + +} + +void CanvasItemEditor::_key_move(const Vector2& p_dir, bool p_snap) { + + + if (drag!=DRAG_NONE) + return; + + if (editor_selection->get_selected_node_list().empty()) + return; + + undo_redo->create_action("Move Action",true); + + List<Node*> &selection = editor_selection->get_selected_node_list(); + + for(List<Node*>::Element *E=selection.front();E;E=E->next()) { + + CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); + if (!canvas_item) + continue; + if (!canvas_item->is_visible()) + continue; + CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); + if (!se) + continue; + + if (canvas_item->has_meta("_edit_lock_")) + continue; + + + Vector2 drag = transform.affine_inverse().basis_xform(p_dir); + drag = canvas_item->get_global_transform_with_canvas().affine_inverse().basis_xform(drag); + + if (p_snap) + drag*=snap; + + undo_redo->add_undo_method(canvas_item,"edit_set_state",canvas_item->edit_get_state()); + Rect2 local_rect = canvas_item->get_item_rect(); + local_rect.pos+=drag; + //canvas_item->edit_set_rect(local_rect); + undo_redo->add_do_method(canvas_item,"edit_set_rect",local_rect); + + } + + undo_redo->commit_action(); +} + +Point2 CanvasItemEditor::_find_topleftmost_point() { + + + + Vector2 tl=Point2(1e10,1e10); + Rect2 r2; + r2.pos=tl; + + + List<Node*> &selection = editor_selection->get_selected_node_list(); + + for(List<Node*>::Element *E=selection.front();E;E=E->next()) { + + CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); + if (!canvas_item) + continue; + if (!canvas_item->is_visible()) + continue; + + + + Rect2 rect=canvas_item->get_item_rect(); + Matrix32 xform=canvas_item->get_global_transform_with_canvas(); + + r2.expand_to(xform.xform(rect.pos)); + r2.expand_to(xform.xform(rect.pos+Vector2(rect.size.x,0))); + r2.expand_to(xform.xform(rect.pos+rect.size)); + r2.expand_to(xform.xform(rect.pos+Vector2(0,rect.size.y))); + + } + + return r2.pos; +} + + + +int CanvasItemEditor::get_item_count() { + + List<Node*> &selection = editor_selection->get_selected_node_list(); + + int ic=0; + for(List<Node*>::Element *E=selection.front();E;E=E->next()) { + + CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); + if (!canvas_item) + continue; + if (!canvas_item->is_visible()) + continue; + + ic++; + }; + + return ic; +} + +CanvasItem *CanvasItemEditor::get_single_item() { + + + Map<Node*,Object*> &selection = editor_selection->get_selection(); + + CanvasItem *single_item=NULL; + + for(Map<Node*,Object*>::Element *E=selection.front();E;E=E->next()) { + + CanvasItem *canvas_item = E->key()->cast_to<CanvasItem>(); + if (!canvas_item) + continue; + if (!canvas_item->is_visible()) + continue; + + if (single_item) + return NULL; //morethan one + + single_item=canvas_item; + }; + + return single_item; +} + +CanvasItemEditor::DragType CanvasItemEditor::_find_drag_type(const Matrix32& p_xform, const Rect2& p_local_rect, const Point2& p_click, Vector2& r_point) { + + CanvasItem *canvas_item = get_single_item(); + + ERR_FAIL_COND_V(!canvas_item,DRAG_NONE); + + Rect2 rect=canvas_item->get_item_rect(); + Matrix32 xforml=canvas_item->get_global_transform_with_canvas(); + Matrix32 xform=transform * xforml; + + Vector2 endpoints[4]={ + + xform.xform(rect.pos), + xform.xform(rect.pos+Vector2(rect.size.x,0)), + xform.xform(rect.pos+rect.size), + xform.xform(rect.pos+Vector2(0,rect.size.y)) + }; + + Vector2 endpointsl[4]={ + + xforml.xform(rect.pos), + xforml.xform(rect.pos+Vector2(rect.size.x,0)), + xforml.xform(rect.pos+rect.size), + xforml.xform(rect.pos+Vector2(0,rect.size.y)) + }; + + DragType dragger[]={ + DRAG_TOP_LEFT, + DRAG_TOP, + DRAG_TOP_RIGHT, + DRAG_RIGHT, + DRAG_BOTTOM_RIGHT, + DRAG_BOTTOM, + DRAG_BOTTOM_LEFT, + DRAG_LEFT + }; + + float radius = (select_handle->get_size().width/2)*1.5; + + //try draggers + + for(int i=0;i<4;i++) { + + int prev = (i+3)%4; + int next = (i+1)%4; + + r_point=endpointsl[i]; + + Vector2 ofs = ((endpoints[i] - endpoints[prev]).normalized() + ((endpoints[i] - endpoints[next]).normalized())).normalized(); + ofs*=1.4144*(select_handle->get_size().width/2); + + ofs+=endpoints[i]; + + if (ofs.distance_to(p_click)<radius) + return dragger[i*2]; + + ofs = (endpoints[i]+endpoints[next])/2; + ofs += (endpoints[next]-endpoints[i]).tangent().normalized()*(select_handle->get_size().width/2); + + r_point=(endpointsl[i]+endpointsl[next])/2; + + + if (ofs.distance_to(p_click)<radius) + return dragger[i*2+1]; + + } + + /* + if (rect.has_point(xform.affine_inverse().xform(p_click))) { + r_point=_find_topleftmost_point(); + return DRAG_ALL; + }*/ + + //try draggers + + return DRAG_NONE; +} + +void CanvasItemEditor::incbeg(float& beg,float &end, float inc, float minsize,bool p_symmetric) { + + if (minsize<0) { + + beg+=inc; + if (p_symmetric) + end-=inc; + } else { + + if (p_symmetric) { + beg+=inc; + end-=inc; + if (end-beg < minsize) { + float center = (beg+end)/2.0; + beg=center-minsize/2.0; + end=center+minsize/2.0; + } + + } else { + if (end-(beg+inc) < minsize) + beg=end-minsize; + else + beg+=inc; + } + + } +} + +void CanvasItemEditor::incend(float &beg,float& end, float inc, float minsize,bool p_symmetric) { + + if (minsize<0) { + + end+=inc; + if (p_symmetric) + beg-=inc; + } else { + + if (p_symmetric) { + + end+=inc; + beg-=inc; + if (end-beg < minsize) { + float center = (beg+end)/2.0; + beg=center-minsize/2.0; + end=center+minsize/2.0; + } + + } else { + if ((end+inc)-beg < minsize) + end=beg+minsize; + else + end+=inc; + } + + } +} + +void CanvasItemEditor::_append_canvas_item(CanvasItem *c) { + + editor_selection->add_node(c); + +} + + +void CanvasItemEditor::_dialog_value_changed(double) { + + if (updating_value_dialog) + return; + + switch(last_option) { + + case SNAP_CONFIGURE: { + + snap=dialog_val->get_val(); + viewport->update(); + } break; + case ZOOM_SET: { + + zoom=dialog_val->get_val()/100.0; + _update_scroll(0); + viewport->update(); + + } break; + default:{} + } +} + +bool CanvasItemEditor::get_remove_list(List<Node*> *p_list) { + + + return false;//!p_list->empty(); +} + + +void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) { + + { + + EditorNode *en = editor; + EditorPlugin *over_plugin = en->get_editor_plugin_over(); + + if (over_plugin) { + bool discard = over_plugin->forward_input_event(p_event); + if (discard) { + accept_event(); + return; + } + } + } + + + if (p_event.type==InputEvent::MOUSE_BUTTON) { + + const InputEventMouseButton &b=p_event.mouse_button; + + + if (b.button_index==BUTTON_WHEEL_DOWN) { + + zoom=zoom*0.95; + _update_scroll(0); + viewport->update(); + return; + } + + if (b.button_index==BUTTON_WHEEL_UP) { + + zoom=zoom*(1.0/0.95); + _update_scroll(0); + viewport->update(); + return; + } + + if (b.button_index==BUTTON_RIGHT) { + + if (get_item_count() > 0 && drag!=DRAG_NONE) { + //cancel drag + + + List<Node*> &selection = editor_selection->get_selected_node_list(); + + for(List<Node*>::Element *E=selection.front();E;E=E->next()) { + + CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); + if (!canvas_item) + continue; + if (!canvas_item->is_visible()) + continue; + + CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); + if (!se) + continue; + + canvas_item->edit_set_state(se->undo_state); + if (canvas_item->cast_to<Node2D>()) + canvas_item->cast_to<Node2D>()->edit_set_pivot(se->undo_pivot); + + } + + } else if (box_selecting) { + box_selecting=false; + viewport->update(); + } else if (b.pressed) { +#if 0 + ref_item = NULL; + Node* scene = get_scene()->get_root_node()->cast_to<EditorNode>()->get_edited_scene(); + if ( scene ) ref_item =_select_canvas_item_at_pos( Point2( b.x, b.y ), scene, transform ); +#endif + //popup->set_pos(Point2(b.x,b.y)); + //popup->popup(); + } + return; + } + //if (!canvas_items.size()) + // return; + + if (b.button_index!=BUTTON_LEFT || Input::get_singleton()->is_key_pressed(KEY_SPACE)) + return; + + if (!b.pressed) { + + if (drag!=DRAG_NONE) { + + if (undo_redo) { + + undo_redo->create_action("Edit CanvasItem"); + + + List<Node*> &selection = editor_selection->get_selected_node_list(); + + for(List<Node*>::Element *E=selection.front();E;E=E->next()) { + + CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); + if (!canvas_item) + continue; + if (!canvas_item->is_visible()) + continue; + CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); + if (!se) + continue; + + Variant state=canvas_item->edit_get_state(); + undo_redo->add_do_method(canvas_item,"edit_set_state",state); + undo_redo->add_undo_method(canvas_item,"edit_set_state",se->undo_state); + if (canvas_item->cast_to<Node2D>()) { + Node2D *pvt = canvas_item->cast_to<Node2D>(); + if (pvt->edit_has_pivot()) { + undo_redo->add_do_method(canvas_item,"edit_set_pivot",pvt->edit_get_pivot()); + undo_redo->add_undo_method(canvas_item,"edit_set_pivot",se->undo_pivot); + } + } + } + undo_redo->commit_action(); + } + + drag=DRAG_NONE; + viewport->update(); + can_move_pivot=false; + + } + + if (box_selecting) { +#if 0 + if ( ! b.mod.shift ) _clear_canvas_items(); + if ( box_selection_end() ) return; +#endif + + Node* scene = editor->get_edited_scene(); + if (scene) { + + List<CanvasItem*> selitems; + + Point2 bsfrom = transform.xform(drag_from); + Point2 bsto= transform.xform(box_selecting_to); + if (bsfrom.x>bsto.x) + SWAP(bsfrom.x,bsto.x); + if (bsfrom.y>bsto.y) + SWAP(bsfrom.y,bsto.y); + + _find_canvas_items_at_rect(Rect2(bsfrom,bsto-bsfrom),scene,transform,Matrix32(),&selitems); + + for(List<CanvasItem*>::Element *E=selitems.front();E;E=E->next()) { + + _append_canvas_item(E->get()); + } + + } + + box_selecting=false; + viewport->update(); + + } + return; + } + + CanvasItem *single_item = get_single_item(); + + if (single_item) { + //try single canvas_item edit + + CanvasItem *canvas_item = single_item; + CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); + ERR_FAIL_COND(!se); + + + Point2 click(b.x,b.y); + + if ((b.mod.control && tool==TOOL_SELECT) || tool==TOOL_ROTATE) { + + drag=DRAG_ROTATE; + drag_from=transform.affine_inverse().xform(click); + se->undo_state=canvas_item->edit_get_state(); + if (canvas_item->cast_to<Node2D>()) + se->undo_pivot=canvas_item->cast_to<Node2D>()->edit_get_pivot(); + return; + } + + Matrix32 xform = transform * canvas_item->get_global_transform_with_canvas(); + Rect2 rect=canvas_item->get_item_rect(); + // float handle_radius = handle_len * 1.4144; //magic number, guess what it means! + + if (tool==TOOL_SELECT) { + drag = _find_drag_type(xform,rect,click,drag_point_from); + + if (b.doubleclick) { + + if (canvas_item->get_filename()!="" && canvas_item!=editor->get_edited_scene()) { + + editor->open_request(canvas_item->get_filename()); + return; + } + } + + if (drag!=DRAG_NONE) { + drag_from=transform.affine_inverse().xform(click); + se->undo_state=canvas_item->edit_get_state(); + if (canvas_item->cast_to<Node2D>()) + se->undo_pivot=canvas_item->cast_to<Node2D>()->edit_get_pivot(); + + return; + } + } else { + + drag=DRAG_NONE; + } + } + + //multi canvas_item edit + + + Point2 click=Point2(b.x,b.y); + + if ((b.mod.alt || tool==TOOL_MOVE) && get_item_count()) { + + + List<Node*> &selection = editor_selection->get_selected_node_list(); + + for(List<Node*>::Element *E=selection.front();E;E=E->next()) { + + CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); + if (!canvas_item) + continue; + if (!canvas_item->is_visible()) + continue; + CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); + if (!se) + continue; + + se->undo_state=canvas_item->edit_get_state(); + if (canvas_item->cast_to<Node2D>()) + se->undo_pivot=canvas_item->cast_to<Node2D>()->edit_get_pivot(); + + } + + + drag=DRAG_ALL; + drag_from=transform.affine_inverse().xform(click); + drag_point_from=_find_topleftmost_point(); + viewport->update(); + return; + + } + + Node* scene = editor->get_edited_scene(); + if (!scene) + return; + + /* + if (current_window) { + //no window.... ? + click-=current_window->get_scroll(); + }*/ + CanvasItem *c=_select_canvas_item_at_pos(click, scene,transform,Matrix32()); + + + CanvasItem* cn = c; + + while(cn) { + if (cn->has_meta("_edit_group_")) { + c=cn; + } + cn=cn->get_parent_item(); + } + + Node* n = c; + + while ((n && n != scene && n->get_owner() != scene) || (n && !n->is_type("CanvasItem"))) { + n = n->get_parent(); + }; + c = n->cast_to<CanvasItem>(); +#if 0 + if ( b.pressed ) box_selection_start( click ); +#endif + if (b.mod.shift) { //additive selection + + if (!c) { + + drag_from=transform.affine_inverse().xform(click); + + box_selecting=true; + box_selecting_to=drag_from; + + return; //nothing to add + } + + if (editor_selection->is_selected(c)) { + //already in here, erase it + editor_selection->remove_node(c); + //_remove_canvas_item(c); + + viewport->update(); + return; + + } + _append_canvas_item(c); + viewport->update(); + } else { + //regular selection + + + + if (!c) { + //clear because nothing clicked + editor_selection->clear();; + + drag_from=transform.affine_inverse().xform(click); + + box_selecting=true; + box_selecting_to=drag_from; + viewport->update(); + return; + } + + if (!editor_selection->is_selected(c)) { + //select a new one and clear previous selection + editor_selection->clear(); + editor_selection->add_node(c); + //reselect + if (get_scene()->is_editor_hint()) { + editor->call("edit_node",c); + } + + } + + //prepare to move! + + List<Node*> &selection = editor_selection->get_selected_node_list(); + + for(List<Node*>::Element *E=selection.front();E;E=E->next()) { + + CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); + if (!canvas_item) + continue; + if (!canvas_item->is_visible()) + continue; + CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); + if (!se) + continue; + + se->undo_state=canvas_item->edit_get_state(); + if (canvas_item->cast_to<Node2D>()) + se->undo_pivot=canvas_item->cast_to<Node2D>()->edit_get_pivot(); + + } + + drag=DRAG_ALL; + drag_from=transform.affine_inverse().xform(click); + drag_point_from=_find_topleftmost_point(); + viewport->update(); + + } + + } + + if (p_event.type==InputEvent::MOUSE_MOTION) { + + if (!viewport->has_focus()) + viewport->call_deferred("grab_focus"); + + const InputEventMouseMotion &m=p_event.mouse_motion; + + if (box_selecting) { + + box_selecting_to=transform.affine_inverse().xform(Point2(m.x,m.y)); + viewport->update(); + return; + + } + + + if (drag==DRAG_NONE) { + + + if (m.button_mask&BUTTON_MASK_MIDDLE || (m.button_mask&BUTTON_MASK_LEFT && Input::get_singleton()->is_key_pressed(KEY_SPACE))) { + + h_scroll->set_val( h_scroll->get_val() - m.relative_x/zoom); + v_scroll->set_val( v_scroll->get_val() - m.relative_y/zoom); + } + + return; + + } + + List<Node*> &selection = editor_selection->get_selected_node_list(); + + for(List<Node*>::Element *E=selection.front();E;E=E->next()) { + + CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); + if (!canvas_item) + continue; + if (!canvas_item->is_visible()) + continue; + CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); + if (!se) + continue; + + canvas_item->edit_set_state(se->undo_state); //reset state and reapply + if (canvas_item->cast_to<Node2D>()) + canvas_item->cast_to<Node2D>()->edit_set_pivot(se->undo_pivot); + + + Vector2 dfrom = drag_from; + Vector2 dto = transform.affine_inverse().xform(Point2(m.x,m.y)); + if (canvas_item->has_meta("_edit_lock_")) + continue; + + + if (drag==DRAG_ROTATE) { + + Vector2 center = canvas_item->get_global_transform_with_canvas().get_origin(); + + Matrix32 rot; + rot.elements[1] = (dfrom - center).normalized(); + rot.elements[0] = rot.elements[1].tangent(); + float ang = rot.xform_inv(dto-center).atan2(); + canvas_item->edit_rotate(ang); + display_rotate_to = dto; + display_rotate_from = center; + + continue; + } + + if (pixel_snap || (is_snap_active() && snap>0)) { + + if (drag!=DRAG_ALL) { + dfrom=drag_point_from; + dto=snapify(dto); + } else { + + Vector2 newpos = drag_point_from + (dto-dfrom); + Vector2 disp; + if (!is_snap_active() || snap<1) { + + disp.x = Math::fposmod(newpos.x,1); + disp.y = Math::fposmod(newpos.y,1); + + } else { + disp.x = Math::fposmod(newpos.x,snap); + disp.y = Math::fposmod(newpos.y,snap); + } + dto-=disp; + } + } + + Vector2 drag_vector = + canvas_item->get_global_transform_with_canvas().affine_inverse().xform(dto) - + canvas_item->get_global_transform_with_canvas().affine_inverse().xform(dfrom); + + + Rect2 local_rect = canvas_item->get_item_rect(); + + if (false && drag!=DRAG_ALL && m.mod.alt) { + float aspect = local_rect.size.get_aspect(); + if (aspect!=0) { + if (ABS(drag_vector.x) > ABS(drag_vector.y)) { + + drag_vector.y = ABS(drag_vector.x)/aspect * SGN(drag_vector.y); + } else { + + drag_vector.x = ABS(drag_vector.y)*aspect * SGN(drag_vector.x); + } + } + } + + + + Vector2 begin=local_rect.pos; + Vector2 end=local_rect.pos+local_rect.size; + Vector2 minsize = canvas_item->edit_get_minimum_size(); + bool symmetric=m.mod.shift; + + + switch(drag) { + case DRAG_ALL: { + begin+=drag_vector; + end+=drag_vector; + } break; + case DRAG_RIGHT: { + + incend(begin.x,end.x,drag_vector.x,minsize.x,symmetric); + + } break; + case DRAG_BOTTOM: { + + incend(begin.y,end.y,drag_vector.y,minsize.y,symmetric); + + } break; + case DRAG_BOTTOM_RIGHT: { + + incend(begin.x,end.x,drag_vector.x,minsize.x,symmetric); + incend(begin.y,end.y,drag_vector.y,minsize.y,symmetric); + } break; + case DRAG_TOP_LEFT: { + incbeg(begin.x,end.x,drag_vector.x,minsize.x,symmetric); + incbeg(begin.y,end.y,drag_vector.y,minsize.y,symmetric); + } break; + case DRAG_TOP: { + + incbeg(begin.y,end.y,drag_vector.y,minsize.y,symmetric); + + } break; + case DRAG_LEFT: { + + incbeg(begin.x,end.x,drag_vector.x,minsize.x,symmetric); + + } break; + case DRAG_TOP_RIGHT: { + + incbeg(begin.y,end.y,drag_vector.y,minsize.y,symmetric); + incend(begin.x,end.x,drag_vector.x,minsize.x,symmetric); + + } break; + case DRAG_BOTTOM_LEFT: { + + incbeg(begin.x,end.x,drag_vector.x,minsize.x,symmetric); + incend(begin.y,end.y,drag_vector.y,minsize.y,symmetric); + } break; + case DRAG_PIVOT: { + + if (canvas_item->cast_to<Node2D>()) { + Node2D *n2d =canvas_item->cast_to<Node2D>(); + n2d->edit_set_pivot(se->undo_pivot+drag_vector); + + } + continue; + } break; + + default:{} + } + + + local_rect.pos=begin; + local_rect.size=end-begin; + canvas_item->edit_set_rect(local_rect); + + } + } + + if (p_event.type==InputEvent::KEY) { + + const InputEventKey &k=p_event.key; + + if (p_event.key.mod.alt || p_event.key.mod.control || p_event.key.mod.meta) + return; + + if (k.pressed && drag==DRAG_NONE) { + + if (k.scancode==KEY_UP) + _key_move(Vector2(0,-1),k.mod.shift); + else if (k.scancode==KEY_DOWN) + _key_move(Vector2(0,1),k.mod.shift); + else if (k.scancode==KEY_LEFT) + _key_move(Vector2(-1,0),k.mod.shift); + else if (k.scancode==KEY_RIGHT) + _key_move(Vector2(1,0),k.mod.shift); + else if (k.scancode==KEY_ESCAPE) { + editor_selection->clear(); + viewport->update(); + } + else + return; + + accept_event(); + } + + } + + + + +} + +void CanvasItemEditor::_viewport_draw() { + + // TODO fetch the viewport? + + Ref<Texture> pivot = get_icon("EditorPivot","EditorIcons"); + _update_scrollbars(); + RID ci=viewport->get_canvas_item(); + + if (snap>0 && is_snap_active() && true ) { + + Size2 s = viewport->get_size(); + + int last_cell; + Matrix32 xform = transform.affine_inverse(); + for(int i=0;i<s.width;i++) { + + int cell = Math::fast_ftoi(Math::floor(xform.xform(Vector2(i,0)).x/snap)); + if (i==0) + last_cell=cell; + if (last_cell!=cell) + viewport->draw_line(Point2(i,0),Point2(i,s.height),Color(0.3,0.7,1,0.3)); + last_cell=cell; + } + + for(int i=0;i<s.height;i++) { + + int cell = Math::fast_ftoi(Math::floor(xform.xform(Vector2(0,i)).y/snap)); + if (i==0) + last_cell=cell; + if (last_cell!=cell) + viewport->draw_line(Point2(0,i),Point2(s.width,i),Color(0.3,0.7,1,0.3)); + last_cell=cell; + } + + } + + if (viewport->has_focus()) { + Size2 size = viewport->get_size(); + if (v_scroll->is_visible()) + size.width-=v_scroll->get_size().width; + if (h_scroll->is_visible()) + size.height-=h_scroll->get_size().height; + + get_stylebox("EditorFocus","EditorStyles")->draw(ci,Rect2(Point2(),size)); + } + + Ref<Texture> lock = get_icon("Lock","EditorIcons"); + Ref<Texture> group = get_icon("Group","EditorIcons"); + + VisualServer::get_singleton()->canvas_item_set_clip(ci,true); + + bool single = get_single_item()!=NULL; + + Map<Node*,Object*> &selection = editor_selection->get_selection(); + + CanvasItem *single_item=NULL; + + for(Map<Node*,Object*>::Element *E=selection.front();E;E=E->next()) { + + + CanvasItem *canvas_item = E->key()->cast_to<CanvasItem>(); + if (!canvas_item) + continue; + if (!canvas_item->is_visible()) + continue; + CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); + if (!se) + continue; + + + Rect2 rect=canvas_item->get_item_rect(); + + Matrix32 xform=transform * canvas_item->get_global_transform_with_canvas(); + VisualServer::get_singleton()->canvas_item_add_set_transform(ci,xform); + Point2 ofs=Point2();//get_global_pos(); + Rect2 draw_rect=rect; + Color light_edit_color=Color(1.0,0.8,0.8); + Color dark_edit_color=Color(0.4,0.1,0.1); + Size2 handle_size=Size2(handle_len,handle_len); + + //select_sb->draw(ci,draw_rect.grow(2)); + //DRAW_EMPTY_RECT( draw_rect.grow(2), light_edit_color ); + //DRAW_EMPTY_RECT( draw_rect.grow(1), dark_edit_color ); + + Vector2 endpoints[4]={ + + xform.xform(rect.pos), + xform.xform(rect.pos+Vector2(rect.size.x,0)), + xform.xform(rect.pos+rect.size), + xform.xform(rect.pos+Vector2(0,rect.size.y)) + }; + + Color c = Color(1,0.6,0.4,0.7); + + VisualServer::get_singleton()->canvas_item_add_set_transform(ci,Matrix32()); + + for(int i=0;i<4;i++) { + viewport->draw_line(endpoints[i],endpoints[(i+1)%4],c,2); + } + + if (single && (tool==TOOL_SELECT || tool == TOOL_MOVE)) { //kind of sucks + + if (canvas_item->cast_to<Node2D>()) { + + + if (canvas_item->cast_to<Node2D>()->edit_has_pivot()) { + viewport->draw_texture(pivot,xform.get_origin()+(-pivot->get_size()/2).floor()); + can_move_pivot=true; + } + + } + + + if (tool==TOOL_SELECT) { + + + for(int i=0;i<4;i++) { + + int prev = (i+3)%4; + int next = (i+1)%4; + + Vector2 ofs = ((endpoints[i] - endpoints[prev]).normalized() + ((endpoints[i] - endpoints[next]).normalized())).normalized(); + ofs*=1.4144*(select_handle->get_size().width/2); + + select_handle->draw(ci,(endpoints[i]+ofs-(select_handle->get_size()/2)).floor()); + + ofs = (endpoints[i]+endpoints[next])/2; + ofs += (endpoints[next]-endpoints[i]).tangent().normalized()*(select_handle->get_size().width/2); + + select_handle->draw(ci,(ofs-(select_handle->get_size()/2)).floor()); + + } + + } + } + + + + //DRAW_EMPTY_RECT( Rect2( current_window->get_scroll()-Point2(1,1), get_size()+Size2(2,2)), Color(0.8,0.8,1.0,0.8) ); + //E->get().last_rect = rect; + } + + VisualServer::get_singleton()->canvas_item_add_set_transform(ci,Matrix32()); + + + + Color x_axis_color(1.0,0.4,0.4,0.6); + Color y_axis_color(0.4,1.0,0.4,0.6); + Color area_axis_color(0.4,0.4,1.0,0.4); + Color rotate_color(0.4,0.7,1.0,0.8); + + VisualServer::get_singleton()->canvas_item_add_line(ci,Point2(h_scroll->get_min(),0)+transform.get_origin(),Point2(h_scroll->get_max(),0)+transform.get_origin(),x_axis_color); + VisualServer::get_singleton()->canvas_item_add_line(ci,Point2(0,v_scroll->get_min())+transform.get_origin(),Point2(0,v_scroll->get_max())+transform.get_origin(),y_axis_color); + + + if (box_selecting) { + + Point2 bsfrom = transform.xform(drag_from); + Point2 bsto= transform.xform(box_selecting_to); + + + VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(bsfrom,bsto-bsfrom),Color(0.7,0.7,1.0,0.3)); + } + + if (drag==DRAG_ROTATE) { + VisualServer::get_singleton()->canvas_item_add_line(ci,transform.xform(display_rotate_from), transform.xform(display_rotate_to),rotate_color); + } + + Size2 screen_size = Size2( Globals::get_singleton()->get("display/width"), Globals::get_singleton()->get("display/height") ); + + Vector2 screen_endpoints[4]= { + transform.xform(Vector2(0,0)), + transform.xform(Vector2(screen_size.width,0)), + transform.xform(Vector2(screen_size.width,screen_size.height)), + transform.xform(Vector2(0,screen_size.height)) + }; + + for(int i=0;i<4;i++) { + + VisualServer::get_singleton()->canvas_item_add_line(ci,screen_endpoints[i], screen_endpoints[(i+1)%4],area_axis_color); + + } + + for(List<LockList>::Element*E=lock_list.front();E;E=E->next()) { + + Vector2 ofs = transform.xform(E->get().pos); + if (E->get().lock) { + + lock->draw(ci,ofs); + ofs.x+=lock->get_width(); + } + if (E->get().group) { + + group->draw(ci,ofs); + } + + } + + +} + +void CanvasItemEditor::_notification(int p_what) { + + if (p_what==NOTIFICATION_FIXED_PROCESS) { + + List<Node*> &selection = editor_selection->get_selected_node_list(); + + for(List<Node*>::Element *E=selection.front();E;E=E->next()) { + + CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); + if (!canvas_item) + continue; + if (!canvas_item->is_visible()) + continue; + + CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); + if (!se) + continue; + + Rect2 r=canvas_item->get_item_rect(); + + Matrix32 xform = canvas_item->get_transform(); + + if (r != se->prev_rect || xform!=se->prev_xform) { + viewport->update(); + se->prev_rect=r; + se->prev_xform=xform; + } + + } + + } + + if (p_what==NOTIFICATION_ENTER_SCENE) { + + select_sb->set_texture( get_icon("EditorRect2D","EditorIcons") ); + for(int i=0;i<4;i++) { + select_sb->set_margin_size(Margin(i),4); + select_sb->set_default_margin(Margin(i),4); + } + + select_button->set_icon( get_icon("ToolSelect","EditorIcons")); + move_button->set_icon( get_icon("ToolMove","EditorIcons")); + rotate_button->set_icon( get_icon("ToolRotate","EditorIcons")); + select_handle=get_icon("EditorHandle","EditorIcons"); + lock_button->set_icon(get_icon("Lock","EditorIcons")); + unlock_button->set_icon(get_icon("Unlock","EditorIcons")); + group_button->set_icon(get_icon("Group","EditorIcons")); + ungroup_button->set_icon(get_icon("Ungroup","EditorIcons")); + + } + + if (p_what==NOTIFICATION_READY) { + + get_scene()->connect("node_removed",this,"_node_removed"); + } + + if (p_what==NOTIFICATION_DRAW) { + + + + } +} + +void CanvasItemEditor::edit(CanvasItem *p_canvas_item) { + + drag=DRAG_NONE; + + editor_selection->clear();//_clear_canvas_items(); + editor_selection->add_node(p_canvas_item); + //_add_canvas_item(p_canvas_item); + viewport->update(); + +} + + +void CanvasItemEditor::_find_canvas_items_span(Node *p_node, Rect2& r_rect, const Matrix32& p_xform) { + + + + if (!p_node) + return; + + CanvasItem *c=p_node->cast_to<CanvasItem>(); + + + for (int i=p_node->get_child_count()-1;i>=0;i--) { + +// CanvasItem *r=NULL; + + if (c && !c->is_set_as_toplevel()) + _find_canvas_items_span(p_node->get_child(i),r_rect,p_xform * c->get_transform()); + else + _find_canvas_items_span(p_node->get_child(i),r_rect,Matrix32()); + } + + + + if (c) { + + Rect2 rect = c->get_item_rect(); + Matrix32 xform = p_xform * c->get_transform(); + + + LockList lock; + lock.lock=c->has_meta("_edit_lock_"); + lock.group=c->has_meta("_edit_group_"); + + if (lock.group || lock.lock) { + lock.pos=xform.xform(rect.pos); + lock_list.push_back(lock); + } + + r_rect.expand_to( xform.xform(rect.pos) ); + r_rect.expand_to( xform.xform(rect.pos+Point2(rect.size.x,0)) ); + r_rect.expand_to( xform.xform(rect.pos+Point2(0,rect.size.y)) ); + r_rect.expand_to( xform.xform(rect.pos+rect.size) ); + + } + +} + +void CanvasItemEditor::_update_scrollbars() { + + + updating_scroll=true; + + Size2 size = viewport->get_size(); + Size2 hmin = h_scroll->get_minimum_size(); + Size2 vmin = v_scroll->get_minimum_size(); + + v_scroll->set_begin( Point2(size.width - vmin.width, 0) ); + v_scroll->set_end( Point2(size.width, size.height) ); + + h_scroll->set_begin( Point2( 0, size.height - hmin.height) ); + h_scroll->set_end( Point2(size.width-vmin.width, size.height) ); + + + Size2 screen_rect = Size2( Globals::get_singleton()->get("display/width"), Globals::get_singleton()->get("display/height") ); + + Rect2 local_rect = Rect2(Point2(),viewport->get_size()-Size2(vmin.width,hmin.height)); + + Rect2 canvas_item_rect=Rect2(Point2(),screen_rect); + + lock_list.clear();; + + if (editor->get_edited_scene()) + _find_canvas_items_span(editor->get_edited_scene(),canvas_item_rect,Matrix32()); + + + //expand area so it's easier to do animations and stuff at 0,0 + canvas_item_rect.size+=screen_rect*2; + canvas_item_rect.pos-=screen_rect; + + Point2 ofs; + + + if (canvas_item_rect.size.height <= (local_rect.size.y/zoom)) { + + v_scroll->hide(); + ofs.y=canvas_item_rect.pos.y; + } else { + + v_scroll->show(); + v_scroll->set_min(canvas_item_rect.pos.y); + v_scroll->set_max(canvas_item_rect.pos.y+canvas_item_rect.size.y); + v_scroll->set_page(local_rect.size.y/zoom); + if (first_update) { + //so 0,0 is visible + v_scroll->set_val(-10); + h_scroll->set_val(-10); + first_update=false; + + } + + ofs.y=v_scroll->get_val(); + } + + if (canvas_item_rect.size.width <= (local_rect.size.x/zoom)) { + + h_scroll->hide(); + ofs.x=canvas_item_rect.pos.x; + } else { + + h_scroll->show(); + h_scroll->set_min(canvas_item_rect.pos.x); + h_scroll->set_max(canvas_item_rect.pos.x+canvas_item_rect.size.x); + h_scroll->set_page(local_rect.size.x/zoom); + ofs.x=h_scroll->get_val(); + } + +// transform=Matrix32(); + transform.elements[2]=-ofs*zoom; + editor->get_scene_root()->set_global_canvas_transform(transform); + + + updating_scroll=false; + +// transform.scale_basis(Vector2(zoom,zoom)); + + +} + +void CanvasItemEditor::_update_scroll(float) { + + + if (updating_scroll) + return; + + Point2 ofs; + ofs.x=h_scroll->get_val(); + ofs.y=v_scroll->get_val(); + +// current_window->set_scroll(-ofs); + + transform=Matrix32(); + + transform.scale_basis(Size2(zoom,zoom)); + transform.elements[2]=-ofs; + + editor->get_scene_root()->set_global_canvas_transform(transform); + + + viewport->update(); + +} + + +Point2 CanvasItemEditor::snapify(const Point2& p_pos) const { + + bool active=is_snap_active(); + + Vector2 pos = p_pos; + + if (!active || snap<1) { + + if (pixel_snap) { + + pos.x=Math::stepify(pos.x,1); + pos.y=Math::stepify(pos.y,1); + } + + return pos; + } + + + pos.x=Math::stepify(pos.x,snap); + pos.y=Math::stepify(pos.y,snap); + return pos; + + +} + + +void CanvasItemEditor::_popup_callback(int p_op) { + + last_option=MenuOption(p_op); + switch(p_op) { + + case SNAP_USE: { + + int idx = edit_menu->get_popup()->get_item_index(SNAP_USE); + edit_menu->get_popup()->set_item_checked( idx,!edit_menu->get_popup()->is_item_checked(0)); + viewport->update(); + } break; + case SNAP_USE_PIXEL: { + pixel_snap = ! pixel_snap; + int idx = edit_menu->get_popup()->get_item_index(SNAP_USE_PIXEL); + edit_menu->get_popup()->set_item_checked(idx,pixel_snap); + } break; + case SNAP_CONFIGURE: { + updating_value_dialog=true; + + dialog_label->set_text("Snap (Pixels):"); + dialog_val->set_min(1); + dialog_val->set_step(1); + dialog_val->set_max(4096); + dialog_val->set_val(snap); + value_dialog->popup_centered(Size2(200,85)); + updating_value_dialog=false; + + } break; + case ZOOM_IN: { + zoom=zoom*(1.0/0.5); + _update_scroll(0); + viewport->update(); + return; + } break; + case ZOOM_OUT: { + zoom=zoom*0.5; + _update_scroll(0); + viewport->update(); + return; + + } break; + case ZOOM_RESET: { + + zoom=1; + _update_scroll(0); + viewport->update(); + return; + + } break; + case ZOOM_SET: { + + updating_value_dialog=true; + + dialog_label->set_text("Zoom (%):"); + dialog_val->set_min(0.1); + dialog_val->set_step(0.1); + dialog_val->set_max(800); + dialog_val->set_val(zoom*100); + value_dialog->popup_centered(Size2(200,85)); + updating_value_dialog=false; + + + } break; + case LOCK_SELECTED: { + + List<Node*> &selection = editor_selection->get_selected_node_list(); + + for(List<Node*>::Element *E=selection.front();E;E=E->next()) { + + CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); + if (!canvas_item) + continue; + if (!canvas_item->is_visible()) + continue; + + canvas_item->set_meta("_edit_lock_",true); + + } + viewport->update(); + } break; + case UNLOCK_SELECTED: { + + List<Node*> &selection = editor_selection->get_selected_node_list(); + + for(List<Node*>::Element *E=selection.front();E;E=E->next()) { + + CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); + if (!canvas_item) + continue; + if (!canvas_item->is_visible()) + continue; + + + canvas_item->set_meta("_edit_lock_",Variant()); + + } + + viewport->update(); + + } break; + case GROUP_SELECTED: { + + List<Node*> &selection = editor_selection->get_selected_node_list(); + + for(List<Node*>::Element *E=selection.front();E;E=E->next()) { + + CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); + if (!canvas_item) + continue; + if (!canvas_item->is_visible()) + continue; + + canvas_item->set_meta("_edit_group_",true); + + } + viewport->update(); + } break; + case UNGROUP_SELECTED: { + + List<Node*> &selection = editor_selection->get_selected_node_list(); + + for(List<Node*>::Element *E=selection.front();E;E=E->next()) { + + CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); + if (!canvas_item) + continue; + if (!canvas_item->is_visible()) + continue; + + + canvas_item->set_meta("_edit_group_",Variant()); + + } + + viewport->update(); + + } break; + + case EXPAND_TO_PARENT: { + + List<Node*> &selection = editor_selection->get_selected_node_list(); + + for(List<Node*>::Element *E=selection.front();E;E=E->next()) { + + CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>(); + if (!canvas_item) + continue; + if (!canvas_item->is_visible()) + continue; + + + Control *c = canvas_item->cast_to<Control>(); + if (!c) + continue; + c->set_area_as_parent_rect(); + + } + + viewport->update(); + + } break; + + case ALIGN_VERTICAL: { +#if 0 + if ( ref_item && canvas_items.size() > 1 ) { + Vector2 ref_pos = ref_item->get_global_transform().elements[2]; + Rect2 ref_r = ref_item->get_item_rect(); + for ( CanvasItemMap::Element *E = canvas_items.front(); E; E = E->next() ) { + CanvasItem *it_curr = E->key(); + if ( it_curr == ref_item ) continue; + Vector2 v = it_curr->get_global_transform().elements[2]; + Rect2 r = it_curr->get_item_rect(); + r.pos.x = ( ref_pos.x + ref_r.size.x / 2 ) - ( v.x + r.size.x / 2 ); + it_curr->edit_set_rect( r ); + } + viewport->update(); + } +#endif + } break; + + case ALIGN_HORIZONTAL: { +#if 0 + if ( ref_item && canvas_items.size() > 1 ) { + Vector2 ref_pos = ref_item->get_global_transform().elements[2]; + Rect2 ref_r = ref_item->get_item_rect(); + for ( CanvasItemMap::Element *E = canvas_items.front(); E; E = E->next() ) { + CanvasItem *it_curr = E->key(); + if ( it_curr == ref_item ) continue; + Vector2 v = it_curr->get_global_transform().elements[2]; + Rect2 r = it_curr->get_item_rect(); + r.pos.y = ( ref_pos.y + ref_r.size.y / 2 ) - ( v.y + r.size.y / 2 ); + it_curr->edit_set_rect( r ); + } + viewport->update(); + } +#endif + } break; + + case SPACE_HORIZONTAL: { + //space_selected_items< proj_vector2_x, compare_items_x >(); + } break; + + case SPACE_VERTICAL: { + //space_selected_items< proj_vector2_y, compare_items_y >(); + } break; + case ANIM_INSERT_KEY: + case ANIM_INSERT_KEY_EXISTING: { + + bool existing = p_op==ANIM_INSERT_KEY_EXISTING; + + Map<Node*,Object*> &selection = editor_selection->get_selection(); + + for(Map<Node*,Object*>::Element *E=selection.front();E;E=E->next()) { + + CanvasItem *canvas_item = E->key()->cast_to<CanvasItem>(); + if (!canvas_item) + continue; + if (!canvas_item->is_visible()) + continue; + + if (canvas_item->cast_to<Node2D>()) { + Node2D *n2d = canvas_item->cast_to<Node2D>(); + + if (key_pos) + editor->get_animation_editor()->insert_node_value_key(n2d,"transform/pos",n2d->get_pos(),existing); + if (key_rot) + editor->get_animation_editor()->insert_node_value_key(n2d,"transform/rot",Math::rad2deg(n2d->get_rot()),existing); + if (key_scale) + editor->get_animation_editor()->insert_node_value_key(n2d,"transform/scale",n2d->get_scale(),existing); + } else if (canvas_item->cast_to<Control>()) { + + Control *ctrl = canvas_item->cast_to<Control>(); + + if (key_pos) + editor->get_animation_editor()->insert_node_value_key(ctrl,"rect/pos",ctrl->get_pos(),existing); + if (key_scale) + editor->get_animation_editor()->insert_node_value_key(ctrl,"rect/size",ctrl->get_size(),existing); + } + + } + + } break; + case ANIM_INSERT_POS: + case ANIM_INSERT_ROT: + case ANIM_INSERT_SCALE: + case ANIM_INSERT_POS_ROT: + case ANIM_INSERT_POS_SCALE: + case ANIM_INSERT_ROT_SCALE: + case ANIM_INSERT_POS_ROT_SCALE: { + + static const bool key_toggles[7][3]={ + {true,false,false}, + {false,true,false}, + {false,false,true}, + {true,true,false}, + {true,false,true}, + {false,true,true}, + {true,true,true} + }; + key_pos=key_toggles[p_op-ANIM_INSERT_POS][0]; + key_rot=key_toggles[p_op-ANIM_INSERT_POS][1]; + key_scale=key_toggles[p_op-ANIM_INSERT_POS][2]; + + for(int i=ANIM_INSERT_POS;i<=ANIM_INSERT_POS_ROT_SCALE;i++) { + int idx = animation_menu->get_popup()->get_item_index(i); + animation_menu->get_popup()->set_item_checked(idx,i==p_op); + } + + } break; + case ANIM_COPY_POSE: { + + pose_clipboard.clear();; + + + Map<Node*,Object*> &selection = editor_selection->get_selection(); + + for(Map<Node*,Object*>::Element *E=selection.front();E;E=E->next()) { + + CanvasItem *canvas_item = E->key()->cast_to<CanvasItem>(); + if (!canvas_item) + continue; + if (!canvas_item->is_visible()) + continue; + + + + if (canvas_item->cast_to<Node2D>()) { + + Node2D *n2d = canvas_item->cast_to<Node2D>(); + PoseClipboard pc; + pc.pos=n2d->get_pos(); + pc.rot=n2d->get_rot(); + pc.scale=n2d->get_scale(); + pc.id=n2d->get_instance_ID(); + pose_clipboard.push_back(pc); + } + } + + + } break; + case ANIM_PASTE_POSE: { + + if (!pose_clipboard.size()) + break; + + undo_redo->create_action("Paste Pose"); + for (List<PoseClipboard>::Element *E=pose_clipboard.front();E;E=E->next()) { + + Object *o = ObjectDB::get_instance(E->get().id); + if (!o) + continue; + Node2D *n2d = o->cast_to<Node2D>(); + if (!n2d) + continue; + undo_redo->add_do_method(n2d,"set_pos",E->get().pos); + undo_redo->add_do_method(n2d,"set_rot",E->get().rot); + undo_redo->add_do_method(n2d,"set_scale",E->get().scale); + undo_redo->add_undo_method(n2d,"set_pos",n2d->get_pos()); + undo_redo->add_undo_method(n2d,"set_rot",n2d->get_rot()); + undo_redo->add_undo_method(n2d,"set_scale",n2d->get_scale()); + } + undo_redo->commit_action(); + + } break; + case ANIM_CLEAR_POSE: { + + Map<Node*,Object*> &selection = editor_selection->get_selection(); + + for(Map<Node*,Object*>::Element *E=selection.front();E;E=E->next()) { + + CanvasItem *canvas_item = E->key()->cast_to<CanvasItem>(); + if (!canvas_item) + continue; + if (!canvas_item->is_visible()) + continue; + + if (canvas_item->cast_to<Node2D>()) { + Node2D *n2d = canvas_item->cast_to<Node2D>(); + + if (key_pos) + n2d->set_pos(Vector2()); + if (key_rot) + n2d->set_rot(0); + if (key_scale) + n2d->set_scale(Vector2(1,1)); + } else if (canvas_item->cast_to<Control>()) { + + Control *ctrl = canvas_item->cast_to<Control>(); + + if (key_pos) + ctrl->set_pos(Point2()); + //if (key_scale) + // editor->get_animation_editor()->insert_node_value_key(ctrl,"rect/size",ctrl->get_size()); + } + + } + + + } break; + + } +} +#if 0 +template< class P, class C > void CanvasItemEditor::space_selected_items() { + P p; + if ( canvas_items.size() > 2 ) { + Vector< CanvasItem * > items; + for ( CanvasItemMap::Element *E = canvas_items.front(); E; E = E->next() ) { + CanvasItem *it_curr = E->key(); + items.push_back( it_curr ); + } + items.sort_custom< C >(); + + float width_s = p.get( items[0]->get_item_rect().size ); + float width_e = p.get( items[ items.size() - 1 ]->get_item_rect().size ); + float start_x = p.get( items[0]->get_global_transform().elements[2] ) + ( width_s / 2 ); + float end_x = p.get( items[ items.size() - 1 ]->get_global_transform().elements[2] ) + ( width_e / 2 ); + float sp = ( end_x - start_x ) / ( items.size() - 1 ); + + for ( int i = 0; i < items.size(); i++ ) { + CanvasItem *it_curr = items[i]; + Vector2 v = it_curr->get_global_transform().elements[2]; + Rect2 r = it_curr->get_item_rect(); + p.set( r.pos, ( start_x + sp * i ) - ( p.get( v ) + p.get( r.size ) / 2 ) ); + it_curr->edit_set_rect( r ); + } + viewport->update(); + } +} +#endif + +void CanvasItemEditor::_bind_methods() { + + ObjectTypeDB::bind_method("_node_removed",&CanvasItemEditor::_node_removed); + ObjectTypeDB::bind_method("_update_scroll",&CanvasItemEditor::_update_scroll); + ObjectTypeDB::bind_method("_popup_callback",&CanvasItemEditor::_popup_callback); + ObjectTypeDB::bind_method("_visibility_changed",&CanvasItemEditor::_visibility_changed); + ObjectTypeDB::bind_method("_dialog_value_changed",&CanvasItemEditor::_dialog_value_changed); + ObjectTypeDB::bind_method("_get_editor_data",&CanvasItemEditor::_get_editor_data); + ObjectTypeDB::bind_method("_tool_select",&CanvasItemEditor::_tool_select); + ObjectTypeDB::bind_method("_keying_changed",&CanvasItemEditor::_keying_changed); + ObjectTypeDB::bind_method("_unhandled_key_input",&CanvasItemEditor::_unhandled_key_input); + ObjectTypeDB::bind_method("_viewport_draw",&CanvasItemEditor::_viewport_draw); + ObjectTypeDB::bind_method("_viewport_input_event",&CanvasItemEditor::_viewport_input_event); + + + +} + +#if 0 +void CanvasItemEditor::end_drag() { + print_line( "end drag" ); + + if (undo_redo) { + + undo_redo->create_action("Edit CanvasItem"); + for(CanvasItemMap::Element *E=canvas_items.front();E;E=E->next()) { + CanvasItem *canvas_item = E->key(); + Variant state=canvas_item->edit_get_state(); + undo_redo->add_do_method(canvas_item,"edit_set_state",state); + undo_redo->add_undo_method(canvas_item,"edit_set_state",E->get().undo_state); + } + undo_redo->commit_action(); + } + + drag=DRAG_NONE; + viewport->update(); +} + +void CanvasItemEditor::box_selection_start( Point2 &click ) { + print_line( "box selection start" ); + + drag_from=transform.affine_inverse().xform(click); + + box_selecting=true; + box_selecting_to=drag_from; + viewport->update(); +} + +bool CanvasItemEditor::box_selection_end() { + print_line( "box selection end" ); + + Node* scene = get_scene()->get_root_node()->cast_to<EditorNode>()->get_edited_scene(); + if (scene) { + + List<CanvasItem*> selitems; + + Point2 bsfrom = transform.xform(drag_from); + Point2 bsto= transform.xform(box_selecting_to); + if (bsfrom.x>bsto.x) + SWAP(bsfrom.x,bsto.x); + if (bsfrom.y>bsto.y) + SWAP(bsfrom.y,bsto.y); + + if ( bsfrom.distance_to( bsto ) < 3 ) { + print_line( "box selection too small" ); + box_selecting=false; + viewport->update(); + return false; + } + + _find_canvas_items_at_rect(Rect2(bsfrom,bsto-bsfrom),scene,transform,&selitems); + + for(List<CanvasItem*>::Element *E=selitems.front();E;E=E->next()) { + + _append_canvas_item(E->get()); + } + + } + + box_selecting=false; + viewport->update(); + + return true; +} +#endif + +void CanvasItemEditor::add_control_to_menu_panel(Control *p_control) { + + hb->add_child(p_control); +} + +CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { + + tool = TOOL_SELECT; + undo_redo=p_editor->get_undo_redo(); + editor=p_editor; + editor_selection=p_editor->get_editor_selection(); + editor_selection->add_editor_plugin(this); + editor_selection->connect("selection_changed",this,"update"); + + + hb = memnew( HBoxContainer ); + add_child( hb ); + hb->set_area_as_parent_rect(); + + Control *vp_base = memnew (Control); + add_child(vp_base); + vp_base->set_v_size_flags(SIZE_EXPAND_FILL); + + Control *vp = memnew (Control); + vp_base->add_child(vp); + vp->set_area_as_parent_rect(); + vp->add_child(p_editor->get_scene_root()); + + viewport = memnew( Control ); + vp_base->add_child(viewport); + viewport->set_area_as_parent_rect(); + + h_scroll = memnew( HScrollBar ); + v_scroll = memnew( VScrollBar ); + + viewport->add_child(h_scroll); + viewport->add_child(v_scroll); + viewport->connect("draw",this,"_viewport_draw"); + viewport->connect("input_event",this,"_viewport_input_event"); + + + h_scroll->connect("value_changed", this,"_update_scroll",Vector<Variant>(),true); + v_scroll->connect("value_changed", this,"_update_scroll",Vector<Variant>(),true); + + h_scroll->hide(); + v_scroll->hide(); + updating_scroll=false; + viewport->set_focus_mode(FOCUS_ALL); + handle_len=10; + first_update=true; + + + select_button = memnew( ToolButton ); + select_button->set_toggle_mode(true); + hb->add_child(select_button); + select_button->connect("pressed",this,"_tool_select",make_binds(TOOL_SELECT)); + select_button->set_pressed(true); + select_button->set_tooltip("Select Mode (Q)\n"+keycode_get_string(KEY_MASK_CMD)+"Drag: Rotate\nAlt+Drag: Move\nPress 'v' to Move Pivot (while moving)"); + + move_button = memnew( ToolButton ); + move_button->set_toggle_mode(true); + hb->add_child(move_button); + move_button->connect("pressed",this,"_tool_select",make_binds(TOOL_MOVE)); + move_button->set_tooltip("Move Mode (W)"); + + rotate_button = memnew( ToolButton ); + rotate_button->set_toggle_mode(true); + hb->add_child(rotate_button); + rotate_button->connect("pressed",this,"_tool_select",make_binds(TOOL_ROTATE)); + rotate_button->set_tooltip("Rotate Mode (E)"); + + hb->add_child(memnew(VSeparator)); + + lock_button = memnew( ToolButton ); + hb->add_child(lock_button); + + lock_button->connect("pressed",this,"_popup_callback",varray(LOCK_SELECTED)); + + unlock_button = memnew( ToolButton ); + hb->add_child(unlock_button); + unlock_button->connect("pressed",this,"_popup_callback",varray(UNLOCK_SELECTED)); + + group_button = memnew( ToolButton ); + hb->add_child(group_button); + group_button->connect("pressed",this,"_popup_callback",varray(GROUP_SELECTED)); + + ungroup_button = memnew( ToolButton ); + hb->add_child(ungroup_button); + ungroup_button->connect("pressed",this,"_popup_callback",varray(UNGROUP_SELECTED)); + + hb->add_child(memnew(VSeparator)); + + edit_menu = memnew( MenuButton ); + edit_menu->set_text("Edit"); + hb->add_child(edit_menu); + edit_menu->get_popup()->connect("item_pressed", this,"_popup_callback"); + + PopupMenu *p; + p = edit_menu->get_popup(); + p->add_check_item("Use Snap",SNAP_USE); + p->add_item("Configure Snap..",SNAP_CONFIGURE); + p->add_separator(); + p->add_check_item("Use Pixel Snap",SNAP_USE_PIXEL); + p->add_separator(); + p->add_item("Expand to Parent",EXPAND_TO_PARENT,KEY_MASK_CMD|KEY_P); + + /* + p->add_item("Align Horizontal",ALIGN_HORIZONTAL); + p->add_item("Align Vertical",ALIGN_VERTICAL); + p->add_item("Space Horizontal",SPACE_HORIZONTAL); + p->add_item("Space Vertical",SPACE_VERTICAL);*/ + + view_menu = memnew( MenuButton ); + view_menu->set_text("View"); + hb->add_child(view_menu); + view_menu->get_popup()->connect("item_pressed", this,"_popup_callback"); + + p = view_menu->get_popup(); + + p->add_item("Zoom In",ZOOM_IN); + p->add_item("Zoom Out",ZOOM_OUT); + p->add_item("Zoom Reset",ZOOM_RESET); + p->add_item("Zoom Set..",ZOOM_SET); + + animation_menu = memnew( MenuButton ); + animation_menu->set_text("Animation"); + hb->add_child(animation_menu); + animation_menu->get_popup()->connect("item_pressed", this,"_popup_callback"); + + p = animation_menu->get_popup(); + + p->add_item("Insert Key",ANIM_INSERT_KEY,KEY_INSERT); + p->add_item("Insert Key (Existing Tracks)",ANIM_INSERT_KEY_EXISTING,KEY_MASK_CMD+KEY_INSERT); + p->add_separator(); + p->add_check_item("Pos",ANIM_INSERT_POS); + p->add_check_item("Rot",ANIM_INSERT_ROT); + p->add_check_item("Scale",ANIM_INSERT_SCALE); + p->add_check_item("Pos+Rot",ANIM_INSERT_POS_ROT); + p->set_item_checked(p->get_item_index(ANIM_INSERT_POS_ROT),true); + p->add_check_item("Pos+Scale",ANIM_INSERT_POS_SCALE); + p->add_check_item("Rot+Scale",ANIM_INSERT_ROT_SCALE); + p->add_check_item("Loc+Rot+Scale",ANIM_INSERT_POS_ROT_SCALE); + p->add_separator(); + p->add_item("Copy Pose",ANIM_COPY_POSE); + p->add_item("Paste Pose",ANIM_PASTE_POSE); + p->add_item("Clear Pose",ANIM_CLEAR_POSE,KEY_MASK_ALT|KEY_K); + + animation_menu->hide(); + + + value_dialog = memnew( AcceptDialog ); + value_dialog->set_title("Set a Value"); + value_dialog->get_ok()->set_text("Close"); + add_child(value_dialog); + + Label *l = memnew(Label); + l->set_text("Snap (Pixels):"); + l->set_pos(Point2(5,5)); + value_dialog->add_child(l); + dialog_label=l; + + dialog_val=memnew(SpinBox); + dialog_val->set_anchor(MARGIN_RIGHT,ANCHOR_END); + dialog_val->set_begin(Point2(15,25)); + dialog_val->set_end(Point2(10,25)); + value_dialog->add_child(dialog_val); + dialog_val->connect("value_changed",this,"_dialog_value_changed"); + select_sb = Ref<StyleBoxTexture>( memnew( StyleBoxTexture) ); + + key_pos=true; + key_rot=true; + key_scale=false; + + zoom=1; + snap=10; + updating_value_dialog=false; + box_selecting=false; + //zoom=0.5; + singleton=this; + editor->get_animation_editor()->connect("keying_changed",this,"_keying_changed"); + add_to_group("unhandled_key_input"); + can_move_pivot=false; + pixel_snap=false; + drag=DRAG_NONE; +} + +CanvasItemEditor *CanvasItemEditor::singleton=NULL; + +void CanvasItemEditorPlugin::edit(Object *p_object) { + + canvas_item_editor->set_undo_redo(&get_undo_redo()); + canvas_item_editor->edit(p_object->cast_to<CanvasItem>()); +} + +bool CanvasItemEditorPlugin::handles(Object *p_object) const { + + return p_object->is_type("CanvasItem"); +} + +void CanvasItemEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + canvas_item_editor->show(); + canvas_item_editor->set_fixed_process(true); + VisualServer::get_singleton()->viewport_set_hide_canvas(editor->get_scene_root()->get_viewport(),false); + canvas_item_editor->viewport->grab_focus(); + + } else { + + canvas_item_editor->hide(); + canvas_item_editor->set_fixed_process(false); + VisualServer::get_singleton()->viewport_set_hide_canvas(editor->get_scene_root()->get_viewport(),true); + } + +} + +Dictionary CanvasItemEditorPlugin::get_state() const { + + return canvas_item_editor->get_state(); +} +void CanvasItemEditorPlugin::set_state(const Dictionary& p_state) { + + canvas_item_editor->set_state(p_state); +} + +CanvasItemEditorPlugin::CanvasItemEditorPlugin(EditorNode *p_node) { + + editor=p_node; + canvas_item_editor = memnew( CanvasItemEditor(editor) ); + editor->get_viewport()->add_child(canvas_item_editor); + canvas_item_editor->set_area_as_parent_rect(); + canvas_item_editor->hide(); + +} + + +CanvasItemEditorPlugin::~CanvasItemEditorPlugin() +{ +} + + diff --git a/tools/editor/plugins/canvas_item_editor_plugin.h b/tools/editor/plugins/canvas_item_editor_plugin.h new file mode 100644 index 000000000..64c5d523c --- /dev/null +++ b/tools/editor/plugins/canvas_item_editor_plugin.h @@ -0,0 +1,339 @@ +/*************************************************************************/ +/* canvas_item_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef CONTROL_EDITOR_PLUGIN_H +#define CONTROL_EDITOR_PLUGIN_H + +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" +#include "scene/gui/spin_box.h" +#include "scene/gui/panel_container.h" +#include "scene/gui/box_container.h" +#include "scene/2d/canvas_item.h" + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + + + + +class CanvasItemEditorSelectedItem : public Object { + + OBJ_TYPE(CanvasItemEditorSelectedItem,Object); +public: + Variant undo_state; + Vector2 undo_pivot; + + Matrix32 prev_xform; + float prev_rot; + Rect2 prev_rect; + + CanvasItemEditorSelectedItem() { prev_rot=0; } +}; + +class CanvasItemEditor : public VBoxContainer { + + OBJ_TYPE(CanvasItemEditor, VBoxContainer ); + + EditorNode *editor; + + + enum Tool { + + TOOL_SELECT, + TOOL_MOVE, + TOOL_ROTATE, + TOOL_MAX + }; + + enum MenuOption { + SNAP_USE, + SNAP_CONFIGURE, + SNAP_USE_PIXEL, + ZOOM_IN, + ZOOM_OUT, + ZOOM_RESET, + ZOOM_SET, + LOCK_SELECTED, + UNLOCK_SELECTED, + GROUP_SELECTED, + UNGROUP_SELECTED, + ALIGN_HORIZONTAL, + ALIGN_VERTICAL, + SPACE_HORIZONTAL, + SPACE_VERTICAL, + EXPAND_TO_PARENT, + ANIM_INSERT_KEY, + ANIM_INSERT_KEY_EXISTING, + ANIM_INSERT_POS, + ANIM_INSERT_ROT, + ANIM_INSERT_SCALE, + ANIM_INSERT_POS_ROT, + ANIM_INSERT_POS_SCALE, + ANIM_INSERT_ROT_SCALE, + ANIM_INSERT_POS_ROT_SCALE, + ANIM_COPY_POSE, + ANIM_PASTE_POSE, + ANIM_CLEAR_POSE + + }; + + enum DragType { + DRAG_NONE, + DRAG_LEFT, + DRAG_TOP_LEFT, + DRAG_TOP, + DRAG_TOP_RIGHT, + DRAG_RIGHT, + DRAG_BOTTOM_RIGHT, + DRAG_BOTTOM, + DRAG_BOTTOM_LEFT, + DRAG_ALL, + DRAG_ROTATE, + DRAG_PIVOT, + + }; + + EditorSelection *editor_selection; + + Tool tool; + bool first_update; + Control *viewport; + + bool can_move_pivot; + + HScrollBar *h_scroll; + VScrollBar *v_scroll; + HBoxContainer *hb; + + Matrix32 transform; + float zoom; + int snap; + bool pixel_snap; + bool box_selecting; + Point2 box_selecting_to; + bool key_pos; + bool key_rot; + bool key_scale; + + void _tool_select(int p_index); + + + MenuOption last_option; + + struct LockList { + Point2 pos; + bool lock; + bool group; + LockList() { lock=false; group=false; } + }; + + List<LockList> lock_list; + + struct PoseClipboard { + + Vector2 pos; + Vector2 scale; + float rot; + ObjectID id; + }; + + List<PoseClipboard> pose_clipboard; + + ToolButton *select_button; + ToolButton *move_button; + ToolButton *rotate_button; + + ToolButton *lock_button; + ToolButton *unlock_button; + + ToolButton *group_button; + ToolButton *ungroup_button; + + MenuButton *edit_menu; + MenuButton *view_menu; + MenuButton *animation_menu; + //PopupMenu *popup; + DragType drag; + Point2 drag_from; + Point2 drag_point_from; + bool updating_value_dialog; + Point2 display_rotate_from; + Point2 display_rotate_to; +#if 0 + struct EditInfo { + + Variant undo_state; + + Matrix32 prev_xform; + float prev_rot; + Rect2 prev_rect; + EditInfo() { prev_rot=0; } + }; + + typedef Map<CanvasItem*,EditInfo> CanvasItemMap; + CanvasItemMap canvas_items; +#endif + Ref<StyleBoxTexture> select_sb; + Ref<Texture> select_handle; + + + int handle_len; + CanvasItem* _select_canvas_item_at_pos(const Point2 &p_pos,Node* p_node,const Matrix32& p_parent_xform,const Matrix32& p_canvas_xform); + void _find_canvas_items_at_rect(const Rect2& p_rect,Node* p_node,const Matrix32& p_parent_xform,const Matrix32& p_canvas_xform,List<CanvasItem*> *r_items); + + AcceptDialog *value_dialog; + Label *dialog_label; + SpinBox *dialog_val; + + CanvasItem *ref_item; + + void _add_canvas_item(CanvasItem *p_canvas_item); + void _remove_canvas_item(CanvasItem *p_canvas_item); + void _clear_canvas_items(); + void _visibility_changed(ObjectID p_canvas_item); + void _key_move(const Vector2& p_dir, bool p_snap); + + DragType _find_drag_type(const Matrix32& p_xform, const Rect2& p_local_rect, const Point2& p_click, Vector2& r_point); + + Point2 snapify(const Point2& p_pos) const; + void _popup_callback(int p_op); + bool updating_scroll; + void _update_scroll(float); + void _update_scrollbars(); + void incbeg(float& beg,float& end, float inc, float minsize,bool p_symmetric); + void incend(float& beg,float& end, float inc, float minsize,bool p_symmetric); + + void _append_canvas_item(CanvasItem *p_item); + void _dialog_value_changed(double); + UndoRedo *undo_redo; + + Point2 _find_topleftmost_point(); + + + void _find_canvas_items_span(Node *p_node, Rect2& r_rect, const Matrix32& p_xform); + + + Object *_get_editor_data(Object *p_what); + + CanvasItem *get_single_item(); + int get_item_count(); + void _keying_changed(bool p_changed); + + void _unhandled_key_input(const InputEvent& p_ev); + + void _viewport_input_event(const InputEvent& p_event); + void _viewport_draw(); +friend class CanvasItemEditorPlugin; +protected: + + + void _notification(int p_what); + + void _node_removed(Node *p_node); + static void _bind_methods(); + void end_drag(); + void box_selection_start( Point2 &click ); + bool box_selection_end(); + + HBoxContainer *get_panel_hb() { return hb; } + + struct compare_items_x { + bool operator()( const CanvasItem *a, const CanvasItem *b ) const { + return a->get_global_transform().elements[2].x < b->get_global_transform().elements[2].x; + } + }; + + struct compare_items_y { + bool operator()( const CanvasItem *a, const CanvasItem *b ) const { + return a->get_global_transform().elements[2].y < b->get_global_transform().elements[2].y; + } + }; + + struct proj_vector2_x { + float get( const Vector2 &v ) { return v.x; } + void set( Vector2 &v, float f ) { v.x = f; } + }; + + struct proj_vector2_y { + float get( const Vector2 &v ) { return v.y; } + void set( Vector2 &v, float f ) { v.y = f; } + }; + + template< class P, class C > void space_selected_items(); + + static CanvasItemEditor *singleton; +public: + + bool is_snap_active() const; + int get_snap() const { return snap; } + + Matrix32 get_canvas_transform() const { return transform; } + + static CanvasItemEditor *get_singleton() { return singleton; } + Dictionary get_state() const; + void set_state(const Dictionary& p_state); + + void add_control_to_menu_panel(Control *p_control); + + Control *get_viewport_control() { return viewport; } + + + bool get_remove_list(List<Node*> *p_list); + void set_undo_redo(UndoRedo *p_undo_redo) {undo_redo=p_undo_redo; } + void edit(CanvasItem *p_canvas_item); + CanvasItemEditor(EditorNode *p_editor); +}; + +class CanvasItemEditorPlugin : public EditorPlugin { + + OBJ_TYPE( CanvasItemEditorPlugin, EditorPlugin ); + + CanvasItemEditor *canvas_item_editor; + EditorNode *editor; + +public: + + virtual String get_name() const { return "2D"; } + bool has_main_screen() const { return true; } + virtual void edit(Object *p_object); + virtual bool handles(Object *p_object) const; + virtual void make_visible(bool p_visible); + virtual bool get_remove_list(List<Node*> *p_list) { return canvas_item_editor->get_remove_list(p_list); } + virtual Dictionary get_state() const; + virtual void set_state(const Dictionary& p_state); + + CanvasItemEditor *get_canvas_item_editor() { return canvas_item_editor; } + + CanvasItemEditorPlugin(EditorNode *p_node); + ~CanvasItemEditorPlugin(); + +}; + +#endif diff --git a/tools/editor/plugins/collision_polygon_editor_plugin.cpp b/tools/editor/plugins/collision_polygon_editor_plugin.cpp new file mode 100644 index 000000000..e468ae573 --- /dev/null +++ b/tools/editor/plugins/collision_polygon_editor_plugin.cpp @@ -0,0 +1,479 @@ +/*************************************************************************/ +/* collision_polygon_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "collision_polygon_editor_plugin.h" +#include "canvas_item_editor_plugin.h" +#include "os/file_access.h" +#include "tools/editor/editor_settings.h" + +void CollisionPolygonEditor::_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); + + + } break; + case NOTIFICATION_FIXED_PROCESS: { + + + } break; + } + +} +void CollisionPolygonEditor::_node_removed(Node *p_node) { + + if(p_node==node) { + node=NULL; + hide(); + } + +} + + +Vector2 CollisionPolygonEditor::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 CollisionPolygonEditor::_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 CollisionPolygonEditor::_wip_close() { + + undo_redo->create_action("Create Poly"); + undo_redo->add_undo_method(node,"set_polygon",node->get_polygon()); + undo_redo->add_do_method(node,"set_polygon",wip); + undo_redo->add_do_method(canvas_item_editor,"update"); + undo_redo->add_undo_method(canvas_item_editor,"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 CollisionPolygonEditor::forward_input_event(const InputEvent& p_event) { + + 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 = xform.affine_inverse().xform(gpoint); + + Vector<Vector2> poly = node->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( snap_point(cpoint) ); + wip_active=true; + edited_point_pos=snap_point(cpoint); + canvas_item_editor->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( snap_point(cpoint) ); + edited_point=wip.size(); + canvas_item_editor->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) { + + + if (poly.size() < 3) { + + undo_redo->create_action("Edit Poly"); + undo_redo->add_undo_method(node,"set_polygon",poly); + poly.push_back(cpoint); + undo_redo->add_do_method(node,"set_polygon",poly); + undo_redo->add_do_method(canvas_item_editor,"update"); + undo_redo->add_undo_method(canvas_item_editor,"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,snap_point(xform.affine_inverse().xform(closest_pos))); + edited_point=closest_idx+1; + edited_point_pos=snap_point(xform.affine_inverse().xform(closest_pos)); + node->set_polygon(poly); + canvas_item_editor->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->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,"set_polygon",poly); + undo_redo->add_undo_method(node,"set_polygon",pre_move_edit); + undo_redo->add_do_method(canvas_item_editor,"update"); + undo_redo->add_undo_method(canvas_item_editor,"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,"set_polygon",poly); + poly.remove(closest_idx); + undo_redo->add_do_method(node,"set_polygon",poly); + undo_redo->add_do_method(canvas_item_editor,"update"); + undo_redo->add_undo_method(canvas_item_editor,"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)) { + + + Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); + + Vector2 gpoint = Point2(mm.x,mm.y); + edited_point_pos = snap_point(xform.affine_inverse().xform(gpoint)); + canvas_item_editor->update(); + + } + + } break; + } + + return false; +} +void CollisionPolygonEditor::_canvas_draw() { + + if (!node) + return; + + + Vector<Vector2> poly; + + if (wip_active) + poly=wip; + else + poly=node->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); + canvas_item_editor->draw_line(point,next_point,col,2); + canvas_item_editor->draw_texture(handle,point-handle->get_size()*0.5); + } +} + + + +void CollisionPolygonEditor::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<CollisionPolygon2D>(); + if (!canvas_item_editor->is_connected("draw",this,"_canvas_draw")) + canvas_item_editor->connect("draw",this,"_canvas_draw"); + wip.clear(); + wip_active=false; + edited_point=-1; + + } else { + node=NULL; + + if (canvas_item_editor->is_connected("draw",this,"_canvas_draw")) + canvas_item_editor->disconnect("draw",this,"_canvas_draw"); + + } + +} + +void CollisionPolygonEditor::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_menu_option"),&CollisionPolygonEditor::_menu_option); + ObjectTypeDB::bind_method(_MD("_canvas_draw"),&CollisionPolygonEditor::_canvas_draw); + +} + +CollisionPolygonEditor::CollisionPolygonEditor(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_edit = memnew( ToolButton ); + add_child(button_edit); + button_edit->connect("pressed",this,"_menu_option",varray(MODE_EDIT)); + button_edit->set_toggle_mode(true); + + //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 CollisionPolygonEditorPlugin::edit(Object *p_object) { + + collision_polygon_editor->edit(p_object->cast_to<Node>()); +} + +bool CollisionPolygonEditorPlugin::handles(Object *p_object) const { + + return p_object->is_type("CollisionPolygon2D"); +} + +void CollisionPolygonEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + collision_polygon_editor->show(); + } else { + + collision_polygon_editor->hide(); + collision_polygon_editor->edit(NULL); + } + +} + +CollisionPolygonEditorPlugin::CollisionPolygonEditorPlugin(EditorNode *p_node) { + + editor=p_node; + collision_polygon_editor = memnew( CollisionPolygonEditor(p_node) ); + CanvasItemEditor::get_singleton()->add_control_to_menu_panel(collision_polygon_editor); + + collision_polygon_editor->hide(); + + + +} + + +CollisionPolygonEditorPlugin::~CollisionPolygonEditorPlugin() +{ +} + diff --git a/tools/editor/plugins/collision_polygon_editor_plugin.h b/tools/editor/plugins/collision_polygon_editor_plugin.h new file mode 100644 index 000000000..a05ac0f8c --- /dev/null +++ b/tools/editor/plugins/collision_polygon_editor_plugin.h @@ -0,0 +1,111 @@ +/*************************************************************************/ +/* collision_polygon_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef COLLISION_POLYGON_EDITOR_PLUGIN_H +#define COLLISION_POLYGON_EDITOR_PLUGIN_H + +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" +#include "scene/2d/collision_polygon_2d.h" +#include "scene/gui/tool_button.h" +#include "scene/gui/button_group.h" + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ +class CanvasItemEditor; + +class CollisionPolygonEditor : public HBoxContainer { + + OBJ_TYPE(CollisionPolygonEditor, 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; + CollisionPolygon2D *node; + MenuButton *options; + + int edited_point; + Vector2 edited_point_pos; + Vector<Vector2> pre_move_edit; + Vector<Vector2> wip; + bool wip_active; + + + void _wip_close(); + void _canvas_draw(); + 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); + CollisionPolygonEditor(EditorNode *p_editor); +}; + +class CollisionPolygonEditorPlugin : public EditorPlugin { + + OBJ_TYPE( CollisionPolygonEditorPlugin, EditorPlugin ); + + CollisionPolygonEditor *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 "CollisionPolygon"; } + 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); + + CollisionPolygonEditorPlugin(EditorNode *p_node); + ~CollisionPolygonEditorPlugin(); + +}; + +#endif // COLLISION_POLYGON_EDITOR_PLUGIN_H diff --git a/tools/editor/plugins/control_editor_plugin.cpp b/tools/editor/plugins/control_editor_plugin.cpp new file mode 100644 index 000000000..8d8e107f4 --- /dev/null +++ b/tools/editor/plugins/control_editor_plugin.cpp @@ -0,0 +1,825 @@ +/*************************************************************************/ +/* control_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#if 0 +#include "control_editor_plugin.h" +#include "print_string.h" +#include "editor_node.h" +#include "os/keyboard.h" +#include "scene/main/viewport.h" + +void ControlEditor::_add_control(Control *p_control,const EditInfo& p_info) { + + if (controls.has(p_control)) + return; + + controls.insert(p_control,p_info); + p_control->call_deferred("connect","visibility_changed",this,"_visibility_changed",varray(p_control->get_instance_ID())); +} + +void ControlEditor::_remove_control(Control *p_control) { + + p_control->call_deferred("disconnect","visibility_changed",this,"_visibility_changed"); + controls.erase(p_control); +} +void ControlEditor::_clear_controls(){ + + while(controls.size()) + _remove_control(controls.front()->key()); +} + +void ControlEditor::_visibility_changed(ObjectID p_control) { + + Object *c = ObjectDB::get_instance(p_control); + if (!c) + return; + Control *ct = c->cast_to<Control>(); + if (!ct) + return; + + _remove_control(ct); +} + + +void ControlEditor::_node_removed(Node *p_node) { + + Control *control = (Control*)p_node; //not a good cast, but safe + if (controls.has(control)) + _remove_control(control); + + if (current_window==p_node) { + _clear_controls(); + } + update(); +} + +// slow as hell +Control* ControlEditor::_select_control_at_pos(const Point2& p_pos,Node* p_node) { + + for (int i=p_node->get_child_count()-1;i>=0;i--) { + + Control *r=_select_control_at_pos(p_pos,p_node->get_child(i)); + if (r) + return r; + } + + Control *c=p_node->cast_to<Control>(); + + if (c) { + Rect2 rect = c->get_window_rect(); + if (c->get_window()==current_window) { + rect.pos=transform.xform(rect.pos).floor(); + } + if (rect.has_point(p_pos)) + return c; + } + + return NULL; +} + + +void ControlEditor::_key_move(const Vector2& p_dir, bool p_snap) { + + if (drag!=DRAG_NONE) + return; + + Vector2 motion=p_dir; + if (p_snap) + motion*=snap_val->get_text().to_double(); + + undo_redo->create_action("Edit Control"); + for(ControlMap::Element *E=controls.front();E;E=E->next()) { + Control *control = E->key(); + undo_redo->add_do_method(control,"set_pos",control->get_pos()+motion); + undo_redo->add_undo_method(control,"set_pos",control->get_pos()); + } + undo_redo->commit_action(); +} + + +void ControlEditor::_input_event(InputEvent p_event) { + + if (p_event.type==InputEvent::MOUSE_BUTTON) { + + const InputEventMouseButton &b=p_event.mouse_button; + + if (b.button_index==BUTTON_RIGHT) { + + if (controls.size() && drag!=DRAG_NONE) { + //cancel drag + for(ControlMap::Element *E=controls.front();E;E=E->next()) { + Control *control = E->key(); + control->set_pos(E->get().drag_pos); + control->set_size(E->get().drag_size); + } + + } else if (b.pressed) { + popup->set_pos(Point2(b.x,b.y)); + popup->popup(); + } + return; + } + //if (!controls.size()) + // return; + + if (b.button_index!=BUTTON_LEFT) + return; + + if (!b.pressed) { + + if (drag!=DRAG_NONE) { + + if (undo_redo) { + + undo_redo->create_action("Edit Control"); + for(ControlMap::Element *E=controls.front();E;E=E->next()) { + Control *control = E->key(); + undo_redo->add_do_method(control,"set_pos",control->get_pos()); + undo_redo->add_do_method(control,"set_size",control->get_size()); + undo_redo->add_undo_method(control,"set_pos",E->get().drag_pos); + undo_redo->add_undo_method(control,"set_size",E->get().drag_size); + } + undo_redo->commit_action(); + } + + drag=DRAG_NONE; + + } + return; + } + + + if (controls.size()==1) { + //try single control edit + Control *control = controls.front()->key(); + ERR_FAIL_COND(!current_window); + + Rect2 rect=control->get_window_rect(); + Point2 ofs=Point2();//get_global_pos(); + Rect2 draw_rect=Rect2(rect.pos-ofs,rect.size); + Point2 click=Point2(b.x,b.y); + click = transform.affine_inverse().xform(click); + Size2 handle_size=Size2(handle_len,handle_len); + + drag = DRAG_NONE; + + if (Rect2(draw_rect.pos-handle_size,handle_size).has_point(click)) + drag=DRAG_TOP_LEFT; + else if (Rect2(draw_rect.pos+draw_rect.size,handle_size).has_point(click)) + drag=DRAG_BOTTOM_RIGHT; + else if(Rect2(draw_rect.pos+Point2(draw_rect.size.width,-handle_size.y),handle_size).has_point(click)) + drag=DRAG_TOP_RIGHT; + else if (Rect2(draw_rect.pos+Point2(-handle_size.x,draw_rect.size.height),handle_size).has_point(click)) + drag=DRAG_BOTTOM_LEFT; + else if (Rect2(draw_rect.pos+Point2(Math::floor((draw_rect.size.width-handle_size.x)/2.0),-handle_size.height),handle_size).has_point(click)) + drag=DRAG_TOP; + else if( Rect2(draw_rect.pos+Point2(-handle_size.width,Math::floor((draw_rect.size.height-handle_size.y)/2.0)),handle_size).has_point(click)) + drag=DRAG_LEFT; + else if ( Rect2(draw_rect.pos+Point2(Math::floor((draw_rect.size.width-handle_size.x)/2.0),draw_rect.size.height),handle_size).has_point(click)) + drag=DRAG_BOTTOM; + else if( Rect2(draw_rect.pos+Point2(draw_rect.size.width,Math::floor((draw_rect.size.height-handle_size.y)/2.0)),handle_size).has_point(click)) + drag=DRAG_RIGHT; + + if (drag!=DRAG_NONE) { + drag_from=click; + controls[control].drag_pos=control->get_pos(); + controls[control].drag_size=control->get_size(); + controls[control].drag_limit=drag_from+controls[control].drag_size-control->get_minimum_size(); + return; + } + + + } + + //multi control edit + + Point2 click=Point2(b.x,b.y); + Node* scene = get_scene()->get_root_node()->cast_to<EditorNode>()->get_edited_scene(); + if (!scene) + return; + /* + if (current_window) { + //no window.... ? + click-=current_window->get_scroll(); + }*/ + Control *c=_select_control_at_pos(click, scene); + + Node* n = c; + while ((n && n != scene && n->get_owner() != scene) || (n && !n->is_type("Control"))) { + n = n->get_parent(); + }; + c = n->cast_to<Control>(); + + + if (b.mod.control) { //additive selection + + if (!c) + return; //nothing to add + + if (current_window && controls.size() && c->get_window()!=current_window) + return; //cant multiple select from multiple windows + + if (!controls.size()) + current_window=c->get_window(); + + if (controls.has(c)) { + //already in here, erase it + _remove_control(c); + update(); + return; + } + + //check parents! + Control *parent = c->get_parent()->cast_to<Control>(); + + while(parent) { + + if (controls.has(parent)) + return; //a parent is already selected, so this is pointless + parent=parent->get_parent()->cast_to<Control>(); + } + + //check childrens of everything! + List<Control*> to_erase; + + for(ControlMap::Element *E=controls.front();E;E=E->next()) { + parent = E->key()->get_parent()->cast_to<Control>(); + while(parent) { + if (parent==c) { + to_erase.push_back(E->key()); + break; + } + parent=parent->get_parent()->cast_to<Control>(); + } + } + + while(to_erase.size()) { + _remove_control(to_erase.front()->get()); + to_erase.pop_front(); + } + + _add_control(c,EditInfo()); + update(); + } else { + //regular selection + if (!c) { + _clear_controls(); + update(); + return; + } + + if (!controls.has(c)) { + _clear_controls(); + current_window=c->get_window(); + _add_control(c,EditInfo()); + //reselect + if (get_scene()->is_editor_hint()) { + get_scene()->get_root_node()->call("edit_node",c); + } + + } + + + + for(ControlMap::Element *E=controls.front();E;E=E->next()) { + + EditInfo &ei=E->get(); + Control *control=E->key(); + ei.drag_pos=control->get_pos(); + ei.drag_size=control->get_size(); + ei.drag_limit=drag_from+ei.drag_size-control->get_minimum_size(); + } + + drag=DRAG_ALL; + drag_from=click; + update(); + } + + } + + if (p_event.type==InputEvent::MOUSE_MOTION) { + + const InputEventMouseMotion &m=p_event.mouse_motion; + + if (drag==DRAG_NONE || !current_window) + return; + + for(ControlMap::Element *E=controls.front();E;E=E->next()) { + + Control *control = E->key(); + Point2 control_drag_pos=E->get().drag_pos; + Point2 control_drag_size=E->get().drag_size; + Point2 control_drag_limit=E->get().drag_limit; + + Point2 pos=Point2(m.x,m.y); + pos = transform.affine_inverse().xform(pos); + + switch(drag) { + case DRAG_ALL: { + + control->set_pos( snapify(control_drag_pos+(pos-drag_from)) ); + } break; + case DRAG_RIGHT: { + + control->set_size( snapify(Size2(control_drag_size.width+(pos-drag_from).x,control_drag_size.height)) ); + } break; + case DRAG_BOTTOM: { + + control->set_size( snapify(Size2(control_drag_size.width,control_drag_size.height+(pos-drag_from).y)) ); + } break; + case DRAG_BOTTOM_RIGHT: { + + control->set_size( snapify(control_drag_size+(pos-drag_from)) ); + } break; + case DRAG_TOP_LEFT: { + + if(pos.x>control_drag_limit.x) + pos.x=control_drag_limit.x; + if(pos.y>control_drag_limit.y) + pos.y=control_drag_limit.y; + + Point2 old_size = control->get_size(); + Point2 new_pos = snapify(control_drag_pos+(pos-drag_from)); + Point2 new_size = old_size + (control->get_pos() - new_pos); + + control->set_pos( new_pos ); + control->set_size( new_size ); + } break; + case DRAG_TOP: { + + if(pos.y>control_drag_limit.y) + pos.y=control_drag_limit.y; + + Point2 old_size = control->get_size(); + Point2 new_pos = snapify(control_drag_pos+Point2(0,pos.y-drag_from.y)); + Point2 new_size = old_size + (control->get_pos() - new_pos); + + control->set_pos( new_pos ); + control->set_size( new_size ); + } break; + case DRAG_LEFT: { + + if(pos.x>control_drag_limit.x) + pos.x=control_drag_limit.x; + + Point2 old_size = control->get_size(); + Point2 new_pos = snapify(control_drag_pos+Point2(pos.x-drag_from.x,0)); + Point2 new_size = old_size + (control->get_pos() - new_pos); + + control->set_pos( new_pos ); + control->set_size( new_size ); + + } break; + case DRAG_TOP_RIGHT: { + + if(pos.y>control_drag_limit.y) + pos.y=control_drag_limit.y; + + Point2 old_size = control->get_size(); + Point2 new_pos = snapify(control_drag_pos+Point2(0,pos.y-drag_from.y)); + + float new_size_y = Point2( old_size + (control->get_pos() - new_pos)).y; + float new_size_x = snapify(control_drag_size+Point2(pos.x-drag_from.x,0)).x; + + control->set_pos( new_pos ); + control->set_size( Point2(new_size_x, new_size_y) ); + } break; + case DRAG_BOTTOM_LEFT: { + + if(pos.x>control_drag_limit.x) + pos.x=control_drag_limit.x; + + Point2 old_size = control->get_size(); + Point2 new_pos = snapify(control_drag_pos+Point2(pos.x-drag_from.x,0)); + + float new_size_y = snapify(control_drag_size+Point2(0,pos.y-drag_from.y)).y; + float new_size_x = Point2( old_size + (control->get_pos() - new_pos)).x; + + control->set_pos( new_pos ); + control->set_size( Point2(new_size_x, new_size_y) ); + + + } break; + + default:{} + } + } + } + + if (p_event.type==InputEvent::KEY) { + + const InputEventKey &k=p_event.key; + + if (k.pressed) { + + if (k.scancode==KEY_UP) + _key_move(Vector2(0,-1),k.mod.shift); + else if (k.scancode==KEY_DOWN) + _key_move(Vector2(0,1),k.mod.shift); + else if (k.scancode==KEY_LEFT) + _key_move(Vector2(-1,0),k.mod.shift); + else if (k.scancode==KEY_RIGHT) + _key_move(Vector2(1,0),k.mod.shift); + } + + } + + +} + + +bool ControlEditor::get_remove_list(List<Node*> *p_list) { + + for(ControlMap::Element *E=controls.front();E;E=E->next()) { + + p_list->push_back(E->key()); + } + + return !p_list->empty(); +} + +void ControlEditor::_update_scroll(float) { + + if (updating_scroll) + return; + + if (!current_window) + return; + + Point2 ofs; + ofs.x=h_scroll->get_val(); + ofs.y=v_scroll->get_val(); + +// current_window->set_scroll(-ofs); + + transform=Matrix32(); + + transform.scale_basis(Size2(zoom,zoom)); + transform.elements[2]=-ofs*zoom; + + + RID viewport = editor->get_scene_root()->get_viewport(); + + VisualServer::get_singleton()->viewport_set_global_canvas_transform(viewport,transform); + + update(); + +} + +void ControlEditor::_notification(int p_what) { + + if (p_what==NOTIFICATION_PROCESS) { + + for(ControlMap::Element *E=controls.front();E;E=E->next()) { + + Control *control = E->key(); + Rect2 r=control->get_window_rect(); + if (r != E->get().last_rect ) { + update(); + E->get().last_rect=r; + } + } + + } + + if (p_what==NOTIFICATION_CHILDREN_CONFIGURED) { + + get_scene()->connect("node_removed",this,"_node_removed"); + } + + if (p_what==NOTIFICATION_DRAW) { + + // TODO fetch the viewport? + /* + if (!control) { + h_scroll->hide(); + v_scroll->hide(); + return; + } + */ + _update_scrollbars(); + + if (!current_window) + return; + + for(ControlMap::Element *E=controls.front();E;E=E->next()) { + + Control *control = E->key(); + + Rect2 rect=control->get_window_rect(); + RID ci=get_canvas_item(); + VisualServer::get_singleton()->canvas_item_set_clip(ci,true); + Point2 ofs=Point2();//get_global_pos(); + Rect2 draw_rect=Rect2(rect.pos-ofs,rect.size); + draw_rect.pos = transform.xform(draw_rect.pos); + Color light_edit_color=Color(1.0,0.8,0.8); + Color dark_edit_color=Color(0.4,0.1,0.1); + Size2 handle_size=Size2(handle_len,handle_len); + +#define DRAW_RECT( m_rect, m_color )\ +VisualServer::get_singleton()->canvas_item_add_rect(ci,m_rect,m_color); + +#define DRAW_EMPTY_RECT( m_rect, m_color )\ + DRAW_RECT( Rect2(m_rect.pos,Size2(m_rect.size.width,1)), m_color );\ + DRAW_RECT(Rect2(Point2(m_rect.pos.x,m_rect.pos.y+m_rect.size.height-1),Size2(m_rect.size.width,1)), m_color);\ + DRAW_RECT(Rect2(m_rect.pos,Size2(1,m_rect.size.height)), m_color);\ + DRAW_RECT(Rect2(Point2(m_rect.pos.x+m_rect.size.width-1,m_rect.pos.y),Size2(1,m_rect.size.height)), m_color); + +#define DRAW_BORDER_RECT( m_rect, m_border_color,m_color )\ + DRAW_RECT( m_rect, m_color );\ + DRAW_EMPTY_RECT( m_rect, m_border_color ); + + DRAW_EMPTY_RECT( draw_rect.grow(2), light_edit_color ); + DRAW_EMPTY_RECT( draw_rect.grow(1), dark_edit_color ); + + if (controls.size()==1) { + DRAW_BORDER_RECT( Rect2(draw_rect.pos-handle_size,handle_size), light_edit_color,dark_edit_color ); + DRAW_BORDER_RECT( Rect2(draw_rect.pos+draw_rect.size,handle_size), light_edit_color,dark_edit_color ); + DRAW_BORDER_RECT( Rect2(draw_rect.pos+Point2(draw_rect.size.width,-handle_size.y),handle_size), light_edit_color,dark_edit_color ); + DRAW_BORDER_RECT( Rect2(draw_rect.pos+Point2(-handle_size.x,draw_rect.size.height),handle_size), light_edit_color,dark_edit_color ); + + DRAW_BORDER_RECT( Rect2(draw_rect.pos+Point2(Math::floor((draw_rect.size.width-handle_size.x)/2.0),-handle_size.height),handle_size), light_edit_color,dark_edit_color ); + DRAW_BORDER_RECT( Rect2(draw_rect.pos+Point2(-handle_size.width,Math::floor((draw_rect.size.height-handle_size.y)/2.0)),handle_size), light_edit_color,dark_edit_color ); + DRAW_BORDER_RECT( Rect2(draw_rect.pos+Point2(Math::floor((draw_rect.size.width-handle_size.x)/2.0),draw_rect.size.height),handle_size), light_edit_color,dark_edit_color ); + DRAW_BORDER_RECT( Rect2(draw_rect.pos+Point2(draw_rect.size.width,Math::floor((draw_rect.size.height-handle_size.y)/2.0)),handle_size), light_edit_color,dark_edit_color ); + } + + //DRAW_EMPTY_RECT( Rect2( current_window->get_scroll()-Point2(1,1), get_size()+Size2(2,2)), Color(0.8,0.8,1.0,0.8) ); + E->get().last_rect = rect; + } + } +} + +void ControlEditor::edit(Control *p_control) { + + drag=DRAG_NONE; + + _clear_controls(); + _add_control(p_control,EditInfo()); + current_window=p_control->get_window(); + update(); + +} + + +void ControlEditor::_find_controls_span(Node *p_node, Rect2& r_rect) { + + if (!editor->get_scene()) + return; + + if (p_node!=editor->get_edited_scene() && p_node->get_owner()!=editor->get_edited_scene()) + return; + + if (p_node->cast_to<Control>()) { + Control *c = p_node->cast_to<Control>(); + if (c->get_viewport() != editor->get_viewport()->get_viewport()) + return; //bye, it's in another viewport + + if (!c->get_parent_control()) { + + Rect2 span = c->get_subtree_span_rect(); + r_rect.merge(span); + } + } + + for(int i=0;i<p_node->get_child_count();i++) { + + _find_controls_span(p_node->get_child(i),r_rect); + } +} + +void ControlEditor::_update_scrollbars() { + + + if (!editor->get_scene()) { + h_scroll->hide(); + v_scroll->hide(); + return; + } + + updating_scroll=true; + + + Size2 size = get_size(); + Size2 hmin = h_scroll->get_minimum_size(); + Size2 vmin = v_scroll->get_minimum_size(); + + v_scroll->set_begin( Point2(size.width - vmin.width, 0) ); + v_scroll->set_end( Point2(size.width, size.height) ); + + h_scroll->set_begin( Point2( 0, size.height - hmin.height) ); + h_scroll->set_end( Point2(size.width-vmin.width, size.height) ); + + + Rect2 local_rect = Rect2(Point2(),get_size()-Size2(vmin.width,hmin.height)); + + Rect2 control_rect=local_rect; + if (editor->get_edited_scene()) + _find_controls_span(editor->get_edited_scene(),control_rect); + control_rect.pos*=zoom; + control_rect.size*=zoom; + + /* + for(ControlMap::Element *E=controls.front();E;E=E->next()) { + + Control *control = E->key(); + Rect2 r = control->get_window()->get_subtree_span_rect(); + if (E==controls.front()) { + control_rect = r.merge(local_rect); + } else { + control_rect = control_rect.merge(r); + } + } + + */ + Point2 ofs; + + + if (control_rect.size.height <= local_rect.size.height) { + + v_scroll->hide(); + ofs.y=0; + } else { + + v_scroll->show(); + v_scroll->set_min(control_rect.pos.y); + v_scroll->set_max(control_rect.pos.y+control_rect.size.y); + v_scroll->set_page(local_rect.size.y); + ofs.y=-v_scroll->get_val(); + } + + if (control_rect.size.width <= local_rect.size.width) { + + h_scroll->hide(); + ofs.x=0; + } else { + + h_scroll->show(); + h_scroll->set_min(control_rect.pos.x); + h_scroll->set_max(control_rect.pos.x+control_rect.size.x); + h_scroll->set_page(local_rect.size.x); + ofs.x=-h_scroll->get_val(); + } + +// transform=Matrix32(); + transform.elements[2]=ofs*zoom; + RID viewport = editor->get_scene_root()->get_viewport(); + VisualServer::get_singleton()->viewport_set_global_canvas_transform(viewport,transform); + +// transform.scale_basis(Vector2(zoom,zoom)); + updating_scroll=false; + +} + + +Point2i ControlEditor::snapify(const Point2i& p_pos) const { + + bool active=popup->is_item_checked(0); + int snap = snap_val->get_text().to_int(); + + if (!active || snap<1) + return p_pos; + + Point2i pos=p_pos; + pos.x-=pos.x%snap; + pos.y-=pos.y%snap; + return pos; + + +} +void ControlEditor::_popup_callback(int p_op) { + + switch(p_op) { + + case SNAP_USE: { + + popup->set_item_checked(0,!popup->is_item_checked(0)); + } break; + case SNAP_CONFIGURE: { + snap_dialog->popup_centered(Size2(200,85)); + } break; + } +} + +void ControlEditor::_bind_methods() { + + ObjectTypeDB::bind_method("_input_event",&ControlEditor::_input_event); + ObjectTypeDB::bind_method("_node_removed",&ControlEditor::_node_removed); + ObjectTypeDB::bind_method("_update_scroll",&ControlEditor::_update_scroll); + ObjectTypeDB::bind_method("_popup_callback",&ControlEditor::_popup_callback); + ObjectTypeDB::bind_method("_visibility_changed",&ControlEditor::_visibility_changed); +} + +ControlEditor::ControlEditor(EditorNode *p_editor) { + + editor=p_editor; + h_scroll = memnew( HScrollBar ); + v_scroll = memnew( VScrollBar ); + + add_child(h_scroll); + add_child(v_scroll); + h_scroll->connect("value_changed", this,"_update_scroll",Vector<Variant>(),true); + v_scroll->connect("value_changed", this,"_update_scroll",Vector<Variant>(),true); + + + updating_scroll=false; + set_focus_mode(FOCUS_ALL); + handle_len=10; + + popup=memnew( PopupMenu ); + popup->add_check_item("Use Snap"); + popup->add_item("Configure Snap.."); + add_child(popup); + + snap_dialog = memnew( ConfirmationDialog ); + snap_dialog->get_ok()->hide(); + snap_dialog->get_cancel()->set_text("Close"); + add_child(snap_dialog); + + Label *l = memnew(Label); + l->set_text("Snap:"); + l->set_pos(Point2(5,5)); + snap_dialog->add_child(l); + + snap_val=memnew(LineEdit); + snap_val->set_text("5"); + snap_val->set_anchor(MARGIN_RIGHT,ANCHOR_END); + snap_val->set_begin(Point2(15,25)); + snap_val->set_end(Point2(10,25)); + snap_dialog->add_child(snap_val); + + popup->connect("item_pressed", this,"_popup_callback"); + current_window=NULL; + + zoom=0.5; +} + + +void ControlEditorPlugin::edit(Object *p_object) { + + control_editor->set_undo_redo(&get_undo_redo()); + control_editor->edit(p_object->cast_to<Control>()); +} + +bool ControlEditorPlugin::handles(Object *p_object) const { + + return p_object->is_type("Control"); +} + +void ControlEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + control_editor->show(); + control_editor->set_process(true); + } else { + + control_editor->hide(); + control_editor->set_process(false); + } + +} + +ControlEditorPlugin::ControlEditorPlugin(EditorNode *p_node) { + + editor=p_node; + control_editor = memnew( ControlEditor(editor) ); + editor->get_viewport()->add_child(control_editor); + control_editor->set_area_as_parent_rect(); + control_editor->hide(); + + + +} + + +ControlEditorPlugin::~ControlEditorPlugin() +{ +} + + +#endif diff --git a/tools/editor/plugins/control_editor_plugin.h b/tools/editor/plugins/control_editor_plugin.h new file mode 100644 index 000000000..a22932799 --- /dev/null +++ b/tools/editor/plugins/control_editor_plugin.h @@ -0,0 +1,141 @@ +/*************************************************************************/ +/* control_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef CONTROL_EDITOR_PLUGIN_H +#define CONTROL_EDITOR_PLUGIN_H + +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + +#if 0 +class ControlEditor : public Control { + + OBJ_TYPE(ControlEditor, Control ); + + EditorNode *editor; + + enum { + SNAP_USE, + SNAP_CONFIGURE + }; + + enum DragType { + DRAG_NONE, + DRAG_LEFT, + DRAG_TOP_LEFT, + DRAG_TOP, + DRAG_TOP_RIGHT, + DRAG_RIGHT, + DRAG_BOTTOM_RIGHT, + DRAG_BOTTOM, + DRAG_BOTTOM_LEFT, + DRAG_ALL + }; + + HScrollBar *h_scroll; + VScrollBar *v_scroll; + + Matrix32 transform; + float zoom; + + Control *current_window; + PopupMenu *popup; + DragType drag; + Point2 drag_from; + + struct EditInfo { + + Point2 drag_pos; + Point2 drag_size; + Point2 drag_limit; + Rect2 last_rect; + }; + + typedef Map<Control*,EditInfo> ControlMap; + ControlMap controls; + int handle_len; + Control* _select_control_at_pos(const Point2& p_pos,Node* p_node); + + ConfirmationDialog *snap_dialog; + LineEdit *snap_val; + + void _add_control(Control *p_control,const EditInfo& p_info); + void _remove_control(Control *p_control); + void _clear_controls(); + void _visibility_changed(ObjectID p_control); + void _key_move(const Vector2& p_dir, bool p_snap); + + + Point2i snapify(const Point2i& p_pos) const; + void _popup_callback(int p_op); + bool updating_scroll; + void _update_scroll(float); + void _update_scrollbars(); + UndoRedo *undo_redo; + + void _find_controls_span(Node *p_node, Rect2& r_rect); + +protected: + void _notification(int p_what); + void _input_event(InputEvent p_event); + void _node_removed(Node *p_node); + static void _bind_methods(); +public: + + bool get_remove_list(List<Node*> *p_list); + void set_undo_redo(UndoRedo *p_undo_redo) {undo_redo=p_undo_redo; } + void edit(Control *p_control); + ControlEditor(EditorNode *p_editor); +}; + +class ControlEditorPlugin : public EditorPlugin { + + OBJ_TYPE( ControlEditorPlugin, EditorPlugin ); + + ControlEditor *control_editor; + EditorNode *editor; + +public: + + virtual String get_name() const { return "GUI"; } + bool has_main_screen() const { return true; } + virtual void edit(Object *p_object); + virtual bool handles(Object *p_object) const; + virtual void make_visible(bool p_visible); + virtual bool get_remove_list(List<Node*> *p_list) { return control_editor->get_remove_list(p_list); } + + + ControlEditorPlugin(EditorNode *p_node); + ~ControlEditorPlugin(); + +}; +#endif +#endif diff --git a/tools/editor/plugins/cube_grid_theme_editor_plugin.cpp b/tools/editor/plugins/cube_grid_theme_editor_plugin.cpp new file mode 100644 index 000000000..aad7cf2c6 --- /dev/null +++ b/tools/editor/plugins/cube_grid_theme_editor_plugin.cpp @@ -0,0 +1,343 @@ +/*************************************************************************/ +/* cube_grid_theme_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "cube_grid_theme_editor_plugin.h" + +#include "scene/3d/mesh_instance.h" +#include "scene/3d/physics_body.h" +#include "scene/main/viewport.h" +#include "scene/resources/packed_scene.h" +#include "tools/editor/editor_node.h" +#include "main/main.h" +#include "tools/editor/editor_settings.h" + +void MeshLibraryEditor::edit(const Ref<MeshLibrary>& p_theme) { + + theme=p_theme; + if (theme.is_valid()) + menu->get_popup()->set_item_disabled(menu->get_popup()->get_item_index(MENU_OPTION_UPDATE_FROM_SCENE),!theme->has_meta("_editor_source_scene")); + +} + +void MeshLibraryEditor::_menu_confirm() { + + + switch(option) { + + case MENU_OPTION_REMOVE_ITEM: { + + theme->remove_item(to_erase); + } break; + case MENU_OPTION_UPDATE_FROM_SCENE: { + String existing = theme->get_meta("_editor_source_scene"); + ERR_FAIL_COND(existing==""); + _import_scene_cbk(existing); + + } break; + default: {}; + } + +} + + +void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library, bool p_merge) { + + if (!p_merge) + p_library->clear(); + + + for(int i=0;i<p_scene->get_child_count();i++) { + + + Node *child = p_scene->get_child(i); + + if (!child->cast_to<MeshInstance>()) { + if (child->get_child_count()>0) { + child=child->get_child(0); + if (!child->cast_to<MeshInstance>()) { + continue; + } + + } else + continue; + + + } + + MeshInstance *mi = child->cast_to<MeshInstance>(); + Ref<Mesh> mesh=mi->get_mesh(); + if (mesh.is_null()) + continue; + + int id = p_library->find_item_name(mi->get_name()); + if (id<0) { + + id=p_library->get_last_unused_item_id(); + p_library->create_item(id); + p_library->set_item_name(id,mi->get_name()); + } + + + p_library->set_item_mesh(id,mesh); + + Ref<Shape> collision; + + for(int j=0;j<mi->get_child_count();j++) { +#if 1 + Node *child2 = mi->get_child(j); + if (!child2->cast_to<StaticBody>()) + continue; + StaticBody *sb = child2->cast_to<StaticBody>(); + if (sb->get_shape_count()==0) + continue; + collision=sb->get_shape(0); + if (!collision.is_null()) + break; +#endif + } + + if (!collision.is_null()) { + + p_library->set_item_shape(id,collision); + } + + } + + + //generate previews! + + if (1) { + Vector<int> ids = p_library->get_item_list(); + RID vp = VS::get_singleton()->viewport_create(); + VS::ViewportRect vr; + vr.x=0; + vr.y=0; + vr.width=EditorSettings::get_singleton()->get("grid_map/preview_size"); + vr.height=EditorSettings::get_singleton()->get("grid_map/preview_size"); + VS::get_singleton()->viewport_set_rect(vp,vr); + VS::get_singleton()->viewport_set_as_render_target(vp,true); + VS::get_singleton()->viewport_set_render_target_update_mode(vp,VS::RENDER_TARGET_UPDATE_ALWAYS); + RID scen = VS::get_singleton()->scenario_create(); + VS::get_singleton()->viewport_set_scenario(vp,scen); + RID cam = VS::get_singleton()->camera_create(); + VS::get_singleton()->camera_set_transform(cam, Transform() ); + VS::get_singleton()->viewport_attach_camera(vp,cam); + RID light = VS::get_singleton()->light_create(VS::LIGHT_DIRECTIONAL); + RID lightinst = VS::get_singleton()->instance_create2(light,scen); + VS::get_singleton()->camera_set_orthogonal(cam,1.0,0.01,1000.0); + + + EditorProgress ep("mlib","Creating Mesh Library",ids.size()); + + for(int i=0;i<ids.size();i++) { + + int id=ids[i]; + Ref<Mesh> mesh = p_library->get_item_mesh(id); + if (!mesh.is_valid()) + continue; + AABB aabb= mesh->get_aabb(); + print_line("aabb: "+aabb); + Vector3 ofs = aabb.pos + aabb.size*0.5; + aabb.pos-=ofs; + Transform xform; + xform.basis=Matrix3().rotated(Vector3(0,1,0),Math_PI*0.25); + xform.basis = Matrix3().rotated(Vector3(1,0,0),-Math_PI*0.25)*xform.basis; + AABB rot_aabb = xform.xform(aabb); + print_line("rot_aabb: "+rot_aabb); + float m = MAX(rot_aabb.size.x,rot_aabb.size.y)*0.5; + if (m==0) + continue; + m=1.0/m; + m*=0.5; + print_line("scale: "+rtos(m)); + xform.basis.scale(Vector3(m,m,m)); + xform.origin=-xform.basis.xform(ofs); //-ofs*m; + xform.origin.z-=rot_aabb.size.z*2; + RID inst = VS::get_singleton()->instance_create2(mesh->get_rid(),scen); + VS::get_singleton()->instance_set_transform(inst,xform); + ep.step("Thumbnail..",i); + VS::get_singleton()->viewport_queue_screen_capture(vp); + Main::iteration(); + Image img = VS::get_singleton()->viewport_get_screen_capture(vp); + ERR_CONTINUE(img.empty()); + Ref<ImageTexture> it( memnew( ImageTexture )); + it->create_from_image(img); + p_library->set_item_preview(id,it); + +// print_line("loaded image, size: "+rtos(m)+" dist: "+rtos(dist)+" empty?"+itos(img.empty())+" w: "+itos(it->get_width())+" h: "+itos(it->get_height())); + VS::get_singleton()->free(inst); + } + + VS::get_singleton()->free(lightinst); + VS::get_singleton()->free(light); + VS::get_singleton()->free(vp); + VS::get_singleton()->free(cam); + VS::get_singleton()->free(scen); + } + + +} + + +void MeshLibraryEditor::_import_scene_cbk(const String& p_str) { + + + print_line("Impot Callback!"); + + Ref<PackedScene> ps = ResourceLoader::load(p_str,"PackedScene"); + ERR_FAIL_COND(ps.is_null()); + Node *scene = ps->instance(); + + _import_scene(scene,theme,option==MENU_OPTION_UPDATE_FROM_SCENE); + + memdelete(scene); + theme->set_meta("_editor_source_scene",p_str); + menu->get_popup()->set_item_disabled(menu->get_popup()->get_item_index(MENU_OPTION_UPDATE_FROM_SCENE),false); + +} + +Error MeshLibraryEditor::update_library_file(Node *p_base_scene, Ref<MeshLibrary> ml,bool p_merge) { + + _import_scene(p_base_scene,ml,p_merge); + return OK; +} + + +void MeshLibraryEditor::_menu_cbk(int p_option) { + + option=p_option; + switch(p_option) { + + case MENU_OPTION_ADD_ITEM: { + + theme->create_item(theme->get_last_unused_item_id()); + } break; + case MENU_OPTION_REMOVE_ITEM: { + + String p = editor->get_property_editor()->get_selected_path(); + if (p.begins_with("/MeshLibrary/item") && p.get_slice_count("/")>=3) { + + to_erase = p.get_slice("/",3).to_int(); + cd->set_text("Remove Item "+itos(to_erase)+"?"); + cd->popup_centered(Size2(300,60)); + } + } break; + case MENU_OPTION_IMPORT_FROM_SCENE: { + + file->popup_centered_ratio(); + } break; + case MENU_OPTION_UPDATE_FROM_SCENE: { + + cd->set_text("Update from existing scene?:\n"+String(theme->get_meta("_editor_source_scene"))); + cd->popup_centered(Size2(500,60)); + } break; + } +} + + +void MeshLibraryEditor::_bind_methods() { + + ObjectTypeDB::bind_method("_menu_cbk",&MeshLibraryEditor::_menu_cbk); + ObjectTypeDB::bind_method("_menu_confirm",&MeshLibraryEditor::_menu_confirm); + ObjectTypeDB::bind_method("_import_scene_cbk",&MeshLibraryEditor::_import_scene_cbk); +} + +MeshLibraryEditor::MeshLibraryEditor(EditorNode *p_editor) { + + file = memnew( FileDialog ); + file->set_mode(FileDialog::MODE_OPEN_FILE); + //not for now? + List<String> extensions; + ResourceLoader::get_recognized_extensions_for_type("PackedScene",&extensions); + file->clear_filters(); + file->set_title("Import Scene"); + for(int i=0;i<extensions.size();i++) { + + file->add_filter("*."+extensions[i]+" ; "+extensions[i].to_upper()); + } + add_child(file); + file->connect("file_selected",this,"_import_scene_cbk"); + + Panel *panel = memnew( Panel ); + panel->set_area_as_parent_rect(); + add_child(panel); + MenuButton * options = memnew( MenuButton ); + panel->add_child(options); + options->set_pos(Point2(1,1)); + options->set_text("Theme"); + options->get_popup()->add_item("Add Item",MENU_OPTION_ADD_ITEM); + options->get_popup()->add_item("Remove Selected Item",MENU_OPTION_REMOVE_ITEM); + options->get_popup()->add_separator(); + options->get_popup()->add_item("Import from Scene",MENU_OPTION_IMPORT_FROM_SCENE); + options->get_popup()->add_item("Update from Scene",MENU_OPTION_UPDATE_FROM_SCENE); + options->get_popup()->set_item_disabled(options->get_popup()->get_item_index(MENU_OPTION_UPDATE_FROM_SCENE),true); + options->get_popup()->connect("item_pressed", this,"_menu_cbk"); + menu=options; + editor=p_editor; + cd = memnew(ConfirmationDialog); + add_child(cd); + cd->get_ok()->connect("pressed", this,"_menu_confirm"); + +} + +void MeshLibraryEditorPlugin::edit(Object *p_node) { + + if (p_node && p_node->cast_to<MeshLibrary>()) { + theme_editor->edit( p_node->cast_to<MeshLibrary>() ); + theme_editor->show(); + } else + theme_editor->hide(); +} + +bool MeshLibraryEditorPlugin::handles(Object *p_node) const{ + + return p_node->is_type("MeshLibrary"); +} + +void MeshLibraryEditorPlugin::make_visible(bool p_visible){ + + if (p_visible) + theme_editor->show(); + else + theme_editor->hide(); +} + +MeshLibraryEditorPlugin::MeshLibraryEditorPlugin(EditorNode *p_node) { + + EDITOR_DEF("grid_map/preview_size",64); + theme_editor = memnew( MeshLibraryEditor(p_node) ); + + p_node->get_viewport()->add_child(theme_editor); + theme_editor->set_area_as_parent_rect(); + theme_editor->set_anchor( MARGIN_RIGHT, Control::ANCHOR_END ); + theme_editor->set_anchor( MARGIN_BOTTOM, Control::ANCHOR_BEGIN ); + theme_editor->set_end( Point2(0,22) ); + theme_editor->hide(); + +} + diff --git a/tools/editor/plugins/cube_grid_theme_editor_plugin.h b/tools/editor/plugins/cube_grid_theme_editor_plugin.h new file mode 100644 index 000000000..0dab1d12b --- /dev/null +++ b/tools/editor/plugins/cube_grid_theme_editor_plugin.h @@ -0,0 +1,95 @@ +/*************************************************************************/ +/* cube_grid_theme_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef CUBE_GRID_THEME_EDITOR_PLUGIN_H +#define CUBE_GRID_THEME_EDITOR_PLUGIN_H + +#include "scene/resources/mesh_library.h" +#include "tools/editor/editor_node.h" + + +class MeshLibraryEditor : public Control { + + OBJ_TYPE( MeshLibraryEditor, Control ); + + Ref<MeshLibrary> theme; + + EditorNode *editor; + MenuButton *menu; + ConfirmationDialog *cd; + FileDialog *file; + int to_erase; + + enum { + + MENU_OPTION_ADD_ITEM, + MENU_OPTION_REMOVE_ITEM, + MENU_OPTION_UPDATE_FROM_SCENE, + MENU_OPTION_IMPORT_FROM_SCENE + }; + + int option; + void _import_scene_cbk(const String& p_str); + void _menu_cbk(int p_option); + void _menu_confirm(); + + static void _import_scene(Node *p_scene, Ref<MeshLibrary> p_library, bool p_merge); + +protected: + static void _bind_methods(); +public: + + void edit(const Ref<MeshLibrary>& p_theme); + static Error update_library_file(Node *p_base_scene, Ref<MeshLibrary> ml,bool p_merge=true); + + MeshLibraryEditor(EditorNode *p_editor); +}; + + + +class MeshLibraryEditorPlugin : public EditorPlugin { + + OBJ_TYPE( MeshLibraryEditorPlugin, EditorPlugin ); + + MeshLibraryEditor *theme_editor; + EditorNode *editor; + +public: + + virtual String get_name() const { return "MeshLibrary"; } + 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); + + MeshLibraryEditorPlugin(EditorNode *p_node); + +}; + + +#endif // CUBE_GRID_THEME_EDITOR_PLUGIN_H diff --git a/tools/editor/plugins/font_editor_plugin.cpp b/tools/editor/plugins/font_editor_plugin.cpp new file mode 100644 index 000000000..d85226959 --- /dev/null +++ b/tools/editor/plugins/font_editor_plugin.cpp @@ -0,0 +1,905 @@ +/*************************************************************************/ +/* font_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "font_editor_plugin.h" +#include "os/file_access.h" +#ifdef FREETYPE_ENABLED + +#include <ft2build.h> +#include FT_FREETYPE_H + +#endif + +#include "core/io/resource_saver.h" + +void FontEditor::edit(const Ref<Font>& p_font) { + + font=p_font; + label->add_font_override("font",font); +} + +void FontEditor::_preview_text_changed(const String& p_text) { + + label->set_text(p_text); +} +struct FontData { + + Vector<uint8_t> bitmap; + int width,height; + int ofs_x; //ofset to center, from ABOVE + int ofs_y; //ofset to begining, from LEFT + int valign; //vertical alignment + int halign; + int advance; + int character; + int glyph; + + int texture; +// bool printable; + +}; + + +struct FontDataSort { + + bool operator()(const FontData *p_A,const FontData *p_B) const { + return p_A->height > p_B->height; + }; +}; + +struct KerningKey { + + CharType A,B; + bool operator<(const KerningKey& p_k) const { return (A==p_k.A)?(B<p_k.B):(A<p_k.A); } + +}; + +void FontEditor::_export_fnt(const String& p_name, Ref<Font> p_font) { + + String fnt_name = p_name + ".fnt"; + FileAccess* f = FileAccess::open(fnt_name, FileAccess::WRITE); + ERR_FAIL_COND(!f); + + f->store_string(String("info face=\"") + p_font->get_name() + "\" size=" + String::num_real(font->get_height()) + " bold=0 italic=0 charset=\"\" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=4,4\n"); + + Vector2 size = p_font->get_texture(0)->get_size(); + f->store_string(String("common lineHeight=") + String::num(font->get_height()) + " base=" + String::num(font->get_ascent()) + " scaleW=" + String::num(size.x) + " scaleH=" + String::num(size.y) + " pages="+String::num(p_font->get_texture_count()) + " packed=0\n"); + + for (int i=0; i<p_font->get_texture_count(); i++) { + + f->store_string(String("page id=")+String::num(i)+ " file=\""+ p_name.get_file() + "_" +String::num(i)+".png\"\n"); + }; + + f->store_string(String("chars count=")+String::num(p_font->get_character_count()) + "\n"); + + Vector<CharType> keys = p_font->get_char_keys(); + keys.sort(); + for (int i=0; i<keys.size(); i++) { + + Font::Character c = p_font->get_character(keys[i]); + int width = c.rect.size.x; + if (keys[i] == 32) { + width = c.advance; + }; + f->store_string(String("char id=") + String::num(keys[i]) + " x=" + String::num(c.rect.pos.x) + " y=" + String::num(c.rect.pos.y) + + " width=" + String::num(width) + " height=" + String::num(c.rect.size.y) + + " xoffset=" + String::num(c.h_align) + " yoffset=" + String::num(c.v_align) + + " xadvance=" + String::num(c.advance) + " page=" + String::num(c.texture_idx) + + " chnl=0 letter=\"\"\n"); + }; + + f->close(); + + for (int i=0; i<p_font->get_texture_count(); i++) { + + ResourceSaver::save(p_name + "_" + String::num(i) + ".png", p_font->get_texture(i)); + }; +}; + +void FontEditor::_import_fnt(const String& p_string) { + //fnt format used by angelcode bmfont + //http://www.angelcode.com/products/bmfont/ + + FileAccess *f = FileAccess::open(p_string,FileAccess::READ); + + if (!f) { + + ERR_EXPLAIN("Can't open font: "+p_string); + ERR_FAIL(); + } + + + font->clear(); + + while(true) { + + String line=f->get_line(); + + int delimiter=line.find(" "); + String type=line.substr(0,delimiter); + int pos = delimiter+1; + Map<String,String> keys; + + while (pos < line.size() && line[pos]==' ') + pos++; + + + while(pos<line.size()) { + + int eq = line.find("=",pos); + if (eq==-1) + break; + String key=line.substr(pos,eq-pos); + int end=-1; + String value; + if (line[eq+1]=='"') { + end=line.find("\"",eq+2); + if (end==-1) + break; + value=line.substr(eq+2,end-1-eq-1); + pos=end+1; + } else { + end=line.find(" ",eq+1); + if (end==-1) + end=line.size(); + + value=line.substr(eq+1,end-eq); + + pos=end; + + } + + while (pos<line.size() && line[pos]==' ') + pos++; + + + keys[key]=value; + + } + + + if (type=="info") { + + if (keys.has("face")) + font->set_name(keys["face"]); + //if (keys.has("size")) + // font->set_height(keys["size"].to_int()); + + } else if (type=="common") { + + if (keys.has("lineHeight")) + font->set_height(keys["lineHeight"].to_int()); + if (keys.has("base")) + font->set_ascent(keys["base"].to_int()); + + } else if (type=="page") { + + if (keys.has("file")) { + + String file = keys["file"]; + file=p_string.get_base_dir()+"/"+file; + Ref<Texture> tex = ResourceLoader::load(file); + if (tex.is_null()) { + ERR_PRINT("Can't load font texture!"); + } else { + font->add_texture(tex); + } + } + } else if (type=="char") { + + CharType idx=0; + if (keys.has("id")) + idx=keys["id"].to_int(); + + Rect2 rect; + + if (keys.has("x")) + rect.pos.x=keys["x"].to_int(); + if (keys.has("y")) + rect.pos.y=keys["y"].to_int(); + if (keys.has("width")) + rect.size.width=keys["width"].to_int(); + if (keys.has("height")) + rect.size.height=keys["height"].to_int(); + + Point2 ofs; + + if (keys.has("xoffset")) + ofs.x=keys["xoffset"].to_int(); + if (keys.has("yoffset")) + ofs.y=keys["yoffset"].to_int(); + + int texture=0; + if (keys.has("page")) + texture=keys["page"].to_int(); + int advance=-1; + if (keys.has("xadvance")) + advance=keys["xadvance"].to_int(); + + font->add_char(idx,texture,rect,ofs,advance); + + } else if (type=="kerning") { + + CharType first=0,second=0; + int k=0; + + if (keys.has("first")) + first=keys["first"].to_int(); + if (keys.has("second")) + second=keys["second"].to_int(); + if (keys.has("amount")) + k=keys["amount"].to_int(); + + font->add_kerning_pair(first,second,-k); + + } + + if (f->eof_reached()) + break; + } + + + + memdelete(f); + + + +} + +void FontEditor::_import_ttf(const String& p_string) { + +#ifdef FREETYPE_ENABLED + FT_Library library; /* handle to library */ + FT_Face face; /* handle to face object */ + + Vector<FontData*> font_data_list; + + int error = FT_Init_FreeType( &library ); + + ERR_EXPLAIN("Error initializing FreeType."); + ERR_FAIL_COND( error !=0 ); + + + error = FT_New_Face( library, p_string.utf8().get_data(),0,&face ); + + if ( error == FT_Err_Unknown_File_Format ) { + ERR_EXPLAIN("Unknown font format."); + FT_Done_FreeType( library ); + } else if ( error ) { + + ERR_EXPLAIN("Error loading font."); + FT_Done_FreeType( library ); + + } + + ERR_FAIL_COND(error); + + + int height=0; + int ascent=0; + int font_spacing=0; + + int size=font_size->get_text().to_int(); + + error = FT_Set_Char_Size(face,0,64*size,512,512); + + if ( error ) { + FT_Done_FreeType( library ); + ERR_EXPLAIN("Invalid font size. "); + ERR_FAIL_COND( error ); + + } + + error = FT_Set_Pixel_Sizes(face,0,size); + + FT_GlyphSlot slot = face->glyph; + +// error = FT_Set_Charmap(face,ft_encoding_unicode ); /* encoding.. */ + + + /* PRINT CHARACTERS TO INDIVIDUAL BITMAPS */ + + +// int space_size=5; //size for space, if none found.. 5! +// int min_valign=500; //some ridiculous number + + FT_ULong charcode; + FT_UInt gindex; + + int max_up=-1324345; ///gibberish + int max_down=124232; + + Map<KerningKey,int> kerning_map; + + charcode = FT_Get_First_Char( face, &gindex ); + + int xsize=0; + while ( gindex != 0 ) + { + + bool skip=false; + error = FT_Load_Char( face, charcode, FT_LOAD_RENDER ); + if (error) skip=true; + else error = FT_Render_Glyph( face->glyph, ft_render_mode_normal ); + if (error) skip=true; + + + if (!skip && (import_chars.has(charcode) && import_chars[charcode] != 0)) { + + skip = false; + + } else { + if (import_option->get_selected() == 0 && charcode>127) + skip=true; + if (import_option->get_selected() == 1 && charcode>0xFE) + skip=true; + }; + + if (charcode<=32) // + skip=true; + + if (skip) { + charcode=FT_Get_Next_Char(face,charcode,&gindex); + continue; + } + + FontData * fdata = memnew( FontData ); + fdata->bitmap.resize( slot->bitmap.width*slot->bitmap.rows ); + fdata->width=slot->bitmap.width; + fdata->height=slot->bitmap.rows; + fdata->character=charcode; + fdata->glyph=FT_Get_Char_Index(face,charcode); + if (charcode=='x') + xsize=slot->bitmap.width; + + + if (charcode<127) { + if (slot->bitmap_top>max_up) { + + max_up=slot->bitmap_top; + } + + + if ( (slot->bitmap_top - fdata->height)<max_down ) { + + max_down=slot->bitmap_top - fdata->height; + } + } + + + fdata->valign=slot->bitmap_top; + fdata->halign=slot->bitmap_left; + fdata->advance=(slot->advance.x+(1<<5))>>6; + fdata->advance+=font_spacing; + + for (int i=0;i<slot->bitmap.width;i++) { + for (int j=0;j<slot->bitmap.rows;j++) { + + fdata->bitmap[j*slot->bitmap.width+i]=slot->bitmap.buffer[j*slot->bitmap.width+i]; + } + } + + font_data_list.push_back(fdata); + charcode=FT_Get_Next_Char(face,charcode,&gindex); +// printf("reading char %i\n",charcode); + } + + /* SPACE */ + + FontData *spd = memnew( FontData ); + spd->advance=0; + spd->character=' '; + spd->halign=0; + spd->valign=0; + spd->width=0; + spd->height=0; + spd->ofs_x=0; + spd->ofs_y=0; + + if (!FT_Load_Char( face, ' ', FT_LOAD_RENDER ) && !FT_Render_Glyph( face->glyph, ft_render_mode_normal )) { + + spd->advance = slot->advance.x>>6; + spd->advance+=font_spacing; + } else { + + spd->advance=xsize; + spd->advance+=font_spacing; + } + + font_data_list.push_back(spd); + + Map<CharType, bool> exported; + for (int i=0; i<font_data_list.size(); i++) { + exported[font_data_list[i]->character] = true; + }; + int missing = 0; + for(Map<CharType,int>::Element *E=import_chars.front();E;E=E->next()) { + CharType c = E->key(); + if (!exported.has(c)) { + CharType str[2] = {c, 0}; + printf("** Warning: character %i (%ls) not exported\n", (int)c, str); + ++missing; + }; + }; + printf("total %i/%i\n", missing, import_chars.size()); + + /* KERNING */ + + + for(int i=0;i<font_data_list.size();i++) { + + for(int j=0;j<font_data_list.size();j++) { + + FT_Vector delta; + FT_Get_Kerning( face, font_data_list[i]->glyph,font_data_list[j]->glyph, FT_KERNING_DEFAULT, &delta ); + + if (delta.x!=0) { + + KerningKey kpk; + kpk.A = font_data_list[i]->character; + kpk.B = font_data_list[j]->character; + int kern = ((-delta.x)+(1<<5))>>6; + + if (kern==0) + continue; + kerning_map[kpk]=kern; + } + } + } + + height=max_up-max_down; + ascent=max_up; + + /* FIND OUT WHAT THE FONT HEIGHT FOR THIS IS */ + + /* ADJUST THE VALIGN FOR EACH CHARACTER */ + + for (int i=0;i<(int)font_data_list.size();i++) { + + font_data_list[i]->valign=max_up-font_data_list[i]->valign; + } + + + + /* ADD THE SPACEBAR CHARACTER */ +/* + FontData * fdata = new FontData; + + fdata->character=32; + fdata->bitmap=0; + fdata->width=xsize; + fdata->height=1; + fdata->valign=0; + + font_data_list.push_back(fdata); +*/ + /* SORT BY HEIGHT, SO THEY FIT BETTER ON THE TEXTURE */ + + font_data_list.sort_custom<FontDataSort>(); + + int spacing=2; + + + int use_width=256; + int use_max_height=256; +// int surf_idx=-1; + + List<Size2> tex_sizes; +// int current_texture=0; + + Size2 first(use_width,nearest_power_of_2( font_data_list[0]->height + spacing )); + Size2 *curtex=&tex_sizes.push_back(first)->get(); + + Point2 tex_ofs; + + /* FIT (NOT COPY YEY) FACES IN TEXTURES */ + + int current_height=font_data_list[0]->height + spacing; + + int font_margin=2; + + + for(int i=0;i<font_data_list.size();i++) { + + FontData *fd=font_data_list[i]; + + if (tex_ofs.x+fd->width >= use_width) { + //end of column, advance a row + tex_ofs.x=0; + tex_ofs.y+=current_height+font_margin; + current_height=fd->height + spacing; + + int new_tex_h = curtex->height; + + while( tex_ofs.y+current_height > new_tex_h ) { + + if (curtex->height * 2 > use_max_height) { + //oops, can't use this texture anymore.. + Size2 newtex( use_width, nearest_power_of_2( fd->height + spacing )); + new_tex_h=newtex.height; + curtex=&tex_sizes.push_back(newtex)->get(); + tex_ofs=Point2(0,0); + + } else { + + new_tex_h*=2; + } + } + + curtex->height=new_tex_h; + + } + + fd->ofs_x=tex_ofs.x; + fd->ofs_y=tex_ofs.y; + fd->texture=tex_sizes.size()-1; + + tex_ofs.x+=fd->width+font_margin; + + + } + + /* WRITE FACES IN TEXTURES */ + + // create textures + + Vector<DVector<uint8_t> >image_data; + Vector<int> image_widths; + Vector<DVector<uint8_t>::Write> image_ptrs; + image_ptrs.resize(tex_sizes.size()); + + for(int i=0;i<tex_sizes.size();i++) { + + DVector<uint8_t> pixels; + int texsize=tex_sizes[i].width * tex_sizes[i].height * 2; + pixels.resize(texsize ); + + image_data.push_back(pixels); + image_widths.push_back( tex_sizes[i].width ); + image_ptrs[i] = image_data[i].write(); + for(int j=0;j<texsize;j++) { + + image_ptrs[i][j]=0; + } + + } + + //blit textures with fonts + for(int i=0;i<font_data_list.size();i++) { + + FontData *fd=font_data_list[i]; + + uint8_t *pixels = image_ptrs[fd->texture].ptr(); + int width = image_widths[fd->texture]; + + for(int y=0;y<fd->height;y++) { + + const uint8_t *src = &fd->bitmap[y*fd->width]; + uint8_t *dst = &pixels[((fd->ofs_y+y)*width+fd->ofs_x)*2]; + + + for(int x=0;x<fd->width;x++) { + + dst[x<<1]=255; //white always + dst[(x<<1) +1]=src[x]; + + } + } + } + + //unlock writing + for(int i=0;i<image_ptrs.size();i++) + image_ptrs[i]=DVector<uint8_t>::Write(); + + /* CREATE FONT */ + + font->clear(); + font->set_height(height); + font->set_ascent(ascent); + + //register texures + for(int i=0;i<tex_sizes.size();i++) { + Image img(tex_sizes[i].width,tex_sizes[i].height,0,Image::FORMAT_GRAYSCALE_ALPHA,image_data[i]); + Ref<ImageTexture> tex = memnew( ImageTexture ); + tex->create_from_image(img,0); //no filter, no repeat + font->add_texture(tex); + //tframe->set_texture(tex); + + } + + //register characters + + for(int i=0;i<font_data_list.size();i++) { + FontData *fd=font_data_list[i]; + + font->add_char(fd->character,fd->texture,Rect2( fd->ofs_x, fd->ofs_y, fd->width, fd->height),Point2(fd->halign,fd->valign), fd->advance); + memdelete(fd); + } + + for(Map<KerningKey,int>::Element *E=kerning_map.front();E;E=E->next()) { + + font->add_kerning_pair(E->key().A,E->key().B,E->get()); + } + + FT_Done_FreeType( library ); +#endif +} + +void FontEditor::_add_source() { + + _source_file->popup_centered_ratio(); +}; + +void FontEditor::_add_source_accept(const String& p_file) { + + FileAccess* f = FileAccess::open(p_file, FileAccess::READ); + ERR_FAIL_COND(!f); + + String line; + while ( !f->eof_reached() ) { + + line = f->get_line(); + for (int i=0; i<line.length(); i++) { + + if (import_chars.has(line[i])) { + import_chars[line[i]] = import_chars[line[i]] + 1; + } else { + import_chars[line[i]] = 1; + }; + }; + }; +}; + +void FontEditor::_export_fnt_pressed() { + + _export_file->popup_centered_ratio(); +}; + +void FontEditor::_export_fnt_accept(const String& p_file) { + + String name = p_file.replace(".fnt", ""); + _export_fnt(name, font); +}; + +void FontEditor::_import_accept(const String& p_string) { + +#ifdef FREETYPE_ENABLED + + if (p_string.extension().nocasecmp_to("ttf")==0 || p_string.extension().nocasecmp_to("otf")==0) { + + _import_ttf(p_string); + } +#endif + + if (p_string.extension().nocasecmp_to("fnt")==0) { + + _import_fnt(p_string); + } + + label->add_font_override("font",font); + label->notification(Control::NOTIFICATION_THEME_CHANGED); + label->update(); +} + +void FontEditor::_import() { + + + file->popup_centered_ratio(); +} + +void FontEditor::_bind_methods() { + + ObjectTypeDB::bind_method("_import",&FontEditor::_import); + ObjectTypeDB::bind_method("_import_accept",&FontEditor::_import_accept); + ObjectTypeDB::bind_method("_preview_text_changed",&FontEditor::_preview_text_changed); + ObjectTypeDB::bind_method("_add_source",&FontEditor::_add_source); + ObjectTypeDB::bind_method("_add_source_accept",&FontEditor::_add_source_accept); + ObjectTypeDB::bind_method("_export_fnt_pressed",&FontEditor::_export_fnt_pressed); + ObjectTypeDB::bind_method("_export_fnt_accept",&FontEditor::_export_fnt_accept); +} + +FontEditor::FontEditor() { + + panel = memnew( Panel ); + add_child(panel); + panel->set_area_as_parent_rect(); + + /* + tframe = memnew( TextureFrame ); + + tframe->set_anchor( MARGIN_RIGHT, ANCHOR_END ); + tframe->set_anchor( MARGIN_BOTTOM, ANCHOR_END ); + + tframe->set_begin( Point2(5, 40 ) ); + tframe->set_end( Point2(5,55 ) ); + + panel->add_child(tframe); +*/ + + Label *l = memnew( Label ); + l->set_pos( Point2(5,13 ) ); + l->set_text("Import: "); + + panel->add_child(l); + + l = memnew( Label ); + l->set_pos( Point2(25,37 ) ); + l->set_text("Size: "); + + panel->add_child(l); + + font_size = memnew( LineEdit ); + font_size->set_text("12"); + font_size->set_pos( Point2(70,35 ) ); + font_size->set_size( Size2(40,10 ) ); + panel->add_child(font_size); + + l = memnew( Label ); + l->set_pos( Point2(140,37 ) ); + l->set_text("Encoding: "); + + panel->add_child(l); + + import_option = memnew( OptionButton ); + import_option->add_item("Ascii"); + import_option->add_item("Latin"); + import_option->add_item("Full Unicode"); + import_option->select(1); + + import_option->set_pos( Point2( 215,35 ) ); + import_option->set_size( Point2( 100,12 ) ); + + panel->add_child(import_option); + + Button* import = memnew( Button ); + import->set_text("Import:.."); + import->set_begin( Point2(80,35) ); + import->set_end( Point2(10,45) ); + + import->set_anchor( MARGIN_LEFT, ANCHOR_END ); + import->set_anchor( MARGIN_RIGHT, ANCHOR_END ); + + panel->add_child(import); + + Button* add_source = memnew( Button ); + add_source->set_text("Add Source..."); + add_source->set_begin( Point2(180,35) ); + add_source->set_end( Point2(90,45) ); + add_source->set_anchor( MARGIN_LEFT, ANCHOR_END ); + add_source->set_anchor( MARGIN_RIGHT, ANCHOR_END ); + + panel->add_child(add_source); + + file = memnew( FileDialog ); + file->set_access(FileDialog::ACCESS_FILESYSTEM); + + _source_file = memnew( FileDialog ); + _source_file->set_access(FileDialog::ACCESS_FILESYSTEM); + _source_file->set_mode(FileDialog::MODE_OPEN_FILE); + _source_file->connect("file_selected", this, "_add_source_accept"); + panel->add_child( _source_file ); + + Button* export_fnt = memnew(Button); + export_fnt->set_text("Export fnt"); + export_fnt->set_begin(Point2(80, 65)); + export_fnt->set_end(Point2(10, 75)); + export_fnt->set_anchor( MARGIN_LEFT, ANCHOR_END ); + export_fnt->set_anchor( MARGIN_RIGHT, ANCHOR_END ); + export_fnt->connect("pressed", this, "_export_fnt_pressed"); + panel->add_child( export_fnt ); + + _export_file = memnew(FileDialog); + _export_file->set_access(FileDialog::ACCESS_FILESYSTEM); + _export_file->set_mode(FileDialog::MODE_SAVE_FILE); + _export_file->connect("file_selected", this, "_export_fnt_accept"); + panel->add_child(_export_file); + + l = memnew( Label ); + l->set_pos( Point2(5,65 ) ); + l->set_text("Preview Text: "); + + panel->add_child(l); + + preview_text = memnew( LineEdit ); + preview_text->set_anchor( MARGIN_RIGHT, ANCHOR_END ); + preview_text->set_begin( Point2(25,85 ) ); + preview_text->set_end( Point2(10,95 ) ); + panel->add_child(preview_text); + preview_text->connect("text_changed", this,"_preview_text_changed"); + preview_text->set_text("The quick brown fox jumped over the lazy dog."); + + l = memnew( Label ); + l->set_pos( Point2(5,115 ) ); + l->set_text("Preview: "); + + panel->add_child(l); + + label = memnew( Label ); + label->set_autowrap(true); + + label->set_anchor( MARGIN_RIGHT, ANCHOR_END ); + label->set_anchor( MARGIN_BOTTOM, ANCHOR_END ); + + label->set_begin( Point2(5, 135 ) ); + label->set_end( Point2(5,5 ) ); + + label->set_text("The quick brown fox jumped over the lazy dog."); + label->set_align( Label::ALIGN_CENTER ); + + panel->add_child(label); + +#ifdef FREETYPE_ENABLED + + file->add_filter("*.ttf"); + file->add_filter("*.otf"); +#endif + file->add_filter("*.fnt ; AngelCode BMFont"); + + file->set_mode(FileDialog::MODE_OPEN_FILE); + panel->add_child( file ); + + import->connect("pressed", this,"_import"); + file->connect("file_selected", this,"_import_accept"); + add_source->connect("pressed", this, "_add_source"); +} + +void FontEditorPlugin::edit(Object *p_node) { + + if (p_node && p_node->cast_to<Font>()) { + font_editor->edit( p_node->cast_to<Font>() ); + font_editor->show(); + } else + font_editor->hide(); +} + +bool FontEditorPlugin::handles(Object *p_node) const{ + + return p_node->is_type("Font"); +} + +void FontEditorPlugin::make_visible(bool p_visible){ + + if (p_visible) + font_editor->show(); + else + font_editor->hide(); +} + +FontEditorPlugin::FontEditorPlugin(EditorNode *p_node) { + + font_editor = memnew( FontEditor ); + + p_node->get_viewport()->add_child(font_editor); + font_editor->set_area_as_parent_rect(); + font_editor->hide(); + + + + +} + diff --git a/tools/editor/plugins/font_editor_plugin.h b/tools/editor/plugins/font_editor_plugin.h new file mode 100644 index 000000000..fe51ce36e --- /dev/null +++ b/tools/editor/plugins/font_editor_plugin.h @@ -0,0 +1,100 @@ +/*************************************************************************/ +/* font_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef FONT_EDITOR_PLUGIN_H +#define FONT_EDITOR_PLUGIN_H + +#include "scene/resources/font.h" +#include "scene/gui/texture_frame.h" +#include "scene/gui/option_button.h" +#include "tools/editor/editor_node.h" + + +class FontEditor : public Control { + + OBJ_TYPE( FontEditor, Control ); + + Panel *panel; + LineEdit *font_size; + //TextureFrame *tframe; //for debug + Label *label; + LineEdit *preview_text; + FileDialog *file; + FileDialog* _source_file; + FileDialog* _export_file; + OptionButton *import_option; + + Ref<Font> font; + + Map<CharType, int> import_chars; + + void _export_fnt(const String& p_name, Ref<Font> p_font); + void _export_fnt_pressed(); + void _export_fnt_accept(const String& p_file); + + void _import_ttf(const String& p_string); + void _import_fnt(const String& p_string); + void _preview_text_changed(const String& p_text); + + void _add_source(); + void _add_source_accept(const String& p_file); + + void _import_accept(const String&); + void _import(); +protected: + static void _bind_methods(); +public: + + void edit(const Ref<Font>& p_font); + + FontEditor(); +}; + + + +class FontEditorPlugin : public EditorPlugin { + + OBJ_TYPE( FontEditorPlugin, EditorPlugin ); + + FontEditor *font_editor; + EditorNode *editor; + +public: + + virtual String get_name() const { return "Font"; } + 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); + + FontEditorPlugin(EditorNode *p_node); + +}; + + +#endif // FONT_EDITOR_PLUGIN_H diff --git a/tools/editor/plugins/item_list_editor_plugin.cpp b/tools/editor/plugins/item_list_editor_plugin.cpp new file mode 100644 index 000000000..a059470ce --- /dev/null +++ b/tools/editor/plugins/item_list_editor_plugin.cpp @@ -0,0 +1,415 @@ +/*************************************************************************/ +/* item_list_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "item_list_editor_plugin.h" + +#include "io/resource_loader.h" + + +bool ItemListPlugin::_set(const StringName& p_name, const Variant& p_value) { + + String name = p_name; + int idx = name.get_slice("/",0).to_int(); + String what=name.get_slice("/",1); + + if (what=="text") + set_item_text(idx,p_value); + else if (what=="icon") + set_item_icon(idx,p_value); + else if (what=="checkable") + set_item_checkable(idx,p_value); + else if (what=="checked") + set_item_checked(idx,p_value); + else if (what=="enabled") + set_item_enabled(idx,p_value); + else if (what=="accel") + set_item_accel(idx,p_value); + else if (what=="id") + set_item_id(idx,p_value); + else if (what=="separator") + set_item_separator(idx,p_value); + else + return false; + + return true; +} + +bool ItemListPlugin::_get(const StringName& p_name,Variant &r_ret) const { + String name = p_name; + int idx = name.get_slice("/",0).to_int(); + String what=name.get_slice("/",1); + + if (what=="text") + r_ret=get_item_text(idx); + else if (what=="icon") + r_ret=get_item_icon(idx); + else if (what=="checkable") + r_ret=is_item_checkable(idx); + else if (what=="checked") + r_ret=is_item_checked(idx); + else if (what=="enabled") + r_ret=is_item_enabled(idx); + else if (what=="accel") + r_ret=get_item_accel(idx); + else if (what=="id") + r_ret=get_item_id(idx); + else if (what=="separator") + r_ret=is_item_separator(idx); + else + return false; + + return true; +} +void ItemListPlugin::_get_property_list( List<PropertyInfo> *p_list) const { + + for(int i=0;i<get_item_count();i++) { + + String base=itos(i)+"/"; + + p_list->push_back( PropertyInfo(Variant::STRING,base+"text") ); + p_list->push_back( PropertyInfo(Variant::OBJECT,base+"icon",PROPERTY_HINT_RESOURCE_TYPE,"Texture") ); + if (get_flags()&FLAG_CHECKABLE) { + + p_list->push_back( PropertyInfo(Variant::BOOL,base+"checkable") ); + p_list->push_back( PropertyInfo(Variant::BOOL,base+"checked") ); + + } + if (get_flags()&FLAG_ENABLE) { + + p_list->push_back( PropertyInfo(Variant::BOOL,base+"enabled") ); + + } + if (get_flags()&FLAG_ACCEL) { + + p_list->push_back( PropertyInfo(Variant::INT,base+"accel",PROPERTY_HINT_KEY_ACCEL) ); + + } + if (get_flags()&FLAG_ID) { + + p_list->push_back( PropertyInfo(Variant::INT,base+"id",PROPERTY_HINT_RANGE,"-1,4096") ); + + } + if (get_flags()&FLAG_SEPARATOR) { + + p_list->push_back( PropertyInfo(Variant::BOOL,base+"separator") ); + + } + } +} + +void ItemListEditor::_node_removed(Node *p_node) { + + if(p_node==item_list) { + item_list=NULL; + hide(); + dialog->hide(); + } + + +} + +void ItemListEditor::_delete_pressed() { + + String p = prop_editor->get_selected_path(); + + if (p.find("/")!=-1) { + + if (selected_idx<0 || selected_idx>=item_plugins.size()) + return; + + item_plugins[selected_idx]->erase(p.get_slice("/",0).to_int());; + } + +} + +void ItemListEditor::_add_pressed() { + + if (selected_idx<0 || selected_idx>=item_plugins.size()) + return; + + item_plugins[selected_idx]->add_item(); +} + +void ItemListEditor::_notification(int p_notification) { + + if (p_notification==NOTIFICATION_ENTER_SCENE) { + + add_button->set_icon(get_icon("Add","EditorIcons")); + del_button->set_icon(get_icon("Del","EditorIcons")); + } +} + + +void ItemListEditor::_menu_option(int p_option) { + + + switch(p_option) { + + case MENU_EDIT_ITEMS: { + + dialog->popup_centered_ratio(); + } break; + } +} + + +void ItemListEditor::edit(Node *p_item_list) { + + item_list=p_item_list; + + for(int i=0;i<item_plugins.size();i++) { + if (item_plugins[i]->handles(p_item_list)) { + + item_plugins[i]->set_object(p_item_list); + prop_editor->edit(item_plugins[i]); + selected_idx=i; + return; + } + } + + selected_idx=-1; + + prop_editor->edit(NULL); + +} + + +void ItemListEditor::_bind_methods() { + + ObjectTypeDB::bind_method("_menu_option",&ItemListEditor::_menu_option); + ObjectTypeDB::bind_method("_add_button",&ItemListEditor::_add_pressed); + ObjectTypeDB::bind_method("_delete_button",&ItemListEditor::_delete_pressed); + + //ObjectTypeDB::bind_method("_populate",&ItemListEditor::_populate); + +} + +bool ItemListEditor::handles(Object *p_object) const { + for(int i=0;i<item_plugins.size();i++) { + if (item_plugins[i]->handles(p_object)) { + return true; + } + } + + return false; + +} +ItemListEditor::ItemListEditor() { + + selected_idx=-1; + options = memnew( MenuButton ); + add_child(options); + options->set_area_as_parent_rect(); + + options->set_text("Items"); + options->get_popup()->add_item("Edit Items",MENU_EDIT_ITEMS); + //options->get_popup()->add_item("Clear",MENU_CLEAR); + + options->get_popup()->connect("item_pressed", this,"_menu_option"); + + dialog = memnew( AcceptDialog ); + add_child( dialog ); + + + + HBoxContainer *hbc = memnew( HBoxContainer ); + + dialog->add_child(hbc); + dialog->set_child_rect(hbc); + + prop_editor = memnew( PropertyEditor ); + + hbc->add_child(prop_editor); + prop_editor->set_h_size_flags(SIZE_EXPAND_FILL); + + VBoxContainer *vbc = memnew( VBoxContainer ); + hbc->add_child(vbc); + + add_button = memnew( Button ); + //add_button->set_text("Add"); + add_button->connect("pressed",this,"_add_button"); + vbc->add_child(add_button); + + del_button = memnew( Button ); + //del_button->set_text("Del"); + del_button->connect("pressed",this,"_delete_button"); + vbc->add_child(del_button); + + dialog->set_title("Item List"); + prop_editor->hide_top_label(); + + + +} + + +void ItemListEditorPlugin::edit(Object *p_object) { + + item_list_editor->edit(p_object->cast_to<Node>()); +} + +bool ItemListEditorPlugin::handles(Object *p_object) const { + + return item_list_editor->handles(p_object); +} + +void ItemListEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + item_list_editor->show(); + } else { + + item_list_editor->hide(); + item_list_editor->edit(NULL); + } + +} + + +ItemListEditor::~ItemListEditor() { + + for(int i=0;i<item_plugins.size();i++) + memdelete( item_plugins[i] ); +} + +///////////////////////// PLUGINS ///////////////////////////// +///////////////////////// PLUGINS ///////////////////////////// +///////////////////////// PLUGINS ///////////////////////////// +///////////////////////// PLUGINS ///////////////////////////// +///////////////////////// PLUGINS ///////////////////////////// + + +class ItemListOptionButtonPlugin : public ItemListPlugin { + + OBJ_TYPE(ItemListOptionButtonPlugin,ItemListPlugin); + + OptionButton *ob; +public: + + virtual void set_object(Object *p_object) { ob = p_object->cast_to<OptionButton>(); } + + virtual bool handles(Object *p_object) const { return p_object->cast_to<OptionButton>()!=NULL; } + + virtual int get_flags() const { return FLAG_ICON|FLAG_ID|FLAG_ENABLE; } + + virtual void set_item_text(int p_idx,const String& p_text){ ob->set_item_text(p_idx,p_text);} + virtual void set_item_icon(int p_idx,const Ref<Texture>& p_tex){ ob->set_item_icon(p_idx,p_tex);} + virtual void set_item_enabled(int p_idx,int p_enabled){ ob->set_item_disabled(p_idx,!p_enabled);} + virtual void set_item_id(int p_idx,int p_id){ ob->set_item_ID(p_idx,p_id);} + + + virtual String get_item_text(int p_idx) const{ return ob->get_item_text(p_idx); }; + virtual Ref<Texture> get_item_icon(int p_idx) const{ return ob->get_item_icon(p_idx); }; + virtual bool is_item_enabled(int p_idx) const{ return !ob->is_item_disabled(p_idx); }; + virtual int get_item_id(int p_idx) const{ return ob->get_item_ID(p_idx); }; + + virtual void add_item() { ob->add_item( "New Item "+itos(ob->get_item_count())); _change_notify();} + virtual int get_item_count() const { return ob->get_item_count(); } + virtual void erase(int p_idx) { ob->remove_item(p_idx); _change_notify();} + + + ItemListOptionButtonPlugin() { ob=NULL; } +}; + +class ItemListPopupMenuPlugin : public ItemListPlugin { + + OBJ_TYPE(ItemListPopupMenuPlugin,ItemListPlugin); + + PopupMenu *pp; +public: + + virtual void set_object(Object *p_object) { + if (p_object->cast_to<MenuButton>()) + pp = p_object->cast_to<MenuButton>()->get_popup(); + else + pp = p_object->cast_to<PopupMenu>(); + } + + virtual bool handles(Object *p_object) const { return p_object->cast_to<PopupMenu>()!=NULL || p_object->cast_to<MenuButton>()!=NULL; } + + virtual int get_flags() const { return FLAG_ICON|FLAG_ID|FLAG_ENABLE|FLAG_CHECKABLE|FLAG_SEPARATOR|FLAG_ACCEL; } + + virtual void set_item_text(int p_idx,const String& p_text){ pp->set_item_text(p_idx,p_text); } + virtual void set_item_icon(int p_idx,const Ref<Texture>& p_tex){ pp->set_item_icon(p_idx,p_tex);} + virtual void set_item_checkable(int p_idx,bool p_check){ pp->set_item_as_checkable(p_idx,p_check);} + virtual void set_item_checked(int p_idx,bool p_checked){ pp->set_item_checked(p_idx,p_checked);} + virtual void set_item_accel(int p_idx,int p_accel){ pp->set_item_accelerator(p_idx,p_accel);} + virtual void set_item_enabled(int p_idx,int p_enabled){ pp->set_item_disabled(p_idx,!p_enabled);} + virtual void set_item_id(int p_idx,int p_id){ pp->set_item_ID(p_idx,p_idx);} + virtual void set_item_separator(int p_idx,bool p_separator){ pp->set_item_as_separator(p_idx,p_separator);} + + + virtual String get_item_text(int p_idx) const{ return pp->get_item_text(p_idx); }; + virtual Ref<Texture> get_item_icon(int p_idx) const{ return pp->get_item_icon(p_idx); }; + virtual bool is_item_checkable(int p_idx) const{ return pp->is_item_checkable(p_idx); }; + virtual bool is_item_checked(int p_idx) const{ return pp->is_item_checked(p_idx); }; + virtual int get_item_accel(int p_idx) const{ return pp->get_item_accelerator(p_idx); }; + virtual bool is_item_enabled(int p_idx) const{ return !pp->is_item_disabled(p_idx); }; + virtual int get_item_id(int p_idx) const{ return pp->get_item_ID(p_idx); }; + virtual bool is_item_separator(int p_idx) const{ return pp->is_item_separator(p_idx); }; + + + + virtual void add_item() { pp->add_item( "New Item "+itos(pp->get_item_count())); _change_notify();} + virtual int get_item_count() const { return pp->get_item_count(); } + virtual void erase(int p_idx) { pp->remove_item(p_idx); _change_notify();} + + + ItemListPopupMenuPlugin() { pp=NULL; } +}; + + + + + + +ItemListEditorPlugin::ItemListEditorPlugin(EditorNode *p_node) { + + editor=p_node; + item_list_editor = memnew( ItemListEditor ); + editor->get_viewport()->add_child(item_list_editor); + +// item_list_editor->set_anchor(MARGIN_LEFT,Control::ANCHOR_END); +// item_list_editor->set_anchor(MARGIN_RIGHT,Control::ANCHOR_END); + item_list_editor->set_margin(MARGIN_LEFT,180); + item_list_editor->set_margin(MARGIN_RIGHT,230); + item_list_editor->set_margin(MARGIN_TOP,0); + item_list_editor->set_margin(MARGIN_BOTTOM,10); + + + item_list_editor->hide(); + item_list_editor->add_plugin( memnew( ItemListOptionButtonPlugin) ); + item_list_editor->add_plugin( memnew( ItemListPopupMenuPlugin) ); +} + + +ItemListEditorPlugin::~ItemListEditorPlugin() +{ +} + + diff --git a/tools/editor/plugins/item_list_editor_plugin.h b/tools/editor/plugins/item_list_editor_plugin.h new file mode 100644 index 000000000..6b4d26fb4 --- /dev/null +++ b/tools/editor/plugins/item_list_editor_plugin.h @@ -0,0 +1,164 @@ +/*************************************************************************/ +/* item_list_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef ITEM_LIST_EDITOR_PLUGIN_H +#define ITEM_LIST_EDITOR_PLUGIN_H + +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" +#include "scene/gui/option_button.h" +#include "scene/gui/menu_button.h" +#include "scene/gui/popup_menu.h" +#include "scene/gui/spin_box.h" + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + + +class ItemListPlugin : public Object { + + OBJ_TYPE(ItemListPlugin,Object); + +protected: + + bool _set(const StringName& p_name, const Variant& p_value); + bool _get(const StringName& p_name,Variant &r_ret) const; + void _get_property_list( List<PropertyInfo> *p_list) const; + + +public: + + enum Flags { + + FLAG_ICON=1, + FLAG_CHECKABLE=2, + FLAG_ACCEL=4, + FLAG_ID=8, + FLAG_ENABLE=16, + FLAG_SEPARATOR=32 + }; + + virtual void set_object(Object *p_object)=0; + + virtual bool handles(Object *p_object) const=0; + + virtual int get_flags() const=0; + + virtual void set_item_text(int p_idx,const String& p_text){} + virtual void set_item_icon(int p_idx,const Ref<Texture>& p_tex){} + virtual void set_item_checkable(int p_idx,bool p_check){} + virtual void set_item_checked(int p_idx,bool p_checked){} + virtual void set_item_accel(int p_idx,int p_accel){} + virtual void set_item_enabled(int p_idx,int p_enabled){} + virtual void set_item_id(int p_idx,int p_id){} + virtual void set_item_separator(int p_idx,bool p_separator){} + + + virtual String get_item_text(int p_idx) const{ return ""; }; + virtual Ref<Texture> get_item_icon(int p_idx) const{ return Ref<Texture>(); }; + virtual bool is_item_checkable(int p_idx) const{ return false; }; + virtual bool is_item_checked(int p_idx) const{ return false; }; + virtual int get_item_accel(int p_idx) const{ return 0; }; + virtual bool is_item_enabled(int p_idx) const{ return false; }; + virtual int get_item_id(int p_idx) const{ return -1; }; + virtual bool is_item_separator(int p_idx) const{ return false; }; + + virtual void add_item()=0; + virtual int get_item_count() const=0; + virtual void erase(int p_idx)=0; + + ItemListPlugin() {} +}; + +class ItemListEditor : public Control { + + OBJ_TYPE(ItemListEditor, Control ); + + Node *item_list; + + enum { + + MENU_EDIT_ITEMS, + MENU_CLEAR + }; + + AcceptDialog *dialog; + + PropertyEditor *prop_editor; + + MenuButton * options; + int selected_idx; + + Button *add_button; + Button *del_button; + + +// FileDialog *emission_file_dialog; + void _menu_option(int); + + Vector<ItemListPlugin*> item_plugins; + + void _node_removed(Node *p_node); + void _add_pressed(); + void _delete_pressed(); +protected: + + void _notification(int p_notification); + + static void _bind_methods(); +public: + + void edit(Node *p_item_list); + bool handles(Object *p_object) const; + void add_plugin(ItemListPlugin* p_plugin) { item_plugins.push_back(p_plugin); } + ItemListEditor(); + ~ItemListEditor(); +}; + +class ItemListEditorPlugin : public EditorPlugin { + + OBJ_TYPE( ItemListEditorPlugin, EditorPlugin ); + + ItemListEditor *item_list_editor; + EditorNode *editor; + +public: + + virtual String get_name() const { return "ItemList"; } + 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); + + ItemListEditorPlugin(EditorNode *p_node); + ~ItemListEditorPlugin(); + +}; + +#endif // ITEM_LIST_EDITOR_PLUGIN_H diff --git a/tools/editor/plugins/multimesh_editor_plugin.cpp b/tools/editor/plugins/multimesh_editor_plugin.cpp new file mode 100644 index 000000000..b2b6cbe9b --- /dev/null +++ b/tools/editor/plugins/multimesh_editor_plugin.cpp @@ -0,0 +1,470 @@ +/*************************************************************************/ +/* multimesh_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "multimesh_editor_plugin.h" +#include "scene/gui/box_container.h" +#include "scene/3d/mesh_instance.h" + + + +void MultiMeshEditor::_node_removed(Node *p_node) { + + if(p_node==node) { + node=NULL; + hide(); + } + +} + + +void MultiMeshEditor::_populate() { + + if(!node) + return; + + + Ref<Mesh> mesh; + + if (mesh_source->get_text()=="") { + + Ref<MultiMesh> multimesh; + multimesh = node->get_multimesh(); + if (multimesh.is_null()) { + + err_dialog->set_text("No mesh source specified (and no MultiMesh set in node)."); + err_dialog->popup_centered(Size2(300,100)); + return; + } + if (multimesh->get_mesh().is_null()) { + + err_dialog->set_text("No mesh source specified (and MultiMesh contains no Mesh)."); + err_dialog->popup_centered(Size2(300,100)); + return; + } + + mesh = multimesh->get_mesh(); + } else { + + Node *ms_node = node->get_node(mesh_source->get_text()); + + if (!ms_node) { + + err_dialog->set_text("Mesh source is invalid (Invalid Path)."); + err_dialog->popup_centered(Size2(300,100)); + return; + } + + MeshInstance *ms_instance = ms_node->cast_to<MeshInstance>(); + + if (!ms_instance) { + + err_dialog->set_text("Mesh source is invalid (Not a MeshInstance)."); + err_dialog->popup_centered(Size2(300,100)); + return; + } + + mesh=ms_instance->get_mesh(); + + if (mesh.is_null()) { + + err_dialog->set_text("Mesh source is invalid (Contains no Mesh resource)."); + err_dialog->popup_centered(Size2(300,100)); + return; + } + + } + + if (surface_source->get_text()=="") { + + err_dialog->set_text("No surface source specified."); + err_dialog->popup_centered(Size2(300,100)); + return; + } + + Node *ss_node = node->get_node(surface_source->get_text()); + + if (!ss_node) { + + err_dialog->set_text("Surface source is invalid (Invalid Path)."); + err_dialog->popup_centered(Size2(300,100)); + return; + } + + GeometryInstance *ss_instance = ss_node->cast_to<MeshInstance>(); + + if (!ss_instance) { + + err_dialog->set_text("Surface source is invalid (Not Geometry)."); + err_dialog->popup_centered(Size2(300,100)); + return; + } + + Transform geom_xform = node->get_global_transform().affine_inverse() * ss_instance->get_global_transform(); + + DVector<Face3> geometry = ss_instance->get_faces(VisualInstance::FACES_SOLID); + + if (geometry.size()==0) { + + err_dialog->set_text("Surface source is invalid (No Faces)."); + err_dialog->popup_centered(Size2(300,100)); + return; + } + + //make all faces local + + int gc = geometry.size(); + DVector<Face3>::Write w = geometry.write(); + + for(int i=0;i<gc;i++) { + for(int j=0;j<3;j++) { + w[i].vertex[j] = geom_xform.xform( w[i].vertex[j] ); + } + } + + + + w = DVector<Face3>::Write(); +#if 0 + node->get_multimesh()->set_instance_count(populate_amount->get_val()); + node->populate_parent(populate_rotate_random->get_val(),populate_tilt_random->get_val(),populate_scale_random->get_val(),populate_scale->get_val()); + + + ERR_EXPLAIN("Parent is not of type VisualInstance."); + ERR_FAIL_COND(!get_parent() || !get_parent()->is_type("VisualInstance")); + + ERR_EXPLAIN("Multimesh not present"); + ERR_FAIL_COND(multimesh.is_null()); + + VisualInstance *vi = get_parent()->cast_to<VisualInstance>(); + ERR_EXPLAIN("Parent is not of type VisualInstance, can't be populated."); + ERR_FAIL_COND(!vi); + +#endif + DVector<Face3> faces = geometry; + ERR_EXPLAIN("Parent has no solid faces to populate."); + int facecount=faces.size(); + ERR_FAIL_COND(!facecount); + + DVector<Face3>::Read r = faces.read(); + + + + float area_accum=0; + Map<float,int> triangle_area_map; + for(int i=0;i<facecount;i++) { + + float area = r[i].get_area();; + if (area<CMP_EPSILON) + continue; + triangle_area_map[area_accum]=i; + area_accum+=area; + } + + ERR_EXPLAIN("Couldn't map area"); + ERR_FAIL_COND(triangle_area_map.size()==0); + ERR_EXPLAIN("Couldn't map area"); + ERR_FAIL_COND(area_accum==0); + + + Ref<MultiMesh> multimesh = memnew( MultiMesh ); + multimesh->set_mesh(mesh); + + int instance_count=populate_amount->get_val(); + + multimesh->set_instance_count(instance_count); + + float _tilt_random = populate_tilt_random->get_val(); + float _rotate_random = populate_rotate_random->get_val(); + float _scale_random = populate_scale_random->get_val(); + float _scale = populate_scale->get_val(); + int axis = populate_axis->get_selected(); + + Transform axis_xform; + if (axis==Vector3::AXIS_Z) { + axis_xform.rotate(Vector3(1,0,0),Math_PI*0.5); + } + if (axis==Vector3::AXIS_X) { + axis_xform.rotate(Vector3(0,0,1),Math_PI*0.5); + } + + for(int i=0;i<instance_count;i++) { + + float areapos = Math::random(0,area_accum); + + Map<float,int>::Element *E = triangle_area_map.find_closest(areapos); + ERR_FAIL_COND(!E) + int index = E->get(); + ERR_FAIL_INDEX(index,facecount); + + // ok FINALLY get face + Face3 face = r[index]; + //now compute some position inside the face... + + Vector3 pos = face.get_random_point_inside(); + Vector3 normal = face.get_plane().normal; + Vector3 op_axis = (face.vertex[0]-face.vertex[1]).normalized(); + + Transform xform; + + xform.set_look_at(pos, pos+op_axis,normal); + xform = xform * axis_xform; + + + Matrix3 post_xform; + + post_xform.rotate(xform.basis.get_axis(0),Math::random(-_tilt_random,_tilt_random)*Math_PI); + post_xform.rotate(xform.basis.get_axis(2),Math::random(-_tilt_random,_tilt_random)*Math_PI); + post_xform.rotate(xform.basis.get_axis(1),Math::random(-_rotate_random,_rotate_random)*Math_PI); + xform.basis = post_xform * xform.basis; + //xform.basis.orthonormalize(); + + + xform.basis.scale(Vector3(1,1,1)*(_scale+Math::random(-_scale_random,_scale_random))); + + + multimesh->set_instance_transform(i,xform); + multimesh->set_instance_color(i,Color(1,1,1,1)); + } + + multimesh->generate_aabb(); + + node->set_multimesh(multimesh); + +} + +void MultiMeshEditor::_browsed(const NodePath& p_path) { + + + NodePath path = node->get_path_to( get_node(p_path) ); + + if (browsing_source) + mesh_source->set_text(path); + else + surface_source->set_text(path); +} + +void MultiMeshEditor::_menu_option(int p_option) { + + + switch(p_option) { + + + case MENU_OPTION_POPULATE: { + + if (_last_pp_node!=node) { + + + surface_source->set_text(".."); + mesh_source->set_text(".."); + populate_axis->select(1); + populate_rotate_random->set_val(0); + populate_tilt_random->set_val(0); + populate_scale_random->set_val(0); + populate_scale->set_val(1); + populate_amount->set_val(128); + + _last_pp_node=node; + } + populate_dialog->popup_centered(Size2(250,395)); + + } break; + } +} + + +void MultiMeshEditor::edit(MultiMeshInstance *p_multimesh) { + + node=p_multimesh; + +} + +void MultiMeshEditor::_browse(bool p_source) { + + browsing_source=p_source; + std->get_tree()->set_marked(node,false); + std->popup_centered_ratio(); + if (p_source) + std->set_title("Select a Source Mesh:"); + else + std->set_title("Select a Target Surface:"); +} + +void MultiMeshEditor::_bind_methods() { + + ObjectTypeDB::bind_method("_menu_option",&MultiMeshEditor::_menu_option); + ObjectTypeDB::bind_method("_populate",&MultiMeshEditor::_populate); + ObjectTypeDB::bind_method("_browsed",&MultiMeshEditor::_browsed); + ObjectTypeDB::bind_method("_browse",&MultiMeshEditor::_browse); +} + +MultiMeshEditor::MultiMeshEditor() { + + + options = memnew( MenuButton ); + add_child(options); + options->set_area_as_parent_rect(); + + options->set_text("MultiMesh"); + options->get_popup()->add_item("Populate Surface"); + options->get_popup()->connect("item_pressed", this,"_menu_option"); + + populate_dialog = memnew( ConfirmationDialog ); + populate_dialog->set_title("Populate MultiMesh"); + add_child(populate_dialog); + + VBoxContainer *vbc = memnew( VBoxContainer ); + populate_dialog->add_child(vbc); + populate_dialog->set_child_rect(vbc); + + + HBoxContainer *hbc = memnew( HBoxContainer ); + + surface_source = memnew( LineEdit ); + hbc->add_child(surface_source); + surface_source->set_h_size_flags(SIZE_EXPAND_FILL); + Button *b = memnew( Button ); + hbc->add_child(b); + b->set_text(".."); + b->connect("pressed",this,"_browse",make_binds(false)); + + vbc->add_margin_child("Target Surface:",hbc); + + hbc = memnew( HBoxContainer ); + mesh_source = memnew( LineEdit ); + hbc->add_child(mesh_source); + mesh_source->set_h_size_flags(SIZE_EXPAND_FILL); + b = memnew( Button ); + hbc->add_child(b); + b->set_text(".."); + vbc->add_margin_child("Source Mesh:",hbc); + b->connect("pressed",this,"_browse",make_binds(true)); + + + populate_axis = memnew( OptionButton ); + populate_axis->add_item("X-Axis"); + populate_axis->add_item("Y-Axis"); + populate_axis->add_item("Z-Axis"); + populate_axis->select(2); + vbc->add_margin_child("Mesh Up Axis:",populate_axis); + + populate_rotate_random = memnew( HScrollBar ); + populate_rotate_random->set_max(1); + populate_rotate_random->set_step(0.01); + vbc->add_margin_child("Random Rotation:",populate_rotate_random); + + populate_tilt_random = memnew( HScrollBar ); + populate_tilt_random->set_max(1); + populate_tilt_random->set_step(0.01); + vbc->add_margin_child("Random Tilt:",populate_tilt_random); + + + populate_scale_random = memnew( SpinBox ); + populate_scale_random->set_min(0); + populate_scale_random->set_max(1); + populate_scale_random->set_val(0); + + vbc->add_margin_child("Random Scale:",populate_scale_random); + + populate_scale = memnew( SpinBox ); + populate_scale->set_min(0.001); + populate_scale->set_max(4096); + populate_scale->set_val(1); + + vbc->add_margin_child("Scale:",populate_scale); + + + populate_amount = memnew( SpinBox ); + populate_amount->set_anchor(MARGIN_RIGHT,ANCHOR_END); + populate_amount->set_begin( Point2(20,232)); + populate_amount->set_end( Point2(5,237)); + populate_amount->set_min(1); + populate_amount->set_max(65536); + populate_amount->set_val(128); + vbc->add_margin_child("Amount:",populate_amount); + + populate_dialog->get_ok()->set_text("Populate"); + + populate_dialog->get_ok()->connect("pressed", this,"_populate"); + std = memnew( SceneTreeDialog ); + populate_dialog->add_child(std); + std->connect("selected",this,"_browsed"); + + _last_pp_node=NULL; + //options->set_anchor(MARGIN_LEFT,Control::ANCHOR_END); + //options->set_anchor(MARGIN_RIGHT,Control::ANCHOR_END); + err_dialog = memnew( AcceptDialog ); + add_child(err_dialog); +} + + +void MultiMeshEditorPlugin::edit(Object *p_object) { + + multimesh_editor->edit(p_object->cast_to<MultiMeshInstance>()); +} + +bool MultiMeshEditorPlugin::handles(Object *p_object) const { + + return p_object->is_type("MultiMeshInstance"); +} + +void MultiMeshEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + multimesh_editor->show(); + } else { + + multimesh_editor->hide(); + multimesh_editor->edit(NULL); + } + +} + +MultiMeshEditorPlugin::MultiMeshEditorPlugin(EditorNode *p_node) { + + editor=p_node; + multimesh_editor = memnew( MultiMeshEditor ); + editor->get_viewport()->add_child(multimesh_editor); + +// multimesh_editor->set_anchor(MARGIN_LEFT,Control::ANCHOR_END); +// multimesh_editor->set_anchor(MARGIN_RIGHT,Control::ANCHOR_END); + multimesh_editor->set_margin(MARGIN_LEFT,253); + multimesh_editor->set_margin(MARGIN_RIGHT,310); + multimesh_editor->set_margin(MARGIN_TOP,0); + multimesh_editor->set_margin(MARGIN_BOTTOM,10); + + + + multimesh_editor->hide(); +} + + +MultiMeshEditorPlugin::~MultiMeshEditorPlugin() +{ +} + + diff --git a/tools/editor/plugins/multimesh_editor_plugin.h b/tools/editor/plugins/multimesh_editor_plugin.h new file mode 100644 index 000000000..a4d5f9bd3 --- /dev/null +++ b/tools/editor/plugins/multimesh_editor_plugin.h @@ -0,0 +1,107 @@ +/*************************************************************************/ +/* multimesh_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef MULTIMESH_EDITOR_PLUGIN_H +#define MULTIMESH_EDITOR_PLUGIN_H + +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" +#include "scene/3d/multimesh_instance.h" +#include "scene/gui/spin_box.h" + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + +class MultiMeshEditor : public Control { + + OBJ_TYPE(MultiMeshEditor, Control ); + + + AcceptDialog *err_dialog; + + MultiMeshInstance *_last_pp_node; + bool browsing_source; + + Panel *panel; + MenuButton * options; + MultiMeshInstance *node; + + LineEdit *surface_source; + LineEdit *mesh_source; + + SceneTreeDialog *std; + + ConfirmationDialog *populate_dialog; + OptionButton *populate_axis; + HScrollBar *populate_rotate_random; + HScrollBar *populate_tilt_random; + SpinBox *populate_scale_random; + SpinBox *populate_scale; + SpinBox *populate_amount; + + enum Menu { + + MENU_OPTION_POPULATE + }; + + void _browsed(const NodePath& p_path); + void _menu_option(int); + void _populate(); + void _browse(bool p_source); + +protected: + void _node_removed(Node *p_node); + static void _bind_methods(); +public: + + void edit(MultiMeshInstance *p_multimesh); + MultiMeshEditor(); +}; + +class MultiMeshEditorPlugin : public EditorPlugin { + + OBJ_TYPE( MultiMeshEditorPlugin, EditorPlugin ); + + MultiMeshEditor *multimesh_editor; + EditorNode *editor; + +public: + + virtual String get_name() const { return "MultiMesh"; } + 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); + + MultiMeshEditorPlugin(EditorNode *p_node); + ~MultiMeshEditorPlugin(); + +}; + +#endif // MULTIMESH_EDITOR_PLUGIN_H diff --git a/tools/editor/plugins/particles_2d_editor_plugin.cpp b/tools/editor/plugins/particles_2d_editor_plugin.cpp new file mode 100644 index 000000000..b23847231 --- /dev/null +++ b/tools/editor/plugins/particles_2d_editor_plugin.cpp @@ -0,0 +1,198 @@ +/*************************************************************************/ +/* particles_2d_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "particles_2d_editor_plugin.h"
+#include "canvas_item_editor_plugin.h"
+#include "io/image_loader.h"
+
+
+void Particles2DEditorPlugin::edit(Object *p_object) {
+
+ if (p_object) {
+ particles=p_object->cast_to<Particles2D>();
+ } else {
+ particles=NULL;
+ }
+}
+
+bool Particles2DEditorPlugin::handles(Object *p_object) const {
+
+ return p_object->is_type("Particles2D");
+}
+
+void Particles2DEditorPlugin::make_visible(bool p_visible) {
+
+ if (p_visible) {
+
+ sep->show();
+ menu->show();
+ } else {
+
+ menu->hide();
+ sep->hide();
+ }
+
+}
+
+void Particles2DEditorPlugin::_file_selected(const String& p_file) {
+
+ print_line("file: "+p_file);
+
+ int epc=epoints->get_val();
+
+ Image img;
+ Error err = ImageLoader::load_image(p_file,&img);
+ ERR_EXPLAIN("Error loading image: "+p_file);
+ ERR_FAIL_COND(err!=OK);
+
+ img.convert(Image::FORMAT_GRAYSCALE_ALPHA);
+ ERR_FAIL_COND(img.get_format()!=Image::FORMAT_GRAYSCALE_ALPHA);
+ Size2i s = Size2(img.get_width(),img.get_height());
+ ERR_FAIL_COND(s.width==0 || s.height==0);
+
+ DVector<uint8_t> data = img.get_data();
+ DVector<uint8_t>::Read r = data.read();
+
+ Vector<Point2i> valid_positions;
+ valid_positions.resize(s.width*s.height);
+ int vpc=0;
+
+
+ for(int i=0;i<s.width*s.height;i++) {
+
+ uint8_t a = r[i*2+1];
+ if (a>128) {
+ valid_positions[vpc++]=Point2i(i%s.width,i/s.width);
+ }
+ }
+
+ valid_positions.resize(vpc);
+
+ ERR_EXPLAIN("No pixels with transparency > 128 in image..");
+ ERR_FAIL_COND(valid_positions.size()==0);
+
+ DVector<Point2> epoints;
+ epoints.resize(epc);
+ DVector<Point2>::Write w = epoints.write();
+
+ Size2 extents = Size2(img.get_width()*0.5,img.get_height()*0.5);
+
+ for(int i=0;i<epc;i++) {
+
+ Point2 p = valid_positions[Math::rand()%vpc];
+ p-=s/2;
+ w[i]=p/extents;
+ }
+
+ w = DVector<Point2>::Write();
+
+ undo_redo->create_action("Set Emission Mask");
+ undo_redo->add_do_method(particles,"set_emission_points",epoints);
+ undo_redo->add_do_method(particles,"set_emission_half_extents",extents);
+ undo_redo->add_undo_method(particles,"set_emission_points",particles->get_emission_points());
+ undo_redo->add_undo_method(particles,"set_emission_half_extents",particles->get_emission_half_extents());
+ undo_redo->commit_action();
+
+}
+
+void Particles2DEditorPlugin::_menu_callback(int p_idx) {
+
+ switch(p_idx) {
+ case MENU_LOAD_EMISSION_MASK: {
+
+
+ file->popup_centered_ratio();
+
+ } break;
+ case MENU_CLEAR_EMISSION_MASK: {
+
+ undo_redo->create_action("Clear Emission Mask");
+ undo_redo->add_do_method(particles,"set_emission_points",DVector<Vector2>());
+ undo_redo->add_undo_method(particles,"set_emission_points",particles->get_emission_points());
+ undo_redo->commit_action();
+ } break;
+ }
+
+}
+
+
+void Particles2DEditorPlugin::_notification(int p_what) {
+
+ if (p_what==NOTIFICATION_ENTER_SCENE) {
+
+ menu->get_popup()->connect("item_pressed",this,"_menu_callback");
+ file->connect("file_selected",this,"_file_selected");
+ }
+}
+
+void Particles2DEditorPlugin::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("_menu_callback"),&Particles2DEditorPlugin::_menu_callback);
+ ObjectTypeDB::bind_method(_MD("_file_selected"),&Particles2DEditorPlugin::_file_selected);
+}
+
+
+
+Particles2DEditorPlugin::Particles2DEditorPlugin(EditorNode *p_node) {
+
+ particles=NULL;
+ editor=p_node;
+ undo_redo=editor->get_undo_redo();
+ sep = memnew( VSeparator );
+ CanvasItemEditor::get_singleton()->add_control_to_menu_panel(sep);
+ sep->hide();
+
+ menu = memnew( MenuButton );
+ menu->get_popup()->add_item("Load Emission Mask",MENU_LOAD_EMISSION_MASK);
+ menu->get_popup()->add_item("Clear Emission Mask",MENU_CLEAR_EMISSION_MASK);
+ menu->set_text("Particles");
+
+ file = memnew(FileDialog);
+ add_child(file);
+ List<String> ext;
+ ImageLoader::get_recognized_extensions(&ext);
+ for(List<String>::Element *E=ext.front();E;E=E->next()) {
+ file->add_filter("*."+E->get()+"; "+E->get().to_upper());
+ }
+ file->set_mode(FileDialog::MODE_OPEN_FILE);
+ CanvasItemEditor::get_singleton()->add_control_to_menu_panel(menu);
+ epoints = memnew( SpinBox );
+ epoints->set_min(1);
+ epoints->set_max(8192);
+ epoints->set_step(1);
+ epoints->set_val(512);
+ file->get_vbox()->add_margin_child("Generated Point Count:",epoints);
+ menu->hide();
+
+}
+
+
+Particles2DEditorPlugin::~Particles2DEditorPlugin()
+{
+}
+
diff --git a/tools/editor/plugins/particles_2d_editor_plugin.h b/tools/editor/plugins/particles_2d_editor_plugin.h new file mode 100644 index 000000000..b824774d0 --- /dev/null +++ b/tools/editor/plugins/particles_2d_editor_plugin.h @@ -0,0 +1,82 @@ +/*************************************************************************/ +/* particles_2d_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef PARTICLES_2D_EDITOR_PLUGIN_H
+#define PARTICLES_2D_EDITOR_PLUGIN_H
+
+#include "tools/editor/editor_plugin.h"
+#include "tools/editor/editor_node.h"
+#include "scene/2d/collision_polygon_2d.h"
+
+#include "scene/gui/separator.h"
+#include "scene/gui/file_dialog.h"
+#include "scene/2d/particles_2d.h"
+
+class Particles2DEditorPlugin : public EditorPlugin {
+
+ OBJ_TYPE( Particles2DEditorPlugin, EditorPlugin );
+
+ enum {
+
+ MENU_LOAD_EMISSION_MASK,
+ MENU_CLEAR_EMISSION_MASK
+ };
+
+
+ FileDialog *file;
+ EditorNode *editor;
+
+ MenuButton *menu;
+
+ VSeparator *sep;
+ Particles2D *particles;
+ SpinBox *epoints;
+
+ UndoRedo *undo_redo;
+ void _file_selected(const String& p_file);
+ void _menu_callback(int p_idx);
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+
+
+ virtual String get_name() const { return "Particles2D"; }
+ 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);
+
+ Particles2DEditorPlugin(EditorNode *p_node);
+ ~Particles2DEditorPlugin();
+
+};
+
+
+#endif // PARTICLES_2D_EDITOR_PLUGIN_H
diff --git a/tools/editor/plugins/particles_editor_plugin.cpp b/tools/editor/plugins/particles_editor_plugin.cpp new file mode 100644 index 000000000..38781d93b --- /dev/null +++ b/tools/editor/plugins/particles_editor_plugin.cpp @@ -0,0 +1,462 @@ +/*************************************************************************/ +/* particles_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "particles_editor_plugin.h" +#include "io/resource_loader.h" +#include "servers/visual/particle_system_sw.h" + + +void ParticlesEditor::_node_removed(Node *p_node) { + + if(p_node==node) { + node=NULL; + hide(); + } + +} + + +void ParticlesEditor::_resource_seleted(const String& p_res) { + + //print_line("selected resource path: "+p_res); +} + +void ParticlesEditor::_node_selected(const NodePath& p_path){ + + + Node *sel = get_node(p_path); + if (!sel) + return; + + VisualInstance *vi = sel->cast_to<VisualInstance>(); + if (!vi) { + + err_dialog->set_text("Node does not contain geometry."); + err_dialog->popup_centered(Size2(300,100)); + return; + } + + geometry = vi->get_faces(VisualInstance::FACES_SOLID); + + if (geometry.size()==0) { + + err_dialog->set_text("Node does not contain geometry (faces)."); + err_dialog->popup_centered(Size2(300,100)); + return; + + } + + Transform geom_xform = node->get_global_transform().affine_inverse() * vi->get_global_transform(); + + int gc = geometry.size(); + DVector<Face3>::Write w = geometry.write(); + + + for(int i=0;i<gc;i++) { + for(int j=0;j<3;j++) { + w[i].vertex[j] = geom_xform.xform( w[i].vertex[j] ); + } + } + + + w = DVector<Face3>::Write(); + + emission_dialog->popup_centered(Size2(300,130)); +} + + +/* + +void ParticlesEditor::_populate() { + + if(!node) + return; + + + if (node->get_particles().is_null()) + return; + + node->get_particles()->set_instance_count(populate_amount->get_text().to_int()); + node->populate_parent(populate_rotate_random->get_val(),populate_tilt_random->get_val(),populate_scale_random->get_text().to_double(),populate_scale->get_text().to_double()); + +} +*/ + +void ParticlesEditor::_notification(int p_notification) { + + if (p_notification==NOTIFICATION_ENTER_SCENE) { + + } +} + + +void ParticlesEditor::_menu_option(int p_option) { + + + switch(p_option) { + + case MENU_OPTION_GENERATE_AABB: { + + Transform globalizer = node->get_global_transform(); + ParticleSystemSW pssw; + for(int i=0;i<VS::PARTICLE_VAR_MAX;i++) { + + pssw.particle_vars[i]=node->get_variable((Particles::Variable)i); + pssw.particle_randomness[i]=node->get_randomness((Particles::Variable)i); + } + + pssw.emission_half_extents=node->get_emission_half_extents(); + pssw.emission_points=node->get_emission_points(); + pssw.emission_base_velocity=node->get_emission_base_velocity(); + pssw.amount=node->get_amount(); + pssw.gravity_normal=node->get_gravity_normal(); + pssw.emitting=true; + pssw.height_from_velocity=node->has_height_from_velocity(); + pssw.color_phase_count=1; + + + ParticleSystemProcessSW pp; + float delta=0.01; + float lifetime=pssw.particle_vars[VS::PARTICLE_LIFETIME]; + + + Transform localizer = globalizer.affine_inverse(); + AABB aabb; + for(float t=0;t<lifetime;t+=delta) { + + pp.process(&pssw,globalizer,delta); + for(int i=0;i<pp.particle_data.size();i++) { + + Vector3 p = localizer.xform(pp.particle_data[i].pos); + + if (t==0 && i==0) + aabb.pos=p; + else + aabb.expand_to(p); + } + } + + aabb.grow_by( aabb.get_longest_axis_size()*0.2); + + node->set_visibility_aabb(aabb); + + + } break; + case MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_MESH: { + + + emission_file_dialog->popup_centered_ratio(); + + } break; + + case MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE: { +/* + Node *root = get_scene()->get_root_node(); + ERR_FAIL_COND(!root); + EditorNode *en = root->cast_to<EditorNode>(); + ERR_FAIL_COND(!en); + Node * node = en->get_edited_scene(); +*/ + emission_tree_dialog->popup_centered_ratio(); + + } break; + } +} + + +void ParticlesEditor::edit(Particles *p_particles) { + + node=p_particles; + +} + +void ParticlesEditor::_generate_emission_points() { + + /// hacer codigo aca + DVector<Vector3> points; + + if (emission_fill->get_selected()==0) { + + float area_accum=0; + Map<float,int> triangle_area_map; + print_line("geometry size: "+itos(geometry.size())); + + for(int i=0;i<geometry.size();i++) { + + float area = geometry[i].get_area();; + if (area<CMP_EPSILON) + continue; + triangle_area_map[area_accum]=i; + area_accum+=area; + } + + if (!triangle_area_map.size() || area_accum==0) { + + err_dialog->set_text("Faces contain no area!"); + err_dialog->popup_centered(Size2(300,100)); + return; + } + + int emissor_count=emission_amount->get_val(); + + for(int i=0;i<emissor_count;i++) { + + float areapos = Math::random(0,area_accum); + + Map<float,int>::Element *E = triangle_area_map.find_closest(areapos); + ERR_FAIL_COND(!E) + int index = E->get(); + ERR_FAIL_INDEX(index,geometry.size()); + + // ok FINALLY get face + Face3 face = geometry[index]; + //now compute some position inside the face... + + Vector3 pos = face.get_random_point_inside(); + + points.push_back(pos); + } + } else { + + int gcount = geometry.size(); + + if (gcount==0) { + + err_dialog->set_text("No Faces!"); + err_dialog->popup_centered(Size2(300,100)); + return; + } + + DVector<Face3>::Read r = geometry.read(); + + AABB aabb; + + for(int i=0;i<gcount;i++) { + + for(int j=0;j<3;j++) { + + if (i==0 && j==0) + aabb.pos=r[i].vertex[j]; + else + aabb.expand_to(r[i].vertex[j]); + } + } + + int emissor_count=emission_amount->get_val(); + + for(int i=0;i<emissor_count;i++) { + + int attempts=5; + + for(int j=0;j<attempts;j++) { + + Vector3 dir; + dir[Math::rand()%3]=1.0; + Vector3 ofs = Vector3(1,1,1)-dir; + ofs=(Vector3(1,1,1)-dir)*Vector3(Math::randf(),Math::randf(),Math::randf())*aabb.size; + ofs+=aabb.pos; + + Vector3 dirv = -dir; + Vector3 ofsv = ofs + aabb.size * dir; + + //space it a little + ofs -= dir; + ofsv += dir; + + float max=-1e7,min=1e7; + + for(int k=0;k<gcount;k++) { + + const Face3& f3 = r[k]; + + Vector3 res; + if (f3.intersects_segment(ofs,ofsv,&res)) { + + res-=ofs; + float d = dir.dot(res); + + if (d<min) + min=d; + if (d>max) + max=d; + + } + } + + + if (max<min) + continue; //lost attempt + + float val = min + (max-min)*Math::randf(); + + Vector3 point = ofs + dir * val; + + points.push_back(point); + break; + } + } + } + + //print_line("point count: "+itos(points.size())); + node->set_emission_points(points); + +} + +void ParticlesEditor::_bind_methods() { + + ObjectTypeDB::bind_method("_menu_option",&ParticlesEditor::_menu_option); + ObjectTypeDB::bind_method("_resource_seleted",&ParticlesEditor::_resource_seleted); + ObjectTypeDB::bind_method("_node_selected",&ParticlesEditor::_node_selected); + ObjectTypeDB::bind_method("_generate_emission_points",&ParticlesEditor::_generate_emission_points); + + //ObjectTypeDB::bind_method("_populate",&ParticlesEditor::_populate); + +} + +ParticlesEditor::ParticlesEditor() { + + options = memnew( MenuButton ); + add_child(options); + options->set_area_as_parent_rect(); + + options->set_text("Particles"); + options->get_popup()->add_item("Generate AABB",MENU_OPTION_GENERATE_AABB); + options->get_popup()->add_separator(); + options->get_popup()->add_item("Create Emitter From Mesh",MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_MESH); + options->get_popup()->add_item("Create Emitter From Node",MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE); + options->get_popup()->add_item("Clear Emitter",MENU_OPTION_CLEAR_EMISSION_VOLUME); + + options->get_popup()->connect("item_pressed", this,"_menu_option"); + + emission_dialog = memnew( ConfirmationDialog ); + emission_dialog->set_title("Create Emitter"); + add_child(emission_dialog); + Label *l = memnew(Label); + l->set_pos(Point2(5,5)); + l->set_text("Emission Positions:"); + emission_dialog->add_child(l); + + + emission_amount = memnew( SpinBox ); + emission_amount->set_anchor(MARGIN_RIGHT,ANCHOR_END); + emission_amount->set_begin( Point2(20,23)); + emission_amount->set_end( Point2(5,25)); + emission_amount->set_min(1); + emission_amount->set_max(65536); + emission_amount->set_val(512); + emission_dialog->add_child(emission_amount); + emission_dialog->get_ok()->set_text("Create"); + emission_dialog->connect("confirmed",this,"_generate_emission_points"); + + l = memnew(Label); + l->set_pos(Point2(5,50)); + l->set_text("Emission Fill:"); + emission_dialog->add_child(l); + + emission_fill = memnew( OptionButton ); + emission_fill->set_anchor(MARGIN_RIGHT,ANCHOR_END); + emission_fill->set_begin( Point2(20,70)); + emission_fill->set_end( Point2(5,75)); + emission_fill->add_item("Surface"); + emission_fill->add_item("Volume"); + emission_dialog->add_child(emission_fill); + + err_dialog = memnew( ConfirmationDialog ); + //err_dialog->get_cancel()->hide(); + add_child(err_dialog); + + + emission_file_dialog = memnew( FileDialog ); + add_child(emission_file_dialog); + emission_file_dialog->connect("file_selected",this,"_resource_seleted"); + emission_tree_dialog = memnew( SceneTreeDialog ); + add_child(emission_tree_dialog); + emission_tree_dialog->connect("selected",this,"_node_selected"); + + List<String> extensions; + ResourceLoader::get_recognized_extensions_for_type("Mesh",&extensions); + + emission_file_dialog->clear_filters(); + for(int i=0;i<extensions.size();i++) { + + emission_file_dialog->add_filter("*."+extensions[i]+" ; "+extensions[i].to_upper()); + } + + emission_file_dialog->set_mode(FileDialog::MODE_OPEN_FILE); + + //options->set_anchor(MARGIN_LEFT,Control::ANCHOR_END); + //options->set_anchor(MARGIN_RIGHT,Control::ANCHOR_END); + +} + + +void ParticlesEditorPlugin::edit(Object *p_object) { + + particles_editor->edit(p_object->cast_to<Particles>()); +} + +bool ParticlesEditorPlugin::handles(Object *p_object) const { + + return p_object->is_type("Particles"); +} + +void ParticlesEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + particles_editor->show(); + } else { + + particles_editor->hide(); + particles_editor->edit(NULL); + } + +} + +ParticlesEditorPlugin::ParticlesEditorPlugin(EditorNode *p_node) { + + editor=p_node; + particles_editor = memnew( ParticlesEditor ); + editor->get_viewport()->add_child(particles_editor); + +// particles_editor->set_anchor(MARGIN_LEFT,Control::ANCHOR_END); +// particles_editor->set_anchor(MARGIN_RIGHT,Control::ANCHOR_END); + particles_editor->set_margin(MARGIN_LEFT,253); + particles_editor->set_margin(MARGIN_RIGHT,280); + particles_editor->set_margin(MARGIN_TOP,0); + particles_editor->set_margin(MARGIN_BOTTOM,10); + + + particles_editor->hide(); +} + + +ParticlesEditorPlugin::~ParticlesEditorPlugin() +{ +} + + diff --git a/tools/editor/plugins/particles_editor_plugin.h b/tools/editor/plugins/particles_editor_plugin.h new file mode 100644 index 000000000..ba655c9b3 --- /dev/null +++ b/tools/editor/plugins/particles_editor_plugin.h @@ -0,0 +1,112 @@ +/*************************************************************************/ +/* particles_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef PARTICLES_EDITOR_PLUGIN_H +#define PARTICLES_EDITOR_PLUGIN_H + +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" +#include "scene/3d/particles.h" +#include "scene/gui/spin_box.h" + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + +class ParticlesEditor : public Control { + + OBJ_TYPE(ParticlesEditor, Control ); + + Panel *panel; + MenuButton * options; + Particles *node; + + + FileDialog *emission_file_dialog; + SceneTreeDialog *emission_tree_dialog; + + ConfirmationDialog *err_dialog; + + ConfirmationDialog *emission_dialog; + SpinBox *emission_amount; + OptionButton *emission_fill; + + + + + enum Menu { + + MENU_OPTION_GENERATE_AABB, + MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE, + MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_MESH, + MENU_OPTION_CLEAR_EMISSION_VOLUME, + + }; + + DVector<Face3> geometry; + + void _generate_emission_points(); + void _resource_seleted(const String& p_res); + void _node_selected(const NodePath& p_path); + + void _menu_option(int); + + void _populate(); + +protected: + + void _notification(int p_notification); + void _node_removed(Node *p_node); + static void _bind_methods(); +public: + + void edit(Particles *p_particles); + ParticlesEditor(); +}; + +class ParticlesEditorPlugin : public EditorPlugin { + + OBJ_TYPE( ParticlesEditorPlugin, EditorPlugin ); + + ParticlesEditor *particles_editor; + EditorNode *editor; + +public: + + virtual String get_name() const { return "Particles"; } + 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); + + ParticlesEditorPlugin(EditorNode *p_node); + ~ParticlesEditorPlugin(); + +}; + +#endif // PARTICLES_EDITOR_PLUGIN_H diff --git a/tools/editor/plugins/path_2d_editor_plugin.cpp b/tools/editor/plugins/path_2d_editor_plugin.cpp new file mode 100644 index 000000000..e83fed5ce --- /dev/null +++ b/tools/editor/plugins/path_2d_editor_plugin.cpp @@ -0,0 +1,604 @@ +/*************************************************************************/ +/* path_2d_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "path_2d_editor_plugin.h" + +#include "canvas_item_editor_plugin.h" +#include "os/file_access.h" +#include "tools/editor/editor_settings.h" + +void Path2DEditor::_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")); + //set_pressed_button(button_edit); + //button_edit->set_pressed(true); + + + } break; + case NOTIFICATION_FIXED_PROCESS: { + + + } break; + } + +} +void Path2DEditor::_node_removed(Node *p_node) { + + if(p_node==node) { + node=NULL; + hide(); + } + +} + + +Vector2 Path2DEditor::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; + } +} + +bool Path2DEditor::forward_input_event(const InputEvent& p_event) { + + if (!node) + return false; + + if (!node->get_curve().is_valid()) + 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 = xform.affine_inverse().xform(gpoint); + + //first check if a point is to be added (segment split) + real_t grab_treshold=EDITOR_DEF("poly_editor/point_grab_radius",8); + + + + // Test move point!! + + if ( mb.pressed && mb.button_index==BUTTON_LEFT ) { + + Ref<Curve2D> curve = node->get_curve(); + + for(int i=0;i<curve->get_point_count();i++) { + + bool pointunder=false; + + { + Point2 p = xform.xform( curve->get_point_pos(i) ); + if (gpoint.distance_to(p) < grab_treshold ) { + + if (!mb.mod.control) { + + action=ACTION_MOVING_POINT; + action_point=i; + moving_from=curve->get_point_pos(i); + moving_screen_from=gpoint; + return true; + } else + pointunder=true; + } + } + + if (i<(curve->get_point_count()-1)) { + Point2 p = xform.xform( curve->get_point_pos(i)+curve->get_point_out(i) ); + if (gpoint.distance_to(p) < grab_treshold ) { + + action=ACTION_MOVING_OUT; + action_point=i; + moving_from=curve->get_point_out(i); + moving_screen_from=gpoint; + return true; + } + } + + if (i>0) { + Point2 p = xform.xform( curve->get_point_pos(i)+curve->get_point_in(i) ); + if (gpoint.distance_to(p) < grab_treshold ) { + + action=ACTION_MOVING_IN; + action_point=i; + moving_from=curve->get_point_in(i); + moving_screen_from=gpoint; + return true; + } + } + + if (pointunder) + return true; + + } + + } + + // Test add point in empty space! + + if ( mb.pressed && mb.mod.control && mb.button_index==BUTTON_LEFT ) { + + Ref<Curve2D> curve = node->get_curve(); + + undo_redo->create_action("Add Point to Curve"); + undo_redo->add_do_method(curve.ptr(),"add_point",cpoint); + undo_redo->add_undo_method(curve.ptr(),"remove_point",curve->get_point_count()); + undo_redo->add_do_method(canvas_item_editor,"update"); + undo_redo->add_undo_method(canvas_item_editor,"update"); + undo_redo->commit_action(); + + action=ACTION_MOVING_POINT; + action_point=curve->get_point_count()-1; + moving_from=curve->get_point_pos(action_point); + moving_screen_from=gpoint; + + return true; + } + + if ( !mb.pressed && mb.button_index==BUTTON_LEFT && action!=ACTION_NONE) { + + + Ref<Curve2D> curve = node->get_curve(); + + Vector2 new_pos = moving_from + xform.basis_xform( gpoint - moving_screen_from ); + switch(action) { + + case ACTION_MOVING_POINT: { + + + undo_redo->create_action("Move Point in Curve"); + undo_redo->add_do_method(curve.ptr(),"set_point_pos",action_point,new_pos); + undo_redo->add_undo_method(curve.ptr(),"set_point_pos",action_point,moving_from); + undo_redo->add_do_method(canvas_item_editor,"update"); + undo_redo->add_undo_method(canvas_item_editor,"update"); + undo_redo->commit_action(); + + } break; + case ACTION_MOVING_IN: { + + undo_redo->create_action("Move In-Control in Curve"); + undo_redo->add_do_method(curve.ptr(),"set_point_in",action_point,new_pos); + undo_redo->add_undo_method(curve.ptr(),"set_point_in",action_point,moving_from); + undo_redo->add_do_method(canvas_item_editor,"update"); + undo_redo->add_undo_method(canvas_item_editor,"update"); + undo_redo->commit_action(); + + } break; + case ACTION_MOVING_OUT: { + + undo_redo->create_action("Move Out-Control in Curve"); + undo_redo->add_do_method(curve.ptr(),"set_point_out",action_point,new_pos); + undo_redo->add_undo_method(curve.ptr(),"set_point_out",action_point,moving_from); + undo_redo->add_do_method(canvas_item_editor,"update"); + undo_redo->add_undo_method(canvas_item_editor,"update"); + undo_redo->commit_action(); + + } break; + + } + + action=ACTION_NONE; + + return true; + } + + +#if 0 + switch(mode) { + + + case MODE_CREATE: { + + if (mb.button_index==BUTTON_LEFT && mb.pressed) { + + + if (!wip_active) { + + wip.clear(); + wip.push_back( snap_point(cpoint) ); + wip_active=true; + edited_point_pos=snap_point(cpoint); + canvas_item_editor->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( snap_point(cpoint) ); + edited_point=wip.size(); + canvas_item_editor->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) { + + + if (poly.size() < 3) { + + undo_redo->create_action("Edit Poly"); + undo_redo->add_undo_method(node,"set_polygon",poly); + poly.push_back(cpoint); + undo_redo->add_do_method(node,"set_polygon",poly); + undo_redo->add_do_method(canvas_item_editor,"update"); + undo_redo->add_undo_method(canvas_item_editor,"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,snap_point(xform.affine_inverse().xform(closest_pos))); + edited_point=closest_idx+1; + edited_point_pos=snap_point(xform.affine_inverse().xform(closest_pos)); + node->set_polygon(poly); + canvas_item_editor->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->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,"set_polygon",poly); + undo_redo->add_undo_method(node,"set_polygon",pre_move_edit); + undo_redo->add_do_method(canvas_item_editor,"update"); + undo_redo->add_undo_method(canvas_item_editor,"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,"set_polygon",poly); + poly.remove(closest_idx); + undo_redo->add_do_method(node,"set_polygon",poly); + undo_redo->add_do_method(canvas_item_editor,"update"); + undo_redo->add_undo_method(canvas_item_editor,"update"); + undo_redo->commit_action(); + return true; + } + + } + + + + } break; + } + + +#endif + } break; + case InputEvent::MOUSE_MOTION: { + + const InputEventMouseMotion &mm=p_event.mouse_motion; + + + if ( action!=ACTION_NONE) { + + Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); + Vector2 gpoint = Point2(mm.x,mm.y); + + Ref<Curve2D> curve = node->get_curve(); + + + Vector2 new_pos = moving_from + xform.basis_xform( gpoint - moving_screen_from ); + + switch(action) { + + case ACTION_MOVING_POINT: { + + curve->set_point_pos(action_point,new_pos); + } break; + case ACTION_MOVING_IN: { + + curve->set_point_in(action_point,new_pos); + + } break; + case ACTION_MOVING_OUT: { + + curve->set_point_out(action_point,new_pos); + + } break; + } + + + canvas_item_editor->update(); + return true; + } + +#if 0 + if (edited_point!=-1 && (wip_active || mm.button_mask&BUTTON_MASK_LEFT)) { + + + Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); + + Vector2 gpoint = Point2(mm.x,mm.y); + edited_point_pos = snap_point(xform.affine_inverse().xform(gpoint)); + canvas_item_editor->update(); + + } +#endif + } break; + } + + return false; +} +void Path2DEditor::_canvas_draw() { + + if (!node) + return ; + + if (!node->get_curve().is_valid()) + return ; + + Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); + Ref<Texture> handle= get_icon("EditorHandle","EditorIcons"); + + Ref<Curve2D> curve = node->get_curve(); + + int len = curve->get_point_count(); + RID ci = canvas_item_editor->get_canvas_item(); + + for(int i=0;i<len;i++) { + + + Vector2 point = xform.xform(curve->get_point_pos(i)); + handle->draw(ci,point-handle->get_size()*0.5,Color(1,1,1,0.3)); + + if (i<len-1) { + Vector2 pointout = xform.xform(curve->get_point_pos(i)+curve->get_point_out(i)); + canvas_item_editor->draw_line(point,pointout,Color(0.5,0.5,1.0,0.8),1.0); + handle->draw(ci,pointout-handle->get_size()*0.5,Color(1,0.5,1,0.3)); + } + + if (i>0) { + Vector2 pointin = xform.xform(curve->get_point_pos(i)+curve->get_point_in(i)); + canvas_item_editor->draw_line(point,pointin,Color(0.5,0.5,1.0,0.8),1.0); + handle->draw(ci,pointin-handle->get_size()*0.5,Color(1,0.5,1,0.3)); + } + + } + +} + + + +void Path2DEditor::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<Path2D>(); + if (!canvas_item_editor->is_connected("draw",this,"_canvas_draw")) + canvas_item_editor->connect("draw",this,"_canvas_draw"); + + + } else { + node=NULL; + if (canvas_item_editor->is_connected("draw",this,"_canvas_draw")) + canvas_item_editor->disconnect("draw",this,"_canvas_draw"); + + } + +} + +void Path2DEditor::_bind_methods() { + + //ObjectTypeDB::bind_method(_MD("_menu_option"),&Path2DEditor::_menu_option); + ObjectTypeDB::bind_method(_MD("_canvas_draw"),&Path2DEditor::_canvas_draw); + +} + +Path2DEditor::Path2DEditor(EditorNode *p_editor) { + + canvas_item_editor=NULL; + editor=p_editor; + undo_redo = editor->get_undo_redo(); + + + action=ACTION_NONE; +#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 + + + +} + + +void Path2DEditorPlugin::edit(Object *p_object) { + + collision_polygon_editor->edit(p_object->cast_to<Node>()); +} + +bool Path2DEditorPlugin::handles(Object *p_object) const { + + return p_object->is_type("Path2D"); +} + +void Path2DEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + collision_polygon_editor->show(); + } else { + + collision_polygon_editor->hide(); + collision_polygon_editor->edit(NULL); + } + +} + +Path2DEditorPlugin::Path2DEditorPlugin(EditorNode *p_node) { + + editor=p_node; + collision_polygon_editor = memnew( Path2DEditor(p_node) ); + editor->get_viewport()->add_child(collision_polygon_editor); + + collision_polygon_editor->set_margin(MARGIN_LEFT,200); + collision_polygon_editor->set_margin(MARGIN_RIGHT,230); + collision_polygon_editor->set_margin(MARGIN_TOP,0); + collision_polygon_editor->set_margin(MARGIN_BOTTOM,10); + + + collision_polygon_editor->hide(); + + + +} + + +Path2DEditorPlugin::~Path2DEditorPlugin() +{ +} + diff --git a/tools/editor/plugins/path_2d_editor_plugin.h b/tools/editor/plugins/path_2d_editor_plugin.h new file mode 100644 index 000000000..3f917f29d --- /dev/null +++ b/tools/editor/plugins/path_2d_editor_plugin.h @@ -0,0 +1,107 @@ +/*************************************************************************/ +/* path_2d_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef PATH_2D_EDITOR_PLUGIN_H +#define PATH_2D_EDITOR_PLUGIN_H + +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" +#include "scene/2d/path_2d.h" +#include "scene/gui/tool_button.h" +#include "scene/gui/button_group.h" + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ +class CanvasItemEditor; + +class Path2DEditor : public ButtonGroup { + + OBJ_TYPE(Path2DEditor, ButtonGroup ); + + UndoRedo *undo_redo; + + CanvasItemEditor *canvas_item_editor; + EditorNode *editor; + Panel *panel; + Path2D *node; + + enum Action { + + ACTION_NONE, + ACTION_MOVING_POINT, + ACTION_MOVING_IN, + ACTION_MOVING_OUT, + }; + + + Action action; + int action_point; + Point2 moving_from; + Point2 moving_screen_from; + + + void _canvas_draw(); + +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); + Path2DEditor(EditorNode *p_editor); +}; + +class Path2DEditorPlugin : public EditorPlugin { + + OBJ_TYPE( Path2DEditorPlugin, EditorPlugin ); + + Path2DEditor *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 "Path2D"; } + 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); + + Path2DEditorPlugin(EditorNode *p_node); + ~Path2DEditorPlugin(); + +}; + + + +#endif // PATH_2D_EDITOR_PLUGIN_H diff --git a/tools/editor/plugins/path_editor_plugin.cpp b/tools/editor/plugins/path_editor_plugin.cpp new file mode 100644 index 000000000..1c910e543 --- /dev/null +++ b/tools/editor/plugins/path_editor_plugin.cpp @@ -0,0 +1,598 @@ +/*************************************************************************/ +/* path_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "path_editor_plugin.h"
+#include "spatial_editor_plugin.h"
+#include "scene/resources/curve.h"
+#include "os/keyboard.h"
+
+String PathSpatialGizmo::get_handle_name(int p_idx) const {
+
+ Ref<Curve3D> c = path->get_curve();
+ if (c.is_null())
+ return "";
+
+ if (p_idx<c->get_point_count()) {
+
+ return "Curve Point #"+itos(p_idx);
+ }
+
+ p_idx=p_idx-c->get_point_count()+1;
+
+ int idx=p_idx/2;
+ int t=p_idx%2;
+ String n = "Curve Point #"+itos(idx);
+ if (t==0)
+ n+=" In";
+ else
+ n+=" Out";
+
+ return n;
+
+
+}
+Variant PathSpatialGizmo::get_handle_value(int p_idx) const{
+
+ Ref<Curve3D> c = path->get_curve();
+ if (c.is_null())
+ return Variant();
+
+ if (p_idx<c->get_point_count()) {
+
+ original=c->get_point_pos(p_idx);
+ return original;
+ }
+
+ p_idx=p_idx-c->get_point_count()+1;
+
+ int idx=p_idx/2;
+ int t=p_idx%2;
+
+ Vector3 ofs;
+ if (t==0)
+ ofs=c->get_point_in(idx);
+ else
+ ofs= c->get_point_out(idx);
+
+ original=ofs+c->get_point_pos(idx);
+
+ return ofs;
+
+}
+void PathSpatialGizmo::set_handle(int p_idx,Camera *p_camera, const Point2& p_point){
+
+ Ref<Curve3D> c = path->get_curve();
+ if (c.is_null())
+ return;
+
+ Transform gt = path->get_global_transform();
+ Transform gi = gt.affine_inverse();
+ Vector3 ray_from = p_camera->project_ray_origin(p_point);
+ Vector3 ray_dir = p_camera->project_ray_normal(p_point);
+
+ if (p_idx<c->get_point_count()) {
+
+ Plane p(gt.xform(original),p_camera->get_transform().basis.get_axis(2));
+
+ Vector3 inters;
+
+ if (p.intersects_ray(ray_from,ray_dir,&inters)) {
+
+ Vector3 local = gi.xform(inters);
+ c->set_point_pos(p_idx,local);
+ }
+
+ return;
+ }
+
+ p_idx=p_idx-c->get_point_count()+1;
+
+ int idx=p_idx/2;
+ int t=p_idx%2;
+
+ Vector3 base = c->get_point_pos(idx);
+
+ Plane p(gt.xform(original),p_camera->get_transform().basis.get_axis(2));
+
+ Vector3 inters;
+
+ if (p.intersects_ray(ray_from,ray_dir,&inters)) {
+
+ Vector3 local = gi.xform(inters)-base;
+ if (t==0) {
+ c->set_point_in(idx,local);
+ } else {
+ c->set_point_out(idx,local);
+ }
+ }
+
+}
+
+void PathSpatialGizmo::commit_handle(int p_idx,const Variant& p_restore,bool p_cancel){
+
+ Ref<Curve3D> c = path->get_curve();
+ if (c.is_null())
+ return;
+
+ UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
+
+ if (p_idx<c->get_point_count()) {
+
+ if (p_cancel) {
+
+ c->set_point_pos(p_idx,p_restore);
+ return;
+ }
+ ur->create_action("Set Curve Point Pos");
+ ur->add_do_method(c.ptr(),"set_point_pos",p_idx,c->get_point_pos(p_idx));
+ ur->add_undo_method(c.ptr(),"set_point_pos",p_idx,p_restore);
+ ur->commit_action();
+
+ return;
+ }
+
+ p_idx=p_idx-c->get_point_count()+1;
+
+ int idx=p_idx/2;
+ int t=p_idx%2;
+
+ Vector3 ofs;
+
+ if (p_cancel) {
+
+
+
+ return;
+ }
+
+
+
+ if (t==0) {
+
+ if (p_cancel) {
+
+ c->set_point_in(p_idx,p_restore);
+ return;
+ }
+ ur->create_action("Set Curve In Pos");
+ ur->add_do_method(c.ptr(),"set_point_in",idx,c->get_point_in(idx));
+ ur->add_undo_method(c.ptr(),"set_point_in",idx,p_restore);
+ ur->commit_action();
+
+
+ } else {
+ if (p_cancel) {
+
+ c->set_point_out(idx,p_restore);
+ return;
+ }
+ ur->create_action("Set Curve Out Pos");
+ ur->add_do_method(c.ptr(),"set_point_out",idx,c->get_point_out(idx));
+ ur->add_undo_method(c.ptr(),"set_point_out",idx,p_restore);
+ ur->commit_action();
+
+ }
+
+}
+
+
+void PathSpatialGizmo::redraw(){
+
+ clear();
+
+ Ref<Curve3D> c = path->get_curve();
+ if (c.is_null())
+ return;
+
+ Vector3Array v3a=c->tesselate();
+ //Vector3Array v3a=c->get_baked_points();
+
+ int v3s = v3a.size();
+ if (v3s==0)
+ return;
+ Vector<Vector3> v3p;
+ Vector3Array::Read r = v3a.read();
+
+ for(int i=0;i<v3s-1;i++) {
+
+ v3p.push_back(r[i]);
+ v3p.push_back(r[i+1]);
+ //v3p.push_back(r[i]);
+ //v3p.push_back(r[i]+Vector3(0,0.2,0));
+ }
+
+ add_lines(v3p,PathEditorPlugin::singleton->path_material);
+ add_collision_segments(v3p);
+
+ if (PathEditorPlugin::singleton->get_edited_path()==path) {
+ v3p.clear();
+ Vector<Vector3> handles;
+ Vector<Vector3> sec_handles;
+
+ for(int i=0;i<c->get_point_count();i++) {
+
+ Vector3 p = c->get_point_pos(i);
+ handles.push_back(p);
+ if (i>0) {
+ v3p.push_back(p);
+ v3p.push_back(p+c->get_point_in(i));
+ sec_handles.push_back(p+c->get_point_in(i));
+ }
+
+ if (i<c->get_point_count()-1) {
+ v3p.push_back(p);
+ v3p.push_back(p+c->get_point_out(i));
+ sec_handles.push_back(p+c->get_point_out(i));
+ }
+ }
+
+ add_lines(v3p,PathEditorPlugin::singleton->path_thin_material);
+ add_handles(handles);
+ add_handles(sec_handles,false,true);
+ }
+
+}
+
+PathSpatialGizmo::PathSpatialGizmo(Path* p_path){
+
+ path=p_path;
+ set_spatial_node(p_path);
+
+
+
+}
+
+bool PathEditorPlugin::create_spatial_gizmo(Spatial* p_spatial) {
+
+ if (p_spatial->cast_to<Path>()) {
+
+
+ Ref<PathSpatialGizmo> psg = memnew( PathSpatialGizmo(p_spatial->cast_to<Path>()));
+ p_spatial->set_gizmo(psg);
+ return true;
+ }
+
+ return false;
+}
+
+bool PathEditorPlugin::forward_spatial_input_event(Camera* p_camera,const InputEvent& p_event) {
+
+ if (!path)
+ return false;
+ Ref<Curve3D> c=path->get_curve();
+ if (c.is_null())
+ return false;
+ Transform gt = path->get_global_transform();
+ Transform it = gt.affine_inverse();
+
+ static const int click_dist = 10; //should make global
+
+
+ if (p_event.type==InputEvent::MOUSE_BUTTON) {
+
+ const InputEventMouseButton &mb=p_event.mouse_button;
+ Point2 mbpos(mb.x,mb.y);
+
+ if (mb.pressed && mb.button_index==BUTTON_LEFT && (curve_create->is_pressed() || (curve_edit->is_pressed() && mb.mod.control))) {
+ //click into curve, break it down
+ Vector3Array v3a = c->tesselate();
+ int idx=0;
+ int rc=v3a.size();
+ int closest_seg=-1;
+ Vector3 closest_seg_point;
+ float closest_d=1e20;
+
+ if (rc>=2) {
+ Vector3Array::Read r = v3a.read();
+
+ if (p_camera->unproject_position(gt.xform(c->get_point_pos(0))).distance_to(mbpos)<click_dist)
+ return false; //nope, existing
+
+
+ for(int i=0;i<c->get_point_count()-1;i++) {
+ //find the offset and point index of the place to break up
+ int j=idx;
+ if (p_camera->unproject_position(gt.xform(c->get_point_pos(i+1))).distance_to(mbpos)<click_dist)
+ return false; //nope, existing
+
+
+ while(j<rc && c->get_point_pos(i+1)!=r[j]) {
+
+ Vector3 from =r[j];
+ Vector3 to =r[j+1];
+ real_t cdist = from.distance_to(to);
+ from=gt.xform(from);
+ to=gt.xform(to);
+ if (cdist>0) {
+ Vector2 s[2];
+ s[0] = p_camera->unproject_position(from);
+ s[1] = p_camera->unproject_position(to);
+ Vector2 inters = Geometry::get_closest_point_to_segment_2d(mbpos,s);
+ float d = inters.distance_to(mbpos);
+
+ if (d<10 && d<closest_d) {
+
+
+ closest_d=d;
+ closest_seg=i;
+ Vector3 ray_from=p_camera->project_ray_origin(mbpos);
+ Vector3 ray_dir=p_camera->project_ray_normal(mbpos);
+
+ Vector3 ra,rb;
+ Geometry::get_closest_points_between_segments(ray_from,ray_from+ray_dir*4096,from,to,ra,rb);
+
+ closest_seg_point=it.xform(rb);
+ }
+
+ }
+ j++;
+
+ }
+ if (idx==j)
+ idx++; //force next
+ else
+ idx=j; //swap
+
+
+ if (j==rc)
+ break;
+ }
+ }
+
+ UndoRedo *ur = editor->get_undo_redo();
+ if (closest_seg!=-1) {
+ //subdivide
+
+ ur->create_action("Split Path");
+ ur->add_do_method(c.ptr(),"add_point",closest_seg_point,Vector3(),Vector3(),closest_seg+1);
+ ur->add_undo_method(c.ptr(),"remove_point",closest_seg+1);
+ ur->commit_action();;
+ return true;
+
+ } else {
+
+ Vector3 org;
+ if (c->get_point_count()==0)
+ org=path->get_transform().get_origin();
+ else
+ org=gt.xform(c->get_point_pos(c->get_point_count()));
+ Plane p(org,p_camera->get_transform().basis.get_axis(2));
+ Vector3 ray_from=p_camera->project_ray_origin(mbpos);
+ Vector3 ray_dir=p_camera->project_ray_normal(mbpos);
+
+ Vector3 inters;
+ if (p.intersects_ray(ray_from,ray_dir,&inters)) {
+
+ ur->create_action("Add Point to Curve");
+ ur->add_do_method(c.ptr(),"add_point",it.xform(inters),Vector3(),Vector3(),-1);
+ ur->add_undo_method(c.ptr(),"remove_point",c->get_point_count());
+ ur->commit_action();;
+ return true;
+ }
+
+ //add new at pos
+ }
+
+ } else if (mb.pressed && ((mb.button_index==BUTTON_LEFT && curve_del->is_pressed()) || (mb.button_index==BUTTON_RIGHT && curve_edit->is_pressed()))) {
+
+ int erase_idx=-1;
+ for(int i=0;i<c->get_point_count();i++) {
+ //find the offset and point index of the place to break up
+ if (p_camera->unproject_position(gt.xform(c->get_point_pos(i))).distance_to(mbpos)<click_dist) {
+
+ erase_idx=i;
+ break;
+ }
+ }
+
+ if (erase_idx!=-1) {
+
+ UndoRedo *ur = editor->get_undo_redo();
+ ur->create_action("Remove Path Point");
+ ur->add_do_method(c.ptr(),"remove_point",erase_idx);
+ ur->add_undo_method(c.ptr(),"add_point",c->get_point_pos(erase_idx),c->get_point_in(erase_idx),c->get_point_out(erase_idx),erase_idx);
+ ur->commit_action();
+ return true;
+ }
+ }
+
+ }
+
+ return false;
+}
+
+
+void PathEditorPlugin::edit(Object *p_object) {
+
+ if (p_object) {
+ path=p_object->cast_to<Path>();
+ if (path) {
+
+ if (path->get_curve().is_valid()) {
+ path->get_curve()->emit_signal("changed");
+ }
+ }
+ } else {
+ Path *pre=path;
+ path=NULL;
+ if (pre) {
+ pre->get_curve()->emit_signal("changed");
+ }
+ }
+// collision_polygon_editor->edit(p_object->cast_to<Node>());
+}
+
+bool PathEditorPlugin::handles(Object *p_object) const {
+
+ return p_object->is_type("Path");
+}
+
+void PathEditorPlugin::make_visible(bool p_visible) {
+
+ if (p_visible) {
+
+ curve_create->show();
+ curve_edit->show();
+ curve_del->show();
+ curve_close->show();
+ sep->show();
+ } else {
+
+ curve_create->hide();
+ curve_edit->hide();
+ curve_del->hide();
+ curve_close->hide();
+ sep->hide();
+
+ {
+ Path *pre=path;
+ path=NULL;
+ if (pre && pre->get_curve().is_valid()) {
+ pre->get_curve()->emit_signal("changed");
+ }
+ }
+ }
+
+}
+
+void PathEditorPlugin::_mode_changed(int p_idx) {
+
+ curve_create->set_pressed(p_idx==0);
+ curve_edit->set_pressed(p_idx==1);
+ curve_del->set_pressed(p_idx==2);
+}
+
+void PathEditorPlugin::_close_curve() {
+
+ Ref<Curve3D> c = path->get_curve();
+ if (c.is_null())
+ return ;
+ if (c->get_point_count()<2)
+ return;
+ c->add_point(c->get_point_pos(0),c->get_point_in(0),c->get_point_out(0));
+
+}
+
+void PathEditorPlugin::_notification(int p_what) {
+
+ if (p_what==NOTIFICATION_ENTER_SCENE) {
+
+ curve_create->connect("pressed",this,"_mode_changed",make_binds(0));
+ curve_edit->connect("pressed",this,"_mode_changed",make_binds(1));
+ curve_del->connect("pressed",this,"_mode_changed",make_binds(2));
+ curve_close->connect("pressed",this,"_close_curve");
+ }
+}
+
+void PathEditorPlugin::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("_mode_changed"),&PathEditorPlugin::_mode_changed);
+ ObjectTypeDB::bind_method(_MD("_close_curve"),&PathEditorPlugin::_close_curve);
+}
+
+PathEditorPlugin* PathEditorPlugin::singleton=NULL;
+
+
+PathEditorPlugin::PathEditorPlugin(EditorNode *p_node) {
+
+ path=NULL;
+ editor=p_node;
+ singleton=this;
+
+ path_material = Ref<FixedMaterial>( memnew( FixedMaterial ));
+ path_material->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(0.5,0.5,1.0,0.8) );
+ path_material->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
+ path_material->set_line_width(3);
+ path_material->set_flag(Material::FLAG_DOUBLE_SIDED,true);
+ path_material->set_flag(Material::FLAG_UNSHADED,true);
+
+ path_thin_material = Ref<FixedMaterial>( memnew( FixedMaterial ));
+ path_thin_material->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(0.5,0.5,1.0,0.4) );
+ path_thin_material->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
+ path_thin_material->set_line_width(1);
+ path_thin_material->set_flag(Material::FLAG_DOUBLE_SIDED,true);
+ path_thin_material->set_flag(Material::FLAG_UNSHADED,true);
+
+ SpatialEditor::get_singleton()->add_gizmo_plugin(this);
+
+ sep = memnew( VSeparator);
+ sep->hide();
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(sep);
+ curve_edit = memnew( ToolButton );
+ curve_edit->set_icon(SpatialEditor::get_singleton()->get_icon("CurveEdit","EditorIcons"));
+ curve_edit->set_toggle_mode(true);
+ curve_edit->hide();
+ curve_edit->set_focus_mode(Control::FOCUS_NONE);
+ curve_edit->set_tooltip("Select Points\nShift+Drag: Select Control Points\n"+keycode_get_string(KEY_MASK_CMD)+"Click: Add Point\nRight Click: Delete Point.");
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(curve_edit);
+ curve_create = memnew( ToolButton );
+ curve_create->set_icon(SpatialEditor::get_singleton()->get_icon("CurveCreate","EditorIcons"));
+ curve_create->set_toggle_mode(true);
+ curve_create->hide();
+ curve_create->set_focus_mode(Control::FOCUS_NONE);
+ curve_create->set_tooltip("Add Point (in empty space)\nSplit Segment (in curve).");
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(curve_create);
+ curve_del = memnew( ToolButton );
+ curve_del->set_icon(SpatialEditor::get_singleton()->get_icon("CurveDelete","EditorIcons"));
+ curve_del->set_toggle_mode(true);
+ curve_del->hide();
+ curve_del->set_focus_mode(Control::FOCUS_NONE);
+ curve_del->set_tooltip("Delete Point.");
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(curve_del);
+ curve_close = memnew( ToolButton );
+//curve_close->set_icon(SpatialEditor::get_singleton()->get_icon("CurveDelete","EditorIcons"));
+ curve_close->set_text("close");
+ curve_close->hide();
+ curve_close->set_focus_mode(Control::FOCUS_NONE);
+ curve_close->set_tooltip("Close Curve");
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(curve_close);
+
+
+
+ curve_edit->set_pressed(true);
+ /*
+ collision_polygon_editor = memnew( PathEditor(p_node) );
+ editor->get_viewport()->add_child(collision_polygon_editor);
+
+ collision_polygon_editor->set_margin(MARGIN_LEFT,200);
+ collision_polygon_editor->set_margin(MARGIN_RIGHT,230);
+ collision_polygon_editor->set_margin(MARGIN_TOP,0);
+ collision_polygon_editor->set_margin(MARGIN_BOTTOM,10);
+
+
+ collision_polygon_editor->hide();
+ */
+
+
+}
+
+
+PathEditorPlugin::~PathEditorPlugin()
+{
+}
+
diff --git a/tools/editor/plugins/path_editor_plugin.h b/tools/editor/plugins/path_editor_plugin.h new file mode 100644 index 000000000..b938358d3 --- /dev/null +++ b/tools/editor/plugins/path_editor_plugin.h @@ -0,0 +1,99 @@ +/*************************************************************************/ +/* path_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef PATH_EDITOR_PLUGIN_H
+#define PATH_EDITOR_PLUGIN_H
+
+
+#include "tools/editor/spatial_editor_gizmos.h"
+#include "scene/3d/path.h"
+class PathSpatialGizmo : public SpatialGizmoTool {
+
+ OBJ_TYPE(PathSpatialGizmo,SpatialGizmoTool);
+
+ Path* path;
+ mutable Vector3 original;
+
+public:
+
+ virtual String get_handle_name(int p_idx) const;
+ virtual Variant get_handle_value(int p_idx) const;
+ virtual void set_handle(int p_idx,Camera *p_camera, const Point2& p_point);
+ virtual void commit_handle(int p_idx,const Variant& p_restore,bool p_cancel=false);
+
+ void redraw();
+ PathSpatialGizmo(Path* p_path=NULL);
+
+};
+
+class PathEditorPlugin : public EditorPlugin {
+
+ OBJ_TYPE( PathEditorPlugin, EditorPlugin );
+
+
+ Separator *sep;
+ ToolButton *curve_create;
+ ToolButton *curve_edit;
+ ToolButton *curve_del;
+ ToolButton *curve_close;
+
+ EditorNode *editor;
+
+
+ Path *path;
+
+ void _mode_changed(int p_idx);
+ void _close_curve();
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+
+ Path *get_edited_path() { return path; }
+
+ static PathEditorPlugin* singleton;
+ Ref<FixedMaterial> path_material;
+ Ref<FixedMaterial> path_thin_material;
+ virtual bool forward_spatial_input_event(Camera* p_camera,const InputEvent& p_event);
+
+// virtual bool forward_input_event(const InputEvent& p_event) { return collision_polygon_editor->forward_input_event(p_event); }
+ virtual bool create_spatial_gizmo(Spatial* p_spatial);
+ virtual String get_name() const { return "Path"; }
+ 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);
+
+ PathEditorPlugin(EditorNode *p_node);
+ ~PathEditorPlugin();
+
+};
+
+
+#endif // PATH_EDITOR_PLUGIN_H
diff --git a/tools/editor/plugins/resource_preloader_editor_plugin.cpp b/tools/editor/plugins/resource_preloader_editor_plugin.cpp new file mode 100644 index 000000000..b0841933e --- /dev/null +++ b/tools/editor/plugins/resource_preloader_editor_plugin.cpp @@ -0,0 +1,388 @@ +/*************************************************************************/ +/* resource_preloader_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "resource_preloader_editor_plugin.h" +#include "io/resource_loader.h" +#include "globals.h" +#include "tools/editor/editor_settings.h" +#include "scene/resources/scene_preloader.h" + + + +void ResourcePreloaderEditor::_input_event(InputEvent p_event) { + + +} + +void ResourcePreloaderEditor::_notification(int p_what) { + + if (p_what==NOTIFICATION_FIXED_PROCESS) { + + } + + if (p_what==NOTIFICATION_ENTER_SCENE) { + load->set_icon( get_icon("Folder","EditorIcons") ); + _delete->set_icon( get_icon("Del","EditorIcons") ); + } + + if (p_what==NOTIFICATION_READY) { + +// NodePath("/root")->connect("node_removed", this,"_node_removed",Vector<Variant>(),true); + } + + if (p_what==NOTIFICATION_DRAW) { + + } +} + +void ResourcePreloaderEditor::_file_load_request(const String& p_path) { + + + RES resource; + + resource = ResourceLoader::load(p_path); + + if (resource.is_null()) { + dialog->set_text("ERROR: Couldn't load resource!"); + dialog->set_title("Error!"); + //dialog->get_cancel()->set_text("Close"); + dialog->get_ok()->set_text("Close"); + dialog->popup_centered(Size2(300,60)); + return; ///beh should show an error i guess + } + + String basename = p_path.get_file().basename(); + String name=basename; + int counter=1; + while(preloader->has_resource(name)) { + counter++; + name=basename+" "+itos(counter); + } + + undo_redo->create_action("Add Resource"); + undo_redo->add_do_method(preloader,"add_resource",name,resource); + undo_redo->add_undo_method(preloader,"remove_resource",name); + undo_redo->add_do_method(this,"_update_library"); + undo_redo->add_undo_method(this,"_update_library"); + undo_redo->commit_action(); +} + +void ResourcePreloaderEditor::_load_pressed() { + + loading_scene=false; + + file->clear_filters(); + List<String> extensions; + ResourceLoader::get_recognized_extensions_for_type("",&extensions); + for(int i=0;i<extensions.size();i++) + file->add_filter("*."+extensions[i]); + + file->set_mode(FileDialog::MODE_OPEN_FILE); + + file->popup_centered_ratio(); + +} + + +void ResourcePreloaderEditor::_item_edited() { + + if (!tree->get_selected()) + return; + + TreeItem *s = tree->get_selected(); + + if (tree->get_selected_column()==0) { + // renamed + String old_name=s->get_metadata(0); + String new_name=s->get_text(0); + if (old_name==new_name) + return; + + if (new_name=="" || new_name.find("\\")!=-1 || new_name.find("/")!=-1 || preloader->has_resource(new_name)) { + + s->set_text(0,old_name); + return; + } + + RES samp = preloader->get_resource(old_name); + undo_redo->create_action("Rename Resource"); + undo_redo->add_do_method(preloader,"remove_resource",old_name); + undo_redo->add_do_method(preloader,"add_resource",new_name,samp); + undo_redo->add_undo_method(preloader,"remove_resource",new_name); + undo_redo->add_undo_method(preloader,"add_resource",old_name,samp); + undo_redo->add_do_method(this,"_update_library"); + undo_redo->add_undo_method(this,"_update_library"); + undo_redo->commit_action(); + + } + + +} + +void ResourcePreloaderEditor::_delete_confirm_pressed() { + + if (!tree->get_selected()) + return; + + String to_remove = tree->get_selected()->get_text(0); + undo_redo->create_action("Delete Resource"); + undo_redo->add_do_method(preloader,"remove_resource",to_remove); + undo_redo->add_undo_method(preloader,"add_resource",to_remove,preloader->get_resource(to_remove)); + undo_redo->add_do_method(this,"_update_library"); + undo_redo->add_undo_method(this,"_update_library"); + undo_redo->commit_action(); +} + + +void ResourcePreloaderEditor::_paste_pressed() { + + RES r=EditorSettings::get_singleton()->get_resource_clipboard(); + if (!r.is_valid()) { + dialog->set_text("Resource clipboard is empty!"); + dialog->set_title("Error!"); + //dialog->get_cancel()->set_text("Close"); + dialog->get_ok()->set_text("Close"); + dialog->popup_centered(Size2(300,60)); + return; ///beh should show an error i guess + } + + String name = r->get_name(); + if (name=="") + name=r->get_path().get_file(); + if (name=="") + name=r->get_type(); + + String basename = name; + int counter=1; + while(preloader->has_resource(name)) { + counter++; + name=basename+" "+itos(counter); + } + + undo_redo->create_action("Paste Resource"); + undo_redo->add_do_method(preloader,"add_resource",name,r); + undo_redo->add_undo_method(preloader,"remove_resource",name); + undo_redo->add_do_method(this,"_update_library"); + undo_redo->add_undo_method(this,"_update_library"); + undo_redo->commit_action(); + +} + + +void ResourcePreloaderEditor::_delete_pressed() { + + + if (!tree->get_selected()) + return; + + _delete_confirm_pressed(); //it has undo.. why bother with a dialog.. + /* + dialog->set_title("Confirm..."); + dialog->set_text("Remove Resource '"+tree->get_selected()->get_text(0)+"' ?"); + //dialog->get_cancel()->set_text("Cancel"); + //dialog->get_ok()->show(); + dialog->get_ok()->set_text("Remove"); + dialog->popup_centered(Size2(300,60));*/ + +} + + +void ResourcePreloaderEditor::_update_library() { + + tree->clear(); + tree->set_hide_root(true); + TreeItem *root = tree->create_item(NULL); + + List<StringName> rnames; + preloader->get_resource_list(&rnames); + + List<String> names; + for(List<StringName>::Element *E=rnames.front();E;E=E->next()) { + names.push_back(E->get()); + } + + names.sort(); + + for(List<String>::Element *E=names.front();E;E=E->next()) { + + TreeItem *ti = tree->create_item(root); + ti->set_cell_mode(0,TreeItem::CELL_MODE_STRING); + ti->set_editable(0,true); + ti->set_selectable(0,true); + ti->set_text(0,E->get()); + ti->set_metadata(0,E->get()); + + + + RES r = preloader->get_resource(E->get()); + + ERR_CONTINUE(r.is_null()); + + ti->set_tooltip(0,r->get_path()); + String type = r->get_type(); + ti->set_text(1,type); + ti->set_selectable(1,false); + + if (has_icon(type,"EditorIcons")) + ti->set_icon( 1, get_icon(type,"EditorIcons") ); + + } + + //player->add_resource("default",resource); +} + + + +void ResourcePreloaderEditor::edit(ResourcePreloader* p_preloader) { + + preloader=p_preloader; + + + if (p_preloader) { + _update_library(); + } else { + + hide(); + set_fixed_process(false); + } + +} + + + +void ResourcePreloaderEditor::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_input_event"),&ResourcePreloaderEditor::_input_event); + ObjectTypeDB::bind_method(_MD("_load_pressed"),&ResourcePreloaderEditor::_load_pressed); + ObjectTypeDB::bind_method(_MD("_item_edited"),&ResourcePreloaderEditor::_item_edited); + ObjectTypeDB::bind_method(_MD("_delete_pressed"),&ResourcePreloaderEditor::_delete_pressed); + ObjectTypeDB::bind_method(_MD("_paste_pressed"),&ResourcePreloaderEditor::_paste_pressed); + ObjectTypeDB::bind_method(_MD("_delete_confirm_pressed"),&ResourcePreloaderEditor::_delete_confirm_pressed); + ObjectTypeDB::bind_method(_MD("_file_load_request"),&ResourcePreloaderEditor::_file_load_request); + ObjectTypeDB::bind_method(_MD("_update_library"),&ResourcePreloaderEditor::_update_library); +} + +ResourcePreloaderEditor::ResourcePreloaderEditor() { + + //add_style_override("panel", get_stylebox("panel","Panel")); + + VBoxContainer *vbc = memnew( VBoxContainer ); + add_child(vbc); + + HBoxContainer *hbc = memnew( HBoxContainer ); + vbc->add_child(hbc); + + load = memnew( Button ); + load->set_tooltip("Load Resource"); + hbc->add_child(load); + + + + _delete = memnew( Button ); + hbc->add_child(_delete); + + paste = memnew( Button ); + paste->set_text("Paste"); + hbc->add_child(paste); + + file = memnew( FileDialog ); + add_child(file); + + + tree = memnew( Tree ); + tree->set_columns(2); + tree->set_column_min_width(0,3); + tree->set_column_min_width(1,1); + tree->set_column_expand(0,true); + tree->set_column_expand(1,true); + tree->set_v_size_flags(SIZE_EXPAND_FILL); + + vbc->add_child(tree); + + dialog = memnew( AcceptDialog ); + add_child( dialog ); + + load->connect("pressed", this,"_load_pressed"); + _delete->connect("pressed", this,"_delete_pressed"); + paste->connect("pressed", this,"_paste_pressed"); + file->connect("file_selected", this,"_file_load_request"); + //dialog->connect("confirmed", this,"_delete_confirm_pressed"); + tree->connect("item_edited", this,"_item_edited"); + loading_scene=false; + +} + + +void ResourcePreloaderEditorPlugin::edit(Object *p_object) { + + preloader_editor->set_undo_redo(&get_undo_redo()); + ResourcePreloader * s = p_object->cast_to<ResourcePreloader>(); + if (!s) + return; + + preloader_editor->edit(s); +} + +bool ResourcePreloaderEditorPlugin::handles(Object *p_object) const { + + return p_object->is_type("ResourcePreloader"); +} + +void ResourcePreloaderEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + preloader_editor->show(); +// preloader_editor->set_process(true); + } else { + + preloader_editor->hide(); +// preloader_editor->set_process(false); + } + +} + +ResourcePreloaderEditorPlugin::ResourcePreloaderEditorPlugin(EditorNode *p_node) { + + editor=p_node; + preloader_editor = memnew( ResourcePreloaderEditor ); + editor->get_viewport()->add_child(preloader_editor); + preloader_editor->set_area_as_parent_rect(); +// preloader_editor->set_anchor( MARGIN_TOP, Control::ANCHOR_END); +// preloader_editor->set_margin( MARGIN_TOP, 120 ); + preloader_editor->hide(); + + + +} + + +ResourcePreloaderEditorPlugin::~ResourcePreloaderEditorPlugin() +{ +} + + diff --git a/tools/editor/plugins/resource_preloader_editor_plugin.h b/tools/editor/plugins/resource_preloader_editor_plugin.h new file mode 100644 index 000000000..e3178bc8f --- /dev/null +++ b/tools/editor/plugins/resource_preloader_editor_plugin.h @@ -0,0 +1,102 @@ +/*************************************************************************/ +/* resource_preloader_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef RESOURCE_PRELOADER_EDITOR_PLUGIN_H +#define RESOURCE_PRELOADER_EDITOR_PLUGIN_H + + +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" +#include "scene/gui/tree.h" +#include "scene/main/resource_preloader.h" +#include "scene/gui/file_dialog.h" +#include "scene/gui/dialogs.h" + + +class ResourcePreloaderEditor : public PanelContainer { + + OBJ_TYPE(ResourcePreloaderEditor, PanelContainer ); + + Button *load; + Button *_delete; + Button *paste; + Tree *tree; + bool loading_scene; + + + FileDialog *file; + + AcceptDialog *dialog; + + ResourcePreloader *preloader; + + + void _load_pressed(); + void _load_scene_pressed(); + void _file_load_request(const String& p_path); + void _paste_pressed(); + void _delete_pressed(); + void _delete_confirm_pressed(); + void _update_library(); + void _item_edited(); + + UndoRedo *undo_redo; + +protected: + void _notification(int p_what); + void _input_event(InputEvent p_event); + static void _bind_methods(); +public: + + void set_undo_redo(UndoRedo *p_undo_redo) {undo_redo=p_undo_redo; } + + void edit(ResourcePreloader* p_preloader); + ResourcePreloaderEditor(); +}; + +class ResourcePreloaderEditorPlugin : public EditorPlugin { + + OBJ_TYPE( ResourcePreloaderEditorPlugin, EditorPlugin ); + + ResourcePreloaderEditor *preloader_editor; + EditorNode *editor; + +public: + + virtual String get_name() const { return "ResourcePreloader"; } + 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); + + ResourcePreloaderEditorPlugin(EditorNode *p_node); + ~ResourcePreloaderEditorPlugin(); + +}; + +#endif // RESOURCE_PRELOADER_EDITOR_PLUGIN_H diff --git a/tools/editor/plugins/rich_text_editor_plugin.cpp b/tools/editor/plugins/rich_text_editor_plugin.cpp new file mode 100644 index 000000000..58b3636dc --- /dev/null +++ b/tools/editor/plugins/rich_text_editor_plugin.cpp @@ -0,0 +1,163 @@ +/*************************************************************************/ +/* rich_text_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "rich_text_editor_plugin.h" +#include "os/file_access.h" +void RichTextEditor::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_FIXED_PROCESS: { + + + } break; + } + +} +void RichTextEditor::_node_removed(Node *p_node) { + + if(p_node==node) { + node=NULL; + hide(); + } + +} + + +void RichTextEditor::_file_selected(const String& p_path) { + + CharString cs; + FileAccess *fa = FileAccess::open(p_path,FileAccess::READ); + if (!fa) { + ERR_FAIL(); + } + + while(!fa->eof_reached()) + cs.push_back(fa->get_8()); + cs.push_back(0); + memdelete(fa); + + String bbcode; + bbcode.parse_utf8(&cs[0]); + node->parse_bbcode(bbcode); + +} + +void RichTextEditor::_menu_option(int p_option) { + + switch(p_option) { + + case PARSE_BBCODE: { + + file_dialog->popup_centered_ratio(); + } break; + case CLEAR: { + + node->clear(); + + } break; + + } +} + +void RichTextEditor::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_menu_option"),&RichTextEditor::_menu_option); + ObjectTypeDB::bind_method(_MD("_file_selected"),&RichTextEditor::_file_selected); + +} + +void RichTextEditor::edit(Node *p_rich_text) { + + node=p_rich_text->cast_to<RichTextLabel>(); + +} +RichTextEditor::RichTextEditor() { + + options = memnew( MenuButton ); + add_child(options); + options->set_area_as_parent_rect(); + + options->set_text("RichText"); + options->get_popup()->add_item("Parse BBCODE",PARSE_BBCODE); + options->get_popup()->add_item("Clear",CLEAR); + + options->get_popup()->connect("item_pressed", this,"_menu_option"); + file_dialog = memnew( FileDialog ); + add_child(file_dialog); + file_dialog->add_filter("*.txt"); + file_dialog->set_mode(FileDialog::MODE_OPEN_FILE); + file_dialog->connect("file_selected",this,"_file_selected"); +} + + +void RichTextEditorPlugin::edit(Object *p_object) { + + rich_text_editor->edit(p_object->cast_to<Node>()); +} + +bool RichTextEditorPlugin::handles(Object *p_object) const { + + return p_object->is_type("RichTextLabel"); +} + +void RichTextEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + rich_text_editor->show(); + } else { + + rich_text_editor->hide(); + rich_text_editor->edit(NULL); + } + +} + +RichTextEditorPlugin::RichTextEditorPlugin(EditorNode *p_node) { + + editor=p_node; + rich_text_editor = memnew( RichTextEditor ); + editor->get_viewport()->add_child(rich_text_editor); + + rich_text_editor->set_margin(MARGIN_LEFT,184); + rich_text_editor->set_margin(MARGIN_RIGHT,230); + rich_text_editor->set_margin(MARGIN_TOP,0); + rich_text_editor->set_margin(MARGIN_BOTTOM,10); + + + rich_text_editor->hide(); + + + +} + + +RichTextEditorPlugin::~RichTextEditorPlugin() +{ +} + diff --git a/tools/editor/plugins/rich_text_editor_plugin.h b/tools/editor/plugins/rich_text_editor_plugin.h new file mode 100644 index 000000000..e51e0653b --- /dev/null +++ b/tools/editor/plugins/rich_text_editor_plugin.h @@ -0,0 +1,89 @@ +/*************************************************************************/ +/* rich_text_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef RICH_TEXT_EDITOR_PLUGIN_H +#define RICH_TEXT_EDITOR_PLUGIN_H + +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" +#include "scene/gui/rich_text_label.h" +#include "scene/gui/file_dialog.h" + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + +class RichTextEditor : public Control { + + OBJ_TYPE(RichTextEditor, Control ); + + enum { + + PARSE_BBCODE, + CLEAR + }; + + Panel *panel; + RichTextLabel *node; + MenuButton *options; + FileDialog *file_dialog; + + void _file_selected(const String& p_path); + void _menu_option(int p_option); + +protected: + void _notification(int p_what); + void _node_removed(Node *p_node); + static void _bind_methods(); +public: + + void edit(Node *p_rich_text); + RichTextEditor(); +}; + +class RichTextEditorPlugin : public EditorPlugin { + + OBJ_TYPE( RichTextEditorPlugin, EditorPlugin ); + + RichTextEditor *rich_text_editor; + EditorNode *editor; + +public: + + virtual String get_name() const { return "RichText"; } + 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); + + RichTextEditorPlugin(EditorNode *p_node); + ~RichTextEditorPlugin(); + +}; + +#endif // RICH_TEXT_EDITOR_PLUGIN_H diff --git a/tools/editor/plugins/sample_editor_plugin.cpp b/tools/editor/plugins/sample_editor_plugin.cpp new file mode 100644 index 000000000..f55d6640c --- /dev/null +++ b/tools/editor/plugins/sample_editor_plugin.cpp @@ -0,0 +1,326 @@ +/*************************************************************************/ +/* sample_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "sample_editor_plugin.h" + +#include "io/resource_loader.h" +#include "globals.h" +#include "tools/editor/editor_settings.h" + + + + +void SampleEditor::_input_event(InputEvent p_event) { + + +} + +void SampleEditor::_notification(int p_what) { + + if (p_what==NOTIFICATION_FIXED_PROCESS) { + + } + + if (p_what==NOTIFICATION_ENTER_SCENE) { + play->set_icon( get_icon("Play","EditorIcons") ); + stop->set_icon( get_icon("Stop","EditorIcons") ); + } + + if (p_what==NOTIFICATION_READY) { + + //get_scene()->connect("node_removed",this,"_node_removed"); + + } + + if (p_what==NOTIFICATION_DRAW) { + + } +} + +void SampleEditor::_play_pressed() { + + player->play("default",true); + stop->set_pressed(false); + play->set_pressed(true); +} +void SampleEditor::_stop_pressed() { + + player->stop_all(); + play->set_pressed(false); +} + +void SampleEditor::generate_preview_texture(const Ref<Sample>& p_sample,Ref<ImageTexture> &p_texture) { + + + DVector<uint8_t> data = p_sample->get_data(); + + DVector<uint8_t> img; + int w = p_texture->get_width(); + int h = p_texture->get_height(); + img.resize(w*h*3); + DVector<uint8_t>::Write imgdata = img.write(); + uint8_t * imgw = imgdata.ptr(); + DVector<uint8_t>::Read sampledata = data.read(); + const uint8_t *sdata=sampledata.ptr(); + + bool stereo = p_sample->is_stereo(); + bool _16=p_sample->get_format()==Sample::FORMAT_PCM16; + int len = p_sample->get_length(); + + if (len<1) + return; + + for(int i=0;i<w;i++) { + // i trust gcc will optimize this loop + float max[2]={-1e10,-1e10}; + float min[2]={1e10,1e10}; + int c=stereo?2:1; + int from = i*len/w; + int to = (i+1)*len/w; + if (to>=len) + to=len-1; + + if (_16) { + const int16_t*src =(const int16_t*)sdata; + + for(int j=0;j<c;j++) { + + for(int k=from;k<=to;k++) { + + float v = src[k*c+j]/32768.0; + if (v>max[j]) + max[j]=v; + if (v<min[j]) + min[j]=v; + } + + } + } else { + + const int8_t*src =(const int8_t*)sdata; + + for(int j=0;j<c;j++) { + + for(int k=from;k<=to;k++) { + + float v = src[k*c+j]/128.0; + if (v>max[j]) + max[j]=v; + if (v<min[j]) + min[j]=v; + } + + } + } + + if (!stereo) { + for(int j=0;j<h;j++) { + float v = (j/(float)h) * 2.0 - 1.0; + uint8_t* imgofs = &imgw[(j*w+i)*3]; + if (v>min[0] && v<max[0]) { + imgofs[0]=255; + imgofs[1]=150; + imgofs[2]=80; + } else { + imgofs[0]=0; + imgofs[1]=0; + imgofs[2]=0; + } + } + } else { + + for(int j=0;j<h;j++) { + + int half,ofs; + float v; + if (j<(h/2)) { + half=0; + ofs=0; + v = (j/(float)(h/2)) * 2.0 - 1.0; + } else { + half=1; + ofs=h/2; + v = ((j-(h/2))/(float)(h/2)) * 2.0 - 1.0; + } + + uint8_t* imgofs = &imgw[(j*w+i)*3]; + if (v>min[half] && v<max[half]) { + imgofs[0]=255; + imgofs[1]=150; + imgofs[2]=80; + } else { + imgofs[0]=0; + imgofs[1]=0; + imgofs[2]=0; + } + } + + } + + } + + imgdata = DVector<uint8_t>::Write(); + + + p_texture->set_data(Image(w,h,0,Image::FORMAT_RGB,img)); + +} + +void SampleEditor::_update_sample() { + + player->stop_all(); + if (sample->get_format()==Sample::FORMAT_IMA_ADPCM) + return; //bye or unsupported + + generate_preview_texture(sample,peakdisplay); + info_label->set_text("Length: "+itos(sample->get_length())+" frames ("+String::num(sample->get_length()/(float)sample->get_mix_rate(),2)+" s), "+(sample->get_format()==Sample::FORMAT_PCM16?"16 Bits, ":"8 bits, ")+(sample->is_stereo()?"Stereo.":"Mono.")); + + library->add_sample("default",sample); +} + + + +void SampleEditor::edit(Ref<Sample> p_sample) { + + sample=p_sample; + + if (!sample.is_null()) + _update_sample(); + else { + + hide(); + set_fixed_process(false); + } + +} + + + +void SampleEditor::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_input_event"),&SampleEditor::_input_event); + ObjectTypeDB::bind_method(_MD("_play_pressed"),&SampleEditor::_play_pressed); + ObjectTypeDB::bind_method(_MD("_stop_pressed"),&SampleEditor::_stop_pressed); + +} + +SampleEditor::SampleEditor() { + + player = memnew(SamplePlayer); + add_child(player); + add_style_override("panel", get_stylebox("panel","Panel")); + library = Ref<SampleLibrary>(memnew(SampleLibrary)); + player->set_sample_library(library); + sample_texframe = memnew( TextureFrame ); + add_child(sample_texframe); + sample_texframe->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_BEGIN,5); + sample_texframe->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,5); + sample_texframe->set_anchor_and_margin(MARGIN_TOP,ANCHOR_BEGIN,30); + sample_texframe->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_END,5); + + info_label = memnew( Label ); + sample_texframe->add_child(info_label); + info_label->set_area_as_parent_rect(); + info_label->set_anchor_and_margin(MARGIN_TOP,ANCHOR_END,15); + info_label->set_margin(MARGIN_BOTTOM,4); + info_label->set_margin(MARGIN_RIGHT,4); + info_label->set_align(Label::ALIGN_RIGHT); + + + play = memnew( Button ); + + play->set_pos(Point2( 5, 5 )); + play->set_size( Size2(1,1 ) ); + play->set_toggle_mode(true); + add_child(play); + + stop = memnew( Button ); + + stop->set_pos(Point2( 35, 5 )); + stop->set_size( Size2(1,1 ) ); + stop->set_toggle_mode(true); + add_child(stop); + + peakdisplay=Ref<ImageTexture>( memnew( ImageTexture) ); + peakdisplay->create( EDITOR_DEF("audio/sample_editor_preview_width",512),EDITOR_DEF("audio/sample_editor_preview_height",128),Image::FORMAT_RGB); + sample_texframe->set_expand(true); + sample_texframe->set_texture(peakdisplay); + + play->connect("pressed", this,"_play_pressed"); + stop->connect("pressed", this,"_stop_pressed"); + +} + + +void SampleEditorPlugin::edit(Object *p_object) { + + Sample * s = p_object->cast_to<Sample>(); + if (!s) + return; + + sample_editor->edit(Ref<Sample>(s)); +} + +bool SampleEditorPlugin::handles(Object *p_object) const { + + return p_object->is_type("Sample"); +} + +void SampleEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + sample_editor->show(); +// sample_editor->set_process(true); + } else { + + sample_editor->hide(); +// sample_editor->set_process(false); + } + +} + +SampleEditorPlugin::SampleEditorPlugin(EditorNode *p_node) { + + editor=p_node; + sample_editor = memnew( SampleEditor ); + editor->get_viewport()->add_child(sample_editor); + sample_editor->set_area_as_parent_rect(); + sample_editor->set_anchor( MARGIN_TOP, Control::ANCHOR_END); + sample_editor->set_margin( MARGIN_TOP, 120 ); + sample_editor->hide(); + + + +} + + +SampleEditorPlugin::~SampleEditorPlugin() +{ +} + + diff --git a/tools/editor/plugins/sample_editor_plugin.h b/tools/editor/plugins/sample_editor_plugin.h new file mode 100644 index 000000000..78d5ed401 --- /dev/null +++ b/tools/editor/plugins/sample_editor_plugin.h @@ -0,0 +1,90 @@ +/*************************************************************************/ +/* sample_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef SAMPLE_EDITOR_PLUGIN_H +#define SAMPLE_EDITOR_PLUGIN_H + + +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" +#include "scene/audio/sample_player.h" +#include "scene/resources/sample.h" +#include "scene/resources/sample_library.h" + + +class SampleEditor : public Panel { + + OBJ_TYPE(SampleEditor, Panel ); + + + SamplePlayer *player; + Label *info_label; + Ref<ImageTexture> peakdisplay; + Ref<Sample> sample; + Ref<SampleLibrary> library; + TextureFrame *sample_texframe; + Button *stop; + Button *play; + + void _play_pressed(); + void _stop_pressed(); + void _update_sample(); + +protected: + void _notification(int p_what); + void _input_event(InputEvent p_event); + static void _bind_methods(); +public: + + static void generate_preview_texture(const Ref<Sample>& p_sample,Ref<ImageTexture> &p_texture); + void edit(Ref<Sample> p_sample); + SampleEditor(); +}; + + +class SampleEditorPlugin : public EditorPlugin { + + OBJ_TYPE( SampleEditorPlugin, EditorPlugin ); + + SampleEditor *sample_editor; + EditorNode *editor; + +public: + + virtual String get_name() const { return "Sample"; } + 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); + + SampleEditorPlugin(EditorNode *p_node); + ~SampleEditorPlugin(); + +}; + +#endif // SAMPLE_EDITOR_PLUGIN_H diff --git a/tools/editor/plugins/sample_library_editor_plugin.cpp b/tools/editor/plugins/sample_library_editor_plugin.cpp new file mode 100644 index 000000000..86ac1671a --- /dev/null +++ b/tools/editor/plugins/sample_library_editor_plugin.cpp @@ -0,0 +1,449 @@ +/*************************************************************************/ +/* sample_library_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "sample_library_editor_plugin.h" + +#include "io/resource_loader.h" +#include "globals.h" +#include "tools/editor/editor_settings.h" +#include "scene/main/viewport.h" +#include "sample_editor_plugin.h" + + + +void SampleLibraryEditor::_input_event(InputEvent p_event) { + + +} + +void SampleLibraryEditor::_notification(int p_what) { + + if (p_what==NOTIFICATION_FIXED_PROCESS) { + + } + + if (p_what==NOTIFICATION_ENTER_SCENE) { + play->set_icon( get_icon("Play","EditorIcons") ); + stop->set_icon( get_icon("Stop","EditorIcons") ); + load->set_icon( get_icon("Folder","EditorIcons") ); + _delete->set_icon( get_icon("Del","EditorIcons") ); + } + + if (p_what==NOTIFICATION_READY) { + +// NodePath("/root")->connect("node_removed", this,"_node_removed",Vector<Variant>(),true); + } + + if (p_what==NOTIFICATION_DRAW) { + + } +} + +void SampleLibraryEditor::_play_pressed() { + + if (!tree->get_selected()) + return; + + String to_play = tree->get_selected()->get_text(0); + + player->play(to_play,true); + play->set_pressed(false); + stop->set_pressed(false); +} +void SampleLibraryEditor::_stop_pressed() { + + player->stop_all(); + play->set_pressed(false); +} + +void SampleLibraryEditor::_file_load_request(const DVector<String>& p_path) { + + + for(int i=0;i<p_path.size();i++) { + + String path = p_path[i]; + Ref<Sample> sample = ResourceLoader::load(path,"Sample"); + if (sample.is_null()) { + dialog->set_text("ERROR: Couldn't load sample!"); + dialog->set_title("Error!"); + //dialog->get_cancel()->set_text("Close"); + dialog->get_ok()->set_text("Close"); + dialog->popup_centered(Size2(300,60)); + return; ///beh should show an error i guess + } + String basename = path.get_file().basename(); + String name=basename; + int counter=0; + while(sample_library->has_sample(name)) { + counter++; + name=basename+"_"+itos(counter); + } + + undo_redo->create_action("Add Sample"); + undo_redo->add_do_method(sample_library.operator->(),"add_sample",name,sample); + undo_redo->add_undo_method(sample_library.operator->(),"remove_sample",name); + undo_redo->add_do_method(this,"_update_library"); + undo_redo->add_undo_method(this,"_update_library"); + undo_redo->commit_action(); + } +} + +void SampleLibraryEditor::_load_pressed() { + + file->popup_centered_ratio(); + +} + +void SampleLibraryEditor::_button_pressed(Object *p_item,int p_column, int p_id) { + + TreeItem *ti=p_item->cast_to<TreeItem>(); + String name = ti->get_text(0); + + if (p_column==0) { + + player->play(name,true); + } else if (p_column==1) { + + get_scene()->get_root()->get_child(0)->call("_resource_selected",sample_library->get_sample(name)); + + } + + +} + + + + + +void SampleLibraryEditor::_item_edited() { + + if (!tree->get_selected()) + return; + + TreeItem *s = tree->get_selected(); + + if (tree->get_selected_column()==0) { + // renamed + String old_name=s->get_metadata(0); + String new_name=s->get_text(0); + if (old_name==new_name) + return; + + if (new_name=="" || new_name.find("\\")!=-1 || new_name.find("/")!=-1 || sample_library->has_sample(new_name)) { + + s->set_text(0,old_name); + return; + } + + Ref<Sample> samp = sample_library->get_sample(old_name); + undo_redo->create_action("Rename Sample"); + undo_redo->add_do_method(sample_library.operator->(),"remove_sample",old_name); + undo_redo->add_do_method(sample_library.operator->(),"add_sample",new_name,samp); + undo_redo->add_undo_method(sample_library.operator->(),"remove_sample",new_name); + undo_redo->add_undo_method(sample_library.operator->(),"add_sample",old_name,samp); + undo_redo->add_do_method(this,"_update_library"); + undo_redo->add_undo_method(this,"_update_library"); + undo_redo->commit_action(); + + } else if (tree->get_selected_column()==3) { + + StringName n = s->get_text(0); + sample_library->sample_set_volume_db(n,s->get_range(3)); + + } else if (tree->get_selected_column()==4) { + + StringName n = s->get_text(0); + sample_library->sample_set_pitch_scale(n,s->get_range(4)); + + } else if (tree->get_selected_column()==5) { + + //edit + + Ref<Sample> samp = sample_library->get_sample(tree->get_selected()->get_metadata(0)); + + get_scene()->get_root()->get_child(0)->call("_resource_selected",samp); + } + + +} + +void SampleLibraryEditor::_delete_confirm_pressed() { + + if (!tree->get_selected()) + return; + + String to_remove = tree->get_selected()->get_text(0); + undo_redo->create_action("Delete Sample"); + undo_redo->add_do_method(sample_library.operator->(),"remove_sample",to_remove); + undo_redo->add_undo_method(sample_library.operator->(),"add_sample",to_remove,sample_library->get_sample(to_remove)); + undo_redo->add_do_method(this,"_update_library"); + undo_redo->add_undo_method(this,"_update_library"); + undo_redo->commit_action(); +} + + +void SampleLibraryEditor::_delete_pressed() { + + + if (!tree->get_selected()) + return; + + _delete_confirm_pressed(); //it has undo.. why bother with a dialog.. + /* + dialog->set_title("Confirm..."); + dialog->set_text("Remove Sample '"+tree->get_selected()->get_text(0)+"' ?"); + //dialog->get_cancel()->set_text("Cancel"); + //dialog->get_ok()->show(); + dialog->get_ok()->set_text("Remove"); + dialog->popup_centered(Size2(300,60));*/ + +} + + +void SampleLibraryEditor::_update_library() { + + player->stop_all(); + + tree->clear(); + tree->set_hide_root(true); + TreeItem *root = tree->create_item(NULL); + + List<StringName> names; + sample_library->get_sample_list(&names); + + for(List<StringName>::Element *E=names.front();E;E=E->next()) { + + TreeItem *ti = tree->create_item(root); + ti->set_cell_mode(0,TreeItem::CELL_MODE_STRING); + ti->set_editable(0,true); + ti->set_selectable(0,true); + ti->set_text(0,E->get()); + ti->set_metadata(0,E->get()); + ti->add_button(0,get_icon("Play","EditorIcons"),0); + ti->add_button(1,get_icon("Edit","EditorIcons"),1); + + Ref<Sample> smp = sample_library->get_sample(E->get()); + + Ref<ImageTexture> preview( memnew( ImageTexture )); + preview->create(128,16,Image::FORMAT_RGB); + SampleEditor::generate_preview_texture(smp,preview); + ti->set_cell_mode(1,TreeItem::CELL_MODE_ICON); + ti->set_selectable(1,false); + ti->set_editable(1,false); + ti->set_icon(1,preview); + + + ti->set_cell_mode(2,TreeItem::CELL_MODE_STRING); + ti->set_editable(2,false); + ti->set_selectable(2,false); + Ref<Sample> s = sample_library->get_sample(E->get()); + ti->set_text(2,String()+/*itos(s->get_length())+" frames ("+String::num(s->get_length()/(float)s->get_mix_rate(),2)+" s), "+*/(s->get_format()==Sample::FORMAT_PCM16?"16 Bits, ":"8 bits, ")+(s->is_stereo()?"Stereo":"Mono")); + + ti->set_cell_mode(3,TreeItem::CELL_MODE_RANGE); + ti->set_range_config(3,-60,24,0.01); + ti->set_selectable(3,true); + ti->set_editable(3,true); + ti->set_range(3,sample_library->sample_get_volume_db(E->get())); + + ti->set_cell_mode(4,TreeItem::CELL_MODE_RANGE); + ti->set_range_config(4,0.01,100,0.01); + ti->set_selectable(4,true); + ti->set_editable(4,true); + ti->set_range(4,sample_library->sample_get_pitch_scale(E->get())); + + //ti->set_cell_mode(5,TreeItem::CELL_MODE_CUSTOM); + //ti->set_text(5,"Edit.."); + //ti->set_selectable(5,true); + //ti->set_editable(5,true); + } + + //player->add_sample("default",sample); +} + + + +void SampleLibraryEditor::edit(Ref<SampleLibrary> p_sample_library) { + + sample_library=p_sample_library; + + + if (!sample_library.is_null()) { + player->set_sample_library(sample_library); + _update_library(); + } else { + + hide(); + set_fixed_process(false); + } + +} + + + +void SampleLibraryEditor::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_input_event"),&SampleLibraryEditor::_input_event); + ObjectTypeDB::bind_method(_MD("_play_pressed"),&SampleLibraryEditor::_play_pressed); + ObjectTypeDB::bind_method(_MD("_stop_pressed"),&SampleLibraryEditor::_stop_pressed); + ObjectTypeDB::bind_method(_MD("_load_pressed"),&SampleLibraryEditor::_load_pressed); + ObjectTypeDB::bind_method(_MD("_item_edited"),&SampleLibraryEditor::_item_edited); + ObjectTypeDB::bind_method(_MD("_delete_pressed"),&SampleLibraryEditor::_delete_pressed); + ObjectTypeDB::bind_method(_MD("_delete_confirm_pressed"),&SampleLibraryEditor::_delete_confirm_pressed); + ObjectTypeDB::bind_method(_MD("_file_load_request"),&SampleLibraryEditor::_file_load_request); + ObjectTypeDB::bind_method(_MD("_update_library"),&SampleLibraryEditor::_update_library); + ObjectTypeDB::bind_method(_MD("_button_pressed"),&SampleLibraryEditor::_button_pressed); +} + +SampleLibraryEditor::SampleLibraryEditor() { + + player = memnew(SamplePlayer); + add_child(player); + add_style_override("panel", get_stylebox("panel","Panel")); + + + play = memnew( Button ); + + play->set_pos(Point2( 5, 5 )); + play->set_size( Size2(1,1 ) ); + play->set_toggle_mode(true); + //add_child(play); + + stop = memnew( Button ); + + stop->set_pos(Point2( 5, 5 )); + stop->set_size( Size2(1,1 ) ); + //stop->set_toggle_mode(true); + add_child(stop); + + load = memnew( Button ); + + load->set_pos(Point2( 35, 5 )); + load->set_size( Size2(1,1 ) ); + add_child(load); + + _delete = memnew( Button ); + + file = memnew( FileDialog ); + add_child(file); + List<String> extensions; + ResourceLoader::get_recognized_extensions_for_type("Sample",&extensions); + for(int i=0;i<extensions.size();i++) + file->add_filter("*."+extensions[i]); + file->set_mode(FileDialog::MODE_OPEN_FILES); + + _delete->set_pos(Point2( 65, 5 )); + _delete->set_size( Size2(1,1 ) ); + add_child(_delete); + + tree = memnew( Tree ); + tree->set_columns(5); + add_child(tree); + tree->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_BEGIN,5); + tree->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,5); + tree->set_anchor_and_margin(MARGIN_TOP,ANCHOR_BEGIN,30); + tree->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_END,5); + tree->set_column_titles_visible(true); + tree->set_column_title(0,"Name"); + + tree->set_column_title(1,"Preview"); + tree->set_column_title(2,"Format"); + tree->set_column_title(3,"dB"); + tree->set_column_title(4,"PScale"); + tree->set_column_min_width(1,150); + tree->set_column_min_width(2,100); + tree->set_column_min_width(3,50); + tree->set_column_min_width(4,50); + tree->set_column_expand(1,false); + tree->set_column_expand(2,false); + tree->set_column_expand(3,false); + tree->set_column_expand(4,false); + + dialog = memnew( ConfirmationDialog ); + add_child( dialog ); + + tree->connect("button_pressed",this,"_button_pressed"); + play->connect("pressed", this,"_play_pressed"); + stop->connect("pressed", this,"_stop_pressed"); + load->connect("pressed", this,"_load_pressed"); + _delete->connect("pressed", this,"_delete_pressed"); + file->connect("files_selected", this,"_file_load_request"); + //dialog->connect("confirmed", this,"_delete_confirm_pressed"); + tree->connect("item_edited", this,"_item_edited"); + + +} + + +void SampleLibraryEditorPlugin::edit(Object *p_object) { + + sample_library_editor->set_undo_redo(&get_undo_redo()); + SampleLibrary * s = p_object->cast_to<SampleLibrary>(); + if (!s) + return; + + sample_library_editor->edit(Ref<SampleLibrary>(s)); +} + +bool SampleLibraryEditorPlugin::handles(Object *p_object) const { + + return p_object->is_type("SampleLibrary"); +} + +void SampleLibraryEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + sample_library_editor->show(); +// sample_library_editor->set_process(true); + } else { + + sample_library_editor->hide(); +// sample_library_editor->set_process(false); + } + +} + +SampleLibraryEditorPlugin::SampleLibraryEditorPlugin(EditorNode *p_node) { + + editor=p_node; + sample_library_editor = memnew( SampleLibraryEditor ); + editor->get_viewport()->add_child(sample_library_editor); + sample_library_editor->set_area_as_parent_rect(); +// sample_library_editor->set_anchor( MARGIN_TOP, Control::ANCHOR_END); +// sample_library_editor->set_margin( MARGIN_TOP, 120 ); + sample_library_editor->hide(); + + + +} + + +SampleLibraryEditorPlugin::~SampleLibraryEditorPlugin() +{ +} + + diff --git a/tools/editor/plugins/sample_library_editor_plugin.h b/tools/editor/plugins/sample_library_editor_plugin.h new file mode 100644 index 000000000..a6ce764b9 --- /dev/null +++ b/tools/editor/plugins/sample_library_editor_plugin.h @@ -0,0 +1,107 @@ +/*************************************************************************/ +/* sample_library_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef SAMPLE_LIBRARY_EDITOR_PLUGIN_H +#define SAMPLE_LIBRARY_EDITOR_PLUGIN_H + + + +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" +#include "scene/audio/sample_player.h" +#include "scene/resources/sample.h" +#include "scene/gui/tree.h" +#include "scene/gui/file_dialog.h" +#include "scene/gui/dialogs.h" + + +class SampleLibraryEditor : public Panel { + + OBJ_TYPE(SampleLibraryEditor, Panel ); + + + + SamplePlayer *player; + Ref<SampleLibrary> sample_library; + Button *stop; + Button *play; + Button *load; + Button *_delete; + Tree *tree; + + FileDialog *file; + + ConfirmationDialog *dialog; + + + void _play_pressed(); + void _stop_pressed(); + void _load_pressed(); + void _file_load_request(const DVector<String>& p_path); + void _delete_pressed(); + void _delete_confirm_pressed(); + void _update_library(); + void _item_edited(); + + UndoRedo *undo_redo; + + void _button_pressed(Object *p_item,int p_column, int p_id); + +protected: + void _notification(int p_what); + void _input_event(InputEvent p_event); + static void _bind_methods(); +public: + + void set_undo_redo(UndoRedo *p_undo_redo) {undo_redo=p_undo_redo; } + + void edit(Ref<SampleLibrary> p_sample); + SampleLibraryEditor(); +}; + +class SampleLibraryEditorPlugin : public EditorPlugin { + + OBJ_TYPE( SampleLibraryEditorPlugin, EditorPlugin ); + + SampleLibraryEditor *sample_library_editor; + EditorNode *editor; + +public: + + virtual String get_name() const { return "SampleLibrary"; } + 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); + + SampleLibraryEditorPlugin(EditorNode *p_node); + ~SampleLibraryEditorPlugin(); + +}; + +#endif // SAMPLE_LIBRARY_EDITOR_PLUGIN_H diff --git a/tools/editor/plugins/sample_player_editor_plugin.cpp b/tools/editor/plugins/sample_player_editor_plugin.cpp new file mode 100644 index 000000000..fa8a285e4 --- /dev/null +++ b/tools/editor/plugins/sample_player_editor_plugin.cpp @@ -0,0 +1,192 @@ +/*************************************************************************/ +/* sample_player_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "sample_player_editor_plugin.h" +#include "scene/resources/sample_library.h" + + +void SamplePlayerEditor::_notification(int p_what) { + + if (p_what==NOTIFICATION_ENTER_SCENE) { + play->set_icon( get_icon("Play","EditorIcons") ); + stop->set_icon( get_icon("Stop","EditorIcons") ); + } + +} + +void SamplePlayerEditor::_node_removed(Node *p_node) { + + if(p_node==node) { + node=NULL; + hide(); + } + +} + +void SamplePlayerEditor::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_play"),&SamplePlayerEditor::_play); + ObjectTypeDB::bind_method(_MD("_stop"),&SamplePlayerEditor::_stop); + +} + + +void SamplePlayerEditor::_play() { + + if (!node) + return; + if (samples->get_item_count()<=0) + return; + + node->call("play",samples->get_item_text( samples->get_selected() )); +} + +void SamplePlayerEditor::_stop() { + + if (!node) + return; + if (samples->get_item_count()<=0) + return; + + node->call("stop_all"); + +} + + +void SamplePlayerEditor::_update_sample_library() { + + samples->clear(); + Ref<SampleLibrary> sl = node->call("get_sample_library"); + if (sl.is_null()) { + samples->add_item("<NO SAMPLE LIBRARY>"); + return; //no sample library;; + } + + List<StringName> samplenames; + sl->get_sample_list(&samplenames); + for(List<StringName>::Element *E=samplenames.front();E;E=E->next()) { + samples->add_item(E->get()); + } + +} + +void SamplePlayerEditor::edit(Node *p_sample_player) { + + node=p_sample_player; + if (node) { + _update_sample_library(); + } + +} +SamplePlayerEditor::SamplePlayerEditor() { + + + play = memnew( Button ); + + play->set_pos(Point2( 5, 5 )); + play->set_toggle_mode(true); + play->set_anchor_and_margin(MARGIN_LEFT,Control::ANCHOR_END,250); + play->set_anchor_and_margin(MARGIN_RIGHT,Control::ANCHOR_END,230); + play->set_anchor_and_margin(MARGIN_TOP,Control::ANCHOR_BEGIN,0); + play->set_anchor_and_margin(MARGIN_BOTTOM,Control::ANCHOR_BEGIN,0); + + add_child(play); + + stop = memnew( Button ); + + stop->set_pos(Point2( 35, 5 )); + stop->set_toggle_mode(true); + stop->set_anchor_and_margin(MARGIN_LEFT,Control::ANCHOR_END,220); + stop->set_anchor_and_margin(MARGIN_RIGHT,Control::ANCHOR_END,200); + stop->set_anchor_and_margin(MARGIN_TOP,Control::ANCHOR_BEGIN,0); + stop->set_anchor_and_margin(MARGIN_BOTTOM,Control::ANCHOR_BEGIN,0); + add_child(stop); + + samples = memnew( OptionButton ); + samples->set_anchor_and_margin(MARGIN_LEFT,Control::ANCHOR_END,190); + samples->set_anchor_and_margin(MARGIN_RIGHT,Control::ANCHOR_END,5); + samples->set_anchor_and_margin(MARGIN_TOP,Control::ANCHOR_BEGIN,0); + samples->set_anchor_and_margin(MARGIN_BOTTOM,Control::ANCHOR_BEGIN,0); + add_child(samples); + + play->connect("pressed", this,"_play"); + stop->connect("pressed", this,"_stop"); + +} + + +void SamplePlayerEditorPlugin::edit(Object *p_object) { + + sample_player_editor->edit(p_object->cast_to<Node>()); +} + +bool SamplePlayerEditorPlugin::handles(Object *p_object) const { + + return p_object->is_type("SamplePlayer2D") || p_object->is_type("SamplePlayer") || p_object->is_type("SpatialSamplePlayer"); +} + +void SamplePlayerEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + sample_player_editor->show(); + sample_player_editor->set_fixed_process(true); + } else { + + sample_player_editor->hide(); + sample_player_editor->set_fixed_process(false); + sample_player_editor->edit(NULL); + } + +} + +SamplePlayerEditorPlugin::SamplePlayerEditorPlugin(EditorNode *p_node) { + + editor=p_node; + sample_player_editor = memnew( SamplePlayerEditor ); + editor->get_viewport()->add_child(sample_player_editor); + + sample_player_editor->set_anchor(MARGIN_LEFT,Control::ANCHOR_END); + sample_player_editor->set_anchor(MARGIN_RIGHT,Control::ANCHOR_END); + sample_player_editor->set_margin(MARGIN_LEFT,250); + sample_player_editor->set_margin(MARGIN_RIGHT,0); + sample_player_editor->set_margin(MARGIN_TOP,0); + sample_player_editor->set_margin(MARGIN_BOTTOM,10); + + + sample_player_editor->hide(); + + + +} + + +SamplePlayerEditorPlugin::~SamplePlayerEditorPlugin() +{ +} + + diff --git a/tools/editor/plugins/sample_player_editor_plugin.h b/tools/editor/plugins/sample_player_editor_plugin.h new file mode 100644 index 000000000..4e35e4d8b --- /dev/null +++ b/tools/editor/plugins/sample_player_editor_plugin.h @@ -0,0 +1,87 @@ +/*************************************************************************/ +/* sample_player_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef SAMPLE_PLAYER_EDITOR_PLUGIN_H +#define SAMPLE_PLAYER_EDITOR_PLUGIN_H + +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" +#include "scene/3d/spatial_sample_player.h" +#include "scene/gui/option_button.h" +#include "scene/audio/sample_player.h" + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + +class SamplePlayerEditor : public Control { + + OBJ_TYPE(SamplePlayerEditor, Control ); + + Panel *panel; + Button * play; + Button * stop; + OptionButton *samples; + Node *node; + + + void _update_sample_library(); + void _play(); + void _stop(); + +protected: + void _notification(int p_what); + void _node_removed(Node *p_node); + static void _bind_methods(); +public: + + void edit(Node *p_sample_player); + SamplePlayerEditor(); +}; + +class SamplePlayerEditorPlugin : public EditorPlugin { + + OBJ_TYPE( SamplePlayerEditorPlugin, EditorPlugin ); + + SamplePlayerEditor *sample_player_editor; + EditorNode *editor; + +public: + + virtual String get_name() const { return "SamplePlayer"; } + 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); + + SamplePlayerEditorPlugin(EditorNode *p_node); + ~SamplePlayerEditorPlugin(); + +}; + +#endif // SAMPLE_PLAYER_EDITOR_PLUGIN_H diff --git a/tools/editor/plugins/script_editor_plugin.cpp b/tools/editor/plugins/script_editor_plugin.cpp new file mode 100644 index 000000000..1a1791639 --- /dev/null +++ b/tools/editor/plugins/script_editor_plugin.cpp @@ -0,0 +1,1519 @@ +/*************************************************************************/ +/* script_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "script_editor_plugin.h" +#include "tools/editor/editor_settings.h" +#include "io/resource_loader.h" +#include "io/resource_saver.h" +#include "os/keyboard.h" +#include "os/os.h" +#include "tools/editor/editor_node.h" +#include "tools/editor/script_editor_debugger.h" +#include "globals.h" +#include "os/file_access.h" +#include "scene/main/viewport.h" +#include "os/keyboard.h" +/*** SCRIPT EDITOR ****/ + + + + +void ScriptEditorQuickOpen::popup(const Vector<String>& p_functions, bool p_dontclear) { + + popup_centered_ratio(0.6); + if (p_dontclear) + search_box->select_all(); + else + search_box->clear(); + search_box->grab_focus(); + functions=p_functions; + _update_search(); + + +} + + +void ScriptEditorQuickOpen::_text_changed(const String& p_newtext) { + + _update_search(); +} + +void ScriptEditorQuickOpen::_sbox_input(const InputEvent& p_ie) { + + if (p_ie.type==InputEvent::KEY && ( + p_ie.key.scancode == KEY_UP || + p_ie.key.scancode == KEY_DOWN || + p_ie.key.scancode == KEY_PAGEUP || + p_ie.key.scancode == KEY_PAGEDOWN ) ) { + + search_options->call("_input_event",p_ie); + search_box->accept_event(); + } + +} + + + +void ScriptEditorQuickOpen::_update_search() { + + + search_options->clear(); + TreeItem *root = search_options->create_item(); + + for(int i=0;i<functions.size();i++) { + + String file = functions[i]; + if ((search_box->get_text()=="" || file.findn(search_box->get_text())!=-1)) { + + TreeItem *ti = search_options->create_item(root); + ti->set_text(0,file); + if (root->get_children()==ti) + ti->select(0); + + } + } + + get_ok()->set_disabled(root->get_children()==NULL); + +} + +void ScriptEditorQuickOpen::_confirmed() { + + TreeItem *ti = search_options->get_selected(); + if (!ti) + return; + int line = ti->get_text(0).get_slice(":",1).to_int(); + + emit_signal("goto_line",line-1); + hide(); +} + +void ScriptEditorQuickOpen::_notification(int p_what) { + + if (p_what==NOTIFICATION_ENTER_SCENE) { + + connect("confirmed",this,"_confirmed"); + } +} + + + + +void ScriptEditorQuickOpen::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_text_changed"),&ScriptEditorQuickOpen::_text_changed); + ObjectTypeDB::bind_method(_MD("_confirmed"),&ScriptEditorQuickOpen::_confirmed); + ObjectTypeDB::bind_method(_MD("_sbox_input"),&ScriptEditorQuickOpen::_sbox_input); + + ADD_SIGNAL(MethodInfo("goto_line",PropertyInfo(Variant::INT,"line"))); + +} + + +ScriptEditorQuickOpen::ScriptEditorQuickOpen() { + + + VBoxContainer *vbc = memnew( VBoxContainer ); + add_child(vbc); + set_child_rect(vbc); + search_box = memnew( LineEdit ); + vbc->add_margin_child("Search:",search_box); + search_box->connect("text_changed",this,"_text_changed"); + search_box->connect("input_event",this,"_sbox_input"); + search_options = memnew( Tree ); + vbc->add_margin_child("Matches:",search_options,true); + get_ok()->set_text("Open"); + get_ok()->set_disabled(true); + register_text_enter(search_box); + set_hide_on_ok(false); + search_options->connect("item_activated",this,"_confirmed"); + search_options->set_hide_root(true); +} + + +///////////////////////////////// + +ScriptEditor *ScriptEditor::script_editor=NULL; + +Vector<String> ScriptTextEditor::get_functions() { + + + String errortxt; + int line=-1,col; + TextEdit *te=get_text_edit(); + String text = te->get_text(); + List<String> fnc; + + if (script->get_language()->validate(text,line,col,errortxt,script->get_path(),&fnc)) { + + //if valid rewrite functions to latest + functions.clear(); + for (List<String>::Element *E=fnc.front();E;E=E->next()) { + + functions.push_back(E->get()); + } + + + } + + return functions; +} + +void ScriptTextEditor::apply_code() { + + if (script.is_null()) + return; + script->set_source_code(get_text_edit()->get_text()); +} + +Ref<Script> ScriptTextEditor::get_edited_script() const { + + return script; +} + +void ScriptTextEditor::_load_theme_settings() { + + get_text_edit()->clear_colors(); + + /* keyword color */ + + + get_text_edit()->set_custom_bg_color(EDITOR_DEF("text_editor/background_color",Color(0,0,0,0))); + get_text_edit()->add_color_override("font_color",EDITOR_DEF("text_editor/text_color",Color(0,0,0))); + get_text_edit()->add_color_override("font_selected_color",EDITOR_DEF("text_editor/text_selected_color",Color(1,1,1))); + get_text_edit()->add_color_override("selection_color",EDITOR_DEF("text_editor/selection_color",Color(0.2,0.2,1))); + + Color keyword_color= EDITOR_DEF("text_editor/keyword_color",Color(0.5,0.0,0.2)); + + get_text_edit()->set_syntax_coloring(true); + List<String> keywords; + script->get_language()->get_reserved_words(&keywords); + for(List<String>::Element *E=keywords.front();E;E=E->next()) { + + get_text_edit()->add_keyword_color(E->get(),keyword_color); + } + + //colorize core types + Color basetype_color= EDITOR_DEF("text_editor/base_type_color",Color(0.3,0.3,0.0)); + + get_text_edit()->add_keyword_color("Vector2",basetype_color); + get_text_edit()->add_keyword_color("Vector3",basetype_color); + get_text_edit()->add_keyword_color("Plane",basetype_color); + get_text_edit()->add_keyword_color("Quat",basetype_color); + get_text_edit()->add_keyword_color("AABB",basetype_color); + get_text_edit()->add_keyword_color("Matrix3",basetype_color); + get_text_edit()->add_keyword_color("Transform",basetype_color); + get_text_edit()->add_keyword_color("Color",basetype_color); + get_text_edit()->add_keyword_color("Image",basetype_color); + get_text_edit()->add_keyword_color("InputEvent",basetype_color); + + //colorize engine types + Color type_color= EDITOR_DEF("text_editor/engine_type_color",Color(0.0,0.2,0.4)); + + List<String> types; + ObjectTypeDB::get_type_list(&types); + + for(List<String>::Element *E=types.front();E;E=E->next()) { + + get_text_edit()->add_keyword_color(E->get(),type_color); + } + + //colorize comments + Color comment_color = EDITOR_DEF("text_editor/comment_color",Color::hex(0x797e7eff)); + List<String> comments; + script->get_language()->get_comment_delimiters(&comments); + + for(List<String>::Element *E=comments.front();E;E=E->next()) { + + String comment = E->get(); + String beg = comment.get_slice(" ",0); + String end = comment.get_slice_count(" ")>1?comment.get_slice(" ",1):String(); + + get_text_edit()->add_color_region(beg,end,comment_color,end==""); + } + + //colorize strings + Color string_color = EDITOR_DEF("text_editor/string_color",Color::hex(0x6b6f00ff)); + List<String> strings; + script->get_language()->get_string_delimiters(&strings); + + for (List<String>::Element *E=strings.front();E;E=E->next()) { + + String string = E->get(); + String beg = string.get_slice(" ",0); + String end = string.get_slice_count(" ")>1?string.get_slice(" ",1):String(); + get_text_edit()->add_color_region(beg,end,string_color,end==""); + } + + //colorize symbols + Color symbol_color= EDITOR_DEF("text_editor/symbol_color",Color::hex(0x005291ff)); + get_text_edit()->set_symbol_color(symbol_color); + +} + + +void ScriptTextEditor::reload_text() { + + ERR_FAIL_COND(script.is_null()) ; + + get_text_edit()->set_text(script->get_source_code()); + get_text_edit()->clear_undo_history(); + _line_col_changed(); + +} + +void ScriptTextEditor::_notification(int p_what) { + + if (p_what==NOTIFICATION_READY) { + + _update_name(); + } +} + +void ScriptTextEditor::_update_name() { + + String name; + + if (script->get_path().find("local://")==-1 && script->get_path().find("::")==-1) { + name=script->get_path().get_file(); + if (get_text_edit()->get_version()!=get_text_edit()->get_saved_version()) { + name+="(*)"; + } + } else if (script->get_name()!="") + name=script->get_name(); + else + name=script->get_type()+"("+itos(script->get_instance_ID())+")"; + + + if (name!=String(get_name())) { + + set_name(name); + + } + + if (!has_meta("_tab_icon")) { + if (get_parent_control() && get_parent_control()->has_icon(script->get_type(),"EditorIcons")) { + set_meta("_tab_icon",get_parent_control()->get_icon(script->get_type(),"EditorIcons")); + } + } + + +} + +void ScriptTextEditor::set_edited_script(const Ref<Script>& p_script) { + + ERR_FAIL_COND(!script.is_null()); + + script=p_script; + + + _load_theme_settings(); + + get_text_edit()->set_text(script->get_source_code()); + get_text_edit()->clear_undo_history(); + get_text_edit()->tag_saved_version(); + + + _update_name(); + + _line_col_changed(); +} + + +void ScriptTextEditor::_validate_script() { + + String errortxt; + int line=-1,col; + TextEdit *te=get_text_edit(); + + String text = te->get_text(); + List<String> fnc; + + if (!script->get_language()->validate(text,line,col,errortxt,script->get_path(),&fnc)) { + String error_text="error("+itos(line)+","+itos(col)+"): "+errortxt; + set_error(error_text); + } else { + set_error(""); + line=-1; + if (!script->is_tool()) { + script->set_source_code(text); + script->reload(); //will update all the variables in property editors + } + + functions.clear(); + for (List<String>::Element *E=fnc.front();E;E=E->next()) { + + functions.push_back(E->get()); + } + + } + + line--; + for(int i=0;i<te->get_line_count();i++) { + te->set_line_as_marked(i,line==i); + } + + _update_name(); +} + +void ScriptTextEditor::_code_complete_script(const String& p_code, const String& p_keyword,int p_line, List<String>* r_options) { + + Error err = script->get_language()->complete_keyword(p_code,p_line,script->get_path().get_base_dir(),p_keyword,r_options); + +} + +ScriptTextEditor::ScriptTextEditor() { + + get_text_edit()->set_draw_tabs(true); +} + +/*** SCRIPT EDITOR ******/ + + +String ScriptEditor::_get_debug_tooltip(const String&p_text,Node *_ste) { + + ScriptTextEditor *ste=_ste->cast_to<ScriptTextEditor>(); + + String val = debugger->get_var_value(p_text); + if (val!=String()) { + return p_text+": "+val; + } else { + + return String(); + } +} + +void ScriptEditor::_breaked(bool p_breaked,bool p_can_debug) { + + debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_NEXT), !(p_breaked && p_can_debug)); + debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_STEP), !(p_breaked && p_can_debug) ); + debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_BREAK), p_breaked ); + debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), !p_breaked ); + +} + +void ScriptEditor::_show_debugger(bool p_show) { + + debug_menu->get_popup()->set_item_checked( debug_menu->get_popup()->get_item_index(DEBUG_SHOW), p_show); + +} + +void ScriptEditor::_goto_script_line2(int p_line) { + + int selected = tab_container->get_current_tab(); + if (selected<0 || selected>=tab_container->get_child_count()) + return; + + ScriptTextEditor *current = tab_container->get_child(selected)->cast_to<ScriptTextEditor>(); + if (!current) + return; + + current->get_text_edit()->cursor_set_line(p_line); + +} + +void ScriptEditor::_goto_script_line(REF p_script,int p_line) { + + + editor->push_item(p_script.ptr()); + _goto_script_line2(p_line); + +} + +void ScriptEditor::_close_current_tab() { + + int selected = tab_container->get_current_tab(); + if (selected<0 || selected>=tab_container->get_child_count()) + return; + + ScriptTextEditor *current = tab_container->get_child(selected)->cast_to<ScriptTextEditor>(); + if (!current) + return; + + apply_scripts(); + + int idx = tab_container->get_current_tab(); + memdelete(current); + if (idx>=tab_container->get_child_count()) + idx=tab_container->get_child_count()-1; + if (idx>=0) + tab_container->set_current_tab(idx); + + _update_window_menu(); + _save_files_state(); + +} + + +void ScriptEditor::_resave_scripts(const String& p_str) { + + apply_scripts(); + + for(int i=0;i<tab_container->get_child_count();i++) { + + ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>(); + if (!ste) + continue; + + + Ref<Script> script = ste->get_edited_script(); + + if (script->get_path()=="" || script->get_path().find("local://")!=-1 || script->get_path().find("::")!=-1) + continue; //internal script, who cares + + + editor->save_resource(script); + ste->get_text_edit()->tag_saved_version(); + } + + disk_changed->hide(); + +} + +void ScriptEditor::_reload_scripts(){ + + + + for(int i=0;i<tab_container->get_child_count();i++) { + + ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>(); + if (!ste) { + + continue; + } + + + Ref<Script> script = ste->get_edited_script(); + + if (script->get_path()=="" || script->get_path().find("local://")!=-1 || script->get_path().find("::")!=-1) { + + continue; //internal script, who cares + } + + + Ref<Script> rel_script = ResourceLoader::load(script->get_path(),script->get_type(),true); + ERR_CONTINUE(!rel_script.is_valid()); + script->set_source_code( rel_script->get_source_code() ); + script->set_last_modified_time( rel_script->get_last_modified_time() ); + script->reload(); + ste->reload_text(); + + + } + + disk_changed->hide(); + +} + + + +void ScriptEditor::_res_saved_callback(const Ref<Resource>& p_res) { + + + + for(int i=0;i<tab_container->get_child_count();i++) { + + ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>(); + if (!ste) { + + continue; + } + + + Ref<Script> script = ste->get_edited_script(); + + if (script->get_path()=="" || script->get_path().find("local://")!=-1 || script->get_path().find("::")!=-1) { + continue; //internal script, who cares + } + + if (script==p_res) { + + ste->get_text_edit()->tag_saved_version(); + } + + ste->_update_name(); + + } + +} + +bool ScriptEditor::_test_script_times_on_disk() { + + + disk_changed_list->clear(); + TreeItem *r = disk_changed_list->create_item(); + disk_changed_list->set_hide_root(true); + + bool all_ok=true; + + + for(int i=0;i<tab_container->get_child_count();i++) { + + ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>(); + if (!ste) + continue; + + + Ref<Script> script = ste->get_edited_script(); + + if (script->get_path()=="" || script->get_path().find("local://")!=-1 || script->get_path().find("::")!=-1) + continue; //internal script, who cares + + + uint64_t last_date = script->get_last_modified_time(); + uint64_t date = FileAccess::get_modified_time(script->get_path()); + + //printf("last date: %lli vs date: %lli\n",last_date,date); + if (last_date!=date) { + + TreeItem *ti = disk_changed_list->create_item(r); + ti->set_text(0,script->get_path().get_file()); + all_ok=false; + //r->set_metadata(0,); + } + } + + + + if (!all_ok) + disk_changed->call_deferred("popup_centered_ratio",0.5); + + return all_ok; +} + +void ScriptEditor::_menu_option(int p_option) { + + + if (p_option==FILE_OPEN) { + + editor->open_resource("Script"); + return; + } + int selected = tab_container->get_current_tab(); + if (selected<0 || selected>=tab_container->get_child_count()) + return; + + ScriptTextEditor *current = tab_container->get_child(selected)->cast_to<ScriptTextEditor>(); + if (!current) + return; + + switch(p_option) { + case FILE_SAVE: { + + if (!_test_script_times_on_disk()) + return; + editor->save_resource( current->get_edited_script() ); + + } break; + case FILE_SAVE_AS: { + + editor->save_resource_as( current->get_edited_script() ); + + } break; + case FILE_SAVE_ALL: { + + if (!_test_script_times_on_disk()) + return; + + for(int i=0;i<tab_container->get_child_count();i++) { + + ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>(); + if (!ste) + continue; + + + Ref<Script> script = ste->get_edited_script(); + + if (script->get_path()=="" || script->get_path().find("local://")!=-1 || script->get_path().find("::")!=-1) + continue; //internal script, who cares + + + editor->save_resource( script ); + } + + + } break; + case EDIT_UNDO: { + + + current->get_text_edit()->undo(); + } break; + case EDIT_REDO: { + current->get_text_edit()->redo(); + + } break; + case EDIT_CUT: { + + current->get_text_edit()->cut(); + } break; + case EDIT_COPY: { + current->get_text_edit()->copy(); + + } break; + case EDIT_PASTE: { + current->get_text_edit()->paste(); + + } break; + case EDIT_SELECT_ALL: { + + current->get_text_edit()->select_all(); + + } break; + case EDIT_COMPLETE: { + + current->get_text_edit()->query_code_comple(); + + } break; + case SEARCH_FIND: { + + find_replace_dialog->set_text_edit(current->get_text_edit()); + find_replace_dialog->popup_search(); + } break; + case SEARCH_FIND_NEXT: { + + find_replace_dialog->set_text_edit(current->get_text_edit()); + find_replace_dialog->search_next(); + } break; + case SEARCH_REPLACE: { + + find_replace_dialog->set_text_edit(current->get_text_edit()); + find_replace_dialog->popup_replace(); + } break; + case SEARCH_LOCATE_FUNCTION: { + + if (!current) + return; + quick_open->popup(current->get_functions()); + } break; + case SEARCH_GOTO_LINE: { + + goto_line_dialog->popup_find_line(current->get_text_edit()); + } break; + case DEBUG_TOGGLE_BREAKPOINT: { + int line=current->get_text_edit()->cursor_get_line(); + bool dobreak = !current->get_text_edit()->is_line_set_as_breakpoint(line); + current->get_text_edit()->set_line_as_breakpoint(line,dobreak); + } break; + case DEBUG_NEXT: { + + if (debugger) + debugger->debug_next(); + } break; + case DEBUG_STEP: { + + if (debugger) + debugger->debug_step(); + + } break; + case DEBUG_BREAK: { + + if (debugger) + debugger->debug_break(); + + } break; + case DEBUG_CONTINUE: { + + if (debugger) + debugger->debug_continue(); + + } break; + case DEBUG_SHOW: { + if (debugger) { + bool visible = debug_menu->get_popup()->is_item_checked( debug_menu->get_popup()->get_item_index(DEBUG_SHOW) ); + debug_menu->get_popup()->set_item_checked( debug_menu->get_popup()->get_item_index(DEBUG_SHOW), !visible); + if (visible) + debugger->hide(); + else + debugger->show(); + } + } break; + case WINDOW_CLOSE: { + + erase_tab_confirm->set_text("Close Tab?:\n\""+current->get_name()+"\""); + erase_tab_confirm->popup_centered(Point2(250,80)); + } break; + case WINDOW_MOVE_LEFT: { + + if (tab_container->get_current_tab()>0) { + tab_container->call_deferred("set_current_tab",tab_container->get_current_tab()-1); + tab_container->move_child(current,tab_container->get_current_tab()-1); + _update_window_menu(); + } + } break; + case WINDOW_MOVE_RIGHT: { + + if (tab_container->get_current_tab()<tab_container->get_child_count()-1) { + tab_container->call_deferred("set_current_tab",tab_container->get_current_tab()+1); + tab_container->move_child(current,tab_container->get_current_tab()+1); + _update_window_menu(); + } + + + } break; + default: { + + if (p_option>=WINDOW_SELECT_BASE) { + + tab_container->set_current_tab(p_option-WINDOW_SELECT_BASE); + } + } + } + +} + +void ScriptEditor::_tab_changed(int p_which) { + + ensure_select_current(); +} + +void ScriptEditor::_notification(int p_what) { + + if (p_what==NOTIFICATION_ENTER_SCENE) { + + editor->connect("play_pressed",this,"_editor_play"); + editor->connect("pause_pressed",this,"_editor_pause"); + editor->connect("stop_pressed",this,"_editor_stop"); + editor->connect("script_add_function_request",this,"_add_callback"); + editor->connect("resource_saved",this,"_res_saved_callback"); + + + } + + if (p_what==NOTIFICATION_READY) { + _update_window_menu(); + } + + if (p_what==NOTIFICATION_EXIT_SCENE) { + + editor->disconnect("play_pressed",this,"_editor_play"); + editor->disconnect("pause_pressed",this,"_editor_pause"); + editor->disconnect("stop_pressed",this,"_editor_stop"); + + } + + if (p_what==MainLoop::NOTIFICATION_WM_FOCUS_IN) { + + _test_script_times_on_disk(); + } + + if (p_what==NOTIFICATION_PROCESS) { + + } + +} + + +static const Node * _find_node_with_script(const Node* p_node, const RefPtr & p_script) { + + if (p_node->get_script()==p_script) + return p_node; + + for(int i=0;i<p_node->get_child_count();i++) { + + const Node *result = _find_node_with_script(p_node->get_child(i),p_script); + if (result) + return result; + } + + return NULL; +} + +Dictionary ScriptEditor::get_state() const { + + apply_scripts(); + + Dictionary state; + + Array paths; + int open=-1; + + for(int i=0;i<tab_container->get_child_count();i++) { + + ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>(); + if (!ste) + continue; + + + Ref<Script> script = ste->get_edited_script(); + if (script->get_path()!="" && script->get_path().find("local://")==-1 && script->get_path().find("::")==-1) { + + paths.push_back(script->get_path()); + } else { + + + const Node *owner = _find_node_with_script(get_scene()->get_root(),script.get_ref_ptr()); + if (owner) + paths.push_back(owner->get_path()); + + } + + if (i==tab_container->get_current_tab()) + open=i; + } + + if (paths.size()) + state["sources"]=paths; + if (open!=-1) + state["current"]=open; + + + return state; +} +void ScriptEditor::set_state(const Dictionary& p_state) { + + + print_line("attempt set state: "+String(Variant(p_state))); + + if (!p_state.has("sources")) + return; //bleh + + Array sources = p_state["sources"]; + for(int i=0;i<sources.size();i++) { + + Variant source=sources[i]; + + Ref<Script> script; + + if (source.get_type()==Variant::NODE_PATH) { + + + Node *owner=get_scene()->get_root()->get_node(source); + if (!owner) + continue; + + script = owner->get_script(); + } else if (source.get_type()==Variant::STRING) { + + + script = ResourceLoader::load(source,"Script"); + } + + + if (script.is_null()) //ah well.. + continue; + + editor->call("_resource_selected",script); + } + + if (p_state.has("current")) { + tab_container->set_current_tab(p_state["current"]); + } + +} +void ScriptEditor::clear() { + + List<ScriptTextEditor*> stes; + for(int i=0;i<tab_container->get_child_count();i++) { + + ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>(); + if (!ste) + continue; + stes.push_back(ste); + + } + + while(stes.size()) { + + memdelete(stes.front()->get()); + stes.pop_front(); + } + + int idx = tab_container->get_current_tab(); + if (idx>=tab_container->get_child_count()) + idx=tab_container->get_child_count()-1; + if (idx>=0) + tab_container->set_current_tab(idx); + + _update_window_menu(); + + +} + +void ScriptEditor::_save_files_state() { + + return; //no thank you + + String rpath="_open_scripts_"+Globals::get_singleton()->get_resource_path(); + rpath=rpath.replace("\\","_-_"); + rpath=rpath.replace("/","_-_"); + rpath=rpath.replace(":","_"); + + Vector<String> scripts; + + for(int i=0;i<tab_container->get_child_count();i++) { + + ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>(); + if (!ste) + continue; + + + Ref<Script> script = ste->get_edited_script(); + if (script->get_path()!="" && script->get_path().find("local://")==-1 && script->get_path().find("::")==-1) { + + + scripts.push_back(script->get_path()); + } + } + + + EditorSettings::get_singleton()->set(rpath,scripts); + EditorSettings::get_singleton()->save(); + +} + +void ScriptEditor::_load_files_state() { + return; + + String rpath="_open_scripts_"+Globals::get_singleton()->get_resource_path(); + rpath=rpath.replace("\\","_-_"); + rpath=rpath.replace("/","_-_"); + rpath=rpath.replace(":","_"); + + if (EditorSettings::get_singleton()->has(rpath)) { + + Vector<String> open_files=EditorSettings::get_singleton()->get("rpath"); + for(int i=0;i<open_files.size();i++) { + Ref<Script> scr = ResourceLoader::load(open_files[i]); + if (!scr.is_valid()) + continue; + + editor->edit_resource(scr); + } + } + +} + +void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) { + + for(int i=0;i<tab_container->get_child_count();i++) { + + ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>(); + if (!ste) + continue; + + List<int> bpoints; + ste->get_text_edit()->get_breakpoints(&bpoints); + + Ref<Script> script = ste->get_edited_script(); + String base = script->get_path(); + ERR_CONTINUE( base.begins_with("local://") || base=="" ); + + for(List<int>::Element *E=bpoints.front();E;E=E->next()) { + + p_breakpoints->push_back(base+":"+itos(E->get()+1)); + } + } + +} + + + +void ScriptEditor::_bind_methods() { + + ObjectTypeDB::bind_method("_tab_changed",&ScriptEditor::_tab_changed); + ObjectTypeDB::bind_method("_menu_option",&ScriptEditor::_menu_option); + ObjectTypeDB::bind_method("_close_current_tab",&ScriptEditor::_close_current_tab); + ObjectTypeDB::bind_method("_editor_play",&ScriptEditor::_editor_play); + ObjectTypeDB::bind_method("_editor_pause",&ScriptEditor::_editor_pause); + ObjectTypeDB::bind_method("_editor_stop",&ScriptEditor::_editor_stop); + ObjectTypeDB::bind_method("_add_callback",&ScriptEditor::_add_callback); + ObjectTypeDB::bind_method("_reload_scripts",&ScriptEditor::_reload_scripts); + ObjectTypeDB::bind_method("_resave_scripts",&ScriptEditor::_resave_scripts); + ObjectTypeDB::bind_method("_res_saved_callback",&ScriptEditor::_res_saved_callback); + ObjectTypeDB::bind_method("_goto_script_line",&ScriptEditor::_goto_script_line); + ObjectTypeDB::bind_method("_goto_script_line2",&ScriptEditor::_goto_script_line2); + ObjectTypeDB::bind_method("_breaked",&ScriptEditor::_breaked); + ObjectTypeDB::bind_method("_show_debugger",&ScriptEditor::_show_debugger); + ObjectTypeDB::bind_method("_get_debug_tooltip",&ScriptEditor::_get_debug_tooltip); + + + + +} + + +void ScriptEditor::ensure_focus_current() { + + int cidx = tab_container->get_current_tab(); + if (cidx<0 || cidx>=tab_container->get_tab_count()); + Control *c = tab_container->get_child(cidx)->cast_to<Control>(); + if (!c) + return; + ScriptTextEditor *ste = c->cast_to<ScriptTextEditor>(); + if (!ste) + return; + ste->get_text_edit()->grab_focus(); +} + +void ScriptEditor::ensure_select_current() { + + + if (tab_container->get_child_count() && tab_container->get_current_tab()>=0) { + + ScriptTextEditor *ste = tab_container->get_child(tab_container->get_current_tab())->cast_to<ScriptTextEditor>(); + if (!ste) + return; + Ref<Script> script = ste->get_edited_script(); + editor->call("_resource_selected",script); + } +} + +void ScriptEditor::edit(const Ref<Script>& p_script) { + + if (p_script.is_null()) + return; + + // see if already has it + + if (p_script->get_path().is_resource_file() && bool(EditorSettings::get_singleton()->get("external_editor/use_external_editor"))) { + + String path = EditorSettings::get_singleton()->get("external_editor/exec_path"); + String flags = EditorSettings::get_singleton()->get("external_editor/exec_flags"); + List<String> args; + flags=flags.strip_edges(); + if (flags!=String()) { + Vector<String> flagss = flags.split(" ",false); + for(int i=0;i<flagss.size();i++) + args.push_back(flagss[i]); + } + + args.push_back(Globals::get_singleton()->globalize_path(p_script->get_path())); + Error err = OS::get_singleton()->execute(path,args,false); + if (err==OK) + return; + WARN_PRINT("Couldn't open external text editor, using internal"); + } + + + for(int i=0;i<tab_container->get_child_count();i++) { + + ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>(); + if (!ste) + continue; + + if (ste->get_edited_script()==p_script) { + + if (tab_container->get_current_tab()!=i) + tab_container->set_current_tab(i); + ste->get_text_edit()->grab_focus(); + return; + } + } + + // doesn't have it, make a new one + + ScriptTextEditor *ste = memnew( ScriptTextEditor ); + ste->set_edited_script(p_script); + ste->get_text_edit()->set_tooltip_request_func(this,"_get_debug_tooltip",ste); + tab_container->add_child(ste); + tab_container->set_current_tab(tab_container->get_tab_count()-1); + + _update_window_menu(); + _save_files_state(); + +} + +void ScriptEditor::save_external_data() { + + apply_scripts(); + + + for(int i=0;i<tab_container->get_child_count();i++) { + + ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>(); + if (!ste) + continue; + + Ref<Script> script = ste->get_edited_script(); + if (script->get_path()!="" && script->get_path().find("local://")==-1 &&script->get_path().find("::")==-1) { + //external script, save it + editor->save_resource(script); + //ResourceSaver::save(script->get_path(),script); + } + } + +} + +void ScriptEditor::apply_scripts() const { + + for(int i=0;i<tab_container->get_child_count();i++) { + + ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>(); + if (!ste) + continue; + ste->apply_code(); + } + +} + +void ScriptEditor::_editor_play() { + + debugger->start(); + debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_NEXT), true ); + debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_STEP), true ); + debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_BREAK), false ); + debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), true ); + + //debugger_gui->start_listening(Globals::get_singleton()->get("debug/debug_port")); +} + +void ScriptEditor::_editor_pause() { + + +} +void ScriptEditor::_editor_stop() { + + debugger->stop(); + debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_NEXT), true ); + debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_STEP), true ); + debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_BREAK), true ); + debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), true ); +} + +void ScriptEditor::_update_window_menu() { + + int idx=0; + for(int i=0;i<tab_container->get_child_count();i++) { + + ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>(); + if (!ste) + continue; + idx++; + } + + if (idx==0) { + window_menu->set_disabled(true); + edit_menu->set_disabled(true); + search_menu->set_disabled(true); + return; + } else { + + window_menu->set_disabled(false); + edit_menu->set_disabled(false); + search_menu->set_disabled(false); + } + + window_menu->get_popup()->clear(); + window_menu->get_popup()->add_item("Close",WINDOW_CLOSE,KEY_MASK_CMD|KEY_W); + window_menu->get_popup()->add_separator(); + window_menu->get_popup()->add_item("Move Left",WINDOW_MOVE_LEFT,KEY_MASK_CMD|KEY_LEFT); + window_menu->get_popup()->add_item("Move Right",WINDOW_MOVE_RIGHT,KEY_MASK_CMD|KEY_RIGHT); + window_menu->get_popup()->add_separator(); + + idx=0; + for(int i=0;i<tab_container->get_child_count();i++) { + + ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>(); + if (!ste) + continue; + String n = ste->get_name(); + uint32_t accel=0; + if (idx<9) { + accel=KEY_MASK_ALT|(KEY_1+idx); + } + window_menu->get_popup()->add_item(n,WINDOW_SELECT_BASE+idx,accel); + idx++; + } +} + +void ScriptEditor::_add_callback(Object *p_obj, const String& p_function, const StringArray& p_args) { + + print_line("add callback! hohoho"); + ERR_FAIL_COND(!p_obj); + Ref<Script> script = p_obj->get_script(); + ERR_FAIL_COND( !script.is_valid() ); + + editor->push_item(script.ptr()); + + for(int i=0;i<tab_container->get_child_count();i++) { + + ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>(); + if (!ste) + continue; + if (ste->get_edited_script()!=script) + continue; + + String code = ste->get_text_edit()->get_text(); + int pos = script->get_language()->find_function(code,p_function); + if (pos==-1) { + //does not exist + + pos=ste->get_text_edit()->get_line_count()+2; + String func = script->get_language()->make_function("",p_function,p_args); + //code=code+func; + ste->get_text_edit()->cursor_set_line(pos+1); + ste->get_text_edit()->cursor_set_column(1000000); //none shall be that big + ste->get_text_edit()->insert_text_at_cursor("\n\n"+func); + } + + tab_container->set_current_tab(i); + ste->get_text_edit()->cursor_set_line(pos); + ste->get_text_edit()->cursor_set_column(1); + + break; + + } + +} + +ScriptEditor::ScriptEditor(EditorNode *p_editor) { + + editor=p_editor; + + menu_hb = memnew( HBoxContainer ); + add_child(menu_hb); + + v_split = memnew( VSplitContainer ); + add_child(v_split); + v_split->set_v_size_flags(SIZE_EXPAND_FILL); + + tab_container = memnew( TabContainer ); + v_split->add_child(tab_container); + tab_container->set_v_size_flags(SIZE_EXPAND_FILL); + + file_menu = memnew( MenuButton ); + menu_hb->add_child(file_menu); + file_menu->set_text("File"); + file_menu->get_popup()->add_item("Open",FILE_OPEN); + file_menu->get_popup()->add_item("Save",FILE_SAVE,KEY_MASK_ALT|KEY_S); + file_menu->get_popup()->add_item("Save As..",FILE_SAVE_AS); + file_menu->get_popup()->add_item("Save All",FILE_SAVE_ALL,KEY_MASK_CMD|KEY_MASK_SHIFT|KEY_S); + file_menu->get_popup()->connect("item_pressed", this,"_menu_option"); + + edit_menu = memnew( MenuButton ); + menu_hb->add_child(edit_menu); + edit_menu->set_text("Edit"); + edit_menu->get_popup()->add_item("Undo"); + edit_menu->get_popup()->add_item("Redo"); + edit_menu->get_popup()->add_separator(); + edit_menu->get_popup()->add_item("Cut",EDIT_CUT,KEY_MASK_CMD|KEY_X); + edit_menu->get_popup()->add_item("Copy",EDIT_COPY,KEY_MASK_CMD|KEY_C); + edit_menu->get_popup()->add_item("Paste",EDIT_PASTE,KEY_MASK_CMD|KEY_V); + edit_menu->get_popup()->add_separator(); + edit_menu->get_popup()->add_item("Select All",EDIT_SELECT_ALL,KEY_MASK_CMD|KEY_A); + edit_menu->get_popup()->add_separator(); + edit_menu->get_popup()->add_item("Complete Symbol",EDIT_COMPLETE,KEY_MASK_CMD|KEY_SPACE); + edit_menu->get_popup()->connect("item_pressed", this,"_menu_option"); + + + search_menu = memnew( MenuButton ); + menu_hb->add_child(search_menu); + search_menu->set_text("Search"); + search_menu->get_popup()->add_item("Find..",SEARCH_FIND,KEY_MASK_CMD|KEY_F); + search_menu->get_popup()->add_item("Find Next",SEARCH_FIND_NEXT,KEY_MASK_CMD|KEY_G); + search_menu->get_popup()->add_item("Replace..",SEARCH_REPLACE,KEY_MASK_CMD|KEY_R); + search_menu->get_popup()->add_separator(); + search_menu->get_popup()->add_item("Goto Function..",SEARCH_LOCATE_FUNCTION,KEY_MASK_SHIFT|KEY_MASK_CMD|KEY_F); + search_menu->get_popup()->add_item("Goto Line..",SEARCH_GOTO_LINE,KEY_MASK_CMD|KEY_L); + search_menu->get_popup()->connect("item_pressed", this,"_menu_option"); + + debug_menu = memnew( MenuButton ); + menu_hb->add_child(debug_menu); + debug_menu->set_text("Debug"); + debug_menu->get_popup()->add_item("Toggle Breakpoint",DEBUG_TOGGLE_BREAKPOINT,KEY_F9); + debug_menu->get_popup()->add_separator(); + debug_menu->get_popup()->add_item("Step Over",DEBUG_NEXT,KEY_F10); + debug_menu->get_popup()->add_item("Step Into",DEBUG_STEP,KEY_F11); + debug_menu->get_popup()->add_separator(); + debug_menu->get_popup()->add_item("Break",DEBUG_BREAK); + debug_menu->get_popup()->add_item("Continue",DEBUG_CONTINUE); + debug_menu->get_popup()->add_separator(); + debug_menu->get_popup()->add_check_item("Show Debugger",DEBUG_SHOW); + debug_menu->get_popup()->connect("item_pressed", this,"_menu_option"); + + debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_NEXT), true); + debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_STEP), true ); + debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_BREAK), true ); + debug_menu->get_popup()->set_item_disabled( debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), true ); + + + window_menu = memnew( MenuButton ); + menu_hb->add_child(window_menu); + window_menu->set_text("Window"); + window_menu->get_popup()->add_item("Close",WINDOW_CLOSE,KEY_MASK_CMD|KEY_W); + window_menu->get_popup()->add_separator(); + window_menu->get_popup()->add_item("Move Left",WINDOW_MOVE_LEFT,KEY_MASK_CMD|KEY_LEFT); + window_menu->get_popup()->add_item("Move Right",WINDOW_MOVE_RIGHT,KEY_MASK_CMD|KEY_RIGHT); + window_menu->get_popup()->add_separator(); + window_menu->get_popup()->connect("item_pressed", this,"_menu_option"); + + tab_container->connect("tab_changed", this,"_tab_changed"); + + find_replace_dialog = memnew(FindReplaceDialog); + add_child(find_replace_dialog); + + erase_tab_confirm = memnew( ConfirmationDialog ); + add_child(erase_tab_confirm); + erase_tab_confirm->connect("confirmed", this,"_close_current_tab"); + + + goto_line_dialog = memnew(GotoLineDialog); + add_child(goto_line_dialog); + + debugger = memnew( ScriptEditorDebugger(editor) ); + debugger->connect("goto_script_line",this,"_goto_script_line"); + debugger->connect("show_debugger",this,"_show_debugger"); + + disk_changed = memnew( ConfirmationDialog ); + { + VBoxContainer *vbc = memnew( VBoxContainer ); + disk_changed->add_child(vbc); + disk_changed->set_child_rect(vbc); + + Label *dl = memnew( Label ); + dl->set_text("The following files are newer on disk.\nWhat action should be taken?:"); + vbc->add_child(dl); + + disk_changed_list = memnew( Tree ); + vbc->add_child(disk_changed_list); + disk_changed_list->set_v_size_flags(SIZE_EXPAND_FILL); + + disk_changed->connect("confirmed",this,"_reload_scripts"); + disk_changed->get_ok()->set_text("Reload"); + + disk_changed->add_button("Resave",!OS::get_singleton()->get_swap_ok_cancel(),"resave"); + disk_changed->connect("custom_action",this,"_resave_scripts"); + + + } + + add_child(disk_changed); + + script_editor=this; + + quick_open = memnew( ScriptEditorQuickOpen ); + add_child(quick_open); + + quick_open->connect("goto_line",this,"_goto_script_line2"); + + v_split->add_child(debugger); + debugger->connect("breaked",this,"_breaked"); +// debugger_gui->hide(); +} + + +void ScriptEditorPlugin::edit(Object *p_object) { + + if (!p_object->cast_to<Script>()) + return; + + script_editor->edit(p_object->cast_to<Script>()); + +} + +bool ScriptEditorPlugin::handles(Object *p_object) const { + + return p_object->is_type("Script"); +} + +void ScriptEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + script_editor->show(); + script_editor->set_process(true); + script_editor->ensure_select_current(); + } else { + + script_editor->hide(); + script_editor->set_process(false); + } + +} + +void ScriptEditorPlugin::selected_notify() { + + script_editor->ensure_select_current(); +} + +Dictionary ScriptEditorPlugin::get_state() const { + + return script_editor->get_state(); +} + +void ScriptEditorPlugin::set_state(const Dictionary& p_state) { + + script_editor->set_state(p_state); +} +void ScriptEditorPlugin::clear() { + + script_editor->clear(); +} + +void ScriptEditorPlugin::save_external_data() { + + script_editor->save_external_data(); +} + +void ScriptEditorPlugin::apply_changes() { + + script_editor->apply_scripts(); +} + +void ScriptEditorPlugin::restore_global_state() { + + if (bool(EDITOR_DEF("text_editor/restore_scripts_on_load",true))) { + script_editor->_load_files_state(); + } + +} + +void ScriptEditorPlugin::save_global_state() { + + if (bool(EDITOR_DEF("text_editor/restore_scripts_on_load",true))) { + script_editor->_save_files_state(); + } + +} + +void ScriptEditorPlugin::get_breakpoints(List<String> *p_breakpoints) { + + + return script_editor->get_breakpoints(p_breakpoints); +} + +ScriptEditorPlugin::ScriptEditorPlugin(EditorNode *p_node) { + + editor=p_node; + script_editor = memnew( ScriptEditor(p_node) ); + editor->get_viewport()->add_child(script_editor); + script_editor->set_area_as_parent_rect(); + + script_editor->hide(); + + 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_FILE)); + EDITOR_DEF("external_editor/exec_flags",""); + +} + + +ScriptEditorPlugin::~ScriptEditorPlugin() +{ +} + diff --git a/tools/editor/plugins/script_editor_plugin.h b/tools/editor/plugins/script_editor_plugin.h new file mode 100644 index 000000000..898ad1daf --- /dev/null +++ b/tools/editor/plugins/script_editor_plugin.h @@ -0,0 +1,250 @@ +/*************************************************************************/ +/* script_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef SCRIPT_EDITOR_PLUGIN_H +#define SCRIPT_EDITOR_PLUGIN_H + +#include "tools/editor/editor_plugin.h" +#include "scene/gui/tab_container.h" +#include "scene/gui/text_edit.h" +#include "scene/gui/menu_button.h" +#include "scene/gui/tree.h" +#include "scene/main/timer.h" +#include "script_language.h" +#include "tools/editor/code_editor.h" +#include "scene/gui/split_container.h" + + +class ScriptEditorQuickOpen : public ConfirmationDialog { + + OBJ_TYPE(ScriptEditorQuickOpen,ConfirmationDialog ) + + LineEdit *search_box; + Tree *search_options; + String function; + + void _update_search(); + + void _sbox_input(const InputEvent& p_ie); + Vector<String> functions; + + + void _confirmed(); + void _text_changed(const String& p_newtext); + +protected: + + void _notification(int p_what); + static void _bind_methods(); +public: + + void popup(const Vector<String>& p_base,bool p_dontclear=false); + ScriptEditorQuickOpen(); +}; + + +class ScriptEditorDebugger; + +class ScriptTextEditor : public CodeTextEditor { + + OBJ_TYPE( ScriptTextEditor, CodeTextEditor ); + + Ref<Script> script; + + + Vector<String> functions; + +protected: + + + + virtual void _validate_script(); + virtual void _code_complete_script(const String& p_code,const String& p_keyword, int p_line, List<String>* r_options); + virtual void _load_theme_settings(); + void _notification(int p_what); + + +public: + + virtual void apply_code(); + Ref<Script> get_edited_script() const; + Vector<String> get_functions() ; + void set_edited_script(const Ref<Script>& p_script); + void reload_text(); + void _update_name(); + + ScriptTextEditor(); + +}; + +class ScriptEditor : public VBoxContainer { + + OBJ_TYPE(ScriptEditor, VBoxContainer ); + + + EditorNode *editor; + enum { + + FILE_OPEN, + FILE_SAVE, + FILE_SAVE_AS, + FILE_SAVE_ALL, + EDIT_UNDO, + EDIT_REDO, + EDIT_CUT, + EDIT_COPY, + EDIT_PASTE, + EDIT_SELECT_ALL, + EDIT_COMPLETE, + SEARCH_FIND, + SEARCH_FIND_NEXT, + SEARCH_REPLACE, + SEARCH_LOCATE_FUNCTION, + SEARCH_GOTO_LINE, + DEBUG_TOGGLE_BREAKPOINT, + DEBUG_NEXT, + DEBUG_STEP, + DEBUG_BREAK, + DEBUG_CONTINUE, + DEBUG_SHOW, + WINDOW_CLOSE, + WINDOW_MOVE_LEFT, + WINDOW_MOVE_RIGHT, + WINDOW_SELECT_BASE=100 + }; + + HBoxContainer *menu_hb; + MenuButton *file_menu; + MenuButton *edit_menu; + MenuButton *search_menu; + MenuButton *window_menu; + MenuButton *debug_menu; + uint64_t idle; + + TabContainer *tab_container; + FindReplaceDialog *find_replace_dialog; + GotoLineDialog *goto_line_dialog; + ConfirmationDialog *erase_tab_confirm; + ScriptEditorDebugger* debugger; + + void _tab_changed(int p_which); + void _menu_option(int p_optin); + + Tree *disk_changed_list; + ConfirmationDialog *disk_changed; + + VSplitContainer *v_split; + + String _get_debug_tooltip(const String&p_text,Node *_ste); + + void _resave_scripts(const String& p_str); + void _reload_scripts(); + + bool _test_script_times_on_disk(); + + void _close_current_tab(); + + ScriptEditorQuickOpen *quick_open; + + + void _editor_play(); + void _editor_pause(); + void _editor_stop(); + + void _add_callback(Object *p_obj, const String& p_function, const StringArray& p_args); + void _res_saved_callback(const Ref<Resource>& p_res); + + void _goto_script_line2(int p_line); + void _goto_script_line(REF p_script,int p_line); + void _breaked(bool p_breaked,bool p_can_debug); + void _show_debugger(bool p_show); + void _update_window_menu(); + static ScriptEditor *script_editor; +protected: + void _notification(int p_what); + static void _bind_methods(); +public: + + static ScriptEditor *get_singleton() { return script_editor; } + void _save_files_state(); + void _load_files_state(); + + + void ensure_focus_current(); + void apply_scripts() const; + + void ensure_select_current(); + void edit(const Ref<Script>& p_script); + + Dictionary get_state() const; + void set_state(const Dictionary& p_state); + void clear(); + + void get_breakpoints(List<String> *p_breakpoints); + + + + void save_external_data(); + + ScriptEditor(EditorNode *p_editor); +}; + +class ScriptEditorPlugin : public EditorPlugin { + + OBJ_TYPE( ScriptEditorPlugin, EditorPlugin ); + + ScriptEditor *script_editor; + EditorNode *editor; +public: + + virtual String get_name() const { return "Script"; } + bool has_main_screen() const { return true; } + virtual void edit(Object *p_node); + virtual bool handles(Object *p_node) const; + virtual void make_visible(bool p_visible); + virtual void selected_notify(); + + Dictionary get_state() const; + virtual void set_state(const Dictionary& p_state); + virtual void clear(); + + virtual void save_external_data(); + virtual void apply_changes(); + + virtual void restore_global_state(); + virtual void save_global_state(); + + virtual void get_breakpoints(List<String> *p_breakpoints); + + + ScriptEditorPlugin(EditorNode *p_node); + ~ScriptEditorPlugin(); + +}; + +#endif // SCRIPT_EDITOR_PLUGIN_H diff --git a/tools/editor/plugins/shader_editor_plugin.cpp b/tools/editor/plugins/shader_editor_plugin.cpp new file mode 100644 index 000000000..4670005cf --- /dev/null +++ b/tools/editor/plugins/shader_editor_plugin.cpp @@ -0,0 +1,598 @@ +/*************************************************************************/ +/* shader_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "shader_editor_plugin.h" +#include "tools/editor/editor_settings.h" + +#include "spatial_editor_plugin.h" +#include "io/resource_loader.h" +#include "io/resource_saver.h" +#include "os/keyboard.h" +#include "tools/editor/editor_node.h" +#include "tools/editor/property_editor.h" +#include "os/os.h" + + +/*** SETTINGS EDITOR ****/ + + + + +/*** SCRIPT EDITOR ****/ + + +Ref<Shader> ShaderTextEditor::get_edited_shader() const { + + return shader; +} +void ShaderTextEditor::set_edited_shader(const Ref<Shader>& p_shader,ShaderLanguage::ShaderType p_type) { + + shader=p_shader; + type=p_type; + + _load_theme_settings(); + + if (p_type==ShaderLanguage::SHADER_MATERIAL_VERTEX) + get_text_edit()->set_text(shader->get_vertex_code()); + else + get_text_edit()->set_text(shader->get_fragment_code()); + + _line_col_changed(); + + +} + + +void ShaderTextEditor::_load_theme_settings() { + + get_text_edit()->clear_colors(); + + /* keyword color */ + + get_text_edit()->set_custom_bg_color(EDITOR_DEF("text_editor/background_color",Color(0,0,0,0))); + get_text_edit()->add_color_override("font_color",EDITOR_DEF("text_editor/text_color",Color(0,0,0))); + get_text_edit()->add_color_override("font_selected_color",EDITOR_DEF("text_editor/text_selected_color",Color(1,1,1))); + get_text_edit()->add_color_override("selection_color",EDITOR_DEF("text_editor/selection_color",Color(0.2,0.2,1))); + + Color keyword_color= EDITOR_DEF("text_editor/keyword_color",Color(0.5,0.0,0.2)); + + get_text_edit()->set_syntax_coloring(true); + + + List<String> keywords; + ShaderLanguage::get_keyword_list(type,&keywords); + + + for(List<String>::Element *E=keywords.front();E;E=E->next()) { + + get_text_edit()->add_keyword_color(E->get(),keyword_color); + } + + //colorize core types +// Color basetype_color= EDITOR_DEF("text_editor/base_type_color",Color(0.3,0.3,0.0)); + + + //colorize comments + Color comment_color = EDITOR_DEF("text_editor/comment_color",Color::hex(0x797e7eff)); + + get_text_edit()->add_color_region("/*","*/",comment_color,false); + get_text_edit()->add_color_region("//","",comment_color,false); + //colorize strings + Color string_color = EDITOR_DEF("text_editor/string_color",Color::hex(0x6b6f00ff)); + /* + List<String> strings; + shader->get_shader_mode()->get_string_delimiters(&strings); + + for (List<String>::Element *E=strings.front();E;E=E->next()) { + + String string = E->get(); + String beg = string.get_slice(" ",0); + String end = string.get_slice_count(" ")>1?string.get_slice(" ",1):String(); + get_text_edit()->add_color_region(beg,end,string_color,end==""); + }*/ + + //colorize symbols + Color symbol_color= EDITOR_DEF("text_editor/symbol_color",Color::hex(0x005291ff)); + get_text_edit()->set_symbol_color(symbol_color); + +} + + +void ShaderTextEditor::_validate_script() { + + String errortxt; + int line,col; + + String code; + if (type==ShaderLanguage::SHADER_MATERIAL_VERTEX) + code=get_text_edit()->get_text(); + else + code=get_text_edit()->get_text(); + + //List<StringName> params; + //shader->get_param_list(¶ms); + + Error err = ShaderLanguage::compile(code,type,NULL,NULL,&errortxt,&line,&col); + + if (err!=OK) { + String error_text="error("+itos(line)+","+itos(col)+"): "+errortxt; + set_error(error_text); + + } else { + set_error(""); + } + + emit_signal("script_changed"); +} + +void ShaderTextEditor::_bind_methods() { + + + ADD_SIGNAL( MethodInfo("script_changed") ); + +} + +ShaderTextEditor::ShaderTextEditor() { + + +} + +/*** SCRIPT EDITOR ******/ + + + +void ShaderEditor::_menu_option(int p_option) { + + int selected = tab_container->get_current_tab(); + if (selected<0 || selected>=tab_container->get_child_count()) + return; + + ShaderTextEditor *current = tab_container->get_child(selected)->cast_to<ShaderTextEditor>(); + if (!current) + return; + + switch(p_option) { + case EDIT_UNDO: { + + + current->get_text_edit()->undo(); + } break; + case EDIT_REDO: { + current->get_text_edit()->redo(); + + } break; + case EDIT_CUT: { + + current->get_text_edit()->cut(); + } break; + case EDIT_COPY: { + current->get_text_edit()->copy(); + + } break; + case EDIT_PASTE: { + current->get_text_edit()->paste(); + + } break; + case EDIT_SELECT_ALL: { + + current->get_text_edit()->select_all(); + + } break; + case SEARCH_FIND: { + + find_replace_dialog->set_text_edit(current->get_text_edit()); + find_replace_dialog->popup_search(); + } break; + case SEARCH_FIND_NEXT: { + + find_replace_dialog->set_text_edit(current->get_text_edit()); + find_replace_dialog->search_next(); + } break; + case SEARCH_REPLACE: { + + find_replace_dialog->set_text_edit(current->get_text_edit()); + find_replace_dialog->popup_replace(); + } break; +// case SEARCH_LOCATE_SYMBOL: { + +// } break; + case SEARCH_GOTO_LINE: { + + goto_line_dialog->popup_find_line(current->get_text_edit()); + } break; + case SHADER_POST_PROCESS_MODE:{ + + fragment_editor->set_edited_shader(shader,ShaderLanguage::SHADER_POST_PROCESS); + fragment_editor->_validate_script(); + apply_shaders(); + settings_menu->get_popup()->set_item_checked( settings_menu->get_popup()->get_item_index(SHADER_MATERIAL_MODE), false); + settings_menu->get_popup()->set_item_checked( settings_menu->get_popup()->get_item_index(SHADER_POST_PROCESS_MODE), true); + + + } break; + case SHADER_MATERIAL_MODE: { + + fragment_editor->set_edited_shader(shader,ShaderLanguage::SHADER_MATERIAL_FRAGMENT); + fragment_editor->_validate_script(); + apply_shaders(); + settings_menu->get_popup()->set_item_checked( settings_menu->get_popup()->get_item_index(SHADER_MATERIAL_MODE), true); + settings_menu->get_popup()->set_item_checked( settings_menu->get_popup()->get_item_index(SHADER_POST_PROCESS_MODE), false); + + } break; + } +} + +void ShaderEditor::_tab_changed(int p_which) { + + ensure_select_current(); +} + +void ShaderEditor::_notification(int p_what) { + + if (p_what==NOTIFICATION_ENTER_SCENE) { + + close->set_normal_texture( get_icon("Close","EditorIcons")); + close->set_hover_texture( get_icon("CloseHover","EditorIcons")); + close->set_pressed_texture( get_icon("Close","EditorIcons")); + close->connect("pressed",this,"_close_callback"); + + } + if (p_what==NOTIFICATION_DRAW) { + + RID ci = get_canvas_item(); + Ref<StyleBox> style = get_stylebox("panel","Panel"); + style->draw( ci, Rect2( Point2(), get_size() ) ); + + } + +} + + + +Dictionary ShaderEditor::get_state() const { +#if 0 + apply_shaders(); + + Dictionary state; + + Array paths; + int open=-1; + + for(int i=0;i<tab_container->get_child_count();i++) { + + ShaderTextEditor *ste = tab_container->get_child(i)->cast_to<ShaderTextEditor>(); + if (!ste) + continue; + + + Ref<Shader> shader = ste->get_edited_shader(); + if (shader->get_path()!="" && shader->get_path().find("local://")==-1 && shader->get_path().find("::")==-1) { + + paths.push_back(shader->get_path()); + } else { + + + const Node *owner = _find_node_with_shader(get_root_node(),shader.get_ref_ptr()); + if (owner) + paths.push_back(owner->get_path()); + + } + + if (i==tab_container->get_current_tab()) + open=i; + } + + if (paths.size()) + state["sources"]=paths; + if (open!=-1) + state["current"]=open; + + + return state; +#endif + return Dictionary(); +} +void ShaderEditor::set_state(const Dictionary& p_state) { +#if 0 + print_line("setting state.."); + if (!p_state.has("sources")) + return; //bleh + + Array sources = p_state["sources"]; + for(int i=0;i<sources.size();i++) { + + Variant source=sources[i]; + + Ref<Shader> shader; + + if (source.get_type()==Variant::NODE_PATH) { + + print_line("cain find owner at path "+String(source)); + Node *owner=get_root_node()->get_node(source); + if (!owner) + continue; + + shader = owner->get_shader(); + } else if (source.get_type()==Variant::STRING) { + + print_line("loading at path "+String(source)); + shader = ResourceLoader::load(source,"Shader"); + } + + print_line("found shader at "+String(source)+"? - "+itos(shader.is_null())); + if (shader.is_null()) //ah well.. + continue; + + get_scene()->get_root_node()->call("_resource_selected",shader); + } + + if (p_state.has("current")) + tab_container->set_current_tab(p_state["current"]); +#endif +} +void ShaderEditor::clear() { + +} + +void ShaderEditor::_params_changed() { + + + fragment_editor->_validate_script(); + vertex_editor->_validate_script(); +} + + +void ShaderEditor::_bind_methods() { + + ObjectTypeDB::bind_method("_tab_changed",&ShaderEditor::_tab_changed); + ObjectTypeDB::bind_method("_menu_option",&ShaderEditor::_menu_option); + ObjectTypeDB::bind_method("_params_changed",&ShaderEditor::_params_changed); + ObjectTypeDB::bind_method("_close_callback",&ShaderEditor::_close_callback); + ObjectTypeDB::bind_method("apply_shaders",&ShaderEditor::apply_shaders); +// ObjectTypeDB::bind_method("_close_current_tab",&ShaderEditor::_close_current_tab); +} + +void ShaderEditor::ensure_select_current() { + +/* + if (tab_container->get_child_count() && tab_container->get_current_tab()>=0) { + + ShaderTextEditor *ste = tab_container->get_child(tab_container->get_current_tab())->cast_to<ShaderTextEditor>(); + if (!ste) + return; + Ref<Shader> shader = ste->get_edited_shader(); + get_scene()->get_root_node()->call("_resource_selected",shader); + }*/ +} + +void ShaderEditor::edit(const Ref<Shader>& p_shader) { + + if (p_shader.is_null()) + return; + + + shader=p_shader; + + if (shader->get_mode()==Shader::MODE_MATERIAL) { + fragment_editor->set_edited_shader(p_shader,ShaderLanguage::SHADER_MATERIAL_FRAGMENT); + settings_menu->get_popup()->set_item_checked( settings_menu->get_popup()->get_item_index(SHADER_MATERIAL_MODE), true); + settings_menu->get_popup()->set_item_checked( settings_menu->get_popup()->get_item_index(SHADER_POST_PROCESS_MODE), false); + } else { + + fragment_editor->set_edited_shader(p_shader,ShaderLanguage::SHADER_POST_PROCESS); + settings_menu->get_popup()->set_item_checked( settings_menu->get_popup()->get_item_index(SHADER_MATERIAL_MODE), false); + settings_menu->get_popup()->set_item_checked( settings_menu->get_popup()->get_item_index(SHADER_POST_PROCESS_MODE), true); + } + + vertex_editor->set_edited_shader(shader,ShaderLanguage::SHADER_MATERIAL_VERTEX); + // see if already has it + + +} + +void ShaderEditor::save_external_data() { + + if (shader.is_null()) + return; + apply_shaders(); + + if (shader->get_path()!="" && shader->get_path().find("local://")==-1 &&shader->get_path().find("::")==-1) { + //external shader, save it + ResourceSaver::save(shader->get_path(),shader); + } +} + +void ShaderEditor::apply_shaders() { + + + if (shader.is_valid()) + shader->set_code(vertex_editor->get_text_edit()->get_text(),fragment_editor->get_text_edit()->get_text(),0,0); +} + +void ShaderEditor::_close_callback() { + + hide(); +} + + +ShaderEditor::ShaderEditor() { + + tab_container = memnew( TabContainer ); + add_child(tab_container); + tab_container->set_area_as_parent_rect(); + tab_container->set_begin(Point2(0,0)); + //tab_container->set_begin(Point2(0,0)); + + close = memnew( TextureButton ); + close->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_END,20); + close->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,4); + close->set_anchor_and_margin(MARGIN_TOP,ANCHOR_BEGIN,2); + add_child(close); + + + + edit_menu = memnew( MenuButton ); + add_child(edit_menu); + edit_menu->set_pos(Point2(5,-1)); + edit_menu->set_text("Edit"); + edit_menu->get_popup()->add_item("Undo",EDIT_UNDO,KEY_MASK_CMD|KEY_Z); + edit_menu->get_popup()->add_item("Redo",EDIT_REDO,KEY_MASK_CMD|KEY_MASK_SHIFT|KEY_Z); + edit_menu->get_popup()->add_separator(); + edit_menu->get_popup()->add_item("Cut",EDIT_CUT,KEY_MASK_CMD|KEY_X); + edit_menu->get_popup()->add_item("Copy",EDIT_COPY,KEY_MASK_CMD|KEY_C); + edit_menu->get_popup()->add_item("Paste",EDIT_PASTE,KEY_MASK_CMD|KEY_V); + edit_menu->get_popup()->add_separator(); + edit_menu->get_popup()->add_item("Select All",EDIT_SELECT_ALL,KEY_MASK_CMD|KEY_A); + edit_menu->get_popup()->connect("item_pressed", this,"_menu_option"); + + + search_menu = memnew( MenuButton ); + add_child(search_menu); + search_menu->set_pos(Point2(38,-1)); + search_menu->set_text("Search"); + search_menu->get_popup()->add_item("Find..",SEARCH_FIND,KEY_MASK_CMD|KEY_F); + search_menu->get_popup()->add_item("Find Next",SEARCH_FIND_NEXT,KEY_F3); + search_menu->get_popup()->add_item("Replace..",SEARCH_REPLACE,KEY_MASK_CMD|KEY_R); + search_menu->get_popup()->add_separator(); +// search_menu->get_popup()->add_item("Locate Symbol..",SEARCH_LOCATE_SYMBOL,KEY_MASK_CMD|KEY_K); + search_menu->get_popup()->add_item("Goto Line..",SEARCH_GOTO_LINE,KEY_MASK_CMD|KEY_G); + search_menu->get_popup()->connect("item_pressed", this,"_menu_option"); + + settings_menu = memnew( MenuButton ); + add_child(settings_menu); + settings_menu->set_pos(Point2(90,-1)); + settings_menu->set_text("Shader"); + settings_menu->get_popup()->add_check_item("Material Mode",SHADER_MATERIAL_MODE); + settings_menu->get_popup()->set_item_checked(settings_menu->get_popup()->get_item_index(SHADER_MATERIAL_MODE),true); + settings_menu->get_popup()->add_check_item("Post Process Mode",SHADER_POST_PROCESS_MODE); + + settings_menu->get_popup()->connect("item_pressed", this,"_menu_option"); + + tab_container->connect("tab_changed", this,"_tab_changed"); + + find_replace_dialog = memnew(FindReplaceDialog); + add_child(find_replace_dialog); + + erase_tab_confirm = memnew( ConfirmationDialog ); + add_child(erase_tab_confirm); + erase_tab_confirm->connect("confirmed", this,"_close_current_tab"); + + + goto_line_dialog = memnew(GotoLineDialog); + add_child(goto_line_dialog); + + vertex_editor = memnew( ShaderTextEditor ); + tab_container->add_child(vertex_editor); + vertex_editor->set_name("Vertex"); + + fragment_editor = memnew( ShaderTextEditor ); + tab_container->add_child(fragment_editor); + fragment_editor->set_name("Fragment"); + + tab_container->set_current_tab(1); + + + vertex_editor->connect("script_changed", this,"apply_shaders"); + fragment_editor->connect("script_changed", this,"apply_shaders"); +} + + +void ShaderEditorPlugin::edit(Object *p_object) { + + if (!p_object->cast_to<Shader>()) + return; + + shader_editor->edit(p_object->cast_to<Shader>()); + +} + +bool ShaderEditorPlugin::handles(Object *p_object) const { + + return p_object->is_type("Shader"); +} + +void ShaderEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + shader_editor->show(); + //shader_editor->set_process(true); + } else { + + shader_editor->apply_shaders(); + //shader_editor->hide(); + //shader_editor->set_process(false); + } + +} + +void ShaderEditorPlugin::selected_notify() { + + shader_editor->ensure_select_current(); +} + +Dictionary ShaderEditorPlugin::get_state() const { + + return shader_editor->get_state(); +} + +void ShaderEditorPlugin::set_state(const Dictionary& p_state) { + + shader_editor->set_state(p_state); +} +void ShaderEditorPlugin::clear() { + + shader_editor->clear(); +} + +void ShaderEditorPlugin::save_external_data() { + + shader_editor->save_external_data(); +} + +void ShaderEditorPlugin::apply_changes() { + + shader_editor->apply_shaders(); +} + +ShaderEditorPlugin::ShaderEditorPlugin(EditorNode *p_node) { + + editor=p_node; + shader_editor = memnew( ShaderEditor ); + + SpatialEditor::get_singleton()->get_shader_split()->add_child(shader_editor); +// editor->get_viewport()->add_child(shader_editor); +// shader_editor->set_area_as_parent_rect(); + + shader_editor->hide(); + +} + + +ShaderEditorPlugin::~ShaderEditorPlugin() { +} + diff --git a/tools/editor/plugins/shader_editor_plugin.h b/tools/editor/plugins/shader_editor_plugin.h new file mode 100644 index 000000000..8d5f2dae4 --- /dev/null +++ b/tools/editor/plugins/shader_editor_plugin.h @@ -0,0 +1,158 @@ +/*************************************************************************/ +/* shader_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef SHADER_EDITOR_PLUGIN_H +#define SHADER_EDITOR_PLUGIN_H + +#include "tools/editor/code_editor.h" +#include "tools/editor/editor_plugin.h" +#include "scene/gui/tab_container.h" +#include "scene/gui/text_edit.h" +#include "scene/gui/menu_button.h" +#include "scene/main/timer.h" +#include "scene/resources/shader.h" +#include "servers/visual/shader_language.h" + + +class ShaderTextEditor : public CodeTextEditor { + + OBJ_TYPE( ShaderTextEditor, CodeTextEditor ); + + Ref<Shader> shader; + ShaderLanguage::ShaderType type; + +protected: + + static void _bind_methods(); + virtual void _load_theme_settings(); +public: + + virtual void _validate_script(); + + + Ref<Shader> get_edited_shader() const; + void set_edited_shader(const Ref<Shader>& p_shader,ShaderLanguage::ShaderType p_type); + ShaderTextEditor(); + +}; + + +class ShaderEditor : public Control { + + OBJ_TYPE(ShaderEditor, Control ); + + enum { + + EDIT_UNDO, + EDIT_REDO, + EDIT_CUT, + EDIT_COPY, + EDIT_PASTE, + EDIT_SELECT_ALL, + SEARCH_FIND, + SEARCH_FIND_NEXT, + SEARCH_REPLACE, + //SEARCH_LOCATE_SYMBOL, + SEARCH_GOTO_LINE, + SHADER_MATERIAL_MODE, + SHADER_POST_PROCESS_MODE, + SHADER_SHADE_MODEL_MODE, + + }; + + MenuButton *edit_menu; + MenuButton *search_menu; + MenuButton *settings_menu; + uint64_t idle; + + TabContainer *tab_container; + FindReplaceDialog *find_replace_dialog; + GotoLineDialog *goto_line_dialog; + ConfirmationDialog *erase_tab_confirm; + + TextureButton *close; + + ShaderTextEditor *vertex_editor; + ShaderTextEditor *fragment_editor; + + void _tab_changed(int p_which); + void _menu_option(int p_optin); + void _params_changed(); + mutable Ref<Shader> shader; + + void _close_callback(); + + + +protected: + void _notification(int p_what); + static void _bind_methods(); +public: + + void apply_shaders(); + + void ensure_select_current(); + void edit(const Ref<Shader>& p_shader); + + Dictionary get_state() const; + void set_state(const Dictionary& p_state); + void clear(); + + virtual Size2 get_minimum_size() const { return Size2(0,200); } + void save_external_data(); + + ShaderEditor(); +}; + +class ShaderEditorPlugin : public EditorPlugin { + + OBJ_TYPE( ShaderEditorPlugin, EditorPlugin ); + + ShaderEditor *shader_editor; + EditorNode *editor; +public: + + virtual String get_name() const { return "Shader"; } + 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); + virtual void selected_notify(); + + Dictionary get_state() const; + virtual void set_state(const Dictionary& p_state); + virtual void clear(); + + virtual void save_external_data(); + virtual void apply_changes(); + + ShaderEditorPlugin(EditorNode *p_node); + ~ShaderEditorPlugin(); + +}; +#endif diff --git a/tools/editor/plugins/shader_graph_editor_plugin.cpp b/tools/editor/plugins/shader_graph_editor_plugin.cpp new file mode 100644 index 000000000..2686ca895 --- /dev/null +++ b/tools/editor/plugins/shader_graph_editor_plugin.cpp @@ -0,0 +1,1109 @@ +/*************************************************************************/ +/* shader_graph_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "shader_graph_editor_plugin.h" + +#if 0 +#include "scene/gui/menu_button.h" +#include "scene/gui/panel.h" + +class _ShaderTester : public ShaderCodeGenerator { +public: + + Set<int> *_set; + + virtual void begin() {} + virtual Error add_node(VS::ShaderNodeType p_type,int p_node_pos,int p_id,const Variant& p_param,const Vector<int>& p_in_connections,const Vector<int>& p_out_connections,const Vector<int>& p_out_connection_outputs) { if (_set) _set->insert(p_id); return OK; } + virtual void end() {} + + _ShaderTester() { _set=NULL; } +}; + + + +void ShaderEditor::edit(Ref<Shader> p_shader) { + + + shader=p_shader; + + if (shader.is_null()) + hide(); + else { + _read_shader_graph(); + } + +} + +Size2 ShaderEditor::_get_maximum_size() { + + Size2 max; + + for(List<int>::Element *E=order.front();E;E=E->next()) { + + Point2 pos = Point2( shader_graph.node_get_pos_x(E->get()), shader_graph.node_get_pos_y(E->get()) ); + + if (click_type==CLICK_NODE && click_node==E->get()) { + + pos+=click_motion-click_pos; + } + pos+=get_node_size(E->get()); + if (pos.x>max.x) + max.x=pos.x; + if (pos.y>max.y) + max.y=pos.y; + + } + + return max; +} + +Size2 ShaderEditor::get_node_size(int p_node) const { + + VisualServer::ShaderNodeType type=shader_graph.node_get_type(p_node); + Ref<StyleBox> style = get_stylebox("panel","PopupMenu"); + Ref<Font> font = get_font("font","PopupMenu"); + Color font_color = get_color("font_color","PopupMenu"); + + Size2 size = style->get_minimum_size(); + + int count=1; // title + count += VisualServer::shader_get_input_count( type) + VisualServer::shader_get_output_count( type); + + float max_w=font->get_string_size( VisualServer::shader_node_get_type_info(type).name ).width; + + for(int i=0;i<VisualServer::shader_get_input_count(type);i++) + max_w = MAX( max_w, font->get_string_size( VisualServer::shader_get_input_name(type,i) ).width ); + + + for(int i=0;i<VisualServer::shader_get_output_count(type);i++) + max_w = MAX( max_w, font->get_string_size( VisualServer::shader_get_output_name(type,i) ).width ); + + + + + switch(type) { + + case VS::NODE_IN: + case VS::NODE_OUT: + case VS::NODE_VEC_IN: + case VS::NODE_VEC_OUT: + case VS::NODE_PARAMETER: + case VS::NODE_VEC_PARAMETER: + case VS::NODE_COLOR_PARAMETER: + case VS::NODE_TEXTURE_PARAMETER: + case VS::NODE_TEXTURE_2D_PARAMETER: + case VS::NODE_TEXTURE_CUBE_PARAMETER: + case VS::NODE_TRANSFORM_PARAMETER: + case VS::NODE_LABEL: { + + max_w=MAX( max_w, font->get_string_size( shader_graph.node_get_param(p_node) ).width ); + count++; + } break; + case VS::NODE_TIME: + case VS::NODE_CONSTANT: + case VS::NODE_VEC_CONSTANT: + case VS::NODE_COLOR_CONSTANT: + case VS::NODE_TRANSFORM_CONSTANT: { + count++; + } break; + case VS::NODE_TEXTURE: + case VS::NODE_VEC_TEXTURE_2D: + case VS::NODE_VEC_TEXTURE_CUBE: { + + RefPtr res = shader_graph.node_get_param(p_node); + Ref<Texture> texture = res; + if (texture.is_null() || texture->get_width()==0) { + + size.y+=max_w; + } else { + + size.y+=max_w * texture->get_height() / texture->get_width(); + } + } break; + default: {} + + } + + size.x+=max_w; + size.y+=count*(font->get_height()+get_constant("vseparation","PopupMenu")); + + return size; +} + + +Error ShaderEditor::validate_graph() { + + _ShaderTester st; + active_nodes.clear(); + st._set=&active_nodes; + return shader_graph.generate(&st); +} + +void ShaderEditor::_draw_node(int p_node) { + + VisualServer::ShaderNodeType type=shader_graph.node_get_type(p_node); + Ref<StyleBox> style = active_nodes.has(p_node)?get_stylebox("panel","PopupMenu"):get_stylebox("panel_disabled","PopupMenu"); + Ref<Font> font = get_font("font","PopupMenu"); + Color font_color = get_color("font_color","PopupMenu"); + Color font_color_title = get_color("font_color_hover","PopupMenu"); + Size2 size=get_node_size(p_node); + Point2 pos = Point2( shader_graph.node_get_pos_x(p_node), shader_graph.node_get_pos_y(p_node) )-offset; + + if (click_type==CLICK_NODE && click_node==p_node) { + + pos+=click_motion-click_pos; + } + + RID ci = get_canvas_item(); + style->draw(ci,Rect2(pos,size)); + + Point2 ofs=style->get_offset()+pos; + Point2 ascent=Point2(0,font->get_ascent()); + float w = size.width-style->get_minimum_size().width; + float h = font->get_height()+get_constant("vseparation","PopupMenu"); + + font->draw_halign( ci, ofs+ascent, HALIGN_CENTER,w, VisualServer::shader_node_get_type_info(type).name,font_color_title); + ofs.y+=h; + + Ref<Texture> vec_icon = get_icon("NodeVecSlot","EditorIcons"); + Ref<Texture> real_icon = get_icon("NodeRealSlot","EditorIcons"); + float icon_h_ofs = Math::floor(( font->get_height()-vec_icon->get_height())/2.0 )+1; + + + for(int i=0;i<VisualServer::shader_get_input_count(type);i++) { + + String name = VisualServer::shader_get_input_name(type,i); + font->draw_halign( ci, ofs+ascent, HALIGN_LEFT,w, name,font_color); + Ref<Texture> icon = VisualServer::shader_is_input_vector(type,i)?vec_icon:real_icon; + icon->draw(ci,ofs+Point2(-real_icon->get_width(),icon_h_ofs)); + ofs.y+=h; + } + + for(int i=0;i<VisualServer::shader_get_output_count(type);i++) { + + String name = VisualServer::shader_get_output_name(type,i); + font->draw_halign( ci, ofs+ascent, HALIGN_RIGHT,w, name,font_color); + Ref<Texture> icon = VisualServer::shader_is_output_vector(type,i)?vec_icon:real_icon; + icon->draw(ci,ofs+Point2(w,icon_h_ofs)); + ofs.y+=h; + } + + switch(type) { + + case VS::NODE_IN: + case VS::NODE_OUT: + case VS::NODE_PARAMETER: + case VS::NODE_VEC_IN: + case VS::NODE_COLOR_PARAMETER: + case VS::NODE_VEC_OUT: + case VS::NODE_TEXTURE_PARAMETER: + case VS::NODE_TEXTURE_2D_PARAMETER: + case VS::NODE_TEXTURE_CUBE_PARAMETER: + case VS::NODE_TRANSFORM_CONSTANT: + case VS::NODE_TRANSFORM_PARAMETER: + case VS::NODE_VEC_PARAMETER: + case VS::NODE_LABEL: { + String text = shader_graph.node_get_param(p_node); + font->draw_halign( ci, ofs+ascent, HALIGN_CENTER,w, text,font_color); + } break; + case VS::NODE_TIME: + case VS::NODE_CONSTANT: { + String text = rtos(shader_graph.node_get_param(p_node)); + font->draw_halign( ci, ofs+ascent, HALIGN_CENTER,w, text,font_color); + + } break; + case VS::NODE_VEC_CONSTANT: { + String text = Vector3(shader_graph.node_get_param(p_node)); + font->draw_halign( ci, ofs+ascent, HALIGN_CENTER,w, text,font_color); + } break; + case VS::NODE_COLOR_CONSTANT: { + + Color color = shader_graph.node_get_param(p_node); + Rect2 r(ofs,Size2(w,h)); + VisualServer::get_singleton()->canvas_item_add_rect(ci,r,color); + } break; + case VS::NODE_TEXTURE: + case VS::NODE_VEC_TEXTURE_2D: + case VS::NODE_VEC_TEXTURE_CUBE: { + + Rect2 r(ofs,Size2(w,(pos.y+size.y-style->get_margin(MARGIN_BOTTOM))-ofs.y)); + Vector<Point2> points; + Vector<Point2> uvs; + points.resize(4); + uvs.resize(4); + points[0]=r.pos; + points[1]=r.pos+Point2(r.size.x,0); + points[2]=r.pos+r.size; + points[3]=r.pos+Point2(0,r.size.y); + uvs[0]=Point2(0,0); + uvs[1]=Point2(1,0); + uvs[2]=Point2(1,1); + uvs[3]=Point2(0,1); + + Ref<Texture> texture = shader_graph.node_get_param(p_node).operator RefPtr(); + if (texture.is_null() || texture->get_width()==0) { + texture=get_icon("Click2Edit","EditorIcons"); + } + + VisualServer::get_singleton()->canvas_item_add_primitive(ci,points,Vector<Color>(),uvs,texture->get_rid()); + } break; + default: {} + } +} + +void ShaderEditor::_node_param_changed() { + + shader_graph.node_set_param( click_node,property_editor->get_variant() ); + update(); + _write_shader_graph(); +} + +ShaderEditor::ClickType ShaderEditor::_locate_click(const Point2& p_click,int *p_node_id,int *p_slot_index) const { + + Ref<StyleBox> style = get_stylebox("panel","PopupMenu"); + Ref<Texture> real_icon = get_icon("NodeRealSlot","EditorIcons"); + Ref<Font> font = get_font("font","PopupMenu"); + float h = font->get_height()+get_constant("vseparation","PopupMenu"); + float extra_left=MAX( real_icon->get_width()-style->get_margin(MARGIN_LEFT), 0 ); + float extra_right=MAX( real_icon->get_width()-style->get_margin(MARGIN_RIGHT), 0 ); + + + for(const List<int>::Element *E=order.back();E;E=E->prev()) { + + Size2 size=get_node_size(E->get()); + size.width+=extra_left+extra_right; + Point2 pos = Point2( shader_graph.node_get_pos_x(E->get()), shader_graph.node_get_pos_y(E->get()) )-offset; + pos.x-=extra_left; + + Rect2 rect( pos, size ); + if (!rect.has_point(p_click)) + continue; + VisualServer::ShaderNodeType type=shader_graph.node_get_type(E->get()); + if (p_node_id) + *p_node_id=E->get(); + float y=p_click.y-(pos.y+style->get_margin(MARGIN_TOP)); + if (y<h) + return CLICK_NODE; + y-=h; + + for(int i=0;i<VisualServer::shader_get_input_count(type);i++) { + + if (y<h) { + if (p_slot_index) + *p_slot_index=i; + return CLICK_INPUT_SLOT; + } + y-=h; + } + + for(int i=0;i<VisualServer::shader_get_output_count(type);i++) { + + if (y<h) { + if (p_slot_index) + *p_slot_index=i; + return CLICK_OUTPUT_SLOT; + } + y-=h; + } + + if (p_click.y<(rect.pos.y+rect.size.height-style->get_margin(MARGIN_BOTTOM))) + return CLICK_PARAMETER; + else + return CLICK_NODE; + + } + + return CLICK_NONE; + +} + +Point2 ShaderEditor::_get_slot_pos(int p_node_id,bool p_input,int p_slot) { + + Ref<StyleBox> style = get_stylebox("panel","PopupMenu"); + float w = get_node_size(p_node_id).width; + Ref<Font> font = get_font("font","PopupMenu"); + float h = font->get_height()+get_constant("vseparation","PopupMenu"); + Ref<Texture> vec_icon = get_icon("NodeVecSlot","EditorIcons"); + Point2 pos = Point2( shader_graph.node_get_pos_x(p_node_id), shader_graph.node_get_pos_y(p_node_id) )-offset; + pos+=style->get_offset(); + pos.y+=h; + + if(p_input) { + + pos.y+=p_slot*h; + pos+=Point2( -vec_icon->get_width()/2.0, h/2.0).floor(); + return pos; + } else { + + pos.y+=VisualServer::shader_get_input_count( shader_graph.node_get_type(p_node_id ) )*h; + } + + pos.y+=p_slot*h; + pos+=Point2( w-style->get_minimum_size().width+vec_icon->get_width()/2.0, h/2.0).floor(); + + return pos; + +} + +void ShaderEditor::_node_edit_property(int p_node) { + + Ref<StyleBox> style = get_stylebox("panel","PopupMenu"); + Size2 size = get_node_size(p_node); + Point2 pos = Point2( shader_graph.node_get_pos_x(p_node), shader_graph.node_get_pos_y(p_node) )-offset; + + VisualServer::ShaderNodeType type=shader_graph.node_get_type(p_node); + + PropertyInfo ph = VisualServer::get_singleton()->shader_node_get_type_info(type); + if (ph.type==Variant::NIL) + return; + if (ph.type==Variant::_RID) + ph.type=Variant::OBJECT; + + property_editor->edit(NULL,ph.name,ph.type,shader_graph.node_get_param(p_node),ph.hint,ph.hint_string); + + Point2 popup_pos=Point2( pos.x+(size.width-property_editor->get_size().width)/2.0,pos.y+(size.y-style->get_margin(MARGIN_BOTTOM))).floor(); + popup_pos+=get_global_pos(); + property_editor->set_pos(popup_pos); + + property_editor->popup(); + +} + +bool ShaderEditor::has_point(const Point2& p_point) const { + + int n,si; + + return _locate_click(p_point,&n,&si)!=CLICK_NONE; +} + +void ShaderEditor::_input_event(InputEvent p_event) { + + switch(p_event.type) { + + case InputEvent::MOUSE_BUTTON: { + + if (p_event.mouse_button.pressed) { + + + if (p_event.mouse_button.button_index==1) { + click_pos=Point2(p_event.mouse_button.x,p_event.mouse_button.y); + click_motion=click_pos; + click_type = _locate_click(click_pos,&click_node,&click_slot); + if( click_type!=CLICK_NONE) { + + order.erase(click_node); + order.push_back(click_node); + update(); + } + switch(click_type) { + case CLICK_INPUT_SLOT: { + click_pos=_get_slot_pos(click_node,true,click_slot); + } break; + case CLICK_OUTPUT_SLOT: { + click_pos=_get_slot_pos(click_node,false,click_slot); + } break; + case CLICK_PARAMETER: { + //open editor + _node_edit_property(click_node); + } break; + } + } + if (p_event.mouse_button.button_index==2) { + + if (click_type!=CLICK_NONE) { + click_type=CLICK_NONE; + update(); + } else { + // try to disconnect/remove + + Point2 rclick_pos=Point2(p_event.mouse_button.x,p_event.mouse_button.y); + rclick_type = _locate_click(rclick_pos,&rclick_node,&rclick_slot); + if (rclick_type==CLICK_INPUT_SLOT || rclick_type==CLICK_OUTPUT_SLOT) { + + node_popup->clear(); + node_popup->add_item("Disconnect",NODE_DISCONNECT); + node_popup->set_pos(rclick_pos); + node_popup->popup(); + + } + + if (rclick_type==CLICK_NODE) { + node_popup->clear(); + node_popup->add_item("Remove",NODE_ERASE); + node_popup->set_pos(rclick_pos); + node_popup->popup(); + } + + + } + } + } else { + + if (p_event.mouse_button.button_index==1 && click_type!=CLICK_NONE) { + + switch(click_type) { + case CLICK_INPUT_SLOT: + case CLICK_OUTPUT_SLOT: { + + Point2 dst_click_pos=Point2(p_event.mouse_button.x,p_event.mouse_button.y); + int id; + int slot; + ClickType dst_click_type = _locate_click(dst_click_pos,&id,&slot); + if (dst_click_type==CLICK_INPUT_SLOT && click_type==CLICK_OUTPUT_SLOT) { + + shader_graph.connect(click_node,click_slot,id,slot); + + Error err = validate_graph(); + if (err==ERR_CYCLIC_LINK) + shader_graph.disconnect(click_node,click_slot,id,slot); + _write_shader_graph(); + + } + if (click_type==CLICK_INPUT_SLOT && dst_click_type==CLICK_OUTPUT_SLOT) { + + shader_graph.connect(id,slot,click_node,click_slot); + + Error err = validate_graph(); + if (err==ERR_CYCLIC_LINK) + shader_graph.disconnect(id,slot,click_node,click_slot); + _write_shader_graph(); + } + + } break; + case CLICK_NODE: { + int new_x=shader_graph.node_get_pos_x(click_node)+(click_motion.x-click_pos.x); + int new_y=shader_graph.node_get_pos_y(click_node)+(click_motion.y-click_pos.y); + shader_graph.node_set_pos(click_node,new_x,new_y); + _write_shader_graph(); + + } break; + } + + click_type=CLICK_NONE; + update(); + } + } + + } + + case InputEvent::MOUSE_MOTION: { + + if (p_event.mouse_motion.button_mask&1 && click_type!=CLICK_NONE) { + + click_motion=Point2(p_event.mouse_button.x,p_event.mouse_button.y); + update(); + } + + } break; + } +} + +void ShaderEditor::_notification(int p_what) { + + + switch(p_what) { + + case NOTIFICATION_DRAW: { + + _update_scrollbars(); + //VisualServer::get_singleton()->canvas_item_add_rect(get_canvas_item(),Rect2(Point2(),get_size()),Color(0,0,0,1)); + + for(List<int>::Element *E=order.front();E;E=E->next()) { + + _draw_node(E->get()); + } + + if (click_type==CLICK_INPUT_SLOT || click_type==CLICK_OUTPUT_SLOT) { + + VisualServer::get_singleton()->canvas_item_add_line(get_canvas_item(),click_pos,click_motion,Color(0.5,1,0.5,0.8),2); + } + + List<ShaderGraph::Connection> connections = shader_graph.get_connection_list(); + for(List<ShaderGraph::Connection>::Element *E=connections.front();E;E=E->next()) { + + const ShaderGraph::Connection &c=E->get(); + Point2 source = _get_slot_pos(c.src_id,false,c.src_slot); + Point2 dest = _get_slot_pos(c.dst_id,true,c.dst_slot); + bool vec = VisualServer::shader_is_input_vector( shader_graph.node_get_type(c.dst_id), c.dst_slot ); + Color col = vec?Color(1,0.5,0.5,0.8):Color(1,1,0.5,0.8); + + if (click_type==CLICK_NODE && click_node==c.src_id) { + + source+=click_motion-click_pos; + } + + if (click_type==CLICK_NODE && click_node==c.dst_id) { + + dest+=click_motion-click_pos; + } + + VisualServer::get_singleton()->canvas_item_add_line(get_canvas_item(),source,dest,col,2); + + } + } break; + } + +} + +void ShaderEditor::_update_scrollbars() { + + Size2 size = get_size(); + Size2 hmin = h_scroll->get_minimum_size(); + Size2 vmin = v_scroll->get_minimum_size(); + + v_scroll->set_begin( Point2(size.width - vmin.width, 0) ); + v_scroll->set_end( Point2(size.width, size.height) ); + + h_scroll->set_begin( Point2( 0, size.height - hmin.height) ); + h_scroll->set_end( Point2(size.width-vmin.width, size.height) ); + + + Size2 min = _get_maximum_size(); + + if (min.height < size.height - hmin.height) { + + v_scroll->hide(); + offset.y=0; + } else { + + v_scroll->show(); + v_scroll->set_max(min.height); + v_scroll->set_page(size.height - hmin.height); + offset.y=v_scroll->get_val(); + } + + if (min.width < size.width - vmin.width) { + + h_scroll->hide(); + offset.x=0; + } else { + + h_scroll->show(); + h_scroll->set_max(min.width); + h_scroll->set_page(size.width - vmin.width); + offset.x=h_scroll->get_val(); + } +} + +void ShaderEditor::_scroll_moved() { + + offset.x=h_scroll->get_val(); + offset.y=v_scroll->get_val(); + update(); +} + +void ShaderEditor::_bind_methods() { + + ObjectTypeDB::bind_method( "_node_menu_item", &ShaderEditor::_node_menu_item ); + ObjectTypeDB::bind_method( "_node_add_callback", &ShaderEditor::_node_add_callback ); + ObjectTypeDB::bind_method( "_input_event", &ShaderEditor::_input_event ); + ObjectTypeDB::bind_method( "_node_param_changed", &ShaderEditor::_node_param_changed ); + ObjectTypeDB::bind_method( "_scroll_moved", &ShaderEditor::_scroll_moved ); + ObjectTypeDB::bind_method( "_vertex_item", &ShaderEditor::_vertex_item ); + ObjectTypeDB::bind_method( "_fragment_item", &ShaderEditor::_fragment_item ); + ObjectTypeDB::bind_method( "_post_item", &ShaderEditor::_post_item ); +} + +void ShaderEditor::_read_shader_graph() { + + shader_graph.clear();; + order.clear(); + List<int> nodes; + shader->get_node_list(&nodes); + int larger_id=0; + for(List<int>::Element *E=nodes.front();E;E=E->next()) { + + if (E->get() > larger_id) + larger_id = E->get(); + + shader_graph.node_add( (VS::ShaderNodeType)shader->node_get_type(E->get()), E->get() ); + shader_graph.node_set_param( E->get(), shader->node_get_param( E->get() ) ); + Point2 pos = shader->node_get_pos(E->get()); + shader_graph.node_set_pos( E->get(), pos.x,pos.y ); + order.push_back(E->get()); + } + + last_id=larger_id+1; + + List<Shader::Connection> connections; + shader->get_connections(&connections); + + for(List<Shader::Connection>::Element *E=connections.front();E;E=E->next()) { + + Shader::Connection &c=E->get(); + shader_graph.connect(c.src_id,c.src_slot,c.dst_id,c.dst_slot); + } + + validate_graph(); + update(); +} + +void ShaderEditor::_write_shader_graph() { + + shader->clear(); + List<int> nodes; + shader_graph.get_node_list(&nodes); + for(List<int>::Element *E=nodes.front();E;E=E->next()) { + + shader->node_add((Shader::NodeType)shader_graph.node_get_type(E->get()),E->get()); + shader->node_set_param(E->get(),shader_graph.node_get_param(E->get())); + shader->node_set_pos(E->get(),Point2( shader_graph.node_get_pos_x(E->get()),shader_graph.node_get_pos_y(E->get()) ) ); + } + + List<ShaderGraph::Connection> connections = shader_graph.get_connection_list(); + for(List<ShaderGraph::Connection>::Element *E=connections.front();E;E=E->next()) { + + const ShaderGraph::Connection &c=E->get(); + shader->connect(c.src_id,c.src_slot,c.dst_id,c.dst_slot); + } +} + +void ShaderEditor::_add_node_from_text(const String& p_text) { + + ERR_FAIL_COND( p_text.get_slice_count(" ") != 3 ); + bool input = p_text.get_slice(" ",0)=="In:"; + String name = p_text.get_slice(" ",1); + bool vec = p_text.get_slice(" ",2)=="(vec3)"; + + _node_add( input? + ( vec? VisualServer::NODE_VEC_IN : VisualServer::NODE_IN ) : + ( vec? VisualServer::NODE_VEC_OUT : VisualServer::NODE_OUT ) ); + + shader_graph.node_set_param( last_id-1,name ); + _write_shader_graph(); +} + +void ShaderEditor::_vertex_item(int p_item) { + + _add_node_from_text(vertex_popup->get_item_text(p_item)); +} +void ShaderEditor::_fragment_item(int p_item) { + + _add_node_from_text(fragment_popup->get_item_text(p_item)); +} +void ShaderEditor::_post_item(int p_item) { + + _add_node_from_text(post_popup->get_item_text(p_item)); +} + + +void ShaderEditor::_node_menu_item(int p_item) { + + switch(p_item) { + + case GRAPH_ADD_NODE: { + add_popup->popup_centered_ratio(); + validate_graph(); + } break; + case NODE_DISCONNECT: { + + if (rclick_type==CLICK_INPUT_SLOT) { + + List<ShaderGraph::Connection> connections = shader_graph.get_connection_list(); + for(List<ShaderGraph::Connection>::Element *E=connections.front();E;E=E->next()) { + + const ShaderGraph::Connection &c=E->get(); + if( c.dst_id==rclick_node && c.dst_slot==rclick_slot) { + + shader_graph.disconnect(c.src_id,c.src_slot,c.dst_id,c.dst_slot); + } + } + update(); + _write_shader_graph(); + validate_graph(); + } + + if (rclick_type==CLICK_OUTPUT_SLOT) { + + List<ShaderGraph::Connection> connections = shader_graph.get_connection_list(); + for(List<ShaderGraph::Connection>::Element *E=connections.front();E;E=E->next()) { + + const ShaderGraph::Connection &c=E->get(); + if( c.src_id==rclick_node && c.src_slot==rclick_slot) { + + shader_graph.disconnect(c.src_id,c.src_slot,c.dst_id,c.dst_slot); + } + } + update(); + _write_shader_graph(); + validate_graph(); + } + + } break; + case NODE_ERASE: { + + order.erase(rclick_node); + shader_graph.node_remove(rclick_node); + update(); + _write_shader_graph(); + validate_graph(); + } break; + case GRAPH_CLEAR: { + + order.clear(); + shader_graph.clear(); + last_id=1; + last_x=20; + last_y=20; + update(); + _write_shader_graph(); + validate_graph(); + + } break; + } +} + +void ShaderEditor::_node_add(VisualServer::ShaderNodeType p_type) { + + shader_graph.node_add(p_type,last_id ); + shader_graph.node_set_pos(last_id ,last_x,last_y); + String test_param; + + switch(p_type) { + case VS::NODE_PARAMETER: { + + test_param="param"; + } break; + case VS::NODE_VEC_PARAMETER: { + + test_param="vec"; + } break; + case VS::NODE_COLOR_PARAMETER: { + + test_param="color"; + } break; + case VS::NODE_TEXTURE_PARAMETER: { + + test_param="tex"; + } break; + case VS::NODE_TEXTURE_2D_PARAMETER: { + + test_param="tex2D"; + } break; + case VS::NODE_TEXTURE_CUBE_PARAMETER: { + + test_param="cubemap"; + } break; + case VS::NODE_TRANSFORM_PARAMETER: { + test_param="xform"; + } break; + case VS::NODE_LABEL: { + + test_param="label"; + } break; + } + + if(test_param!="") { + + int iter=0; + List<int> l; + + shader_graph.get_node_list(&l); + + bool found; + String test; + do { + iter++; + test=test_param; + if (iter>1) + test+="_"+itos(iter); + found=false; + for(List<int>::Element *E=l.front();E;E=E->next()) { + + + String param = shader_graph.node_get_param( E->get() ); + if (param==test) { + found=true; + break; + } + } + + } while (found); + + + shader_graph.node_set_param(last_id,test); + + } + order.push_back(last_id); + last_x+=10; + last_y+=10; + last_id++; + last_x=last_x % (int)get_size().width; + last_y=last_y % (int)get_size().height; + update(); + add_popup->hide();; + _write_shader_graph(); + +} + +void ShaderEditor::_node_add_callback() { + + TreeItem * item = add_types->get_selected(); + ERR_FAIL_COND(!item); + _node_add((VisualServer::ShaderNodeType)(int)item->get_metadata(0)); + add_popup->hide() ; +} + +ShaderEditor::ShaderEditor() { + + set_focus_mode(FOCUS_ALL); + + Panel* menu_panel = memnew( Panel ); + menu_panel->set_anchor( MARGIN_RIGHT, Control::ANCHOR_END ); + menu_panel->set_end( Point2(0,22) ); + + add_child( menu_panel ); + + PopupMenu *p; + List<PropertyInfo> defaults; + + MenuButton* node_menu = memnew( MenuButton ); + node_menu->set_text("Graph"); + node_menu->set_pos( Point2( 5,0) ); + menu_panel->add_child( node_menu ); + + p=node_menu->get_popup(); + p->add_item("Add Node",GRAPH_ADD_NODE); + p->add_separator(); + p->add_item("Clear",GRAPH_CLEAR); + p->connect("item_pressed", this,"_node_menu_item"); + + MenuButton* vertex_menu = memnew( MenuButton ); + vertex_menu->set_text("Vertex"); + vertex_menu->set_pos( Point2( 49,0) ); + menu_panel->add_child( vertex_menu ); + + p=vertex_menu->get_popup(); + defaults.clear(); + VisualServer::shader_get_default_input_nodes(VisualServer::SHADER_VERTEX,&defaults); + + int id=0; + for(int i=0;i<defaults.size();i++) { + + p->add_item("In: "+defaults[i].name+(defaults[i].type==Variant::VECTOR3?" (vec3)":" (real)"),id++); + } + p->add_separator(); + id++; + + defaults.clear(); + VisualServer::shader_get_default_output_nodes(VisualServer::SHADER_VERTEX,&defaults); + + for(int i=0;i<defaults.size();i++) { + + p->add_item("Out: "+defaults[i].name+(defaults[i].type==Variant::VECTOR3?" (vec3)":" (real)"),id++); + } + + vertex_popup=p; + vertex_popup->connect("item_pressed", this,"_vertex_item"); + MenuButton* fragment_menu = memnew( MenuButton ); + fragment_menu->set_text("Fragment"); + fragment_menu->set_pos( Point2( 95 ,0) ); + menu_panel->add_child( fragment_menu ); + + p=fragment_menu->get_popup(); + defaults.clear(); + VisualServer::shader_get_default_input_nodes(VisualServer::SHADER_FRAGMENT,&defaults); + id=0; + for(int i=0;i<defaults.size();i++) { + + p->add_item("In: "+defaults[i].name+(defaults[i].type==Variant::VECTOR3?" (vec3)":" (real)"),id++); + } + p->add_separator(); + id++; + defaults.clear(); + VisualServer::shader_get_default_output_nodes(VisualServer::SHADER_FRAGMENT,&defaults); + + for(int i=0;i<defaults.size();i++) { + + p->add_item("Out: "+defaults[i].name+(defaults[i].type==Variant::VECTOR3?" (vec3)":" (real)"),id++); + } + + fragment_popup=p; + fragment_popup->connect("item_pressed", this,"_fragment_item"); + + MenuButton* post_menu = memnew( MenuButton ); + post_menu->set_text("Post"); + post_menu->set_pos( Point2( 161,0) ); + menu_panel->add_child( post_menu ); + + p=post_menu->get_popup(); + defaults.clear(); + VisualServer::shader_get_default_input_nodes(VisualServer::SHADER_POST_PROCESS,&defaults); + id=0; + for(int i=0;i<defaults.size();i++) { + + p->add_item("In: "+defaults[i].name+(defaults[i].type==Variant::VECTOR3?" (vec3)":" (real)"),id++); + } + p->add_separator(); + id++; + + defaults.clear(); + VisualServer::shader_get_default_output_nodes(VisualServer::SHADER_POST_PROCESS,&defaults); + + for(int i=0;i<defaults.size();i++) { + + p->add_item("Out: "+defaults[i].name+(defaults[i].type==Variant::VECTOR3?" (vec3)":" (real)"),id++); + } + + post_popup=p; + post_popup->connect("item_pressed", this,"_post_item"); + + + /* add popup */ + + add_popup = memnew( Popup ); + add_child(add_popup); + add_popup->set_as_toplevel(true); + Panel *add_panel = memnew( Panel ); + add_popup->add_child(add_panel); + add_panel->set_area_as_parent_rect(); + + Label *add_label = memnew (Label ); + add_label->set_pos(Point2(5,5)); + add_label->set_text("Available Nodes:"); + add_panel->add_child(add_label); + + + add_types = memnew( Tree ); + add_types->set_anchor( MARGIN_RIGHT, ANCHOR_END ); + add_types->set_anchor( MARGIN_BOTTOM, ANCHOR_END ); + add_types->set_begin( Point2( 20,25 ) ); + add_types->set_end( Point2( 10, 30 ) ); + add_types->set_hide_root(true); + add_types->set_columns(4); + add_types->set_select_mode(Tree::SELECT_ROW); + + + TreeItem *add_types_root = add_types->create_item(NULL); + TreeItem *info_item = add_types->create_item(add_types_root); + + for(int i=0;i<VisualServer::NODE_TYPE_MAX;i++) { + + TreeItem *item = add_types->create_item(add_types_root); + PropertyInfo prop = VisualServer::shader_node_get_type_info((VisualServer::ShaderNodeType)i); + item->set_text(0,prop.name); + item->set_text(1,itos(VisualServer::shader_get_input_count((VisualServer::ShaderNodeType)i))); + item->set_text(2,itos(VisualServer::shader_get_output_count((VisualServer::ShaderNodeType)i))); + String hint = (prop.type==Variant::_RID)?prop.hint_string:Variant::get_type_name(prop.type); + item->set_text(3,hint); + item->set_metadata(0,i); + } + info_item->set_text(0,"::NODE::"); + info_item->set_custom_color(0,Color(0.6,0.1,0.1)); + info_item->set_text(1,"::INPUTS::"); + info_item->set_custom_color(1,Color(0.6,0.1,0.1)); + info_item->set_text(2,"::OUTPUTS::"); + info_item->set_custom_color(2,Color(0.6,0.1,0.1)); + info_item->set_text(3,"::PARAM::"); + info_item->set_custom_color(3,Color(0.6,0.1,0.1)); + info_item->set_selectable(0,false); + info_item->set_selectable(1,false); + info_item->set_selectable(2,false); + info_item->set_selectable(3,false); + + add_panel->add_child(add_types); + + add_confirm = memnew( Button ); + add_confirm->set_anchor( MARGIN_LEFT, ANCHOR_END ); + add_confirm->set_anchor( MARGIN_TOP, ANCHOR_END ); + add_confirm->set_anchor( MARGIN_RIGHT, ANCHOR_END ); + add_confirm->set_anchor( MARGIN_BOTTOM, ANCHOR_END ); + add_confirm->set_begin( Point2( 75, 29 ) ); + add_confirm->set_end( Point2( 10, 15 ) ); + add_confirm->set_text("Add"); + add_panel->add_child(add_confirm); + add_confirm->connect("pressed", this,"_node_add_callback"); + + last_id=1; + last_x=20; + last_y=20; + + property_editor = memnew( CustomPropertyEditor ); + add_child(property_editor); + property_editor->connect("variant_changed", this,"_node_param_changed"); + + h_scroll = memnew( HScrollBar ); + v_scroll = memnew( VScrollBar ); + + add_child(h_scroll); + add_child(v_scroll); + + h_scroll->connect("value_changed", this,"_scroll_moved"); + v_scroll->connect("value_changed", this,"_scroll_moved"); + + node_popup= memnew(PopupMenu ); + add_child(node_popup); + node_popup->set_as_toplevel(true); + + node_popup->connect("item_pressed", this,"_node_menu_item"); + +} + + +void ShaderEditorPlugin::edit(Object *p_object) { + + shader_editor->edit(p_object->cast_to<Shader>()); +} + +bool ShaderEditorPlugin::handles(Object *p_object) const { + + return p_object->is_type("Shader"); +} + +void ShaderEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + shader_editor->show(); + shader_editor->set_process(true); + } else { + + shader_editor->hide(); + shader_editor->set_process(false); + } + +} + +ShaderEditorPlugin::ShaderEditorPlugin(EditorNode *p_node) { + + editor=p_node; + shader_editor = memnew( ShaderEditor ); + editor->get_viewport()->add_child(shader_editor); + shader_editor->set_area_as_parent_rect(); + shader_editor->hide(); + + + +} + + +ShaderEditorPlugin::~ShaderEditorPlugin() +{ +} + + +#endif diff --git a/tools/editor/plugins/shader_graph_editor_plugin.h b/tools/editor/plugins/shader_graph_editor_plugin.h new file mode 100644 index 000000000..5b0767dc8 --- /dev/null +++ b/tools/editor/plugins/shader_graph_editor_plugin.h @@ -0,0 +1,150 @@ +/*************************************************************************/ +/* shader_graph_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef SHADER_GRAPH_EDITOR_PLUGIN_H +#define SHADER_GRAPH_EDITOR_PLUGIN_H + +#if 0 +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" +#include "scene/resources/shader.h" +#include "servers/visual/shader_graph.h" +#include "scene/gui/tree.h" +#include "scene/gui/button.h" +#include "scene/gui/popup.h" +#include "tools/editor/property_editor.h" +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + +class ShaderEditor : public Control { + + OBJ_TYPE(ShaderEditor, Control ); + + enum MenuAction { + + GRAPH_ADD_NODE, + GRAPH_CLEAR, + NODE_DISCONNECT, + NODE_ERASE, + + }; + + enum ClickType { + CLICK_NONE, + CLICK_NODE, + CLICK_INPUT_SLOT, + CLICK_OUTPUT_SLOT, + CLICK_PARAMETER + }; + + PopupMenu *node_popup; + Popup *add_popup; + PopupMenu *vertex_popup; + PopupMenu *fragment_popup; + PopupMenu *post_popup; + Tree *add_types; + Button *add_confirm; + HScrollBar *h_scroll; + VScrollBar *v_scroll; + + Ref<Shader> shader; + List<int> order; + Set<int> active_nodes; + ShaderGraph shader_graph; + int last_x,last_y; + uint32_t last_id; + + CustomPropertyEditor *property_editor; + + Point2 offset; + ClickType click_type; + Point2 click_pos; + int click_node; + int click_slot; + Point2 click_motion; + ClickType rclick_type; + int rclick_node; + int rclick_slot; + + Size2 _get_maximum_size(); + Size2 get_node_size(int p_node) const; + void _draw_node(int p_node); + + void _add_node_from_text(const String& p_text); + void _update_scrollbars(); + void _scroll_moved(); + void _node_param_changed(); + void _node_add_callback(); + void _node_add(VisualServer::ShaderNodeType p_type); + void _node_edit_property(int p_node); + void _node_menu_item(int p_item); + void _vertex_item(int p_item); + void _fragment_item(int p_item); + void _post_item(int p_item); + + ClickType _locate_click(const Point2& p_click,int *p_node_id,int *p_slot_index) const; + Point2 _get_slot_pos(int p_node_id,bool p_input,int p_slot); + + Error validate_graph(); + + void _read_shader_graph(); + void _write_shader_graph(); + + virtual bool has_point(const Point2& p_point) const; +protected: + void _notification(int p_what); + void _input_event(InputEvent p_event); + static void _bind_methods(); +public: + + void edit(Ref<Shader> p_shader); + ShaderEditor(); +}; + +class ShaderEditorPlugin : public EditorPlugin { + + OBJ_TYPE( ShaderEditorPlugin, EditorPlugin ); + + ShaderEditor *shader_editor; + EditorNode *editor; + +public: + + virtual String get_name() const { return "Shader"; } + 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); + + ShaderEditorPlugin(EditorNode *p_node); + ~ShaderEditorPlugin(); + +}; +#endif +#endif // SHADER_GRAPH_EDITOR_PLUGIN_H diff --git a/tools/editor/plugins/spatial_editor_plugin.cpp b/tools/editor/plugins/spatial_editor_plugin.cpp new file mode 100644 index 000000000..be7214aaa --- /dev/null +++ b/tools/editor/plugins/spatial_editor_plugin.cpp @@ -0,0 +1,3311 @@ +/*************************************************************************/ +/* spatial_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "spatial_editor_plugin.h" +#include "print_string.h" + +#include "os/keyboard.h" +#include "scene/3d/visual_instance.h" +#include "scene/3d/camera.h" +#include "camera_matrix.h" +#include "sort.h" +#include "tools/editor/editor_node.h" +#include "tools/editor/editor_settings.h" +#include "scene/resources/surface_tool.h" +#include "tools/editor/spatial_editor_gizmos.h" + +#define DISTANCE_DEFAULT 4 + + +#define GIZMO_ARROW_SIZE 0.3 +#define GIZMO_RING_HALF_WIDTH 0.1 +//#define GIZMO_SCALE_DEFAULT 0.28 +#define GIZMO_SCALE_DEFAULT 0.15 + + +//void SpatialEditorViewport::_update_camera(); + +String SpatialEditorGizmo::get_handle_name(int p_idx) const { + + return ""; +} + +Variant SpatialEditorGizmo::get_handle_value(int p_idx) const{ + + return Variant(); +} + +void SpatialEditorGizmo::set_handle(int p_idx,Camera *p_camera, const Point2& p_point) { + +} + +void SpatialEditorGizmo::commit_handle(int p_idx,const Variant& p_restore,bool p_cancel){ + + +} + +bool SpatialEditorGizmo::intersect_frustum(const Camera *p_camera,const Vector<Plane> &p_frustum) { + + return false; +} + +bool SpatialEditorGizmo::intersect_ray(const Camera *p_camera, const Point2 &p_point, Vector3& r_pos, Vector3& r_normal,int *r_gizmo_handle,bool p_sec_first) { + + return false; +} + +SpatialEditorGizmo::SpatialEditorGizmo(){ + + +} + + + +int SpatialEditorViewport::get_selected_count() const { + + + Map<Node*,Object*> &selection = editor_selection->get_selection(); + + int count=0; + + for(Map<Node*,Object*>::Element *E=selection.front();E;E=E->next()) { + + Spatial *sp = E->key()->cast_to<Spatial>(); + if (!sp) + continue; + + SpatialEditorSelectedItem *se=editor_selection->get_node_editor_data<SpatialEditorSelectedItem>(sp); + if (!se) + continue; + + count++; + } + + return count; +} + + + +float SpatialEditorViewport::get_znear() const { + + float val = spatial_editor->get_znear(); + if (val<0.001) + val=0.001; + return val; +} +float SpatialEditorViewport::get_zfar() const{ + + float val = spatial_editor->get_zfar(); + if (val<0.001) + val=0.001; + return val; + +} +float SpatialEditorViewport::get_fov() const{ + + float val = spatial_editor->get_fov(); + if (val<0.001) + val=0.001; + if (val>89) + val=89; + return val; + +} + + + +Transform SpatialEditorViewport::_get_camera_transform() const { + + return camera->get_global_transform(); +} + +Vector3 SpatialEditorViewport::_get_camera_pos() const { + + return _get_camera_transform().origin; +} + +Point2 SpatialEditorViewport::_point_to_screen(const Vector3& p_point) { + + return camera->unproject_position(p_point); + +} + +Vector3 SpatialEditorViewport::_get_ray_pos(const Vector2& p_pos) const { + + return camera->project_ray_origin(p_pos); +} + + +Vector3 SpatialEditorViewport::_get_camera_normal() const { + + return -_get_camera_transform().basis.get_axis(2); +} + +Vector3 SpatialEditorViewport::_get_ray(const Vector2& p_pos) { + + + return camera->project_ray_normal(p_pos); + + +} +/* +void SpatialEditorViewport::_clear_id(Spatial *p_node) { + + + editor_selection->remove_node(p_node); + + +} +*/ +void SpatialEditorViewport::_clear_selected() { + + editor_selection->clear(); +} + + + +void SpatialEditorViewport::_select_clicked(bool p_append,bool p_single) { + + if (!clicked) + return; + + Object *obj = ObjectDB::get_instance(clicked); + if (!obj) + return; + + + Spatial *sp = obj->cast_to<Spatial>(); + if (!sp) + return; + + _select(sp, clicked_wants_append,true); +} + + + +void SpatialEditorViewport::_select(Spatial *p_node, bool p_append,bool p_single) { + + + if (!p_append) { + + // should not modify the selection.. + + editor_selection->clear(); + editor_selection->add_node(p_node); + + } else { + + if (editor_selection->is_selected(p_node) && p_single) { + //erase + editor_selection->remove_node(p_node); + } else { + + editor_selection->add_node(p_node); + } + + } + +} + + +struct _RayResult { + + Spatial* item; + float depth; + int handle; + _FORCE_INLINE_ bool operator<(const _RayResult& p_rr) const { return depth<p_rr.depth; } +}; + +ObjectID SpatialEditorViewport::_select_ray(const Point2& p_pos, bool p_append,bool &r_includes_current,int *r_gizmo_handle,bool p_alt_select) { + + if (r_gizmo_handle) + *r_gizmo_handle=-1; + + Vector3 ray=_get_ray(p_pos); + Vector3 pos=_get_ray_pos(p_pos); + + Vector<RID> instances=VisualServer::get_singleton()->instances_cull_ray(pos,ray,get_scene()->get_root()->get_world()->get_scenario() ); + Set<Ref<SpatialEditorGizmo> > found_gizmos; + + //uint32_t closest=0; +// float closest_dist=0; + + r_includes_current=false; + + List<_RayResult> results; + Vector3 cn=_get_camera_normal(); + Plane cplane(pos,cn.normalized()); + + float min_d=1e20; + + for (int i=0;i<instances.size();i++) { + + uint32_t id=VisualServer::get_singleton()->instance_get_object_instance_ID(instances[i]); + Object *obj=ObjectDB::get_instance(id); + if (!obj) + continue; + + Spatial *spat=obj->cast_to<Spatial>(); + + if (!spat) + continue; + + Ref<SpatialEditorGizmo> seg = spat->get_gizmo(); + + if (!seg.is_valid()) + continue; + + if (found_gizmos.has(seg)) + continue; + + found_gizmos.insert(seg); + Vector3 point; + Vector3 normal; + + int handle=-1; + bool inters = seg->intersect_ray(camera,p_pos,point,normal,&handle,p_alt_select); + + if (!inters) + continue; + + float dist = pos.distance_to(point); + + if (dist<0) + continue; + + + + if (editor_selection->is_selected(spat)) + r_includes_current=true; + + _RayResult res; + res.item=spat; + res.depth=dist; + res.handle=handle; + results.push_back(res); + } + + + if (results.empty()) + return 0; + + results.sort(); + Spatial *s=NULL; + + + if (!r_includes_current || results.size()==1 || (r_gizmo_handle && results.front()->get().handle>=0)) { + + //return the nearest one + s = results.front()->get().item; + if (r_gizmo_handle) + *r_gizmo_handle=results.front()->get().handle; + + } else { + + //returns the next one from a curent selection + List<_RayResult>::Element *E=results.front(); + List<_RayResult>::Element *S=NULL; + + + while(true) { + + //very strange loop algorithm that complies with object selection standards (tm). + + if (S==E) { + //went all around and anothing was found + //since can't rotate the selection + //just return the first one + + s=results.front()->get().item; + break; + + } + + if (!S && editor_selection->is_selected(E->get().item)) { + //found an item currently in the selection, + //so start from this one + S=E; + } + + if (S && !editor_selection->is_selected(E->get().item)) { + // free item after a selected item, this one is desired. + s=E->get().item; + break; + } + + E=E->next(); + if (!E) { + + if (!S) { + //did a loop but nothing was selected, select first + s=results.front()->get().item; + break; + + } + E=results.front(); + } + } + } + + if (!s) + return 0; + + return s->get_instance_ID(); + +} + + +Vector3 SpatialEditorViewport::_get_screen_to_space(const Vector3& p_pos) { + + + CameraMatrix cm; + cm.set_perspective(get_fov(),get_size().get_aspect(),get_znear(),get_zfar()); + float screen_w,screen_h; + cm.get_viewport_size(screen_w,screen_h); + + Transform camera_transform; + camera_transform.translate( cursor.pos ); + camera_transform.basis.rotate(Vector3(0,1,0),cursor.y_rot); + camera_transform.basis.rotate(Vector3(1,0,0),cursor.x_rot); + camera_transform.translate(0,0,cursor.distance); + + return camera_transform.xform(Vector3( ((p_pos.x/get_size().width)*2.0-1.0)*screen_w, ((1.0-(p_pos.y/get_size().height))*2.0-1.0)*screen_h,-get_znear())); + +} + + +void SpatialEditorViewport::_select_region() { + + if (cursor.region_begin==cursor.region_end) + return; //nothing really + + Vector3 box[4]={ + Vector3( + MIN( cursor.region_begin.x, cursor.region_end.x), + MIN( cursor.region_begin.y, cursor.region_end.y), + 0 + ), + Vector3( + MAX( cursor.region_begin.x, cursor.region_end.x), + MIN( cursor.region_begin.y, cursor.region_end.y), + 0 + ), + Vector3( + MAX( cursor.region_begin.x, cursor.region_end.x), + MAX( cursor.region_begin.y, cursor.region_end.y), + 0 + ), + Vector3( + MIN( cursor.region_begin.x, cursor.region_end.x), + MAX( cursor.region_begin.y, cursor.region_end.y), + 0 + ) + }; + + Vector<Plane> frustum; + + Vector3 cam_pos=_get_camera_pos(); + Set<Ref<SpatialEditorGizmo> > found_gizmos; + + for(int i=0;i<4;i++) { + + Vector3 a=_get_screen_to_space(box[i]); + Vector3 b=_get_screen_to_space(box[(i+1)%4]); + frustum.push_back( Plane(a,b,cam_pos) ); + } + + Plane near( cam_pos, -_get_camera_normal() ); + near.d-=get_znear(); + + frustum.push_back( near ); + + Plane far=-near; + far.d+=500.0; + + frustum.push_back( far ); + + Vector<RID> instances=VisualServer::get_singleton()->instances_cull_convex(frustum,get_scene()->get_root()->get_world()->get_scenario()); + + + for (int i=0;i<instances.size();i++) { + + uint32_t id=VisualServer::get_singleton()->instance_get_object_instance_ID(instances[i]); + + Object *obj=ObjectDB::get_instance(id); + if (!obj) + continue; + Spatial *sp = obj->cast_to<Spatial>(); + if (!sp) + continue; + + Ref<SpatialEditorGizmo> seg = sp->get_gizmo(); + + if (!seg.is_valid()) + continue; + + if (found_gizmos.has(seg)) + continue; + + if (seg->intersect_frustum(camera,frustum)) + _select(sp,true,false); + } + +} + + +void SpatialEditorViewport::_compute_edit(const Point2& p_point) { + + _edit.click_ray=_get_ray( Vector2( p_point.x, p_point.y ) ); + _edit.click_ray_pos=_get_ray_pos( Vector2( p_point.x, p_point.y ) ); + _edit.plane=TRANSFORM_VIEW; + spatial_editor->update_transform_gizmo(); + _edit.center=spatial_editor->get_gizmo_transform().origin; + + List<Node*> &selection = editor_selection->get_selected_node_list(); + +// Vector3 center; +// int nc=0; + for(List<Node*>::Element *E=selection.front();E;E=E->next()) { + + Spatial *sp = E->get()->cast_to<Spatial>(); + if (!sp) + continue; + + SpatialEditorSelectedItem *se=editor_selection->get_node_editor_data<SpatialEditorSelectedItem>(sp); + if (!se) + continue; + + se->original=se->sp->get_global_transform(); +// center+=se->original.origin; +// nc++; + } + + +// if (nc) +// _edit.center=center/float(nc); + + + +} + +static int _get_key_modifier(const String& p_property) { + + switch(EditorSettings::get_singleton()->get(p_property).operator int()) { + + case 0: return 0; + case 1: return KEY_SHIFT; + case 2: return KEY_ALT; + case 3: return KEY_META; + case 4: return KEY_CONTROL; + } + return 0; +} + + +bool SpatialEditorViewport::_gizmo_select(const Vector2& p_screenpos,bool p_hilite_only) { + + if (!spatial_editor->is_gizmo_visible()) + return false; + if (get_selected_count()==0) { + if (p_hilite_only) + spatial_editor->select_gizmo_hilight_axis(-1); + return false; + } + + + Vector3 ray_pos=_get_ray_pos( Vector2( p_screenpos.x, p_screenpos.y ) ); + Vector3 ray=_get_ray( Vector2( p_screenpos.x, p_screenpos.y ) ); + + Vector3 cn=_get_camera_normal(); + Plane cplane(ray_pos,cn.normalized()); + + Transform gt = spatial_editor->get_gizmo_transform(); + float gs=0; + if (orthogonal) { + gs= cursor.distance/surface->get_size().get_aspect(); + + } else { + gs = cplane.distance_to(gt.origin); + } + + gs*=GIZMO_SCALE_DEFAULT; + + + if (spatial_editor->get_tool_mode()==SpatialEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode()==SpatialEditor::TOOL_MODE_MOVE) { + + int col_axis=-1; + float col_d=1e20; + + for(int i=0;i<3;i++) { + + Vector3 grabber_pos = gt.origin+gt.basis.get_axis(i)*gs; + float grabber_radius = gs*GIZMO_ARROW_SIZE; + + Vector3 r; + if (Geometry::segment_intersects_sphere(ray_pos,ray_pos+ray*10000.0,grabber_pos,grabber_radius,&r)) { + float d = r.distance_to(ray_pos); + if (d<col_d) { + col_d=d; + col_axis=i; + } + } + } + + if (col_axis!=-1) { + + + if (p_hilite_only) { + + spatial_editor->select_gizmo_hilight_axis(col_axis); + + + } else { + //handle rotate + _edit.mode=TRANSFORM_TRANSLATE; + _compute_edit(Point2(p_screenpos.x,p_screenpos.y)); + _edit.plane=TransformPlane(TRANSFORM_X_AXIS+col_axis); + } + return true; + + + } + + } + + + if (spatial_editor->get_tool_mode()==SpatialEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode()==SpatialEditor::TOOL_MODE_ROTATE) { + + int col_axis=-1; + float col_d=1e20; + + for(int i=0;i<3;i++) { + + Plane plane(gt.origin,gt.basis.get_axis(i).normalized()); + Vector3 r; + if (!plane.intersects_ray(ray_pos,ray,&r)) + continue; + + float dist = r.distance_to(gt.origin); + + + + if (dist > gs*(1-GIZMO_RING_HALF_WIDTH) && dist < gs *(1+GIZMO_RING_HALF_WIDTH)) { + + float d = ray_pos.distance_to(r); + if (d<col_d) { + col_d=d; + col_axis=i; + } + } + } + + if (col_axis!=-1) { + + if (p_hilite_only) { + + spatial_editor->select_gizmo_hilight_axis(col_axis+3); + } else { + //handle rotate + _edit.mode=TRANSFORM_ROTATE; + _compute_edit(Point2(p_screenpos.x,p_screenpos.y)); + _edit.plane=TransformPlane(TRANSFORM_X_AXIS+col_axis); + } + return true; + } + } + + + if (p_hilite_only) + spatial_editor->select_gizmo_hilight_axis(-1); + + return false; + +} + + +void SpatialEditorViewport::_smouseenter() { + + surface->grab_focus(); +} + +void SpatialEditorViewport::_sinput(const InputEvent &p_event) { + + if (previewing) + return; //do NONE + + + { + + EditorNode *en = editor; + EditorPlugin *over_plugin = en->get_editor_plugin_over(); + + if (over_plugin) { + bool discard = over_plugin->forward_spatial_input_event(camera,p_event); + if (discard) + return; + } + } + + + switch(p_event.type) { + case InputEvent::MOUSE_BUTTON: { + + const InputEventMouseButton &b=p_event.mouse_button; + + switch(b.button_index) { + + case BUTTON_WHEEL_UP: { + + cursor.distance/=1.08; + } break; + case BUTTON_WHEEL_DOWN: { + + cursor.distance*=1.08; + + } break; + case BUTTON_RIGHT: { + + + if (b.pressed && _edit.gizmo.is_valid()) { + //restore + _edit.gizmo->commit_handle(_edit.gizmo_handle,_edit.gizmo_initial_value,true); + _edit.gizmo=Ref<SpatialEditorGizmo>(); + } + + if (_edit.mode==TRANSFORM_NONE && b.pressed) { + + Plane cursor_plane(cursor.cursor_pos,_get_camera_normal()); + + Vector3 ray_origin = _get_ray_pos(Vector2(b.x,b.y)); + Vector3 ray_dir = _get_ray(Vector2(b.x,b.y)); + + + //gizmo modify + + if (b.mod.control) { + + Vector<RID> instances=VisualServer::get_singleton()->instances_cull_ray(ray_origin,ray_dir,get_scene()->get_root()->get_world()->get_scenario() ); + + Plane p(ray_origin,_get_camera_normal()); + + float min_d=1e10; + bool found=false; + + for (int i=0;i<instances.size();i++) { + + uint32_t id=VisualServer::get_singleton()->instance_get_object_instance_ID(instances[i]); + Object *obj=ObjectDB::get_instance(id); + if (!obj) + continue; + + VisualInstance *vi=obj->cast_to<VisualInstance>(); + if (!vi) + continue; + + //optimize by checking AABB (although should pre sort by distance) + AABB aabb = vi->get_global_transform().xform(vi->get_aabb()); + if (p.distance_to(aabb.get_support(-ray_dir))>min_d) + continue; + + DVector<Face3> faces = vi->get_faces(VisualInstance::FACES_SOLID); + int c = faces.size(); + if (c>0) { + DVector<Face3>::Read r = faces.read(); + + for(int j=0;j<c;j++) { + + Vector3 inters; + if (r[j].intersects_ray(ray_origin,ray_dir,&inters)) { + + float d = p.distance_to(inters); + if (d<0) + continue; + + if (d<min_d) { + min_d=d; + found=true; + } + } + + } + } + + } + + if (found) { + + //cursor.cursor_pos=ray_origin+ray_dir*min_d; + //VisualServer::get_singleton()->instance_set_transform(cursor_instance,Transform(Matrix3(),cursor.cursor_pos)); + + } + + } else { + Vector3 new_pos; + if (cursor_plane.intersects_ray(ray_origin,ray_dir,&new_pos)) { + + //cursor.cursor_pos=new_pos; + //VisualServer::get_singleton()->instance_set_transform(cursor_instance,Transform(Matrix3(),cursor.cursor_pos)); + } + } + } + + if (_edit.mode!=TRANSFORM_NONE && b.pressed) { + //cancel motion + _edit.mode=TRANSFORM_NONE; + //_validate_selection(); + + List<Node*> &selection = editor_selection->get_selected_node_list(); + + for(List<Node*>::Element *E=selection.front();E;E=E->next()) { + + Spatial *sp = E->get()->cast_to<Spatial>(); + if (!sp) + continue; + + SpatialEditorSelectedItem *se=editor_selection->get_node_editor_data<SpatialEditorSelectedItem>(sp); + if (!se) + continue; + + sp->set_global_transform( se->original ); + + } + surface->update(); + //VisualServer::get_singleton()->poly_clear(indicators); + set_message("Transform Aborted.",3); + } + } break; + case BUTTON_MIDDLE: { + + if (b.pressed && _edit.mode!=TRANSFORM_NONE) { + + switch(_edit.plane ) { + + case TRANSFORM_VIEW: { + + _edit.plane=TRANSFORM_X_AXIS; + set_message("View Plane Transform.",2); + } break; + case TRANSFORM_X_AXIS: { + + _edit.plane=TRANSFORM_Y_AXIS; + set_message("X-Axis Transform.",2); + + } break; + case TRANSFORM_Y_AXIS: { + + _edit.plane=TRANSFORM_Z_AXIS; + set_message("Y-Axis Transform.",2); + + } break; + case TRANSFORM_Z_AXIS: { + + _edit.plane=TRANSFORM_VIEW; + set_message("Z-Axis Transform.",2); + + } break; + } + + } + } break; + case BUTTON_LEFT: { + + if (b.pressed) { + + _edit.mouse_pos=Point2(b.x,b.y); + _edit.snap=false; + _edit.mode=TRANSFORM_NONE; + + //gizmo has priority over everything + + if (_gizmo_select(_edit.mouse_pos)) + break; + + clicked=0; + clicked_includes_current=false; + + + if ((spatial_editor->get_tool_mode()==SpatialEditor::TOOL_MODE_SELECT && b.mod.control) || spatial_editor->get_tool_mode()==SpatialEditor::TOOL_MODE_ROTATE) { + + /* HANDLE ROTATION */ + if (get_selected_count()==0) + break; //bye + //handle rotate + _edit.mode=TRANSFORM_ROTATE; + _compute_edit(Point2(b.x,b.y)); + break; + + } + + if (spatial_editor->get_tool_mode()==SpatialEditor::TOOL_MODE_MOVE) { + + if (get_selected_count()==0) + break; //bye + //handle rotate + _edit.mode=TRANSFORM_TRANSLATE; + _compute_edit(Point2(b.x,b.y)); + break; + + + } + + + if (spatial_editor->get_tool_mode()==SpatialEditor::TOOL_MODE_SCALE) { + + if (get_selected_count()==0) + break; //bye + //handle rotate + _edit.mode=TRANSFORM_SCALE; + _compute_edit(Point2(b.x,b.y)); + break; + + + } + + + + // todo scale + + int gizmo_handle=-1; + + clicked=_select_ray(Vector2( b.x, b.y ),b.mod.shift,clicked_includes_current,&gizmo_handle,b.mod.shift); + + //clicking is always deferred to either move or release + + clicked_wants_append=b.mod.shift; + + if (!clicked) { + + if (!clicked_wants_append) + _clear_selected(); + + //default to regionselect + cursor.region_select=true; + cursor.region_begin=Point2(b.x,b.y); + cursor.region_end=Point2(b.x,b.y); + } + + if (clicked && gizmo_handle>=0) { + + Object *obj=ObjectDB::get_instance(clicked); + if (obj) { + + Spatial *spa = obj->cast_to<Spatial>(); + if (spa) { + + Ref<SpatialEditorGizmo> seg=spa->get_gizmo(); + if (seg.is_valid()) { + + _edit.gizmo=seg; + _edit.gizmo_handle=gizmo_handle; + //_edit.gizmo_initial_pos=seg->get_handle_pos(gizmo_handle); + _edit.gizmo_initial_value=seg->get_handle_value(gizmo_handle); + //print_line("GIZMO: "+itos(gizmo_handle)+" FROMPOS: "+_edit.orig_gizmo_pos); + break; + + } + } + + } + //_compute_edit(Point2(b.x,b.y)); //in case a motion happens.. + } + + + + surface->update(); + } else { + + + if (_edit.gizmo.is_valid()) { + + _edit.gizmo->commit_handle(_edit.gizmo_handle,_edit.gizmo_initial_value,false); + _edit.gizmo=Ref<SpatialEditorGizmo>(); + break; + } + if (clicked) { + _select_clicked(clicked_wants_append,true); + //clickd processing was deferred + clicked=0; + + + } + + if (cursor.region_select) { + _select_region(); + cursor.region_select=false; + surface->update(); + } + + + if (_edit.mode!=TRANSFORM_NONE) { + + + static const char* _transform_name[4]={"None","Rotate","Translate","Scale"}; + undo_redo->create_action(_transform_name[_edit.mode]); + + List<Node*> &selection = editor_selection->get_selected_node_list(); + + for(List<Node*>::Element *E=selection.front();E;E=E->next()) { + + Spatial *sp = E->get()->cast_to<Spatial>(); + if (!sp) + continue; + + SpatialEditorSelectedItem *se=editor_selection->get_node_editor_data<SpatialEditorSelectedItem>(sp); + if (!se) + continue; + + undo_redo->add_do_method(sp,"set_global_transform",sp->get_global_transform()); + undo_redo->add_undo_method(sp,"set_global_transform",se->original); + } + undo_redo->commit_action(); + _edit.mode=TRANSFORM_NONE; + //VisualServer::get_singleton()->poly_clear(indicators); + set_message(""); + } + + + surface->update(); + } + } break; + } + } break; + case InputEvent::MOUSE_MOTION: { + + const InputEventMouseMotion &m=p_event.mouse_motion; + _edit.mouse_pos=Point2(p_event.mouse_motion.x,p_event.mouse_motion.y); + + if (!(m.button_mask&1) && !_edit.gizmo.is_valid()) { + + _gizmo_select(_edit.mouse_pos,true); + + } + + + if (_edit.gizmo.is_valid()) { + + Plane plane=Plane(_edit.gizmo_initial_pos,_get_camera_normal()); + + + Vector3 ray_pos=_get_ray_pos( Vector2( m.x, m.y ) ); + Vector3 ray=_get_ray( Vector2( m.x, m.y ) ); + + //Vector3 intersection; + //if (!plane.intersects_ray(ray_pos,ray,&intersection)) + // break; + + _edit.gizmo->set_handle(_edit.gizmo_handle,camera,Vector2(m.x,m.y)); + Variant v = _edit.gizmo->get_handle_value(_edit.gizmo_handle); + String n = _edit.gizmo->get_handle_name(_edit.gizmo_handle); + set_message(n+": "+String(v)); + + } else if (m.button_mask&1) { + + if (clicked) { + + if (!clicked_includes_current) { + + _select_clicked(clicked_wants_append,true); + //clickd processing was deferred + } + + + _compute_edit(_edit.mouse_pos); + clicked=0; + + _edit.mode=TRANSFORM_TRANSLATE; + + } + + if (cursor.region_select) { + + cursor.region_end=Point2(m.x,m.y); + surface->update(); + return; + } + + if (_edit.mode==TRANSFORM_NONE) + break; + + Vector3 ray_pos=_get_ray_pos( Vector2( m.x, m.y ) ); + Vector3 ray=_get_ray( Vector2( m.x, m.y ) ); + + + switch(_edit.mode) { + + case TRANSFORM_SCALE: { + + + Plane plane=Plane(_edit.center,_get_camera_normal()); + + + Vector3 intersection; + if (!plane.intersects_ray(ray_pos,ray,&intersection)) + break; + + Vector3 click; + if (!plane.intersects_ray(_edit.click_ray_pos,_edit.click_ray,&click)) + break; + + float center_click_dist = click.distance_to(_edit.center); + float center_inters_dist = intersection.distance_to(_edit.center); + if (center_click_dist==0) + break; + + float scale = (center_inters_dist / center_click_dist)*100.0; + + if (_edit.snap || spatial_editor->is_snap_enabled()) { + + scale = Math::stepify(scale,spatial_editor->get_scale_snap()); + } + + set_message("Scaling to "+String::num(scale,1)+"%."); + scale/=100.0; + + Transform r; + r.basis.scale(Vector3(scale,scale,scale)); + + + List<Node*> &selection = editor_selection->get_selected_node_list(); + + for(List<Node*>::Element *E=selection.front();E;E=E->next()) { + + Spatial *sp = E->get()->cast_to<Spatial>(); + if (!sp) + continue; + + SpatialEditorSelectedItem *se=editor_selection->get_node_editor_data<SpatialEditorSelectedItem>(sp); + if (!se) + continue; + + + Transform original=se->original; + + Transform base=Transform( Matrix3(), _edit.center); + Transform t=base * (r * (base.inverse() * original)); + + sp->set_global_transform(t); + } + + surface->update(); + + } break; + + case TRANSFORM_TRANSLATE: { + + + Vector3 motion_mask; + Plane plane; + + switch( _edit.plane ) { + case TRANSFORM_VIEW: + motion_mask=Vector3(0,0,0); + plane=Plane(_edit.center,_get_camera_normal()); + break; + case TRANSFORM_X_AXIS: + motion_mask=spatial_editor->get_gizmo_transform().basis.get_axis(0); + plane=Plane(_edit.center,motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized()); + break; + case TRANSFORM_Y_AXIS: + motion_mask=spatial_editor->get_gizmo_transform().basis.get_axis(1); + plane=Plane(_edit.center,motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized()); + break; + case TRANSFORM_Z_AXIS: + motion_mask=spatial_editor->get_gizmo_transform().basis.get_axis(2); + plane=Plane(_edit.center,motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized()); + break; + } + + Vector3 intersection; + if (!plane.intersects_ray(ray_pos,ray,&intersection)) + break; + + Vector3 click; + if (!plane.intersects_ray(_edit.click_ray_pos,_edit.click_ray,&click)) + break; + + //_validate_selection(); + Vector3 motion = intersection-click; + if (motion_mask!=Vector3()) { + motion=motion_mask.dot(motion)*motion_mask; + } + + float snap=0; + + if (_edit.snap || spatial_editor->is_snap_enabled()) { + + snap = spatial_editor->get_translate_snap(); + motion.snap(snap); + } + + //set_message("Translating: "+motion); + + List<Node*> &selection = editor_selection->get_selected_node_list(); + + for(List<Node*>::Element *E=selection.front();E;E=E->next()) { + + Spatial *sp = E->get()->cast_to<Spatial>(); + if (!sp) { + continue; + } + + SpatialEditorSelectedItem *se=editor_selection->get_node_editor_data<SpatialEditorSelectedItem>(sp); + if (!se) { + continue; + } + + Transform t=se->original; + t.origin+=motion; + sp->set_global_transform(t); + } + } break; + + case TRANSFORM_ROTATE: { + + + Plane plane; + + switch( _edit.plane ) { + case TRANSFORM_VIEW: + plane=Plane(_edit.center,_get_camera_normal()); + break; + case TRANSFORM_X_AXIS: + plane=Plane(_edit.center,spatial_editor->get_gizmo_transform().basis.get_axis(0)); + break; + case TRANSFORM_Y_AXIS: + plane=Plane(_edit.center,spatial_editor->get_gizmo_transform().basis.get_axis(1)); + break; + case TRANSFORM_Z_AXIS: + plane=Plane(_edit.center,spatial_editor->get_gizmo_transform().basis.get_axis(2)); + break; + } + + Vector3 intersection; + if (!plane.intersects_ray(ray_pos,ray,&intersection)) + break; + + Vector3 click; + if (!plane.intersects_ray(_edit.click_ray_pos,_edit.click_ray,&click)) + break; + + + Vector3 y_axis=(click-_edit.center).normalized(); + Vector3 x_axis=plane.normal.cross(y_axis).normalized(); + + float angle=Math::atan2( x_axis.dot(intersection-_edit.center), y_axis.dot(intersection-_edit.center) ); + if (_edit.snap || spatial_editor->is_snap_enabled()) { + + float snap = spatial_editor->get_rotate_snap(); + + if (snap) { + angle=Math::rad2deg(angle)+snap*0.5; //else it wont reach +180 + angle-=Math::fmod(angle,snap); + set_message("Rotating "+rtos(angle)+" degrees."); + angle=Math::deg2rad(angle); + } else + set_message("Rotating "+rtos(Math::rad2deg(angle))+" degrees."); + + } else { + set_message("Rotating "+rtos(Math::rad2deg(angle))+" degrees."); + } + + + + + Transform r; + r.basis.rotate(plane.normal,-angle); + + List<Node*> &selection = editor_selection->get_selected_node_list(); + + for(List<Node*>::Element *E=selection.front();E;E=E->next()) { + + Spatial *sp = E->get()->cast_to<Spatial>(); + if (!sp) + continue; + + SpatialEditorSelectedItem *se=editor_selection->get_node_editor_data<SpatialEditorSelectedItem>(sp); + if (!se) + continue; + + + Transform original=se->original; + + Transform base=Transform( Matrix3(), _edit.center); + Transform t=base * (r * (base.inverse() * original)); + + sp->set_global_transform(t); + } + + surface->update(); + /* + VisualServer::get_singleton()->poly_clear(indicators); + + Vector<Vector3> points; + Vector<Vector3> empty; + Vector<Color> colors; + points.push_back(intersection); + points.push_back(_edit.original.origin); + colors.push_back( Color(255,155,100) ); + colors.push_back( Color(255,155,100) ); + VisualServer::get_singleton()->poly_add_primitive(indicators,points,empty,colors,empty); + */ + } break; + default:{} + } + + } else if (m.button_mask&4) { + + + int mod = 0; + if (m.mod.shift) + mod=KEY_SHIFT; + if (m.mod.alt) + mod=KEY_ALT; + if (m.mod.control) + mod=KEY_CONTROL; + if (m.mod.meta) + mod=KEY_META; + + + + if (mod == _get_key_modifier("3d_editor/pan_modifier")) { + + + Transform camera_transform; + + camera_transform.translate(cursor.pos); + camera_transform.basis.rotate(Vector3(0,1,0),cursor.y_rot); + camera_transform.basis.rotate(Vector3(1,0,0),cursor.x_rot); + Vector3 translation(-m.relative_x/150.0,m.relative_y/150.0,0); + translation*=cursor.distance/DISTANCE_DEFAULT; + camera_transform.translate(translation); + cursor.pos=camera_transform.origin; + + } else if (mod == _get_key_modifier("3d_editor/zoom_modifier")) { + + if ( m.relative_y > 0) + cursor.distance*=1+m.relative_y/80.0; + else if (m.relative_y < 0) + cursor.distance/=1-m.relative_y/80.0; + + } else if (mod == _get_key_modifier("3d_editor/orbit_modifier")) { + cursor.x_rot+=m.relative_y/80.0; + cursor.y_rot+=m.relative_x/80.0; + if (cursor.x_rot>Math_PI/2.0) + cursor.x_rot=Math_PI/2.0; + if (cursor.x_rot<-Math_PI/2.0) + cursor.x_rot=-Math_PI/2.0; + + } + } + + } break; + + case InputEvent::KEY: { + + const InputEventKey &k = p_event.key; + switch(k.scancode) { + + case KEY_S: { + + if (_edit.mode!=TRANSFORM_NONE) { + + _edit.snap=true; + } + } break; + case KEY_KP_7: { + + cursor.y_rot=0; + if (k.mod.shift) { + cursor.x_rot=-Math_PI/2.0; + set_message("Bottom View.",2); + } else { + cursor.x_rot=Math_PI/2.0; + set_message("Top View.",2); + } + } break; + case KEY_KP_1: { + + cursor.x_rot=0; + if (k.mod.shift) { + cursor.y_rot=Math_PI; + set_message("Rear View.",2); + + } else { + cursor.y_rot=0; + set_message("Front View.",2); + } + + } break; + case KEY_KP_3: { + + cursor.x_rot=0; + if (k.mod.shift) { + cursor.y_rot=Math_PI/2.0; + set_message("Left View.",2); + } else { + cursor.y_rot=-Math_PI/2.0; + set_message("Right View.",2); + } + + } break; + case KEY_KP_5: { + + orthogonal = !orthogonal; + _menu_option(orthogonal?VIEW_PERSPECTIVE:VIEW_ORTHOGONAL); + + } break; + case KEY_K: { + + if (!get_selected_count() || _edit.mode!=TRANSFORM_NONE) + break; + + if (!editor->get_animation_editor()->has_keying()) { + set_message("Keying is disabled (no key inserted)."); + break; + } + + List<Node*> &selection = editor_selection->get_selected_node_list(); + + for(List<Node*>::Element *E=selection.front();E;E=E->next()) { + + Spatial *sp = E->get()->cast_to<Spatial>(); + if (!sp) + continue; + + emit_signal("transform_key_request",sp,"",sp->get_transform()); + } + + + set_message("Animation Key Inserted."); + + + + } break; + + } + + + } break; + + } + +} + +void SpatialEditorViewport::set_message(String p_message,float p_time) { + + message=p_message; + message_time=p_time; + +} + + + +void SpatialEditorViewport::_notification(int p_what) { + + + if (p_what==NOTIFICATION_VISIBILITY_CHANGED) { + + bool visible=is_visible(); + + set_process(visible); + } + + if (p_what==NOTIFICATION_PROCESS) { + + + //force editr camera + /* + current_camera=get_root_node()->get_current_camera(); + if (current_camera!=camera) { + + + } + */ + + if (orthogonal) { + Size2 size=get_size(); + Size2 vpsize = Point2(cursor.distance*size.get_aspect(),cursor.distance/size.get_aspect()); + //camera->set_orthogonal(size.width*cursor.distance,get_znear(),get_zfar()); + camera->set_orthogonal(2*cursor.distance,0.1,8192); + } else + camera->set_perspective(get_fov(),get_znear(),get_zfar()); + + Transform camera_transform; + camera_transform.translate( cursor.pos ); + camera_transform.basis.rotate(Vector3(0,1,0),cursor.y_rot); + camera_transform.basis.rotate(Vector3(1,0,0),cursor.x_rot); + + if (orthogonal) + camera_transform.translate(0,0,4096); + else + camera_transform.translate(0,0,cursor.distance); + + if (camera->get_global_transform()!=camera_transform) { + camera->set_global_transform( camera_transform ); + //_update_transform_gizmo_view(); + } + + Map<Node*,Object*> &selection = editor_selection->get_selection(); + + bool changed=false; + bool exist=false; + + for(Map<Node*,Object*>::Element *E=selection.front();E;E=E->next()) { + + Spatial *sp = E->key()->cast_to<Spatial>(); + if (!sp) + continue; + + SpatialEditorSelectedItem *se=editor_selection->get_node_editor_data<SpatialEditorSelectedItem>(sp); + if (!se) + continue; + + + /* + ?? + if (!se->poly_instance.is_valid()) + continue; + if (!ObjectDB::get_instance( E->key() )) { + VisualServer::get_singleton()->free( se->poly_instance ); + se->poly_instance=RID(); + continue; + } + */ + VisualInstance *vi=sp->cast_to<VisualInstance>(); + + + if (se->aabb.has_no_surface()) { + + se->aabb=vi?vi->get_aabb():AABB( Vector3(-0.2,-0.2,-0.2),Vector3(0.4,0.4,0.4)); + } + + Transform t=sp->get_global_transform(); + t.translate(se->aabb.pos); + t.basis.scale( se->aabb.size ); + + exist=true; + if (se->last_xform==t) + continue; + changed=true; + se->last_xform=t; + VisualServer::get_singleton()->instance_set_transform(se->sbox_instance,t); + + } + + if (changed || (spatial_editor->is_gizmo_visible() && !exist)) { + spatial_editor->update_transform_gizmo(); + } + + if (message_time>0) { + + if (message!=last_message) { + surface->update(); + last_message=message; + } + + message_time-=get_fixed_process_delta_time(); + if (message_time<0) + surface->update(); + } + + //grid + Vector3 grid_cam_axis=_get_camera_normal(); + /* + for(int i=0;i<3;i++) { + + + Vector3 axis; + axis[i]=1; + + bool should_be_visible= grid_enabled && (grid_enable[i] || (Math::abs(grid_cam_axis.dot(axis))>0.99 && orthogonal)); + + if (should_be_visible!=grid_visible[i]) { + + VisualServer::get_singleton()->instance_geometry_set_flag(grid_instance[i],VS::INSTANCE_FLAG_VISIBLE,should_be_visible); + grid_visible[i]=should_be_visible; + } + } + + if (last_grid_snap!=spatial_editor->get_translate_snap()) { + + + last_grid_snap=spatial_editor->get_translate_snap() + Transform gridt; + gridt.basis.scale(Vector3(last_grid_snap,last_grid_snap,last_grid_snap)); + for(int i=0;i<3;i++) + VisualServer::get_singleton()->instance_set_transform(grid_instance[i],gridt); + + }*/ + + } + + if (p_what==NOTIFICATION_ENTER_SCENE) { + + surface->connect("draw",this,"_draw"); + surface->connect("input_event",this,"_sinput"); + surface->connect("mouse_enter",this,"_smouseenter"); + preview_camera->set_icon(get_icon("Camera","EditorIcons")); + + } + + if (p_what==NOTIFICATION_MOUSE_ENTER) { + + + } + + + if (p_what==NOTIFICATION_DRAW) { + + + + } + +} + +void SpatialEditorViewport::_draw() { + + if (surface->has_focus()) { + Size2 size = surface->get_size(); + Rect2 r =Rect2(Point2(),size); + get_stylebox("EditorFocus","EditorStyles")->draw(surface->get_canvas_item(),r); + } + + + RID ci=surface->get_canvas_item(); + + if (cursor.region_select) { + + VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(cursor.region_begin,cursor.region_end-cursor.region_begin),Color(0.7,0.7,1.0,0.3)); + } + + if (message_time>0) { + Ref<Font> font = get_font("font","Label"); + Point2 msgpos=Point2(5,get_size().y-20); + font->draw(ci,msgpos+Point2(1,1),message,Color(0,0,0,0.8)); + font->draw(ci,msgpos+Point2(-1,-1),message,Color(0,0,0,0.8)); + font->draw(ci,msgpos,message,Color(1,1,1,1)); + } + + + if (_edit.mode==TRANSFORM_ROTATE) { + + Point2 center = _point_to_screen(_edit.center); + VisualServer::get_singleton()->canvas_item_add_line(ci,_edit.mouse_pos, center, Color(0.4,0.7,1.0,0.8)); + + + } + +} + + +void SpatialEditorViewport::_menu_option(int p_option) { + + switch(p_option) { + + case VIEW_TOP: { + + cursor.x_rot=Math_PI/2.0; + cursor.y_rot=0; + } break; + case VIEW_BOTTOM: { + + cursor.x_rot=-Math_PI/2.0; + cursor.y_rot=0; + + } break; + case VIEW_LEFT: { + + cursor.y_rot=Math_PI/2.0; + cursor.x_rot=0; + + } break; + case VIEW_RIGHT: { + + cursor.y_rot=-Math_PI/2.0; + cursor.x_rot=0; + + } break; + case VIEW_FRONT: { + + cursor.y_rot=0; + cursor.x_rot=0; + + } break; + case VIEW_REAR: { + + cursor.y_rot=Math_PI; + cursor.x_rot=0; + + } break; + case VIEW_CENTER_TO_SELECTION: { + + if (!get_selected_count()) + break; + + Vector3 center; + int count=0; + + List<Node*> &selection = editor_selection->get_selected_node_list(); + + for(List<Node*>::Element *E=selection.front();E;E=E->next()) { + + Spatial *sp = E->get()->cast_to<Spatial>(); + if (!sp) + continue; + + SpatialEditorSelectedItem *se=editor_selection->get_node_editor_data<SpatialEditorSelectedItem>(sp); + if (!se) + continue; + + center+=sp->get_global_transform().origin; + count++; + } + + center/=float(count); + + cursor.pos=center; + } break; + case VIEW_ENVIRONMENT: { + + int idx = view_menu->get_popup()->get_item_index(VIEW_ENVIRONMENT); + bool current = view_menu->get_popup()->is_item_checked( idx ); + current=!current; + if (current) { + + camera->set_environment(RES()); + } else { + + camera->set_environment(SpatialEditor::get_singleton()->get_viewport_environment()); + } + + view_menu->get_popup()->set_item_checked( idx, current ); + + + } break; + case VIEW_PERSPECTIVE: { + + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(VIEW_PERSPECTIVE), true ); + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(VIEW_ORTHOGONAL), false ); + orthogonal=false; + } break; + case VIEW_ORTHOGONAL: { + + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(VIEW_PERSPECTIVE), false ); + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(VIEW_ORTHOGONAL), true ); + orthogonal=true; + + } break; + + } + +} + + +void SpatialEditorViewport::_preview_exited_scene() { + + preview_camera->set_pressed(false); + _toggle_camera_preview(false); + view_menu->show(); +} + + +void SpatialEditorViewport::_toggle_camera_preview(bool p_activate) { + + + ERR_FAIL_COND(p_activate && !preview); + ERR_FAIL_COND(!p_activate && !previewing); + + if (!p_activate) { + + previewing->disconnect("exit_scene",this,"_preview_exited_scene"); + previewing=NULL; + VS::get_singleton()->viewport_attach_camera( viewport->get_viewport(), camera->get_camera() ); //restore + if (!preview) + preview_camera->hide(); + view_menu->show(); + + } else { + + previewing=preview; + previewing->connect("exit_scene",this,"_preview_exited_scene"); + VS::get_singleton()->viewport_attach_camera( viewport->get_viewport(), preview->get_camera() ); //replace + view_menu->hide(); + + } +} + +void SpatialEditorViewport::set_can_preview(Camera* p_preview) { + + preview=p_preview; + + if (!preview_camera->is_pressed()) { + + if (p_preview) { + preview_camera->show(); + } else { + preview_camera->hide(); + } + } +} + +void SpatialEditorViewport::set_state(const Dictionary& p_state) { + + cursor.pos=p_state["pos"]; + cursor.x_rot=p_state["x_rot"]; + cursor.y_rot=p_state["y_rot"]; + cursor.distance=p_state["distance"]; + bool env = p_state["use_environment"]; + bool orth = p_state["use_orthogonal"]; + if (orth) + _menu_option(VIEW_ORTHOGONAL); + else + _menu_option(VIEW_PERSPECTIVE); + if (env != camera->get_environment().is_valid()) + _menu_option(VIEW_ENVIRONMENT); + + +} + +Dictionary SpatialEditorViewport::get_state() const { + + Dictionary d; + d["pos"]=cursor.pos; + d["x_rot"]=cursor.x_rot; + d["y_rot"]=cursor.y_rot; + d["distance"]=cursor.distance; + d["use_environment"]=camera->get_environment().is_valid(); + d["use_orthogonal"]=camera->get_projection()==Camera::PROJECTION_ORTHOGONAL; + return d; +} + + +void SpatialEditorViewport::_bind_methods(){ + + ObjectTypeDB::bind_method(_MD("_draw"),&SpatialEditorViewport::_draw); + ObjectTypeDB::bind_method(_MD("_smouseenter"),&SpatialEditorViewport::_smouseenter); + ObjectTypeDB::bind_method(_MD("_sinput"),&SpatialEditorViewport::_sinput); + ObjectTypeDB::bind_method(_MD("_menu_option"),&SpatialEditorViewport::_menu_option); + ObjectTypeDB::bind_method(_MD("_toggle_camera_preview"),&SpatialEditorViewport::_toggle_camera_preview); + ObjectTypeDB::bind_method(_MD("_preview_exited_scene"),&SpatialEditorViewport::_preview_exited_scene); + +} + + + + +SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor,EditorNode *p_editor) { + + editor=p_editor; + editor_selection=editor->get_editor_selection();; + undo_redo=editor->get_undo_redo(); + clicked=0; + clicked_includes_current=false; + orthogonal=false; + message_time=0; + + spatial_editor=p_spatial_editor; + Control *c=memnew(Control); + add_child(c); + c->set_area_as_parent_rect(); + viewport = memnew( Viewport ); + c->add_child(viewport); + surface = memnew( Control ); + add_child(surface); + surface->set_area_as_parent_rect(); + camera = memnew(Camera); + camera->set_disable_gizmo(true); + //camera->set_environment(SpatialEditor::get_singleton()->get_viewport_environment()); + viewport->add_child(camera); + camera->make_current(); + surface->set_focus_mode(FOCUS_ALL); + + view_menu = memnew( MenuButton ); + surface->add_child(view_menu); + view_menu->set_pos( Point2(4,4)); + view_menu->set_text("[view]"); + view_menu->set_self_opacity(0.5); + + view_menu->get_popup()->add_item("Top",VIEW_TOP); + view_menu->get_popup()->add_item("Bottom",VIEW_BOTTOM); + view_menu->get_popup()->add_item("Left",VIEW_LEFT); + view_menu->get_popup()->add_item("Right",VIEW_RIGHT); + view_menu->get_popup()->add_item("Front",VIEW_FRONT); + view_menu->get_popup()->add_item("Rear",VIEW_REAR); + view_menu->get_popup()->add_separator(); + view_menu->get_popup()->add_check_item("Perspective",VIEW_PERSPECTIVE); + view_menu->get_popup()->add_check_item("Orthogonal",VIEW_ORTHOGONAL); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_PERSPECTIVE),true); + view_menu->get_popup()->add_separator(); + view_menu->get_popup()->add_check_item("Environment",VIEW_ENVIRONMENT); + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(VIEW_ENVIRONMENT),true); + view_menu->get_popup()->add_separator(); + view_menu->get_popup()->add_item("Selection",VIEW_CENTER_TO_SELECTION); + view_menu->get_popup()->connect("item_pressed",this,"_menu_option"); + + preview_camera = memnew( Button ); + preview_camera->set_toggle_mode(true); + preview_camera->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_END,90); + preview_camera->set_anchor_and_margin(MARGIN_TOP,ANCHOR_BEGIN,10); + preview_camera->set_text("preview"); + surface->add_child(preview_camera); + preview_camera->hide(); + preview_camera->connect("toggled",this,"_toggle_camera_preview"); + previewing=NULL; + preview=NULL; + +} + + + + + + + +SpatialEditor *SpatialEditor::singleton=NULL; + +SpatialEditorSelectedItem::~SpatialEditorSelectedItem() { + + if (sbox_instance.is_valid()) + VisualServer::get_singleton()->free(sbox_instance); +} + + +void SpatialEditor::_update_transform_gizmo_view() { + + + Transform xform = gizmo.transform; + +/* + Transform camera_xform = camera->get_transform(); + Vector3 camz = -camera_xform.get_basis().get_axis(2).normalized(); + Vector3 camy = -camera_xform.get_basis().get_axis(1).normalized(); + Plane p(camera_xform.origin,camz); + float gizmo_d = Math::abs( p.distance_to(xform.origin )); + float d0 = camera->unproject_position(camera_xform.origin+camz*gizmo_d).y; + float d1 = camera->unproject_position(camera_xform.origin+camz*gizmo_d+camy).y; + float dd = Math::abs(d0-d1); + if (dd==0) + dd=0.0001; + + gizmo.scale=(60.0/Math::abs(dd)); + Vector3 scale = Vector3(1,1,1) * gizmo.scale; + + //xform.basis.scale(scale); +*/ + xform.basis.scale(GIZMO_SCALE_DEFAULT*Vector3(1,1,1)); + + + for(int i=0;i<3;i++) { + VisualServer::get_singleton()->instance_set_transform(move_gizmo_instance[i], xform ); + VisualServer::get_singleton()->instance_geometry_set_flag(move_gizmo_instance[i],VS::INSTANCE_FLAG_VISIBLE,gizmo.visible && (tool_mode==TOOL_MODE_SELECT || tool_mode==TOOL_MODE_MOVE) ); + VisualServer::get_singleton()->instance_set_transform(rotate_gizmo_instance[i], xform ); + VisualServer::get_singleton()->instance_geometry_set_flag(rotate_gizmo_instance[i],VS::INSTANCE_FLAG_VISIBLE,gizmo.visible && (tool_mode==TOOL_MODE_SELECT || tool_mode==TOOL_MODE_ROTATE) ); + } + +} + +void SpatialEditor::select_gizmo_hilight_axis(int p_axis) { + + + for(int i=0;i<3;i++) { + + move_gizmo[i]->surface_set_material(0,i==p_axis?gizmo_hl:gizmo_color[i]); + rotate_gizmo[i]->surface_set_material(0,(i+3)==p_axis?gizmo_hl:gizmo_color[i]); + } + +} + +void SpatialEditor::update_transform_gizmo() { + + List<Node*> &selection = editor_selection->get_selected_node_list(); + AABB center; + bool first=true; + + Matrix3 gizmo_basis; + bool local_gizmo_coords = transform_menu->get_popup()->is_item_checked( transform_menu->get_popup()->get_item_index(MENU_TRANSFORM_LOCAL_COORDS) ); + + + for(List<Node*>::Element *E=selection.front();E;E=E->next()) { + + Spatial *sp = E->get()->cast_to<Spatial>(); + if (!sp) + continue; + + SpatialEditorSelectedItem *se=editor_selection->get_node_editor_data<SpatialEditorSelectedItem>(sp); + if (!se) + continue; + + Transform xf = se->sp->get_global_transform(); + if (first) { + center.pos=xf.origin; + first=false; + if (local_gizmo_coords) { + gizmo_basis=xf.basis; + gizmo_basis.orthonormalize(); + } + } else { + center.expand_to(xf.origin); + gizmo_basis=Matrix3(); + } +// count++; + } + + Vector3 pcenter = center.pos+center.size*0.5; + gizmo.visible=!first; + gizmo.transform.origin=pcenter; + gizmo.transform.basis=gizmo_basis; + + _update_transform_gizmo_view(); + +} + + +Object *SpatialEditor::_get_editor_data(Object *p_what) { + + Spatial *sp = p_what->cast_to<Spatial>(); + if (!sp) + return NULL; + + + SpatialEditorSelectedItem *si = memnew( SpatialEditorSelectedItem ); + + si->sp=sp; + si->sbox_instance=VisualServer::get_singleton()->instance_create2(selection_box->get_rid(),sp->get_world()->get_scenario()); + VS::get_singleton()->instance_geometry_set_flag(si->sbox_instance,VS::INSTANCE_FLAG_CAST_SHADOW,false); + + RID inst = sp->call("_get_visual_instance_rid"); + +// if (inst.is_valid()) +// si->aabb = VisualServer::get_singleton()->instance_get_base_aabb(inst); + + + if (get_scene()->is_editor_hint()) + editor->call("edit_node",sp); + + + return si; +} + +void SpatialEditor::_generate_selection_box() { + + AABB aabb( Vector3(), Vector3(1,1,1) ); + aabb.grow_by( aabb.get_longest_axis_size()/20.0 ); + + Ref<SurfaceTool> st = memnew( SurfaceTool ); + + st->begin(Mesh::PRIMITIVE_LINES); + for (int i=0;i<12;i++) { + + Vector3 a,b; + aabb.get_edge(i,a,b); + + /*Vector<Vector3> points; + Vector<Color> colors; + points.push_back(a); + points.push_back(b);*/ + + st->add_color( Color(1.0,1.0,0.8,0.8) ); + st->add_vertex(a); + st->add_color( Color(1.0,1.0,0.8,0.4) ); + st->add_vertex(a.linear_interpolate(b,0.2)); + + st->add_color( Color(1.0,1.0,0.8,0.4) ); + st->add_vertex(a.linear_interpolate(b,0.8)); + st->add_color( Color(1.0,1.0,0.8,0.8) ); + st->add_vertex(b); + + } + + Ref<FixedMaterial> mat = memnew( FixedMaterial ); + mat->set_flag(Material::FLAG_UNSHADED,true); + mat->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1)); + mat->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA,true); + mat->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY,true); + st->set_material(mat); + selection_box = st->commit(); +} + + +Dictionary SpatialEditor::get_state() const { + + + Dictionary d; + + + int vc=0; + if (view_menu->get_popup()->is_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT) )) + vc=1; + else if (view_menu->get_popup()->is_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS) )) + vc=2; + else if (view_menu->get_popup()->is_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS) )) + vc=3; + else if (view_menu->get_popup()->is_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS) )) + vc=4; + + d["viewport_mode"]=vc; + Array vpdata; + for(int i=0;i<4;i++) { + vpdata.push_back( viewports[i]->get_state() ); + } + + d["viewports"]=vpdata; + + d["default_light"]=view_menu->get_popup()->is_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_DEFAULT_LIGHT) );; + d["show_grid"]=view_menu->get_popup()->is_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_GRID) );; + d["show_origin"]=view_menu->get_popup()->is_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_ORIGIN) );; + d["fov"]=get_fov(); + d["znear"]=get_znear(); + d["zfar"]=get_zfar(); + + return d; +} +void SpatialEditor::set_state(const Dictionary& p_state) { + + Dictionary d = p_state; + + ERR_FAIL_COND(!d.has("viewport_mode")); + ERR_FAIL_COND(!d.has("viewports")); + ERR_FAIL_COND(!d.has("default_light")); + ERR_FAIL_COND(!d.has("show_grid")); + ERR_FAIL_COND(!d.has("show_origin")); + ERR_FAIL_COND(!d.has("fov")); + ERR_FAIL_COND(!d.has("znear")); + ERR_FAIL_COND(!d.has("zfar")); + + int vc = d["viewport_mode"]; + + if (vc==1) + _menu_item_pressed(MENU_VIEW_USE_1_VIEWPORT); + else if (vc==2) + _menu_item_pressed(MENU_VIEW_USE_2_VIEWPORTS); + else if (vc==3) + _menu_item_pressed(MENU_VIEW_USE_3_VIEWPORTS); + else if (vc==4) + _menu_item_pressed(MENU_VIEW_USE_4_VIEWPORTS); + + Array vp = d["viewports"]; + ERR_FAIL_COND(vp.size()>4); + + for(int i=0;i<4;i++) { + viewports[i]->set_state(vp[i]); + } + + + if (d.has("zfar")) + settings_zfar->set_text(d["zfar"]); + if (d.has("znear")) + settings_znear->set_text(d["znear"]); + if (d.has("fov")) + settings_fov->set_text(d["fov"]); + if (d.has("default_light")) { + bool use = d["default_light"]; + + bool existing = light_instance.is_valid(); + if (use!=existing) { + if (existing) { + VisualServer::get_singleton()->free(light_instance); + light_instance=RID(); + } else { + light_instance=VisualServer::get_singleton()->instance_create2(light,get_scene()->get_root()->get_world()->get_scenario()); + VisualServer::get_singleton()->instance_set_transform(light_instance,light_transform); + + } + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_DEFAULT_LIGHT), light_instance.is_valid() ); + } + + } + + if (d.has("show_grid")) { + bool use = d["show_grid"]; + + if (use!=view_menu->get_popup()->is_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_GRID))) { + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_GRID), use ); + grid_enabled=use; + } + } + + if (d.has("show_origin")) { + bool use = d["show_origin"]; + + if (use!=view_menu->get_popup()->is_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_ORIGIN))) { + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_ORIGIN), use ); + VisualServer::get_singleton()->instance_geometry_set_flag(origin_instance,VS::INSTANCE_FLAG_VISIBLE,use); + } + } + + + +} + + +void SpatialEditor::edit(Spatial *p_spatial) { + + + if (p_spatial) { + //_validate_selection(); + //if (selected.has(p_spatial->get_instance_ID()) && selected.size()==1) + // return; + //_select(p_spatial->get_instance_ID(),false,true); + + // should become the selection + } + + +} + +void SpatialEditor::_xform_dialog_action() { + + Transform t; + //translation + Vector3 scale; + Vector3 rotate; + Vector3 translate; + + for(int i=0;i<3;i++) { + translate[i]=xform_translate[i]->get_text().to_double(); + rotate[i]=Math::deg2rad(xform_rotate[i]->get_text().to_double()); + scale[i]=xform_scale[i]->get_text().to_double(); + } + + t.origin=translate; + for(int i=0;i<3;i++) { + if (!rotate[i]) + continue; + Vector3 axis; + axis[i]=1.0; + t.basis.rotate(axis,rotate[i]); + } + + for(int i=0;i<3;i++) { + if (scale[i]==1) + continue; + t.basis.set_axis(i,t.basis.get_axis(i)*scale[i]); + } + + + undo_redo->create_action("XForm Dialog"); + + List<Node*> &selection = editor_selection->get_selected_node_list(); + + for(List<Node*>::Element *E=selection.front();E;E=E->next()) { + + Spatial *sp = E->get()->cast_to<Spatial>(); + if (!sp) + continue; + + SpatialEditorSelectedItem *se=editor_selection->get_node_editor_data<SpatialEditorSelectedItem>(sp); + if (!se) + continue; + + bool post = xform_type->get_selected()>0; + + Transform tr = sp->get_global_transform(); + if (post) + tr = tr * t; + else { + + tr.basis = t.basis * tr.basis; + tr.origin+=t.origin; + } + + undo_redo->add_do_method(sp,"set_global_transform",tr); + undo_redo->add_undo_method(sp,"set_global_transform",sp->get_global_transform()); + } + undo_redo->commit_action(); +} + +void SpatialEditor::_menu_item_pressed(int p_option) { + + switch(p_option) { + + case MENU_TOOL_SELECT: + case MENU_TOOL_MOVE: + case MENU_TOOL_ROTATE: + case MENU_TOOL_SCALE: { + + for(int i=0;i<4;i++) + tool_button[i]->set_pressed(i==p_option); + tool_mode=(ToolMode)p_option; + + static const char *_mode[]={"Selection Mode.","Translation Mode.","Rotation Mode.","Scale Mode."}; +// set_message(_mode[p_option],3); + update_transform_gizmo(); + + } break; + case MENU_TRANSFORM_USE_SNAP: { + + bool is_checked = transform_menu->get_popup()->is_item_checked( transform_menu->get_popup()->get_item_index(p_option) ); + snap_enabled=!is_checked; + transform_menu->get_popup()->set_item_checked( transform_menu->get_popup()->get_item_index(p_option), snap_enabled ); + } break; + case MENU_TRANSFORM_CONFIGURE_SNAP: { + + snap_dialog->popup_centered(Size2(200,160)); + } break; + case MENU_TRANSFORM_LOCAL_COORDS: { + + bool is_checked = transform_menu->get_popup()->is_item_checked( transform_menu->get_popup()->get_item_index(p_option) ); + transform_menu->get_popup()->set_item_checked( transform_menu->get_popup()->get_item_index(p_option), !is_checked ); + update_transform_gizmo(); + + } break; + case MENU_TRANSFORM_DIALOG: { + + for(int i=0;i<3;i++) { + + + xform_translate[i]->set_text("0"); + xform_rotate[i]->set_text("0"); + xform_scale[i]->set_text("1"); + + } + + xform_dialog->popup_centered(Size2(200,200)); + + } break; + case MENU_VIEW_USE_DEFAULT_LIGHT: { + + bool is_checked = view_menu->get_popup()->is_item_checked( view_menu->get_popup()->get_item_index(p_option) ); + + if (is_checked) { + VisualServer::get_singleton()->free(light_instance); + light_instance=RID(); + } else { + light_instance=VisualServer::get_singleton()->instance_create2(light,get_scene()->get_root()->get_world()->get_scenario()); + VisualServer::get_singleton()->instance_set_transform(light_instance,light_transform); + } + + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(p_option), light_instance.is_valid() ); + + } break; + case MENU_VIEW_USE_1_VIEWPORT: { + + for(int i=1;i<4;i++) { + + viewports[i]->hide(); + } + + viewports[0]->set_area_as_parent_rect(); + + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), true ); + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false ); + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), false ); + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), false ); + + } break; + case MENU_VIEW_USE_2_VIEWPORTS: { + + for(int i=1;i<4;i++) { + + if (i==1 || i==3) + viewports[i]->hide(); + else + viewports[i]->show(); + + + } + viewports[0]->set_area_as_parent_rect(); + viewports[0]->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_RATIO,0.5); + viewports[2]->set_area_as_parent_rect(); + viewports[2]->set_anchor_and_margin(MARGIN_TOP,ANCHOR_RATIO,0.5); + + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false ); + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), true ); + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), false ); + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), false ); + + } break; + case MENU_VIEW_USE_3_VIEWPORTS: { + + for(int i=1;i<4;i++) { + + if (i==1) + viewports[i]->hide(); + else + viewports[i]->show(); + } + viewports[0]->set_area_as_parent_rect(); + viewports[0]->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_RATIO,0.5); + viewports[2]->set_area_as_parent_rect(); + viewports[2]->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_RATIO,0.5); + viewports[2]->set_anchor_and_margin(MARGIN_TOP,ANCHOR_RATIO,0.5); + viewports[3]->set_area_as_parent_rect(); + viewports[3]->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_RATIO,0.5); + viewports[3]->set_anchor_and_margin(MARGIN_TOP,ANCHOR_RATIO,0.5); + + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false ); + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false ); + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), true ); + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), false ); + + } break; + case MENU_VIEW_USE_4_VIEWPORTS: { + + for(int i=1;i<4;i++) { + + viewports[i]->show(); + } + viewports[0]->set_area_as_parent_rect(); + viewports[0]->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_RATIO,0.5); + viewports[0]->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_RATIO,0.5); + viewports[1]->set_area_as_parent_rect(); + viewports[1]->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_RATIO,0.5); + viewports[1]->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_RATIO,0.5); + viewports[2]->set_area_as_parent_rect(); + viewports[2]->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_RATIO,0.5); + viewports[2]->set_anchor_and_margin(MARGIN_TOP,ANCHOR_RATIO,0.5); + viewports[3]->set_area_as_parent_rect(); + viewports[3]->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_RATIO,0.5); + viewports[3]->set_anchor_and_margin(MARGIN_TOP,ANCHOR_RATIO,0.5); + + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false ); + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false ); + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), false ); + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), true ); + + } break; + case MENU_VIEW_DISPLAY_NORMAL: { + + + VisualServer::get_singleton()->scenario_set_debug( get_scene()->get_root()->get_world()->get_scenario(), VisualServer::SCENARIO_DEBUG_DISABLED ); + + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_NORMAL), true ); + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_WIREFRAME), false ); + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_OVERDRAW), false ); + + } break; + case MENU_VIEW_DISPLAY_WIREFRAME: { + + VisualServer::get_singleton()->scenario_set_debug( get_scene()->get_root()->get_world()->get_scenario(), VisualServer::SCENARIO_DEBUG_WIREFRAME ); + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_NORMAL), false ); + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_WIREFRAME), true ); + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_OVERDRAW), false ); + + } break; + case MENU_VIEW_DISPLAY_OVERDRAW: { + + VisualServer::get_singleton()->scenario_set_debug( get_scene()->get_root()->get_world()->get_scenario(), VisualServer::SCENARIO_DEBUG_OVERDRAW ); + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_NORMAL), false ); + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_WIREFRAME), false ); + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_OVERDRAW), true ); + + } break; + case MENU_VIEW_ORIGIN: { + + bool is_checked = view_menu->get_popup()->is_item_checked( view_menu->get_popup()->get_item_index(p_option) ); + + is_checked=!is_checked; + VisualServer::get_singleton()->instance_geometry_set_flag(origin_instance,VS::INSTANCE_FLAG_VISIBLE,is_checked); + + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(p_option), is_checked); + } break; + case MENU_VIEW_GRID: { + + bool is_checked = view_menu->get_popup()->is_item_checked( view_menu->get_popup()->get_item_index(p_option) ); + + grid_enabled=!is_checked; + + view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(p_option), grid_enabled ); + + + } break; + case MENU_VIEW_CAMERA_SETTINGS: { + + settings_dialog->popup_centered(Size2(200,160)); + } break; + + } +} + + +void SpatialEditor::_init_indicators() { + + //make sure that the camera indicator is not selectable + light=VisualServer::get_singleton()->light_create( VisualServer::LIGHT_DIRECTIONAL ); + //VisualServer::get_singleton()->light_set_shadow( light, true ); + light_instance=VisualServer::get_singleton()->instance_create2(light,get_scene()->get_root()->get_world()->get_scenario()); + + + light_transform.rotate(Vector3(1,0,0),Math_PI/5.0); + VisualServer::get_singleton()->instance_set_transform(light_instance,light_transform); + + + RID mat = VisualServer::get_singleton()->fixed_material_create(); + VisualServer::get_singleton()->fixed_material_set_flag(mat, VisualServer::FIXED_MATERIAL_FLAG_USE_ALPHA,true); + VisualServer::get_singleton()->fixed_material_set_flag(mat, VisualServer::FIXED_MATERIAL_FLAG_USE_COLOR_ARRAY,true); + + + { + + RID indicator_mat = VisualServer::get_singleton()->fixed_material_create(); + VisualServer::get_singleton()->material_set_flag( indicator_mat, VisualServer::MATERIAL_FLAG_UNSHADED, true ); + VisualServer::get_singleton()->material_set_flag( indicator_mat, VisualServer::MATERIAL_FLAG_ONTOP, false ); + VisualServer::get_singleton()->fixed_material_set_flag(indicator_mat, VisualServer::FIXED_MATERIAL_FLAG_USE_ALPHA,true); + VisualServer::get_singleton()->fixed_material_set_flag(indicator_mat, VisualServer::FIXED_MATERIAL_FLAG_USE_COLOR_ARRAY,true); + + DVector<Color> grid_colors[3]; + DVector<Vector3> grid_points[3]; + Vector<Color> origin_colors; + Vector<Vector3> origin_points; + + for(int i=0;i<3;i++) { + Vector3 axis; + axis[i]=1; + Vector3 axis_n1; + axis_n1[(i+1)%3]=1; + Vector3 axis_n2; + axis_n2[(i+2)%3]=1; + + origin_colors.push_back(Color(axis.x,axis.y,axis.z)); + origin_colors.push_back(Color(axis.x,axis.y,axis.z)); + origin_points.push_back(axis*4096); + origin_points.push_back(axis*-4096); +#define ORIGIN_GRID_SIZE 25 + + for(int j=-ORIGIN_GRID_SIZE;j<=ORIGIN_GRID_SIZE;j++) { + + + grid_colors[i].push_back(Color(axis.x,axis.y,axis.z,0.2)); + grid_colors[i].push_back(Color(axis.x,axis.y,axis.z,0.2)); + grid_colors[i].push_back(Color(axis.x,axis.y,axis.z,0.2)); + grid_colors[i].push_back(Color(axis.x,axis.y,axis.z,0.2)); + grid_points[i].push_back(axis_n1*ORIGIN_GRID_SIZE+axis_n2*j); + grid_points[i].push_back(-axis_n1*ORIGIN_GRID_SIZE+axis_n2*j); + grid_points[i].push_back(axis_n2*ORIGIN_GRID_SIZE+axis_n1*j); + grid_points[i].push_back(-axis_n2*ORIGIN_GRID_SIZE+axis_n1*j); + + } + + grid[i] = VisualServer::get_singleton()->mesh_create(); + Array d; + d.resize(VS::ARRAY_MAX); + d[VisualServer::ARRAY_VERTEX]=grid_points[i]; + d[VisualServer::ARRAY_COLOR]=grid_colors[i]; + VisualServer::get_singleton()->mesh_add_surface(grid[i],VisualServer::PRIMITIVE_LINES,d); + VisualServer::get_singleton()->mesh_surface_set_material(grid[i],0,indicator_mat); + grid_instance[i] = VisualServer::get_singleton()->instance_create2(grid[i],get_scene()->get_root()->get_world()->get_scenario()); + grid_visible[i]=false; + grid_enable[i]=false; + VisualServer::get_singleton()->instance_geometry_set_flag(grid_instance[i],VS::INSTANCE_FLAG_VISIBLE,false); + VisualServer::get_singleton()->instance_geometry_set_flag(grid_instance[i],VS::INSTANCE_FLAG_CAST_SHADOW,false); + + + } + + origin = VisualServer::get_singleton()->mesh_create(); + Array d; + d.resize(VS::ARRAY_MAX); + d[VisualServer::ARRAY_VERTEX]=origin_points; + d[VisualServer::ARRAY_COLOR]=origin_colors; + + VisualServer::get_singleton()->mesh_add_surface(origin,VisualServer::PRIMITIVE_LINES,d); + VisualServer::get_singleton()->mesh_surface_set_material(origin,0,indicator_mat,true); + + +// origin = VisualServer::get_singleton()->poly_create(); +// VisualServer::get_singleton()->poly_add_primitive(origin,origin_points,Vector<Vector3>(),origin_colors,Vector<Vector3>()); +// VisualServer::get_singleton()->poly_set_material(origin,indicator_mat,true); + origin_instance = VisualServer::get_singleton()->instance_create2(origin,get_scene()->get_root()->get_world()->get_scenario()); + VisualServer::get_singleton()->instance_geometry_set_flag(origin_instance,VS::INSTANCE_FLAG_CAST_SHADOW,false); + + + + VisualServer::get_singleton()->instance_geometry_set_flag(grid_instance[1],VS::INSTANCE_FLAG_VISIBLE,true); + grid_enable[1]=true; + grid_visible[1]=true; + grid_enabled=true; + last_grid_snap=1; + + } + + { + cursor_mesh = VisualServer::get_singleton()->mesh_create(); + DVector<Vector3> cursor_points; + float cs = 0.25; + cursor_points.push_back(Vector3(+cs,0,0)); + cursor_points.push_back(Vector3(-cs,0,0)); + cursor_points.push_back(Vector3(0,+cs,0)); + cursor_points.push_back(Vector3(0,-cs,0)); + cursor_points.push_back(Vector3(0,0,+cs)); + cursor_points.push_back(Vector3(0,0,-cs)); + RID cmat=VisualServer::get_singleton()->fixed_material_create(); + VisualServer::get_singleton()->fixed_material_set_param(cmat,VS::FIXED_MATERIAL_PARAM_DIFFUSE,Color(0,1,1)); + VisualServer::get_singleton()->material_set_flag( cmat, VisualServer::MATERIAL_FLAG_UNSHADED, true ); + VisualServer::get_singleton()->fixed_material_set_flag(cmat, VisualServer::FIXED_MATERIAL_FLAG_USE_ALPHA,true); + VisualServer::get_singleton()->fixed_material_set_flag(cmat, VisualServer::FIXED_MATERIAL_FLAG_USE_COLOR_ARRAY,true); + + Array d; + d.resize(VS::ARRAY_MAX); + d[VS::ARRAY_VERTEX]=cursor_points; + VisualServer::get_singleton()->mesh_add_surface(cursor_mesh,VS::PRIMITIVE_LINES,d); + VisualServer::get_singleton()->mesh_surface_set_material(cursor_mesh,0,cmat,true); + + cursor_instance = VisualServer::get_singleton()->instance_create2(cursor_mesh,get_scene()->get_root()->get_world()->get_scenario()); + VisualServer::get_singleton()->instance_geometry_set_flag(cursor_instance,VS::INSTANCE_FLAG_CAST_SHADOW,false); + + + } + + + { + + //move gizmo + + + gizmo_hl = Ref<FixedMaterial>( memnew( FixedMaterial ) ); + gizmo_hl->set_flag(Material::FLAG_UNSHADED, true); + gizmo_hl->set_flag(Material::FLAG_ONTOP, true); + gizmo_hl->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true); + gizmo_hl->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1,0.4)); + + for(int i=0;i<3;i++) { + + move_gizmo[i]=Ref<Mesh>( memnew( Mesh ) ); + move_gizmo_instance[i]=VS::get_singleton()->instance_create(); + VS::get_singleton()->instance_set_base(move_gizmo_instance[i],move_gizmo[i]->get_rid()); + VS::get_singleton()->instance_set_scenario(move_gizmo_instance[i],get_scene()->get_root()->get_world()->get_scenario()); + VS::get_singleton()->instance_geometry_set_flag(move_gizmo_instance[i],VS::INSTANCE_FLAG_VISIBLE,false); + VS::get_singleton()->instance_geometry_set_flag(move_gizmo_instance[i],VS::INSTANCE_FLAG_DEPH_SCALE,true); + VS::get_singleton()->instance_geometry_set_flag(move_gizmo_instance[i],VS::INSTANCE_FLAG_CAST_SHADOW,false); + + + + rotate_gizmo[i]=Ref<Mesh>( memnew( Mesh ) ); + rotate_gizmo_instance[i]=VS::get_singleton()->instance_create(); + VS::get_singleton()->instance_set_base(rotate_gizmo_instance[i],rotate_gizmo[i]->get_rid()); + VS::get_singleton()->instance_set_scenario(rotate_gizmo_instance[i],get_scene()->get_root()->get_world()->get_scenario()); + VS::get_singleton()->instance_geometry_set_flag(rotate_gizmo_instance[i],VS::INSTANCE_FLAG_VISIBLE,false); + VS::get_singleton()->instance_geometry_set_flag(rotate_gizmo_instance[i],VS::INSTANCE_FLAG_DEPH_SCALE,true); + VS::get_singleton()->instance_geometry_set_flag(rotate_gizmo_instance[i],VS::INSTANCE_FLAG_CAST_SHADOW,false); + + + Ref<FixedMaterial> mat = memnew( FixedMaterial ); + mat->set_flag(Material::FLAG_UNSHADED, true); + mat->set_flag(Material::FLAG_ONTOP, true); + mat->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true); + Color col; + col[i]=1.0; + col.a=0.2; + mat->set_parameter(FixedMaterial::PARAM_DIFFUSE,col); + gizmo_color[i]=mat; + + + + + Vector3 ivec; + ivec[i]=1; + Vector3 nivec; + nivec[(i+1)%3]=1; + nivec[(i+2)%3]=1; + Vector3 ivec2; + ivec2[(i+1)%3]=1; + Vector3 ivec3; + ivec3[(i+2)%3]=1; + + + { + + Ref<SurfaceTool> surftool = memnew( SurfaceTool ); + surftool->begin(Mesh::PRIMITIVE_TRIANGLES); + + //translate + + const int arrow_points=5; + Vector3 arrow[5]={ + nivec*0.0+ivec*0.0, + nivec*0.01+ivec*0.0, + nivec*0.01+ivec*1.0, + nivec*0.1+ivec*1.0, + nivec*0.0+ivec*(1+GIZMO_ARROW_SIZE), + }; + + int arrow_sides=6; + + + for(int i = 0; i < 7 ; i++) { + + + Matrix3 ma(ivec,Math_PI*2*float(i)/arrow_sides); + Matrix3 mb(ivec,Math_PI*2*float(i+1)/arrow_sides); + + + for(int j=0;j<arrow_points-1;j++) { + + Vector3 points[4]={ + ma.xform(arrow[j]), + mb.xform(arrow[j]), + mb.xform(arrow[j+1]), + ma.xform(arrow[j+1]), + }; + surftool->add_vertex(points[0]); + surftool->add_vertex(points[1]); + surftool->add_vertex(points[2]); + + surftool->add_vertex(points[0]); + surftool->add_vertex(points[2]); + surftool->add_vertex(points[3]); + } + + } + + surftool->set_material(mat); + surftool->commit(move_gizmo[i]); + } + + { + + + Ref<SurfaceTool> surftool = memnew( SurfaceTool ); + surftool->begin(Mesh::PRIMITIVE_TRIANGLES); + + Vector3 circle[5]={ + ivec*0.02+ivec2*0.02+ivec2*1.0, + ivec*-0.02+ivec2*0.02+ivec2*1.0, + ivec*-0.02+ivec2*-0.02+ivec2*1.0, + ivec*0.02+ivec2*-0.02+ivec2*1.0, + ivec*0.02+ivec2*0.02+ivec2*1.0, + }; + + + for(int k = 0; k < 33 ; k++) { + + + Matrix3 ma(ivec,Math_PI*2*float(k)/32); + Matrix3 mb(ivec,Math_PI*2*float(k+1)/32); + + + for(int j=0;j<4;j++) { + + Vector3 points[4]={ + ma.xform(circle[j]), + mb.xform(circle[j]), + mb.xform(circle[j+1]), + ma.xform(circle[j+1]), + }; + surftool->add_vertex(points[0]); + surftool->add_vertex(points[1]); + surftool->add_vertex(points[2]); + + surftool->add_vertex(points[0]); + surftool->add_vertex(points[2]); + surftool->add_vertex(points[3]); + } + + } + + surftool->set_material(mat); + surftool->commit(rotate_gizmo[i]); + + } + + + } + } + + + _generate_selection_box(); + + + //get_scene()->get_root_node()->cast_to<EditorNode>()->get_scene_root()->add_child(camera); + + //current_camera=camera; + +} + +void SpatialEditor::_finish_indicators() { + + + VisualServer::get_singleton()->free(origin_instance); + VisualServer::get_singleton()->free(origin); + for(int i=0;i<3;i++) { + VisualServer::get_singleton()->free(grid_instance[i]); + VisualServer::get_singleton()->free(grid[i]); + } + VisualServer::get_singleton()->free(light_instance); + VisualServer::get_singleton()->free(light); + //VisualServer::get_singleton()->free(poly); + //VisualServer::get_singleton()->free(indicators_instance); + //VisualServer::get_singleton()->free(indicators); + + VisualServer::get_singleton()->free(cursor_instance); + VisualServer::get_singleton()->free(cursor_mesh); +} + +void SpatialEditor::_instance_scene() { +#if 0 + EditorNode *en = get_scene()->get_root_node()->cast_to<EditorNode>(); + ERR_FAIL_COND(!en); + String path = en->get_scenes_dock()->get_selected_path(); + if (path=="") { + set_message("No scene selected to instance!"); + return; + } + + undo_redo->create_action("Instance at Cursor"); + + Node* scene = en->request_instance_scene(path); + + if (!scene) { + set_message("Could not instance scene!"); + undo_redo->commit_action(); //bleh + return; + } + + Spatial *s = scene->cast_to<Spatial>(); + if (s) { + + undo_redo->add_do_method(s,"set_global_transform",Transform(Matrix3(),cursor.cursor_pos)); + } + + undo_redo->commit_action(); +#endif +} +/* +void SpatialEditor::_update_selection() { + + + +} +*/ +void SpatialEditor::_unhandled_key_input(InputEvent p_event) { + + if (!is_visible()) + return; + + { + + EditorNode *en = editor; + EditorPlugin *over_plugin = en->get_editor_plugin_over(); + + if (over_plugin && over_plugin->forward_input_event(p_event)) { + + return; //ate the over input event + } + + } + + switch(p_event.type) { + + case InputEvent::KEY: { + + + const InputEventKey &k=p_event.key; + + if (!k.pressed) + break; + + switch(k.scancode) { + + case KEY_Q: _menu_item_pressed(MENU_TOOL_SELECT); break; + case KEY_W: _menu_item_pressed(MENU_TOOL_MOVE); break; + case KEY_E: _menu_item_pressed(MENU_TOOL_ROTATE); break; + case KEY_R: _menu_item_pressed(MENU_TOOL_SCALE); break; + +#if 0 +#endif + } + + } break; + } +} +void SpatialEditor::_notification(int p_what) { + + if (p_what==NOTIFICATION_READY) { + + tool_button[SpatialEditor::TOOL_MODE_SELECT]->set_icon( get_icon("ToolSelect","EditorIcons") ); + tool_button[SpatialEditor::TOOL_MODE_MOVE]->set_icon( get_icon("ToolMove","EditorIcons") ); + tool_button[SpatialEditor::TOOL_MODE_ROTATE]->set_icon( get_icon("ToolRotate","EditorIcons") ); + tool_button[SpatialEditor::TOOL_MODE_SCALE]->set_icon( get_icon("ToolScale","EditorIcons") ); + instance_button->set_icon( get_icon("SpatialAdd","EditorIcons") ); + + + view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT),get_icon("Panels1","EditorIcons")); + view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS),get_icon("Panels2","EditorIcons")); + view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS),get_icon("Panels3","EditorIcons")); + view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS),get_icon("Panels4","EditorIcons")); + + _menu_item_pressed(MENU_VIEW_USE_1_VIEWPORT); + + get_scene()->connect("node_removed",this,"_node_removed"); + } + + if (p_what==NOTIFICATION_ENTER_SCENE) { + + gizmos = memnew( SpatialEditorGizmos ); + _init_indicators(); + + } + if (p_what==NOTIFICATION_EXIT_SCENE) { + + _finish_indicators(); + memdelete( gizmos ); + + } +} + +void SpatialEditor::add_control_to_menu_panel(Control *p_control) { + + + hbc_menu->add_child(p_control); +} + +void SpatialEditor::set_can_preview(Camera* p_preview) { + + for(int i=0;i<4;i++) { + viewports[i]->set_can_preview(p_preview); + } +} + +VSplitContainer *SpatialEditor::get_shader_split() { + + return shader_split; +} + +HSplitContainer *SpatialEditor::get_palette_split() { + + return palette_split; +} + + +void SpatialEditor::_request_gizmo(Object* p_obj) { + + Spatial *sp=p_obj->cast_to<Spatial>(); + if (!sp) + return; + if (editor->get_edited_scene() && (sp==editor->get_edited_scene() || sp->get_owner()==editor->get_edited_scene())) { + + Ref<SpatialEditorGizmo> seg = gizmos->get_gizmo(sp); + + if (seg.is_valid()) + sp->set_gizmo(seg); + + for (List<EditorPlugin*>::Element *E=gizmo_plugins.front();E;E=E->next()) { + + if (E->get()->create_spatial_gizmo(sp)) + return; + } + } + +} + +void SpatialEditor::_bind_methods() { + +// ObjectTypeDB::bind_method("_input_event",&SpatialEditor::_input_event); + ObjectTypeDB::bind_method("_unhandled_key_input",&SpatialEditor::_unhandled_key_input); + //ObjectTypeDB::bind_method("_node_removed",&SpatialEditor::_node_removed); + ObjectTypeDB::bind_method("_menu_item_pressed",&SpatialEditor::_menu_item_pressed); + ObjectTypeDB::bind_method("_xform_dialog_action",&SpatialEditor::_xform_dialog_action); + ObjectTypeDB::bind_method("_instance_scene",&SpatialEditor::_instance_scene); +// ObjectTypeDB::bind_method("_update_selection",&SpatialEditor::_update_selection); + ObjectTypeDB::bind_method("_get_editor_data",&SpatialEditor::_get_editor_data); + ObjectTypeDB::bind_method("_request_gizmo",&SpatialEditor::_request_gizmo); + + ADD_SIGNAL( MethodInfo("transform_key_request") ); + +} + +SpatialEditor::SpatialEditor(EditorNode *p_editor) { + + + viewport_environment = Ref<Environment>( memnew( Environment ) ); + undo_redo=p_editor->get_undo_redo(); + VBoxContainer *vbc = this; + + custom_camera=NULL; + singleton=this; + editor=p_editor; + editor_selection=editor->get_editor_selection(); + editor_selection->add_editor_plugin(this); + editor_selection->connect("selection_changed",this,"_update_selection"); + + snap_enabled=false; + tool_mode = TOOL_MODE_SELECT; + + //set_focus_mode(FOCUS_ALL); + + hbc_menu = memnew( HBoxContainer ); + vbc->add_child(hbc_menu); + + + Vector<Variant> button_binds; + button_binds.resize(1); + + tool_button[TOOL_MODE_SELECT] = memnew( ToolButton ); + hbc_menu->add_child( tool_button[TOOL_MODE_SELECT] ); + tool_button[TOOL_MODE_SELECT]->set_toggle_mode(true); + tool_button[TOOL_MODE_SELECT]->set_flat(true); + tool_button[TOOL_MODE_SELECT]->set_pressed(true); + button_binds[0]=MENU_TOOL_SELECT; + tool_button[TOOL_MODE_SELECT]->connect("pressed", this,"_menu_item_pressed",button_binds); + tool_button[TOOL_MODE_SELECT]->set_tooltip("Select Mode (Q)"); + + + tool_button[TOOL_MODE_MOVE] = memnew( ToolButton ); + + hbc_menu->add_child( tool_button[TOOL_MODE_MOVE] ); + tool_button[TOOL_MODE_MOVE]->set_toggle_mode(true); + tool_button[TOOL_MODE_MOVE]->set_flat(true); + button_binds[0]=MENU_TOOL_MOVE; + tool_button[TOOL_MODE_MOVE]->connect("pressed", this,"_menu_item_pressed",button_binds); + tool_button[TOOL_MODE_MOVE]->set_tooltip("Move Mode (W)"); + + tool_button[TOOL_MODE_ROTATE] = memnew( ToolButton ); + hbc_menu->add_child( tool_button[TOOL_MODE_ROTATE] ); + tool_button[TOOL_MODE_ROTATE]->set_toggle_mode(true); + tool_button[TOOL_MODE_ROTATE]->set_flat(true); + button_binds[0]=MENU_TOOL_ROTATE; + tool_button[TOOL_MODE_ROTATE]->connect("pressed", this,"_menu_item_pressed",button_binds); + tool_button[TOOL_MODE_ROTATE]->set_tooltip("Rotate Mode (E)"); + + tool_button[TOOL_MODE_SCALE] = memnew( ToolButton ); + hbc_menu->add_child( tool_button[TOOL_MODE_SCALE] ); + tool_button[TOOL_MODE_SCALE]->set_toggle_mode(true); + tool_button[TOOL_MODE_SCALE]->set_flat(true); + button_binds[0]=MENU_TOOL_SCALE; + tool_button[TOOL_MODE_SCALE]->connect("pressed", this,"_menu_item_pressed",button_binds); + tool_button[TOOL_MODE_SCALE]->set_tooltip("Scale Mode (R)"); + + instance_button = memnew( Button ); + hbc_menu->add_child( instance_button ); + instance_button->set_flat(true); + instance_button->connect("pressed",this,"_instance_scene"); + + VSeparator *vs = memnew( VSeparator ); + hbc_menu->add_child(vs); + + + PopupMenu *p; + + transform_menu = memnew( MenuButton ); + transform_menu->set_text("Transform"); + hbc_menu->add_child( transform_menu ); + + p = transform_menu->get_popup(); + p->add_check_item("Use Snap",MENU_TRANSFORM_USE_SNAP); + p->add_item("Configure Snap..",MENU_TRANSFORM_CONFIGURE_SNAP); + p->add_separator(); + p->add_check_item("Local Coords",MENU_TRANSFORM_LOCAL_COORDS); + //p->set_item_checked(p->get_item_count()-1,true); + p->add_separator(); + p->add_item("Transform Dialog..",MENU_TRANSFORM_DIALOG); + + p->connect("item_pressed", this,"_menu_item_pressed"); + + view_menu = memnew( MenuButton ); + view_menu->set_text("View"); + view_menu->set_pos( Point2( 212,0) ); + hbc_menu->add_child( view_menu ); + + p = view_menu->get_popup(); + + p->add_check_item("Use Default Light",MENU_VIEW_USE_DEFAULT_LIGHT); + p->add_separator(); + + p->add_check_item("1 Viewport",MENU_VIEW_USE_1_VIEWPORT); + p->add_check_item("2 Viewports",MENU_VIEW_USE_2_VIEWPORTS); + p->add_check_item("3 Viewports",MENU_VIEW_USE_3_VIEWPORTS); + p->add_check_item("4 Viewports",MENU_VIEW_USE_4_VIEWPORTS); + p->add_separator(); + + p->add_check_item("Display Normal",MENU_VIEW_DISPLAY_NORMAL); + p->add_check_item("Display Wireframe",MENU_VIEW_DISPLAY_WIREFRAME); + p->add_check_item("Display Overdraw",MENU_VIEW_DISPLAY_OVERDRAW); + p->add_separator(); + p->add_check_item("View Origin",MENU_VIEW_ORIGIN); + p->add_check_item("View Grid",MENU_VIEW_GRID); + p->add_separator(); + p->add_check_item("Settings",MENU_VIEW_CAMERA_SETTINGS ); + + + p->set_item_checked( p->get_item_index(MENU_VIEW_USE_DEFAULT_LIGHT), true ); + p->set_item_checked( p->get_item_index(MENU_VIEW_DISPLAY_NORMAL), true ); + p->set_item_checked( p->get_item_index(MENU_VIEW_ORIGIN), true ); + p->set_item_checked( p->get_item_index(MENU_VIEW_GRID), true ); + + + p->connect("item_pressed", this,"_menu_item_pressed"); + + + /* REST OF MENU */ + + palette_split = memnew( HSplitContainer); + palette_split->set_v_size_flags(SIZE_EXPAND_FILL); + vbc->add_child(palette_split); + + shader_split = memnew( VSplitContainer ); + shader_split->set_h_size_flags(SIZE_EXPAND_FILL); + palette_split->add_child(shader_split); + viewport_base = memnew( Control ); + shader_split->add_child(viewport_base); + viewport_base->set_v_size_flags(SIZE_EXPAND_FILL); + for(int i=0;i<4;i++) { + + viewports[i] = memnew( SpatialEditorViewport(this,editor) ); + viewport_base->add_child(viewports[i]); + } + //vbc->add_child(viewport_base); + + + + + /* SNAP DIALOG */ + + snap_dialog = memnew( ConfirmationDialog ); + snap_dialog->set_title("Snap Settings"); + add_child(snap_dialog); + Label *l = memnew(Label); + l->set_text("Translate Snap:"); + l->set_pos(Point2(5,5)); + snap_dialog->add_child(l); + + snap_translate = memnew( LineEdit ); + snap_translate->set_anchor( MARGIN_RIGHT, ANCHOR_END ); + snap_translate->set_begin( Point2(15,22) ); + snap_translate->set_end( Point2(15,35) ); + snap_translate->set_text("1"); + snap_dialog->add_child(snap_translate); + + l = memnew(Label); + l->set_text("Rotate Snap (deg.):"); + l->set_pos(Point2(5,45)); + snap_dialog->add_child(l); + + snap_rotate = memnew( LineEdit ); + snap_rotate->set_anchor( MARGIN_RIGHT, ANCHOR_END ); + snap_rotate->set_begin( Point2(15,62) ); + snap_rotate->set_end( Point2(15,75) ); + snap_rotate->set_text("5"); + snap_dialog->add_child(snap_rotate); + + + l = memnew(Label); + l->set_text("Scale Snap (%):"); + l->set_pos(Point2(5,85)); + snap_dialog->add_child(l); + + snap_scale = memnew( LineEdit ); + snap_scale->set_anchor( MARGIN_RIGHT, ANCHOR_END ); + snap_scale->set_begin( Point2(15,102) ); + snap_scale->set_end( Point2(15,115) ); + snap_scale->set_text("5"); + snap_dialog->add_child(snap_scale); + + //snap_dialog->get_cancel()->hide(); + + /* SNAP DIALOG */ + + settings_dialog = memnew( ConfirmationDialog ); + settings_dialog->set_title("Viewport Settings"); + add_child(settings_dialog); + l = memnew(Label); + l->set_text("Perspective FOV (deg.):"); + l->set_pos(Point2(5,5)); + settings_dialog->add_child(l); + + settings_fov = memnew( LineEdit ); + settings_fov->set_anchor( MARGIN_RIGHT, ANCHOR_END ); + settings_fov->set_begin( Point2(15,22) ); + settings_fov->set_end( Point2(15,35) ); + settings_fov->set_text(EDITOR_DEF("3d_editor/default_fov",60.0)); + settings_dialog->add_child(settings_fov); + + l = memnew(Label); + l->set_text("View Z-Near"); + l->set_pos(Point2(5,45)); + settings_dialog->add_child(l); + + settings_znear = memnew( LineEdit ); + settings_znear->set_anchor( MARGIN_RIGHT, ANCHOR_END ); + settings_znear->set_begin( Point2(15,62) ); + settings_znear->set_end( Point2(15,75) ); + settings_znear->set_text(EDITOR_DEF("3d_editor/default_z_near",0.1)); + settings_dialog->add_child(settings_znear); + + + l = memnew(Label); + l->set_text("View Z-Far"); + l->set_pos(Point2(5,85)); + settings_dialog->add_child(l); + + settings_zfar = memnew( LineEdit ); + settings_zfar->set_anchor( MARGIN_RIGHT, ANCHOR_END ); + settings_zfar->set_begin( Point2(15,102) ); + settings_zfar->set_end( Point2(15,115) ); + settings_zfar->set_text(EDITOR_DEF("3d_editor/default_z_far",500.0)); + settings_dialog->add_child(settings_zfar); + + //settings_dialog->get_cancel()->hide(); + /* XFORM DIALOG */ + + xform_dialog = memnew( ConfirmationDialog ); + xform_dialog->set_title("Transform Change"); + add_child(xform_dialog); + l = memnew(Label); + l->set_text("Translate:"); + l->set_pos(Point2(5,5)); + xform_dialog->add_child(l); + + for(int i=0;i<3;i++) { + + xform_translate[i] = memnew( LineEdit ); + xform_translate[i]->set_pos( Point2(15+i*60,22) ); + xform_translate[i]->set_size( Size2(50,12 ) ); + xform_dialog->add_child( xform_translate[i] ); + } + + l = memnew(Label); + l->set_text("Rotate (deg.):"); + l->set_pos(Point2(5,45)); + xform_dialog->add_child(l); + + for(int i=0;i<3;i++) { + xform_rotate[i] = memnew( LineEdit ); + xform_rotate[i]->set_pos( Point2(15+i*60,62) ); + xform_rotate[i]->set_size( Size2(50,22 ) ); + xform_dialog->add_child(xform_rotate[i]); + } + + l = memnew(Label); + l->set_text("Scale (ratio):"); + l->set_pos(Point2(5,85)); + xform_dialog->add_child(l); + + for(int i=0;i<3;i++) { + xform_scale[i] = memnew( LineEdit ); + xform_scale[i]->set_pos( Point2(15+i*60,102) ); + xform_scale[i]->set_size( Size2(50,22 ) ); + xform_dialog->add_child(xform_scale[i]); + } + + l = memnew(Label); + l->set_text("Transform Type"); + l->set_pos(Point2(5,125)); + xform_dialog->add_child(l); + + xform_type = memnew( OptionButton ); + xform_type->set_anchor( MARGIN_RIGHT, ANCHOR_END ); + xform_type->set_begin( Point2(15,142) ); + xform_type->set_end( Point2(15,75) ); + xform_type->add_item("Pre"); + xform_type->add_item("Post"); + xform_dialog->add_child(xform_type); + + xform_dialog->connect("confirmed", this,"_xform_dialog_action"); + + scenario_debug=VisualServer::SCENARIO_DEBUG_DISABLED; + + + add_to_group("unhandled_key_input"); + add_to_group("_spatial_editor_group"); +} + +SpatialEditor::~SpatialEditor() { + + +} + + + + +void SpatialEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + + + spatial_editor->show(); + spatial_editor->set_process(true); + //VisualServer::get_singleton()->viewport_set_hide_scenario(editor->get_scene_root()->get_viewport(),false); + spatial_editor->grab_focus(); + + } else { + + spatial_editor->hide(); + spatial_editor->set_process(false); + //VisualServer::get_singleton()->viewport_set_hide_scenario(editor->get_scene_root()->get_viewport(),true); + + } + +} +void SpatialEditorPlugin::edit(Object *p_object) { + + spatial_editor->edit(p_object->cast_to<Spatial>()); +} + +bool SpatialEditorPlugin::handles(Object *p_object) const { + + return p_object->is_type("Spatial"); +} + +Dictionary SpatialEditorPlugin::get_state() const { + return spatial_editor->get_state(); +} + +void SpatialEditorPlugin::set_state(const Dictionary& p_state) { + + spatial_editor->set_state(p_state); +} + +void SpatialEditor::snap_cursor_to_plane(const Plane& p_plane) { + +// cursor.pos=p_plane.project(cursor.pos); +} + +void SpatialEditorPlugin::_bind_methods() { + + ObjectTypeDB::bind_method("snap_cursor_to_plane",&SpatialEditorPlugin::snap_cursor_to_plane); + +} + +void SpatialEditorPlugin::snap_cursor_to_plane(const Plane& p_plane) { + + + spatial_editor->snap_cursor_to_plane(p_plane); +} + + + + +SpatialEditorPlugin::SpatialEditorPlugin(EditorNode *p_node) { + + editor=p_node; + spatial_editor = memnew( SpatialEditor(p_node) ); + editor->get_viewport()->add_child(spatial_editor); + spatial_editor->set_area_as_parent_rect(); + spatial_editor->hide(); + spatial_editor->connect("transform_key_request",editor,"_transform_keyed"); + + //spatial_editor->set_process(true); +} + + +SpatialEditorPlugin::~SpatialEditorPlugin() { + +} + + + diff --git a/tools/editor/plugins/spatial_editor_plugin.h b/tools/editor/plugins/spatial_editor_plugin.h new file mode 100644 index 000000000..308590ece --- /dev/null +++ b/tools/editor/plugins/spatial_editor_plugin.h @@ -0,0 +1,467 @@ +/*************************************************************************/ +/* spatial_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef SPATIAL_EDITOR_PLUGIN_H +#define SPATIAL_EDITOR_PLUGIN_H + +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" +#include "scene/3d/visual_instance.h" +#include "scene/gui/panel_container.h" +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + +class Camera; +class SpatialEditor; +class SpatialEditorGizmos; + +class SpatialEditorGizmo : public SpatialGizmo { + + OBJ_TYPE(SpatialEditorGizmo,SpatialGizmo); +public: + + virtual String get_handle_name(int p_idx) const; + virtual Variant get_handle_value(int p_idx) const; + virtual void set_handle(int p_idx,Camera *p_camera, const Point2& p_point); + virtual void commit_handle(int p_idx,const Variant& p_restore,bool p_cancel=false); + + virtual bool intersect_frustum(const Camera *p_camera,const Vector<Plane> &p_frustum); + virtual bool intersect_ray(const Camera *p_camera,const Point2& p_point, Vector3& r_pos, Vector3& r_normal,int *r_gizmo_handle=NULL,bool p_sec_first=false); + SpatialEditorGizmo(); +}; + + +class SpatialEditorViewport : public Control { + + OBJ_TYPE( SpatialEditorViewport, Control ); + + enum { + + VIEW_TOP, + VIEW_BOTTOM, + VIEW_LEFT, + VIEW_RIGHT, + VIEW_FRONT, + VIEW_REAR, + VIEW_CENTER_TO_SELECTION, + VIEW_PERSPECTIVE, + VIEW_ENVIRONMENT, + VIEW_ORTHOGONAL + }; + + void _menu_option(int p_option); + + EditorNode *editor; + EditorSelection *editor_selection; + UndoRedo *undo_redo; + + Button *preview_camera; + + MenuButton *view_menu; + + Control *surface; + Viewport *viewport; + Camera *camera; + bool transforming; + bool orthogonal; + + void _compute_edit(const Point2& p_point); + void _clear_selected(); + void _select_clicked(bool p_append,bool p_single); + void _select(Spatial *p_node, bool p_append,bool p_single); + ObjectID _select_ray(const Point2& p_pos, bool p_append,bool &r_includes_current,int *r_gizmo_handle=NULL,bool p_alt_select=false); + Vector3 _get_ray_pos(const Vector2& p_pos) const; + Vector3 _get_ray(const Vector2& p_pos); + Point2 _point_to_screen(const Vector3& p_point); + Transform _get_camera_transform() const; + int get_selected_count() const; + + Vector3 _get_camera_pos() const; + Vector3 _get_camera_normal() const; + Vector3 _get_screen_to_space(const Vector3& p_vector3); + + void _select_region(); + void _update_selection(); + bool _gizmo_select(const Vector2& p_screenpos,bool p_hilite_only=false); + + + float get_znear() const; + float get_zfar() const; + float get_fov() const; + + ObjectID clicked; + bool clicked_includes_current; + bool clicked_wants_append; + + enum TransformMode { + TRANSFORM_NONE, + TRANSFORM_ROTATE, + TRANSFORM_TRANSLATE, + TRANSFORM_SCALE + + }; + enum TransformPlane { + TRANSFORM_VIEW, + TRANSFORM_X_AXIS, + TRANSFORM_Y_AXIS, + TRANSFORM_Z_AXIS, + }; + + struct EditData { + TransformMode mode; + TransformPlane plane; + Transform original; + Vector3 click_ray; + Vector3 click_ray_pos; + Vector3 center; + Vector3 orig_gizmo_pos; + int edited_gizmo; + Point2 mouse_pos; + bool snap; + Ref<SpatialEditorGizmo> gizmo; + int gizmo_handle; + Variant gizmo_initial_value; + Vector3 gizmo_initial_pos; + } _edit; + + struct Cursor { + + Vector3 cursor_pos; + + Vector3 pos; + float x_rot,y_rot,distance; + bool region_select; + Point2 region_begin,region_end; + + Cursor() { x_rot=y_rot=0; distance=4; region_select=false; } + } cursor; + + String last_message; + String message; + float message_time; + + void set_message(String p_message,float p_time=5); + + // + void _update_camera(); + void _draw(); + + void _smouseenter(); + void _sinput(const InputEvent& p_ie); + SpatialEditor *spatial_editor; + + Camera* previewing; + Camera* preview; + + void _preview_exited_scene(); + void _toggle_camera_preview(bool); +protected: + + void _notification(int p_what); + static void _bind_methods(); +public: + + void set_can_preview(Camera* p_preview); + void set_state(const Dictionary& p_state); + Dictionary get_state() const; + + + + SpatialEditorViewport(SpatialEditor *p_spatial_editor,EditorNode *p_editor); +}; + + + +class SpatialEditorSelectedItem : public Object { + + OBJ_TYPE(SpatialEditorSelectedItem,Object); + +public: + + AABB aabb; + Transform original; // original location when moving + Transform last_xform; // last transform + Spatial *sp; + RID sbox_instance; + + SpatialEditorSelectedItem() { sp=NULL; } + ~SpatialEditorSelectedItem(); +}; + +class SpatialEditor : public VBoxContainer { + + OBJ_TYPE(SpatialEditor, VBoxContainer ); +public: + + enum ToolMode { + + TOOL_MODE_SELECT, + TOOL_MODE_MOVE, + TOOL_MODE_ROTATE, + TOOL_MODE_SCALE + + }; + + +private: + EditorNode *editor; + EditorSelection *editor_selection; + + + + Control *viewport_base; + SpatialEditorViewport *viewports[4]; + VSplitContainer *shader_split; + HSplitContainer *palette_split; + + ///// + + ToolMode tool_mode; + bool orthogonal; + + + VisualServer::ScenarioDebugMode scenario_debug; + + RID light; + RID light_instance; + Transform light_transform; + + + RID origin; + RID origin_instance; + RID grid[3]; + RID grid_instance[3]; + bool grid_visible[3]; //currently visible + float last_grid_snap; + bool grid_enable[3]; //should be always visible if true + bool grid_enabled; + + Ref<Mesh> move_gizmo[3], rotate_gizmo[3]; + RID move_gizmo_instance[3], rotate_gizmo_instance[3]; + Ref<FixedMaterial> gizmo_color[3]; + Ref<FixedMaterial> gizmo_hl; + + + + + Ref<Mesh> selection_box; + RID indicators; + RID indicators_instance; + RID cursor_mesh; + RID cursor_instance; + +/* + struct Selected { + AABB aabb; + Transform original; // original location when moving + Transform last_xform; // last transform + Spatial *sp; + RID poly_instance; + }; + + Map<uint32_t,Selected> selected; +*/ + struct Gizmo { + + bool visible; + float scale; + Transform transform; + } gizmo; + + + + + enum MenuOption { + + MENU_TOOL_SELECT, + MENU_TOOL_MOVE, + MENU_TOOL_ROTATE, + MENU_TOOL_SCALE, + MENU_TRANSFORM_USE_SNAP, + MENU_TRANSFORM_CONFIGURE_SNAP, + MENU_TRANSFORM_LOCAL_COORDS, + MENU_TRANSFORM_DIALOG, + MENU_VIEW_USE_1_VIEWPORT, + MENU_VIEW_USE_2_VIEWPORTS, + MENU_VIEW_USE_3_VIEWPORTS, + MENU_VIEW_USE_4_VIEWPORTS, + MENU_VIEW_USE_DEFAULT_LIGHT, + MENU_VIEW_DISPLAY_NORMAL, + MENU_VIEW_DISPLAY_WIREFRAME, + MENU_VIEW_DISPLAY_OVERDRAW, + MENU_VIEW_ORIGIN, + MENU_VIEW_GRID, + MENU_VIEW_CAMERA_SETTINGS, + + }; + + + Button *tool_button[4]; + Button *instance_button; + + + MenuButton* transform_menu; + MenuButton* view_menu; + + ConfirmationDialog *snap_dialog; + ConfirmationDialog *xform_dialog; + ConfirmationDialog *settings_dialog; + + bool snap_enabled; + LineEdit *snap_translate; + LineEdit *snap_rotate; + LineEdit *snap_scale; + PanelContainer* menu_panel; + + LineEdit *xform_translate[3]; + LineEdit *xform_rotate[3]; + LineEdit *xform_scale[3]; + OptionButton *xform_type; + + LineEdit *settings_fov; + LineEdit *settings_znear; + LineEdit *settings_zfar; + + void _xform_dialog_action(); + void _menu_item_pressed(int p_option); + + HBoxContainer *hbc_menu; + + void _update_transform_gizmo_view(); + +// +// + void _generate_selection_box(); + UndoRedo *undo_redo; + + void _instance_scene(); + void _init_indicators(); + void _finish_indicators(); + + Node *custom_camera; + + Object *_get_editor_data(Object *p_what); + + Ref<Environment> viewport_environment; + + List<EditorPlugin*> gizmo_plugins; + + void _request_gizmo(Object* p_obj); + + static SpatialEditor *singleton; + + SpatialEditorGizmos *gizmos; + SpatialEditor(); + +protected: + + + + + void _notification(int p_what); + //void _input_event(InputEvent p_event); + void _unhandled_key_input(InputEvent p_event); + + static void _bind_methods(); +public: + + + static SpatialEditor *get_singleton() { return singleton; } + void snap_cursor_to_plane(const Plane& p_plane); + + float get_znear() const { return settings_znear->get_text().to_double(); } + float get_zfar() const { return settings_zfar->get_text().to_double(); } + float get_fov() const { return settings_fov->get_text().to_double(); } + + Transform get_gizmo_transform() const { return gizmo.transform; } + bool is_gizmo_visible() const { return gizmo.visible; } + + ToolMode get_tool_mode() const { return tool_mode; } + bool is_snap_enabled() const { return snap_enabled; } + float get_translate_snap() const { return snap_translate->get_text().to_double(); } + float get_rotate_snap() const { return snap_rotate->get_text().to_double(); } + float get_scale_snap() const { return snap_scale->get_text().to_double(); } + + void update_transform_gizmo(); + + void select_gizmo_hilight_axis(int p_axis); + void set_custom_camera(Node *p_camera) { custom_camera=p_camera; } + + void set_undo_redo(UndoRedo *p_undo_redo) {undo_redo=p_undo_redo; } + Dictionary get_state() const; + void set_state(const Dictionary& p_state); + + Ref<Environment> get_viewport_environment() { return viewport_environment; } + + UndoRedo *get_undo_redo() { return undo_redo; } + + void add_gizmo_plugin(EditorPlugin* p_plugin) { gizmo_plugins.push_back(p_plugin); } + + void add_control_to_menu_panel(Control *p_control); + + VSplitContainer *get_shader_split(); + HSplitContainer *get_palette_split(); + + void set_can_preview(Camera* p_preview); + + Camera *get_camera() { return NULL; } + void edit(Spatial *p_spatial); + SpatialEditor(EditorNode *p_editor); + ~SpatialEditor(); +}; + +class SpatialEditorPlugin : public EditorPlugin { + + OBJ_TYPE( SpatialEditorPlugin, EditorPlugin ); + + SpatialEditor *spatial_editor; + EditorNode *editor; +protected: + static void _bind_methods(); +public: + + void snap_cursor_to_plane(const Plane& p_plane); + + SpatialEditor *get_spatial_editor() { return spatial_editor; } + virtual String get_name() const { return "3D"; } + bool has_main_screen() const { return true; } + virtual void make_visible(bool p_visible); + virtual void edit(Object *p_object); + virtual bool handles(Object *p_object) const; + + virtual Dictionary get_state() const; + virtual void set_state(const Dictionary& p_state); + + + SpatialEditorPlugin(EditorNode *p_node); + ~SpatialEditorPlugin(); + +}; + +#endif diff --git a/tools/editor/plugins/sprite_frames_editor_plugin.cpp b/tools/editor/plugins/sprite_frames_editor_plugin.cpp new file mode 100644 index 000000000..a26fab0f9 --- /dev/null +++ b/tools/editor/plugins/sprite_frames_editor_plugin.cpp @@ -0,0 +1,483 @@ +/*************************************************************************/ +/* sprite_frames_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "sprite_frames_editor_plugin.h" + +#include "io/resource_loader.h" +#include "globals.h" +#include "tools/editor/editor_settings.h" + + + + +void SpriteFramesEditor::_input_event(InputEvent p_event) { + + +} + +void SpriteFramesEditor::_notification(int p_what) { + + if (p_what==NOTIFICATION_FIXED_PROCESS) { + + } + + if (p_what==NOTIFICATION_ENTER_SCENE) { + load->set_icon( get_icon("Folder","EditorIcons") ); + _delete->set_icon( get_icon("Del","EditorIcons") ); + } + + if (p_what==NOTIFICATION_READY) { + +// NodePath("/root")->connect("node_removed", this,"_node_removed",Vector<Variant>(),true); + } + + if (p_what==NOTIFICATION_DRAW) { + + } +} + +void SpriteFramesEditor::_file_load_request(const DVector<String>& p_path) { + + + List< Ref<Texture> > resources; + + for(int i=0;i<p_path.size();i++) { + + Ref<Texture> resource; + resource = ResourceLoader::load(p_path[i]); + + if (resource.is_null()) { + dialog->set_text("ERROR: Couldn't load frame resource!"); + dialog->set_title("Error!"); + //dialog->get_cancel()->set_text("Close"); + dialog->get_ok()->set_text("Close"); + dialog->popup_centered(Size2(300,60)); + return; ///beh should show an error i guess + } + + resources.push_back(resource); + } + + + if (resources.empty()) { + print_line("added frames!"); + return; + } + + undo_redo->create_action("Add Frame"); + int fc=frames->get_frame_count(); + + for(List< Ref<Texture> >::Element *E=resources.front();E;E=E->next() ) { + + undo_redo->add_do_method(frames,"add_frame",E->get()); + undo_redo->add_undo_method(frames,"remove_frame",fc++); + + } + undo_redo->add_do_method(this,"_update_library"); + undo_redo->add_undo_method(this,"_update_library"); + + undo_redo->commit_action(); + print_line("added frames!"); +} + +void SpriteFramesEditor::_load_pressed() { + + loading_scene=false; + + file->clear_filters(); + List<String> extensions; + ResourceLoader::get_recognized_extensions_for_type("Texture",&extensions); + for(int i=0;i<extensions.size();i++) + file->add_filter("*."+extensions[i]); + + file->set_mode(FileDialog::MODE_OPEN_FILES); + + file->popup_centered_ratio(); + +} + + +void SpriteFramesEditor::_item_edited() { + +#if 0 + if (!tree->get_selected()) + return; + + TreeItem *s = tree->get_selected(); + + if (tree->get_selected_column()==0) { + // renamed + String old_name=s->get_metadata(0); + String new_name=s->get_text(0); + if (old_name==new_name) + return; + + if (new_name=="" || new_name.find("\\")!=-1 || new_name.find("/")!=-1 || frames->has_resource(new_name)) { + + s->set_text(0,old_name); + return; + } + + RES samp = frames->get_resource(old_name); + undo_redo->create_action("Rename Resource"); + undo_redo->add_do_method(frames,"remove_resource",old_name); + undo_redo->add_do_method(frames,"add_resource",new_name,samp); + undo_redo->add_undo_method(frames,"remove_resource",new_name); + undo_redo->add_undo_method(frames,"add_resource",old_name,samp); + undo_redo->add_do_method(this,"_update_library"); + undo_redo->add_undo_method(this,"_update_library"); + undo_redo->commit_action(); + + } +#endif + +} + +void SpriteFramesEditor::_delete_confirm_pressed() { + + if (!tree->get_selected()) + return; + + sel-=1; + if (sel<0 && frames->get_frame_count()) + sel=0; + + int to_remove = tree->get_selected()->get_metadata(0); + sel=to_remove; + Ref<Texture> r = frames->get_frame(to_remove); + undo_redo->create_action("Delete Resource"); + undo_redo->add_do_method(frames,"remove_frame",to_remove); + undo_redo->add_undo_method(frames,"add_frame",r,to_remove); + undo_redo->add_do_method(this,"_update_library"); + undo_redo->add_undo_method(this,"_update_library"); + undo_redo->commit_action(); + +} + + +void SpriteFramesEditor::_paste_pressed() { + + Ref<Texture> r=EditorSettings::get_singleton()->get_resource_clipboard(); + if (!r.is_valid()) { + dialog->set_text("Resource clipboard is empty or not a texture!"); + dialog->set_title("Error!"); + //dialog->get_cancel()->set_text("Close"); + dialog->get_ok()->set_text("Close"); + dialog->popup_centered(Size2(300,60)); + return; ///beh should show an error i guess + } + + + undo_redo->create_action("Paste Frame"); + undo_redo->add_do_method(frames,"add_frame",r); + undo_redo->add_undo_method(frames,"remove_frame",frames->get_frame_count()); + undo_redo->add_do_method(this,"_update_library"); + undo_redo->add_undo_method(this,"_update_library"); + undo_redo->commit_action(); + +} + +void SpriteFramesEditor::_empty_pressed() { + + + int from=-1; + + if (tree->get_selected()) { + + from = tree->get_selected()->get_metadata(0); + sel=from; + + } else { + from=frames->get_frame_count(); + } + + + + Ref<Texture> r; + + undo_redo->create_action("Add Empty"); + undo_redo->add_do_method(frames,"add_frame",r,from); + undo_redo->add_undo_method(frames,"remove_frame",from); + undo_redo->add_do_method(this,"_update_library"); + undo_redo->add_undo_method(this,"_update_library"); + undo_redo->commit_action(); + +} + +void SpriteFramesEditor::_up_pressed() { + + if (!tree->get_selected()) + return; + int to_move = tree->get_selected()->get_metadata(0); + if (to_move<1) + return; + + sel=to_move; + sel-=1; + + Ref<Texture> r = frames->get_frame(to_move); + undo_redo->create_action("Delete Resource"); + undo_redo->add_do_method(frames,"set_frame",to_move,frames->get_frame(to_move-1)); + undo_redo->add_do_method(frames,"set_frame",to_move-1,frames->get_frame(to_move)); + undo_redo->add_undo_method(frames,"set_frame",to_move,frames->get_frame(to_move)); + undo_redo->add_undo_method(frames,"set_frame",to_move-1,frames->get_frame(to_move-1)); + undo_redo->add_do_method(this,"_update_library"); + undo_redo->add_undo_method(this,"_update_library"); + undo_redo->commit_action(); + +} + +void SpriteFramesEditor::_down_pressed() { + + if (!tree->get_selected()) + return; + int to_move = tree->get_selected()->get_metadata(0); + if (to_move<0 || to_move>=frames->get_frame_count()-1) + return; + + sel=to_move; + sel+=1; + + Ref<Texture> r = frames->get_frame(to_move); + undo_redo->create_action("Delete Resource"); + undo_redo->add_do_method(frames,"set_frame",to_move,frames->get_frame(to_move+1)); + undo_redo->add_do_method(frames,"set_frame",to_move+1,frames->get_frame(to_move)); + undo_redo->add_undo_method(frames,"set_frame",to_move,frames->get_frame(to_move)); + undo_redo->add_undo_method(frames,"set_frame",to_move+1,frames->get_frame(to_move+1)); + undo_redo->add_do_method(this,"_update_library"); + undo_redo->add_undo_method(this,"_update_library"); + undo_redo->commit_action(); + + + +} + + +void SpriteFramesEditor::_delete_pressed() { + + + if (!tree->get_selected()) + return; + + _delete_confirm_pressed(); //it has undo.. why bother with a dialog.. + /* + dialog->set_title("Confirm..."); + dialog->set_text("Remove Resource '"+tree->get_selected()->get_text(0)+"' ?"); + //dialog->get_cancel()->set_text("Cancel"); + //dialog->get_ok()->show(); + dialog->get_ok()->set_text("Remove"); + dialog->popup_centered(Size2(300,60));*/ + +} + + +void SpriteFramesEditor::_update_library() { + + tree->clear(); + tree->set_hide_root(true); + TreeItem *root = tree->create_item(NULL); + + if (sel>=frames->get_frame_count()) + sel=frames->get_frame_count()-1; + else if (sel<0 && frames->get_frame_count()) + sel=0; + + for(int i=0;i<frames->get_frame_count();i++) { + + TreeItem *ti = tree->create_item(root); + ti->set_cell_mode(0,TreeItem::CELL_MODE_STRING); + ti->set_editable(0,true); + ti->set_selectable(0,true); + + if (frames->get_frame(i).is_null()) { + + ti->set_text(0,"Frame "+itos(i)+" (empty)"); + + } else { + ti->set_text(0,"Frame "+itos(i)); + ti->set_icon(0,frames->get_frame(i)); + } + ti->set_metadata(0,i); + ti->set_icon_max_width(0,96); + if (sel==i) + ti->select(0); + } + + //player->add_resource("default",resource); +} + + + +void SpriteFramesEditor::edit(SpriteFrames* p_frames) { + + frames=p_frames; + + + if (p_frames) { + _update_library(); + } else { + + hide(); + //set_fixed_process(false); + } + +} + + + +void SpriteFramesEditor::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_input_event"),&SpriteFramesEditor::_input_event); + ObjectTypeDB::bind_method(_MD("_load_pressed"),&SpriteFramesEditor::_load_pressed); + ObjectTypeDB::bind_method(_MD("_empty_pressed"),&SpriteFramesEditor::_empty_pressed); + ObjectTypeDB::bind_method(_MD("_item_edited"),&SpriteFramesEditor::_item_edited); + ObjectTypeDB::bind_method(_MD("_delete_pressed"),&SpriteFramesEditor::_delete_pressed); + ObjectTypeDB::bind_method(_MD("_paste_pressed"),&SpriteFramesEditor::_paste_pressed); + ObjectTypeDB::bind_method(_MD("_delete_confirm_pressed"),&SpriteFramesEditor::_delete_confirm_pressed); + ObjectTypeDB::bind_method(_MD("_file_load_request"),&SpriteFramesEditor::_file_load_request); + ObjectTypeDB::bind_method(_MD("_update_library"),&SpriteFramesEditor::_update_library); + ObjectTypeDB::bind_method(_MD("_up_pressed"),&SpriteFramesEditor::_up_pressed); + ObjectTypeDB::bind_method(_MD("_down_pressed"),&SpriteFramesEditor::_down_pressed); +} + +SpriteFramesEditor::SpriteFramesEditor() { + + //add_style_override("panel", get_stylebox("panel","Panel")); + + VBoxContainer *vbc = memnew( VBoxContainer ); + add_child(vbc); + + HBoxContainer *hbc = memnew( HBoxContainer ); + vbc->add_child(hbc); + + load = memnew( Button ); + load->set_tooltip("Load Resource"); + hbc->add_child(load); + + + + + paste = memnew( Button ); + paste->set_text("Paste"); + hbc->add_child(paste); + + empty = memnew( Button ); + empty->set_text("Insert Empty"); + hbc->add_child(empty); + + move_up = memnew( Button ); + move_up->set_text("Up"); + hbc->add_child(move_up); + + move_down = memnew( Button ); + move_down->set_text("Down"); + hbc->add_child(move_down); + + _delete = memnew( Button ); + hbc->add_child(_delete); + + file = memnew( FileDialog ); + add_child(file); + + + tree = memnew( Tree ); + tree->set_columns(2); + tree->set_column_min_width(0,3); + tree->set_column_min_width(1,1); + tree->set_column_expand(0,true); + tree->set_column_expand(1,true); + tree->set_v_size_flags(SIZE_EXPAND_FILL); + + vbc->add_child(tree); + + dialog = memnew( AcceptDialog ); + add_child( dialog ); + + load->connect("pressed", this,"_load_pressed"); + _delete->connect("pressed", this,"_delete_pressed"); + paste->connect("pressed", this,"_paste_pressed"); + empty->connect("pressed", this,"_empty_pressed"); + move_up->connect("pressed", this,"_up_pressed"); + move_down->connect("pressed", this,"_down_pressed"); + file->connect("files_selected", this,"_file_load_request"); + //dialog->connect("confirmed", this,"_delete_confirm_pressed"); + tree->connect("item_edited", this,"_item_edited"); + loading_scene=false; + sel=-1; + +} + + +void SpriteFramesEditorPlugin::edit(Object *p_object) { + + frames_editor->set_undo_redo(&get_undo_redo()); + SpriteFrames * s = p_object->cast_to<SpriteFrames>(); + if (!s) + return; + + frames_editor->edit(s); +} + +bool SpriteFramesEditorPlugin::handles(Object *p_object) const { + + return p_object->is_type("SpriteFrames"); +} + +void SpriteFramesEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + frames_editor->show(); +// frames_editor->set_process(true); + } else { + + frames_editor->hide(); +// frames_editor->set_process(false); + } + +} + +SpriteFramesEditorPlugin::SpriteFramesEditorPlugin(EditorNode *p_node) { + + editor=p_node; + frames_editor = memnew( SpriteFramesEditor ); + editor->get_viewport()->add_child(frames_editor); + frames_editor->set_area_as_parent_rect(); +// frames_editor->set_anchor( MARGIN_TOP, Control::ANCHOR_END); +// frames_editor->set_margin( MARGIN_TOP, 120 ); + frames_editor->hide(); + + + +} + + +SpriteFramesEditorPlugin::~SpriteFramesEditorPlugin() +{ +} + + diff --git a/tools/editor/plugins/sprite_frames_editor_plugin.h b/tools/editor/plugins/sprite_frames_editor_plugin.h new file mode 100644 index 000000000..99c6ad486 --- /dev/null +++ b/tools/editor/plugins/sprite_frames_editor_plugin.h @@ -0,0 +1,109 @@ +/*************************************************************************/ +/* sprite_frames_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef SPRITE_FRAMES_EDITOR_PLUGIN_H +#define SPRITE_FRAMES_EDITOR_PLUGIN_H + + +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" +#include "scene/gui/tree.h" +#include "scene/2d/animated_sprite.h" +#include "scene/gui/file_dialog.h" +#include "scene/gui/dialogs.h" + + +class SpriteFramesEditor : public PanelContainer { + + OBJ_TYPE(SpriteFramesEditor, PanelContainer ); + + Button *load; + Button *_delete; + Button *paste; + Button *empty; + Button *move_up; + Button *move_down; + Tree *tree; + bool loading_scene; + int sel; + + + FileDialog *file; + + AcceptDialog *dialog; + + SpriteFrames *frames; + + + void _load_pressed(); + void _load_scene_pressed(); + void _file_load_request(const DVector<String>& p_path); + void _paste_pressed(); + void _empty_pressed(); + void _delete_pressed(); + void _delete_confirm_pressed(); + void _up_pressed(); + void _down_pressed(); + void _update_library(); + void _item_edited(); + + UndoRedo *undo_redo; + +protected: + void _notification(int p_what); + void _input_event(InputEvent p_event); + static void _bind_methods(); +public: + + void set_undo_redo(UndoRedo *p_undo_redo) {undo_redo=p_undo_redo; } + + void edit(SpriteFrames* p_frames); + SpriteFramesEditor(); +}; + +class SpriteFramesEditorPlugin : public EditorPlugin { + + OBJ_TYPE( SpriteFramesEditorPlugin, EditorPlugin ); + + SpriteFramesEditor *frames_editor; + EditorNode *editor; + +public: + + virtual String get_name() const { return "SpriteFrames"; } + 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); + + SpriteFramesEditorPlugin(EditorNode *p_node); + ~SpriteFramesEditorPlugin(); + +}; + +#endif // SPRITE_FRAMES_EDITOR_PLUGIN_H diff --git a/tools/editor/plugins/stream_editor_plugin.cpp b/tools/editor/plugins/stream_editor_plugin.cpp new file mode 100644 index 000000000..4588c694e --- /dev/null +++ b/tools/editor/plugins/stream_editor_plugin.cpp @@ -0,0 +1,148 @@ +/*************************************************************************/ +/* stream_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "stream_editor_plugin.h" + + + +void StreamEditor::_notification(int p_what) { + + if (p_what==NOTIFICATION_ENTER_SCENE) { + play->set_icon( get_icon("Play","EditorIcons") ); + stop->set_icon( get_icon("Stop","EditorIcons") ); + } + +} +void StreamEditor::_node_removed(Node *p_node) { + + if(p_node==node) { + node=NULL; + hide(); + } + +} + +void StreamEditor::_play() { + + node->call("play"); +} + +void StreamEditor::_stop() { + + node->call("stop"); +} + +void StreamEditor::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_play"),&StreamEditor::_play); + ObjectTypeDB::bind_method(_MD("_stop"),&StreamEditor::_stop); + +} + +void StreamEditor::edit(Node *p_stream) { + + node=p_stream; + +} +StreamEditor::StreamEditor() { + + play = memnew( Button ); + + + play->set_anchor_and_margin(MARGIN_LEFT,Control::ANCHOR_END,60); + play->set_anchor_and_margin(MARGIN_RIGHT,Control::ANCHOR_END,40); + play->set_anchor_and_margin(MARGIN_TOP,Control::ANCHOR_BEGIN,0); + play->set_anchor_and_margin(MARGIN_BOTTOM,Control::ANCHOR_BEGIN,0); + + add_child(play); + + stop = memnew( Button ); + + stop->set_pos(Point2( 35, 5 )); + stop->set_anchor_and_margin(MARGIN_LEFT,Control::ANCHOR_END,30); + stop->set_anchor_and_margin(MARGIN_RIGHT,Control::ANCHOR_END,10); + stop->set_anchor_and_margin(MARGIN_TOP,Control::ANCHOR_BEGIN,0); + stop->set_anchor_and_margin(MARGIN_BOTTOM,Control::ANCHOR_BEGIN,0); + add_child(stop); + + + play->connect("pressed", this,"_play"); + stop->connect("pressed", this,"_stop"); + +} + + +void StreamEditorPlugin::edit(Object *p_object) { + + stream_editor->edit(p_object->cast_to<Node>()); +} + +bool StreamEditorPlugin::handles(Object *p_object) const { + + return p_object->is_type("StreamPlayer") || p_object->is_type("SpatialStreamPlayer"); +} + +void StreamEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + stream_editor->show(); + stream_editor->set_fixed_process(true); + } else { + + stream_editor->hide(); + stream_editor->set_fixed_process(false); + stream_editor->edit(NULL); + } + +} + +StreamEditorPlugin::StreamEditorPlugin(EditorNode *p_node) { + + editor=p_node; + stream_editor = memnew( StreamEditor ); + editor->get_viewport()->add_child(stream_editor); + + stream_editor->set_anchor(MARGIN_LEFT,Control::ANCHOR_END); + stream_editor->set_anchor(MARGIN_RIGHT,Control::ANCHOR_END); + stream_editor->set_margin(MARGIN_LEFT,60); + stream_editor->set_margin(MARGIN_RIGHT,0); + stream_editor->set_margin(MARGIN_TOP,0); + stream_editor->set_margin(MARGIN_BOTTOM,10); + + + stream_editor->hide(); + + + +} + + +StreamEditorPlugin::~StreamEditorPlugin() +{ +} + diff --git a/tools/editor/plugins/stream_editor_plugin.h b/tools/editor/plugins/stream_editor_plugin.h new file mode 100644 index 000000000..d49d15b76 --- /dev/null +++ b/tools/editor/plugins/stream_editor_plugin.h @@ -0,0 +1,83 @@ +/*************************************************************************/ +/* stream_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef STREAM_EDITOR_PLUGIN_H +#define STREAM_EDITOR_PLUGIN_H + + +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" +#include "scene/audio/stream_player.h" + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + +class StreamEditor : public Control { + + OBJ_TYPE(StreamEditor, Control ); + + Button * play; + Button * stop; + + Panel *panel; + Node *node; + + void _play(); + void _stop(); +protected: + void _notification(int p_what); + void _node_removed(Node *p_node); + static void _bind_methods(); +public: + + void edit(Node *p_stream); + StreamEditor(); +}; + +class StreamEditorPlugin : public EditorPlugin { + + OBJ_TYPE( StreamEditorPlugin, EditorPlugin ); + + StreamEditor *stream_editor; + EditorNode *editor; + +public: + + virtual String get_name() const { return "Stream"; } + 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); + + StreamEditorPlugin(EditorNode *p_node); + ~StreamEditorPlugin(); + +}; + +#endif // STREAM_EDITOR_PLUGIN_H diff --git a/tools/editor/plugins/style_box_editor_plugin.cpp b/tools/editor/plugins/style_box_editor_plugin.cpp new file mode 100644 index 000000000..3b537fb5c --- /dev/null +++ b/tools/editor/plugins/style_box_editor_plugin.cpp @@ -0,0 +1,109 @@ +/*************************************************************************/ +/* style_box_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "style_box_editor_plugin.h" + + +void StyleBoxEditor::edit(const Ref<StyleBox>& p_stylebox) { + + if (stylebox.is_valid()) + stylebox->disconnect("changed",this,"_sb_changed"); + stylebox=p_stylebox; + if (p_stylebox.is_valid()) { + preview->add_style_override("panel",stylebox); + stylebox->connect("changed",this,"_sb_changed"); + + } +} + +void StyleBoxEditor::_sb_changed() { + + preview->update(); +} + +void StyleBoxEditor::_bind_methods() { + + ObjectTypeDB::bind_method("_sb_changed",&StyleBoxEditor::_sb_changed); +// ObjectTypeDB::bind_method("_import",&StyleBoxEditor::_import); +// ObjectTypeDB::bind_method("_import_accept",&StyleBoxEditor::_import_accept); +// ObjectTypeDB::bind_method("_preview_text_changed",&StyleBoxEditor::_preview_text_changed); +} + +StyleBoxEditor::StyleBoxEditor() { + + panel = memnew( Panel ); + add_child(panel); + panel->set_area_as_parent_rect(); + + Label *l = memnew( Label ); + l->set_text("StyleBox Preview:"); + l->set_pos(Point2(5,5)); + panel->add_child(l); + + + preview = memnew( Panel ); + panel->add_child(preview); + preview->set_pos(Point2(50,50)); + preview->set_size(Size2(200,100)); + + +} + +void StyleBoxEditorPlugin::edit(Object *p_node) { + + if (p_node && p_node->cast_to<StyleBox>()) { + stylebox_editor->edit( p_node->cast_to<StyleBox>() ); + stylebox_editor->show(); + } else + stylebox_editor->hide(); +} + +bool StyleBoxEditorPlugin::handles(Object *p_node) const{ + + return p_node->is_type("StyleBox"); +} + +void StyleBoxEditorPlugin::make_visible(bool p_visible){ + + if (p_visible) + stylebox_editor->show(); + else + stylebox_editor->hide(); +} + +StyleBoxEditorPlugin::StyleBoxEditorPlugin(EditorNode *p_node) { + + stylebox_editor = memnew( StyleBoxEditor ); + + p_node->get_viewport()->add_child(stylebox_editor); + stylebox_editor->set_area_as_parent_rect(); + stylebox_editor->hide(); + + +} + diff --git a/tools/editor/plugins/style_box_editor_plugin.h b/tools/editor/plugins/style_box_editor_plugin.h new file mode 100644 index 000000000..87f72b3cc --- /dev/null +++ b/tools/editor/plugins/style_box_editor_plugin.h @@ -0,0 +1,82 @@ +/*************************************************************************/ +/* style_box_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef STYLE_BOX_EDITOR_PLUGIN_H +#define STYLE_BOX_EDITOR_PLUGIN_H + +#include "scene/resources/style_box.h" +#include "scene/gui/texture_frame.h" +#include "scene/gui/option_button.h" +#include "tools/editor/editor_node.h" + + +class StyleBoxEditor : public Control { + + OBJ_TYPE( StyleBoxEditor, Control ); + + Panel *panel; + Panel *preview; + + Ref<StyleBox> stylebox; + + void _sb_changed(); + +protected: + + + static void _bind_methods(); +public: + + void edit(const Ref<StyleBox>& p_stylebox); + + StyleBoxEditor(); +}; + + + +class StyleBoxEditorPlugin : public EditorPlugin { + + OBJ_TYPE( StyleBoxEditorPlugin, EditorPlugin ); + + StyleBoxEditor *stylebox_editor; + EditorNode *editor; + +public: + + virtual String get_name() const { return "StyleBox"; } + 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); + + StyleBoxEditorPlugin(EditorNode *p_node); + +}; + + +#endif // STYLE_BOX_EDITOR_PLUGIN_H diff --git a/tools/editor/plugins/theme_editor_plugin.cpp b/tools/editor/plugins/theme_editor_plugin.cpp new file mode 100644 index 000000000..f17ab9449 --- /dev/null +++ b/tools/editor/plugins/theme_editor_plugin.cpp @@ -0,0 +1,696 @@ +/*************************************************************************/ +/* theme_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "version.h" +#include "theme_editor_plugin.h" +#include "os/file_access.h" + +void ThemeEditor::edit(const Ref<Theme>& p_theme) { + + theme=p_theme; + panel->set_theme(p_theme); + +} + +void ThemeEditor::_open_file_dialog() { + + test_file_dialog->popup_centered_ratio(); +} + +void ThemeEditor::_type_menu_cbk(int p_option) { + + + type_edit->set_text( type_menu->get_popup()->get_item_text(p_option) ); +} + +void ThemeEditor::_name_menu_about_to_show() { + + String fromtype=type_edit->get_text(); + List<StringName> names; + + if (popup_mode==POPUP_ADD) { + + switch(type_select->get_selected()) { + + case 0: Theme::get_default()->get_icon_list(fromtype,&names); break; + case 1: Theme::get_default()->get_stylebox_list(fromtype,&names); break; + case 2: Theme::get_default()->get_font_list(fromtype,&names); break; + case 3: Theme::get_default()->get_color_list(fromtype,&names); break; + case 4: Theme::get_default()->get_constant_list(fromtype,&names); break; + } + } else if (popup_mode==POPUP_REMOVE) { + + theme->get_icon_list(fromtype,&names); + theme->get_stylebox_list(fromtype,&names); + theme->get_font_list(fromtype,&names); + theme->get_color_list(fromtype,&names); + theme->get_constant_list(fromtype,&names); + } + + + name_menu->get_popup()->clear(); + + for(List<StringName>::Element *E=names.front();E;E=E->next()) { + + name_menu->get_popup()->add_item(E->get()); + } +} + +void ThemeEditor::_name_menu_cbk(int p_option) { + + name_edit->set_text( name_menu->get_popup()->get_item_text(p_option) ); +} + +struct _TECategory { + + template<class T> + struct RefItem { + + Ref<T> item; + StringName name; + bool operator<(const RefItem<T>& p) const { return item->get_instance_ID() < p.item->get_instance_ID(); } + }; + + template<class T> + struct Item { + + T item; + String name; + bool operator<(const Item<T>& p) const { return name < p.name; } + }; + + + Set<RefItem<StyleBox> > stylebox_items; + Set<RefItem<Font> > font_items; + Set<RefItem<Texture> > icon_items; + + Set<Item<Color> > color_items; + Set<Item<int> > constant_items; + +}; + + +void ThemeEditor::_save_template_cbk(String fname) { + + String filename = file_dialog->get_current_path(); + + Map<String,_TECategory> categories; + + //fill types + List<StringName> type_list; + Theme::get_default()->get_type_list(&type_list); + for (List<StringName>::Element *E=type_list.front();E;E=E->next()) { + categories.insert(E->get(),_TECategory()); + } + + //fill default theme + for(Map<String,_TECategory>::Element *E=categories.front();E;E=E->next() ) { + + _TECategory &tc = E->get(); + + List<StringName> stylebox_list; + Theme::get_default()->get_stylebox_list(E->key(),&stylebox_list); + for (List<StringName>::Element *F=stylebox_list.front();F;F=F->next()) { + _TECategory::RefItem<StyleBox> it; + it.name=F->get(); + it.item=Theme::get_default()->get_stylebox(F->get(),E->key()); + tc.stylebox_items.insert(it); + } + + List<StringName> font_list; + Theme::get_default()->get_font_list(E->key(),&font_list); + for (List<StringName>::Element *F=font_list.front();F;F=F->next()) { + _TECategory::RefItem<Font> it; + it.name=F->get(); + it.item=Theme::get_default()->get_font(F->get(),E->key()); + tc.font_items.insert(it); + } + + List<StringName> icon_list; + Theme::get_default()->get_icon_list(E->key(),&icon_list); + for (List<StringName>::Element *F=icon_list.front();F;F=F->next()) { + _TECategory::RefItem<Texture> it; + it.name=F->get(); + it.item=Theme::get_default()->get_icon(F->get(),E->key()); + tc.icon_items.insert(it); + } + + List<StringName> color_list; + Theme::get_default()->get_color_list(E->key(),&color_list); + for (List<StringName>::Element *F=color_list.front();F;F=F->next()) { + _TECategory::Item<Color> it; + it.name=F->get(); + it.item=Theme::get_default()->get_color(F->get(),E->key()); + tc.color_items.insert(it); + } + + List<StringName> constant_list; + Theme::get_default()->get_constant_list(E->key(),&constant_list); + for (List<StringName>::Element *F=constant_list.front();F;F=F->next()) { + _TECategory::Item<int> it; + it.name=F->get(); + it.item=Theme::get_default()->get_constant(F->get(),E->key()); + tc.constant_items.insert(it); + } + + } + + FileAccess *file = FileAccess::open(filename,FileAccess::WRITE); + if (!file) { + + + ERR_EXPLAIN("Can't save theme to file: "+filename); + return; + } + file->store_line("; ******************* "); + file->store_line("; Template Theme File "); + file->store_line("; ******************* "); + file->store_line("; "); + file->store_line("; Theme Syntax: "); + file->store_line("; ------------- "); + file->store_line("; "); + file->store_line("; Must be placed in section [theme]"); + file->store_line("; "); + file->store_line("; Type.item = [value] "); + file->store_line("; "); + file->store_line("; [value] examples:"); + file->store_line("; "); + file->store_line("; Type.item = 6 ; numeric constant. "); + file->store_line("; Type.item = #FF00FF ; HTML color "); + file->store_line("; Type.item = #55FF00FF ; HTML color with alpha 55."); + file->store_line("; Type.item = icon(image.png) ; icon in a png file (relative to theme file)."); + file->store_line("; Type.item = font(font.xres) ; font in a resource (relative to theme file)."); + file->store_line("; Type.item = sbox(stylebox.xres) ; stylebox in a resource (relative to theme file)."); + file->store_line("; Type.item = sboxf(2,#FF00FF) ; flat stylebox with margin 2."); + file->store_line("; Type.item = sboxf(2,#FF00FF,#FFFFFF) ; flat stylebox with margin 2 and border."); + file->store_line("; Type.item = sboxf(2,#FF00FF,#FFFFFF,#000000) ; flat stylebox with margin 2, light & dark borders."); + file->store_line("; Type.item = sboxt(base.png,2,2,2,2) ; textured stylebox with 3x3 stretch and stretch margins."); + file->store_line("; -Additionally, 4 extra integers can be added to sboxf and sboxt to specify custom padding of contents:"); + file->store_line("; Type.item = sboxt(base.png,2,2,2,2,5,4,2,4) ;"); + file->store_line("; -Order for all is always left, top, right, bottom."); + file->store_line("; "); + file->store_line("; Special values:"); + file->store_line("; Type.item = default ; use the value in the default theme (must exist there)."); + file->store_line("; Type.item = @somebutton_color ; reference to a library value previously defined."); + file->store_line("; "); + file->store_line("; Library Syntax: "); + file->store_line("; --------------- "); + file->store_line("; "); + file->store_line("; Must be placed in section [library], but usage is optional."); + file->store_line("; "); + file->store_line("; item = [value] ; same as Theme, but assign to library."); + file->store_line("; "); + file->store_line("; examples:"); + file->store_line("; "); + file->store_line("; [library]"); + file->store_line("; "); + file->store_line("; default_button_color = #FF00FF"); + file->store_line("; "); + file->store_line("; [theme]"); + file->store_line("; "); + file->store_line("; Button.color = @default_button_color ; used reference."); + file->store_line("; "); + file->store_line("; ******************* "); + file->store_line("; "); + file->store_line("; Template Generated Using: "+String(VERSION_MKSTRING)); + file->store_line("; "); + file->store_line("; "); + file->store_line(""); + file->store_line("[library]"); + file->store_line(""); + file->store_line("; place library stuff here"); + file->store_line(""); + file->store_line("[theme]"); + file->store_line(""); + file->store_line(""); + + //write default theme + for(Map<String,_TECategory>::Element *E=categories.front();E;E=E->next() ) { + + _TECategory &tc = E->get(); + + String underline="; "; + for(int i=0;i<E->key().length();i++) + underline+="*"; + + file->store_line(""); + file->store_line(underline); + file->store_line("; "+E->key()); + file->store_line(underline); + + if (tc.stylebox_items.size()) + file->store_line("\n; StyleBox Items:\n"); + + for (Set<_TECategory::RefItem<StyleBox> >::Element *F=tc.stylebox_items.front();F;F=F->next()) { + + file->store_line(E->key()+"."+F->get().name+" = default"); + } + + if (tc.font_items.size()) + file->store_line("\n; Font Items:\n"); + + for (Set<_TECategory::RefItem<Font> >::Element *F=tc.font_items.front();F;F=F->next()) { + + file->store_line(E->key()+"."+F->get().name+" = default"); + } + + if (tc.icon_items.size()) + file->store_line("\n; Icon Items:\n"); + + for (Set<_TECategory::RefItem<Texture> >::Element *F=tc.icon_items.front();F;F=F->next()) { + + file->store_line(E->key()+"."+F->get().name+" = default"); + } + + if (tc.color_items.size()) + file->store_line("\n; Color Items:\n"); + + for (Set<_TECategory::Item<Color> >::Element *F=tc.color_items.front();F;F=F->next()) { + + file->store_line(E->key()+"."+F->get().name+" = default"); + } + + if (tc.constant_items.size()) + file->store_line("\n; Constant Items:\n"); + + for (Set<_TECategory::Item<int> >::Element *F=tc.constant_items.front();F;F=F->next()) { + + file->store_line(E->key()+"."+F->get().name+" = default"); + } + + } + + file->close(); + memdelete(file); +} + +void ThemeEditor::_dialog_cbk() { + + switch(popup_mode) { + case POPUP_ADD: { + + switch(type_select->get_selected()) { + + case 0: theme->set_icon(name_edit->get_text(),type_edit->get_text(),Ref<Texture>()); break; + case 1: theme->set_stylebox(name_edit->get_text(),type_edit->get_text(),Ref<StyleBox>()); break; + case 2: theme->set_font(name_edit->get_text(),type_edit->get_text(),Ref<Font>()); break; + case 3: theme->set_color(name_edit->get_text(),type_edit->get_text(),Color()); break; + case 4: theme->set_constant(name_edit->get_text(),type_edit->get_text(),0); break; + } + + } break; + case POPUP_CLASS_ADD: { + + StringName fromtype = type_edit->get_text(); + List<StringName> names; + + { + names.clear(); + Theme::get_default()->get_icon_list(fromtype,&names); + for(List<StringName>::Element *E=names.front();E;E=E->next()) { + theme->set_icon(E->get(),fromtype,Theme::get_default()->get_icon(E->get(),fromtype)); + + } + + } + { + names.clear(); + Theme::get_default()->get_stylebox_list(fromtype,&names); + for(List<StringName>::Element *E=names.front();E;E=E->next()) { + theme->set_stylebox(E->get(),fromtype,Theme::get_default()->get_stylebox(E->get(),fromtype)); + + } + + } + { + names.clear(); + Theme::get_default()->get_font_list(fromtype,&names); + for(List<StringName>::Element *E=names.front();E;E=E->next()) { + theme->set_font(E->get(),fromtype,Theme::get_default()->get_font(E->get(),fromtype)); + + } + } + { + names.clear(); + Theme::get_default()->get_color_list(fromtype,&names); + for(List<StringName>::Element *E=names.front();E;E=E->next()) { + theme->set_color(E->get(),fromtype,Theme::get_default()->get_color(E->get(),fromtype)); + + } + } + { + names.clear(); + Theme::get_default()->get_constant_list(fromtype,&names); + for(List<StringName>::Element *E=names.front();E;E=E->next()) { + theme->set_constant(E->get(),fromtype,Theme::get_default()->get_constant(E->get(),fromtype)); + + } + } + } break; + case POPUP_REMOVE: { + switch(type_select->get_selected()) { + + case 0: theme->clear_icon(name_edit->get_text(),type_edit->get_text()); break; + case 1: theme->clear_stylebox(name_edit->get_text(),type_edit->get_text()); break; + case 2: theme->clear_font(name_edit->get_text(),type_edit->get_text()); break; + case 3: theme->clear_color(name_edit->get_text(),type_edit->get_text()); break; + case 4: theme->clear_constant(name_edit->get_text(),type_edit->get_text()); break; + } + + + } break; + } + +} + +void ThemeEditor::_theme_menu_cbk(int p_option) { + + + if (p_option==POPUP_CREATE_TEMPLATE) { + + file_dialog->set_mode(FileDialog::MODE_SAVE_FILE); + file_dialog->set_current_path("custom.theme"); + file_dialog->popup_centered_ratio(); + return; + } + + Ref<Theme> base_theme; + + type_select->show(); + type_select_label->show(); + name_select_label->show(); + name_edit->show(); + name_menu->show(); + + + if (p_option==POPUP_ADD) {//add + + add_del_dialog->set_title("Add Item"); + add_del_dialog->get_ok()->set_text("Add"); + add_del_dialog->popup_centered(Size2(490,85)); + + base_theme=Theme::get_default(); + + } else if (p_option==POPUP_CLASS_ADD) {//add + + add_del_dialog->set_title("Add All Items"); + add_del_dialog->get_ok()->set_text("Add All"); + add_del_dialog->popup_centered(Size2(240,85)); + + base_theme=Theme::get_default(); + + type_select->hide(); + name_select_label->hide(); + type_select_label->hide(); + name_edit->hide(); + name_menu->hide(); + + } else if (p_option==POPUP_REMOVE) { + + add_del_dialog->set_title("Remove Item"); + add_del_dialog->get_ok()->set_text("Remove"); + add_del_dialog->popup_centered(Size2(490,85)); + + base_theme=theme; + + } + popup_mode=p_option; + + ERR_FAIL_COND( theme.is_null() ); + + List<StringName> types; + base_theme->get_type_list(&types); + type_menu->get_popup()->clear();; + + if (p_option==0 || p_option==1) {//add + + List<StringName> new_types; + theme->get_type_list(&new_types); + //uh kind of sucks + for(List<StringName>::Element *F=new_types.front();F;F=F->next()) { + + bool found=false; + for(List<StringName>::Element *E=types.front();E;E=E->next()) { + + if (E->get()==F->get()) { + found=true; + break; + } + } + + if (!found) + types.push_back(F->get()); + } + } + + types.sort(); + + for(List<StringName>::Element *E=types.front();E;E=E->next()) { + + type_menu->get_popup()->add_item( E->get() ); + } + +} + +void ThemeEditor::_bind_methods() { + + ObjectTypeDB::bind_method("_type_menu_cbk",&ThemeEditor::_type_menu_cbk); + ObjectTypeDB::bind_method("_name_menu_about_to_show",&ThemeEditor::_name_menu_about_to_show); + ObjectTypeDB::bind_method("_name_menu_cbk",&ThemeEditor::_name_menu_cbk); + ObjectTypeDB::bind_method("_theme_menu_cbk",&ThemeEditor::_theme_menu_cbk); + ObjectTypeDB::bind_method("_dialog_cbk",&ThemeEditor::_dialog_cbk); + ObjectTypeDB::bind_method("_save_template_cbk",&ThemeEditor::_save_template_cbk); +} + +ThemeEditor::ThemeEditor() { + + Panel *main_panel = memnew( Panel ); + add_child(main_panel); + main_panel->set_area_as_parent_rect(); + panel = memnew( Panel ); + + main_panel->add_child(panel); + panel->set_area_as_parent_rect(); + panel->set_margin( MARGIN_TOP,20 ); + + test_button = memnew( Button ); + test_button->set_pos(Point2(25,25)); + test_button->set_text("Button"); + panel->add_child(test_button); + + test_label = memnew( Label ); + test_label->set_pos(Point2(25,75)); + test_label->set_text("Label"); + panel->add_child(test_label); + + test_menu_button = memnew( MenuButton ); + test_menu_button->set_pos(Point2(25,125)); + test_menu_button->set_text("Menu Button"); + test_menu_button->get_popup()->add_item("Item"); + test_menu_button->get_popup()->add_separator(); + test_menu_button->get_popup()->add_check_item("Check Item"); + test_menu_button->get_popup()->add_check_item("Checked Item"); + test_menu_button->get_popup()->set_item_checked(2,true); + panel->add_child(test_menu_button); + + test_option_button = memnew( OptionButton ); + test_option_button->set_pos(Point2(25,175)); + test_option_button->add_item("OptionButton"); + test_option_button->add_separator(); + test_option_button->add_item("Has"); + test_option_button->add_item("Many"); + test_option_button->add_item("Options"); + panel->add_child(test_option_button); + + test_h_scroll = memnew( HScrollBar ); + test_h_scroll->set_pos( Point2( 25, 225 ) ); + test_h_scroll->set_size( Point2( 150, 5 ) ); + panel->add_child(test_h_scroll); + + line_edit = memnew( LineEdit ); + line_edit->set_pos( Point2( 25, 275 ) ); + line_edit->set_size( Point2( 150, 5 ) ); + line_edit->set_text("Line Edit"); + panel->add_child(line_edit); + + test_v_scroll = memnew( VScrollBar ); + test_v_scroll->set_pos( Point2( 200, 25 ) ); + test_v_scroll->set_size( Point2( 5, 150 ) ); + panel->add_child(test_v_scroll); + + test_tree = memnew(Tree); + test_tree->set_pos( Point2( 300, 25 ) ); + test_tree->set_size( Point2( 200, 200 ) ); + panel->add_child(test_tree); + + + TreeItem *item = test_tree->create_item(); + item->set_editable(0,true); + item->set_text(0,"root"); + item = test_tree->create_item( test_tree->get_root() ); + item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + item->set_editable(0,true); + item->set_text(0,"check"); + item = test_tree->create_item( test_tree->get_root() ); + item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE); + item->set_editable(0,true); + item->set_range_config(0,0,20,0.1); + item->set_range(0,2); + item = test_tree->create_item( test_tree->get_root() ); + item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE); + item->set_editable(0,true); + item->set_text(0,"Have,Many,Several,Options!"); + item->set_range(0,2); + + Button *fd_button= memnew( Button ); + fd_button->set_pos(Point2(300,275)); + fd_button->set_text("Open File Dialog"); + panel->add_child(fd_button); + + test_file_dialog = memnew( FileDialog ); + panel->add_child(test_file_dialog); + + fd_button->connect("pressed", this,"_open_file_dialog"); + + theme_menu = memnew( MenuButton ); + theme_menu->set_text("Theme.."); + theme_menu->get_popup()->add_item("Add Item",POPUP_ADD); + theme_menu->get_popup()->add_item("Add Class Items",POPUP_CLASS_ADD); + theme_menu->get_popup()->add_item("Remove Item",POPUP_REMOVE); + theme_menu->get_popup()->add_separator(); + theme_menu->get_popup()->add_item("Create Template",POPUP_CREATE_TEMPLATE); + main_panel->add_child(theme_menu); + theme_menu->set_pos(Point2(5,5)); + theme_menu->get_popup()->connect("item_pressed", this,"_theme_menu_cbk"); + + add_del_dialog = memnew(ConfirmationDialog); + add_del_dialog->hide(); + main_panel->add_child(add_del_dialog); + + + Label *l = memnew( Label ); + l->set_pos( Point2(5,5) ); + l->set_text("Type:"); + add_del_dialog->add_child(l); + dtype_select_label=l; + + + type_edit = memnew( LineEdit ); + type_edit->set_pos(Point2(5,25)); + type_edit->set_size(Point2(150,5)); + add_del_dialog->add_child(type_edit); + type_menu = memnew( MenuButton ); + type_menu->set_pos(Point2(160,25)); + type_menu->set_size(Point2(30,5)); + type_menu->set_text(".."); + add_del_dialog->add_child(type_menu); + + type_menu->get_popup()->connect("item_pressed", this,"_type_menu_cbk"); + + l = memnew( Label ); + l->set_pos( Point2(200,5) ); + l->set_text("Name:"); + add_del_dialog->add_child(l); + name_select_label=l; + + name_edit = memnew( LineEdit ); + name_edit->set_pos(Point2(200,25)); + name_edit->set_size(Point2(150,5)); + add_del_dialog->add_child(name_edit); + name_menu = memnew( MenuButton ); + name_menu->set_pos(Point2(360,25)); + name_menu->set_size(Point2(30,5)); + name_menu->set_text(".."); + + add_del_dialog->add_child(name_menu); + + name_menu->get_popup()->connect("about_to_show", this,"_name_menu_about_to_show"); + name_menu->get_popup()->connect("item_pressed", this,"_name_menu_cbk"); + + type_select_label= memnew( Label ); + type_select_label->set_pos( Point2(400,5) ); + type_select_label->set_text("Data Type:"); + add_del_dialog->add_child(type_select_label); + + type_select = memnew( OptionButton ); + type_select->add_item("Icon"); + type_select->add_item("Style"); + type_select->add_item("Font"); + type_select->add_item("Color"); + type_select->add_item("Constant"); + type_select->set_pos( Point2( 400,25 ) ); + type_select->set_size( Point2( 80,5 ) ); + + + add_del_dialog->add_child(type_select); + + add_del_dialog->get_ok()->connect("pressed", this,"_dialog_cbk"); + + + file_dialog = memnew( FileDialog ); + file_dialog->add_filter("*.theme ; Theme File"); + add_child(file_dialog); + file_dialog->connect("file_selected",this,"_save_template_cbk"); + + //MenuButton *name_menu; + //LineEdit *name_edit; + +} + +void ThemeEditorPlugin::edit(Object *p_node) { + + if (p_node && p_node->cast_to<Theme>()) { + theme_editor->edit( p_node->cast_to<Theme>() ); + theme_editor->show(); + } else + theme_editor->hide(); +} + +bool ThemeEditorPlugin::handles(Object *p_node) const{ + + return p_node->is_type("Theme"); +} + +void ThemeEditorPlugin::make_visible(bool p_visible){ + + if (p_visible) + theme_editor->show(); + else + theme_editor->hide(); +} + +ThemeEditorPlugin::ThemeEditorPlugin(EditorNode *p_node) { + + theme_editor = memnew( ThemeEditor ); + + p_node->get_viewport()->add_child(theme_editor); + theme_editor->set_area_as_parent_rect(); + theme_editor->hide(); + +} + diff --git a/tools/editor/plugins/theme_editor_plugin.h b/tools/editor/plugins/theme_editor_plugin.h new file mode 100644 index 000000000..a52173095 --- /dev/null +++ b/tools/editor/plugins/theme_editor_plugin.h @@ -0,0 +1,122 @@ +/*************************************************************************/ +/* theme_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef THEME_EDITOR_PLUGIN_H +#define THEME_EDITOR_PLUGIN_H + +#include "scene/resources/theme.h" +#include "scene/gui/texture_frame.h" +#include "scene/gui/option_button.h" +#include "scene/gui/file_dialog.h" + +#include "tools/editor/editor_node.h" + + + +class ThemeEditor : public Control { + + OBJ_TYPE( ThemeEditor, Control ); + + Panel *panel; + + Ref<Theme> theme; + + FileDialog *file_dialog; + + Button *test_button; + Label *test_label; + MenuButton *test_menu_button; + OptionButton *test_option_button; + HScrollBar *test_h_scroll; + VScrollBar *test_v_scroll; + LineEdit *line_edit; + FileDialog *test_file_dialog; + + + MenuButton *theme_menu; + ConfirmationDialog *add_del_dialog; + MenuButton *type_menu; + LineEdit *type_edit; + MenuButton *name_menu; + LineEdit *name_edit; + OptionButton *type_select; + Label * type_select_label; + Label * name_select_label; + Label * dtype_select_label; + + enum PopupMode { + POPUP_ADD, + POPUP_CLASS_ADD, + POPUP_REMOVE, + POPUP_CREATE_TEMPLATE + }; + + int popup_mode; + + Tree *test_tree; + + void _save_template_cbk(String fname); + void _dialog_cbk(); + void _type_menu_cbk(int p_option); + void _name_menu_about_to_show(); + void _name_menu_cbk(int p_option); + void _theme_menu_cbk(int p_option); + void _open_file_dialog(); + +protected: + static void _bind_methods(); +public: + + void edit(const Ref<Theme>& p_theme); + + ThemeEditor(); +}; + + + +class ThemeEditorPlugin : public EditorPlugin { + + OBJ_TYPE( ThemeEditorPlugin, EditorPlugin ); + + ThemeEditor *theme_editor; + EditorNode *editor; + +public: + + virtual String get_name() const { return "Theme"; } + 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); + + ThemeEditorPlugin(EditorNode *p_node); + +}; + + +#endif // THEME_EDITOR_PLUGIN_H diff --git a/tools/editor/plugins/tile_map_editor_plugin.cpp b/tools/editor/plugins/tile_map_editor_plugin.cpp new file mode 100644 index 000000000..64ba59abb --- /dev/null +++ b/tools/editor/plugins/tile_map_editor_plugin.cpp @@ -0,0 +1,688 @@ +/*************************************************************************/ +/* tile_map_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "tile_map_editor_plugin.h" +#include "tools/editor/editor_node.h" +#include "os/keyboard.h" + +#include "canvas_item_editor_plugin.h" +#include "os/file_access.h" +#include "tools/editor/editor_settings.h" +#include "os/input.h" + + +void TileMapEditor::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_READY: { + + pane_drag->connect("dragged", this,"_pane_drag"); + mirror_x->set_icon( get_icon("MirrorX","EditorIcons")); + mirror_y->set_icon( get_icon("MirrorY","EditorIcons")); + + } break; + } + +} + +void TileMapEditor::_canvas_mouse_enter() { + + mouse_over=true; + canvas_item_editor->update(); + + +} + +void TileMapEditor::_canvas_mouse_exit() { + + mouse_over=false; + canvas_item_editor->update(); + +} + +int TileMapEditor::get_selected_tile() const { + + TreeItem *item = palette->get_selected(); + if (!item) + return TileMap::INVALID_CELL; + return item->get_metadata(0); +} + +void TileMapEditor::_set_cell(const Point2i& p_pos,int p_value,bool p_flip_h, bool p_flip_v) { + + 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); + 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) + return; //check that it's actually different + + node->set_cell(p_pos.x,p_pos.y,p_value,p_flip_h,p_flip_v); + +#if 0 +//not yet + undo_redo->create_action("Set Tile"); + 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->commit_action(); +#endif + +} + +void TileMapEditor::_update_palette() { + + if (!node) + return; + + palette->clear();; + + Ref<TileSet> tileset=node->get_tileset(); + if (!tileset.is_valid()) + return; + + + TreeItem *root = palette->create_item(); + palette->set_hide_root(true); + List<int> tiles; + tileset->get_tile_list(&tiles); + + for(List<int>::Element *E=tiles.front();E;E=E->next()) { + + TreeItem *tile = palette->create_item(root); + + tile->set_icon_max_width(0,64); + Ref<Texture> tex = tileset->tile_get_texture(E->get()); + if (tex.is_valid()) { + tile->set_icon(0,tex); + Rect2 region = tileset->tile_get_region(E->get()); + if (region!=Rect2()) + tile->set_icon_region(0,region); + + } else if (tileset->tile_get_name(E->get())!="") + tile->set_text(0,tileset->tile_get_name(E->get())); + else + tile->set_text(0,"#"+itos(E->get())); + + tile->set_metadata(0,E->get()); + + } +} + +void TileMapEditor::_node_removed(Node *p_node) { + + if(p_node==node) { + node=NULL; + hide(); + } + +} +void TileMapEditor::_menu_option(int p_option) { + + switch(p_option) { + + + } +} + +struct _TileMapEditorCopyData { + Point2i pos; + int cell; + bool flip_h; + bool flip_v; +}; + +bool TileMapEditor::forward_input_event(const InputEvent& p_event) { + + if (!node || !node->get_tileset().is_valid()) + return false; + + Matrix32 xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * node->get_global_transform(); + Matrix32 xform_inv = xform.affine_inverse(); + Vector2 snap = Vector2(1,1)*node->get_cell_size(); + + + switch(p_event.type) { + + case InputEvent::MOUSE_BUTTON: { + + const InputEventMouseButton &mb=p_event.mouse_button; + + if (mb.button_index==BUTTON_LEFT) { + + + if (mb.pressed && tool==TOOL_DUPLICATING) { + + + List<_TileMapEditorCopyData> dupdata; + Point2 ofs = over_tile-selection.pos; + + for(int i=selection.pos.y;i<=selection.pos.y+selection.size.y;i++) { + + for(int j=selection.pos.x;j<=selection.pos.x+selection.size.x;j++) { + + _TileMapEditorCopyData tcd; + tcd.pos=Point2i(j,i); + 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); + dupdata.push_back(tcd); + + + } + } + + 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); + } + + tool=TOOL_NONE; + canvas_item_editor->update(); + selection.pos=over_tile; + + } else if (mb.pressed && tool==TOOL_NONE) { + + if (Input::get_singleton()->is_key_pressed(KEY_SPACE)) + return false; //drag + if (mb.mod.shift) { + + tool=TOOL_SELECTING; + selection_begin =(xform_inv.xform(Point2(mb.x,mb.y))/snap).floor(); + selection.pos=selection_begin; + selection.size=Point2(0,0); + selection_active=true; + canvas_item_editor->update(); + return true; + + } else { + int id = get_selected_tile(); + if (id!=TileMap::INVALID_CELL) { + tool=TOOL_PAINTING; + Point2i local =(xform_inv.xform(Point2(mb.x,mb.y))/snap).floor(); + paint_undo.clear(); + CellOp op; + op.idx = node->get_cell(local.x,local.y); + if (op.idx>=0) { + if (node->is_cell_x_flipped(local.x,local.y)) + op.xf=true; + if (node->is_cell_y_flipped(local.x,local.y)) + op.yf=true; + } + paint_undo[local]=op; + node->set_cell(local.x,local.y,id,mirror_x->is_pressed(),mirror_y->is_pressed()); + return true; + } + } + } else { + + if (tool==TOOL_PAINTING || tool == TOOL_SELECTING) { + + if (tool==TOOL_PAINTING) { + + if (paint_undo.size()) { + undo_redo->create_action("Paint TileMap"); + 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->commit_action(); + } + } + tool=TOOL_NONE; + return true; + } + } + } + + if (mb.button_index==BUTTON_RIGHT) { + + if (mb.pressed && tool==TOOL_DUPLICATING) { + + tool=TOOL_NONE; + canvas_item_editor->update(); + } else if (mb.pressed && tool==TOOL_NONE) { + + tool=TOOL_ERASING; + Point2i local =(xform_inv.xform(Point2(mb.x,mb.y))/snap).floor(); + _set_cell(local,TileMap::INVALID_CELL); + return true; + } else { + + if (tool==TOOL_ERASING) { + + tool=TOOL_NONE; + return true; + } + } + } + + } break; + case InputEvent::MOUSE_MOTION: { + + const InputEventMouseMotion &mm=p_event.mouse_motion; + + Point2i new_over_tile = (xform_inv.xform(Point2(mm.x,mm.y))/snap).floor(); + if (new_over_tile!=over_tile) { + + over_tile=new_over_tile; + canvas_item_editor->update(); + } + + + + if (tool==TOOL_PAINTING) { + + int id = get_selected_tile(); + if (id!=TileMap::INVALID_CELL) { + + if (!paint_undo.has(over_tile)) { + + CellOp op; + op.idx = node->get_cell(over_tile.x,over_tile.y); + if (op.idx>=0) { + if (node->is_cell_x_flipped(over_tile.x,over_tile.y)) + op.xf=true; + if (node->is_cell_y_flipped(over_tile.x,over_tile.y)) + op.yf=true; + } + paint_undo[over_tile]=op; + } + node->set_cell(over_tile.x,over_tile.y,id,mirror_x->is_pressed(),mirror_y->is_pressed()); + + return true; + } + } + + if (tool==TOOL_SELECTING) { + + Point2i begin=selection_begin; + Point2i end =over_tile; + + if (begin.x > end.x) { + + SWAP( begin.x, end.x); + } + if (begin.y > end.y) { + + SWAP( begin.y, end.y); + } + + selection.pos=begin; + selection.size=end-begin; + canvas_item_editor->update(); + + return true; + + } + if (tool==TOOL_ERASING) { + Point2i local =over_tile; + _set_cell(local,TileMap::INVALID_CELL); + return true; + } + + + } break; + case InputEvent::KEY: { + + const InputEventKey &k = p_event.key; + if (!node) + break; + + if (k.pressed && k.scancode==KEY_DELETE && selection_active && tool==TOOL_NONE) { + + for(int i=selection.pos.y;i<=selection.pos.y+selection.size.y;i++) { + + for(int j=selection.pos.x;j<=selection.pos.x+selection.size.x;j++) { + + + _set_cell(Point2i(j,i),TileMap::INVALID_CELL); + } + } + + selection_active=false; + canvas_item_editor->update(); + return true; + } + + if (mouse_over && k.pressed && k.scancode==KEY_A && tool==TOOL_NONE) { + + /*int cell = node->get_cell(over_tile.x,over_tile.y); + if (cell!=TileMap::INVALID_CELL) { + bool flip_h = node->is_cell_x_flipped(over_tile.x,over_tile.y); + bool flip_v = node->is_cell_y_flipped(over_tile.x,over_tile.y); + _set_cell(over_tile,cell,!flip_h,flip_v); + }*/ + + mirror_x->set_pressed( ! mirror_x->is_pressed() ); + canvas_item_editor->update(); + return true; + } + if (mouse_over && k.pressed && k.scancode==KEY_S && tool==TOOL_NONE) { + + + /* + int cell = node->get_cell(over_tile.x,over_tile.y); + if (cell!=TileMap::INVALID_CELL) { + + bool flip_h = node->is_cell_x_flipped(over_tile.x,over_tile.y); + bool flip_v = node->is_cell_y_flipped(over_tile.x,over_tile.y); + _set_cell(over_tile,cell,flip_h,!flip_v); + }*/ + + mirror_y->set_pressed( ! mirror_y->is_pressed() ); + canvas_item_editor->update(); + return true; + } + + if (mouse_over && selection_active && k.pressed && k.mod.command && k.scancode==KEY_D && tool==TOOL_NONE) { + + tool=TOOL_DUPLICATING; + canvas_item_editor->update(); + return true; + } + + + + } break; + } + + return false; +} +void TileMapEditor::_canvas_draw() { + + if (!node) + return; + + int cell_size=node->get_cell_size(); + + Matrix32 xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * node->get_global_transform(); + Matrix32 xform_inv = xform.affine_inverse(); + + + Size2 screen_size=canvas_item_editor->get_size(); + Rect2 aabb; + aabb.pos=xform_inv.xform(Vector2()); + aabb.expand_to(xform_inv.xform(Vector2(0,screen_size.height))); + aabb.expand_to(xform_inv.xform(Vector2(screen_size.width,0))); + aabb.expand_to(xform_inv.xform(screen_size)); + Rect2i si=aabb; + + for(int i=(si.pos.x/cell_size)-1;i<=(si.pos.x+si.size.x)/cell_size;i++) { + + int ofs = i*cell_size; + + Color col=i==0?Color(1,0.8,0.2,0.5):Color(1,0.3,0.1,0.2); + canvas_item_editor->draw_line(xform.xform(Point2(ofs,si.pos.y)),xform.xform(Point2(ofs,si.pos.y+si.size.y)),col,1); + + } + + for(int i=(si.pos.y/cell_size)-1;i<=(si.pos.y+si.size.y)/cell_size;i++) { + + int ofs = i*cell_size; + Color col=i==0?Color(1,0.8,0.2,0.5):Color(1,0.3,0.1,0.2); + canvas_item_editor->draw_line(xform.xform(Point2(si.pos.x,ofs)),xform.xform(Point2(si.pos.x+si.size.x,ofs)),col,1); + } + + + if (selection_active) { + + Vector<Vector2> points; + points.push_back( xform.xform( selection.pos * cell_size) ); + points.push_back( xform.xform( (selection.pos+Point2(selection.size.x+1,0)) * cell_size) ); + points.push_back( xform.xform( (selection.pos+Point2(selection.size.x+1,selection.size.y+1)) * cell_size) ); + points.push_back( xform.xform( (selection.pos+Point2(0,selection.size.y+1)) * cell_size) ); + Color col=Color(0.2,0.8,1,0.4); + + canvas_item_editor->draw_colored_polygon(points,col); + } + + + if (mouse_over){ + + const Vector2 endpoints[4]={ + + xform.xform( over_tile * cell_size) , + xform.xform( (over_tile+Point2(1,0)) * cell_size) , + xform.xform( (over_tile+Point2(1,1)) * cell_size) , + xform.xform( (over_tile+Point2(0,1)) * cell_size) , + + + }; + Color col; + if (node->get_cell(over_tile.x,over_tile.y)!=TileMap::INVALID_CELL) + col=Color(0.2,0.8,1.0,0.8); + else + col=Color(1.0,0.4,0.2,0.8); + + for(int i=0;i<4;i++) + canvas_item_editor->draw_line(endpoints[i],endpoints[(i+1)%4],col,2); + + + + if (tool==TOOL_DUPLICATING) { + + Rect2i duplicate=selection; + duplicate.pos=over_tile; + + + Vector<Vector2> points; + points.push_back( xform.xform( duplicate.pos * cell_size) ); + points.push_back( xform.xform( (duplicate.pos+Point2(duplicate.size.x+1,0)) * cell_size) ); + points.push_back( xform.xform( (duplicate.pos+Point2(duplicate.size.x+1,duplicate.size.y+1)) * cell_size) ); + points.push_back( xform.xform( (duplicate.pos+Point2(0,duplicate.size.y+1)) * cell_size) ); + Color col=Color(0.2,1.0,0.8,0.4); + + canvas_item_editor->draw_colored_polygon(points,col); + + } else { + + Ref<TileSet> ts = node->get_tileset(); + + + if (ts.is_valid()) { + + int st = get_selected_tile(); + if (ts->has_tile(st)) { + + Ref<Texture> t = ts->tile_get_texture(st); + if (t.is_valid()) { + Rect2 r = ts->tile_get_region(st); + Size2 sc = (endpoints[2]-endpoints[0])/cell_size; + if (mirror_x->is_pressed()) + sc.x*=-1.0; + if (mirror_y->is_pressed()) + sc.y*=-1.0; + if (r==Rect2()) { + + canvas_item_editor->draw_texture_rect(t,Rect2(endpoints[0],t->get_size()*sc),false,Color(1,1,1,0.5)); + } else { + + canvas_item_editor->draw_texture_rect_region(t,Rect2(endpoints[0],r.get_size()*sc),r,Color(1,1,1,0.5)); + } + } + } + } + + } + } + + + +} + + + +void TileMapEditor::edit(Node *p_tile_map) { + + if (!canvas_item_editor) { + canvas_item_editor=CanvasItemEditor::get_singleton()->get_viewport_control(); + } + + if (p_tile_map) { + + node=p_tile_map->cast_to<TileMap>(); + if (!canvas_item_editor->is_connected("draw",this,"_canvas_draw")) + canvas_item_editor->connect("draw",this,"_canvas_draw"); + if (!canvas_item_editor->is_connected("mouse_enter",this,"_canvas_mouse_enter")) + canvas_item_editor->connect("mouse_enter",this,"_canvas_mouse_enter"); + if (!canvas_item_editor->is_connected("mouse_exit",this,"_canvas_mouse_exit")) + canvas_item_editor->connect("mouse_exit",this,"_canvas_mouse_exit"); + + _update_palette(); + + } else { + node=NULL; + + if (canvas_item_editor->is_connected("draw",this,"_canvas_draw")) + canvas_item_editor->disconnect("draw",this,"_canvas_draw"); + if (canvas_item_editor->is_connected("mouse_enter",this,"_canvas_mouse_enter")) + canvas_item_editor->disconnect("mouse_enter",this,"_canvas_mouse_enter"); + if (canvas_item_editor->is_connected("mouse_exit",this,"_canvas_mouse_exit")) + canvas_item_editor->disconnect("mouse_exit",this,"_canvas_mouse_exit"); + + _update_palette(); + } + + +} + +void TileMapEditor::_pane_drag(const Point2& p_to) { + + int x = theme_panel->get_margin(MARGIN_RIGHT); + + x+=p_to.x; + if (x<10) + x=10; + if (x>300) + x=300; + theme_panel->set_margin(MARGIN_RIGHT,x); +} + +void TileMapEditor::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("_menu_option"),&TileMapEditor::_menu_option); + ObjectTypeDB::bind_method(_MD("_canvas_draw"),&TileMapEditor::_canvas_draw); + ObjectTypeDB::bind_method(_MD("_pane_drag"),&TileMapEditor::_pane_drag); + ObjectTypeDB::bind_method(_MD("_canvas_mouse_enter"),&TileMapEditor::_canvas_mouse_enter); + ObjectTypeDB::bind_method(_MD("_canvas_mouse_exit"),&TileMapEditor::_canvas_mouse_exit); + +} + +TileMapEditor::TileMapEditor(EditorNode *p_editor) { + + canvas_item_editor=NULL; + editor=p_editor; + undo_redo = editor->get_undo_redo(); + + theme_panel = memnew( Panel ); + theme_panel->set_anchor(MARGIN_BOTTOM,ANCHOR_END); + theme_panel->set_begin( Point2(0,20)); + theme_panel->set_end( Point2(100,0) ); + p_editor->get_viewport()->add_child(theme_panel); + theme_panel->hide(); + + palette = memnew( Tree ); + palette->set_area_as_parent_rect(4); + palette->set_margin(MARGIN_TOP,25);; + theme_panel->add_child(palette); + + pane_drag = memnew( PaneDrag ) ; + pane_drag->set_anchor(MARGIN_LEFT,ANCHOR_END); + pane_drag->set_begin(Point2(16,4)); + theme_panel->add_child(pane_drag); + + add_child( memnew( VSeparator )); + + mirror_x = memnew( ToolButton ); + mirror_x->set_toggle_mode(true); + mirror_x->set_tooltip("Mirror X (A)"); + mirror_x->set_focus_mode(FOCUS_NONE); + 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); + add_child(mirror_y); + + + tool=TOOL_NONE; + selection_active=false; + mouse_over=false; +} + + +void TileMapEditorPlugin::edit(Object *p_object) { + + tile_map_editor->edit(p_object->cast_to<Node>()); +} + +bool TileMapEditorPlugin::handles(Object *p_object) const { + + return p_object->is_type("TileMap"); +} + +void TileMapEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + tile_map_editor->show(); + tile_map_editor->theme_panel->show(); + + } else { + + tile_map_editor->hide(); + tile_map_editor->theme_panel->hide(); + tile_map_editor->edit(NULL); + } + +} + +TileMapEditorPlugin::TileMapEditorPlugin(EditorNode *p_node) { + + editor=p_node; + tile_map_editor = memnew( TileMapEditor(p_node) ); + CanvasItemEditor::get_singleton()->add_control_to_menu_panel(tile_map_editor); + + tile_map_editor->hide(); + + + + +} + + +TileMapEditorPlugin::~TileMapEditorPlugin() +{ +} + diff --git a/tools/editor/plugins/tile_map_editor_plugin.h b/tools/editor/plugins/tile_map_editor_plugin.h new file mode 100644 index 000000000..cd037113b --- /dev/null +++ b/tools/editor/plugins/tile_map_editor_plugin.h @@ -0,0 +1,138 @@ +/*************************************************************************/ +/* tile_map_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef TILE_MAP_EDITOR_PLUGIN_H +#define TILE_MAP_EDITOR_PLUGIN_H + +#include "tools/editor/editor_plugin.h" +#include "tools/editor/editor_node.h" +#include "scene/2d/tile_map.h" +#include "scene/gui/tool_button.h" +#include "scene/gui/button_group.h" +#include "tools/editor/pane_drag.h" +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ +class CanvasItemEditor; + +class TileMapEditor : public HBoxContainer { + + OBJ_TYPE(TileMapEditor, BoxContainer ); + + UndoRedo *undo_redo; + + enum Tool { + + TOOL_NONE, + TOOL_PAINTING, + TOOL_SELECTING, + TOOL_ERASING, + TOOL_DUPLICATING + }; + + Tool tool; + Control *canvas_item_editor; + + Tree *palette; + EditorNode *editor; + Panel *panel; + TileMap *node; + MenuButton *options; + PaneDrag *pane_drag; + + bool selection_active; + Point2i selection_begin; + Rect2i selection; + Point2i over_tile; + bool mouse_over; + + Label *mirror_label; + ToolButton *mirror_x; + ToolButton *mirror_y; + + + struct CellOp { + int idx; + bool xf; + bool yf; + CellOp() { idx=-1; xf=false; yf=false; } + }; + + Map<Point2i,CellOp> paint_undo; + + int get_selected_tile() const; + + void _update_palette(); + void _pane_drag(const Point2& p_to); + 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); + + void _canvas_mouse_enter(); + void _canvas_mouse_exit(); + + +friend class TileMapEditorPlugin; + Panel *theme_panel; +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_tile_map); + TileMapEditor(EditorNode *p_editor); +}; + +class TileMapEditorPlugin : public EditorPlugin { + + OBJ_TYPE( TileMapEditorPlugin, EditorPlugin ); + + TileMapEditor *tile_map_editor; + EditorNode *editor; + +public: + + virtual bool forward_input_event(const InputEvent& p_event) { return tile_map_editor->forward_input_event(p_event); } + + virtual String get_name() const { return "TileMap"; } + 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); + + TileMapEditorPlugin(EditorNode *p_node); + ~TileMapEditorPlugin(); + +}; + + +#endif // TILE_MAP_EDITOR_PLUGIN_H diff --git a/tools/editor/plugins/tile_set_editor_plugin.cpp b/tools/editor/plugins/tile_set_editor_plugin.cpp new file mode 100644 index 000000000..203233c4a --- /dev/null +++ b/tools/editor/plugins/tile_set_editor_plugin.cpp @@ -0,0 +1,231 @@ +/*************************************************************************/ +/* tile_set_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "tile_set_editor_plugin.h" +#include "scene/2d/sprite.h" +#include "scene/2d/physics_body_2d.h" +void TileSetEditor::edit(const Ref<TileSet>& p_tileset) { + + tileset=p_tileset; +} + +void TileSetEditor::_import_scene(Node *scene, Ref<TileSet> p_library, bool p_merge) { + + if (!p_merge) + p_library->clear(); + + for(int i=0;i<scene->get_child_count();i++) { + + + Node *child = scene->get_child(i); + + if (!child->cast_to<Sprite>()) { + if (child->get_child_count()>0) { + child=child->get_child(0); + if (!child->cast_to<Sprite>()) { + continue; + } + + } else + continue; + + + } + + Sprite *mi = child->cast_to<Sprite>(); + Ref<Texture> texture=mi->get_texture(); + if (texture.is_null()) + continue; + + int id = p_library->find_tile_by_name(mi->get_name()); + if (id<0) { + + id=p_library->get_last_unused_tile_id(); + p_library->create_tile(id); + p_library->tile_set_name(id,mi->get_name()); + } + + + p_library->tile_set_texture(id,texture); + if (mi->is_centered()) { + p_library->tile_set_offset(id,texture->get_size()/2); + } + if (mi->is_region()) { + p_library->tile_set_region(id,mi->get_region_rect()); + } + + Vector<Ref<Shape2D> >collisions; + + for(int j=0;j<mi->get_child_count();j++) { + + Node *child2 = mi->get_child(j); + if (!child2->cast_to<StaticBody2D>()) + continue; + StaticBody2D *sb = child2->cast_to<StaticBody2D>(); + if (sb->get_shape_count()==0) + continue; + Ref<Shape2D> collision=sb->get_shape(0); + if (collision.is_valid()) + collisions.push_back(collision); + } + + if (collisions.size()) { + + p_library->tile_set_shapes(id,collisions); + } + + } +} + +void TileSetEditor::_menu_confirm() { + + switch(option) { + + case MENU_OPTION_REMOVE_ITEM: { + + tileset->remove_tile(to_erase); + } break; + case MENU_OPTION_MERGE_FROM_SCENE: + case MENU_OPTION_CREATE_FROM_SCENE: { + + + EditorNode *en = editor; + Node * scene = en->get_edited_scene(); + if (!scene) + break; + + _import_scene(scene,tileset,option==MENU_OPTION_MERGE_FROM_SCENE); + + + } break; + } +} + +void TileSetEditor::_menu_cbk(int p_option) { + + option=p_option; + switch(p_option) { + + case MENU_OPTION_ADD_ITEM: { + + tileset->create_tile(tileset->get_last_unused_tile_id()); + } break; + case MENU_OPTION_REMOVE_ITEM: { + + String p = editor->get_property_editor()->get_selected_path(); + if (p.begins_with("/TileSet") && p.get_slice_count("/")>=2) { + + to_erase = p.get_slice("/",2).to_int(); + cd->set_text("Remove Item "+itos(to_erase)+"?"); + cd->popup_centered(Size2(300,60)); + } + } break; + case MENU_OPTION_CREATE_FROM_SCENE: { + + cd->set_text("Create from scene?"); + cd->popup_centered(Size2(300,60)); + } break; + case MENU_OPTION_MERGE_FROM_SCENE: { + + cd->set_text("Merge from scene?"); + cd->popup_centered(Size2(300,60)); + } break; + } +} + + + +Error TileSetEditor::update_library_file(Node *p_base_scene, Ref<TileSet> ml,bool p_merge) { + + _import_scene(p_base_scene,ml,p_merge); + return OK; + +} + +void TileSetEditor::_bind_methods() { + + ObjectTypeDB::bind_method("_menu_cbk",&TileSetEditor::_menu_cbk); + ObjectTypeDB::bind_method("_menu_confirm",&TileSetEditor::_menu_confirm); +} + +TileSetEditor::TileSetEditor(EditorNode *p_editor) { + + Panel *panel = memnew( Panel ); + panel->set_area_as_parent_rect(); + add_child(panel); + MenuButton * options = memnew( MenuButton ); + panel->add_child(options); + options->set_pos(Point2(1,1)); + options->set_text("Theme"); + options->get_popup()->add_item("Add Item",MENU_OPTION_ADD_ITEM); + options->get_popup()->add_item("Remove Selected Item",MENU_OPTION_REMOVE_ITEM); + options->get_popup()->add_separator(); + options->get_popup()->add_item("Create from Scene",MENU_OPTION_CREATE_FROM_SCENE); + options->get_popup()->add_item("Merge from Scene",MENU_OPTION_MERGE_FROM_SCENE); + options->get_popup()->connect("item_pressed", this,"_menu_cbk"); + editor=p_editor; + cd = memnew(ConfirmationDialog); + add_child(cd); + cd->get_ok()->connect("pressed", this,"_menu_confirm"); + +} + +void TileSetEditorPlugin::edit(Object *p_node) { + + if (p_node && p_node->cast_to<TileSet>()) { + tileset_editor->edit( p_node->cast_to<TileSet>() ); + tileset_editor->show(); + } else + tileset_editor->hide(); +} + +bool TileSetEditorPlugin::handles(Object *p_node) const{ + + return p_node->is_type("TileSet"); +} + +void TileSetEditorPlugin::make_visible(bool p_visible){ + + if (p_visible) + tileset_editor->show(); + else + tileset_editor->hide(); +} + +TileSetEditorPlugin::TileSetEditorPlugin(EditorNode *p_node) { + + tileset_editor = memnew( TileSetEditor(p_node) ); + + p_node->get_viewport()->add_child(tileset_editor); + tileset_editor->set_area_as_parent_rect(); + tileset_editor->set_anchor( MARGIN_RIGHT, Control::ANCHOR_END ); + tileset_editor->set_anchor( MARGIN_BOTTOM, Control::ANCHOR_BEGIN ); + tileset_editor->set_end( Point2(0,22) ); + tileset_editor->hide(); + +} diff --git a/tools/editor/plugins/tile_set_editor_plugin.h b/tools/editor/plugins/tile_set_editor_plugin.h new file mode 100644 index 000000000..2531ec55b --- /dev/null +++ b/tools/editor/plugins/tile_set_editor_plugin.h @@ -0,0 +1,98 @@ +/*************************************************************************/ +/* tile_set_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef TILE_SET_EDITOR_PLUGIN_H +#define TILE_SET_EDITOR_PLUGIN_H + + + +#include "scene/resources/tile_set.h" +#include "tools/editor/editor_node.h" + + +class TileSetEditor : public Control { + + OBJ_TYPE( TileSetEditor, Control ); + + Ref<TileSet> tileset; + + EditorNode *editor; + MenuButton *menu; + ConfirmationDialog *cd; + int to_erase; + + enum { + + MENU_OPTION_ADD_ITEM, + MENU_OPTION_REMOVE_ITEM, + MENU_OPTION_CREATE_FROM_SCENE, + MENU_OPTION_MERGE_FROM_SCENE + }; + + int option; + void _menu_cbk(int p_option); + void _menu_confirm(); + + static void _import_scene(Node *p_scene, Ref<TileSet> p_library, bool p_merge); + + +protected: + static void _bind_methods(); +public: + + void edit(const Ref<TileSet>& p_tileset); + static Error update_library_file(Node *p_base_scene, Ref<TileSet> ml,bool p_merge=true); + + TileSetEditor(EditorNode *p_editor); +}; + + + +class TileSetEditorPlugin : public EditorPlugin { + + OBJ_TYPE( TileSetEditorPlugin, EditorPlugin ); + + TileSetEditor *tileset_editor; + EditorNode *editor; + +public: + + virtual String get_name() const { return "TileSet"; } + 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); + + + + TileSetEditorPlugin(EditorNode *p_node); + +}; + + +#endif // TILE_SET_EDITOR_PLUGIN_H |
