aboutsummaryrefslogtreecommitdiff
path: root/tools/editor/plugins
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--tools/editor/plugins/SCsub7
-rw-r--r--tools/editor/plugins/animation_data_editor_plugin.cpp33
-rw-r--r--tools/editor/plugins/animation_data_editor_plugin.h38
-rw-r--r--tools/editor/plugins/animation_editor_plugin.cpp806
-rw-r--r--tools/editor/plugins/animation_editor_plugin.h123
-rw-r--r--tools/editor/plugins/animation_player_editor_plugin.cpp1157
-rw-r--r--tools/editor/plugins/animation_player_editor_plugin.h166
-rw-r--r--tools/editor/plugins/animation_tree_editor_plugin.cpp1535
-rw-r--r--tools/editor/plugins/animation_tree_editor_plugin.h193
-rw-r--r--tools/editor/plugins/camera_editor_plugin.cpp151
-rw-r--r--tools/editor/plugins/camera_editor_plugin.h79
-rw-r--r--tools/editor/plugins/canvas_item_editor_plugin.cpp2308
-rw-r--r--tools/editor/plugins/canvas_item_editor_plugin.h339
-rw-r--r--tools/editor/plugins/collision_polygon_editor_plugin.cpp479
-rw-r--r--tools/editor/plugins/collision_polygon_editor_plugin.h111
-rw-r--r--tools/editor/plugins/control_editor_plugin.cpp825
-rw-r--r--tools/editor/plugins/control_editor_plugin.h141
-rw-r--r--tools/editor/plugins/cube_grid_theme_editor_plugin.cpp343
-rw-r--r--tools/editor/plugins/cube_grid_theme_editor_plugin.h95
-rw-r--r--tools/editor/plugins/font_editor_plugin.cpp905
-rw-r--r--tools/editor/plugins/font_editor_plugin.h100
-rw-r--r--tools/editor/plugins/item_list_editor_plugin.cpp415
-rw-r--r--tools/editor/plugins/item_list_editor_plugin.h164
-rw-r--r--tools/editor/plugins/multimesh_editor_plugin.cpp470
-rw-r--r--tools/editor/plugins/multimesh_editor_plugin.h107
-rw-r--r--tools/editor/plugins/particles_2d_editor_plugin.cpp198
-rw-r--r--tools/editor/plugins/particles_2d_editor_plugin.h82
-rw-r--r--tools/editor/plugins/particles_editor_plugin.cpp462
-rw-r--r--tools/editor/plugins/particles_editor_plugin.h112
-rw-r--r--tools/editor/plugins/path_2d_editor_plugin.cpp604
-rw-r--r--tools/editor/plugins/path_2d_editor_plugin.h107
-rw-r--r--tools/editor/plugins/path_editor_plugin.cpp598
-rw-r--r--tools/editor/plugins/path_editor_plugin.h99
-rw-r--r--tools/editor/plugins/resource_preloader_editor_plugin.cpp388
-rw-r--r--tools/editor/plugins/resource_preloader_editor_plugin.h102
-rw-r--r--tools/editor/plugins/rich_text_editor_plugin.cpp163
-rw-r--r--tools/editor/plugins/rich_text_editor_plugin.h89
-rw-r--r--tools/editor/plugins/sample_editor_plugin.cpp326
-rw-r--r--tools/editor/plugins/sample_editor_plugin.h90
-rw-r--r--tools/editor/plugins/sample_library_editor_plugin.cpp449
-rw-r--r--tools/editor/plugins/sample_library_editor_plugin.h107
-rw-r--r--tools/editor/plugins/sample_player_editor_plugin.cpp192
-rw-r--r--tools/editor/plugins/sample_player_editor_plugin.h87
-rw-r--r--tools/editor/plugins/script_editor_plugin.cpp1519
-rw-r--r--tools/editor/plugins/script_editor_plugin.h250
-rw-r--r--tools/editor/plugins/shader_editor_plugin.cpp598
-rw-r--r--tools/editor/plugins/shader_editor_plugin.h158
-rw-r--r--tools/editor/plugins/shader_graph_editor_plugin.cpp1109
-rw-r--r--tools/editor/plugins/shader_graph_editor_plugin.h150
-rw-r--r--tools/editor/plugins/spatial_editor_plugin.cpp3311
-rw-r--r--tools/editor/plugins/spatial_editor_plugin.h467
-rw-r--r--tools/editor/plugins/sprite_frames_editor_plugin.cpp483
-rw-r--r--tools/editor/plugins/sprite_frames_editor_plugin.h109
-rw-r--r--tools/editor/plugins/stream_editor_plugin.cpp148
-rw-r--r--tools/editor/plugins/stream_editor_plugin.h83
-rw-r--r--tools/editor/plugins/style_box_editor_plugin.cpp109
-rw-r--r--tools/editor/plugins/style_box_editor_plugin.h82
-rw-r--r--tools/editor/plugins/theme_editor_plugin.cpp696
-rw-r--r--tools/editor/plugins/theme_editor_plugin.h122
-rw-r--r--tools/editor/plugins/tile_map_editor_plugin.cpp688
-rw-r--r--tools/editor/plugins/tile_map_editor_plugin.h138
-rw-r--r--tools/editor/plugins/tile_set_editor_plugin.cpp231
-rw-r--r--tools/editor/plugins/tile_set_editor_plugin.h98
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(&params);
+
+ 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