aboutsummaryrefslogtreecommitdiff
path: root/editor/plugins
diff options
context:
space:
mode:
authorRémi Verschelde2017-03-18 23:45:45 +0100
committerRémi Verschelde2017-03-18 23:45:45 +0100
commit1b0e2b0c39f5fe36adaee8aa1a2eee39534850c0 (patch)
treeac279269dcb840e5629b0f0fad33ccd43b04c439 /editor/plugins
parent9d2c0f6c6e2603fa36fb376f9b0ab7d7d02ff8c8 (diff)
downloadgodot-1b0e2b0c39f5fe36adaee8aa1a2eee39534850c0.tar.gz
godot-1b0e2b0c39f5fe36adaee8aa1a2eee39534850c0.tar.zst
godot-1b0e2b0c39f5fe36adaee8aa1a2eee39534850c0.zip
Refactoring: rename tools/editor/ to editor/
The other subfolders of tools/ had already been moved to either editor/, misc/ or thirdparty/, so the hiding the editor code that deep was no longer meaningful. (Manual redo of 49c065d29ca07040c3fd810026121164ad86b247)
Diffstat (limited to 'editor/plugins')
-rw-r--r--editor/plugins/SCsub5
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp1596
-rw-r--r--editor/plugins/animation_player_editor_plugin.h219
-rw-r--r--editor/plugins/animation_tree_editor_plugin.cpp1536
-rw-r--r--editor/plugins/animation_tree_editor_plugin.h194
-rw-r--r--editor/plugins/baked_light_baker.cpp2724
-rw-r--r--editor/plugins/baked_light_baker.h377
-rw-r--r--editor/plugins/baked_light_baker_cmpxchg.cpp112
-rw-r--r--editor/plugins/baked_light_editor_plugin.cpp375
-rw-r--r--editor/plugins/baked_light_editor_plugin.h120
-rw-r--r--editor/plugins/camera_editor_plugin.cpp151
-rw-r--r--editor/plugins/camera_editor_plugin.h79
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp4002
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h495
-rw-r--r--editor/plugins/collision_polygon_2d_editor_plugin.cpp482
-rw-r--r--editor/plugins/collision_polygon_2d_editor_plugin.h111
-rw-r--r--editor/plugins/collision_polygon_editor_plugin.cpp644
-rw-r--r--editor/plugins/collision_polygon_editor_plugin.h121
-rw-r--r--editor/plugins/collision_shape_2d_editor_plugin.cpp601
-rw-r--r--editor/plugins/collision_shape_2d_editor_plugin.h101
-rw-r--r--editor/plugins/color_ramp_editor_plugin.cpp111
-rw-r--r--editor/plugins/color_ramp_editor_plugin.h62
-rw-r--r--editor/plugins/cube_grid_theme_editor_plugin.cpp356
-rw-r--r--editor/plugins/cube_grid_theme_editor_plugin.h95
-rw-r--r--editor/plugins/editor_preview_plugins.cpp903
-rw-r--r--editor/plugins/editor_preview_plugins.h127
-rw-r--r--editor/plugins/item_list_editor_plugin.cpp381
-rw-r--r--editor/plugins/item_list_editor_plugin.h230
-rw-r--r--editor/plugins/light_occluder_2d_editor_plugin.cpp518
-rw-r--r--editor/plugins/light_occluder_2d_editor_plugin.h115
-rw-r--r--editor/plugins/material_editor_plugin.cpp381
-rw-r--r--editor/plugins/material_editor_plugin.h71
-rw-r--r--editor/plugins/mesh_editor_plugin.cpp243
-rw-r--r--editor/plugins/mesh_editor_plugin.h95
-rw-r--r--editor/plugins/mesh_instance_editor_plugin.cpp306
-rw-r--r--editor/plugins/mesh_instance_editor_plugin.h69
-rw-r--r--editor/plugins/multimesh_editor_plugin.cpp459
-rw-r--r--editor/plugins/multimesh_editor_plugin.h107
-rw-r--r--editor/plugins/navigation_polygon_editor_plugin.cpp566
-rw-r--r--editor/plugins/navigation_polygon_editor_plugin.h118
-rw-r--r--editor/plugins/particles_2d_editor_plugin.cpp199
-rw-r--r--editor/plugins/particles_2d_editor_plugin.h82
-rw-r--r--editor/plugins/particles_editor_plugin.cpp458
-rw-r--r--editor/plugins/particles_editor_plugin.h115
-rw-r--r--editor/plugins/path_2d_editor_plugin.cpp718
-rw-r--r--editor/plugins/path_2d_editor_plugin.h126
-rw-r--r--editor/plugins/path_editor_plugin.cpp601
-rw-r--r--editor/plugins/path_editor_plugin.h99
-rw-r--r--editor/plugins/polygon_2d_editor_plugin.cpp1027
-rw-r--r--editor/plugins/polygon_2d_editor_plugin.h167
-rw-r--r--editor/plugins/resource_preloader_editor_plugin.cpp508
-rw-r--r--editor/plugins/resource_preloader_editor_plugin.h108
-rw-r--r--editor/plugins/rich_text_editor_plugin.cpp163
-rw-r--r--editor/plugins/rich_text_editor_plugin.h91
-rw-r--r--editor/plugins/sample_editor_plugin.cpp450
-rw-r--r--editor/plugins/sample_editor_plugin.h90
-rw-r--r--editor/plugins/sample_library_editor_plugin.cpp543
-rw-r--r--editor/plugins/sample_library_editor_plugin.h107
-rw-r--r--editor/plugins/sample_player_editor_plugin.cpp198
-rw-r--r--editor/plugins/sample_player_editor_plugin.h87
-rw-r--r--editor/plugins/script_editor_plugin.cpp3190
-rw-r--r--editor/plugins/script_editor_plugin.h400
-rw-r--r--editor/plugins/shader_editor_plugin.cpp624
-rw-r--r--editor/plugins/shader_editor_plugin.h157
-rw-r--r--editor/plugins/shader_graph_editor_plugin.cpp2941
-rw-r--r--editor/plugins/shader_graph_editor_plugin.h242
-rw-r--r--editor/plugins/spatial_editor_plugin.cpp4187
-rw-r--r--editor/plugins/spatial_editor_plugin.h573
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.cpp969
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.h138
-rw-r--r--editor/plugins/stream_editor_plugin.cpp148
-rw-r--r--editor/plugins/stream_editor_plugin.h83
-rw-r--r--editor/plugins/style_box_editor_plugin.cpp115
-rw-r--r--editor/plugins/style_box_editor_plugin.h83
-rw-r--r--editor/plugins/texture_editor_plugin.cpp141
-rw-r--r--editor/plugins/texture_editor_plugin.h49
-rw-r--r--editor/plugins/texture_region_editor_plugin.cpp1010
-rw-r--r--editor/plugins/texture_region_editor_plugin.h153
-rw-r--r--editor/plugins/theme_editor_plugin.cpp996
-rw-r--r--editor/plugins/theme_editor_plugin.h125
-rw-r--r--editor/plugins/tile_map_editor_plugin.cpp1595
-rw-r--r--editor/plugins/tile_map_editor_plugin.h207
-rw-r--r--editor/plugins/tile_set_editor_plugin.cpp295
-rw-r--r--editor/plugins/tile_set_editor_plugin.h101
84 files changed, 43817 insertions, 0 deletions
diff --git a/editor/plugins/SCsub b/editor/plugins/SCsub
new file mode 100644
index 000000000..f1fa50148
--- /dev/null
+++ b/editor/plugins/SCsub
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+
+Import('env')
+Export('env')
+env.add_source_files(env.editor_sources, "*.cpp")
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
new file mode 100644
index 000000000..afb13b0e6
--- /dev/null
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -0,0 +1,1596 @@
+/*************************************************************************/
+/* animation_player_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "globals.h"
+#include "io/resource_loader.h"
+#include "io/resource_saver.h"
+#include "os/keyboard.h"
+#include "editor/editor_settings.h"
+#include "editor/animation_editor.h"
+
+void AnimationPlayerEditor::_node_removed(Node *p_node) {
+
+ if (player && player == p_node) {
+ player=NULL;
+
+ set_process(false);
+
+ key_editor->set_animation(Ref<Animation>());
+ key_editor->set_root(NULL);
+ key_editor->show_select_node_warning(true);
+ _update_player();
+ //editor->animation_editor_make_visible(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()) {
+
+ frame->set_max(anim->get_length());
+ }
+ }
+ }
+ frame->set_val(player->get_current_animation_pos());
+ key_editor->set_anim_pos(player->get_current_animation_pos());
+ EditorNode::get_singleton()->get_property_editor()->refresh();
+
+ } else if (last_active) {
+ //need the last frame after it stopped
+
+ frame->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_TREE) {
+
+// 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") );
+ save_anim->set_icon(get_icon("Save", "EditorIcons"));
+ save_anim->get_popup()->connect("item_pressed", this, "_animation_save_menu");
+ remove_anim->set_icon( get_icon("Remove","EditorIcons") );
+
+ blend_anim->set_icon( get_icon("Blend","EditorIcons") );
+ play->set_icon( get_icon("PlayStart","EditorIcons") );
+ play_from->set_icon( get_icon("Play","EditorIcons") );
+ play_bw->set_icon( get_icon("PlayStartBackwards","EditorIcons") );
+ play_bw_from->set_icon( get_icon("PlayBackwards","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_icon(get_icon("Pin","EditorIcons") );
+ tool_anim->set_icon(get_icon("Tools","EditorIcons"));
+ tool_anim->get_popup()->connect("item_pressed",this,"_animation_tool_menu");
+
+ blend_editor.next->connect("item_selected", this, "_blend_editor_next_changed");
+
+ nodename->set_icon(get_icon("AnimationPlayer","EditorIcons"));
+
+/*
+ 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"));
+*/
+
+ get_tree()->connect("node_removed",this,"_node_removed");
+ }
+}
+
+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(TTR("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(TTR("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::_play_from_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!="") {
+
+ float time = player->get_current_animation_pos();
+
+ if (current==player->get_current_animation() && player->is_playing()) {
+
+ player->stop(); //so it wont blend with itself
+ }
+
+ player->play( current );
+ player->seek(time);
+ }
+
+ //unstop
+ stop->set_pressed(false);
+ //unpause
+ //pause->set_pressed(false);
+}
+
+
+void AnimationPlayerEditor::_play_bw_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,-1,-1,true);
+ }
+
+ //unstop
+ stop->set_pressed(false);
+ //unpause
+ //pause->set_pressed(false);
+}
+
+void AnimationPlayerEditor::_play_bw_from_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!="") {
+
+ float time = player->get_current_animation_pos();
+ if (current==player->get_current_animation())
+ player->stop(); //so it wont blend with itself
+
+ player->play(current,-1,-1,true);
+ player->seek(time);
+ }
+
+ //unstop
+ stop->set_pressed(false);
+ //unpause
+ //pause->set_pressed(false);
+}
+void AnimationPlayerEditor::_stop_pressed() {
+
+ player->stop(false);
+ 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);
+ {
+
+ key_editor->set_animation(anim);
+ Node *root = player->get_node(player->get_root());
+ if (root) {
+ key_editor->set_root(root);
+ }
+ }
+ frame->set_max(anim->get_length());
+ if (anim->get_step())
+ frame->set_step(anim->get_step());
+ else
+ frame->set_step(0.00001);
+
+
+
+ } else {
+ key_editor->set_animation(Ref<Animation>());
+ key_editor->set_root(NULL);
+
+ }
+
+
+ autoplay->set_pressed(current==player->get_autoplay());
+}
+
+void AnimationPlayerEditor::_animation_new() {
+
+ renaming=false;
+ name_title->set_text(TTR("New Animation Name:"));
+
+ int count=1;
+ String base=TTR("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(TTR("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( EditorFileDialog::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();
+ current_option = RESOURCE_LOAD;
+}
+
+
+void AnimationPlayerEditor::_animation_save_in_path(const Ref<Resource>& p_resource, const String& p_path) {
+
+ int flg = 0;
+ if (EditorSettings::get_singleton()->get("on_save/compress_binary_resources"))
+ flg |= ResourceSaver::FLAG_COMPRESS;
+ //if (EditorSettings::get_singleton()->get("on_save/save_paths_as_relative"))
+ // flg |= ResourceSaver::FLAG_RELATIVE_PATHS;
+
+ String path = Globals::get_singleton()->localize_path(p_path);
+ Error err = ResourceSaver::save(path, p_resource, flg | ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS);
+
+ if (err != OK) {
+ accept->set_text(TTR("Error saving resource!"));
+ accept->popup_centered_minsize();
+ return;
+ }
+ // EditorFileSystem::get_singleton()->update_file(path,p_resource->get_type());
+
+ ((Resource*)p_resource.ptr())->set_path(path);
+ editor->emit_signal("resource_saved", p_resource);
+
+}
+
+void AnimationPlayerEditor::_animation_save(const Ref<Resource>& p_resource) {
+
+ if (p_resource->get_path().is_resource_file()) {
+ _animation_save_in_path(p_resource, p_resource->get_path());
+ }
+ else {
+ _animation_save_as(p_resource);
+ }
+}
+
+void AnimationPlayerEditor::_animation_save_as(const Ref<Resource>& p_resource) {
+
+ file->set_mode(EditorFileDialog::MODE_SAVE_FILE);
+
+ List<String> extensions;
+ ResourceSaver::get_recognized_extensions(p_resource, &extensions);
+ file->clear_filters();
+ for (int i = 0; i<extensions.size(); i++) {
+
+ file->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper());
+ }
+
+ //file->set_current_path(current_path);
+ if (p_resource->get_path() != "") {
+ file->set_current_path(p_resource->get_path());
+ if (extensions.size()) {
+ String ext = p_resource->get_path().extension().to_lower();
+ if (extensions.find(ext) == NULL) {
+ file->set_current_path(p_resource->get_path().replacen("." + ext, "." + extensions.front()->get()));
+ }
+ }
+ }
+ else {
+
+ String existing;
+ if (extensions.size()) {
+ existing = "new_" + p_resource->get_type().to_lower() + "." + extensions.front()->get().to_lower();
+ }
+ file->set_current_path(existing);
+
+ }
+ file->popup_centered_ratio();
+ file->set_title(TTR("Save Resource As.."));
+ current_option = RESOURCE_SAVE;
+}
+
+void AnimationPlayerEditor::_animation_remove() {
+
+ if (animation->get_item_count() == 0)
+ return;
+
+ delete_dialog->set_text(TTR("Delete Animation?"));
+ delete_dialog->popup_centered_minsize();
+}
+
+void AnimationPlayerEditor::_animation_remove_confirmed() {
+
+ String current = animation->get_item_text(animation->get_selected());
+ Ref<Animation> anim = player->get_animation(current);
+
+ undo_redo->create_action(TTR("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(TTR("ERROR: Invalid animation name!"));
+ error_dialog->popup_centered_minsize();
+ 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(TTR("ERROR: Animation name already exists!"));
+ error_dialog->popup_centered_minsize();
+ return;
+ }
+
+ if (renaming) {
+ String current = animation->get_item_text(animation->get_selected());
+ Ref<Animation> anim = player->get_animation(current);
+
+ undo_redo->create_action(TTR("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(TTR("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 int p_idx) {
+
+ if (animation->get_item_count()==0)
+ return;
+
+ String current = animation->get_item_text(animation->get_selected());
+
+ undo_redo->create_action(TTR("Blend Next Changed"));
+ undo_redo->add_do_method(player,"animation_set_next",current,blend_editor.next->get_item_text(p_idx));
+ undo_redo->add_undo_method(player,"animation_set_next",current,player->animation_get_next(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();
+}
+
+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;
+
+ int i = 0;
+ bool anim_found = false;
+ blend_editor.next->clear();
+ blend_editor.next->add_item("", i);
+
+ 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));
+
+ i++;
+ blend_editor.next->add_item(to, i);
+ if (to == player->animation_get_next(current)) {
+ blend_editor.next->select(i);
+ anim_found = true;
+ }
+ }
+
+ // make sure we reset it else it becomes out of sync and could contain a deleted animation
+ if (!anim_found) {
+ blend_editor.next->select(0);
+ player->animation_set_next(current, blend_editor.next->get_item_text(0));
+ }
+
+ 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(TTR("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() {
+
+ if (player && pin->is_pressed())
+ return; // another player is pinned, don't reset
+
+ _animation_edit();
+}
+
+Dictionary AnimationPlayerEditor::get_state() const {
+
+
+ Dictionary d;
+
+ d["visible"]=is_visible();
+ if (EditorNode::get_singleton()->get_edited_scene() && is_visible() && player) {
+ d["player"]=EditorNode::get_singleton()->get_edited_scene()->get_path_to(player);
+ d["animation"]=player->get_current_animation();
+
+ }
+
+ return d;
+
+}
+void AnimationPlayerEditor::set_state(const Dictionary& p_state) {
+
+ if (p_state.has("visible") && p_state["visible"]) {
+
+ if (!EditorNode::get_singleton()->get_edited_scene())
+ return;
+
+ Node *n = EditorNode::get_singleton()->get_edited_scene()->get_node(p_state["player"]);
+ if (n && n->cast_to<AnimationPlayer>() && EditorNode::get_singleton()->get_editor_selection()->is_selected(n)) {
+ player=n->cast_to<AnimationPlayer>();
+ _update_player();
+ show();
+ set_process(true);
+ ensure_visibility();
+// EditorNode::get_singleton()->animation_panel_make_visible(true);
+
+ if (p_state.has("animation")) {
+ String anim = p_state["animation"];
+ _select_anim_by_name(anim);
+ _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()) {
+ String current = animation->get_item_text(animation->get_selected());
+ Ref<Animation> anim = player->get_animation(current);
+ key_editor->set_animation(anim);
+ Node *root = player->get_node(player->get_root());
+ if (root) {
+ key_editor->set_root(root);
+ }
+
+ } else {
+
+ key_editor->set_animation(Ref<Animation>());
+ key_editor->set_root(NULL);
+
+ }
+
+}
+void AnimationPlayerEditor::_dialog_action(String p_file) {
+
+ switch (current_option) {
+ case RESOURCE_LOAD: {
+ 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(TTR("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();
+ break;
+ }
+ case RESOURCE_SAVE: {
+
+ String current = animation->get_item_text(animation->get_selected());
+ if (current != "") {
+ Ref<Animation> anim = player->get_animation(current);
+
+ ERR_FAIL_COND(!anim->cast_to<Resource>())
+
+ RES current_res = RES(anim->cast_to<Resource>());
+
+ _animation_save_in_path(current_res, p_file);
+ }
+ }
+ }
+}
+
+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() {
+
+
+ updating=true;
+ List<StringName> animlist;
+ if (player)
+ player->get_animation_list(&animlist);
+
+ animation->clear();
+ if (player)
+ nodename->set_text(player->get_name());
+ else
+ nodename->set_text("<empty>");
+
+
+ add_anim->set_disabled(player==NULL);
+ load_anim->set_disabled(player==NULL);
+ stop->set_disabled(animlist.size()==0);
+ play->set_disabled(animlist.size()==0);
+ play_bw->set_disabled(animlist.size()==0);
+ play_bw_from->set_disabled(animlist.size()==0);
+ play_from->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);
+ save_anim->set_disabled(animlist.size() == 0);
+ tool_anim->set_disabled(player==NULL);
+
+
+ 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;
+
+ }
+
+ if (!player)
+ return;
+
+ 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 (animation->get_item_count()) {
+ String current = animation->get_item_text(animation->get_selected());
+ Ref<Animation> anim = player->get_animation(current);
+ key_editor->set_animation(anim);
+ Node *root = player->get_node(player->get_root());
+ if (root) {
+ key_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();
+ key_editor->show_select_node_warning(false);
+ } else {
+ key_editor->show_select_node_warning(true);
+
+// 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(TTR("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(player,"animation_set_next",new_name,player->animation_get_next(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();
+
+
+ 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,bool p_set) {
+
+ 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 / frame->get_max());
+ float step = anim->get_step();
+ if (step) {
+ pos=Math::stepify(pos, step);
+ if (pos<0)
+ pos=0;
+ if (pos>=anim->get_length())
+ pos=anim->get_length();
+ }
+
+ if (player->is_valid() && !p_set) {
+ float cpos = player->get_current_animation_pos();
+
+ player->seek_delta(pos,pos-cpos);
+ } else {
+ player->seek(pos,true);
+ }
+
+
+ key_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 (key_editor->get_current_animation()==anim)
+ return; //already there
+
+
+ undo_redo->create_action("Store anim in editor");
+ undo_redo->add_do_method(key_editor,"set_animation",anim);
+ undo_redo->add_undo_method(key_editor,"remove_animation",anim);
+ undo_redo->commit_action();
+}
+
+void AnimationPlayerEditor::_editor_load(){
+
+ Ref<Animation> anim = key_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) {
+
+
+ frame->set_max(p_len);
+
+}
+
+void AnimationPlayerEditor::_animation_key_editor_anim_step_changed(float p_len) {
+
+ if (p_len)
+ frame->set_step(p_len);
+ else
+ frame->set_step(0.00001);
+
+}
+
+
+void AnimationPlayerEditor::_animation_key_editor_seek(float p_pos,bool p_drag) {
+
+ if (!is_visible())
+ return;
+ if (!player)
+ return;
+
+ if (player->is_playing() )
+ return;
+
+ updating=true;
+ frame->set_val(p_pos);
+ updating=false;
+ _seek_value_changed(p_pos,!p_drag);
+
+ EditorNode::get_singleton()->get_property_editor()->refresh();
+
+
+
+ //seekit
+}
+
+void AnimationPlayerEditor::_hide_anim_editors() {
+
+ player=NULL;
+ hide();
+ set_process(false);
+
+ key_editor->set_animation(Ref<Animation>());
+ key_editor->set_root(NULL);
+ key_editor->show_select_node_warning(true);
+// editor->animation_editor_make_visible(false);
+
+}
+
+
+void AnimationPlayerEditor::_animation_tool_menu(int p_option) {
+
+ switch(p_option) {
+
+ case TOOL_COPY_ANIM: {
+
+ if (!animation->get_item_count()) {
+ error_dialog->set_text(TTR("ERROR: No animation to copy!"));
+ error_dialog->popup_centered_minsize();
+ return;
+ }
+
+ String current = animation->get_item_text(animation->get_selected());
+ Ref<Animation> anim = player->get_animation(current);
+ //editor->edit_resource(anim);
+ EditorSettings::get_singleton()->set_resource_clipboard(anim);
+
+ } break;
+ case TOOL_PASTE_ANIM: {
+
+ Ref<Animation> anim = EditorSettings::get_singleton()->get_resource_clipboard();
+ if (!anim.is_valid()) {
+ error_dialog->set_text(TTR("ERROR: No animation resource on clipboard!"));
+ error_dialog->popup_centered_minsize();
+ return;
+ }
+
+ String name = anim->get_name();
+ if (name=="") {
+ name=TTR("Pasted Animation");
+ }
+
+ int idx=1;
+ String base = name;
+ while (player->has_animation(name)) {
+
+ idx++;
+ name=base+" "+itos(idx);
+ }
+
+ undo_redo->create_action(TTR("Paste Animation"));
+ undo_redo->add_do_method(player,"add_animation",name,anim);
+ undo_redo->add_undo_method(player,"remove_animation",name);
+ undo_redo->add_do_method(this,"_animation_player_changed",player);
+ undo_redo->add_undo_method(this,"_animation_player_changed",player);
+ undo_redo->commit_action();
+
+ _select_anim_by_name(name);
+
+
+ } break;
+ case TOOL_EDIT_RESOURCE: {
+
+ if (!animation->get_item_count()) {
+ error_dialog->set_text(TTR("ERROR: No animation to edit!"));
+ error_dialog->popup_centered_minsize();
+ return;
+ }
+
+ String current = animation->get_item_text(animation->get_selected());
+ Ref<Animation> anim = player->get_animation(current);
+ editor->edit_resource(anim);
+
+ } break;
+
+ }
+}
+
+void AnimationPlayerEditor::_animation_save_menu(int p_option) {
+
+ String current = animation->get_item_text(animation->get_selected());
+ if (current != "") {
+ Ref<Animation> anim = player->get_animation(current);
+
+ switch (p_option) {
+ case ANIM_SAVE:
+ _animation_save(anim);
+ break;
+ case ANIM_SAVE_AS:
+ _animation_save_as(anim);
+ break;
+ }
+ }
+}
+
+void AnimationPlayerEditor::_unhandled_key_input(const InputEvent& p_ev) {
+
+ if (is_visible() && p_ev.type==InputEvent::KEY && p_ev.key.pressed && !p_ev.key.echo && !p_ev.key.mod.alt && !p_ev.key.mod.control && !p_ev.key.mod.meta) {
+
+ switch(p_ev.key.scancode) {
+
+ case KEY_A: {
+ if (!p_ev.key.mod.shift)
+ _play_bw_from_pressed();
+ else
+ _play_bw_pressed();
+ } break;
+ case KEY_S: {
+ _stop_pressed();
+ } break;
+ case KEY_D: {
+ if (!p_ev.key.mod.shift)
+ _play_from_pressed();
+ else
+ _play_pressed();
+ } break;
+ }
+ }
+}
+
+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("_play_from_pressed"),&AnimationPlayerEditor::_play_from_pressed);
+ ObjectTypeDB::bind_method(_MD("_play_bw_pressed"),&AnimationPlayerEditor::_play_bw_pressed);
+ ObjectTypeDB::bind_method(_MD("_play_bw_from_pressed"),&AnimationPlayerEditor::_play_bw_from_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_remove_confirmed"),&AnimationPlayerEditor::_animation_remove_confirmed);
+ 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("_dialog_action"),&AnimationPlayerEditor::_dialog_action);
+ ObjectTypeDB::bind_method(_MD("_seek_value_changed"),&AnimationPlayerEditor::_seek_value_changed,DEFVAL(true));
+ 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("_animation_key_editor_anim_step_changed"),&AnimationPlayerEditor::_animation_key_editor_anim_step_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);
+ ObjectTypeDB::bind_method(_MD("_unhandled_key_input"),&AnimationPlayerEditor::_unhandled_key_input);
+ ObjectTypeDB::bind_method(_MD("_animation_tool_menu"),&AnimationPlayerEditor::_animation_tool_menu);
+ ObjectTypeDB::bind_method(_MD("_animation_save_menu"), &AnimationPlayerEditor::_animation_save_menu);
+
+
+
+
+}
+
+AnimationPlayerEditor *AnimationPlayerEditor::singleton=NULL;
+
+AnimationPlayer *AnimationPlayerEditor::get_player() const {
+
+ return player;
+}
+AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) {
+ editor=p_editor;
+ singleton=this;
+
+ 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);
+
+
+ play_bw_from = memnew( ToolButton );
+ play_bw_from->set_tooltip(TTR("Play selected animation backwards from current pos. (A)"));
+ hb->add_child(play_bw_from);
+
+ play_bw = memnew( ToolButton );
+ play_bw->set_tooltip(TTR("Play selected animation backwards from end. (Shift+A)"));
+ hb->add_child(play_bw);
+
+ stop = memnew( ToolButton );
+ stop->set_toggle_mode(true);
+ hb->add_child(stop);
+ stop->set_tooltip(TTR("Stop animation playback. (S)"));
+
+ play = memnew( ToolButton );
+ play->set_tooltip(TTR("Play selected animation from start. (Shift+D)"));
+ hb->add_child(play);
+
+
+ play_from = memnew( ToolButton );
+ play_from->set_tooltip(TTR("Play selected animation from current pos. (D)"));
+ hb->add_child(play_from);
+
+
+
+ //pause = memnew( Button );
+ //pause->set_toggle_mode(true);
+ //hb->add_child(pause);
+
+ frame = memnew( SpinBox );
+ hb->add_child(frame);
+ frame->set_custom_minimum_size(Size2(60,0));
+ frame->set_stretch_ratio(2);
+ frame->set_tooltip(TTR("Animation position (in seconds)."));
+
+ hb->add_child( memnew( VSeparator));
+
+ scale = memnew( LineEdit );
+ hb->add_child(scale);
+ scale->set_h_size_flags(SIZE_EXPAND_FILL);
+ scale->set_stretch_ratio(1);
+ scale->set_tooltip(TTR("Scale animation playback globally for the node."));
+ scale->hide();
+
+
+ add_anim = memnew( ToolButton );
+ ED_SHORTCUT("animation_player_editor/add_animation", TTR("Create new animation in player."));
+ add_anim->set_shortcut(ED_GET_SHORTCUT("animation_player_editor/add_animation"));
+ add_anim->set_tooltip(TTR("Create new animation in player."));
+
+ hb->add_child(add_anim);
+
+
+ load_anim = memnew( ToolButton );
+ ED_SHORTCUT("animation_player_editor/load_from_disk", TTR("Load animation from disk."));
+ add_anim->set_shortcut(ED_GET_SHORTCUT("animation_player_editor/load_from_disk"));
+ load_anim->set_tooltip(TTR("Load an animation from disk."));
+ hb->add_child(load_anim);
+
+ save_anim = memnew(MenuButton);
+ save_anim->set_tooltip(TTR("Save the current animation"));
+ save_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/save", TTR("Save")), ANIM_SAVE);
+ save_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/save_as", TTR("Save As")), ANIM_SAVE_AS);
+ save_anim->set_focus_mode(Control::FOCUS_NONE);
+ hb->add_child(save_anim);
+
+ accept = memnew(AcceptDialog);
+ add_child(accept);
+ accept->connect("confirmed", this, "_menu_confirm_current");
+
+ delete_dialog = memnew(ConfirmationDialog);
+ add_child(delete_dialog);
+ delete_dialog->connect("confirmed", this, "_animation_remove_confirmed");
+
+ duplicate_anim = memnew( ToolButton );
+ hb->add_child(duplicate_anim);
+ ED_SHORTCUT("animation_player_editor/duplicate_animation", TTR("Duplicate Animation"));
+ duplicate_anim->set_shortcut(ED_GET_SHORTCUT("animation_player_editor/duplicate_animation"));
+ duplicate_anim->set_tooltip(TTR("Duplicate Animation"));
+
+ rename_anim = memnew( ToolButton );
+ hb->add_child(rename_anim);
+ ED_SHORTCUT("animation_player_editor/rename_animation", TTR("Rename Animation"));
+ rename_anim->set_shortcut(ED_GET_SHORTCUT("animation_player_editor/rename_animation"));
+ rename_anim->set_tooltip(TTR("Rename Animation"));
+
+ remove_anim = memnew( ToolButton );
+ hb->add_child(remove_anim);
+ ED_SHORTCUT("animation_player_editor/remove_animation", TTR("Remove Animation"));
+ remove_anim->set_shortcut(ED_GET_SHORTCUT("animation_player_editor/remove_animation"));
+ remove_anim->set_tooltip(TTR("Remove Animation"));
+
+
+ animation = memnew( OptionButton );
+ hb->add_child(animation);
+ animation->set_h_size_flags(SIZE_EXPAND_FILL);
+ animation->set_tooltip(TTR("Display list of animations in player."));
+ animation->set_clip_text(true);
+
+ autoplay = memnew( ToolButton );
+ hb->add_child(autoplay);
+ autoplay->set_tooltip(TTR("Autoplay on Load"));
+
+
+
+ blend_anim = memnew( ToolButton );
+ hb->add_child(blend_anim);
+ blend_anim->set_tooltip(TTR("Edit Target Blend Times"));
+
+ tool_anim = memnew( MenuButton);
+ //tool_anim->set_flat(false);
+ tool_anim->set_tooltip(TTR("Animation Tools"));
+ tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/copy_animation", TTR("Copy Animation")),TOOL_COPY_ANIM);
+ tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/paste_animation", TTR("Paste Animation")),TOOL_PASTE_ANIM);
+ //tool_anim->get_popup()->add_separator();
+ //tool_anim->get_popup()->add_item("Edit Anim Resource",TOOL_PASTE_ANIM);
+ hb->add_child(tool_anim);
+
+ nodename = memnew( Button );
+ hb->add_child(nodename);
+ pin = memnew( ToolButton );
+ pin->set_toggle_mode(true);
+ hb->add_child(pin);
+
+
+
+ resource_edit_anim= memnew( Button );
+ hb->add_child(resource_edit_anim);
+ resource_edit_anim->hide();
+
+
+ file = memnew(EditorFileDialog);
+ add_child(file);
+
+ name_dialog = memnew( ConfirmationDialog );
+ name_dialog->set_title(TTR("Create New Animation"));
+ 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(TTR("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(TTR("Close"));
+ //error_dialog->get_cancel()->set_text("Close");
+ error_dialog->set_text(TTR("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(TTR("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(TTR("Blend Times:"),blend_editor.tree,true);
+ blend_editor.next = memnew( OptionButton );
+ blend_vb->add_margin_child(TTR("Next (Auto Queue):"),blend_editor.next);
+ blend_editor.dialog->set_title(TTR("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");
+ play_from->connect("pressed", this,"_play_from_pressed");
+ play_bw->connect("pressed", this,"_play_bw_pressed");
+ play_bw_from->connect("pressed", this,"_play_bw_from_pressed");
+ stop->connect("pressed", this,"_stop_pressed");
+ //pause->connect("pressed", this,"_pause_pressed");
+ 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");
+
+ 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,"_dialog_action");
+ frame->connect("value_changed", this, "_seek_value_changed",Vector<Variant>(),true);
+ scale->connect("text_entered", this, "_scale_changed",Vector<Variant>(),true);
+
+
+
+ renaming=false;
+ last_active=false;
+
+ set_process_unhandled_key_input(true);
+
+ key_editor = memnew( AnimationKeyEditor);
+ add_child(key_editor);
+ add_constant_override("separation",get_constant("separation","VBoxContainer"));
+ key_editor->set_v_size_flags(SIZE_EXPAND_FILL);
+ key_editor->connect("timeline_changed",this,"_animation_key_editor_seek");
+ key_editor->connect("animation_len_changed",this,"_animation_key_editor_anim_len_changed");
+ key_editor->connect("animation_step_changed",this,"_animation_key_editor_anim_step_changed");
+
+ _update_player();
+}
+
+
+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) {
+
+ editor->make_bottom_panel_item_visible(anim_editor);
+ anim_editor->set_process(true);
+ anim_editor->ensure_visibility();
+// editor->animation_panel_make_visible(true);
+ } else {
+
+// anim_editor->hide();
+// anim_editor->set_idle_process(false);
+ }
+
+}
+
+AnimationPlayerEditorPlugin::AnimationPlayerEditorPlugin(EditorNode *p_node) {
+
+ editor=p_node;
+ anim_editor = memnew( AnimationPlayerEditor(editor) );
+ anim_editor->set_undo_redo(editor->get_undo_redo());
+
+ editor->add_bottom_panel_item(TTR("Animation"),anim_editor);
+ /*
+ editor->get_viewport()->add_child(anim_editor);
+ anim_editor->set_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/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h
new file mode 100644
index 000000000..7b5014215
--- /dev/null
+++ b/editor/plugins/animation_player_editor_plugin.h
@@ -0,0 +1,219 @@
+/*************************************************************************/
+/* animation_player_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "editor/editor_plugin.h"
+#include "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 AnimationKeyEditor;
+class AnimationPlayerEditor : public VBoxContainer {
+
+ OBJ_TYPE(AnimationPlayerEditor, VBoxContainer );
+
+ EditorNode *editor;
+ AnimationPlayer *player;
+
+ enum {
+ TOOL_COPY_ANIM,
+ TOOL_PASTE_ANIM,
+ TOOL_EDIT_RESOURCE
+ };
+
+ enum {
+ ANIM_SAVE,
+ ANIM_SAVE_AS
+ };
+
+ enum {
+ RESOURCE_LOAD,
+ RESOURCE_SAVE
+ };
+
+
+ OptionButton *animation;
+ Button *stop;
+ Button *play;
+ Button *play_from;
+ Button *play_bw;
+ Button *play_bw_from;
+
+// Button *pause;
+ Button *add_anim;
+ Button *autoplay;
+ Button *rename_anim;
+ Button *duplicate_anim;
+
+ Button *resource_edit_anim;
+ Button *load_anim;
+ MenuButton *save_anim;
+ Button *blend_anim;
+ Button *remove_anim;
+ MenuButton *tool_anim;
+ ToolButton *pin;
+ Button *nodename;
+ SpinBox *frame;
+ LineEdit *scale;
+ LineEdit *name;
+ Label *name_title;
+ UndoRedo *undo_redo;
+ Ref<Texture> autoplay_icon;
+ bool last_active;
+
+ EditorFileDialog *file;
+ AcceptDialog *accept;
+ ConfirmationDialog* delete_dialog;
+ int current_option;
+
+ struct BlendEditor {
+
+ AcceptDialog * dialog;
+ Tree *tree;
+ OptionButton *next;
+
+ } blend_editor;
+
+
+ ConfirmationDialog *name_dialog;
+ ConfirmationDialog *error_dialog;
+ bool renaming;
+
+
+ bool updating;
+ bool updating_blends;
+
+ AnimationKeyEditor *key_editor;
+
+
+ void _select_anim_by_name(const String& p_anim);
+ void _play_pressed();
+ void _play_from_pressed();
+ void _play_bw_pressed();
+ void _play_bw_from_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_save_in_path(const Ref<Resource>& p_resource, const String& p_path);
+ void _animation_save(const Ref<Resource>& p_resource);
+ void _animation_save_as(const Ref<Resource>& p_resource);
+
+ void _animation_remove();
+ void _animation_remove_confirmed();
+ void _animation_blend();
+ void _animation_edit();
+ void _animation_duplicate();
+ void _animation_resource_edit();
+ void _scale_changed(const String& p_scale);
+ void _dialog_action(String p_file);
+ void _seek_frame_changed(const String& p_frame);
+ void _seek_value_changed(float p_value, bool p_set=false);
+ void _blend_editor_next_changed(const int p_idx);
+
+ 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, bool p_drag);
+ void _animation_key_editor_anim_len_changed(float p_new);
+ void _animation_key_editor_anim_step_changed(float p_len);
+
+ void _unhandled_key_input(const InputEvent& p_ev);
+ void _animation_tool_menu(int p_option);
+ void _animation_save_menu(int p_option);
+
+
+ AnimationPlayerEditor();
+protected:
+
+ void _notification(int p_what);
+ void _input_event(InputEvent p_event);
+ void _node_removed(Node *p_node);
+ static void _bind_methods();
+public:
+
+ AnimationPlayer *get_player() const;
+ static AnimationPlayerEditor *singleton;
+
+ AnimationKeyEditor* get_key_editor() { return key_editor; }
+ Dictionary get_state() const;
+ void set_state(const Dictionary& p_state);
+
+
+ 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 Dictionary get_state() const { return anim_editor->get_state(); }
+ virtual void set_state(const Dictionary& p_state) { anim_editor->set_state(p_state); }
+
+ 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/editor/plugins/animation_tree_editor_plugin.cpp b/editor/plugins/animation_tree_editor_plugin.cpp
new file mode 100644
index 000000000..54d747b7a
--- /dev/null
+++ b/editor/plugins/animation_tree_editor_plugin.cpp
@@ -0,0 +1,1536 @@
+/*************************************************************************/
+/* animation_tree_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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");
+
+ 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_tree()->is_editor_hint()) {
+ get_tree()->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) {
+
+ if(p_item == 0) _edit_filters();
+ else {
+
+ 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(TTR("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();
+ master_anim_popup->add_item("Edit Filters");
+ master_anim_popup->add_separator();
+ 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(TTR("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(TTR("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(TTR("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(TTR("Blend"),0);
+ edit_option->add_item(TTR("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(TTR("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(TTR("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(TTR("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(TTR("Start!"));
+
+ edit_button->show();
+
+ edit_dialog->set_size(Size2(180,293));
+
+ break;
+
+ case AnimationTreePlayer::NODE_MIX:
+
+ edit_label[0]->set_text(TTR("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(TTR("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(TTR("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(TTR("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(TTR("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(TTR("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(TTR("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");
+
+ 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<2*h)
+ return CLICK_NODE;
+ y-=2*h;
+
+ int inputs = anim_tree->node_get_input_count(node);
+ int count = MAX(inputs,1);
+
+ if (inputs==0 || (pos.x > size.width/2 && type != AnimationTreePlayer::NODE_OUTPUT)) {
+
+ if (y<count*h) {
+
+ if (p_slot_index)
+ *p_slot_index=0;
+ return CLICK_OUTPUT_SLOT;
+ }
+ }
+
+ for(int i=0;i<count;i++) {
+
+ if (y<h) {
+ if (p_slot_index)
+ *p_slot_index=i;
+ return CLICK_INPUT_SLOT;
+ }
+ y-=h;
+ }
+
+ bool has_parameters = type!=AnimationTreePlayer::NODE_OUTPUT && type!=AnimationTreePlayer::NODE_TIMESEEK;
+ return has_parameters ? 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(TTR("Disconnect"),NODE_DISCONNECT);
+ if (anim_tree->node_get_type(rclick_node)==AnimationTreePlayer::NODE_TRANSITION) {
+ node_popup->add_item(TTR("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(TTR("Clear Auto-Advance"),NODE_CLEAR_AUTOADVANCE);
+ else
+ node_popup->add_item(TTR("Set Auto-Advance"),NODE_SET_AUTOADVANCE);
+ node_popup->add_item(TTR("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(TTR("Rename"),NODE_RENAME);
+ node_popup->add_item(TTR("Remove"),NODE_ERASE);
+ if (anim_tree->node_get_type(rclick_node)==AnimationTreePlayer::NODE_TRANSITION)
+ node_popup->add_item(TTR("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_TREE: {
+
+ 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()),TTR("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()),TTR("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(EditorFileDialog::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));
+ } else if (anim_tree->node_get_type(edited_node)==AnimationTreePlayer::NODE_ANIMATION) {
+ anim_tree->animation_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()));
+ } else if (anim_tree->node_get_type(edited_node)==AnimationTreePlayer::NODE_ANIMATION) {
+ it->set_checked(0, anim_tree->animation_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(TTR("Animation Node"),AnimationTreePlayer::NODE_ANIMATION);
+ p->add_item(TTR("OneShot Node"),AnimationTreePlayer::NODE_ONESHOT);
+ p->add_item(TTR("Mix Node"),AnimationTreePlayer::NODE_MIX);
+ p->add_item(TTR("Blend2 Node"),AnimationTreePlayer::NODE_BLEND2);
+ p->add_item(TTR("Blend3 Node"),AnimationTreePlayer::NODE_BLEND3);
+ p->add_item(TTR("Blend4 Node"),AnimationTreePlayer::NODE_BLEND4);
+ p->add_item(TTR("TimeScale Node"),AnimationTreePlayer::NODE_TIMESCALE);
+ p->add_item(TTR("TimeSeek Node"),AnimationTreePlayer::NODE_TIMESEEK);
+ p->add_item(TTR("Transition Node"),AnimationTreePlayer::NODE_TRANSITION);
+ p->add_separator();
+ p->add_item(TTR("Import Animations.."), MENU_IMPORT_ANIMATIONS); // wtf
+ p->add_separator();
+ p->add_item(TTR("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( EditorFileDialog );
+ 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(TTR("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(TTR("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);
+ button->show();
+ editor->make_bottom_panel_item_visible(anim_tree_editor);
+ anim_tree_editor->set_fixed_process(true);
+ } else {
+
+ if (anim_tree_editor->is_visible())
+ editor->hide_bottom_panel();
+ button->hide();
+ anim_tree_editor->set_fixed_process(false);
+ }
+}
+
+AnimationTreeEditorPlugin::AnimationTreeEditorPlugin(EditorNode *p_node) {
+
+ editor=p_node;
+ anim_tree_editor = memnew( AnimationTreeEditor );
+ anim_tree_editor->set_custom_minimum_size(Size2(0,300));
+
+ button=editor->add_bottom_panel_item("AnimationTree",anim_tree_editor);
+ button->hide();
+
+
+
+}
+
+
+AnimationTreeEditorPlugin::~AnimationTreeEditorPlugin()
+{
+}
+
diff --git a/editor/plugins/animation_tree_editor_plugin.h b/editor/plugins/animation_tree_editor_plugin.h
new file mode 100644
index 000000000..4bdd8e7cf
--- /dev/null
+++ b/editor/plugins/animation_tree_editor_plugin.h
@@ -0,0 +1,194 @@
+/*************************************************************************/
+/* animation_tree_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "editor/editor_plugin.h"
+#include "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 "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;
+ EditorFileDialog* 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;
+ Button *button;
+
+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/editor/plugins/baked_light_baker.cpp b/editor/plugins/baked_light_baker.cpp
new file mode 100644
index 000000000..e4847725c
--- /dev/null
+++ b/editor/plugins/baked_light_baker.cpp
@@ -0,0 +1,2724 @@
+/*************************************************************************/
+/* baked_light_baker.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "baked_light_baker.h"
+#include <stdlib.h>
+#include <cmath>
+#include "io/marshalls.h"
+#include "editor/editor_node.h"
+#include "editor/editor_settings.h"
+
+
+void baked_light_baker_add_64f(double *dst,double value);
+void baked_light_baker_add_64i(int64_t *dst,int64_t value);
+
+//-separar en 2 testuras?
+//*mejorar performance y threads
+//*modos lineales
+//*saturacion
+
+_FORCE_INLINE_ static uint64_t get_uv_normal_bit(const Vector3& p_vector) {
+
+ int lat = Math::fast_ftoi(Math::floor(Math::acos(p_vector.dot(Vector3(0,1,0)))*6.0/Math_PI+0.5));
+
+ if (lat==0) {
+ return 60;
+ } else if (lat==6) {
+ return 61;
+ }
+
+ int lon = Math::fast_ftoi(Math::floor( (Math_PI+Math::atan2(p_vector.x,p_vector.z))*12.0/(Math_PI*2.0) + 0.5))%12;
+
+ return lon+(lat-1)*12;
+}
+
+
+
+_FORCE_INLINE_ static Vector3 get_bit_normal(int p_bit) {
+
+ if (p_bit==61) {
+ return Vector3(0,1,0);
+ } else if (p_bit==62){
+ return Vector3(0,-1,0);
+ }
+
+ float latang = ((p_bit / 12)+1)*Math_PI/6.0;
+
+ Vector2 latv(Math::sin(latang),Math::cos(latang));
+
+ float lonang = ((p_bit%12)*Math_PI*2.0/12.0)-Math_PI;
+
+ Vector2 lonv(Math::sin(lonang),Math::cos(lonang));
+
+ return Vector3(lonv.x*latv.x,latv.y,lonv.y*latv.x).normalized();
+
+}
+
+
+BakedLightBaker::MeshTexture* BakedLightBaker::_get_mat_tex(const Ref<Texture>& p_tex) {
+
+ if (!tex_map.has(p_tex)) {
+
+ Ref<ImageTexture> imgtex=p_tex;
+ if (imgtex.is_null())
+ return NULL;
+ Image image=imgtex->get_data();
+ if (image.empty())
+ return NULL;
+
+ if (image.get_format()!=Image::FORMAT_RGBA) {
+ if (image.get_format()>Image::FORMAT_INDEXED_ALPHA) {
+ Error err = image.decompress();
+ if (err)
+ return NULL;
+ }
+
+ if (image.get_format()!=Image::FORMAT_RGBA)
+ image.convert(Image::FORMAT_RGBA);
+ }
+
+ if (imgtex->get_flags()&Texture::FLAG_CONVERT_TO_LINEAR) {
+ Image copy = image;
+ copy.srgb_to_linear();
+ image=copy;
+ }
+
+ DVector<uint8_t> dvt=image.get_data();
+ DVector<uint8_t>::Read r=dvt.read();
+ MeshTexture mt;
+ mt.tex_w=image.get_width();
+ mt.tex_h=image.get_height();
+ int len = image.get_width()*image.get_height()*4;
+ mt.tex.resize(len);
+ copymem(mt.tex.ptr(),r.ptr(),len);
+
+ textures.push_back(mt);
+ tex_map[p_tex]=&textures.back()->get();
+ }
+
+ return tex_map[p_tex];
+}
+
+
+void BakedLightBaker::_add_mesh(const Ref<Mesh>& p_mesh,const Ref<Material>& p_mat_override,const Transform& p_xform,int p_baked_texture) {
+
+
+ for(int i=0;i<p_mesh->get_surface_count();i++) {
+
+ if (p_mesh->surface_get_primitive_type(i)!=Mesh::PRIMITIVE_TRIANGLES)
+ continue;
+ Ref<Material> mat = p_mat_override.is_valid()?p_mat_override:p_mesh->surface_get_material(i);
+
+ MeshMaterial *matptr=NULL;
+ int baked_tex=p_baked_texture;
+
+ if (mat.is_valid()) {
+
+ if (!mat_map.has(mat)) {
+
+ MeshMaterial mm;
+
+ Ref<FixedMaterial> fm = mat;
+ if (fm.is_valid()) {
+ //fixed route
+ mm.diffuse.color=fm->get_parameter(FixedMaterial::PARAM_DIFFUSE);
+ if (linear_color)
+ mm.diffuse.color=mm.diffuse.color.to_linear();
+ mm.diffuse.tex=_get_mat_tex(fm->get_texture(FixedMaterial::PARAM_DIFFUSE));
+ mm.specular.color=fm->get_parameter(FixedMaterial::PARAM_SPECULAR);
+ if (linear_color)
+ mm.specular.color=mm.specular.color.to_linear();
+
+ mm.specular.tex=_get_mat_tex(fm->get_texture(FixedMaterial::PARAM_SPECULAR));
+ } else {
+
+ mm.diffuse.color=Color(1,1,1,1);
+ mm.diffuse.tex=NULL;
+ mm.specular.color=Color(0,0,0,1);
+ mm.specular.tex=NULL;
+ }
+
+ materials.push_back(mm);
+ mat_map[mat]=&materials.back()->get();
+
+ }
+
+ matptr=mat_map[mat];
+
+ }
+
+
+ int facecount=0;
+
+
+ if (p_mesh->surface_get_format(i)&Mesh::ARRAY_FORMAT_INDEX) {
+
+ facecount=p_mesh->surface_get_array_index_len(i);
+ } else {
+
+ facecount=p_mesh->surface_get_array_len(i);
+ }
+
+ ERR_CONTINUE((facecount==0 || (facecount%3)!=0));
+
+ facecount/=3;
+
+ int tbase=triangles.size();
+ triangles.resize(facecount+tbase);
+
+
+ Array a = p_mesh->surface_get_arrays(i);
+
+ DVector<Vector3> vertices = a[Mesh::ARRAY_VERTEX];
+ DVector<Vector3>::Read vr=vertices.read();
+ DVector<Vector2> uv;
+ DVector<Vector2>::Read uvr;
+ DVector<Vector2> uv2;
+ DVector<Vector2>::Read uv2r;
+ DVector<Vector3> normal;
+ DVector<Vector3>::Read normalr;
+ bool read_uv=false;
+ bool read_normal=false;
+
+ if (p_mesh->surface_get_format(i)&Mesh::ARRAY_FORMAT_TEX_UV) {
+
+ uv=a[Mesh::ARRAY_TEX_UV];
+ uvr=uv.read();
+ read_uv=true;
+
+ if (mat.is_valid() && mat->get_flag(Material::FLAG_LIGHTMAP_ON_UV2) && p_mesh->surface_get_format(i)&Mesh::ARRAY_FORMAT_TEX_UV2) {
+
+ uv2=a[Mesh::ARRAY_TEX_UV2];
+ uv2r=uv2.read();
+
+ } else {
+ uv2r=uv.read();
+ if (baked_light->get_transfer_lightmaps_only_to_uv2()) {
+ baked_tex=-1;
+ }
+ }
+ }
+
+ if (p_mesh->surface_get_format(i)&Mesh::ARRAY_FORMAT_NORMAL) {
+
+ normal=a[Mesh::ARRAY_NORMAL];
+ normalr=normal.read();
+ read_normal=true;
+ }
+
+ Matrix3 normal_xform = p_xform.basis.inverse().transposed();
+
+
+ if (p_mesh->surface_get_format(i)&Mesh::ARRAY_FORMAT_INDEX) {
+
+ DVector<int> indices = a[Mesh::ARRAY_INDEX];
+ DVector<int>::Read ir = indices.read();
+
+ for(int i=0;i<facecount;i++) {
+ Triangle &t=triangles[tbase+i];
+ t.vertices[0]=p_xform.xform(vr[ ir[i*3+0] ]);
+ t.vertices[1]=p_xform.xform(vr[ ir[i*3+1] ]);
+ t.vertices[2]=p_xform.xform(vr[ ir[i*3+2] ]);
+ t.material=matptr;
+ t.baked_texture=baked_tex;
+ if (read_uv) {
+
+ t.uvs[0]=uvr[ ir[i*3+0] ];
+ t.uvs[1]=uvr[ ir[i*3+1] ];
+ t.uvs[2]=uvr[ ir[i*3+2] ];
+
+ t.bake_uvs[0]=uv2r[ ir[i*3+0] ];
+ t.bake_uvs[1]=uv2r[ ir[i*3+1] ];
+ t.bake_uvs[2]=uv2r[ ir[i*3+2] ];
+ }
+ if (read_normal) {
+
+ t.normals[0]=normal_xform.xform(normalr[ ir[i*3+0] ]).normalized();
+ t.normals[1]=normal_xform.xform(normalr[ ir[i*3+1] ]).normalized();
+ t.normals[2]=normal_xform.xform(normalr[ ir[i*3+2] ]).normalized();
+ }
+ }
+
+ } else {
+
+ for(int i=0;i<facecount;i++) {
+ Triangle &t=triangles[tbase+i];
+ t.vertices[0]=p_xform.xform(vr[ i*3+0 ]);
+ t.vertices[1]=p_xform.xform(vr[ i*3+1 ]);
+ t.vertices[2]=p_xform.xform(vr[ i*3+2 ]);
+ t.material=matptr;
+ t.baked_texture=baked_tex;
+ if (read_uv) {
+
+ t.uvs[0]=uvr[ i*3+0 ];
+ t.uvs[1]=uvr[ i*3+1 ];
+ t.uvs[2]=uvr[ i*3+2 ];
+
+ t.bake_uvs[0]=uv2r[ i*3+0 ];
+ t.bake_uvs[1]=uv2r[ i*3+1 ];
+ t.bake_uvs[2]=uv2r[ i*3+2 ];
+
+ }
+ if (read_normal) {
+
+ t.normals[0]=normal_xform.xform(normalr[ i*3+0 ]).normalized();
+ t.normals[1]=normal_xform.xform(normalr[ i*3+1 ]).normalized();
+ t.normals[2]=normal_xform.xform(normalr[ i*3+2 ]).normalized();
+ }
+ }
+ }
+ }
+
+}
+
+
+void BakedLightBaker::_parse_geometry(Node* p_node) {
+
+ if (p_node->cast_to<MeshInstance>()) {
+
+ MeshInstance *meshi=p_node->cast_to<MeshInstance>();
+ Ref<Mesh> mesh=meshi->get_mesh();
+ if (mesh.is_valid()) {
+ _add_mesh(mesh,meshi->get_material_override(),base_inv * meshi->get_global_transform(),meshi->get_baked_light_texture_id());
+ }
+ } else if (p_node->cast_to<Light>()) {
+
+ Light *dl=p_node->cast_to<Light>();
+
+ if (dl->get_bake_mode()!=Light::BAKE_MODE_DISABLED) {
+
+
+ LightData dirl;
+ dirl.type=VS::LightType(dl->get_light_type());
+ dirl.diffuse=dl->get_color(DirectionalLight::COLOR_DIFFUSE);
+ dirl.specular=dl->get_color(DirectionalLight::COLOR_SPECULAR);
+ if (linear_color)
+ dirl.diffuse=dirl.diffuse.to_linear();
+ if (linear_color)
+ dirl.specular=dirl.specular.to_linear();
+
+ dirl.energy=dl->get_parameter(DirectionalLight::PARAM_ENERGY);
+ dirl.pos=dl->get_global_transform().origin;
+ dirl.up=dl->get_global_transform().basis.get_axis(1).normalized();
+ dirl.left=dl->get_global_transform().basis.get_axis(0).normalized();
+ dirl.dir=-dl->get_global_transform().basis.get_axis(2).normalized();
+ dirl.spot_angle=dl->get_parameter(DirectionalLight::PARAM_SPOT_ANGLE);
+ dirl.spot_attenuation=dl->get_parameter(DirectionalLight::PARAM_SPOT_ATTENUATION);
+ dirl.attenuation=dl->get_parameter(DirectionalLight::PARAM_ATTENUATION);
+ dirl.darkening=dl->get_parameter(DirectionalLight::PARAM_SHADOW_DARKENING);
+ dirl.radius=dl->get_parameter(DirectionalLight::PARAM_RADIUS);
+ dirl.bake_direct=dl->get_bake_mode()==Light::BAKE_MODE_FULL;
+ dirl.rays_thrown=0;
+ dirl.bake_shadow=dl->get_bake_mode()==Light::BAKE_MODE_INDIRECT_AND_SHADOWS;
+ lights.push_back(dirl);
+ }
+
+ } else if (p_node->cast_to<Spatial>()){
+
+ Spatial *sp = p_node->cast_to<Spatial>();
+
+ Array arr = p_node->call("_get_baked_light_meshes");
+ for(int i=0;i<arr.size();i+=2) {
+
+ Transform xform=arr[i];
+ Ref<Mesh> mesh=arr[i+1];
+ _add_mesh(mesh,Ref<Material>(),base_inv * (sp->get_global_transform() * xform));
+ }
+ }
+
+ for(int i=0;i<p_node->get_child_count();i++) {
+
+ _parse_geometry(p_node->get_child(i));
+ }
+}
+
+
+void BakedLightBaker::_fix_lights() {
+
+
+ total_light_area=0;
+ for(int i=0;i<lights.size();i++) {
+
+ LightData &dl=lights[i];
+
+ switch(dl.type) {
+ case VS::LIGHT_DIRECTIONAL: {
+
+ float up_max=-1e10;
+ float dir_max=-1e10;
+ float left_max=-1e10;
+ float up_min=1e10;
+ float dir_min=1e10;
+ float left_min=1e10;
+
+ for(int j=0;j<triangles.size();j++) {
+
+ for(int k=0;k<3;k++) {
+
+ Vector3 v = triangles[j].vertices[k];
+
+ float up_d = dl.up.dot(v);
+ float dir_d = dl.dir.dot(v);
+ float left_d = dl.left.dot(v);
+
+ if (up_d>up_max)
+ up_max=up_d;
+ if (up_d<up_min)
+ up_min=up_d;
+
+ if (left_d>left_max)
+ left_max=left_d;
+ if (left_d<left_min)
+ left_min=left_d;
+
+ if (dir_d>dir_max)
+ dir_max=dir_d;
+ if (dir_d<dir_min)
+ dir_min=dir_d;
+
+ }
+ }
+
+ //make a center point, then the upvector and leftvector
+ dl.pos = dl.left*( left_max+left_min )*0.5 + dl.up*( up_max+up_min )*0.5 + dl.dir*(dir_min-(dir_max-dir_min));
+ dl.left*=(left_max-left_min)*0.5;
+ dl.up*=(up_max-up_min)*0.5;
+ dl.length = (dir_max - dir_min)*10; //arbitrary number to keep it in scale
+ dl.area=dl.left.length()*2*dl.up.length()*2;
+ dl.constant=1.0/dl.area;
+ } break;
+ case VS::LIGHT_OMNI:
+ case VS::LIGHT_SPOT: {
+
+ dl.attenuation_table.resize(ATTENUATION_CURVE_LEN);
+ for(int j=0;j<ATTENUATION_CURVE_LEN;j++) {
+ dl.attenuation_table[j]=1.0-Math::pow(j/float(ATTENUATION_CURVE_LEN),dl.attenuation);
+ float falloff=j*dl.radius/float(ATTENUATION_CURVE_LEN);
+ if (falloff==0)
+ falloff=0.000001;
+ float intensity=4*Math_PI*(falloff*falloff);
+ //dl.attenuation_table[j]*=falloff*falloff;
+ dl.attenuation_table[j]*=1.0/(3.0/intensity);
+
+ }
+ if (dl.type==VS::LIGHT_OMNI) {
+
+ dl.area=4.0*Math_PI*pow(dl.radius,2.0f);
+ dl.constant=1.0/3.5;
+ } else {
+
+
+ float r = Math::tan(Math::deg2rad(dl.spot_angle))*dl.radius;
+ float c = 1.0-(Math::deg2rad(dl.spot_angle)*0.5+0.5);
+ dl.constant=1.0/3.5;
+ dl.constant*=1.0/c;
+
+ dl.area=Math_PI*r*r*c;
+ }
+
+ } break;
+
+
+ }
+
+ total_light_area+=dl.area;
+ }
+}
+
+BakedLightBaker::BVH* BakedLightBaker::_parse_bvh(BVH** p_children, int p_size, int p_depth, int &max_depth) {
+
+ if (p_depth>max_depth) {
+ max_depth=p_depth;
+ }
+
+ if (p_size==1) {
+
+ return p_children[0];
+ } else if (p_size==0) {
+
+ return NULL;
+ }
+
+
+ AABB aabb;
+ aabb=p_children[0]->aabb;
+ for(int i=1;i<p_size;i++) {
+
+ aabb.merge_with(p_children[i]->aabb);
+ }
+
+ int li=aabb.get_longest_axis_index();
+
+ switch(li) {
+
+ case Vector3::AXIS_X: {
+ SortArray<BVH*,BVHCmpX> sort_x;
+ sort_x.nth_element(0,p_size,p_size/2,p_children);
+ //sort_x.sort(&p_bb[p_from],p_size);
+ } break;
+ case Vector3::AXIS_Y: {
+ SortArray<BVH*,BVHCmpY> sort_y;
+ sort_y.nth_element(0,p_size,p_size/2,p_children);
+ //sort_y.sort(&p_bb[p_from],p_size);
+ } break;
+ case Vector3::AXIS_Z: {
+ SortArray<BVH*,BVHCmpZ> sort_z;
+ sort_z.nth_element(0,p_size,p_size/2,p_children);
+ //sort_z.sort(&p_bb[p_from],p_size);
+
+ } break;
+ }
+
+
+ BVH* left = _parse_bvh(p_children,p_size/2,p_depth+1,max_depth);
+ BVH* right = _parse_bvh(&p_children[p_size/2],p_size-p_size/2,p_depth+1,max_depth);
+
+ BVH *_new = memnew(BVH);
+ _new->aabb=aabb;
+ _new->center=aabb.pos+aabb.size*0.5;
+ _new->children[0]=left;
+ _new->children[1]=right;
+ _new->leaf=NULL;
+
+ return _new;
+}
+
+void BakedLightBaker::_make_bvh() {
+
+ Vector<BVH*> bases;
+ bases.resize(triangles.size());
+ int max_depth=0;
+ for(int i=0;i<triangles.size();i++) {
+ bases[i]=memnew( BVH );
+ bases[i]->leaf=&triangles[i];
+ bases[i]->aabb.pos=triangles[i].vertices[0];
+ bases[i]->aabb.expand_to(triangles[i].vertices[1]);
+ bases[i]->aabb.expand_to(triangles[i].vertices[2]);
+ triangles[i].aabb=bases[i]->aabb;
+ bases[i]->center=bases[i]->aabb.pos+bases[i]->aabb.size*0.5;
+ }
+
+ bvh=_parse_bvh(bases.ptr(),bases.size(),1,max_depth);
+
+ ray_stack = memnew_arr(uint32_t,max_depth);
+ bvh_stack = memnew_arr(BVH*,max_depth);
+
+ bvh_depth = max_depth;
+}
+
+void BakedLightBaker::_octree_insert(int p_octant,Triangle* p_triangle, int p_depth) {
+
+
+
+
+ uint32_t *stack=octant_stack;
+ uint32_t *ptr_stack=octantptr_stack;
+ Octant *octants=octant_pool.ptr();
+
+ stack[0]=0;
+ ptr_stack[0]=0;
+
+ int stack_pos=0;
+
+
+ while(true) {
+
+ Octant *octant=&octants[ptr_stack[stack_pos]];
+ if (stack[stack_pos]<8) {
+
+ int i = stack[stack_pos];
+ stack[stack_pos]++;
+
+
+
+ //fit_aabb=fit_aabb.grow(bvh->aabb.size.x*0.0001);
+
+ int child_idx =octant->children[i];
+ bool encloses;
+ if (!child_idx) {
+
+ AABB aabb=octant->aabb;
+ aabb.size*=0.5;
+ if (i&1)
+ aabb.pos.x+=aabb.size.x;
+ if (i&2)
+ aabb.pos.y+=aabb.size.y;
+ if (i&4)
+ aabb.pos.z+=aabb.size.z;
+
+ aabb.grow_by(cell_size*octree_extra_margin);
+ if (!aabb.intersects(p_triangle->aabb))
+ continue;
+ encloses=aabb.grow(cell_size*-octree_extra_margin*2.0).encloses(p_triangle->aabb);
+ if (!encloses && !Face3(p_triangle->vertices[0],p_triangle->vertices[1],p_triangle->vertices[2]).intersects_aabb2(aabb))
+ continue;
+ } else {
+
+ Octant *child=&octants[child_idx];
+ AABB aabb=child->aabb;
+ aabb.grow_by(cell_size*octree_extra_margin);
+ if (!aabb.intersects(p_triangle->aabb))
+ continue;
+ encloses=aabb.grow(cell_size*-octree_extra_margin*2.0).encloses(p_triangle->aabb);
+ if (!encloses && !Face3(p_triangle->vertices[0],p_triangle->vertices[1],p_triangle->vertices[2]).intersects_aabb2(aabb))
+ continue;
+
+ }
+
+ if (encloses)
+ stack[stack_pos]=8; // quick and dirty opt
+
+ if (!child_idx) {
+
+
+ if (octant_pool_size==octant_pool.size()) {
+ octant_pool.resize(octant_pool_size+OCTANT_POOL_CHUNK);
+ octants=octant_pool.ptr();
+ octant=&octants[ptr_stack[stack_pos]];
+ }
+ child_idx=octant_pool_size++;
+ octant->children[i]=child_idx;
+ Octant *child=&octants[child_idx];
+
+ child->aabb=octant->aabb;
+ child->texture_x=0;
+ child->texture_y=0;
+
+ child->aabb.size*=0.5;
+ if (i&1)
+ child->aabb.pos.x+=child->aabb.size.x;
+ if (i&2)
+ child->aabb.pos.y+=child->aabb.size.y;
+ if (i&4)
+ child->aabb.pos.z+=child->aabb.size.z;
+
+
+ child->full_accum[0]=0;
+ child->full_accum[1]=0;
+ child->full_accum[2]=0;
+ child->sampler_ofs=0;
+
+
+
+ if (stack_pos==octree_depth-1) {
+ child->leaf=true;
+ child->offset[0]=child->aabb.pos.x+child->aabb.size.x*0.5;
+ child->offset[1]=child->aabb.pos.y+child->aabb.size.y*0.5;
+ child->offset[2]=child->aabb.pos.z+child->aabb.size.z*0.5;
+ child->next_leaf=leaf_list;
+
+
+ for(int ci=0;ci<8;ci++) {
+ child->normal_accum[ci][0]=0;
+ child->normal_accum[ci][1]=0;
+ child->normal_accum[ci][2]=0;
+
+ }
+
+ child->bake_neighbour=0;
+ child->first_neighbour=true;
+ leaf_list=child_idx;
+ cell_count++;
+
+ for(int ci=0;ci<8;ci++) {
+ child->light_accum[ci][0]=0;
+ child->light_accum[ci][1]=0;
+ child->light_accum[ci][2]=0;
+ }
+
+ child->parent=ptr_stack[stack_pos];
+
+ } else {
+
+ child->leaf=false;
+ for(int j=0;j<8;j++) {
+ child->children[j]=0;
+ }
+ }
+ }
+
+ if (!octants[child_idx].leaf) {
+ stack_pos++;
+ stack[stack_pos]=0;
+ ptr_stack[stack_pos]=child_idx;
+ } else {
+
+ Octant *child=&octants[child_idx];
+
+ Vector3 n = Plane(p_triangle->vertices[0],p_triangle->vertices[1],p_triangle->vertices[2]).normal;
+
+
+ for(int ci=0;ci<8;ci++) {
+
+ Vector3 pos = child->aabb.pos;
+
+ if (ci&1)
+ pos.x+=child->aabb.size.x;
+ if (ci&2)
+ pos.y+=child->aabb.size.y;
+ if (ci&4)
+ pos.z+=child->aabb.size.z;
+
+
+ pos.x=floor((pos.x+cell_size*0.5)/cell_size);
+ pos.y=floor((pos.y+cell_size*0.5)/cell_size);
+ pos.z=floor((pos.z+cell_size*0.5)/cell_size);
+
+ {
+ Map<Vector3,Vector3>::Element *E=endpoint_normal.find(pos);
+ if (!E) {
+ endpoint_normal[pos]=n;
+ } else {
+ E->get()+=n;
+ }
+ }
+
+ {
+
+ uint64_t bit = get_uv_normal_bit(n);
+
+ Map<Vector3,uint64_t>::Element *E=endpoint_normal_bits.find(pos);
+ if (!E) {
+ endpoint_normal_bits[pos]=(1<<bit);
+ } else {
+ E->get()|=(1<<bit);
+ }
+
+ }
+
+ }
+
+ }
+
+
+ } else {
+ stack_pos--;
+ if (stack_pos<0)
+ break;
+ }
+ }
+
+
+}
+
+
+void BakedLightBaker::_make_octree() {
+
+
+ AABB base = bvh->aabb;
+ float lal=base.get_longest_axis_size();
+ //must be square because we want square blocks
+ base.size.x=lal;
+ base.size.y=lal;
+ base.size.z=lal;
+ base.grow_by(lal*0.001); //for precision
+ octree_aabb=base;
+
+ cell_size=base.size.x;
+ for(int i=0;i<octree_depth;i++)
+ cell_size/=2.0;
+ octant_stack = memnew_arr(uint32_t,octree_depth*2 );
+ octantptr_stack = memnew_arr(uint32_t,octree_depth*2 );
+
+ octant_pool.resize(OCTANT_POOL_CHUNK);
+ octant_pool_size=1;
+ Octant *root=octant_pool.ptr();
+ root->leaf=false;
+ root->aabb=octree_aabb;
+ root->parent=-1;
+ for(int i=0;i<8;i++)
+ root->children[i]=0;
+
+ EditorProgress ep("bake_octree",vformat(TTR("Parsing %d Triangles:"), triangles.size()),triangles.size());
+
+ for(int i=0;i<triangles.size();i++) {
+
+ _octree_insert(0,&triangles[i],octree_depth-1);
+ if ((i%1000)==0) {
+
+ ep.step(TTR("Triangle #")+itos(i),i);
+ }
+ }
+
+ {
+ uint32_t oct_idx=leaf_list;
+ Octant *octants=octant_pool.ptr();
+ while(oct_idx) {
+
+ BakedLightBaker::Octant *oct = &octants[oct_idx];
+ for(int ci=0;ci<8;ci++) {
+
+
+ Vector3 pos = oct->aabb.pos;
+
+ if (ci&1)
+ pos.x+=oct->aabb.size.x;
+ if (ci&2)
+ pos.y+=oct->aabb.size.y;
+ if (ci&4)
+ pos.z+=oct->aabb.size.z;
+
+
+ pos.x=floor((pos.x+cell_size*0.5)/cell_size);
+ pos.y=floor((pos.y+cell_size*0.5)/cell_size);
+ pos.z=floor((pos.z+cell_size*0.5)/cell_size);
+
+ {
+ Map<Vector3,Vector3>::Element *E=endpoint_normal.find(pos);
+ if (!E) {
+ //?
+ print_line("lolwut?");
+ } else {
+ Vector3 n = E->get().normalized();
+ oct->normal_accum[ci][0]=n.x;
+ oct->normal_accum[ci][1]=n.y;
+ oct->normal_accum[ci][2]=n.z;
+
+ }
+
+ }
+
+ {
+
+ Map<Vector3,uint64_t>::Element *E=endpoint_normal_bits.find(pos);
+ if (!E) {
+ //?
+ print_line("lolwut?");
+ } else {
+
+ float max_aper=0;
+ for(uint64_t i=0;i<62;i++) {
+
+ if (!(E->get()&(1<<i)))
+ continue;
+ Vector3 ang_i = get_bit_normal(i);
+
+ for(uint64_t j=0;j<62;j++) {
+
+ if (i==j)
+ continue;
+ if (!(E->get()&(1<<j)))
+ continue;
+ Vector3 ang_j = get_bit_normal(j);
+ float ang = Math::acos(ang_i.dot(ang_j));
+ if (ang>max_aper)
+ max_aper=ang;
+ }
+ }
+ if (max_aper>0.75*Math_PI) {
+ //angle too wide prevent problems and forget
+ oct->normal_accum[ci][0]=0;
+ oct->normal_accum[ci][1]=0;
+ oct->normal_accum[ci][2]=0;
+ }
+ }
+ }
+
+
+ }
+
+ oct_idx=oct->next_leaf;
+ }
+ }
+
+
+}
+
+
+
+
+
+void BakedLightBaker::_plot_light(ThreadStack& thread_stack,const Vector3& p_plot_pos, const AABB& p_plot_aabb, const Color& p_light,const Color& p_tint_light,bool p_only_full, const Plane& p_plane) {
+
+ //stackless version
+
+ uint32_t *stack=thread_stack.octant_stack;
+ uint32_t *ptr_stack=thread_stack.octantptr_stack;
+ Octant *octants=octant_pool.ptr();
+
+ stack[0]=0;
+ ptr_stack[0]=0;
+
+ int stack_pos=0;
+
+
+ while(true) {
+
+ Octant &octant=octants[ptr_stack[stack_pos]];
+
+ if (stack[stack_pos]==0) {
+
+
+ Vector3 pos = octant.aabb.pos + octant.aabb.size*0.5;
+ float md = 1<<(octree_depth - stack_pos );
+ float r=cell_size*plot_size*md;
+ float div = 1.0/(md*md*md);
+ //div=1.0;
+
+
+ float d = p_plot_pos.distance_to(pos);
+
+ if ((p_plane.distance_to(pos)>-cell_size*1.75*md) && d<=r) {
+
+
+ float intensity = 1.0 - (d/r)*(d/r); //not gauss but..
+
+ baked_light_baker_add_64f(&octant.full_accum[0],p_tint_light.r*intensity*div);
+ baked_light_baker_add_64f(&octant.full_accum[1],p_tint_light.g*intensity*div);
+ baked_light_baker_add_64f(&octant.full_accum[2],p_tint_light.b*intensity*div);
+ }
+ }
+
+ if (octant.leaf) {
+
+
+
+ //if (p_plane.normal.dot(octant.aabb.get_support(p_plane.normal)) < p_plane.d-CMP_EPSILON) { //octants behind are no go
+
+
+ if (!p_only_full) {
+ float r=cell_size*plot_size;
+ for(int i=0;i<8;i++) {
+ Vector3 pos=octant.aabb.pos;
+ if (i&1)
+ pos.x+=octant.aabb.size.x;
+ if (i&2)
+ pos.y+=octant.aabb.size.y;
+ if (i&4)
+ pos.z+=octant.aabb.size.z;
+
+
+
+ float d = p_plot_pos.distance_to(pos);
+
+ if ((p_plane.distance_to(pos)>-cell_size*1.75) && d<=r) {
+
+
+ float intensity = 1.0 - (d/r)*(d/r); //not gauss but..
+ if (edge_damp>0) {
+ Vector3 normal = Vector3(octant.normal_accum[i][0],octant.normal_accum[i][1],octant.normal_accum[i][2]);
+ if (normal.x>0 || normal.y>0 || normal.z>0) {
+
+ float damp = Math::abs(p_plane.normal.dot(normal));
+ intensity*=pow(damp,edge_damp);
+
+ }
+ }
+
+ //intensity*=1.0-Math::abs(p_plane.distance_to(pos))/(plot_size*cell_size);
+ //intensity = Math::cos(d*Math_PI*0.5/r);
+
+ baked_light_baker_add_64f(&octant.light_accum[i][0],p_light.r*intensity);
+ baked_light_baker_add_64f(&octant.light_accum[i][1],p_light.g*intensity);
+ baked_light_baker_add_64f(&octant.light_accum[i][2],p_light.b*intensity);
+
+
+ }
+ }
+ }
+
+ stack_pos--;
+ } else if (stack[stack_pos]<8) {
+
+ int i = stack[stack_pos];
+ stack[stack_pos]++;
+
+ if (!octant.children[i]) {
+ continue;
+ }
+
+ Octant &child=octants[octant.children[i]];
+
+ if (!child.aabb.intersects(p_plot_aabb))
+ continue;
+
+ if (child.aabb.encloses(p_plot_aabb)) {
+ stack[stack_pos]=8; //don't test the rest
+ }
+
+ stack_pos++;
+ stack[stack_pos]=0;
+ ptr_stack[stack_pos]=octant.children[i];
+ } else {
+ stack_pos--;
+ if (stack_pos<0)
+ break;
+ }
+ }
+
+
+}
+
+
+float BakedLightBaker::_throw_ray(ThreadStack& thread_stack,bool p_bake_direct,const Vector3& p_begin, const Vector3& p_end,float p_rest,const Color& p_light,float *p_att_curve,float p_att_pos,int p_att_curve_len,int p_bounces,bool p_first_bounce,bool p_only_dist) {
+
+
+ uint32_t* stack = thread_stack.ray_stack;
+ BVH **bstack = thread_stack.bvh_stack;
+
+ enum {
+ TEST_AABB_BIT=0,
+ VISIT_LEFT_BIT=1,
+ VISIT_RIGHT_BIT=2,
+ VISIT_DONE_BIT=3,
+
+
+ };
+
+ Vector3 n = (p_end-p_begin);
+ float len=n.length();
+ if (len==0)
+ return 0;
+ n/=len;
+
+
+
+ real_t d=1e10;
+ bool inters=false;
+ Vector3 r_normal;
+ Vector3 r_point;
+ Vector3 end=p_end;
+
+ Triangle *triangle=NULL;
+
+ //for(int i=0;i<max_depth;i++)
+ // stack[i]=0;
+
+ int level=0;
+ //AABB ray_aabb;
+ //ray_aabb.pos=p_begin;
+ //ray_aabb.expand_to(p_end);
+
+
+ bstack[0]=bvh;
+ stack[0]=TEST_AABB_BIT;
+
+
+ while(true) {
+
+ uint32_t mode = stack[level];
+ const BVH &b = *bstack[level];
+ bool done=false;
+
+ switch(mode) {
+ case TEST_AABB_BIT: {
+
+ if (b.leaf) {
+
+
+ Face3 f3(b.leaf->vertices[0],b.leaf->vertices[1],b.leaf->vertices[2]);
+
+
+ Vector3 res;
+
+ if (f3.intersects_segment(p_begin,end,&res)) {
+
+
+ float nd = n.dot(res);
+ if (nd<d) {
+
+ d=nd;
+ r_point=res;
+ end=res;
+ len=(p_begin-end).length();
+ r_normal=f3.get_plane().get_normal();
+ triangle=b.leaf;
+ inters=true;
+ }
+
+ }
+
+ stack[level]=VISIT_DONE_BIT;
+ } else {
+
+
+ bool valid = b.aabb.smits_intersect_ray(p_begin,n,0,len);
+ //bool valid = b.aabb.intersects_segment(p_begin,p_end);
+ // bool valid = b.aabb.intersects(ray_aabb);
+
+ if (!valid) {
+
+ stack[level]=VISIT_DONE_BIT;
+
+ } else {
+
+ stack[level]=VISIT_LEFT_BIT;
+ }
+ }
+
+ } continue;
+ case VISIT_LEFT_BIT: {
+
+ stack[level]=VISIT_RIGHT_BIT;
+ bstack[level+1]=b.children[0];
+ stack[level+1]=TEST_AABB_BIT;
+ level++;
+
+ } continue;
+ case VISIT_RIGHT_BIT: {
+
+ stack[level]=VISIT_DONE_BIT;
+ bstack[level+1]=b.children[1];
+ stack[level+1]=TEST_AABB_BIT;
+ level++;
+ } continue;
+ case VISIT_DONE_BIT: {
+
+ if (level==0) {
+ done=true;
+ break;
+ } else
+ level--;
+
+ } continue;
+ }
+
+
+ if (done)
+ break;
+ }
+
+
+
+ if (inters) {
+
+ if (p_only_dist) {
+
+ return p_begin.distance_to(r_point);
+ }
+
+
+ //should check if there is normals first
+ Vector2 uv;
+ if (true) {
+
+ triangle->get_uv_and_normal(r_point,uv,r_normal);
+
+ } else {
+
+ }
+
+ if (n.dot(r_normal)>0)
+ return -1;
+
+ if (n.dot(r_normal)>0)
+ r_normal=-r_normal;
+
+
+ //ok...
+ Color diffuse_at_point(0.8,0.8,0.8);
+ Color specular_at_point(0.0,0.0,0.0);
+
+
+ float dist = p_begin.distance_to(r_point);
+
+ AABB aabb;
+ aabb.pos=r_point;
+ aabb.pos-=Vector3(1,1,1)*cell_size*plot_size;
+ aabb.size=Vector3(2,2,2)*cell_size*plot_size;
+
+ Color res_light=p_light;
+ float att=1.0;
+ float dp=(1.0-normal_damp)*n.dot(-r_normal)+normal_damp;
+
+ if (p_att_curve) {
+
+ p_att_pos+=dist;
+ int cpos = Math::fast_ftoi((p_att_pos/p_att_curve_len)*ATTENUATION_CURVE_LEN);
+ cpos=CLAMP(cpos,0,ATTENUATION_CURVE_LEN-1);
+ att=p_att_curve[cpos];
+ }
+
+
+ res_light.r*=dp;
+ res_light.g*=dp;
+ res_light.b*=dp;
+
+ //light is plotted before multiplication with diffuse, this way
+ //the multiplication can happen with more detail in the shader
+
+
+
+ if (triangle->material) {
+
+ //triangle->get_uv(r_point);
+
+ diffuse_at_point=triangle->material->diffuse.get_color(uv);
+ specular_at_point=triangle->material->specular.get_color(uv);
+ }
+
+
+ diffuse_at_point.r=res_light.r*diffuse_at_point.r;
+ diffuse_at_point.g=res_light.g*diffuse_at_point.g;
+ diffuse_at_point.b=res_light.b*diffuse_at_point.b;
+
+ float ret=1e6;
+
+ if (p_bounces>0) {
+
+
+ p_rest-=dist;
+ if (p_rest<CMP_EPSILON)
+ return 0;
+
+ if (r_normal==-n)
+ return 0; //todo change a little
+
+ r_point+=r_normal*0.01;
+
+
+
+
+ specular_at_point.r=res_light.r*specular_at_point.r;
+ specular_at_point.g=res_light.g*specular_at_point.g;
+ specular_at_point.b=res_light.b*specular_at_point.b;
+
+
+
+ if (use_diffuse && (diffuse_at_point.r>CMP_EPSILON || diffuse_at_point.g>CMP_EPSILON || diffuse_at_point.b>CMP_EPSILON)) {
+ //diffuse bounce
+
+ Vector3 c1=r_normal.cross(n).normalized();
+ Vector3 c2=r_normal.cross(c1).normalized();
+ double r1 = double(rand())/RAND_MAX;
+ double r2 = double(rand())/RAND_MAX;
+ double r3 = double(rand())/RAND_MAX;
+#if 0
+ Vector3 next = - ((c1*(r1-0.5)) + (c2*(r2-0.5)) + (r_normal*(r3-0.5))).normalized()*0.5 + r_normal*0.5;
+
+ if (next==Vector3())
+ next=r_normal;
+ Vector3 rn=next.normalized();
+
+#else
+ Vector3 rn = ((c1*(r1-0.5)) + (c2*(r2-0.5)) + (r_normal*r3*0.5)).normalized();
+#endif
+
+
+ ret=_throw_ray(thread_stack,p_bake_direct,r_point,r_point+rn*p_rest,p_rest,diffuse_at_point,p_att_curve,p_att_pos,p_att_curve_len,p_bounces-1);
+ }
+
+ if (use_specular && (specular_at_point.r>CMP_EPSILON || specular_at_point.g>CMP_EPSILON || specular_at_point.b>CMP_EPSILON)) {
+ //specular bounce
+
+ //Vector3 c1=r_normal.cross(n).normalized();
+ //Vector3 c2=r_normal.cross(c1).normalized();
+
+ Vector3 rn = n - r_normal *r_normal.dot(n) * 2.0;
+
+ _throw_ray(thread_stack,p_bake_direct,r_point,r_point+rn*p_rest,p_rest,specular_at_point,p_att_curve,p_att_pos,p_att_curve_len,p_bounces-1);
+ }
+ }
+
+ //specular later
+// _plot_light_point(r_point,octree,octree_aabb,p_light);
+
+
+ Color plot_light=res_light.linear_interpolate(diffuse_at_point,tint);
+ plot_light.r*=att;
+ plot_light.g*=att;
+ plot_light.b*=att;
+ Color tint_light=diffuse_at_point;
+ tint_light.r*=att;
+ tint_light.g*=att;
+ tint_light.b*=att;
+
+ bool skip=false;
+
+ if (!p_first_bounce || p_bake_direct) {
+
+
+ float r = plot_size * cell_size*2;
+ if (dist<r) {
+ //avoid accumulaiton of light on corners
+ //plot_light=plot_light.linear_interpolate(Color(0,0,0,0),1.0-sd/plot_size*plot_size);
+ skip=true;
+
+ } else {
+
+
+ Vector3 c1=r_normal.cross(n).normalized();
+ Vector3 c2=r_normal.cross(c1).normalized();
+ double r1 = double(rand())/RAND_MAX;
+ double r2 = double(rand())/RAND_MAX;
+ double r3 = double(rand())/RAND_MAX;
+ Vector3 rn = ((c1*(r1-0.5)) + (c2*(r2-0.5)) + (r_normal*r3*0.25)).normalized();
+ float d =_throw_ray(thread_stack,p_bake_direct,r_point,r_point+rn*p_rest,p_rest,diffuse_at_point,p_att_curve,p_att_pos,p_att_curve_len,p_bounces-1,false,true);
+ r = plot_size*cell_size*ao_radius;
+ if (d>0 && d<r) {
+ //avoid accumulaiton of light on corners
+ //plot_light=plot_light.linear_interpolate(Color(0,0,0,0),1.0-sd/plot_size*plot_size);
+ skip=true;
+
+ } else {
+ //plot_light=Color(0,0,0,0);
+ }
+ }
+ }
+
+
+ Plane plane(r_point,r_normal);
+ if (!skip)
+ _plot_light(thread_stack,r_point,aabb,plot_light,tint_light,!(!p_first_bounce || p_bake_direct),plane);
+
+
+ return dist;
+ }
+
+ return -1;
+
+}
+
+
+
+
+void BakedLightBaker::_make_octree_texture() {
+
+
+ BakedLightBaker::Octant *octants=octant_pool.ptr();
+
+ //find neighbours first, to have a better idea of what amount of space is needed
+ {
+
+ Vector<OctantHash> octant_hashing;
+ octant_hashing.resize(octant_pool_size);
+ Vector<uint32_t> hash_table;
+ int hash_table_size=Math::larger_prime(16384);
+ hash_table.resize(hash_table_size);
+ uint32_t*hashptr = hash_table.ptr();
+ OctantHash*octhashptr = octant_hashing.ptr();
+
+ for(int i=0;i<hash_table_size;i++)
+ hashptr[i]=0;
+
+
+ //step 1 add to hash table
+
+ uint32_t oct_idx=leaf_list;
+
+
+ while(oct_idx) {
+
+ BakedLightBaker::Octant *oct = &octants[oct_idx];
+ uint64_t base=0;
+ Vector3 pos = oct->aabb.pos - octree_aabb.pos; //make sure is always positive
+ base=int((pos.x+cell_size*0.5)/cell_size);
+ base<<=16;
+ base|=int((pos.y+cell_size*0.5)/cell_size);
+ base<<=16;
+ base|=int((pos.z+cell_size*0.5)/cell_size);
+
+ uint32_t hash = HashMapHahserDefault::hash(base);
+ uint32_t idx = hash % hash_table_size;
+ octhashptr[oct_idx].next=hashptr[idx];
+ octhashptr[oct_idx].hash=hash;
+ octhashptr[oct_idx].value=base;
+ hashptr[idx]=oct_idx;
+
+ oct_idx=oct->next_leaf;
+
+ }
+
+ //step 2 find neighbours
+ oct_idx=leaf_list;
+ int neighbours=0;
+
+
+ while(oct_idx) {
+
+ BakedLightBaker::Octant *oct = &octants[oct_idx];
+ Vector3 pos = oct->aabb.pos - octree_aabb.pos; //make sure is always positive
+ pos.x+=cell_size;
+ uint64_t base=0;
+ base=int((pos.x+cell_size*0.5)/cell_size);
+ base<<=16;
+ base|=int((pos.y+cell_size*0.5)/cell_size);
+ base<<=16;
+ base|=int((pos.z+cell_size*0.5)/cell_size);
+
+ uint32_t hash = HashMapHahserDefault::hash(base);
+ uint32_t idx = hash % hash_table_size;
+
+ uint32_t bucket = hashptr[idx];
+
+ while(bucket) {
+
+ if (octhashptr[bucket].value==base) {
+
+ oct->bake_neighbour=bucket;
+ octants[bucket].first_neighbour=false;
+ neighbours++;
+ break;
+ }
+
+ bucket = octhashptr[bucket].next;
+ }
+
+ oct_idx=oct->next_leaf;
+
+ }
+
+ print_line("octant with neighbour: "+itos(neighbours));
+
+ }
+
+
+ //ok let's try to just create a texture
+
+ int otex_w=256;
+
+ while (true) {
+
+
+
+ uint32_t oct_idx=leaf_list;
+
+ int row=0;
+
+
+ print_line("begin at row "+itos(row));
+ int longest_line_reused=0;
+ int col=0;
+ int processed=0;
+
+ //reset
+ while(oct_idx) {
+
+ BakedLightBaker::Octant *oct = &octants[oct_idx];
+ oct->texture_x=0;
+ oct->texture_y=0;
+ oct_idx=oct->next_leaf;
+
+ }
+
+ oct_idx=leaf_list;
+ //assign
+ while(oct_idx) {
+
+ BakedLightBaker::Octant *oct = &octants[oct_idx];
+ if (oct->first_neighbour && oct->texture_x==0 && oct->texture_y==0) {
+ //was not processed
+ uint32_t current_idx=oct_idx;
+ int reused=0;
+
+ while(current_idx) {
+ BakedLightBaker::Octant *o = &octants[current_idx];
+ if (col+1 >= otex_w) {
+ col=0;
+ row+=4;
+ }
+ o->texture_x=col;
+ o->texture_y=row;
+ processed++;
+
+ if (o->bake_neighbour) {
+ reused++;
+ }
+ col+=o->bake_neighbour ? 1 : 2; //reuse neighbour
+ current_idx=o->bake_neighbour;
+ }
+
+ if (reused>longest_line_reused) {
+ longest_line_reused=reused;
+ }
+ }
+ oct_idx=oct->next_leaf;
+ }
+
+ row+=4;
+
+ if (otex_w < row) {
+
+ otex_w*=2;
+ } else {
+
+ baked_light_texture_w=otex_w;
+ baked_light_texture_h=nearest_power_of_2(row);
+ print_line("w: "+itos(otex_w));
+ print_line("h: "+itos(row));
+ break;
+ }
+
+
+ }
+
+
+ {
+
+ otex_w=(1<<lattice_size)*(1<<lattice_size)*2; //make sure lattice fits horizontally
+ Vector3 lattice_cell_size=octree_aabb.size;
+ for(int i=0;i<lattice_size;i++) {
+
+ lattice_cell_size*=0.5;
+ }
+
+
+
+ while(true) {
+
+ //let's plot the leafs first, given the octree is not so obvious which size it will have
+ int row=4+4*(1<<lattice_size);
+ int col=0;
+
+ col=0;
+ row+=4;
+ print_line("end at row "+itos(row));
+
+ //put octree, no need for recursion, just loop backwards.
+ int regular_octants=0;
+ for(int i=octant_pool_size-1;i>=0;i--) {
+
+ BakedLightBaker::Octant *oct = &octants[i];
+ if (oct->leaf) //ignore leaf
+ continue;
+ if (oct->aabb.size.x>lattice_cell_size.x*1.1) { //bigger than latice, skip
+ oct->texture_x=0;
+ oct->texture_y=0;
+ } else if (oct->aabb.size.x>lattice_cell_size.x*0.8) {
+ //this is the initial lattice
+ Vector3 pos = oct->aabb.pos - octree_aabb.pos; //make sure is always positive
+ int x = int((pos.x+lattice_cell_size.x*0.5)/lattice_cell_size.x);
+ int y = int((pos.y+lattice_cell_size.y*0.5)/lattice_cell_size.y);
+ int z = int((pos.z+lattice_cell_size.z*0.5)/lattice_cell_size.z);
+ //bug net
+ ERR_FAIL_INDEX(x,(1<<lattice_size));
+ ERR_FAIL_INDEX(y,(1<<lattice_size));
+ ERR_FAIL_INDEX(z,(1<<lattice_size));
+
+ /*int ofs = z*(1<<lattice_size)*(1<<lattice_size)+y*(1<<lattice_size)+x;
+ ofs*=4;
+ oct->texture_x=ofs%otex_w;
+ oct->texture_y=(ofs/otex_w)*4+4;
+ */
+
+ oct->texture_x=(x+(1<<lattice_size)*z)*2;
+ oct->texture_y=4+y*4;
+ //print_line("pos: "+itos(x)+","+itos(y)+","+itos(z)+" - ofs"+itos(oct->texture_x)+","+itos(oct->texture_y));
+
+
+ } else {
+ //an everyday regular octant
+
+ if (col+2 > otex_w) {
+ col=0;
+ row+=4;
+ }
+
+ oct->texture_x=col;
+ oct->texture_y=row;
+ col+=2;
+ regular_octants++;
+
+
+ }
+ }
+ print_line("octants end at row "+itos(row)+" totalling"+itos(regular_octants));
+
+ //ok evaluation.
+
+ if (otex_w<=2048 && row>2048) { //too big upwards, try bigger texture
+ otex_w*=2;
+ continue;
+ } else {
+ baked_octree_texture_w=otex_w;
+ baked_octree_texture_h=row+4;
+ break;
+ }
+
+ }
+
+
+ }
+
+
+ baked_octree_texture_h=nearest_power_of_2(baked_octree_texture_h);
+ print_line("RESULT! "+itos(baked_octree_texture_w)+","+itos(baked_octree_texture_h));
+
+}
+
+
+
+
+
+
+
+
+double BakedLightBaker::get_normalization(int p_light_idx) const {
+
+ double nrg=0;
+
+ const LightData &dl=lights[p_light_idx];
+ double cell_area = cell_size*cell_size;
+ //nrg+= /*dl.energy */ (dl.rays_thrown * cell_area / dl.area);
+ nrg=dl.rays_thrown * cell_area;
+ nrg*=(Math_PI*plot_size*plot_size)*0.5; // damping of radial linear gradient kernel
+ nrg*=dl.constant;
+ //nrg*=5;
+
+
+ return nrg;
+}
+
+
+
+double BakedLightBaker::get_modifier(int p_light_idx) const {
+
+ double nrg=0;
+
+ const LightData &dl=lights[p_light_idx];
+ double cell_area = cell_size*cell_size;
+ //nrg+= /*dl.energy */ (dl.rays_thrown * cell_area / dl.area);
+ nrg=cell_area;
+ nrg*=(Math_PI*plot_size*plot_size)*0.5; // damping of radial linear gradient kernel
+ nrg*=dl.constant;
+ //nrg*=5;
+
+
+ return nrg;
+}
+
+void BakedLightBaker::throw_rays(ThreadStack& thread_stack,int p_amount) {
+
+
+
+ for(int i=0;i<lights.size();i++) {
+
+ LightData &dl=lights[i];
+
+
+ int amount = p_amount * total_light_area / dl.area;
+ double mod = 1.0/double(get_modifier(i));
+ mod*=p_amount/float(amount);
+
+ switch(dl.type) {
+
+ case VS::LIGHT_DIRECTIONAL: {
+
+
+ for(int j=0;j<amount;j++) {
+ Vector3 from = dl.pos;
+ double r1 = double(rand())/RAND_MAX;
+ double r2 = double(rand())/RAND_MAX;
+ from+=dl.up*(r1*2.0-1.0);
+ from+=dl.left*(r2*2.0-1.0);
+ Vector3 to = from+dl.dir*dl.length;
+ Color col=dl.diffuse;
+ float m = mod*dl.energy;
+ col.r*=m;
+ col.g*=m;
+ col.b*=m;
+
+ dl.rays_thrown++;
+ baked_light_baker_add_64i(&total_rays,1);
+
+ _throw_ray(thread_stack,dl.bake_direct,from,to,dl.length,col,NULL,0,0,max_bounces,true);
+ }
+ } break;
+ case VS::LIGHT_OMNI: {
+
+
+ for(int j=0;j<amount;j++) {
+ Vector3 from = dl.pos;
+
+ double r1 = double(rand())/RAND_MAX;
+ double r2 = double(rand())/RAND_MAX;
+ double r3 = double(rand())/RAND_MAX;
+
+#if 0
+ //crap is not uniform..
+ Vector3 dir = Vector3(r1*2.0-1.0,r2*2.0-1.0,r3*2.0-1.0).normalized();
+
+#else
+
+ double phi = r1*Math_PI*2.0;
+ double costheta = r2*2.0-1.0;
+ double u = r3;
+
+ double theta = acos( costheta );
+ double r = 1.0 * pow( u,1/3.0 );
+
+ Vector3 dir(
+ r * sin( theta) * cos( phi ),
+ r * sin( theta) * sin( phi ),
+ r * cos( theta )
+ );
+ dir.normalize();
+
+#endif
+ Vector3 to = dl.pos+dir*dl.radius;
+ Color col=dl.diffuse;
+ float m = mod*dl.energy;
+ col.r*=m;
+ col.g*=m;
+ col.b*=m;
+
+ dl.rays_thrown++;
+ baked_light_baker_add_64i(&total_rays,1);
+ _throw_ray(thread_stack,dl.bake_direct,from,to,dl.radius,col,dl.attenuation_table.ptr(),0,dl.radius,max_bounces,true);
+// _throw_ray(i,from,to,dl.radius,col,NULL,0,dl.radius,max_bounces,true);
+ }
+
+ } break;
+ case VS::LIGHT_SPOT: {
+
+ for(int j=0;j<amount;j++) {
+ Vector3 from = dl.pos;
+
+ double r1 = double(rand())/RAND_MAX;
+ //double r2 = double(rand())/RAND_MAX;
+ double r3 = double(rand())/RAND_MAX;
+
+ float d=Math::tan(Math::deg2rad(dl.spot_angle));
+
+ float x = sin(r1*Math_PI*2.0)*d;
+ float y = cos(r1*Math_PI*2.0)*d;
+
+ Vector3 dir = r3*(dl.dir + dl.up*y + dl.left*x) + (1.0-r3)*dl.dir;
+ dir.normalize();
+
+
+ Vector3 to = dl.pos+dir*dl.radius;
+ Color col=dl.diffuse;
+ float m = mod*dl.energy;
+ col.r*=m;
+ col.g*=m;
+ col.b*=m;
+
+ dl.rays_thrown++;
+ baked_light_baker_add_64i(&total_rays,1);
+ _throw_ray(thread_stack,dl.bake_direct,from,to,dl.radius,col,dl.attenuation_table.ptr(),0,dl.radius,max_bounces,true);
+ // _throw_ray(i,from,to,dl.radius,col,NULL,0,dl.radius,max_bounces,true);
+ }
+
+ } break;
+
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+void BakedLightBaker::bake(const Ref<BakedLight> &p_light, Node* p_node) {
+
+ if (baking)
+ return;
+ cell_count=0;
+
+ base_inv=p_node->cast_to<Spatial>()->get_global_transform().affine_inverse();
+ EditorProgress ep("bake",TTR("Light Baker Setup:"),5);
+ baked_light=p_light;
+ lattice_size=baked_light->get_initial_lattice_subdiv();
+ octree_depth=baked_light->get_cell_subdivision();
+ plot_size=baked_light->get_plot_size();
+ max_bounces=baked_light->get_bounces();
+ use_diffuse=baked_light->get_bake_flag(BakedLight::BAKE_DIFFUSE);
+ use_specular=baked_light->get_bake_flag(BakedLight::BAKE_SPECULAR);
+ use_translucency=baked_light->get_bake_flag(BakedLight::BAKE_TRANSLUCENT);
+
+ edge_damp=baked_light->get_edge_damp();
+ normal_damp=baked_light->get_normal_damp();
+ octree_extra_margin=baked_light->get_cell_extra_margin();
+ tint=baked_light->get_tint();
+ ao_radius=baked_light->get_ao_radius();
+ ao_strength=baked_light->get_ao_strength();
+ linear_color=baked_light->get_bake_flag(BakedLight::BAKE_LINEAR_COLOR);
+
+ baked_textures.clear();
+ for(int i=0;i<baked_light->get_lightmaps_count();i++) {
+ BakeTexture bt;
+ bt.width=baked_light->get_lightmap_gen_size(i).x;
+ bt.height=baked_light->get_lightmap_gen_size(i).y;
+ baked_textures.push_back(bt);
+ }
+
+
+ ep.step(TTR("Parsing Geometry"),0);
+ _parse_geometry(p_node);
+ mat_map.clear();
+ tex_map.clear();
+ print_line("\ttotal triangles: "+itos(triangles.size()));
+ // no geometry
+ if (triangles.size() == 0) {
+ return;
+ }
+ ep.step(TTR("Fixing Lights"),1);
+ _fix_lights();
+ ep.step(TTR("Making BVH"),2);
+ _make_bvh();
+ ep.step(TTR("Creating Light Octree"),3);
+ _make_octree();
+ ep.step(TTR("Creating Octree Texture"),4);
+ _make_octree_texture();
+ baking=true;
+ _start_thread();
+
+}
+
+
+void BakedLightBaker::update_octree_sampler(DVector<int> &p_sampler) {
+
+ BakedLightBaker::Octant *octants=octant_pool.ptr();
+ double norm = 1.0/double(total_rays);
+
+
+
+ if (p_sampler.size()==0 || first_bake_to_map) {
+
+ Vector<int> tmp_smp;
+ tmp_smp.resize(32); //32 for header
+
+ for(int i=0;i<32;i++) {
+ tmp_smp[i]=0;
+ }
+
+ for(int i=octant_pool_size-1;i>=0;i--) {
+
+ if (i==0)
+ tmp_smp[1]=tmp_smp.size();
+
+ Octant &octant=octants[i];
+ octant.sampler_ofs = tmp_smp.size();
+ int idxcol[2]={0,0};
+
+ int r = CLAMP((octant.full_accum[0]*norm)*2048,0,32767);
+ int g = CLAMP((octant.full_accum[1]*norm)*2048,0,32767);
+ int b = CLAMP((octant.full_accum[2]*norm)*2048,0,32767);
+
+ idxcol[0]|=r;
+ idxcol[1]|=(g<<16)|b;
+
+ if (octant.leaf) {
+ tmp_smp.push_back(idxcol[0]);
+ tmp_smp.push_back(idxcol[1]);
+ } else {
+
+ for(int j=0;j<8;j++) {
+ if (octant.children[j]) {
+ idxcol[0]|=(1<<(j+16));
+ }
+ }
+ tmp_smp.push_back(idxcol[0]);
+ tmp_smp.push_back(idxcol[1]);
+ for(int j=0;j<8;j++) {
+ if (octant.children[j]) {
+ tmp_smp.push_back(octants[octant.children[j]].sampler_ofs);
+ if (octants[octant.children[j]].sampler_ofs==0) {
+ print_line("FUUUUUUUUCK");
+ }
+ }
+ }
+ }
+
+ }
+
+ p_sampler.resize(tmp_smp.size());
+ DVector<int>::Write w = p_sampler.write();
+ int ss = tmp_smp.size();
+ for(int i=0;i<ss;i++) {
+ w[i]=tmp_smp[i];
+ }
+
+ first_bake_to_map=false;
+
+ }
+
+ double gamma = baked_light->get_gamma_adjust();
+ double mult = baked_light->get_energy_multiplier();
+ float saturation = baked_light->get_saturation();
+
+ DVector<int>::Write w = p_sampler.write();
+
+ encode_uint32(octree_depth,(uint8_t*)&w[2]);
+ encode_uint32(linear_color,(uint8_t*)&w[3]);
+
+ encode_float(octree_aabb.pos.x,(uint8_t*)&w[4]);
+ encode_float(octree_aabb.pos.y,(uint8_t*)&w[5]);
+ encode_float(octree_aabb.pos.z,(uint8_t*)&w[6]);
+ encode_float(octree_aabb.size.x,(uint8_t*)&w[7]);
+ encode_float(octree_aabb.size.y,(uint8_t*)&w[8]);
+ encode_float(octree_aabb.size.z,(uint8_t*)&w[9]);
+
+ //norm*=multiplier;
+
+ for(int i=octant_pool_size-1;i>=0;i--) {
+
+ Octant &octant=octants[i];
+ int idxcol[2]={w[octant.sampler_ofs],w[octant.sampler_ofs+1]};
+
+ double rf=pow(octant.full_accum[0]*norm*mult,gamma);
+ double gf=pow(octant.full_accum[1]*norm*mult,gamma);
+ double bf=pow(octant.full_accum[2]*norm*mult,gamma);
+
+ double gray = (rf+gf+bf)/3.0;
+ rf = gray + (rf-gray)*saturation;
+ gf = gray + (gf-gray)*saturation;
+ bf = gray + (bf-gray)*saturation;
+
+
+ int r = CLAMP((rf)*2048,0,32767);
+ int g = CLAMP((gf)*2048,0,32767);
+ int b = CLAMP((bf)*2048,0,32767);
+
+ idxcol[0]=((idxcol[0]>>16)<<16)|r;
+ idxcol[1]=(g<<16)|b;
+ w[octant.sampler_ofs]=idxcol[0];
+ w[octant.sampler_ofs+1]=idxcol[1];
+ }
+
+}
+
+void BakedLightBaker::update_octree_images(DVector<uint8_t> &p_octree,DVector<uint8_t> &p_light) {
+
+
+ int len = baked_octree_texture_w*baked_octree_texture_h*4;
+ p_octree.resize(len);
+
+ int ilen = baked_light_texture_w*baked_light_texture_h*4;
+ p_light.resize(ilen);
+
+
+ DVector<uint8_t>::Write w = p_octree.write();
+ zeromem(w.ptr(),len);
+
+ DVector<uint8_t>::Write iw = p_light.write();
+ zeromem(iw.ptr(),ilen);
+
+ float gamma = baked_light->get_gamma_adjust();
+ float mult = baked_light->get_energy_multiplier();
+
+ for(int i=0;i<len;i+=4) {
+ w[i+0]=0xFF;
+ w[i+1]=0;
+ w[i+2]=0xFF;
+ w[i+3]=0xFF;
+ }
+
+ for(int i=0;i<ilen;i+=4) {
+ iw[i+0]=0xFF;
+ iw[i+1]=0;
+ iw[i+2]=0xFF;
+ iw[i+3]=0xFF;
+ }
+
+ float multiplier=1.0;
+
+ if (baked_light->get_format()==BakedLight::FORMAT_HDR8)
+ multiplier=8;
+ encode_uint32(baked_octree_texture_w,&w[0]);
+ encode_uint32(baked_octree_texture_h,&w[4]);
+ encode_uint32(0,&w[8]);
+ encode_float(1<<lattice_size,&w[12]);
+ encode_uint32(octree_depth-lattice_size,&w[16]);
+ encode_uint32(multiplier,&w[20]);
+ encode_uint16(baked_light_texture_w,&w[24]); //if present, use the baked light texture
+ encode_uint16(baked_light_texture_h,&w[26]);
+ encode_uint32(0,&w[28]); //baked light texture format
+
+ encode_float(octree_aabb.pos.x,&w[32]);
+ encode_float(octree_aabb.pos.y,&w[36]);
+ encode_float(octree_aabb.pos.z,&w[40]);
+ encode_float(octree_aabb.size.x,&w[44]);
+ encode_float(octree_aabb.size.y,&w[48]);
+ encode_float(octree_aabb.size.z,&w[52]);
+
+
+ BakedLightBaker::Octant *octants=octant_pool.ptr();
+ int octant_count=octant_pool_size;
+ uint8_t *ptr = w.ptr();
+ uint8_t *lptr = iw.ptr();
+
+
+ int child_offsets[8]={
+ 0,
+ 4,
+ baked_octree_texture_w*4,
+ baked_octree_texture_w*4+4,
+ baked_octree_texture_w*8+0,
+ baked_octree_texture_w*8+4,
+ baked_octree_texture_w*8+baked_octree_texture_w*4,
+ baked_octree_texture_w*8+baked_octree_texture_w*4+4,
+ };
+
+ int lchild_offsets[8]={
+ 0,
+ 4,
+ baked_light_texture_w*4,
+ baked_light_texture_w*4+4,
+ baked_light_texture_w*8+0,
+ baked_light_texture_w*8+4,
+ baked_light_texture_w*8+baked_light_texture_w*4,
+ baked_light_texture_w*8+baked_light_texture_w*4+4,
+ };
+
+ /*Vector<double> norm_arr;
+ norm_arr.resize(lights.size());
+
+ for(int i=0;i<lights.size();i++) {
+ norm_arr[i] = 1.0/get_normalization(i);
+ }
+
+ const double *normptr=norm_arr.ptr();
+*/
+ double norm = 1.0/double(total_rays);
+ mult/=multiplier;
+ double saturation = baked_light->get_saturation();
+
+ for(int i=0;i<octant_count;i++) {
+
+ Octant &oct=octants[i];
+ if (oct.texture_x==0 && oct.texture_y==0)
+ continue;
+
+
+ if (oct.leaf) {
+
+ int ofs = (oct.texture_y * baked_light_texture_w + oct.texture_x)<<2;
+ ERR_CONTINUE(ofs<0 || ofs >ilen);
+ //write colors
+ for(int j=0;j<8;j++) {
+
+ //if (!oct.children[j])
+ // continue;
+ uint8_t *iptr=&lptr[ofs+lchild_offsets[j]];
+
+ float r=oct.light_accum[j][0]*norm;
+ float g=oct.light_accum[j][1]*norm;
+ float b=oct.light_accum[j][2]*norm;
+
+ r=pow(r*mult,gamma);
+ g=pow(g*mult,gamma);
+ b=pow(b*mult,gamma);
+
+ double gray = (r+g+b)/3.0;
+ r = gray + (r-gray)*saturation;
+ g = gray + (g-gray)*saturation;
+ b = gray + (b-gray)*saturation;
+
+ float ic[3]={
+ r,
+ g,
+ b,
+ };
+ iptr[0]=CLAMP(ic[0]*255.0,0,255);
+ iptr[1]=CLAMP(ic[1]*255.0,0,255);
+ iptr[2]=CLAMP(ic[2]*255.0,0,255);
+ iptr[3]=255;
+ }
+
+ } else {
+
+ int ofs = (oct.texture_y * baked_octree_texture_w + oct.texture_x)<<2;
+ ERR_CONTINUE(ofs<0 || ofs >len);
+
+ //write indices
+ for(int j=0;j<8;j++) {
+
+ if (!oct.children[j])
+ continue;
+ Octant&choct=octants[oct.children[j]];
+ uint8_t *iptr=&ptr[ofs+child_offsets[j]];
+
+ iptr[0]=choct.texture_x>>8;
+ iptr[1]=choct.texture_x&0xFF;
+ iptr[2]=choct.texture_y>>8;
+ iptr[3]=choct.texture_y&0xFF;
+
+ }
+ }
+
+ }
+
+
+}
+
+
+void BakedLightBaker::_free_bvh(BVH* p_bvh) {
+
+ if (!p_bvh->leaf) {
+ if (p_bvh->children[0])
+ _free_bvh(p_bvh->children[0]);
+ if (p_bvh->children[1])
+ _free_bvh(p_bvh->children[1]);
+ }
+
+ memdelete(p_bvh);
+
+}
+
+
+bool BakedLightBaker::is_baking() {
+
+ return baking;
+}
+
+void BakedLightBaker::set_pause(bool p_pause){
+
+ if (paused==p_pause)
+ return;
+
+ paused=p_pause;
+
+ if (paused) {
+ _stop_thread();
+ } else {
+ _start_thread();
+ }
+}
+bool BakedLightBaker::is_paused() {
+
+ return paused;
+
+}
+
+void BakedLightBaker::_bake_thread_func(void *arg) {
+
+ BakedLightBaker *ble = (BakedLightBaker*)arg;
+
+
+
+ ThreadStack thread_stack;
+
+ thread_stack.ray_stack = memnew_arr(uint32_t,ble->bvh_depth);
+ thread_stack.bvh_stack = memnew_arr(BVH*,ble->bvh_depth);
+ thread_stack.octant_stack = memnew_arr(uint32_t,ble->octree_depth*2 );
+ thread_stack.octantptr_stack = memnew_arr(uint32_t,ble->octree_depth*2 );
+
+ while(!ble->bake_thread_exit) {
+
+ ble->throw_rays(thread_stack,1000);
+ }
+
+ memdelete_arr(thread_stack.ray_stack );
+ memdelete_arr(thread_stack.bvh_stack );
+ memdelete_arr(thread_stack.octant_stack );
+ memdelete_arr(thread_stack.octantptr_stack );
+
+}
+
+void BakedLightBaker::_start_thread() {
+
+ if (threads.size()!=0)
+ return;
+ bake_thread_exit=false;
+
+ int thread_count = EDITOR_DEF("light_baker/custom_bake_threads",0);
+ if (thread_count<=0 || thread_count>64)
+ thread_count=OS::get_singleton()->get_processor_count();
+
+ //thread_count=1;
+ threads.resize(thread_count);
+ for(int i=0;i<threads.size();i++) {
+ threads[i]=Thread::create(_bake_thread_func,this);
+ }
+}
+
+void BakedLightBaker::_stop_thread() {
+
+ if (threads.size()==0)
+ return;
+ bake_thread_exit=true;
+ for(int i=0;i<threads.size();i++) {
+ Thread::wait_to_finish(threads[i]);
+ memdelete(threads[i]);
+ }
+ threads.clear();
+}
+
+void BakedLightBaker::_plot_pixel_to_lightmap(int x, int y, int width, int height, uint8_t *image, const Vector3& p_pos,const Vector3& p_normal,double *p_norm_ptr,float mult,float gamma) {
+
+
+ uint8_t *ptr = &image[(y*width+x)*4];
+ //int lc = lights.size();
+ double norm = 1.0/double(total_rays);
+
+
+ Color color;
+
+ Octant *octants=octant_pool.ptr();
+
+
+ int octant_idx=0;
+
+
+ while(true) {
+
+ Octant &octant=octants[octant_idx];
+
+ if (octant.leaf) {
+
+ Vector3 lpos = p_pos-octant.aabb.pos;
+ lpos/=octant.aabb.size;
+
+ Vector3 cols[8];
+
+ for(int i=0;i<8;i++) {
+
+ cols[i].x+=octant.light_accum[i][0]*norm;
+ cols[i].y+=octant.light_accum[i][1]*norm;
+ cols[i].z+=octant.light_accum[i][2]*norm;
+ }
+
+
+ /*Vector3 final = (cols[0] + (cols[1] - cols[0]) * lpos.y);
+ final = final + ((cols[2] + (cols[3] - cols[2]) * lpos.y) - final)*lpos.x;
+
+ Vector3 final2 = (cols[4+0] + (cols[4+1] - cols[4+0]) * lpos.y);
+ final2 = final2 + ((cols[4+2] + (cols[4+3] - cols[4+2]) * lpos.y) - final2)*lpos.x;*/
+
+ Vector3 finala = cols[0].linear_interpolate(cols[1],lpos.x);
+ Vector3 finalb = cols[2].linear_interpolate(cols[3],lpos.x);
+ Vector3 final = finala.linear_interpolate(finalb,lpos.y);
+
+ Vector3 final2a = cols[4+0].linear_interpolate(cols[4+1],lpos.x);
+ Vector3 final2b = cols[4+2].linear_interpolate(cols[4+3],lpos.x);
+ Vector3 final2 = final2a.linear_interpolate(final2b,lpos.y);
+
+ final = final.linear_interpolate(final2,lpos.z);
+ if (baked_light->get_format()==BakedLight::FORMAT_HDR8)
+ final*=8.0;
+
+
+ color.r=pow(final.x*mult,gamma);
+ color.g=pow(final.y*mult,gamma);
+ color.b=pow(final.z*mult,gamma);
+ color.a=1.0;
+
+ int lc = lights.size();
+ LightData *lv = lights.ptr();
+ for(int i=0;i<lc;i++) {
+ //shadow baking
+ if (!lv[i].bake_shadow)
+ continue;
+ Vector3 from = p_pos+p_normal*0.01;
+ Vector3 to;
+ float att=0;
+ switch(lv[i].type) {
+ case VS::LIGHT_DIRECTIONAL: {
+ to=from-lv[i].dir*lv[i].length;
+ } break;
+ case VS::LIGHT_OMNI: {
+ to=lv[i].pos;
+ float d = MIN(lv[i].radius,to.distance_to(from))/lv[i].radius;
+ att=d;//1.0-d;
+ } break;
+ default: continue;
+ }
+
+ uint32_t* stack = ray_stack;
+ BVH **bstack = bvh_stack;
+
+ enum {
+ TEST_RAY_BIT=0,
+ VISIT_LEFT_BIT=1,
+ VISIT_RIGHT_BIT=2,
+ VISIT_DONE_BIT=3,
+
+
+ };
+
+ bool intersected=false;
+
+ int level=0;
+
+ Vector3 n = (to-from);
+ float len=n.length();
+ if (len==0)
+ continue;
+ n/=len;
+
+ bstack[0]=bvh;
+ stack[0]=TEST_RAY_BIT;
+
+
+ while(!intersected) {
+
+ uint32_t mode = stack[level];
+ const BVH &b = *bstack[level];
+ bool done=false;
+
+ switch(mode) {
+ case TEST_RAY_BIT: {
+
+ if (b.leaf) {
+
+
+ Face3 f3(b.leaf->vertices[0],b.leaf->vertices[1],b.leaf->vertices[2]);
+
+
+ Vector3 res;
+
+ if (f3.intersects_segment(from,to)) {
+ intersected=true;
+ done=true;
+ }
+
+ stack[level]=VISIT_DONE_BIT;
+ } else {
+
+
+ bool valid = b.aabb.smits_intersect_ray(from,n,0,len);
+ //bool valid = b.aabb.intersects_segment(p_begin,p_end);
+ // bool valid = b.aabb.intersects(ray_aabb);
+
+ if (!valid) {
+
+ stack[level]=VISIT_DONE_BIT;
+
+ } else {
+
+ stack[level]=VISIT_LEFT_BIT;
+ }
+ }
+
+ } continue;
+ case VISIT_LEFT_BIT: {
+
+ stack[level]=VISIT_RIGHT_BIT;
+ bstack[level+1]=b.children[0];
+ stack[level+1]=TEST_RAY_BIT;
+ level++;
+
+ } continue;
+ case VISIT_RIGHT_BIT: {
+
+ stack[level]=VISIT_DONE_BIT;
+ bstack[level+1]=b.children[1];
+ stack[level+1]=TEST_RAY_BIT;
+ level++;
+ } continue;
+ case VISIT_DONE_BIT: {
+
+ if (level==0) {
+ done=true;
+ break;
+ } else
+ level--;
+
+ } continue;
+ }
+
+
+ if (done)
+ break;
+ }
+
+
+
+ if (intersected) {
+
+ color.a=Math::lerp(MAX(0.01,lv[i].darkening),1.0,att);
+ }
+
+ }
+
+ break;
+ } else {
+
+ Vector3 lpos = p_pos - octant.aabb.pos;
+ Vector3 half = octant.aabb.size * 0.5;
+
+ int ofs=0;
+
+ if (lpos.x >= half.x)
+ ofs|=1;
+ if (lpos.y >= half.y)
+ ofs|=2;
+ if (lpos.z >= half.z)
+ ofs|=4;
+
+ octant_idx = octant.children[ofs];
+
+ if (octant_idx==0)
+ return;
+
+ }
+ }
+
+ ptr[0]=CLAMP(color.r*255.0,0,255);
+ ptr[1]=CLAMP(color.g*255.0,0,255);
+ ptr[2]=CLAMP(color.b*255.0,0,255);
+ ptr[3]=CLAMP(color.a*255.0,0,255);
+
+}
+
+
+Error BakedLightBaker::transfer_to_lightmaps() {
+
+ if (!triangles.size() || baked_textures.size()==0)
+ return ERR_UNCONFIGURED;
+
+ EditorProgress ep("transfer_to_lightmaps",TTR("Transfer to Lightmaps:"),baked_textures.size()*2+triangles.size());
+
+ for(int i=0;i<baked_textures.size();i++) {
+
+ ERR_FAIL_COND_V( baked_textures[i].width<=0 || baked_textures[i].height<=0,ERR_UNCONFIGURED );
+
+ baked_textures[i].data.resize( baked_textures[i].width*baked_textures[i].height*4 );
+ zeromem(baked_textures[i].data.ptr(),baked_textures[i].data.size());
+ ep.step(TTR("Allocating Texture #")+itos(i+1),i);
+ }
+
+ Vector<double> norm_arr;
+ norm_arr.resize(lights.size());
+
+ for(int i=0;i<lights.size();i++) {
+ norm_arr[i] = 1.0/get_normalization(i);
+ }
+ float gamma = baked_light->get_gamma_adjust();
+ float mult = baked_light->get_energy_multiplier();
+
+ for(int i=0;i<triangles.size();i++) {
+
+ if (i%200==0) {
+ ep.step(TTR("Baking Triangle #")+itos(i),i+baked_textures.size());
+ }
+ Triangle &t=triangles[i];
+ if (t.baked_texture<0 || t.baked_texture>=baked_textures.size())
+ continue;
+
+ BakeTexture &bt=baked_textures[t.baked_texture];
+ Vector3 normal = Plane(t.vertices[0],t.vertices[1],t.vertices[2]).normal;
+
+
+ int x[3];
+ int y[3];
+
+ Vector3 vertices[3]={
+ t.vertices[0],
+ t.vertices[1],
+ t.vertices[2]
+ };
+
+ for(int j=0;j<3;j++) {
+
+ x[j]=t.bake_uvs[j].x*bt.width;
+ y[j]=t.bake_uvs[j].y*bt.height;
+ x[j]=CLAMP(x[j],0,bt.width-1);
+ y[j]=CLAMP(y[j],0,bt.height-1);
+ }
+
+
+ {
+
+ // sort the points vertically
+ if (y[1] > y[2]) {
+ SWAP(x[1], x[2]);
+ SWAP(y[1], y[2]);
+ SWAP(vertices[1],vertices[2]);
+ }
+ if (y[0] > y[1]) {
+ SWAP(x[0], x[1]);
+ SWAP(y[0], y[1]);
+ SWAP(vertices[0],vertices[1]);
+ }
+ if (y[1] > y[2]) {
+ SWAP(x[1], x[2]);
+ SWAP(y[1], y[2]);
+ SWAP(vertices[1],vertices[2]);
+ }
+
+ double dx_far = double(x[2] - x[0]) / (y[2] - y[0] + 1);
+ double dx_upper = double(x[1] - x[0]) / (y[1] - y[0] + 1);
+ double dx_low = double(x[2] - x[1]) / (y[2] - y[1] + 1);
+ double xf = x[0];
+ double xt = x[0] + dx_upper; // if y[0] == y[1], special case
+ for (int yi = y[0]; yi <= (y[2] > bt.height-1 ? bt.height-1 : y[2]); yi++)
+ {
+ if (yi >= 0) {
+ for (int xi = (xf > 0 ? int(xf) : 0); xi <= (xt < bt.width ? xt : bt.width-1) ; xi++) {
+ //pixels[int(x + y * width)] = color;
+
+ Vector2 v0 = Vector2(x[1]-x[0],y[1]-y[0]);
+ Vector2 v1 = Vector2(x[2]-x[0],y[2]-y[0]);
+ //vertices[2] - vertices[0];
+ Vector2 v2 = Vector2(xi-x[0],yi-y[0]);
+ float d00 = v0.dot( v0);
+ float d01 = v0.dot( v1);
+ float d11 = v1.dot( v1);
+ float d20 = v2.dot( v0);
+ float d21 = v2.dot( v1);
+ float denom = (d00 * d11 - d01 * d01);
+ Vector3 pos;
+ if (denom==0) {
+ pos=t.vertices[0];
+ } else {
+ float v = (d11 * d20 - d01 * d21) / denom;
+ float w = (d00 * d21 - d01 * d20) / denom;
+ float u = 1.0f - v - w;
+ pos = vertices[0]*u + vertices[1]*v + vertices[2]*w;
+ }
+ _plot_pixel_to_lightmap(xi,yi,bt.width,bt.height,bt.data.ptr(),pos,normal,norm_arr.ptr(),mult,gamma);
+
+ }
+
+ for (int xi = (xf < bt.width ? int(xf) : bt.width-1); xi >= (xt > 0 ? xt : 0); xi--) {
+ //pixels[int(x + y * width)] = color;
+ Vector2 v0 = Vector2(x[1]-x[0],y[1]-y[0]);
+ Vector2 v1 = Vector2(x[2]-x[0],y[2]-y[0]);
+ //vertices[2] - vertices[0];
+ Vector2 v2 = Vector2(xi-x[0],yi-y[0]);
+ float d00 = v0.dot( v0);
+ float d01 = v0.dot( v1);
+ float d11 = v1.dot( v1);
+ float d20 = v2.dot( v0);
+ float d21 = v2.dot( v1);
+ float denom = (d00 * d11 - d01 * d01);
+ Vector3 pos;
+ if (denom==0) {
+ pos=t.vertices[0];
+ } else {
+ float v = (d11 * d20 - d01 * d21) / denom;
+ float w = (d00 * d21 - d01 * d20) / denom;
+ float u = 1.0f - v - w;
+ pos = vertices[0]*u + vertices[1]*v + vertices[2]*w;
+ }
+
+ _plot_pixel_to_lightmap(xi,yi,bt.width,bt.height,bt.data.ptr(),pos,normal,norm_arr.ptr(),mult,gamma);
+
+ }
+ }
+ xf += dx_far;
+ if (yi < y[1])
+ xt += dx_upper;
+ else
+ xt += dx_low;
+ }
+ }
+
+ }
+
+
+ for(int i=0;i<baked_textures.size();i++) {
+
+
+ {
+
+ ep.step(TTR("Post-Processing Texture #")+itos(i),i+baked_textures.size()+triangles.size());
+
+ BakeTexture &bt=baked_textures[i];
+
+ Vector<uint8_t> copy_data=bt.data;
+ uint8_t *data=bt.data.ptr();
+ const int max_radius=8;
+ const int shadow_radius=2;
+ const int max_dist=0x7FFFFFFF;
+
+ for(int x=0;x<bt.width;x++) {
+
+ for(int y=0;y<bt.height;y++) {
+
+
+ uint8_t a = copy_data[(y*bt.width+x)*4+3];
+
+ if (a>0) {
+ //blur shadow
+
+ int from_x = MAX(0,x-shadow_radius);
+ int to_x = MIN(bt.width-1,x+shadow_radius);
+ int from_y = MAX(0,y-shadow_radius);
+ int to_y = MIN(bt.height-1,y+shadow_radius);
+
+ int sum=0;
+ int sumc=0;
+
+ for(int k=from_y;k<=to_y;k++) {
+ for(int l=from_x;l<=to_x;l++) {
+
+ const uint8_t * rp = &copy_data[(k*bt.width+l)<<2];
+
+ sum+=rp[3];
+ sumc++;
+ }
+ }
+
+ sum/=sumc;
+ data[(y*bt.width+x)*4+3]=sum;
+
+ } else {
+
+ int closest_dist=max_dist;
+ uint8_t closest_color[4];
+
+ int from_x = MAX(0,x-max_radius);
+ int to_x = MIN(bt.width-1,x+max_radius);
+ int from_y = MAX(0,y-max_radius);
+ int to_y = MIN(bt.height-1,y+max_radius);
+
+ for(int k=from_y;k<=to_y;k++) {
+ for(int l=from_x;l<=to_x;l++) {
+
+ int dy = y-k;
+ int dx = x-l;
+ int dist = dy*dy+dx*dx;
+ if (dist>=closest_dist)
+ continue;
+
+ const uint8_t * rp = &copy_data[(k*bt.width+l)<<2];
+
+ if (rp[3]==0)
+ continue;
+
+ closest_dist=dist;
+ closest_color[0]=rp[0];
+ closest_color[1]=rp[1];
+ closest_color[2]=rp[2];
+ closest_color[3]=rp[3];
+ }
+ }
+
+
+ if (closest_dist!=max_dist) {
+
+ data[(y*bt.width+x)*4+0]=closest_color[0];
+ data[(y*bt.width+x)*4+1]=closest_color[1];
+ data[(y*bt.width+x)*4+2]=closest_color[2];
+ data[(y*bt.width+x)*4+3]=closest_color[3];
+ }
+ }
+ }
+ }
+ }
+
+ DVector<uint8_t> dv;
+ dv.resize(baked_textures[i].data.size());
+ {
+ DVector<uint8_t>::Write w = dv.write();
+ copymem(w.ptr(),baked_textures[i].data.ptr(),baked_textures[i].data.size());
+ }
+
+ Image img(baked_textures[i].width,baked_textures[i].height,0,Image::FORMAT_RGBA,dv);
+ Ref<ImageTexture> tex = memnew( ImageTexture );
+ tex->create_from_image(img);
+ baked_light->set_lightmap_texture(i,tex);
+ }
+
+
+ return OK;
+}
+
+void BakedLightBaker::clear() {
+
+
+
+ _stop_thread();
+
+ if (bvh)
+ _free_bvh(bvh);
+
+ if (ray_stack)
+ memdelete_arr(ray_stack);
+ if (octant_stack)
+ memdelete_arr(octant_stack);
+ if (octantptr_stack)
+ memdelete_arr(octantptr_stack);
+ if (bvh_stack)
+ memdelete_arr(bvh_stack);
+/*
+ * ???
+ for(int i=0;i<octant_pool.size();i++) {
+ //if (octant_pool[i].leaf) {
+ // memdelete_arr( octant_pool[i].light );
+ //} Vector<double> norm_arr;
+ //norm_arr.resize(lights.size());
+
+ for(int i=0;i<lights.size();i++) {
+ norm_arr[i] = 1.0/get_normalization(i);
+ }
+
+ const double *normptr=norm_arr.ptr();
+ }
+*/
+ octant_pool.clear();
+ octant_pool_size=0;
+ bvh=NULL;
+ leaf_list=0;
+ cell_count=0;
+ ray_stack=NULL;
+ octant_stack=NULL;
+ octantptr_stack=NULL;
+ bvh_stack=NULL;
+ materials.clear();
+ materials.clear();
+ textures.clear();
+ lights.clear();
+ triangles.clear();
+ endpoint_normal.clear();
+ endpoint_normal_bits.clear();
+ baked_octree_texture_w=0;
+ baked_octree_texture_h=0;
+ paused=false;
+ baking=false;
+
+ bake_thread_exit=false;
+ first_bake_to_map=true;
+ baked_light=Ref<BakedLight>();
+ total_rays=0;
+
+}
+
+BakedLightBaker::BakedLightBaker() {
+ octree_depth=9;
+ lattice_size=4;
+ octant_pool.clear();
+ octant_pool_size=0;
+ bvh=NULL;
+ leaf_list=0;
+ cell_count=0;
+ ray_stack=NULL;
+ bvh_stack=NULL;
+ octant_stack=NULL;
+ octantptr_stack=NULL;
+ plot_size=2.5;
+ max_bounces=2;
+ materials.clear();
+ baked_octree_texture_w=0;
+ baked_octree_texture_h=0;
+ paused=false;
+ baking=false;
+
+ bake_thread_exit=false;
+ total_rays=0;
+ first_bake_to_map=true;
+ linear_color=false;
+
+}
+
+BakedLightBaker::~BakedLightBaker() {
+
+ clear();
+}
diff --git a/editor/plugins/baked_light_baker.h b/editor/plugins/baked_light_baker.h
new file mode 100644
index 000000000..cfce1b81e
--- /dev/null
+++ b/editor/plugins/baked_light_baker.h
@@ -0,0 +1,377 @@
+/*************************************************************************/
+/* baked_light_baker.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 BAKED_LIGHT_BAKER_H
+#define BAKED_LIGHT_BAKER_H
+
+#include "scene/3d/baked_light_instance.h"
+#include "scene/3d/light.h"
+#include "scene/3d/mesh_instance.h"
+#include "os/thread.h"
+
+class BakedLightBaker {
+public:
+
+ enum {
+
+ ATTENUATION_CURVE_LEN=256,
+ OCTANT_POOL_CHUNK=1000000
+ };
+
+ //struct OctantLight {
+
+ // double accum[8][3];
+ //};
+
+ struct Octant {
+ bool leaf;
+ AABB aabb;
+ uint16_t texture_x;
+ uint16_t texture_y;
+ int sampler_ofs;
+ float normal_accum[8][3];
+ double full_accum[3];
+ int parent;
+ union {
+ struct {
+ int next_leaf;
+ float offset[3];
+ int bake_neighbour;
+ bool first_neighbour;
+ double light_accum[8][3];
+ };
+ int children[8];
+ };
+ };
+
+ struct OctantHash {
+
+ int next;
+ uint32_t hash;
+ uint64_t value;
+
+ };
+
+ struct MeshTexture {
+
+ Vector<uint8_t> tex;
+ int tex_w,tex_h;
+
+ _FORCE_INLINE_ void get_color(const Vector2& p_uv,Color& ret) {
+
+ if (tex_w && tex_h) {
+
+ int x = Math::fast_ftoi(Math::fposmod(p_uv.x,1.0)*tex_w);
+ int y = Math::fast_ftoi(Math::fposmod(p_uv.y,1.0)*tex_w);
+ x=CLAMP(x,0,tex_w-1);
+ y=CLAMP(y,0,tex_h-1);
+ const uint8_t*ptr = &tex[(y*tex_w+x)*4];
+ ret.r*=ptr[0]/255.0;
+ ret.g*=ptr[1]/255.0;
+ ret.b*=ptr[2]/255.0;
+ ret.a*=ptr[3]/255.0;
+ }
+ }
+
+ };
+
+ struct Param {
+
+ Color color;
+ MeshTexture*tex;
+ _FORCE_INLINE_ Color get_color(const Vector2& p_uv) {
+
+ Color ret=color;
+ if (tex)
+ tex->get_color(p_uv,ret);
+ return ret;
+
+ }
+
+ };
+
+ struct MeshMaterial {
+
+ Param diffuse;
+ Param specular;
+ Param emission;
+ };
+
+ struct Triangle {
+
+ AABB aabb;
+ Vector3 vertices[3];
+ Vector2 uvs[3];
+ Vector2 bake_uvs[3];
+ Vector3 normals[3];
+ MeshMaterial *material;
+ int baked_texture;
+
+ _FORCE_INLINE_ Vector2 get_uv(const Vector3& p_pos) {
+
+ Vector3 v0 = vertices[1] - vertices[0];
+ Vector3 v1 = vertices[2] - vertices[0];
+ Vector3 v2 = p_pos - vertices[0];
+
+ float d00 = v0.dot( v0);
+ float d01 = v0.dot( v1);
+ float d11 = v1.dot( v1);
+ float d20 = v2.dot( v0);
+ float d21 = v2.dot( v1);
+ float denom = (d00 * d11 - d01 * d01);
+ if (denom==0)
+ return uvs[0];
+ float v = (d11 * d20 - d01 * d21) / denom;
+ float w = (d00 * d21 - d01 * d20) / denom;
+ float u = 1.0f - v - w;
+
+ return uvs[0]*u + uvs[1]*v + uvs[2]*w;
+ }
+
+ _FORCE_INLINE_ void get_uv_and_normal(const Vector3& p_pos,Vector2& r_uv,Vector3& r_normal) {
+
+ Vector3 v0 = vertices[1] - vertices[0];
+ Vector3 v1 = vertices[2] - vertices[0];
+ Vector3 v2 = p_pos - vertices[0];
+
+ float d00 = v0.dot( v0);
+ float d01 = v0.dot( v1);
+ float d11 = v1.dot( v1);
+ float d20 = v2.dot( v0);
+ float d21 = v2.dot( v1);
+ float denom = (d00 * d11 - d01 * d01);
+ if (denom==0) {
+ r_normal=normals[0];
+ r_uv=uvs[0];
+ return;
+ }
+ float v = (d11 * d20 - d01 * d21) / denom;
+ float w = (d00 * d21 - d01 * d20) / denom;
+ float u = 1.0f - v - w;
+
+ r_uv=uvs[0]*u + uvs[1]*v + uvs[2]*w;
+ r_normal=(normals[0]*u+normals[1]*v+normals[2]*w).normalized();
+ }
+ };
+
+
+ struct BVH {
+
+ AABB aabb;
+ Vector3 center;
+ Triangle *leaf;
+ BVH*children[2];
+ };
+
+
+ struct BVHCmpX {
+
+ bool operator()(const BVH* p_left, const BVH* p_right) const {
+
+ return p_left->center.x < p_right->center.x;
+ }
+ };
+
+ struct BVHCmpY {
+
+ bool operator()(const BVH* p_left, const BVH* p_right) const {
+
+ return p_left->center.y < p_right->center.y;
+ }
+ };
+ struct BVHCmpZ {
+
+ bool operator()(const BVH* p_left, const BVH* p_right) const {
+
+ return p_left->center.z < p_right->center.z;
+ }
+ };
+
+ struct BakeTexture {
+
+ Vector<uint8_t> data;
+ int width,height;
+ };
+
+
+ struct LightData {
+
+ VS::LightType type;
+
+ Vector3 pos;
+ Vector3 up;
+ Vector3 left;
+ Vector3 dir;
+ Color diffuse;
+ Color specular;
+ float energy;
+ float length;
+ int rays_thrown;
+ bool bake_shadow;
+
+ float radius;
+ float attenuation;
+ float spot_angle;
+ float darkening;
+ float spot_attenuation;
+ float area;
+
+ float constant;
+
+ bool bake_direct;
+
+ Vector<float> attenuation_table;
+
+ };
+
+
+ Vector<LightData> lights;
+
+ List<MeshMaterial> materials;
+ List<MeshTexture> textures;
+
+ AABB octree_aabb;
+ Vector<Octant> octant_pool;
+ int octant_pool_size;
+ BVH*bvh;
+ Vector<Triangle> triangles;
+ Vector<BakeTexture> baked_textures;
+ Transform base_inv;
+ int leaf_list;
+ int octree_depth;
+ int bvh_depth;
+ int cell_count;
+ uint32_t *ray_stack;
+ BVH **bvh_stack;
+ uint32_t *octant_stack;
+ uint32_t *octantptr_stack;
+
+ struct ThreadStack {
+ uint32_t *octant_stack;
+ uint32_t *octantptr_stack;
+ uint32_t *ray_stack;
+ BVH **bvh_stack;
+ };
+
+ Map<Vector3,Vector3> endpoint_normal;
+ Map<Vector3,uint64_t> endpoint_normal_bits;
+
+ float cell_size;
+ float plot_size; //multiplied by cell size
+ float octree_extra_margin;
+
+ int max_bounces;
+ int64_t total_rays;
+ bool use_diffuse;
+ bool use_specular;
+ bool use_translucency;
+ bool linear_color;
+
+
+ int baked_octree_texture_w;
+ int baked_octree_texture_h;
+ int baked_light_texture_w;
+ int baked_light_texture_h;
+ int lattice_size;
+ float edge_damp;
+ float normal_damp;
+ float tint;
+ float ao_radius;
+ float ao_strength;
+
+ bool paused;
+ bool baking;
+ bool first_bake_to_map;
+
+ Map<Ref<Material>,MeshMaterial*> mat_map;
+ Map<Ref<Texture>,MeshTexture*> tex_map;
+
+
+
+ MeshTexture* _get_mat_tex(const Ref<Texture>& p_tex);
+ void _add_mesh(const Ref<Mesh>& p_mesh,const Ref<Material>& p_mat_override,const Transform& p_xform,int p_baked_texture=-1);
+ void _parse_geometry(Node* p_node);
+ BVH* _parse_bvh(BVH** p_children,int p_size,int p_depth,int& max_depth);
+ void _make_bvh();
+ void _make_octree();
+ void _make_octree_texture();
+ void _octree_insert(int p_octant, Triangle* p_triangle, int p_depth);
+ _FORCE_INLINE_ void _plot_pixel_to_lightmap(int x, int y, int width, int height, uint8_t *image, const Vector3& p_pos,const Vector3& p_normal,double *p_norm_ptr,float mult,float gamma);
+
+
+ void _free_bvh(BVH* p_bvh);
+
+ void _fix_lights();
+
+ Ref<BakedLight> baked_light;
+
+
+ //void _plot_light(const Vector3& p_plot_pos,const AABB& p_plot_aabb,const Color& p_light,int p_octant=0);
+ void _plot_light(ThreadStack& thread_stack,const Vector3& p_plot_pos,const AABB& p_plot_aabb,const Color& p_light,const Color& p_tint_light,bool p_only_full,const Plane& p_plane);
+ //void _plot_light_point(const Vector3& p_plot_pos, Octant *p_octant, const AABB& p_aabb,const Color& p_light);
+
+ float _throw_ray(ThreadStack& thread_stack,bool p_bake_direct,const Vector3& p_begin, const Vector3& p_end,float p_rest,const Color& p_light,float *p_att_curve,float p_att_pos,int p_att_curve_len,int p_bounces,bool p_first_bounce=false,bool p_only_dist=false);
+
+
+ float total_light_area;
+
+ Vector<Thread*> threads;
+
+ bool bake_thread_exit;
+ static void _bake_thread_func(void *arg);
+
+ void _start_thread();
+ void _stop_thread();
+public:
+
+
+ void throw_rays(ThreadStack &thread_stack, int p_amount);
+ double get_normalization(int p_light_idx) const;
+ double get_modifier(int p_light_idx) const;
+
+ void bake(const Ref<BakedLight>& p_light,Node *p_base);
+ bool is_baking();
+ void set_pause(bool p_pause);
+ bool is_paused();
+ uint64_t get_rays_thrown() { return total_rays; }
+
+ Error transfer_to_lightmaps();
+
+ void update_octree_sampler(DVector<int> &p_sampler);
+ void update_octree_images(DVector<uint8_t> &p_octree,DVector<uint8_t> &p_light);
+
+ Ref<BakedLight> get_baked_light() { return baked_light; }
+
+ void clear();
+
+ BakedLightBaker();
+ ~BakedLightBaker();
+
+};
+
+#endif // BAKED_LIGHT_BAKER_H
diff --git a/editor/plugins/baked_light_baker_cmpxchg.cpp b/editor/plugins/baked_light_baker_cmpxchg.cpp
new file mode 100644
index 000000000..5e9228b7d
--- /dev/null
+++ b/editor/plugins/baked_light_baker_cmpxchg.cpp
@@ -0,0 +1,112 @@
+/*************************************************************************/
+/* baked_light_baker_cmpxchg.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "typedefs.h"
+
+
+#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) > 40100
+
+void baked_light_baker_add_64f(double *dst,double value) {
+
+
+ union {
+ int64_t i;
+ double f;
+ } swapy;
+
+
+ while(true) {
+ swapy.f=*dst;
+ int64_t from = swapy.i;
+ swapy.f+=value;
+ int64_t to=swapy.i;
+ if (__sync_bool_compare_and_swap((int64_t*)dst,from,to))
+ break;
+ }
+}
+
+void baked_light_baker_add_64i(int64_t *dst,int64_t value) {
+
+ while(!__sync_bool_compare_and_swap(dst,*dst,(*dst)+value)) {}
+
+}
+
+#elif defined(WINDOWS_ENABLED)
+
+#include "windows.h"
+
+void baked_light_baker_add_64f(double *dst,double value) {
+
+ union {
+ int64_t i;
+ double f;
+ } swapy;
+
+
+ while(true) {
+ swapy.f=*dst;
+ int64_t from = swapy.i;
+ swapy.f+=value;
+ int64_t to=swapy.i;
+ int64_t result = InterlockedCompareExchange64((int64_t*)dst,to,from);
+ if (result==from)
+ break;
+ }
+
+}
+
+void baked_light_baker_add_64i(int64_t *dst,int64_t value) {
+
+ while(true) {
+ int64_t from = *dst;
+ int64_t to = from+value;
+ int64_t result = InterlockedCompareExchange64(dst,to,from);
+ if (result==from)
+ break;
+ }
+}
+
+
+#else
+
+//in goder (the god of programmers) we trust
+#warning seems this platform or compiler does not support safe cmpxchg, your baked lighting may be funny
+
+void baked_light_baker_add_64f(double *dst,double value) {
+
+ *dst+=value;
+
+}
+
+void baked_light_baker_add_64i(int64_t *dst,int64_t value) {
+
+ *dst+=value;
+
+}
+
+#endif
diff --git a/editor/plugins/baked_light_editor_plugin.cpp b/editor/plugins/baked_light_editor_plugin.cpp
new file mode 100644
index 000000000..c8e55ca54
--- /dev/null
+++ b/editor/plugins/baked_light_editor_plugin.cpp
@@ -0,0 +1,375 @@
+/*************************************************************************/
+/* baked_light_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "baked_light_editor_plugin.h"
+#include "scene/gui/box_container.h"
+#include "scene/3d/mesh_instance.h"
+#include "io/marshalls.h"
+#include "io/resource_saver.h"
+
+
+
+
+
+void BakedLightEditor::_end_baking() {
+
+ baker->clear();
+ set_process(false);
+ button_bake->set_pressed(false);
+ bake_info->set_text("");
+}
+
+void BakedLightEditor::_node_removed(Node *p_node) {
+
+ if(p_node==node) {
+ _end_baking();
+ node=NULL;
+
+ hide();
+ }
+
+}
+
+
+
+
+
+void BakedLightEditor::_notification(int p_option) {
+
+
+ if (p_option==NOTIFICATION_ENTER_TREE) {
+
+ button_bake->set_icon(get_icon("Bake","EditorIcons"));
+ button_reset->set_icon(get_icon("Reload","EditorIcons"));
+ button_make_lightmaps->set_icon(get_icon("LightMap","EditorIcons"));
+ }
+
+ if (p_option==NOTIFICATION_PROCESS) {
+
+ if (baker->is_baking() && !baker->is_paused()) {
+
+ update_timeout-=get_process_delta_time();
+ if (update_timeout<0) {
+
+ if (baker->get_baked_light()!=node->get_baked_light()) {
+ _end_baking();
+ return;
+ }
+
+ uint64_t t = OS::get_singleton()->get_ticks_msec();
+
+#ifdef DEBUG_CUBES
+ double norm = baker->get_normalization();
+ float max_lum=0;
+
+ {
+ DVector<Color>::Write cw=colors.write();
+ BakedLightBaker::Octant *octants=baker->octant_pool.ptr();
+ BakedLightBaker::Octant *oct = &octants[baker->leaf_list];
+ int vert_idx=0;
+
+ while(oct) {
+
+
+
+ Color colors[8];
+ for(int i=0;i<8;i++) {
+
+ colors[i].r=oct->light_accum[i][0]/norm;
+ colors[i].g=oct->light_accum[i][1]/norm;
+ colors[i].b=oct->light_accum[i][2]/norm;
+
+ float lum = colors[i].get_v();
+ //if (lum<0.05)
+ // color.a=0;
+ if (lum>max_lum)
+ max_lum=lum;
+
+ }
+ static const int vert2cub[36]={7,3,1,1,5,7,7,6,2,2,3,7,7,5,4,4,6,7,2,6,4,4,0,2,4,5,1,1,0,4,1,3,2,2,0,1};
+ for (int i=0;i<36;i++) {
+
+
+ cw[vert_idx++]=colors[vert2cub[i]];
+ }
+
+ if (oct->next_leaf)
+ oct=&octants[oct->next_leaf];
+ else
+ oct=NULL;
+
+ }
+ }
+ print_line("MSCOL: "+itos(OS::get_singleton()->get_ticks_msec()-t));
+ t = OS::get_singleton()->get_ticks_msec();
+
+ Array a;
+ a.resize(Mesh::ARRAY_MAX);
+ a[Mesh::ARRAY_VERTEX]=vertices;
+ a[Mesh::ARRAY_COLOR]=colors;
+ while(mesh->get_surface_count())
+ mesh->surface_remove(0);
+ mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES,a);
+ mesh->surface_set_material(0,material);
+#endif
+ ERR_FAIL_COND(node->get_baked_light().is_null());
+
+ baker->update_octree_images(octree_texture,light_texture);
+ baker->update_octree_sampler(octree_sampler);
+ // print_line("sampler size: "+itos(octree_sampler.size()*4));
+
+#if 1
+//debug
+ Image img(baker->baked_octree_texture_w,baker->baked_octree_texture_h,0,Image::FORMAT_RGBA,octree_texture);
+ Ref<ImageTexture> it = memnew( ImageTexture );
+ it->create_from_image(img);
+ ResourceSaver::save("baked_octree.png",it);
+
+
+#endif
+
+
+ uint64_t rays_snap = baker->get_rays_thrown();
+ int rays_sec = (rays_snap-last_rays_time)*1.0-(update_timeout);
+ last_rays_time=rays_snap;
+
+ bake_info->set_text("rays/s: "+itos(rays_sec));
+ update_timeout=1;
+ print_line("MSUPDATE: "+itos(OS::get_singleton()->get_ticks_msec()-t));
+ t=OS::get_singleton()->get_ticks_msec();
+ node->get_baked_light()->set_octree(octree_texture);
+ node->get_baked_light()->set_light(light_texture);
+ node->get_baked_light()->set_sampler_octree(octree_sampler);
+ node->get_baked_light()->set_edited(true);
+
+ print_line("MSSET: "+itos(OS::get_singleton()->get_ticks_msec()-t));
+
+
+
+ }
+ }
+ }
+}
+
+
+void BakedLightEditor::_menu_option(int p_option) {
+
+
+ switch(p_option) {
+
+
+ case MENU_OPTION_BAKE: {
+
+ ERR_FAIL_COND(!node);
+ ERR_FAIL_COND(node->get_baked_light().is_null());
+ baker->bake(node->get_baked_light(),node);
+ node->get_baked_light()->set_mode(BakedLight::MODE_OCTREE);
+ update_timeout=0;
+ set_process(true);
+
+
+ } break;
+ case MENU_OPTION_CLEAR: {
+
+
+
+ } break;
+
+ }
+}
+
+void BakedLightEditor::_bake_pressed() {
+
+ ERR_FAIL_COND(!node);
+ const String conf_warning = node->get_configuration_warning();
+ if (!conf_warning.empty()) {
+ err_dialog->set_text(conf_warning);
+ err_dialog->popup_centered_minsize();
+ button_bake->set_pressed(false);
+ return;
+ }
+
+ if (baker->is_baking()) {
+
+ baker->set_pause(!button_bake->is_pressed());
+ if (baker->is_paused()) {
+
+ set_process(false);
+ bake_info->set_text("");
+ button_reset->show();
+ button_make_lightmaps->show();
+
+ } else {
+
+ update_timeout=0;
+ set_process(true);
+ button_make_lightmaps->hide();
+ button_reset->hide();
+ }
+ } else {
+ baker->bake(node->get_baked_light(),node);
+ node->get_baked_light()->set_mode(BakedLight::MODE_OCTREE);
+ update_timeout=0;
+
+ last_rays_time=0;
+ button_bake->set_pressed(false);
+
+ set_process(true);
+ }
+
+}
+
+void BakedLightEditor::_clear_pressed(){
+
+ baker->clear();
+ button_bake->set_pressed(false);
+ bake_info->set_text("");
+
+}
+
+void BakedLightEditor::edit(BakedLightInstance *p_baked_light) {
+
+ if (p_baked_light==NULL || node==p_baked_light) {
+ return;
+ }
+ if (node && node!=p_baked_light)
+ _end_baking();
+
+
+ node=p_baked_light;
+ //_end_baking();
+
+}
+
+void BakedLightEditor::_bake_lightmaps() {
+
+ Error err = baker->transfer_to_lightmaps();
+ if (err) {
+
+ err_dialog->set_text("Error baking to lightmaps!\nMake sure that a bake has just\n happened and that lightmaps are\n configured. ");
+ err_dialog->popup_centered_minsize();
+ return;
+ }
+
+ node->get_baked_light()->set_mode(BakedLight::MODE_LIGHTMAPS);
+
+
+}
+
+void BakedLightEditor::_bind_methods() {
+
+ ObjectTypeDB::bind_method("_menu_option",&BakedLightEditor::_menu_option);
+ ObjectTypeDB::bind_method("_bake_pressed",&BakedLightEditor::_bake_pressed);
+ ObjectTypeDB::bind_method("_clear_pressed",&BakedLightEditor::_clear_pressed);
+ ObjectTypeDB::bind_method("_bake_lightmaps",&BakedLightEditor::_bake_lightmaps);
+}
+
+BakedLightEditor::BakedLightEditor() {
+
+
+ bake_hbox = memnew( HBoxContainer );
+ button_bake = memnew( ToolButton );
+ button_bake->set_text(TTR("Bake!"));
+ button_bake->set_toggle_mode(true);
+ button_reset = memnew( Button );
+ button_make_lightmaps = memnew( Button );
+ button_bake->set_tooltip("Start/Unpause the baking process.\nThis bakes lighting into the lightmap octree.");
+ button_make_lightmaps ->set_tooltip("Convert the lightmap octree to lightmap textures\n(must have set up UV/Lightmaps properly before!).");
+
+
+ bake_info = memnew( Label );
+ bake_hbox->add_child( button_bake );
+ bake_hbox->add_child( button_reset );
+ bake_hbox->add_child( bake_info );
+
+ err_dialog = memnew( AcceptDialog );
+ add_child(err_dialog);
+ node=NULL;
+ baker = memnew( BakedLightBaker );
+
+ bake_hbox->add_child(button_make_lightmaps);
+ button_make_lightmaps->hide();
+
+ button_bake->connect("pressed",this,"_bake_pressed");
+ button_reset->connect("pressed",this,"_clear_pressed");
+ button_make_lightmaps->connect("pressed",this,"_bake_lightmaps");
+ button_reset->hide();
+ button_reset->set_tooltip(TTR("Reset the lightmap octree baking process (start over)."));
+
+
+ update_timeout=0;
+
+
+
+}
+
+BakedLightEditor::~BakedLightEditor() {
+
+ memdelete(baker);
+}
+
+void BakedLightEditorPlugin::edit(Object *p_object) {
+
+ baked_light_editor->edit(p_object->cast_to<BakedLightInstance>());
+}
+
+bool BakedLightEditorPlugin::handles(Object *p_object) const {
+
+ return p_object->is_type("BakedLightInstance");
+}
+
+void BakedLightEditorPlugin::make_visible(bool p_visible) {
+
+ if (p_visible) {
+ baked_light_editor->show();
+ baked_light_editor->bake_hbox->show();
+ } else {
+
+ baked_light_editor->hide();
+ baked_light_editor->bake_hbox->hide();
+ baked_light_editor->edit(NULL);
+ }
+
+}
+
+BakedLightEditorPlugin::BakedLightEditorPlugin(EditorNode *p_node) {
+
+ editor=p_node;
+ baked_light_editor = memnew( BakedLightEditor );
+ editor->get_viewport()->add_child(baked_light_editor);
+ add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU,baked_light_editor->bake_hbox);
+ baked_light_editor->hide();
+ baked_light_editor->bake_hbox->hide();
+}
+
+
+BakedLightEditorPlugin::~BakedLightEditorPlugin()
+{
+}
+
+
diff --git a/editor/plugins/baked_light_editor_plugin.h b/editor/plugins/baked_light_editor_plugin.h
new file mode 100644
index 000000000..d533de15a
--- /dev/null
+++ b/editor/plugins/baked_light_editor_plugin.h
@@ -0,0 +1,120 @@
+/*************************************************************************/
+/* baked_light_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 BAKED_LIGHT_EDITOR_PLUGIN_H
+#define BAKED_LIGHT_EDITOR_PLUGIN_H
+
+#include "editor/editor_plugin.h"
+#include "editor/editor_node.h"
+#include "editor/plugins/baked_light_baker.h"
+#include "scene/gui/spin_box.h"
+
+
+
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+
+
+
+class MeshInstance;
+
+class BakedLightEditor : public Control {
+
+ OBJ_TYPE(BakedLightEditor, Control );
+
+
+ float update_timeout;
+ DVector<uint8_t> octree_texture;
+ DVector<uint8_t> light_texture;
+ DVector<int> octree_sampler;
+
+ BakedLightBaker *baker;
+ AcceptDialog *err_dialog;
+
+ HBoxContainer *bake_hbox;
+ Button *button_bake;
+ Button *button_reset;
+ Button *button_make_lightmaps;
+ Label *bake_info;
+
+ uint64_t last_rays_time;
+
+
+
+ BakedLightInstance *node;
+
+ enum Menu {
+
+ MENU_OPTION_BAKE,
+ MENU_OPTION_CLEAR
+ };
+
+ void _bake_lightmaps();
+
+ void _bake_pressed();
+ void _clear_pressed();
+
+ void _end_baking();
+ void _menu_option(int);
+
+friend class BakedLightEditorPlugin;
+protected:
+ void _node_removed(Node *p_node);
+ static void _bind_methods();
+ void _notification(int p_what);
+public:
+
+ void edit(BakedLightInstance *p_baked_light);
+ BakedLightEditor();
+ ~BakedLightEditor();
+};
+
+class BakedLightEditorPlugin : public EditorPlugin {
+
+ OBJ_TYPE( BakedLightEditorPlugin, EditorPlugin );
+
+ BakedLightEditor *baked_light_editor;
+ EditorNode *editor;
+
+public:
+
+ virtual String get_name() const { return "BakedLight"; }
+ 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);
+
+ BakedLightEditorPlugin(EditorNode *p_node);
+ ~BakedLightEditorPlugin();
+
+};
+
+#endif // MULTIMESH_EDITOR_PLUGIN_H
+
+
diff --git a/editor/plugins/camera_editor_plugin.cpp b/editor/plugins/camera_editor_plugin.cpp
new file mode 100644
index 000000000..0881bfd91
--- /dev/null
+++ b/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-2017 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(TTR("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/editor/plugins/camera_editor_plugin.h b/editor/plugins/camera_editor_plugin.h
new file mode 100644
index 000000000..45d33c948
--- /dev/null
+++ b/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-2017 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 "editor/editor_plugin.h"
+#include "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/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
new file mode 100644
index 000000000..1177940b5
--- /dev/null
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -0,0 +1,4002 @@
+/*************************************************************************/
+/* canvas_item_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "editor/editor_node.h"
+#include "os/keyboard.h"
+#include "scene/main/viewport.h"
+#include "scene/main/canvas_layer.h"
+#include "scene/2d/sprite.h"
+#include "scene/2d/light_2d.h"
+#include "scene/2d/particles_2d.h"
+#include "scene/2d/polygon_2d.h"
+#include "scene/2d/screen_button.h"
+#include "globals.h"
+#include "os/input.h"
+#include "editor/editor_settings.h"
+#include "scene/gui/grid_container.h"
+#include "scene/gui/patch_9_frame.h"
+#include "editor/animation_editor.h"
+#include "editor/plugins/animation_player_editor_plugin.h"
+#include "editor/script_editor_debugger.h"
+#include "editor/plugins/script_editor_plugin.h"
+#include "scene/resources/packed_scene.h"
+
+
+#define MIN_ZOOM 0.01
+#define MAX_ZOOM 100
+
+
+class SnapDialog : public ConfirmationDialog {
+
+ OBJ_TYPE(SnapDialog,ConfirmationDialog);
+
+friend class CanvasItemEditor;
+
+ SpinBox *grid_offset_x;
+ SpinBox *grid_offset_y;
+ SpinBox *grid_step_x;
+ SpinBox *grid_step_y;
+ SpinBox *rotation_offset;
+ SpinBox *rotation_step;
+
+public:
+ SnapDialog() : ConfirmationDialog() {
+ const int SPIN_BOX_GRID_RANGE = 256;
+ const int SPIN_BOX_ROTATION_RANGE = 360;
+ Label *label;
+ VBoxContainer *container;
+ GridContainer *child_container;
+
+ set_title(TTR("Configure Snap"));
+ get_ok()->set_text(TTR("Close"));
+
+ container = memnew( VBoxContainer );
+ add_child(container);
+ set_child_rect(container);
+
+ child_container = memnew( GridContainer );
+ child_container->set_columns(3);
+ container->add_child(child_container);
+
+ label = memnew( Label );
+ label->set_text(TTR("Grid Offset:"));
+ child_container->add_child(label);
+ label->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ grid_offset_x = memnew( SpinBox );
+ grid_offset_x->set_min(-SPIN_BOX_GRID_RANGE);
+ grid_offset_x->set_max(SPIN_BOX_GRID_RANGE);
+ grid_offset_x->set_suffix("px");
+ child_container->add_child(grid_offset_x);
+
+ grid_offset_y = memnew( SpinBox );
+ grid_offset_y->set_min(-SPIN_BOX_GRID_RANGE);
+ grid_offset_y->set_max(SPIN_BOX_GRID_RANGE);
+ grid_offset_y->set_suffix("px");
+ child_container->add_child(grid_offset_y);
+
+ label = memnew( Label );
+ label->set_text(TTR("Grid Step:"));
+ child_container->add_child(label);
+ label->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ grid_step_x = memnew( SpinBox );
+ grid_step_x->set_min(-SPIN_BOX_GRID_RANGE);
+ grid_step_x->set_max(SPIN_BOX_GRID_RANGE);
+ grid_step_x->set_suffix("px");
+ child_container->add_child(grid_step_x);
+
+ grid_step_y = memnew( SpinBox );
+ grid_step_y->set_min(-SPIN_BOX_GRID_RANGE);
+ grid_step_y->set_max(SPIN_BOX_GRID_RANGE);
+ grid_step_y->set_suffix("px");
+ child_container->add_child(grid_step_y);
+
+ container->add_child( memnew( HSeparator ) );
+
+ child_container = memnew( GridContainer );
+ child_container->set_columns(2);
+ container->add_child(child_container);
+
+ label = memnew( Label );
+ label->set_text(TTR("Rotation Offset:"));
+ child_container->add_child(label);
+ label->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ rotation_offset = memnew( SpinBox );
+ rotation_offset->set_min(-SPIN_BOX_ROTATION_RANGE);
+ rotation_offset->set_max(SPIN_BOX_ROTATION_RANGE);
+ rotation_offset->set_suffix("deg");
+ child_container->add_child(rotation_offset);
+
+ label = memnew( Label );
+ label->set_text(TTR("Rotation Step:"));
+ child_container->add_child(label);
+ label->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ rotation_step = memnew( SpinBox );
+ rotation_step->set_min(-SPIN_BOX_ROTATION_RANGE);
+ rotation_step->set_max(SPIN_BOX_ROTATION_RANGE);
+ rotation_step->set_suffix("deg");
+ child_container->add_child(rotation_step);
+ }
+
+ void set_fields(const Point2 p_grid_offset, const Size2 p_grid_step, const float p_rotation_offset, const float p_rotation_step) {
+ grid_offset_x->set_val(p_grid_offset.x);
+ grid_offset_y->set_val(p_grid_offset.y);
+ grid_step_x->set_val(p_grid_step.x);
+ grid_step_y->set_val(p_grid_step.y);
+ rotation_offset->set_val(p_rotation_offset * (180 / Math_PI));
+ rotation_step->set_val(p_rotation_step * (180 / Math_PI));
+ }
+
+ void get_fields(Point2 &p_grid_offset, Size2 &p_grid_step, float &p_rotation_offset, float &p_rotation_step) {
+ p_grid_offset.x = grid_offset_x->get_val();
+ p_grid_offset.y = grid_offset_y->get_val();
+ p_grid_step.x = grid_step_x->get_val();
+ p_grid_step.y = grid_step_y->get_val();
+ p_rotation_offset = rotation_offset->get_val() / (180 / Math_PI);
+ p_rotation_step = rotation_step->get_val() / (180 / Math_PI);
+ }
+};
+
+void CanvasItemEditor::_edit_set_pivot(const Vector2& mouse_pos) {
+ List<Node*> &selection = editor_selection->get_selected_node_list();
+
+ undo_redo->create_action(TTR("Move Pivot"));
+
+ for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
+
+ Node2D *n2d = E->get()->cast_to<Node2D>();
+
+ if (n2d && n2d->edit_has_pivot()) {
+
+ Vector2 offset = n2d->edit_get_pivot();
+ Vector2 gpos = n2d->get_global_pos();
+
+ Vector2 local_mouse_pos = n2d->get_canvas_transform().affine_inverse().xform(mouse_pos);
+
+ Vector2 motion_ofs = gpos-local_mouse_pos;
+
+ undo_redo->add_do_method(n2d,"set_global_pos",local_mouse_pos);
+ undo_redo->add_do_method(n2d,"edit_set_pivot",offset+n2d->get_global_transform().affine_inverse().basis_xform(motion_ofs));
+ undo_redo->add_undo_method(n2d,"set_global_pos",gpos);
+ undo_redo->add_undo_method(n2d,"edit_set_pivot",offset);
+ for(int i=0;i<n2d->get_child_count();i++) {
+ Node2D *n2dc = n2d->get_child(i)->cast_to<Node2D>();
+ if (!n2dc)
+ continue;
+
+ undo_redo->add_do_method(n2dc,"set_global_pos",n2dc->get_global_pos());
+ undo_redo->add_undo_method(n2dc,"set_global_pos",n2dc->get_global_pos());
+
+ }
+
+ }
+
+ }
+
+ undo_redo->commit_action();
+
+}
+
+void CanvasItemEditor::_unhandled_key_input(const InputEvent& p_ev) {
+
+ if (!is_visible() || get_viewport()->gui_has_modal_stack())
+ return;
+
+ if (p_ev.key.mod.control)
+ return;
+
+ if (p_ev.key.pressed && !p_ev.key.echo && p_ev.key.scancode==KEY_V && drag==DRAG_NONE && can_move_pivot) {
+
+ if (p_ev.key.mod.shift) {
+ //move drag pivot
+ drag=DRAG_PIVOT;
+ } else if (!Input::get_singleton()->is_mouse_button_pressed(0)) {
+
+ List<Node*> &selection = editor_selection->get_selected_node_list();
+
+ Vector2 mouse_pos = viewport->get_local_mouse_pos();
+ if (selection.size() && viewport->get_rect().has_point(mouse_pos)) {
+ //just in case, make it work if over viewport
+ mouse_pos=transform.affine_inverse().xform(mouse_pos);
+ mouse_pos=snap_point(mouse_pos);
+
+ _edit_set_pivot(mouse_pos);
+ }
+
+ }
+ }
+
+}
+
+void CanvasItemEditor::_tool_select(int p_index) {
+
+
+ ToolButton *tb[TOOL_MAX]={select_button,list_select_button,move_button,rotate_button,pivot_button,pan_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 );
+}
+
+inline float _snap_scalar(float p_offset, float p_step, bool p_snap_relative, float p_target, float p_start) {
+ float offset = p_snap_relative ? p_start : p_offset;
+ return p_step != 0 ? Math::stepify(p_target - offset, p_step) + offset : p_target;
+}
+
+Vector2 CanvasItemEditor::snap_point(Vector2 p_target, Vector2 p_start) const {
+ if (snap_grid) {
+ p_target.x = _snap_scalar(snap_offset.x, snap_step.x, snap_relative, p_target.x, p_start.x);
+ p_target.y = _snap_scalar(snap_offset.y, snap_step.y, snap_relative, p_target.y, p_start.y);
+ }
+ if (snap_pixel)
+ p_target = p_target.snapped(Size2(1, 1));
+
+ return p_target;
+}
+
+float CanvasItemEditor::snap_angle(float p_target, float p_start) const {
+ return snap_rotation ? _snap_scalar(snap_rotation_offset, snap_rotation_step, snap_relative, p_target, p_start) : p_target;
+}
+
+Dictionary CanvasItemEditor::get_state() const {
+
+ Dictionary state;
+ state["zoom"]=zoom;
+ state["ofs"]=Point2(h_scroll->get_val(),v_scroll->get_val());
+// state["ofs"]=-transform.get_origin();
+ state["snap_offset"]=snap_offset;
+ state["snap_step"]=snap_step;
+ state["snap_rotation_offset"]=snap_rotation_offset;
+ state["snap_rotation_step"]=snap_rotation_step;
+ state["snap_grid"]=snap_grid;
+ state["snap_show_grid"]=snap_show_grid;
+ state["snap_rotation"]=snap_rotation;
+ state["snap_relative"]=snap_relative;
+ state["snap_pixel"]=snap_pixel;
+ 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("snap_step")) {
+ snap_step=state["snap_step"];
+ }
+
+ if (state.has("snap_offset")) {
+ snap_offset=state["snap_offset"];
+ }
+
+ if (state.has("snap_rotation_step")) {
+ snap_rotation_step=state["snap_rotation_step"];
+ }
+
+ if (state.has("snap_rotation_offset")) {
+ snap_rotation_offset=state["snap_rotation_offset"];
+ }
+
+ if (state.has("snap_grid")) {
+ snap_grid=state["snap_grid"];
+ int idx = edit_menu->get_popup()->get_item_index(SNAP_USE);
+ edit_menu->get_popup()->set_item_checked(idx,snap_grid);
+ }
+
+ if (state.has("snap_show_grid")) {
+ snap_show_grid=state["snap_show_grid"];
+ int idx = edit_menu->get_popup()->get_item_index(SNAP_SHOW_GRID);
+ edit_menu->get_popup()->set_item_checked(idx,snap_show_grid);
+ }
+
+ if (state.has("snap_rotation")) {
+ snap_rotation=state["snap_rotation"];
+ int idx = edit_menu->get_popup()->get_item_index(SNAP_USE_ROTATION);
+ edit_menu->get_popup()->set_item_checked(idx,snap_rotation);
+ }
+
+ if (state.has("snap_relative")) {
+ snap_relative=state["snap_relative"];
+ int idx = edit_menu->get_popup()->get_item_index(SNAP_RELATIVE);
+ edit_menu->get_popup()->set_item_checked(idx,snap_relative);
+ }
+
+ if (state.has("snap_pixel")) {
+ snap_pixel=state["snap_pixel"];
+ int idx = edit_menu->get_popup()->get_item_index(SNAP_USE_PIXEL);
+ edit_menu->get_popup()->set_item_checked(idx,snap_pixel);
+ }
+}
+
+
+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() {
+
+ if (AnimationPlayerEditor::singleton->get_key_editor()->is_visible())
+ animation_hb->show();
+ else
+ animation_hb->hide();
+}
+
+bool CanvasItemEditor::_is_part_of_subscene(CanvasItem *p_item) {
+
+ Node* scene_node = get_tree()->get_edited_scene_root();
+ Node* item_owner = p_item->get_owner();
+
+ return item_owner && item_owner!=scene_node && p_item!=scene_node && item_owner->get_filename()!="";
+}
+
+// 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>();
+ 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_") && !_is_part_of_subscene(c) && !c->cast_to<CanvasLayer>()) {
+
+ 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_pos(const Point2 &p_pos,Node* p_node,const Matrix32& p_parent_xform,const Matrix32& p_canvas_xform, Vector<_SelectResult> &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_pos(p_pos,p_node->get_child(i),p_parent_xform * c->get_transform(),p_canvas_xform, r_items);
+ else {
+ CanvasLayer *cl = p_node->cast_to<CanvasLayer>();
+ _find_canvas_items_at_pos(p_pos,p_node->get_child(i),transform ,cl ? cl->get_transform() : p_canvas_xform, r_items); //use base transform
+ }
+ }
+
+
+ if (c && c->is_visible() && !c->has_meta("_edit_lock_") && !c->cast_to<CanvasLayer>()) {
+
+ 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)) {
+ Node2D *node=c->cast_to<Node2D>();
+
+ _SelectResult res;
+ res.item=c;
+ res.z=node?node->get_z():0;
+ res.has_z=node;
+ r_items.push_back(res);
+ }
+
+ }
+
+ return;
+}
+
+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>();
+
+
+ bool inherited=p_node!=get_tree()->get_edited_scene_root() && p_node->get_filename()!="";
+ bool editable=false;
+ if (inherited){
+ editable=EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(p_node);
+ }
+ bool lock_children=p_node->has_meta("_edit_group_") && p_node->get_meta("_edit_group_");
+ if (!lock_children && (!inherited || editable)) {
+ 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>();
+ _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_") && !c->cast_to<CanvasLayer>()) {
+
+ 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);
+
+ }
+ }
+
+
+}
+
+bool CanvasItemEditor::_select(CanvasItem *item, Point2 p_click_pos, bool p_append, bool p_drag) {
+
+ if (p_append) {
+ //additive selection
+
+ if (!item) {
+
+ if (p_drag) {
+ drag_from=transform.affine_inverse().xform(p_click_pos);
+
+ box_selecting=true;
+ box_selecting_to=drag_from;
+ }
+
+ return false; //nothing to add
+ }
+
+ if (editor_selection->is_selected(item)) {
+ //already in here, erase it
+ editor_selection->remove_node(item);
+ //_remove_canvas_item(c);
+
+ viewport->update();
+ return false;
+
+ }
+ _append_canvas_item(item);
+ viewport->update();
+
+ return true;
+
+ } else {
+ //regular selection
+
+ if (!item) {
+ //clear because nothing clicked
+ editor_selection->clear();
+
+ if (p_drag) {
+ drag_from=transform.affine_inverse().xform(p_click_pos);
+
+ box_selecting=true;
+ box_selecting_to=drag_from;
+ }
+
+ viewport->update();
+ return false;
+ }
+
+ if (!editor_selection->is_selected(item)) {
+ //select a new one and clear previous selection
+ editor_selection->clear();
+ editor_selection->add_node(item);
+ //reselect
+ if (get_tree()->is_editor_hint()) {
+ editor->call("edit_node",item);
+ }
+
+ }
+
+ if (p_drag) {
+ //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 || !canvas_item->is_visible())
+ continue;
+ if (canvas_item->get_viewport()!=EditorNode::get_singleton()->get_scene_root())
+ 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(p_click_pos);
+ drag_point_from=_find_topleftmost_point();
+ }
+
+ viewport->update();
+
+ return true;
+
+ }
+}
+
+void CanvasItemEditor::_key_move(const Vector2& p_dir, bool p_snap, KeyMoveMODE p_move_mode) {
+
+
+ if (drag!=DRAG_NONE)
+ return;
+
+ if (editor_selection->get_selected_node_list().empty())
+ return;
+
+ undo_redo->create_action(TTR("Move Action"),UndoRedo::MERGE_ENDS);
+
+ 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 || !canvas_item->is_visible())
+ continue;
+ if (canvas_item->get_viewport()!=EditorNode::get_singleton()->get_scene_root())
+ 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 = p_dir;
+ if (p_snap)
+ drag*=snap_step;
+
+ undo_redo->add_undo_method(canvas_item,"edit_set_state",canvas_item->edit_get_state());
+
+ if (p_move_mode == MOVE_VIEW_BASE) {
+
+ // drag = transform.affine_inverse().basis_xform(p_dir); // zoom sensitive
+ drag = canvas_item->get_global_transform_with_canvas().affine_inverse().basis_xform(drag);
+ Rect2 local_rect = canvas_item->get_item_rect();
+ local_rect.pos+=drag;
+ undo_redo->add_do_method(canvas_item,"edit_set_rect",local_rect);
+
+ } else { // p_move_mode==MOVE_LOCAL_BASE || p_move_mode==MOVE_LOCAL_WITH_ROT
+
+ if (Node2D *node_2d = canvas_item->cast_to<Node2D>()) {
+
+ if (p_move_mode == MOVE_LOCAL_WITH_ROT) {
+ Matrix32 m;
+ m.rotate( node_2d->get_rot() );
+ drag = m.xform(drag);
+ }
+ node_2d->set_pos(node_2d->get_pos() + drag);
+
+ } else if (Control *control = canvas_item->cast_to<Control>()) {
+
+ control->set_pos(control->get_pos()+drag);
+ }
+ }
+ }
+
+ 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 || !canvas_item->is_visible())
+ continue;
+ if (canvas_item->get_viewport()!=EditorNode::get_singleton()->get_scene_root())
+ 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 || !canvas_item->is_visible())
+ continue;
+
+ if (canvas_item->get_viewport()!=EditorNode::get_singleton()->get_scene_root())
+ 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 || !canvas_item->is_visible())
+ continue;
+ if (canvas_item->get_viewport()!=EditorNode::get_singleton()->get_scene_root())
+ 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::_snap_changed() {
+ ((SnapDialog *)snap_dialog)->get_fields(snap_offset, snap_step, snap_rotation_offset, snap_rotation_step);
+ viewport->update();
+}
+
+void CanvasItemEditor::_dialog_value_changed(double) {
+
+ if (updating_value_dialog)
+ return;
+
+ switch(last_option) {
+
+ case ZOOM_SET: {
+
+ zoom=dialog_val->get_val()/100.0;
+ _update_scroll(0);
+ viewport->update();
+
+ } break;
+ default:{}
+ }
+}
+
+void CanvasItemEditor::_selection_result_pressed(int p_result) {
+
+ if (selection_results.size() <= p_result)
+ return;
+
+ CanvasItem *item=selection_results[p_result].item;
+
+ if (item)
+ _select(item, Point2(), additive_selection, false);
+}
+
+void CanvasItemEditor::_selection_menu_hide() {
+
+ selection_results.clear();
+ selection_menu->clear();
+ selection_menu->set_size(Vector2(0, 0));
+}
+
+bool CanvasItemEditor::get_remove_list(List<Node*> *p_list) {
+
+
+ return false;//!p_list->empty();
+}
+
+
+void CanvasItemEditor::_list_select(const InputEventMouseButton& b) {
+
+ Point2 click=Point2(b.x,b.y);
+
+ Node* scene = editor->get_edited_scene();
+ if (!scene)
+ return;
+
+ _find_canvas_items_at_pos(click, scene,transform,Matrix32(), selection_results);
+
+ for(int i=0;i<selection_results.size();i++) {
+ CanvasItem *item=selection_results[i].item;
+ if (item!=scene && item->get_owner()!=scene && !scene->is_editable_instance(item->get_owner())) {
+ //invalid result
+ selection_results.remove(i);
+ i--;
+ }
+
+ }
+
+ if (selection_results.size() == 1) {
+
+ CanvasItem *item = selection_results[0].item;
+ selection_results.clear();
+
+ additive_selection=b.mod.shift;
+ if (!_select(item, click, additive_selection, false))
+ return;
+
+ } else if (!selection_results.empty()) {
+
+ selection_results.sort();
+
+ NodePath root_path = get_tree()->get_edited_scene_root()->get_path();
+ StringName root_name = root_path.get_name(root_path.get_name_count()-1);
+
+ for (int i = 0; i < selection_results.size(); i++) {
+
+ CanvasItem *item=selection_results[i].item;
+
+
+ Ref<Texture> icon;
+ if (item->has_meta("_editor_icon"))
+ icon=item->get_meta("_editor_icon");
+ else
+ icon=get_icon( has_icon(item->get_type(),"EditorIcons")?item->get_type():String("Object"),"EditorIcons");
+
+ String node_path="/"+root_name+"/"+root_path.rel_path_to(item->get_path());
+
+ selection_menu->add_item(item->get_name());
+ selection_menu->set_item_icon(i, icon );
+ selection_menu->set_item_metadata(i, node_path);
+ selection_menu->set_item_tooltip(i,String(item->get_name())+
+ "\nType: "+item->get_type()+"\nPath: "+node_path);
+ }
+
+ additive_selection=b.mod.shift;
+
+ selection_menu->set_global_pos(Vector2( b.global_x, b.global_y ));
+ selection_menu->popup();
+ selection_menu->call_deferred("grab_click_focus");
+ selection_menu->set_invalidate_click_until_motion();
+
+
+ return;
+ }
+
+}
+
+void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) {
+
+ {
+
+ EditorNode *en = editor;
+ EditorPluginList *over_plugin_list = en->get_editor_plugins_over();
+
+ if (!over_plugin_list->empty()) {
+ bool discard = over_plugin_list->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) {
+
+ if (zoom<MIN_ZOOM)
+ return;
+
+ float prev_zoom=zoom;
+ zoom=zoom*0.95;
+ {
+ Point2 ofs(b.x,b.y);
+ ofs = ofs/prev_zoom - ofs/zoom;
+ h_scroll->set_val( h_scroll->get_val() + ofs.x );
+ v_scroll->set_val( v_scroll->get_val() + ofs.y );
+ }
+ _update_scroll(0);
+ viewport->update();
+ return;
+ }
+
+ if (b.button_index==BUTTON_WHEEL_UP) {
+
+ if (zoom>MAX_ZOOM)
+ return;
+
+ float prev_zoom=zoom;
+ zoom=zoom*(1.0/0.95);
+ {
+ Point2 ofs(b.x,b.y);
+ ofs = ofs/prev_zoom - ofs/zoom;
+ h_scroll->set_val( h_scroll->get_val() + ofs.x );
+ v_scroll->set_val( v_scroll->get_val() + ofs.y );
+ }
+
+ _update_scroll(0);
+ viewport->update();
+ return;
+ }
+
+ if (b.button_index==BUTTON_RIGHT) {
+
+
+ if (b.pressed && (tool==TOOL_SELECT && b.mod.alt)) {
+
+ _list_select(b);
+ return;
+ }
+
+ if (get_item_count() > 0 && drag!=DRAG_NONE) {
+ //cancel drag
+
+ if (bone_ik_list.size()) {
+
+ for(List<BoneIK>::Element *E=bone_ik_list.back();E;E=E->prev()) {
+
+ E->get().node->edit_set_state(E->get().orig_state);
+ }
+
+ bone_ik_list.clear();
+
+ } else {
+
+
+ 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 || !canvas_item->is_visible())
+ continue;
+ if (canvas_item->get_viewport()!=EditorNode::get_singleton()->get_scene_root())
+ 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);
+
+ }
+ }
+
+ drag=DRAG_NONE;
+ viewport->update();
+ can_move_pivot=false;
+
+ } 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 && tool==TOOL_LIST_SELECT) {
+ if (b.pressed)
+ _list_select(b);
+ return;
+ }
+
+
+ if (b.button_index==BUTTON_LEFT && tool==TOOL_EDIT_PIVOT) {
+ if (b.pressed) {
+
+ Point2 mouse_pos(b.x,b.y);
+ mouse_pos=transform.affine_inverse().xform(mouse_pos);
+ mouse_pos=snap_point(mouse_pos);
+ _edit_set_pivot(mouse_pos);
+ }
+ return;
+ }
+
+
+
+ if (tool==TOOL_PAN || b.button_index!=BUTTON_LEFT || Input::get_singleton()->is_key_pressed(KEY_SPACE))
+ return;
+
+ if (!b.pressed) {
+
+ if (drag!=DRAG_NONE) {
+
+ if (undo_redo) {
+
+
+ if (bone_ik_list.size()) {
+
+
+ undo_redo->create_action(TTR("Edit IK Chain"));
+
+ for(List<BoneIK>::Element *E=bone_ik_list.back();E;E=E->prev()) {
+
+ undo_redo->add_do_method(E->get().node,"edit_set_state",E->get().node->edit_get_state());
+ undo_redo->add_undo_method(E->get().node,"edit_set_state",E->get().orig_state);
+ }
+
+ undo_redo->add_do_method(viewport,"update");
+ undo_redo->add_undo_method(viewport,"update");
+
+ bone_ik_list.clear();
+
+ undo_redo->commit_action();
+ } else {
+
+ undo_redo->create_action(TTR("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 || !canvas_item->is_visible())
+ continue;
+ if (canvas_item->get_viewport()!=EditorNode::get_singleton()->get_scene_root())
+ 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;
+ }
+
+
+ Map<ObjectID,BoneList>::Element *Cbone=NULL; //closest
+
+ {
+ bone_ik_list.clear();
+ float closest_dist=1e20;
+ int bone_width = EditorSettings::get_singleton()->get("2d_editor/bone_width");
+ for(Map<ObjectID,BoneList>::Element *E=bone_list.front();E;E=E->next()) {
+
+ if (E->get().from == E->get().to)
+ continue;
+ Vector2 s[2]={
+ E->get().from,
+ E->get().to
+ };
+
+ Vector2 p = Geometry::get_closest_point_to_segment_2d(Vector2(b.x,b.y),s);
+ float d = p.distance_to(Vector2(b.x,b.y));
+ if (d<bone_width && d<closest_dist) {
+ Cbone=E;
+ closest_dist=d;
+ }
+ }
+
+ if (Cbone) {
+ Node2D *b=NULL;
+ Object* obj=ObjectDB::get_instance(Cbone->get().bone);
+ if (obj)
+ b=obj->cast_to<Node2D>();
+
+ if (b) {
+
+
+ bool ik_found=false;
+ bool first=true;
+
+
+
+ while(b) {
+
+ CanvasItem *pi=b->get_parent_item();
+ if (!pi)
+ break;
+
+ float len=pi->get_global_transform().get_origin().distance_to(b->get_global_pos());
+ b=pi->cast_to<Node2D>();
+ if (!b)
+ break;
+
+ if (first) {
+
+ bone_orig_xform=b->get_global_transform();
+ first=false;
+ }
+
+ BoneIK bik;
+ bik.node=b;
+ bik.len=len;
+ bik.orig_state=b->edit_get_state();
+
+ bone_ik_list.push_back(bik);
+
+ if (b->has_meta("_edit_ik_")) {
+
+ ik_found=bone_ik_list.size()>1;
+ break;
+ }
+
+ if (!pi->has_meta("_edit_bone_"))
+ break;
+
+ }
+
+ if (!ik_found)
+ bone_ik_list.clear();
+
+ }
+ }
+ }
+
+ 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();
+ if (canvas_item->cast_to<Control>())
+ se->undo_pivot=Vector2();
+ 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 && (!Cbone || drag!=DRAG_ALL)) {
+ 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 || !canvas_item->is_visible())
+ continue;
+ if (canvas_item->get_viewport()!=EditorNode::get_singleton()->get_scene_root())
+ 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=NULL;
+
+ if (Cbone) {
+
+ Object* obj=ObjectDB::get_instance(Cbone->get().bone);
+ if (obj)
+ c=obj->cast_to<CanvasItem>();
+ if (c)
+ c=c->get_parent_item();
+
+
+ }
+ if (!c) {
+ 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
+
+ additive_selection=b.mod.shift;
+ if (!_select(c, click, additive_selection))
+ return;
+
+ }
+
+ if (p_event.type==InputEvent::MOUSE_MOTION) {
+
+ if (!viewport->has_focus() && (!get_focus_owner() || !get_focus_owner()->is_text_field()))
+ 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_LEFT && tool == TOOL_PAN) || 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 || !canvas_item->is_visible())
+ continue;
+ if (canvas_item->get_viewport()!=EditorNode::get_singleton()->get_scene_root())
+ continue;
+
+ CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
+ if (!se)
+ continue;
+
+ bool dragging_bone = drag==DRAG_ALL && selection.size()==1 && bone_ik_list.size();
+
+
+ if (!dragging_bone) {
+ 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();
+ {
+ Node2D *node = canvas_item->cast_to<Node2D>();
+
+
+ if (node) {
+ Matrix32 rot;
+ rot.elements[1] = (dfrom - center).normalized();
+ rot.elements[0] = rot.elements[1].tangent();
+ node->set_rot(snap_angle(rot.xform_inv(dto-center).angle() + node->get_rot(), node->get_rot()));
+ display_rotate_to = dto;
+ display_rotate_from = center;
+ viewport->update();
+ }
+ }
+
+ {
+ Control *node = canvas_item->cast_to<Control>();
+
+
+ if (node) {
+ Matrix32 rot;
+ rot.elements[1] = (dfrom - center).normalized();
+ rot.elements[0] = rot.elements[1].tangent();
+ node->set_rotation(snap_angle(rot.xform_inv(dto-center).angle() + node->get_rotation(), node->get_rotation()));
+ display_rotate_to = dto;
+ display_rotate_from = center;
+ viewport->update();
+ }
+ }
+
+ continue;
+ }
+
+
+ bool uniform = m.mod.shift;
+ bool symmetric=m.mod.alt;
+
+ dto = dto - (drag == DRAG_ALL ? drag_from - drag_point_from : Vector2(0, 0));
+
+ if(uniform && drag == DRAG_ALL) {
+ if(ABS(dto.x - drag_point_from.x) > ABS(dto.y - drag_point_from.y)) {
+ dto.y = drag_point_from.y;
+ } else {
+ dto.x = drag_point_from.x;
+ }
+ }
+
+ dfrom = drag_point_from;
+ dto = snap_point(dto, drag_point_from);
+
+ 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();
+ Vector2 begin=local_rect.pos;
+ Vector2 end=local_rect.pos+local_rect.size;
+ Vector2 minsize = canvas_item->edit_get_minimum_size();
+
+ if (uniform) {
+ float aspect = local_rect.size.get_aspect();
+ switch(drag) {
+ case DRAG_BOTTOM_LEFT:
+ case DRAG_TOP_RIGHT: {
+ if (aspect > 1.0) { // width > height, take x as reference
+ drag_vector.y = -drag_vector.x/aspect;
+ } else { // height > width, take y as reference
+ drag_vector.x = -drag_vector.y*aspect;
+ }
+ } break;
+ case DRAG_BOTTOM_RIGHT:
+ case DRAG_TOP_LEFT: {
+ if (aspect > 1.0) { // width > height, take x as reference
+ drag_vector.y = drag_vector.x/aspect;
+ } else { // height > width, take y as reference
+ drag_vector.x = drag_vector.y*aspect;
+ }
+ } break;
+ default: {}
+ }
+ }
+
+ 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:{}
+ }
+
+
+
+ if (!dragging_bone) {
+
+ local_rect.pos=begin;
+ local_rect.size=end-begin;
+ canvas_item->edit_set_rect(local_rect);
+
+ } else {
+ //ok, all that had to be done was done, now solve IK
+
+
+
+
+ Node2D *n2d = canvas_item->cast_to<Node2D>();
+ Matrix32 final_xform = bone_orig_xform;
+
+
+
+ if (n2d) {
+
+ float total_len = 0;
+ for (List<BoneIK>::Element *E=bone_ik_list.front();E;E=E->next()) {
+ if (E->prev())
+ total_len+=E->get().len;
+ E->get().pos = E->get().node->get_global_transform().get_origin();
+ }
+
+ {
+
+ final_xform.elements[2]+=dto-dfrom;//final_xform.affine_inverse().basis_xform_inv(drag_vector);
+ //n2d->set_global_transform(final_xform);
+
+ }
+
+
+ CanvasItem *last = bone_ik_list.back()->get().node;
+ if (!last)
+ break;
+
+ Vector2 root_pos = last->get_global_transform().get_origin();
+ Vector2 leaf_pos = final_xform.get_origin();
+
+ if ((leaf_pos.distance_to(root_pos)) > total_len) {
+ //oops dude you went too far
+ //print_line("TOO FAR!");
+ Vector2 rel = leaf_pos - root_pos;
+ rel = rel.normalized() * total_len;
+ leaf_pos=root_pos+rel;
+
+ }
+
+ bone_ik_list.front()->get().pos=leaf_pos;
+
+ //print_line("BONE IK LIST "+itos(bone_ik_list.size()));
+
+
+ if (bone_ik_list.size()>2) {
+ int solver_iterations=64;
+ float solver_k=0.3;
+
+ for(int i=0;i<solver_iterations;i++) {
+
+ for (List<BoneIK>::Element *E=bone_ik_list.front();E;E=E->next()) {
+
+
+
+ if (E==bone_ik_list.back()) {
+
+ break;
+ }
+
+ float len = E->next()->get().len;
+
+ if (E->next()==bone_ik_list.back()) {
+
+ //print_line("back");
+
+ Vector2 rel = E->get().pos - E->next()->get().pos;
+ //print_line("PREV "+E->get().pos);
+ Vector2 desired = E->next()->get().pos+rel.normalized()*len;
+ //print_line("DESIRED "+desired);
+ E->get().pos=E->get().pos.linear_interpolate(desired,solver_k);
+ //print_line("POST "+E->get().pos);
+
+
+ } else if (E==bone_ik_list.front()) {
+ //only adjust parent
+ //print_line("front");
+ Vector2 rel = E->next()->get().pos - E->get().pos;
+ //print_line("PREV "+E->next()->get().pos);
+ Vector2 desired = E->get().pos+rel.normalized()*len;
+ //print_line("DESIRED "+desired);
+ E->next()->get().pos=E->next()->get().pos.linear_interpolate(desired,solver_k);
+ //print_line("POST "+E->next()->get().pos);
+ } else {
+
+ Vector2 rel = E->next()->get().pos - E->get().pos;
+ Vector2 cen = (E->next()->get().pos + E->get().pos)*0.5;
+ rel=rel.linear_interpolate(rel.normalized()*len,solver_k);
+ rel*=0.5;
+ E->next()->get().pos=cen+rel;
+ E->get().pos=cen-rel;
+ //print_line("mid");
+
+ }
+ }
+ }
+ }
+ }
+
+ for (List<BoneIK>::Element *E=bone_ik_list.back();E;E=E->prev()) {
+
+ Node2D *n = E->get().node;
+
+ if (!E->prev()) {
+ //last goes to what it was
+ final_xform.set_origin(n->get_global_pos());
+ n->set_global_transform(final_xform);
+
+ } else {
+ Vector2 rel = (E->prev()->get().node->get_global_pos() - n->get_global_pos()).normalized();
+ Vector2 rel2 = (E->prev()->get().pos - E->get().pos).normalized();
+ float rot = rel.angle_to(rel2);
+ if (n->get_global_transform().basis_determinant()<0) {
+ //mirrored, rotate the other way
+ rot=-rot;
+ }
+
+ n->rotate(rot);
+ }
+
+ }
+
+
+
+ break;
+ }
+ }
+ }
+
+ if (p_event.type==InputEvent::KEY) {
+
+ const InputEventKey &k=p_event.key;
+
+ if (k.pressed && drag==DRAG_NONE) {
+
+ KeyMoveMODE move_mode = MOVE_VIEW_BASE;
+ if (k.mod.alt) move_mode = MOVE_LOCAL_BASE;
+ if (k.mod.control || k.mod.meta) move_mode = MOVE_LOCAL_WITH_ROT;
+
+ if (k.scancode==KEY_UP)
+ _key_move( Vector2(0,-1), k.mod.shift, move_mode );
+ else if (k.scancode==KEY_DOWN)
+ _key_move( Vector2(0,1), k.mod.shift, move_mode );
+ else if (k.scancode==KEY_LEFT)
+ _key_move( Vector2(-1,0), k.mod.shift, move_mode );
+ else if (k.scancode==KEY_RIGHT)
+ _key_move( Vector2(1,0), k.mod.shift, move_mode );
+ 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_show_grid) {
+ Size2 s = viewport->get_size();
+ int last_cell;
+ Matrix32 xform = transform.affine_inverse();
+
+ if (snap_step.x!=0) {
+ for(int i=0;i<s.width;i++) {
+ int cell = Math::fast_ftoi(Math::floor((xform.xform(Vector2(i,0)).x-snap_offset.x)/snap_step.x));
+ 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;
+ }
+ }
+
+ if (snap_step.y!=0) {
+ for(int i=0;i<s.height;i++) {
+ int cell = Math::fast_ftoi(Math::floor((xform.xform(Vector2(0,i)).y-snap_offset.y)/snap_step.y));
+ 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();
+
+ bool pivot_found=false;
+
+ for(Map<Node*,Object*>::Element *E=selection.front();E;E=E->next()) {
+
+
+ CanvasItem *canvas_item = E->key()->cast_to<CanvasItem>();
+ if (!canvas_item || !canvas_item->is_visible())
+ continue;
+ if (canvas_item->get_viewport()!=EditorNode::get_singleton()->get_scene_root())
+ 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);
+
+ 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 || tool == TOOL_ROTATE || tool==TOOL_EDIT_PIVOT)) { //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;
+ pivot_found=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;
+ }
+
+ pivot_button->set_disabled(!pivot_found);
+ 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);
+ }
+
+ }
+
+ int bone_width = EditorSettings::get_singleton()->get("2d_editor/bone_width");
+ Color bone_color1 = EditorSettings::get_singleton()->get("2d_editor/bone_color1");
+ Color bone_color2 = EditorSettings::get_singleton()->get("2d_editor/bone_color2");
+ Color bone_ik_color = EditorSettings::get_singleton()->get("2d_editor/bone_ik_color");
+ Color bone_selected_color = EditorSettings::get_singleton()->get("2d_editor/bone_selected_color");
+
+ for(Map<ObjectID,BoneList>::Element*E=bone_list.front();E;E=E->next()) {
+
+ E->get().from=Vector2();
+ E->get().to=Vector2();
+
+ Object *obj = ObjectDB::get_instance(E->get().bone);
+ if (!obj)
+ continue;
+
+ Node2D* n2d = obj->cast_to<Node2D>();
+ if (!n2d)
+ continue;
+
+ if (!n2d->get_parent())
+ continue;
+
+ CanvasItem *pi = n2d->get_parent_item();
+
+
+ Node2D* pn2d=n2d->get_parent()->cast_to<Node2D>();
+
+ if (!pn2d)
+ continue;
+
+ Vector2 from = transform.xform(pn2d->get_global_pos());
+ Vector2 to = transform.xform(n2d->get_global_pos());
+
+ E->get().from=from;
+ E->get().to=to;
+
+ Vector2 rel = to-from;
+ Vector2 relt = rel.tangent().normalized()*bone_width;
+
+
+
+ Vector<Vector2> bone_shape;
+ bone_shape.push_back(from);
+ bone_shape.push_back(from+rel*0.2+relt);
+ bone_shape.push_back(to);
+ bone_shape.push_back(from+rel*0.2-relt);
+ Vector<Color> colors;
+ if (pi->has_meta("_edit_ik_")) {
+
+ colors.push_back(bone_ik_color);
+ colors.push_back(bone_ik_color);
+ colors.push_back(bone_ik_color);
+ colors.push_back(bone_ik_color);
+ } else {
+ colors.push_back(bone_color1);
+ colors.push_back(bone_color2);
+ colors.push_back(bone_color1);
+ colors.push_back(bone_color2);
+ }
+
+
+ VisualServer::get_singleton()->canvas_item_add_primitive(ci,bone_shape,colors,Vector<Vector2>(),RID());
+
+ if (editor_selection->is_selected(pi)) {
+ for(int i=0;i<bone_shape.size();i++) {
+
+ VisualServer::get_singleton()->canvas_item_add_line(ci,bone_shape[i],bone_shape[(i+1)%bone_shape.size()],bone_selected_color,2);
+ }
+ }
+
+ }
+}
+
+void CanvasItemEditor::_notification(int p_what) {
+
+ if (p_what==NOTIFICATION_FIXED_PROCESS) {
+
+ List<Node*> &selection = editor_selection->get_selected_node_list();
+
+ bool all_control=true;
+ bool has_control=false;
+
+ for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
+
+ CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
+ if (!canvas_item || !canvas_item->is_visible())
+ continue;
+
+ if (canvas_item->get_viewport()!=EditorNode::get_singleton()->get_scene_root())
+ continue;
+
+ if (canvas_item->cast_to<Control>())
+ has_control=true;
+ else
+ all_control=false;
+
+ 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;
+ }
+
+ }
+
+ bool show_anchor = all_control && has_control;
+ if (show_anchor != !anchor_menu->is_hidden()) {
+ if (show_anchor)
+ anchor_menu->show();
+ else
+ anchor_menu->hide();
+ }
+
+ for(Map<ObjectID,BoneList>::Element *E=bone_list.front();E;E=E->next()) {
+
+ Object *b = ObjectDB::get_instance(E->get().bone);
+ if (!b) {
+
+ viewport->update();
+ break;
+ }
+
+ Node2D *b2 = b->cast_to<Node2D>();
+ if (!b2) {
+ continue;
+ }
+
+ if (b2->get_global_transform()!=E->get().xform) {
+
+ E->get().xform=b2->get_global_transform();
+ viewport->update();
+ }
+ }
+ }
+
+ if (p_what==NOTIFICATION_ENTER_TREE) {
+
+ 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"));
+ list_select_button->set_icon( get_icon("ListSelect","EditorIcons"));
+ move_button->set_icon( get_icon("ToolMove","EditorIcons"));
+ rotate_button->set_icon( get_icon("ToolRotate","EditorIcons"));
+ pan_button->set_icon( get_icon("ToolPan", "EditorIcons"));
+ pivot_button->set_icon( get_icon("EditPivot", "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"));
+ key_insert_button->set_icon(get_icon("Key","EditorIcons"));
+
+
+ //anchor_menu->add_icon_override("Align Top Left");
+ anchor_menu->set_icon(get_icon("Anchor","EditorIcons"));
+ PopupMenu *p=anchor_menu->get_popup();
+
+ p->add_icon_item(get_icon("ControlAlignTopLeft","EditorIcons"),"Top Left",ANCHOR_ALIGN_TOP_LEFT);
+ p->add_icon_item(get_icon("ControlAlignTopRight","EditorIcons"),"Top Right",ANCHOR_ALIGN_TOP_RIGHT);
+ p->add_icon_item(get_icon("ControlAlignBottomRight","EditorIcons"),"Bottom Right",ANCHOR_ALIGN_BOTTOM_RIGHT);
+ p->add_icon_item(get_icon("ControlAlignBottomLeft","EditorIcons"),"Bottom Left",ANCHOR_ALIGN_BOTTOM_LEFT);
+ p->add_separator();
+ p->add_icon_item(get_icon("ControlAlignLeftCenter","EditorIcons"),"Center Left",ANCHOR_ALIGN_CENTER_LEFT);
+ p->add_icon_item(get_icon("ControlAlignTopCenter","EditorIcons"),"Center Top",ANCHOR_ALIGN_CENTER_TOP);
+ p->add_icon_item(get_icon("ControlAlignRightCenter","EditorIcons"),"Center Right",ANCHOR_ALIGN_CENTER_RIGHT);
+ p->add_icon_item(get_icon("ControlAlignBottomCenter","EditorIcons"),"Center Bottom",ANCHOR_ALIGN_CENTER_BOTTOM);
+ p->add_icon_item(get_icon("ControlAlignCenter","EditorIcons"),"Center",ANCHOR_ALIGN_CENTER);
+ p->add_separator();
+ p->add_icon_item(get_icon("ControlAlignLeftWide","EditorIcons"),"Left Wide",ANCHOR_ALIGN_LEFT_WIDE);
+ p->add_icon_item(get_icon("ControlAlignTopWide","EditorIcons"),"Top Wide",ANCHOR_ALIGN_TOP_WIDE);
+ p->add_icon_item(get_icon("ControlAlignRightWide","EditorIcons"),"Right Wide",ANCHOR_ALIGN_RIGHT_WIDE);
+ p->add_icon_item(get_icon("ControlAlignBottomWide","EditorIcons"),"Bottom Wide",ANCHOR_ALIGN_BOTTOM_WIDE);
+ p->add_icon_item(get_icon("ControlVcenterWide","EditorIcons"),"VCenter Wide ",ANCHOR_ALIGN_VCENTER_WIDE);
+ p->add_icon_item(get_icon("ControlHcenterWide","EditorIcons"),"HCenter Wide ",ANCHOR_ALIGN_HCENTER_WIDE);
+ p->add_separator();
+ p->add_icon_item(get_icon("ControlAlignWide","EditorIcons"),"Full Rect",ANCHOR_ALIGN_WIDE);
+
+
+ AnimationPlayerEditor::singleton->get_key_editor()->connect("visibility_changed",this,"_keying_changed");
+ _keying_changed();
+ }
+
+ if (p_what==NOTIFICATION_READY) {
+
+ get_tree()->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 && c->is_visible()) {
+
+ 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);
+ }
+
+ if (c->has_meta("_edit_bone_")) {
+
+ ObjectID id = c->get_instance_ID();
+ if (!bone_list.has(id)) {
+ BoneList bone;
+ bone.bone=id;
+ bone_list[id]=bone;
+ }
+
+ bone_list[id].last_pass=bone_last_frame;
+ }
+
+ 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();
+ bone_last_frame++;
+
+
+
+ if (editor->get_edited_scene())
+ _find_canvas_items_span(editor->get_edited_scene(),canvas_item_rect,Matrix32());
+
+ List<Map<ObjectID,BoneList>::Element*> bone_to_erase;
+
+ for(Map<ObjectID,BoneList>::Element*E=bone_list.front();E;E=E->next()) {
+
+ if (E->get().last_pass!=bone_last_frame) {
+ bone_to_erase.push_back(E);
+ }
+ }
+
+ while(bone_to_erase.size()) {
+ bone_list.erase(bone_to_erase.front()->get());
+ bone_to_erase.pop_front();
+ }
+
+ //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();
+
+}
+
+void CanvasItemEditor::_set_anchor(Control::AnchorType p_left,Control::AnchorType p_top,Control::AnchorType p_right,Control::AnchorType p_bottom) {
+ List<Node*> &selection = editor_selection->get_selected_node_list();
+
+ undo_redo->create_action(TTR("Change Anchors"));
+ for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
+
+ Control *c = E->get()->cast_to<Control>();
+
+ undo_redo->add_do_method(c,"set_anchor",MARGIN_LEFT,p_left);
+ undo_redo->add_do_method(c,"set_anchor",MARGIN_TOP,p_top);
+ undo_redo->add_do_method(c,"set_anchor",MARGIN_RIGHT,p_right);
+ undo_redo->add_do_method(c,"set_anchor",MARGIN_BOTTOM,p_bottom);
+ undo_redo->add_undo_method(c,"set_anchor",MARGIN_LEFT,c->get_anchor(MARGIN_LEFT));
+ undo_redo->add_undo_method(c,"set_anchor",MARGIN_TOP,c->get_anchor(MARGIN_TOP));
+ undo_redo->add_undo_method(c,"set_anchor",MARGIN_RIGHT,c->get_anchor(MARGIN_RIGHT));
+ undo_redo->add_undo_method(c,"set_anchor",MARGIN_BOTTOM,c->get_anchor(MARGIN_BOTTOM));
+ }
+
+ undo_redo->commit_action();
+
+}
+
+void CanvasItemEditor::_popup_callback(int p_op) {
+
+ last_option=MenuOption(p_op);
+ switch(p_op) {
+
+ case SNAP_USE: {
+ snap_grid = !snap_grid;
+ int idx = edit_menu->get_popup()->get_item_index(SNAP_USE);
+ edit_menu->get_popup()->set_item_checked(idx,snap_grid);
+ } break;
+ case SNAP_SHOW_GRID: {
+ snap_show_grid = !snap_show_grid;
+ int idx = edit_menu->get_popup()->get_item_index(SNAP_SHOW_GRID);
+ edit_menu->get_popup()->set_item_checked(idx,snap_show_grid);
+ viewport->update();
+ } break;
+ case SNAP_USE_ROTATION: {
+ snap_rotation = !snap_rotation;
+ int idx = edit_menu->get_popup()->get_item_index(SNAP_USE_ROTATION);
+ edit_menu->get_popup()->set_item_checked(idx,snap_rotation);
+ } break;
+ case SNAP_RELATIVE: {
+ snap_relative = !snap_relative;
+ int idx = edit_menu->get_popup()->get_item_index(SNAP_RELATIVE);
+ edit_menu->get_popup()->set_item_checked(idx,snap_relative);
+ } break;
+ case SNAP_USE_PIXEL: {
+ snap_pixel = !snap_pixel;
+ int idx = edit_menu->get_popup()->get_item_index(SNAP_USE_PIXEL);
+ edit_menu->get_popup()->set_item_checked(idx,snap_pixel);
+ } break;
+ case SNAP_CONFIGURE: {
+ ((SnapDialog *)snap_dialog)->set_fields(snap_offset, snap_step, snap_rotation_offset, snap_rotation_step);
+ snap_dialog->popup_centered(Size2(220,160));
+ } break;
+ case ZOOM_IN: {
+ if (zoom>MAX_ZOOM)
+ return;
+ zoom=zoom*(1.0/0.5);
+ _update_scroll(0);
+ viewport->update();
+ return;
+ } break;
+ case ZOOM_OUT: {
+ if (zoom<MIN_ZOOM)
+ return;
+
+ 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(TTR("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 || !canvas_item->is_visible())
+ continue;
+
+ if (canvas_item->get_viewport()!=EditorNode::get_singleton()->get_scene_root())
+ continue;
+
+ canvas_item->set_meta("_edit_lock_",true);
+ emit_signal("item_lock_status_changed");
+ }
+ 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 || !canvas_item->is_visible())
+ continue;
+
+ if (canvas_item->get_viewport()!=EditorNode::get_singleton()->get_scene_root())
+ continue;
+
+
+ canvas_item->set_meta("_edit_lock_",Variant());
+ emit_signal("item_lock_status_changed");
+ }
+
+ 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 || !canvas_item->is_visible())
+ continue;
+
+ if (canvas_item->get_viewport()!=EditorNode::get_singleton()->get_scene_root())
+ continue;
+
+ canvas_item->set_meta("_edit_group_",true);
+ emit_signal("item_group_status_changed");
+ }
+ 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 || !canvas_item->is_visible())
+ continue;
+
+ if (canvas_item->get_viewport()!=EditorNode::get_singleton()->get_scene_root())
+ continue;
+
+ canvas_item->set_meta("_edit_group_",Variant());
+ emit_signal("item_group_status_changed");
+ }
+
+ 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 || !canvas_item->is_visible())
+ continue;
+
+ if (canvas_item->get_viewport()!=EditorNode::get_singleton()->get_scene_root())
+ 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 ANCHOR_ALIGN_TOP_LEFT: {
+ _set_anchor(ANCHOR_BEGIN,ANCHOR_BEGIN,ANCHOR_BEGIN,ANCHOR_BEGIN);
+ } break;
+ case ANCHOR_ALIGN_TOP_RIGHT: {
+ _set_anchor(ANCHOR_END,ANCHOR_BEGIN,ANCHOR_END,ANCHOR_BEGIN);
+ } break;
+ case ANCHOR_ALIGN_BOTTOM_LEFT: {
+ _set_anchor(ANCHOR_BEGIN,ANCHOR_END,ANCHOR_BEGIN,ANCHOR_END);
+ } break;
+ case ANCHOR_ALIGN_BOTTOM_RIGHT: {
+ _set_anchor(ANCHOR_END,ANCHOR_END,ANCHOR_END,ANCHOR_END);
+ } break;
+ case ANCHOR_ALIGN_CENTER_LEFT: {
+ _set_anchor(ANCHOR_BEGIN,ANCHOR_CENTER,ANCHOR_BEGIN,ANCHOR_CENTER);
+ } break;
+ case ANCHOR_ALIGN_CENTER_RIGHT: {
+
+ _set_anchor(ANCHOR_END,ANCHOR_CENTER,ANCHOR_END,ANCHOR_CENTER);
+ } break;
+ case ANCHOR_ALIGN_CENTER_TOP: {
+ _set_anchor(ANCHOR_CENTER,ANCHOR_BEGIN,ANCHOR_CENTER,ANCHOR_BEGIN);
+ } break;
+ case ANCHOR_ALIGN_CENTER_BOTTOM: {
+ _set_anchor(ANCHOR_CENTER,ANCHOR_END,ANCHOR_CENTER,ANCHOR_END);
+ } break;
+ case ANCHOR_ALIGN_CENTER: {
+ _set_anchor(ANCHOR_CENTER,ANCHOR_CENTER,ANCHOR_CENTER,ANCHOR_CENTER);
+ } break;
+ case ANCHOR_ALIGN_TOP_WIDE: {
+ _set_anchor(ANCHOR_BEGIN,ANCHOR_BEGIN,ANCHOR_END,ANCHOR_BEGIN);
+ } break;
+ case ANCHOR_ALIGN_LEFT_WIDE: {
+ _set_anchor(ANCHOR_BEGIN,ANCHOR_BEGIN,ANCHOR_BEGIN,ANCHOR_END);
+ } break;
+ case ANCHOR_ALIGN_RIGHT_WIDE: {
+ _set_anchor(ANCHOR_END,ANCHOR_BEGIN,ANCHOR_END,ANCHOR_END);
+ } break;
+ case ANCHOR_ALIGN_BOTTOM_WIDE: {
+ _set_anchor(ANCHOR_BEGIN,ANCHOR_END,ANCHOR_END,ANCHOR_END);
+ } break;
+ case ANCHOR_ALIGN_VCENTER_WIDE: {
+ _set_anchor(ANCHOR_CENTER,ANCHOR_BEGIN,ANCHOR_CENTER,ANCHOR_END);
+ } break;
+ case ANCHOR_ALIGN_HCENTER_WIDE: {
+ _set_anchor(ANCHOR_BEGIN,ANCHOR_CENTER,ANCHOR_END,ANCHOR_CENTER);
+ } break;
+ case ANCHOR_ALIGN_WIDE: {
+ _set_anchor(ANCHOR_BEGIN,ANCHOR_BEGIN,ANCHOR_END,ANCHOR_END);
+ } 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 || !canvas_item->is_visible())
+ continue;
+
+ if (canvas_item->get_viewport()!=EditorNode::get_singleton()->get_scene_root())
+ continue;
+
+ if (canvas_item->cast_to<Node2D>()) {
+ Node2D *n2d = canvas_item->cast_to<Node2D>();
+
+ if (key_pos)
+ AnimationPlayerEditor::singleton->get_key_editor()->insert_node_value_key(n2d,"transform/pos",n2d->get_pos(),existing);
+ if (key_rot)
+ AnimationPlayerEditor::singleton->get_key_editor()->insert_node_value_key(n2d,"transform/rot",Math::rad2deg(n2d->get_rot()),existing);
+ if (key_scale)
+ AnimationPlayerEditor::singleton->get_key_editor()->insert_node_value_key(n2d,"transform/scale",n2d->get_scale(),existing);
+
+
+ if (n2d->has_meta("_edit_bone_") && n2d->get_parent_item()) {
+ //look for an IK chain
+ List<Node2D*> ik_chain;
+
+ Node2D *n = n2d->get_parent_item()->cast_to<Node2D>();
+ bool has_chain=false;
+
+ while(n) {
+
+ ik_chain.push_back(n);
+ if (n->has_meta("_edit_ik_")) {
+ has_chain=true;
+ break;
+ }
+
+ if (!n->get_parent_item())
+ break;
+ n=n->get_parent_item()->cast_to<Node2D>();
+ }
+
+ if (has_chain && ik_chain.size()) {
+
+ for(List<Node2D*>::Element *F=ik_chain.front();F;F=F->next()) {
+
+ if (key_pos)
+ AnimationPlayerEditor::singleton->get_key_editor()->insert_node_value_key(F->get(),"transform/pos",F->get()->get_pos(),existing);
+ if (key_rot)
+ AnimationPlayerEditor::singleton->get_key_editor()->insert_node_value_key(F->get(),"transform/rot",Math::rad2deg(F->get()->get_rot()),existing);
+ if (key_scale)
+ AnimationPlayerEditor::singleton->get_key_editor()->insert_node_value_key(F->get(),"transform/scale",F->get()->get_scale(),existing);
+
+
+ }
+ }
+ }
+
+ } else if (canvas_item->cast_to<Control>()) {
+
+ Control *ctrl = canvas_item->cast_to<Control>();
+
+ if (key_pos)
+ AnimationPlayerEditor::singleton->get_key_editor()->insert_node_value_key(ctrl,"rect/pos",ctrl->get_pos(),existing);
+ if (key_scale)
+ AnimationPlayerEditor::singleton->get_key_editor()->insert_node_value_key(ctrl,"rect/size",ctrl->get_size(),existing);
+ }
+
+ }
+
+ } break;
+ case ANIM_INSERT_POS: {
+
+ key_pos = key_loc_button->is_pressed();
+ } break;
+ case ANIM_INSERT_ROT: {
+
+ key_rot = key_rot_button->is_pressed();
+ } break;
+ case ANIM_INSERT_SCALE: {
+
+ key_scale = key_scale_button->is_pressed();
+ } break;
+ /*
+ 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 || !canvas_item->is_visible())
+ continue;
+
+ if (canvas_item->get_viewport()!=EditorNode::get_singleton()->get_scene_root())
+ 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(TTR("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 || !canvas_item->is_visible())
+ continue;
+
+ if (canvas_item->get_viewport()!=EditorNode::get_singleton()->get_scene_root())
+ 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)
+ // AnimationPlayerEditor::singleton->get_key_editor()->insert_node_value_key(ctrl,"rect/size",ctrl->get_size());
+ }
+
+ }
+
+
+ } break;
+ case VIEW_CENTER_TO_SELECTION:
+ case VIEW_FRAME_TO_SELECTION: {
+
+ Vector2 center(0.f, 0.f);
+ Rect2 rect;
+ int count = 0;
+
+ 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->get_viewport()!=EditorNode::get_singleton()->get_scene_root())
+ continue;
+
+
+ // counting invisible items, for now
+ //if (!canvas_item->is_visible()) continue;
+ ++count;
+
+ Rect2 item_rect = canvas_item->get_item_rect();
+
+ Vector2 pos = canvas_item->get_global_transform().get_origin();
+ Vector2 scale = canvas_item->get_global_transform().get_scale();
+ real_t angle = canvas_item->get_global_transform().get_rotation();
+
+ Matrix32 t(angle, Vector2(0.f,0.f));
+ item_rect = t.xform(item_rect);
+ Rect2 canvas_item_rect(pos + scale*item_rect.pos, scale*item_rect.size);
+ if (count == 1) {
+ rect = canvas_item_rect;
+ } else {
+ rect = rect.merge(canvas_item_rect);
+ }
+ };
+ if (count==0) break;
+
+ if (p_op == VIEW_CENTER_TO_SELECTION) {
+
+ center = rect.pos + rect.size/2;
+ Vector2 offset = viewport->get_size()/2 - editor->get_scene_root()->get_global_canvas_transform().xform(center);
+ h_scroll->set_val(h_scroll->get_val() - offset.x/zoom);
+ v_scroll->set_val(v_scroll->get_val() - offset.y/zoom);
+
+ } else { // VIEW_FRAME_TO_SELECTION
+
+ if (rect.size.x > CMP_EPSILON && rect.size.y > CMP_EPSILON) {
+ float scale_x = viewport->get_size().x/rect.size.x;
+ float scale_y = viewport->get_size().y/rect.size.y;
+ zoom = scale_x < scale_y? scale_x:scale_y;
+ zoom *= 0.90;
+ _update_scroll(0);
+ call_deferred("_popup_callback", VIEW_CENTER_TO_SELECTION);
+ }
+ }
+
+ } break;
+ case SKELETON_MAKE_BONES: {
+
+
+
+ Map<Node*,Object*> &selection = editor_selection->get_selection();
+
+ for(Map<Node*,Object*>::Element *E=selection.front();E;E=E->next()) {
+
+ Node2D *n2d = E->key()->cast_to<Node2D>();
+ if (!n2d)
+ continue;
+ if (!n2d->is_visible())
+ continue;
+ if (!n2d->get_parent_item())
+ continue;
+
+ n2d->set_meta("_edit_bone_",true);
+
+ }
+ viewport->update();
+
+ } break;
+ case SKELETON_CLEAR_BONES: {
+
+ Map<Node*,Object*> &selection = editor_selection->get_selection();
+
+ for(Map<Node*,Object*>::Element *E=selection.front();E;E=E->next()) {
+
+ Node2D *n2d = E->key()->cast_to<Node2D>();
+ if (!n2d)
+ continue;
+ if (!n2d->is_visible())
+ continue;
+
+ n2d->set_meta("_edit_bone_",Variant());
+
+ }
+ viewport->update();
+
+ } break;
+ case SKELETON_SET_IK_CHAIN: {
+
+ 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 || !canvas_item->is_visible())
+ continue;
+
+ if (canvas_item->get_viewport()!=EditorNode::get_singleton()->get_scene_root())
+ continue;
+
+ canvas_item->set_meta("_edit_ik_",true);
+
+ }
+
+ viewport->update();
+
+ } break;
+ case SKELETON_CLEAR_IK_CHAIN: {
+
+ Map<Node*,Object*> &selection = editor_selection->get_selection();
+
+ for(Map<Node*,Object*>::Element *E=selection.front();E;E=E->next()) {
+
+ CanvasItem *n2d = E->key()->cast_to<CanvasItem>();
+ if (!n2d)
+ continue;
+ if (!n2d->is_visible())
+ continue;
+
+ n2d->set_meta("_edit_ik_",Variant());
+
+ }
+ viewport->update();
+
+ } 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);
+ ObjectTypeDB::bind_method("_snap_changed",&CanvasItemEditor::_snap_changed);
+ ObjectTypeDB::bind_method(_MD("_selection_result_pressed"),&CanvasItemEditor::_selection_result_pressed);
+ ObjectTypeDB::bind_method(_MD("_selection_menu_hide"),&CanvasItemEditor::_selection_menu_hide);
+
+ ADD_SIGNAL( MethodInfo("item_lock_status_changed") );
+ ADD_SIGNAL( MethodInfo("item_group_status_changed") );
+
+}
+
+#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);
+}
+
+HSplitContainer *CanvasItemEditor::get_palette_split() {
+
+ return palette_split;
+}
+
+VSplitContainer *CanvasItemEditor::get_bottom_split() {
+
+ return bottom_split;
+}
+
+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();
+
+ bottom_split = memnew( VSplitContainer );
+ bottom_split->set_v_size_flags(SIZE_EXPAND_FILL);
+ add_child(bottom_split);
+
+ palette_split = memnew( HSplitContainer);
+ palette_split->set_v_size_flags(SIZE_EXPAND_FILL);
+ bottom_split->add_child(palette_split);
+
+ Control *vp_base = memnew (Control);
+ vp_base->set_v_size_flags(SIZE_EXPAND_FILL);
+ palette_split->add_child(vp_base);
+
+ 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( CanvasItemEditorViewport(p_editor, this) );
+ 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_shortcut(ED_SHORTCUT("canvas_item_editor/select_mode",TTR("Select Mode"),KEY_Q));
+ select_button->set_tooltip(TTR("Select Mode")+" $sc\n"+keycode_get_string(KEY_MASK_CMD)+TTR("Drag: Rotate")+"\n"+TTR("Alt+Drag: Move")+"\n"+TTR("Press 'v' to Change Pivot, 'Shift+v' to Drag Pivot (while moving).")+"\n"+TTR("Alt+RMB: Depth list selection"));
+
+
+ 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_shortcut(ED_SHORTCUT("canvas_item_editor/move_mode",TTR("Move Mode"),KEY_W));
+ move_button->set_tooltip(TTR("Move Mode"));
+
+ 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_shortcut(ED_SHORTCUT("canvas_item_editor/rotate_mode",TTR("Rotate Mode"),KEY_E));
+ rotate_button->set_tooltip(TTR("Rotate Mode"));
+
+ hb->add_child(memnew(VSeparator));
+
+ list_select_button = memnew( ToolButton );
+ list_select_button->set_toggle_mode(true);
+ hb->add_child(list_select_button);
+ list_select_button->connect("pressed",this,"_tool_select",make_binds(TOOL_LIST_SELECT));
+ list_select_button->set_tooltip(TTR("Show a list of all objects at the position clicked\n(same as Alt+RMB in select mode)."));
+
+ pivot_button = memnew( ToolButton );
+ pivot_button->set_toggle_mode(true);
+ hb->add_child(pivot_button);
+ pivot_button->connect("pressed",this,"_tool_select",make_binds(TOOL_EDIT_PIVOT));
+ pivot_button->set_tooltip(TTR("Click to change object's rotation pivot."));
+
+ pan_button = memnew( ToolButton );
+ pan_button->set_toggle_mode(true);
+ hb->add_child(pan_button);
+ pan_button->connect("pressed",this,"_tool_select",make_binds(TOOL_PAN));
+ pan_button->set_tooltip(TTR("Pan Mode"));
+
+ hb->add_child(memnew(VSeparator));
+
+ lock_button = memnew( ToolButton );
+ hb->add_child(lock_button);
+
+ lock_button->connect("pressed",this,"_popup_callback",varray(LOCK_SELECTED));
+ lock_button->set_tooltip(TTR("Lock the selected object in place (can't be moved)."));
+
+ unlock_button = memnew( ToolButton );
+ hb->add_child(unlock_button);
+ unlock_button->connect("pressed",this,"_popup_callback",varray(UNLOCK_SELECTED));
+ unlock_button->set_tooltip(TTR("Unlock the selected object (can be moved)."));
+
+ group_button = memnew( ToolButton );
+ hb->add_child(group_button);
+ group_button->connect("pressed",this,"_popup_callback",varray(GROUP_SELECTED));
+ group_button->set_tooltip(TTR("Makes sure the object's children are not selectable."));
+
+ ungroup_button = memnew( ToolButton );
+ hb->add_child(ungroup_button);
+ ungroup_button->connect("pressed",this,"_popup_callback",varray(UNGROUP_SELECTED));
+ ungroup_button->set_tooltip(TTR("Restores the object's children's ability to be selected."));
+
+ hb->add_child(memnew(VSeparator));
+
+ edit_menu = memnew( MenuButton );
+ edit_menu->set_text(TTR("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_shortcut(ED_SHORTCUT("canvas_item_editor/use_snap", TTR("Use Snap")), SNAP_USE);
+ p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_grid", TTR("Show Grid")), SNAP_SHOW_GRID);
+ p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/use_rotation_snap", TTR("Use Rotation Snap")), SNAP_USE_ROTATION);
+ p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_relative", TTR("Snap Relative")), SNAP_RELATIVE);
+ p->add_shortcut(ED_SHORTCUT("canvas_item_editor/configure_snap", TTR("Configure Snap..")), SNAP_CONFIGURE);
+ p->add_separator();
+ p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/use_pixel_snap", TTR("Use Pixel Snap")), SNAP_USE_PIXEL);
+ p->add_separator();
+ p->add_shortcut(ED_SHORTCUT("canvas_item_editor/expand_to_parent", TTR("Expand to Parent"), KEY_MASK_CMD | KEY_P), EXPAND_TO_PARENT);
+ p->add_separator();
+ p->add_submenu_item(TTR("Skeleton.."),"skeleton");
+ PopupMenu *p2 = memnew(PopupMenu);
+ p->add_child(p2);
+ p2->set_name("skeleton");
+ p2->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_make_bones", TTR("Make Bones"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_B ),SKELETON_MAKE_BONES);
+ p2->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_clear_bones", TTR("Clear Bones")), SKELETON_CLEAR_BONES);
+ p2->add_separator();
+ p2->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_set_ik_chain", TTR("Make IK Chain")), SKELETON_SET_IK_CHAIN);
+ p2->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_clear_ik_chain", TTR("Clear IK Chain")), SKELETON_CLEAR_IK_CHAIN);
+ p2->connect("item_pressed", this,"_popup_callback");
+
+
+ /*
+ 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(TTR("View"));
+ hb->add_child(view_menu);
+ view_menu->get_popup()->connect("item_pressed", this,"_popup_callback");
+
+ p = view_menu->get_popup();
+
+ p->add_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_in", TTR("Zoom In")), ZOOM_IN);
+ p->add_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_out", TTR("Zoom Out")), ZOOM_OUT);
+ p->add_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_reset", TTR("Zoom Reset")), ZOOM_RESET);
+ p->add_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_set", TTR("Zoom Set..")), ZOOM_SET);
+ p->add_separator();
+ p->add_shortcut(ED_SHORTCUT("canvas_item_editor/center_selection", TTR("Center Selection"), KEY_F), VIEW_CENTER_TO_SELECTION);
+ p->add_shortcut(ED_SHORTCUT("canvas_item_editor/frame_selection", TTR("Frame Selection"), KEY_MASK_SHIFT | KEY_F), VIEW_FRAME_TO_SELECTION);
+
+ anchor_menu = memnew( MenuButton );
+ anchor_menu->set_text(TTR("Anchor"));
+ hb->add_child(anchor_menu);
+ anchor_menu->get_popup()->connect("item_pressed", this,"_popup_callback");
+ anchor_menu->hide();
+
+ //p = anchor_menu->get_popup();
+
+
+
+ animation_hb = memnew( HBoxContainer );
+ hb->add_child(animation_hb);
+ animation_hb->add_child( memnew( VSeparator ));
+ animation_hb->hide();
+
+ key_loc_button = memnew( Button("loc"));
+ key_loc_button->set_toggle_mode(true);
+ key_loc_button->set_pressed(true);
+ key_loc_button->set_focus_mode(FOCUS_NONE);
+ key_loc_button->add_color_override("font_color",Color(1,0.6,0.6));
+ key_loc_button->add_color_override("font_color_pressed",Color(0.6,1,0.6));
+ key_loc_button->connect("pressed",this,"_popup_callback",varray(ANIM_INSERT_POS));
+ animation_hb->add_child(key_loc_button);
+ key_rot_button = memnew( Button("rot"));
+ key_rot_button->set_toggle_mode(true);
+ key_rot_button->set_pressed(true);
+ key_rot_button->set_focus_mode(FOCUS_NONE);
+ key_rot_button->add_color_override("font_color",Color(1,0.6,0.6));
+ key_rot_button->add_color_override("font_color_pressed",Color(0.6,1,0.6));
+ key_rot_button->connect("pressed",this,"_popup_callback",varray(ANIM_INSERT_ROT));
+ animation_hb->add_child(key_rot_button);
+ key_scale_button = memnew( Button("scl"));
+ key_scale_button->set_toggle_mode(true);
+ key_scale_button->set_focus_mode(FOCUS_NONE);
+ key_scale_button->add_color_override("font_color",Color(1,0.6,0.6));
+ key_scale_button->add_color_override("font_color_pressed",Color(0.6,1,0.6));
+ key_scale_button->connect("pressed",this,"_popup_callback",varray(ANIM_INSERT_SCALE));
+ animation_hb->add_child(key_scale_button);
+ key_insert_button = memnew( Button );
+ key_insert_button->set_focus_mode(FOCUS_NONE);
+ key_insert_button->connect("pressed",this,"_popup_callback",varray(ANIM_INSERT_KEY));
+ key_insert_button->set_tooltip(TTR("Insert Keys"));
+ key_insert_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/anim_insert_key", TTR("Insert Key"), KEY_INSERT));
+
+ animation_hb->add_child(key_insert_button);
+
+ animation_menu = memnew( MenuButton );
+ animation_menu->set_text(TTR("Animation"));
+ animation_hb->add_child(animation_menu);
+ animation_menu->get_popup()->connect("item_pressed", this,"_popup_callback");
+
+ p = animation_menu->get_popup();
+
+ p->add_shortcut(ED_GET_SHORTCUT("canvas_item_editor/anim_insert_key"), ANIM_INSERT_KEY);
+ p->add_shortcut(ED_SHORTCUT("canvas_item_editor/anim_insert_key_existing_tracks", TTR("Insert Key (Existing Tracks)"), KEY_MASK_CMD+KEY_INSERT), ANIM_INSERT_KEY_EXISTING);
+ p->add_separator();
+ p->add_shortcut(ED_SHORTCUT("canvas_item_editor/anim_copy_pose", TTR("Copy Pose")), ANIM_COPY_POSE);
+ p->add_shortcut(ED_SHORTCUT("canvas_item_editor/anim_paste_pose", TTR("Paste Pose")), ANIM_PASTE_POSE);
+ p->add_shortcut(ED_SHORTCUT("canvas_item_editor/anim_clear_pose", TTR("Clear Pose"), KEY_MASK_SHIFT | KEY_K), ANIM_CLEAR_POSE);
+
+ snap_dialog = memnew( SnapDialog );
+ snap_dialog->connect("confirmed",this,"_snap_changed");
+ add_child(snap_dialog);
+
+ value_dialog = memnew( AcceptDialog );
+ value_dialog->set_title(TTR("Set a Value"));
+ value_dialog->get_ok()->set_text(TTR("Close"));
+ add_child(value_dialog);
+
+ Label *l = memnew(Label);
+ l->set_text(TTR("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) );
+
+ selection_menu = memnew( PopupMenu );
+ add_child(selection_menu);
+ selection_menu->set_custom_minimum_size(Vector2(100, 0));
+ selection_menu->connect("item_pressed", this, "_selection_result_pressed");
+ selection_menu->connect("popup_hide", this, "_selection_menu_hide");
+
+ key_pos=true;
+ key_rot=true;
+ key_scale=false;
+
+ zoom=1;
+ snap_offset=Vector2(0, 0);
+ snap_step=Vector2(10, 10);
+ snap_rotation_offset=0;
+ snap_rotation_step=15 / (180 / Math_PI);
+ snap_grid=false;
+ snap_show_grid=false;
+ snap_rotation=false;
+ snap_pixel=false;
+ updating_value_dialog=false;
+ box_selecting=false;
+ //zoom=0.5;
+ singleton=this;
+
+ set_process_unhandled_key_input(true);
+ can_move_pivot=false;
+ drag=DRAG_NONE;
+ bone_last_frame=0;
+ additive_selection=false;
+}
+
+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) );
+ canvas_item_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ editor->get_viewport()->add_child(canvas_item_editor);
+ canvas_item_editor->set_area_as_parent_rect();
+ canvas_item_editor->hide();
+
+}
+
+
+CanvasItemEditorPlugin::~CanvasItemEditorPlugin()
+{
+}
+
+
+void CanvasItemEditorViewport::_on_mouse_exit() {
+ if (selector->is_hidden()){
+ _remove_preview();
+ }
+}
+
+void CanvasItemEditorViewport::_on_select_type(Object* selected) {
+ CheckBox* check = selected->cast_to<CheckBox>();
+ String type = check->get_text();
+ selector_label->set_text(vformat(TTR("Add %s"),type));
+ label->set_text(vformat(TTR("Adding %s..."),type));
+}
+
+void CanvasItemEditorViewport::_on_change_type() {
+ CheckBox* check=btn_group->get_pressed_button()->cast_to<CheckBox>();
+ default_type=check->get_text();
+ _perform_drop_data();
+ selector->hide();
+}
+
+void CanvasItemEditorViewport::_create_preview(const Vector<String>& files) const {
+ label->set_pos(get_global_pos()+Point2(14,14));
+ label_desc->set_pos(label->get_pos()+Point2(0,label->get_size().height));
+ for (int i=0;i<files.size();i++) {
+ String path=files[i];
+ RES res=ResourceLoader::load(path);
+ String type=res->get_type();
+ if (type=="ImageTexture" || type=="PackedScene") {
+ if (type=="ImageTexture") {
+ Ref<ImageTexture> texture=Ref<ImageTexture> ( ResourceCache::get(path)->cast_to<ImageTexture>() );
+ Sprite* sprite=memnew(Sprite);
+ sprite->set_texture(texture);
+ sprite->set_opacity(0.7f);
+ preview->add_child(sprite);
+ label->show();
+ label_desc->show();
+ } else if (type=="PackedScene") {
+ Ref<PackedScene> scn=ResourceLoader::load(path);
+ if (scn.is_valid()){
+ Node* instance=scn->instance();
+ if (instance){
+ preview->add_child(instance);
+ }
+ }
+ }
+ editor->get_scene_root()->add_child(preview);
+ }
+ }
+}
+
+void CanvasItemEditorViewport::_remove_preview() {
+ if (preview->get_parent()){
+ editor->get_scene_root()->remove_child(preview);
+ for (int i=preview->get_child_count()-1;i>=0;i--){
+ Node* node=preview->get_child(i);
+ memdelete(node);
+ }
+ label->hide();
+ label_desc->hide();
+ }
+}
+
+bool CanvasItemEditorViewport::_cyclical_dependency_exists(const String& p_target_scene_path, Node* p_desired_node) {
+ if (p_desired_node->get_filename()==p_target_scene_path) {
+ return true;
+ }
+
+ int childCount=p_desired_node->get_child_count();
+ for (int i=0;i<childCount;i++) {
+ Node* child=p_desired_node->get_child(i);
+ if(_cyclical_dependency_exists(p_target_scene_path,child)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void CanvasItemEditorViewport::_create_nodes(Node* parent, Node* child, String& path, const Point2& p_point) {
+ child->set_name(path.get_file().basename());
+ Ref<ImageTexture> texture=Ref<ImageTexture> ( ResourceCache::get(path)->cast_to<ImageTexture>() );
+ Size2 texture_size = texture->get_size();
+
+ editor_data->get_undo_redo().add_do_method(parent,"add_child",child);
+ editor_data->get_undo_redo().add_do_method(child,"set_owner",editor->get_edited_scene());
+ editor_data->get_undo_redo().add_do_reference(child);
+ editor_data->get_undo_redo().add_undo_method(parent,"remove_child",child);
+
+ String new_name=parent->validate_child_name(child->get_name());
+ ScriptEditorDebugger *sed=ScriptEditor::get_singleton()->get_debugger();
+ editor_data->get_undo_redo().add_do_method(sed,"live_debug_create_node",editor->get_edited_scene()->get_path_to(parent),child->get_type(),new_name);
+ editor_data->get_undo_redo().add_undo_method(sed,"live_debug_remove_node",NodePath(String(editor->get_edited_scene()->get_path_to(parent))+"/"+new_name));
+
+ // handle with different property for texture
+ String property = "texture";
+ List<PropertyInfo> props;
+ child->get_property_list(&props);
+ for(const List<PropertyInfo>::Element *E=props.front();E;E=E->next() ) {
+ if (E->get().name=="config/texture") { // Particles2D
+ property="config/texture";
+ break;
+ } else if (E->get().name=="texture/texture") { // Polygon2D
+ property="texture/texture";
+ break;
+ } else if (E->get().name=="normal") { // TouchScreenButton
+ property="normal";
+ break;
+ }
+ }
+ editor_data->get_undo_redo().add_do_property(child,property,texture);
+
+ // make visible for certain node type
+ if (default_type=="Patch9Frame") {
+ editor_data->get_undo_redo().add_do_property(child,"rect/size",texture_size);
+ } else if (default_type=="Polygon2D") {
+ DVector<Vector2> list;
+ list.push_back(Vector2(0,0));
+ list.push_back(Vector2(texture_size.width,0));
+ list.push_back(Vector2(texture_size.width,texture_size.height));
+ list.push_back(Vector2(0,texture_size.height));
+ editor_data->get_undo_redo().add_do_property(child,"polygon",list);
+ }
+
+ // locate at preview position
+ Point2 pos;
+ if (parent->has_method("get_global_pos")) {
+ pos=parent->call("get_global_pos");
+ }
+ Matrix32 trans=canvas->get_canvas_transform();
+ Point2 target_pos = (p_point-trans.get_origin())/trans.get_scale().x-pos;
+ if (default_type=="Polygon2D" || default_type=="TouchScreenButton" || default_type=="TextureFrame" || default_type=="Patch9Frame") {
+ target_pos -= texture_size/2;
+ }
+ editor_data->get_undo_redo().add_do_method(child,"set_pos",target_pos);
+}
+
+bool CanvasItemEditorViewport::_create_instance(Node* parent, String& path, const Point2& p_point) {
+ Ref<PackedScene> sdata=ResourceLoader::load(path);
+ if (!sdata.is_valid()) { // invalid scene
+ return false;
+ }
+
+ Node* instanced_scene=sdata->instance(true);
+ if (!instanced_scene) { // error on instancing
+ return false;
+ }
+
+ if (editor->get_edited_scene()->get_filename()!="") { // cyclical instancing
+ if (_cyclical_dependency_exists(editor->get_edited_scene()->get_filename(), instanced_scene)) {
+ memdelete(instanced_scene);
+ return false;
+ }
+ }
+
+ instanced_scene->set_filename( Globals::get_singleton()->localize_path(path) );
+
+ editor_data->get_undo_redo().add_do_method(parent,"add_child",instanced_scene);
+ editor_data->get_undo_redo().add_do_method(instanced_scene,"set_owner",editor->get_edited_scene());
+ editor_data->get_undo_redo().add_do_reference(instanced_scene);
+ editor_data->get_undo_redo().add_undo_method(parent,"remove_child",instanced_scene);
+
+ String new_name=parent->validate_child_name(instanced_scene->get_name());
+ ScriptEditorDebugger *sed=ScriptEditor::get_singleton()->get_debugger();
+ editor_data->get_undo_redo().add_do_method(sed,"live_debug_instance_node",editor->get_edited_scene()->get_path_to(parent),path,new_name);
+ editor_data->get_undo_redo().add_undo_method(sed,"live_debug_remove_node",NodePath(String(editor->get_edited_scene()->get_path_to(parent))+"/"+new_name));
+
+ Point2 pos;
+ Node2D* parent_node2d=parent->cast_to<Node2D>();
+ if (parent_node2d) {
+ pos=parent_node2d->get_global_pos();
+ } else {
+ Control* parent_control=parent->cast_to<Control>();
+ if (parent_control) {
+ pos=parent_control->get_global_pos();
+ }
+ }
+ Matrix32 trans=canvas->get_canvas_transform();
+ editor_data->get_undo_redo().add_do_method(instanced_scene,"set_pos",(p_point-trans.get_origin())/trans.get_scale().x-pos);
+
+ return true;
+}
+
+void CanvasItemEditorViewport::_perform_drop_data(){
+ _remove_preview();
+
+ Vector<String> error_files;
+
+ editor_data->get_undo_redo().create_action(TTR("Create Node"));
+
+ for (int i=0;i<selected_files.size();i++) {
+ String path=selected_files[i];
+ RES res=ResourceLoader::load(path);
+ if (res.is_null()) {
+ continue;
+ }
+ String type=res->get_type();
+ if (type=="ImageTexture") {
+ Node* child;
+ if (default_type=="Light2D") child=memnew(Light2D);
+ else if (default_type=="Particles2D") child=memnew(Particles2D);
+ else if (default_type=="Polygon2D") child=memnew(Polygon2D);
+ else if (default_type=="TouchScreenButton") child=memnew(TouchScreenButton);
+ else if (default_type=="TextureFrame") child=memnew(TextureFrame);
+ else if (default_type=="Patch9Frame") child=memnew(Patch9Frame);
+ else child=memnew(Sprite); // default
+
+ _create_nodes(target_node, child, path, drop_pos);
+ } else if (type=="PackedScene") {
+ bool success=_create_instance(target_node, path, drop_pos);
+ if (!success) {
+ error_files.push_back(path);
+ }
+ }
+ }
+
+ editor_data->get_undo_redo().commit_action();
+
+ if (error_files.size()>0) {
+ String files_str;
+ for (int i=0;i<error_files.size();i++) {
+ files_str += error_files[i].get_file().basename() + ",";
+ }
+ files_str=files_str.substr(0,files_str.length()-1);
+ accept->get_ok()->set_text(TTR("Ugh"));
+ accept->set_text(vformat(TTR("Error instancing scene from %s"),files_str.c_str()));
+ accept->popup_centered_minsize();
+ }
+}
+
+bool CanvasItemEditorViewport::can_drop_data(const Point2& p_point,const Variant& p_data) const {
+ Dictionary d=p_data;
+ if (d.has("type")) {
+ if (String(d["type"])=="files") {
+ Vector<String> files=d["files"];
+ bool can_instance=false;
+ for (int i=0;i<files.size();i++) { // check if dragged files contain resource or scene can be created at least one
+ RES res=ResourceLoader::load(files[i]);
+ if (res.is_null()) {
+ continue;
+ }
+ String type=res->get_type();
+ if (type=="PackedScene") {
+ Ref<PackedScene> sdata=ResourceLoader::load(files[i]);
+ Node* instanced_scene=sdata->instance(true);
+ if (!instanced_scene) {
+ continue;
+ }
+ memdelete(instanced_scene);
+ }
+ can_instance=true;
+ break;
+ }
+ if (can_instance) {
+ if (!preview->get_parent()){ // create preview only once
+ _create_preview(files);
+ }
+ Matrix32 trans=canvas->get_canvas_transform();
+ preview->set_pos((p_point-trans.get_origin())/trans.get_scale().x);
+ label->set_text(vformat(TTR("Adding %s..."),default_type));
+ }
+ return can_instance;
+ }
+ }
+ label->hide();
+ return false;
+}
+
+void CanvasItemEditorViewport::drop_data(const Point2& p_point,const Variant& p_data) {
+ bool is_shift=Input::get_singleton()->is_key_pressed(KEY_SHIFT);
+ bool is_alt=Input::get_singleton()->is_key_pressed(KEY_ALT);
+
+ selected_files.clear();
+ Dictionary d=p_data;
+ if (d.has("type") && String(d["type"])=="files"){
+ selected_files=d["files"];
+ }
+
+ List<Node*> list=editor->get_editor_selection()->get_selected_node_list();
+ if (list.size()==0) {
+ accept->get_ok()->set_text(TTR("OK :("));
+ accept->set_text(TTR("No parent to instance a child at."));
+ accept->popup_centered_minsize();
+ _remove_preview();
+ return;
+ }
+ if (list.size()!=1) {
+ accept->get_ok()->set_text(TTR("I see.."));
+ accept->set_text(TTR("This operation requires a single selected node."));
+ accept->popup_centered_minsize();
+ _remove_preview();
+ return;
+ }
+
+ target_node=list[0];
+ if (is_shift && target_node!=editor->get_edited_scene()) {
+ target_node=target_node->get_parent();
+ }
+ drop_pos=p_point;
+
+ if (is_alt) {
+ List<BaseButton*> btn_list;
+ btn_group->get_button_list(&btn_list);
+ for (int i=0;i<btn_list.size();i++) {
+ CheckBox* check=btn_list[i]->cast_to<CheckBox>();
+ check->set_pressed(check->get_text()==default_type);
+ }
+ selector_label->set_text(vformat(TTR("Add %s"),default_type));
+ selector->popup_centered_minsize();
+ } else {
+ _perform_drop_data();
+ }
+}
+
+void CanvasItemEditorViewport::_notification(int p_what) {
+ if (p_what==NOTIFICATION_ENTER_TREE) {
+ connect("mouse_exit",this,"_on_mouse_exit");
+ } else if (p_what==NOTIFICATION_EXIT_TREE) {
+ disconnect("mouse_exit",this,"_on_mouse_exit");
+ }
+}
+
+void CanvasItemEditorViewport::_bind_methods() {
+ ObjectTypeDB::bind_method(_MD("_on_select_type"),&CanvasItemEditorViewport::_on_select_type);
+ ObjectTypeDB::bind_method(_MD("_on_change_type"),&CanvasItemEditorViewport::_on_change_type);
+ ObjectTypeDB::bind_method(_MD("_on_mouse_exit"),&CanvasItemEditorViewport::_on_mouse_exit);
+}
+
+CanvasItemEditorViewport::CanvasItemEditorViewport(EditorNode *p_node, CanvasItemEditor* p_canvas) {
+ default_type="Sprite";
+ // Node2D
+ types.push_back("Sprite");
+ types.push_back("Light2D");
+ types.push_back("Particles2D");
+ types.push_back("Polygon2D");
+ types.push_back("TouchScreenButton");
+ // Control
+ types.push_back("TextureFrame");
+ types.push_back("Patch9Frame");
+
+ target_node=NULL;
+ editor=p_node;
+ editor_data=editor->get_scene_tree_dock()->get_editor_data();
+ canvas=p_canvas;
+ preview=memnew( Node2D );
+ accept=memnew( AcceptDialog );
+ editor->get_gui_base()->add_child(accept);
+
+ selector=memnew( WindowDialog );
+ selector->set_title(TTR("Change default type"));
+
+ VBoxContainer* vbc=memnew(VBoxContainer);
+ vbc->add_constant_override("separation",10*EDSCALE);
+ vbc->set_custom_minimum_size(Size2(200,260)*EDSCALE);
+
+ selector_label=memnew(Label);
+ selector_label->set_align(Label::ALIGN_CENTER);
+ selector_label->set_valign(Label::VALIGN_BOTTOM);
+ selector_label->set_custom_minimum_size(Size2(0,30)*EDSCALE);
+ vbc->add_child(selector_label);
+
+ btn_group=memnew( ButtonGroup );
+ btn_group->set_h_size_flags(0);
+ btn_group->connect("button_selected", this, "_on_select_type");
+
+ for (int i=0;i<types.size();i++) {
+ CheckBox* check=memnew(CheckBox);
+ check->set_text(types[i]);
+ btn_group->add_child(check);
+ }
+ vbc->add_child(btn_group);
+
+ Button* ok=memnew(Button);
+ ok->set_text(TTR("OK"));
+ ok->set_h_size_flags(0);
+ vbc->add_child(ok);
+ ok->connect("pressed", this, "_on_change_type");
+
+ selector->add_child(vbc);
+ editor->get_gui_base()->add_child(selector);
+
+ label=memnew(Label);
+ label->add_color_override("font_color", Color(1,1,0,1));
+ label->add_color_override("font_color_shadow", Color(0,0,0,1));
+ label->add_constant_override("shadow_as_outline", 1*EDSCALE);
+ label->hide();
+ editor->get_gui_base()->add_child(label);
+
+ label_desc=memnew(Label);
+ label_desc->set_text(TTR("Drag & drop + Shift : Add node as sibling\nDrag & drop + Alt : Change node type"));
+ label_desc->add_color_override("font_color", Color(0.6,0.6,0.6,1));
+ label_desc->add_color_override("font_color_shadow", Color(0.2,0.2,0.2,1));
+ label_desc->add_constant_override("shadow_as_outline", 1*EDSCALE);
+ label_desc->add_constant_override("line_spacing", 0);
+ label_desc->hide();
+ editor->get_gui_base()->add_child(label_desc);
+}
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
new file mode 100644
index 000000000..3e73d841c
--- /dev/null
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -0,0 +1,495 @@
+/*************************************************************************/
+/* canvas_item_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "editor/editor_plugin.h"
+#include "editor/editor_node.h"
+#include "scene/gui/button_group.h"
+#include "scene/gui/check_box.h"
+#include "scene/gui/label.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 CanvasItemEditorViewport;
+
+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_LIST_SELECT,
+ TOOL_MOVE,
+ TOOL_ROTATE,
+ TOOL_EDIT_PIVOT,
+ TOOL_PAN,
+ TOOL_MAX
+ };
+
+ enum MenuOption {
+ SNAP_USE,
+ SNAP_SHOW_GRID,
+ SNAP_USE_ROTATION,
+ SNAP_RELATIVE,
+ 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,
+ ANCHOR_ALIGN_TOP_LEFT,
+ ANCHOR_ALIGN_TOP_RIGHT,
+ ANCHOR_ALIGN_BOTTOM_LEFT,
+ ANCHOR_ALIGN_BOTTOM_RIGHT,
+ ANCHOR_ALIGN_CENTER_LEFT,
+ ANCHOR_ALIGN_CENTER_RIGHT,
+ ANCHOR_ALIGN_CENTER_TOP,
+ ANCHOR_ALIGN_CENTER_BOTTOM,
+ ANCHOR_ALIGN_CENTER,
+ ANCHOR_ALIGN_TOP_WIDE,
+ ANCHOR_ALIGN_LEFT_WIDE,
+ ANCHOR_ALIGN_RIGHT_WIDE,
+ ANCHOR_ALIGN_BOTTOM_WIDE,
+ ANCHOR_ALIGN_VCENTER_WIDE,
+ ANCHOR_ALIGN_HCENTER_WIDE,
+ ANCHOR_ALIGN_WIDE,
+
+ SPACE_HORIZONTAL,
+ SPACE_VERTICAL,
+ EXPAND_TO_PARENT,
+ ANIM_INSERT_KEY,
+ ANIM_INSERT_KEY_EXISTING,
+ ANIM_INSERT_POS,
+ ANIM_INSERT_ROT,
+ ANIM_INSERT_SCALE,
+ ANIM_COPY_POSE,
+ ANIM_PASTE_POSE,
+ ANIM_CLEAR_POSE,
+ VIEW_CENTER_TO_SELECTION,
+ VIEW_FRAME_TO_SELECTION,
+ SKELETON_MAKE_BONES,
+ SKELETON_CLEAR_BONES,
+ SKELETON_SET_IK_CHAIN,
+ SKELETON_CLEAR_IK_CHAIN
+
+ };
+
+ 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,
+
+ };
+
+ enum KeyMoveMODE {
+ MOVE_VIEW_BASE,
+ MOVE_LOCAL_BASE,
+ MOVE_LOCAL_WITH_ROT
+ };
+
+ EditorSelection *editor_selection;
+ bool additive_selection;
+
+ Tool tool;
+ bool first_update;
+ Control *viewport;
+
+ bool can_move_pivot;
+
+ HScrollBar *h_scroll;
+ VScrollBar *v_scroll;
+ HBoxContainer *hb;
+
+ Matrix32 transform;
+ float zoom;
+ Vector2 snap_offset;
+ Vector2 snap_step;
+ float snap_rotation_step;
+ float snap_rotation_offset;
+ bool snap_grid;
+ bool snap_show_grid;
+ bool snap_rotation;
+ bool snap_relative;
+ bool snap_pixel;
+ 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 _SelectResult {
+
+ CanvasItem* item;
+ float z;
+ bool has_z;
+ _FORCE_INLINE_ bool operator<(const _SelectResult& p_rr) const {
+ return has_z && p_rr.has_z ? p_rr.z < z : p_rr.has_z;
+ }
+ };
+
+ Vector<_SelectResult> selection_results;
+
+ struct LockList {
+ Point2 pos;
+ bool lock;
+ bool group;
+ LockList() { lock=false; group=false; }
+ };
+
+ List<LockList> lock_list;
+
+ struct BoneList {
+
+ Matrix32 xform;
+ Vector2 from;
+ Vector2 to;
+ ObjectID bone;
+ uint64_t last_pass;
+ };
+
+ uint64_t bone_last_frame;
+ Map<ObjectID,BoneList> bone_list;
+
+ Matrix32 bone_orig_xform;
+
+ struct BoneIK {
+
+ Variant orig_state;
+ Vector2 pos;
+ float len;
+ Node2D *node;
+ };
+
+ List<BoneIK> bone_ik_list;
+
+ struct PoseClipboard {
+
+ Vector2 pos;
+ Vector2 scale;
+ float rot;
+ ObjectID id;
+ };
+
+ List<PoseClipboard> pose_clipboard;
+
+ ToolButton *select_button;
+ ToolButton *list_select_button;
+ ToolButton *move_button;
+ ToolButton *rotate_button;
+
+ ToolButton *pivot_button;
+ ToolButton *pan_button;
+
+ ToolButton *lock_button;
+ ToolButton *unlock_button;
+
+ ToolButton *group_button;
+ ToolButton *ungroup_button;
+
+ MenuButton *edit_menu;
+ MenuButton *view_menu;
+ HBoxContainer *animation_hb;
+ MenuButton *animation_menu;
+ MenuButton *anchor_menu;
+
+ Button *key_loc_button;
+ Button *key_rot_button;
+ Button *key_scale_button;
+ Button *key_insert_button;
+
+ PopupMenu *selection_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;
+ bool _is_part_of_subscene(CanvasItem *p_item);
+ 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_pos(const Point2 &p_pos,Node* p_node,const Matrix32& p_parent_xform,const Matrix32& p_canvas_xform, Vector<_SelectResult> &r_items);
+ 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);
+
+ bool _select(CanvasItem *item, Point2 p_click_pos, bool p_append, bool p_drag=true);
+
+ ConfirmationDialog *snap_dialog;
+
+ AcceptDialog *value_dialog;
+ Label *dialog_label;
+ SpinBox *dialog_val;
+
+ CanvasItem *ref_item;
+
+ void _edit_set_pivot(const Vector2& mouse_pos);
+ 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, KeyMoveMODE p_move_mode);
+ void _list_select(const InputEventMouseButton& b);
+
+ DragType _find_drag_type(const Matrix32& p_xform, const Rect2& p_local_rect, const Point2& p_click, Vector2& r_point);
+
+ 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);
+ void _snap_changed();
+ void _selection_result_pressed(int);
+ void _selection_menu_hide();
+
+ 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();
+
+ void _unhandled_key_input(const InputEvent& p_ev);
+
+ void _viewport_input_event(const InputEvent& p_event);
+ void _viewport_draw();
+
+ void _set_anchor(Control::AnchorType p_left,Control::AnchorType p_top,Control::AnchorType p_right,Control::AnchorType p_bottom);
+
+ HSplitContainer *palette_split;
+ VSplitContainer *bottom_split;
+
+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:
+
+ Vector2 snap_point(Vector2 p_target, Vector2 p_start = Vector2(0, 0)) const;
+ float snap_angle(float p_target, float p_start = 0) const;
+
+ 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);
+
+ HSplitContainer *get_palette_split();
+ VSplitContainer *get_bottom_split();
+
+ 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();
+
+};
+
+class CanvasItemEditorViewport : public Control {
+ OBJ_TYPE( CanvasItemEditorViewport, Control );
+
+ String default_type;
+ Vector<String> types;
+
+ Vector<String> selected_files;
+ Node* target_node;
+ Point2 drop_pos;
+
+ EditorNode* editor;
+ EditorData* editor_data;
+ CanvasItemEditor* canvas;
+ Node2D* preview;
+ AcceptDialog* accept;
+ WindowDialog* selector;
+ Label* selector_label;
+ Label* label;
+ Label* label_desc;
+ ButtonGroup* btn_group;
+
+ void _on_mouse_exit();
+ void _on_select_type(Object* selected);
+ void _on_change_type();
+
+ void _create_preview(const Vector<String>& files) const;
+ void _remove_preview();
+
+ bool _cyclical_dependency_exists(const String& p_target_scene_path, Node* p_desired_node);
+ void _create_nodes(Node* parent, Node* child, String& path, const Point2& p_point);
+ bool _create_instance(Node* parent, String& path, const Point2& p_point);
+ void _perform_drop_data();
+
+ static void _bind_methods();
+
+protected:
+ void _notification(int p_what);
+
+public:
+ virtual bool can_drop_data(const Point2& p_point,const Variant& p_data) const;
+ virtual void drop_data(const Point2& p_point,const Variant& p_data);
+
+ CanvasItemEditorViewport(EditorNode *p_node, CanvasItemEditor* p_canvas);
+};
+
+#endif
diff --git a/editor/plugins/collision_polygon_2d_editor_plugin.cpp b/editor/plugins/collision_polygon_2d_editor_plugin.cpp
new file mode 100644
index 000000000..2ace72c66
--- /dev/null
+++ b/editor/plugins/collision_polygon_2d_editor_plugin.cpp
@@ -0,0 +1,482 @@
+/*************************************************************************/
+/* collision_polygon_2d_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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_2d_editor_plugin.h"
+
+#include "canvas_item_editor_plugin.h"
+#include "os/file_access.h"
+#include "editor/editor_settings.h"
+
+
+void CollisionPolygon2DEditor::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_READY: {
+
+ button_create->set_icon( get_icon("Edit","EditorIcons"));
+ button_edit->set_icon( get_icon("MovePoint","EditorIcons"));
+ button_edit->set_pressed(true);
+ get_tree()->connect("node_removed",this,"_node_removed");
+
+ } break;
+ case NOTIFICATION_FIXED_PROCESS: {
+
+
+ } break;
+ }
+
+}
+void CollisionPolygon2DEditor::_node_removed(Node *p_node) {
+
+ if(p_node==node) {
+ node=NULL;
+ hide();
+ canvas_item_editor->get_viewport_control()->update();
+ }
+
+}
+
+
+void CollisionPolygon2DEditor::_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 CollisionPolygon2DEditor::_wip_close() {
+
+ undo_redo->create_action(TTR("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->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+ wip.clear();
+ wip_active=false;
+ mode=MODE_EDIT;
+ button_edit->set_pressed(true);
+ button_create->set_pressed(false);
+ edited_point=-1;
+}
+
+bool CollisionPolygon2DEditor::forward_input_event(const InputEvent& p_event) {
+
+
+ if (!node)
+ return false;
+
+ switch(p_event.type) {
+
+ case InputEvent::MOUSE_BUTTON: {
+
+ const InputEventMouseButton &mb=p_event.mouse_button;
+
+ Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
+
+
+ Vector2 gpoint = Point2(mb.x,mb.y);
+ Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint);
+ cpoint=canvas_item_editor->snap_point(cpoint);
+ cpoint = node->get_global_transform().affine_inverse().xform(cpoint);
+
+ 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( cpoint );
+ wip_active=true;
+ edited_point_pos=cpoint;
+ canvas_item_editor->get_viewport_control()->update();
+ edited_point=1;
+ return true;
+ } else {
+
+
+ if (wip.size()>1 && xform.xform(wip[0]).distance_to(gpoint)<grab_treshold) {
+ //wip closed
+ _wip_close();
+
+ return true;
+ } else {
+
+ wip.push_back( cpoint );
+ edited_point=wip.size();
+ canvas_item_editor->get_viewport_control()->update();
+ return true;
+
+ //add wip point
+ }
+ }
+ } else if (mb.button_index==BUTTON_RIGHT && mb.pressed && wip_active) {
+ _wip_close();
+ }
+
+
+
+ } break;
+
+ case MODE_EDIT: {
+
+ if (mb.button_index==BUTTON_LEFT) {
+ if (mb.pressed) {
+
+ if (mb.mod.control) {
+
+
+ if (poly.size() < 3) {
+
+ undo_redo->create_action(TTR("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->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+ return true;
+ }
+
+ //search edges
+ int closest_idx=-1;
+ Vector2 closest_pos;
+ real_t closest_dist=1e10;
+ for(int i=0;i<poly.size();i++) {
+
+ Vector2 points[2] ={ xform.xform(poly[i]),
+ xform.xform(poly[(i+1)%poly.size()]) };
+
+ Vector2 cp = Geometry::get_closest_point_to_segment_2d(gpoint,points);
+ if (cp.distance_squared_to(points[0])<CMP_EPSILON2 || cp.distance_squared_to(points[1])<CMP_EPSILON2)
+ continue; //not valid to reuse point
+
+ real_t d = cp.distance_to(gpoint);
+ if (d<closest_dist && d<grab_treshold) {
+ closest_dist=d;
+ closest_pos=cp;
+ closest_idx=i;
+ }
+
+
+ }
+
+ if (closest_idx>=0) {
+
+ pre_move_edit=poly;
+ poly.insert(closest_idx+1,xform.affine_inverse().xform(closest_pos));
+ edited_point=closest_idx+1;
+ edited_point_pos=xform.affine_inverse().xform(closest_pos);
+ node->set_polygon(poly);
+ canvas_item_editor->get_viewport_control()->update();
+ return true;
+ }
+ } else {
+
+ //look for points to move
+
+ int closest_idx=-1;
+ Vector2 closest_pos;
+ real_t closest_dist=1e10;
+ for(int i=0;i<poly.size();i++) {
+
+ Vector2 cp =xform.xform(poly[i]);
+
+ real_t d = cp.distance_to(gpoint);
+ if (d<closest_dist && d<grab_treshold) {
+ closest_dist=d;
+ closest_pos=cp;
+ closest_idx=i;
+ }
+
+ }
+
+ if (closest_idx>=0) {
+
+ pre_move_edit=poly;
+ edited_point=closest_idx;
+ edited_point_pos=xform.affine_inverse().xform(closest_pos);
+ canvas_item_editor->get_viewport_control()->update();
+ return true;
+ }
+ }
+ } else {
+
+ if (edited_point!=-1) {
+
+ //apply
+
+ ERR_FAIL_INDEX_V(edited_point,poly.size(),false);
+ poly[edited_point]=edited_point_pos;
+ undo_redo->create_action(TTR("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->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+
+ edited_point=-1;
+ return true;
+ }
+ }
+ } if (mb.button_index==BUTTON_RIGHT && mb.pressed && edited_point==-1) {
+
+
+
+ int closest_idx=-1;
+ Vector2 closest_pos;
+ real_t closest_dist=1e10;
+ for(int i=0;i<poly.size();i++) {
+
+ Vector2 cp =xform.xform(poly[i]);
+
+ real_t d = cp.distance_to(gpoint);
+ if (d<closest_dist && d<grab_treshold) {
+ closest_dist=d;
+ closest_pos=cp;
+ closest_idx=i;
+ }
+
+ }
+
+ if (closest_idx>=0) {
+
+
+ undo_redo->create_action(TTR("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->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+ return true;
+ }
+
+ }
+
+
+
+ } break;
+ }
+
+
+
+ } break;
+ case InputEvent::MOUSE_MOTION: {
+
+ const InputEventMouseMotion &mm=p_event.mouse_motion;
+
+ if (edited_point!=-1 && (wip_active || mm.button_mask&BUTTON_MASK_LEFT)) {
+
+ Vector2 gpoint = Point2(mm.x,mm.y);
+ Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint);
+ cpoint=canvas_item_editor->snap_point(cpoint);
+ edited_point_pos = node->get_global_transform().affine_inverse().xform(cpoint);
+
+ canvas_item_editor->get_viewport_control()->update();
+
+ }
+
+ } break;
+ }
+
+ return false;
+}
+void CollisionPolygon2DEditor::_canvas_draw() {
+
+ if (!node)
+ return;
+
+ Control *vpc = canvas_item_editor->get_viewport_control();
+
+ 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");
+
+ 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);
+ vpc->draw_line(point,next_point,col,2);
+ vpc->draw_texture(handle,point-handle->get_size()*0.5);
+ }
+}
+
+
+
+void CollisionPolygon2DEditor::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->get_viewport_control()->is_connected("draw",this,"_canvas_draw"))
+ canvas_item_editor->get_viewport_control()->connect("draw",this,"_canvas_draw");
+ wip.clear();
+ wip_active=false;
+ edited_point=-1;
+ canvas_item_editor->get_viewport_control()->update();
+
+ } else {
+ node=NULL;
+
+ if (canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw"))
+ canvas_item_editor->get_viewport_control()->disconnect("draw",this,"_canvas_draw");
+
+ }
+
+}
+
+void CollisionPolygon2DEditor::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("_menu_option"),&CollisionPolygon2DEditor::_menu_option);
+ ObjectTypeDB::bind_method(_MD("_canvas_draw"),&CollisionPolygon2DEditor::_canvas_draw);
+ ObjectTypeDB::bind_method(_MD("_node_removed"),&CollisionPolygon2DEditor::_node_removed);
+
+}
+
+CollisionPolygon2DEditor::CollisionPolygon2DEditor(EditorNode *p_editor) {
+
+ node=NULL;
+ canvas_item_editor=NULL;
+ editor=p_editor;
+ undo_redo = editor->get_undo_redo();
+
+ add_child( memnew( VSeparator ));
+ button_create = memnew( ToolButton );
+ add_child(button_create);
+ button_create->connect("pressed",this,"_menu_option",varray(MODE_CREATE));
+ button_create->set_toggle_mode(true);
+ button_create->set_tooltip(TTR("Create a new polygon from scratch."));
+
+ button_edit = memnew( ToolButton );
+ add_child(button_edit);
+ button_edit->connect("pressed",this,"_menu_option",varray(MODE_EDIT));
+ button_edit->set_toggle_mode(true);
+ button_edit->set_tooltip("Edit existing polygon:\nLMB: Move Point.\nCtrl+LMB: Split Segment.\nRMB: Erase Point.");
+
+ //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 CollisionPolygon2DEditorPlugin::edit(Object *p_object) {
+
+ collision_polygon_editor->edit(p_object->cast_to<Node>());
+}
+
+bool CollisionPolygon2DEditorPlugin::handles(Object *p_object) const {
+
+ return p_object->is_type("CollisionPolygon2D");
+}
+
+void CollisionPolygon2DEditorPlugin::make_visible(bool p_visible) {
+
+ if (p_visible) {
+ collision_polygon_editor->show();
+ } else {
+
+ collision_polygon_editor->hide();
+ collision_polygon_editor->edit(NULL);
+ }
+
+}
+
+CollisionPolygon2DEditorPlugin::CollisionPolygon2DEditorPlugin(EditorNode *p_node) {
+
+ editor=p_node;
+ collision_polygon_editor = memnew( CollisionPolygon2DEditor(p_node) );
+ CanvasItemEditor::get_singleton()->add_control_to_menu_panel(collision_polygon_editor);
+
+ collision_polygon_editor->hide();
+
+
+
+}
+
+
+CollisionPolygon2DEditorPlugin::~CollisionPolygon2DEditorPlugin()
+{
+}
+
diff --git a/editor/plugins/collision_polygon_2d_editor_plugin.h b/editor/plugins/collision_polygon_2d_editor_plugin.h
new file mode 100644
index 000000000..61b721792
--- /dev/null
+++ b/editor/plugins/collision_polygon_2d_editor_plugin.h
@@ -0,0 +1,111 @@
+/*************************************************************************/
+/* collision_polygon_2d_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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_2D_EDITOR_PLUGIN_H
+#define COLLISION_POLYGON_2D_EDITOR_PLUGIN_H
+
+
+#include "editor/editor_plugin.h"
+#include "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 CollisionPolygon2DEditor : public HBoxContainer {
+
+ OBJ_TYPE(CollisionPolygon2DEditor, 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:
+
+ bool forward_input_event(const InputEvent& p_event);
+ void edit(Node *p_collision_polygon);
+ CollisionPolygon2DEditor(EditorNode *p_editor);
+};
+
+class CollisionPolygon2DEditorPlugin : public EditorPlugin {
+
+ OBJ_TYPE( CollisionPolygon2DEditorPlugin, EditorPlugin );
+
+ CollisionPolygon2DEditor *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 "CollisionPolygon2D"; }
+ 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);
+
+ CollisionPolygon2DEditorPlugin(EditorNode *p_node);
+ ~CollisionPolygon2DEditorPlugin();
+
+};
+
+#endif // COLLISION_POLYGON_2D_EDITOR_PLUGIN_H
diff --git a/editor/plugins/collision_polygon_editor_plugin.cpp b/editor/plugins/collision_polygon_editor_plugin.cpp
new file mode 100644
index 000000000..906628fa0
--- /dev/null
+++ b/editor/plugins/collision_polygon_editor_plugin.cpp
@@ -0,0 +1,644 @@
+/*************************************************************************/
+/* collision_polygon_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "spatial_editor_plugin.h"
+#include "os/file_access.h"
+#include "editor/editor_settings.h"
+#include "scene/3d/camera.h"
+#include "canvas_item_editor_plugin.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);
+ get_tree()->connect("node_removed",this,"_node_removed");
+
+
+ } break;
+ case NOTIFICATION_PROCESS: {
+
+ if (node->get_depth() != prev_depth) {
+ _polygon_draw();
+ prev_depth=node->get_depth();
+ }
+
+ } break;
+ }
+
+}
+void CollisionPolygonEditor::_node_removed(Node *p_node) {
+
+ if(p_node==node) {
+ node=NULL;
+ if (imgeom->get_parent()==p_node)
+ p_node->remove_child(imgeom);
+ hide();
+ set_process(false);
+ }
+
+}
+
+
+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(TTR("Create Poly3D"));
+ 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(this,"_polygon_draw");
+ undo_redo->add_undo_method(this,"_polygon_draw");
+ wip.clear();
+ wip_active=false;
+ mode=MODE_EDIT;
+ button_edit->set_pressed(true);
+ button_create->set_pressed(false);
+ edited_point=-1;
+ undo_redo->commit_action();
+
+}
+
+bool CollisionPolygonEditor::forward_spatial_input_event(Camera* p_camera,const InputEvent& p_event) {
+
+ if (!node)
+ return false;
+
+ Transform gt = node->get_global_transform();
+ Transform gi = gt.affine_inverse();
+ float depth = node->get_depth()*0.5;
+ Vector3 n = gt.basis.get_axis(2).normalized();
+ Plane p(gt.origin+n*depth,n);
+
+
+ switch(p_event.type) {
+
+ case InputEvent::MOUSE_BUTTON: {
+
+ const InputEventMouseButton &mb=p_event.mouse_button;
+
+
+
+ Vector2 gpoint=Point2(mb.x,mb.y);
+ Vector3 ray_from = p_camera->project_ray_origin(gpoint);
+ Vector3 ray_dir = p_camera->project_ray_normal(gpoint);
+
+ Vector3 spoint;
+
+ if (!p.intersects_ray(ray_from,ray_dir,&spoint))
+ break;
+
+ spoint = gi.xform(spoint);
+
+ Vector2 cpoint(spoint.x,spoint.y);
+
+ cpoint=CanvasItemEditor::get_singleton()->snap_point(cpoint);
+
+ 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( cpoint );
+ wip_active=true;
+ edited_point_pos=cpoint;
+ _polygon_draw();
+ edited_point=1;
+ return true;
+ } else {
+
+
+ if (wip.size()>1 && p_camera->unproject_position(gt.xform(Vector3(wip[0].x,wip[0].y,depth))).distance_to(gpoint)<grab_treshold) {
+ //wip closed
+ _wip_close();
+
+ return true;
+ } else {
+
+ wip.push_back( cpoint );
+ edited_point=wip.size();
+ _polygon_draw();
+ 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(TTR("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(this,"_polygon_draw");
+ undo_redo->add_undo_method(this,"_polygon_draw");
+ 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] ={
+ p_camera->unproject_position(gt.xform(Vector3(poly[i].x,poly[i].y,depth))),
+ p_camera->unproject_position(gt.xform(Vector3(poly[(i+1)%poly.size()].x,poly[(i+1)%poly.size()].y,depth)))
+ };
+
+ 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,cpoint);
+ edited_point=closest_idx+1;
+ edited_point_pos=cpoint;
+ node->set_polygon(poly);
+ _polygon_draw();
+ 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 = p_camera->unproject_position(gt.xform(Vector3(poly[i].x,poly[i].y,depth)));
+
+ 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=poly[closest_idx];
+ _polygon_draw();
+ 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(TTR("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(this,"_polygon_draw");
+ undo_redo->add_undo_method(this,"_polygon_draw");
+ 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 = p_camera->unproject_position(gt.xform(Vector3(poly[i].x,poly[i].y,depth)));
+
+ 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(TTR("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(this,"_polygon_draw");
+ undo_redo->add_undo_method(this,"_polygon_draw");
+ undo_redo->commit_action();
+ return true;
+ }
+
+ }
+
+
+
+ } break;
+ }
+
+
+
+ } break;
+ case InputEvent::MOUSE_MOTION: {
+
+ const InputEventMouseMotion &mm=p_event.mouse_motion;
+
+ if (edited_point!=-1 && (wip_active || mm.button_mask&BUTTON_MASK_LEFT)) {
+
+ Vector2 gpoint = Point2(mm.x,mm.y);
+
+ Vector3 ray_from = p_camera->project_ray_origin(gpoint);
+ Vector3 ray_dir = p_camera->project_ray_normal(gpoint);
+
+ Vector3 spoint;
+
+ if (!p.intersects_ray(ray_from,ray_dir,&spoint))
+ break;
+
+ spoint = gi.xform(spoint);
+
+ Vector2 cpoint(spoint.x,spoint.y);
+
+ cpoint=CanvasItemEditor::get_singleton()->snap_point(cpoint);
+ edited_point_pos = cpoint;
+
+ _polygon_draw();
+
+ }
+
+ } break;
+ }
+
+ return false;
+}
+void CollisionPolygonEditor::_polygon_draw() {
+
+ if (!node)
+ return;
+
+ Vector<Vector2> poly;
+
+ if (wip_active)
+ poly=wip;
+ else
+ poly=node->get_polygon();
+
+
+ float depth = node->get_depth()*0.5;
+
+ imgeom->clear();
+ imgeom->set_material_override(line_material);
+ imgeom->begin(Mesh::PRIMITIVE_LINES,Ref<Texture>());
+
+
+ Rect2 rect;
+
+ 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()];
+
+ if (i==0)
+ rect.pos=p;
+ else
+ rect.expand_to(p);
+
+ Vector3 point = Vector3(p.x,p.y,depth);
+ Vector3 next_point = Vector3(p2.x,p2.y,depth);
+
+ imgeom->set_color(Color(1,0.3,0.1,0.8));
+ imgeom->add_vertex(point);
+ imgeom->set_color(Color(1,0.3,0.1,0.8));
+ imgeom->add_vertex(next_point);
+
+ //Color col=Color(1,0.3,0.1,0.8);
+ //vpc->draw_line(point,next_point,col,2);
+ //vpc->draw_texture(handle,point-handle->get_size()*0.5);
+ }
+
+ rect=rect.grow(1);
+
+ AABB r;
+ r.pos.x=rect.pos.x;
+ r.pos.y=rect.pos.y;
+ r.pos.z=depth;
+ r.size.x=rect.size.x;
+ r.size.y=rect.size.y;
+ r.size.z=0;
+
+ imgeom->set_color(Color(0.8,0.8,0.8,0.2));
+ imgeom->add_vertex(r.pos);
+ imgeom->set_color(Color(0.8,0.8,0.8,0.2));
+ imgeom->add_vertex(r.pos+Vector3(0.3,0,0));
+ imgeom->set_color(Color(0.8,0.8,0.8,0.2));
+ imgeom->add_vertex(r.pos);
+ imgeom->set_color(Color(0.8,0.8,0.8,0.2));
+ imgeom->add_vertex(r.pos+Vector3(0.0,0.3,0));
+
+ imgeom->set_color(Color(0.8,0.8,0.8,0.2));
+ imgeom->add_vertex(r.pos+Vector3(r.size.x,0,0));
+ imgeom->set_color(Color(0.8,0.8,0.8,0.2));
+ imgeom->add_vertex(r.pos+Vector3(r.size.x,0,0)-Vector3(0.3,0,0));
+ imgeom->set_color(Color(0.8,0.8,0.8,0.2));
+ imgeom->add_vertex(r.pos+Vector3(r.size.x,0,0));
+ imgeom->set_color(Color(0.8,0.8,0.8,0.2));
+ imgeom->add_vertex(r.pos+Vector3(r.size.x,0,0)+Vector3(0,0.3,0));
+
+ imgeom->set_color(Color(0.8,0.8,0.8,0.2));
+ imgeom->add_vertex(r.pos+Vector3(0,r.size.y,0));
+ imgeom->set_color(Color(0.8,0.8,0.8,0.2));
+ imgeom->add_vertex(r.pos+Vector3(0,r.size.y,0)-Vector3(0,0.3,0));
+ imgeom->set_color(Color(0.8,0.8,0.8,0.2));
+ imgeom->add_vertex(r.pos+Vector3(0,r.size.y,0));
+ imgeom->set_color(Color(0.8,0.8,0.8,0.2));
+ imgeom->add_vertex(r.pos+Vector3(0,r.size.y,0)+Vector3(0.3,0,0));
+
+ imgeom->set_color(Color(0.8,0.8,0.8,0.2));
+ imgeom->add_vertex(r.pos+r.size);
+ imgeom->set_color(Color(0.8,0.8,0.8,0.2));
+ imgeom->add_vertex(r.pos+r.size-Vector3(0.3,0,0));
+ imgeom->set_color(Color(0.8,0.8,0.8,0.2));
+ imgeom->add_vertex(r.pos+r.size);
+ imgeom->set_color(Color(0.8,0.8,0.8,0.2));
+ imgeom->add_vertex(r.pos+r.size-Vector3(0.0,0.3,0));
+
+ imgeom->end();
+
+
+ while(m->get_surface_count()) {
+ m->surface_remove(0);
+ }
+
+ if (poly.size()==0)
+ return;
+
+ Array a;
+ a.resize(Mesh::ARRAY_MAX);
+ DVector<Vector3> va;
+ {
+
+ va.resize(poly.size());
+ DVector<Vector3>::Write w=va.write();
+ for(int i=0;i<poly.size();i++) {
+
+
+ Vector2 p,p2;
+ p = i==edited_point ? edited_point_pos : poly[i];
+
+ Vector3 point = Vector3(p.x,p.y,depth);
+ w[i]=point;
+ }
+ }
+ a[Mesh::ARRAY_VERTEX]=va;
+ m->add_surface(Mesh::PRIMITIVE_POINTS,a);
+ m->surface_set_material(0,handle_material);
+
+}
+
+
+
+void CollisionPolygonEditor::edit(Node *p_collision_polygon) {
+
+
+
+ if (p_collision_polygon) {
+
+ node=p_collision_polygon->cast_to<CollisionPolygon>();
+ wip.clear();
+ wip_active=false;
+ edited_point=-1;
+ p_collision_polygon->add_child(imgeom);
+ _polygon_draw();
+ set_process(true);
+ prev_depth=-1;
+
+ } else {
+ node=NULL;
+
+ if (imgeom->get_parent())
+ imgeom->get_parent()->remove_child(imgeom);
+
+ set_process(false);
+ }
+
+}
+
+void CollisionPolygonEditor::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("_menu_option"),&CollisionPolygonEditor::_menu_option);
+ ObjectTypeDB::bind_method(_MD("_polygon_draw"),&CollisionPolygonEditor::_polygon_draw);
+ ObjectTypeDB::bind_method(_MD("_node_removed"),&CollisionPolygonEditor::_node_removed);
+
+}
+
+CollisionPolygonEditor::CollisionPolygonEditor(EditorNode *p_editor) {
+
+
+ node=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;
+ imgeom = memnew( ImmediateGeometry );
+ imgeom->set_transform(Transform(Matrix3(),Vector3(0,0,0.00001)));
+
+
+ line_material = Ref<FixedMaterial>( memnew( FixedMaterial ));
+ line_material->set_flag(Material::FLAG_UNSHADED, true);
+ line_material->set_line_width(3.0);
+ line_material->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
+ line_material->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY, true);
+ line_material->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1));
+
+
+
+
+ handle_material = Ref<FixedMaterial>( memnew( FixedMaterial ));
+ handle_material->set_flag(Material::FLAG_UNSHADED, true);
+ handle_material->set_fixed_flag(FixedMaterial::FLAG_USE_POINT_SIZE, true);
+ handle_material->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1));
+ handle_material->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA, true);
+ handle_material->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY, false);
+ Ref<Texture> handle=editor->get_gui_base()->get_icon("Editor3DHandle","EditorIcons");
+ handle_material->set_point_size(handle->get_width());
+ handle_material->set_texture(FixedMaterial::PARAM_DIFFUSE,handle);
+
+ pointsm = memnew( MeshInstance );
+ imgeom->add_child(pointsm);
+ m = Ref<Mesh>( memnew( Mesh ) );
+ pointsm->set_mesh(m);
+ pointsm->set_transform(Transform(Matrix3(),Vector3(0,0,0.00001)));
+
+
+}
+
+CollisionPolygonEditor::~CollisionPolygonEditor() {
+
+ memdelete( imgeom );
+}
+
+
+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("CollisionPolygon");
+}
+
+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) );
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(collision_polygon_editor);
+
+ collision_polygon_editor->hide();
+
+
+
+}
+
+
+CollisionPolygonEditorPlugin::~CollisionPolygonEditorPlugin()
+{
+}
+
diff --git a/editor/plugins/collision_polygon_editor_plugin.h b/editor/plugins/collision_polygon_editor_plugin.h
new file mode 100644
index 000000000..291b3313d
--- /dev/null
+++ b/editor/plugins/collision_polygon_editor_plugin.h
@@ -0,0 +1,121 @@
+/*************************************************************************/
+/* collision_polygon_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "editor/editor_plugin.h"
+#include "editor/editor_node.h"
+#include "scene/3d/collision_polygon.h"
+#include "scene/3d/immediate_geometry.h"
+#include "scene/3d/mesh_instance.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;
+
+
+ Ref<FixedMaterial> line_material;
+ Ref<FixedMaterial> handle_material;
+
+ EditorNode *editor;
+ Panel *panel;
+ CollisionPolygon *node;
+ ImmediateGeometry *imgeom;
+ MeshInstance *pointsm;
+ Ref<Mesh> m;
+
+ MenuButton *options;
+
+ int edited_point;
+ Vector2 edited_point_pos;
+ Vector<Vector2> pre_move_edit;
+ Vector<Vector2> wip;
+ bool wip_active;
+
+ float prev_depth;
+
+ void _wip_close();
+ void _polygon_draw();
+ void _menu_option(int p_option);
+
+protected:
+ void _notification(int p_what);
+ void _node_removed(Node *p_node);
+ static void _bind_methods();
+public:
+
+ virtual bool forward_spatial_input_event(Camera* p_camera,const InputEvent& p_event);
+ void edit(Node *p_collision_polygon);
+ CollisionPolygonEditor(EditorNode *p_editor);
+ ~CollisionPolygonEditor();
+};
+
+class CollisionPolygonEditorPlugin : public EditorPlugin {
+
+ OBJ_TYPE( CollisionPolygonEditorPlugin, EditorPlugin );
+
+ CollisionPolygonEditor *collision_polygon_editor;
+ EditorNode *editor;
+
+public:
+
+ virtual bool forward_spatial_input_event(Camera* p_camera,const InputEvent& p_event) { return collision_polygon_editor->forward_spatial_input_event(p_camera,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/editor/plugins/collision_shape_2d_editor_plugin.cpp b/editor/plugins/collision_shape_2d_editor_plugin.cpp
new file mode 100644
index 000000000..6787aeba0
--- /dev/null
+++ b/editor/plugins/collision_shape_2d_editor_plugin.cpp
@@ -0,0 +1,601 @@
+/*************************************************************************/
+/* collision_shape_2d_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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_shape_2d_editor_plugin.h"
+
+#include "canvas_item_editor_plugin.h"
+
+#include "scene/resources/segment_shape_2d.h"
+#include "scene/resources/shape_line_2d.h"
+#include "scene/resources/circle_shape_2d.h"
+#include "scene/resources/rectangle_shape_2d.h"
+#include "scene/resources/capsule_shape_2d.h"
+#include "scene/resources/convex_polygon_shape_2d.h"
+#include "scene/resources/concave_polygon_shape_2d.h"
+
+Variant CollisionShape2DEditor::get_handle_value(int idx) const {
+
+ switch ( shape_type ) {
+ case CAPSULE_SHAPE: {
+ Ref<CapsuleShape2D> capsule = node->get_shape();
+
+ if (idx==0) {
+ return capsule->get_radius();
+ } else if (idx==1) {
+ return capsule->get_height();
+ }
+
+ } break;
+
+ case CIRCLE_SHAPE: {
+ Ref<CircleShape2D> circle = node->get_shape();
+
+ if (idx==0) {
+ return circle->get_radius();
+ }
+
+ } break;
+
+ case CONCAVE_POLYGON_SHAPE: {
+
+ } break;
+
+ case CONVEX_POLYGON_SHAPE: {
+
+ } break;
+
+ case LINE_SHAPE: {
+ Ref<LineShape2D> line = node->get_shape();
+
+ if (idx==0) {
+ return line->get_d();
+ } else {
+ return line->get_normal();
+ }
+
+ } break;
+
+ case RAY_SHAPE: {
+ Ref<RayShape2D> ray = node->get_shape();
+
+ if (idx==0) {
+ return ray->get_length();
+ }
+
+ } break;
+
+ case RECTANGLE_SHAPE: {
+ Ref<RectangleShape2D> rect = node->get_shape();
+
+ if (idx<2) {
+ return rect->get_extents().abs();
+ }
+
+ } break;
+
+ case SEGMENT_SHAPE: {
+ Ref<SegmentShape2D> seg = node->get_shape();
+
+ if (idx==0) {
+ return seg->get_a();
+ } else if (idx==1) {
+ return seg->get_b();
+ }
+
+ } break;
+ }
+
+ return Variant();
+}
+
+void CollisionShape2DEditor::set_handle(int idx, Point2& p_point) {
+
+ switch ( shape_type ) {
+ case CAPSULE_SHAPE: {
+ if (idx < 2) {
+ Ref<CapsuleShape2D> capsule = node->get_shape();
+
+ real_t parameter = Math::abs(p_point[idx]);
+
+ if (idx==0) {
+ capsule->set_radius(parameter);
+ } else if (idx==1){
+ capsule->set_height(parameter*2 - capsule->get_radius()*2);
+ }
+
+ canvas_item_editor->get_viewport_control()->update();
+ }
+
+ } break;
+
+ case CIRCLE_SHAPE: {
+ Ref<CircleShape2D> circle = node->get_shape();
+ circle->set_radius(p_point.length());
+
+ canvas_item_editor->get_viewport_control()->update();
+
+ } break;
+
+ case CONCAVE_POLYGON_SHAPE: {
+
+ } break;
+
+ case CONVEX_POLYGON_SHAPE: {
+
+ } break;
+
+ case LINE_SHAPE: {
+ if (idx<2) {
+ Ref<LineShape2D> line = node->get_shape();
+
+ if (idx==0){
+ line->set_d(p_point.length());
+ }else{
+ line->set_normal(p_point.normalized());
+ }
+
+ canvas_item_editor->get_viewport_control()->update();
+ }
+
+
+ } break;
+
+ case RAY_SHAPE: {
+ Ref<RayShape2D> ray = node->get_shape();
+
+ ray->set_length(Math::abs(p_point.y));
+
+ canvas_item_editor->get_viewport_control()->update();
+
+ } break;
+
+ case RECTANGLE_SHAPE: {
+ if (idx<2) {
+ Ref<RectangleShape2D> rect = node->get_shape();
+
+ Vector2 extents = rect->get_extents();
+ extents[idx] = p_point[idx];
+
+ rect->set_extents(extents.abs());
+
+ canvas_item_editor->get_viewport_control()->update();
+ }
+
+ } break;
+
+ case SEGMENT_SHAPE: {
+ if (edit_handle < 2) {
+ Ref<SegmentShape2D> seg = node->get_shape();
+
+ if (idx==0) {
+ seg->set_a(p_point);
+ } else if (idx==1) {
+ seg->set_b(p_point);
+ }
+
+ canvas_item_editor->get_viewport_control()->update();
+ }
+
+ } break;
+ }
+}
+
+void CollisionShape2DEditor::commit_handle(int idx, Variant& p_org) {
+
+ Control* c = canvas_item_editor->get_viewport_control();
+ undo_redo->create_action(TTR("Set Handle"));
+
+ switch ( shape_type ) {
+ case CAPSULE_SHAPE: {
+ Ref<CapsuleShape2D> capsule = node->get_shape();
+
+ if (idx==0) {
+ undo_redo->add_do_method(capsule.ptr(),"set_radius",capsule->get_radius());
+ undo_redo->add_do_method(c,"update");
+ undo_redo->add_undo_method(capsule.ptr(),"set_radius",p_org);
+ undo_redo->add_do_method(c,"update");
+ } else if (idx==1) {
+ undo_redo->add_do_method(capsule.ptr(),"set_height",capsule->get_height());
+ undo_redo->add_do_method(c,"update");
+ undo_redo->add_undo_method(capsule.ptr(),"set_height",p_org);
+ undo_redo->add_undo_method(c,"update");
+ }
+
+ } break;
+
+ case CIRCLE_SHAPE: {
+ Ref<CircleShape2D> circle = node->get_shape();
+
+ undo_redo->add_do_method(circle.ptr(),"set_radius",circle->get_radius());
+ undo_redo->add_do_method(c,"update");
+ undo_redo->add_undo_method(circle.ptr(),"set_radius",p_org);
+ undo_redo->add_undo_method(c,"update");
+
+ } break;
+
+ case CONCAVE_POLYGON_SHAPE: {
+
+ } break;
+
+ case CONVEX_POLYGON_SHAPE: {
+
+ } break;
+
+ case LINE_SHAPE: {
+ Ref<LineShape2D> line = node->get_shape();
+
+ if (idx==0) {
+ undo_redo->add_do_method(line.ptr(),"set_d",line->get_d());
+ undo_redo->add_do_method(c,"update");
+ undo_redo->add_undo_method(line.ptr(),"set_d",p_org);
+ undo_redo->add_undo_method(c,"update");
+ } else {
+ undo_redo->add_do_method(line.ptr(),"set_normal",line->get_normal());
+ undo_redo->add_do_method(c,"update");
+ undo_redo->add_undo_method(line.ptr(),"set_normal",p_org);
+ undo_redo->add_undo_method(c,"update");
+ }
+
+ } break;
+
+ case RAY_SHAPE: {
+ Ref<RayShape2D> ray = node->get_shape();
+
+ undo_redo->add_do_method(ray.ptr(),"set_length",ray->get_length());
+ undo_redo->add_do_method(c,"update");
+ undo_redo->add_undo_method(ray.ptr(),"set_length",p_org);
+ undo_redo->add_undo_method(c,"update");
+
+ } break;
+
+ case RECTANGLE_SHAPE: {
+ Ref<RectangleShape2D> rect = node->get_shape();
+
+ undo_redo->add_do_method(rect.ptr(),"set_extents",rect->get_extents());
+ undo_redo->add_do_method(c,"update");
+ undo_redo->add_undo_method(rect.ptr(),"set_extents",p_org);
+ undo_redo->add_undo_method(c,"update");
+
+ } break;
+
+ case SEGMENT_SHAPE: {
+ Ref<SegmentShape2D> seg = node->get_shape();
+ if (idx==0) {
+ undo_redo->add_do_method(seg.ptr(),"set_a",seg->get_a());
+ undo_redo->add_do_method(c,"update");
+ undo_redo->add_undo_method(seg.ptr(),"set_a",p_org);
+ undo_redo->add_undo_method(c,"update");
+ } else if (idx==1) {
+ undo_redo->add_do_method(seg.ptr(),"set_b",seg->get_b());
+ undo_redo->add_do_method(c,"update");
+ undo_redo->add_undo_method(seg.ptr(),"set_b",p_org);
+ undo_redo->add_undo_method(c,"update");
+ }
+
+ } break;
+ }
+
+ undo_redo->commit_action();
+}
+
+bool CollisionShape2DEditor::forward_input_event(const InputEvent& p_event) {
+
+ if (!node) {
+ return false;
+ }
+
+ if (!node->get_shape().is_valid()) {
+ return false;
+ }
+
+ if (shape_type == -1) {
+ return false;
+ }
+
+ switch( p_event.type ) {
+ case InputEvent::MOUSE_BUTTON: {
+ const InputEventMouseButton& mb = p_event.mouse_button;
+
+ Matrix32 gt = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
+
+ Point2 gpoint(mb.x,mb.y);
+
+ if (mb.button_index == BUTTON_LEFT) {
+ if (mb.pressed) {
+ for (int i = 0; i < handles.size(); i++) {
+ if (gt.xform(handles[i]).distance_to(gpoint) < 8) {
+ edit_handle = i;
+
+ break;
+ }
+ }
+
+ if (edit_handle==-1) {
+ pressed = false;
+
+ return false;
+ }
+
+ original = get_handle_value(edit_handle);
+ pressed = true;
+
+ return true;
+
+ } else {
+ if (pressed) {
+ commit_handle(edit_handle, original);
+
+ edit_handle = -1;
+ pressed = false;
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+
+ } break;
+
+ case InputEvent::MOUSE_MOTION: {
+ const InputEventMouseMotion& mm = p_event.mouse_motion;
+
+ if (edit_handle == -1 || !pressed) {
+ return false;
+ }
+
+ Point2 gpoint = Point2(mm.x,mm.y);
+ Point2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint);
+ cpoint = canvas_item_editor->snap_point(cpoint);
+ cpoint = node->get_global_transform().affine_inverse().xform(cpoint);
+
+ set_handle(edit_handle, cpoint);
+
+ return true;
+
+ } break;
+ }
+
+ return false;
+}
+
+void CollisionShape2DEditor::_get_current_shape_type() {
+
+ if (!node) {
+ return;
+ }
+
+ Ref<Shape2D> s = node->get_shape();
+
+ if (!s.is_valid()) {
+ return;
+ }
+
+ if (s->cast_to<CapsuleShape2D>()) {
+ shape_type = CAPSULE_SHAPE;
+ } else if (s->cast_to<CircleShape2D>()) {
+ shape_type = CIRCLE_SHAPE;
+ } else if (s->cast_to<ConcavePolygonShape2D>()) {
+ shape_type = CONCAVE_POLYGON_SHAPE;
+ } else if (s->cast_to<ConvexPolygonShape2D>()) {
+ shape_type = CONVEX_POLYGON_SHAPE;
+ } else if (s->cast_to<LineShape2D>()) {
+ shape_type = LINE_SHAPE;
+ } else if (s->cast_to<RayShape2D>()) {
+ shape_type = RAY_SHAPE;
+ } else if (s->cast_to<RectangleShape2D>()) {
+ shape_type = RECTANGLE_SHAPE;
+ } else if (s->cast_to<SegmentShape2D>()) {
+ shape_type = SEGMENT_SHAPE;
+ } else {
+ shape_type = -1;
+ }
+
+ canvas_item_editor->get_viewport_control()->update();
+}
+
+void CollisionShape2DEditor::_canvas_draw() {
+
+ if (!node) {
+ return;
+ }
+
+ if (!node->get_shape().is_valid()) {
+ return;
+ }
+
+ _get_current_shape_type();
+
+ if (shape_type == -1) {
+ return;
+ }
+
+ Control *c = canvas_item_editor->get_viewport_control();
+ Matrix32 gt = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
+
+ Ref<Texture> h = get_icon("EditorHandle","EditorIcons");
+ Vector2 size = h->get_size()*0.5;
+
+ handles.clear();
+
+ switch (shape_type) {
+ case CAPSULE_SHAPE: {
+ Ref<CapsuleShape2D> shape = node->get_shape();
+
+ handles.resize(2);
+ float radius = shape->get_radius();
+ float height = shape->get_height()/2;
+
+ handles[0] = Point2(radius, -height);
+ handles[1] = Point2(0,-(height + radius));
+
+ c->draw_texture(h, gt.xform(handles[0])-size);
+ c->draw_texture(h, gt.xform(handles[1])-size);
+
+ } break;
+
+ case CIRCLE_SHAPE: {
+ Ref<CircleShape2D> shape = node->get_shape();
+
+ handles.resize(1);
+ handles[0] = Point2(shape->get_radius(),0);
+
+ c->draw_texture(h, gt.xform(handles[0])-size);
+
+ } break;
+
+ case CONCAVE_POLYGON_SHAPE: {
+
+ } break;
+
+ case CONVEX_POLYGON_SHAPE: {
+
+ } break;
+
+ case LINE_SHAPE: {
+ Ref<LineShape2D> shape = node->get_shape();
+
+ handles.resize(2);
+ handles[0] = shape->get_normal() * shape->get_d();
+ handles[1] = shape->get_normal() * (shape->get_d() + 30.0);
+
+ c->draw_texture(h,gt.xform(handles[0])-size);
+ c->draw_texture(h,gt.xform(handles[1])-size);
+
+ } break;
+
+ case RAY_SHAPE: {
+ Ref<RayShape2D> shape = node->get_shape();
+
+ handles.resize(1);
+ handles[0] = Point2(0,shape->get_length());
+
+ c->draw_texture(h,gt.xform(handles[0])-size);
+
+ } break;
+
+ case RECTANGLE_SHAPE: {
+ Ref<RectangleShape2D> shape = node->get_shape();
+
+ handles.resize(2);
+ Vector2 ext = shape->get_extents();
+ handles[0] = Point2(ext.x,0);
+ handles[1] = Point2(0,-ext.y);
+
+ c->draw_texture(h,gt.xform(handles[0])-size);
+ c->draw_texture(h,gt.xform(handles[1])-size);
+
+ } break;
+
+ case SEGMENT_SHAPE: {
+ Ref<SegmentShape2D> shape = node->get_shape();
+
+ handles.resize(2);
+ handles[0] = shape->get_a();
+ handles[1] = shape->get_b();
+
+ c->draw_texture(h, gt.xform(handles[0])-size);
+ c->draw_texture(h, gt.xform(handles[1])-size);
+
+ } break;
+ }
+}
+
+void CollisionShape2DEditor::edit(Node* p_node) {
+
+ if (!canvas_item_editor) {
+ canvas_item_editor=CanvasItemEditor::get_singleton();
+ }
+
+ if (p_node) {
+ node=p_node->cast_to<CollisionShape2D>();
+
+ if (!canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw"))
+ canvas_item_editor->get_viewport_control()->connect("draw",this,"_canvas_draw");
+
+ _get_current_shape_type();
+
+ } else {
+ edit_handle = -1;
+ shape_type = -1;
+
+ if (canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw"))
+ canvas_item_editor->get_viewport_control()->disconnect("draw",this,"_canvas_draw");
+
+ node=NULL;
+ }
+
+ canvas_item_editor->get_viewport_control()->update();
+}
+
+void CollisionShape2DEditor::_bind_methods() {
+
+ ObjectTypeDB::bind_method("_canvas_draw",&CollisionShape2DEditor::_canvas_draw);
+ ObjectTypeDB::bind_method("_get_current_shape_type",&CollisionShape2DEditor::_get_current_shape_type);
+}
+
+CollisionShape2DEditor::CollisionShape2DEditor(EditorNode* p_editor) {
+
+ node = NULL;
+ canvas_item_editor = NULL;
+ editor = p_editor;
+
+ undo_redo = p_editor->get_undo_redo();
+
+ edit_handle = -1;
+ pressed = false;
+}
+
+void CollisionShape2DEditorPlugin::edit(Object* p_obj) {
+
+ collision_shape_2d_editor->edit(p_obj->cast_to<Node>());
+}
+
+bool CollisionShape2DEditorPlugin::handles(Object* p_obj) const {
+
+ return p_obj->is_type("CollisionShape2D");
+}
+
+void CollisionShape2DEditorPlugin::make_visible(bool visible) {
+
+ if (!visible) {
+ edit(NULL);
+ }
+}
+
+CollisionShape2DEditorPlugin::CollisionShape2DEditorPlugin(EditorNode* p_node) {
+
+ editor=p_node;
+
+ collision_shape_2d_editor = memnew( CollisionShape2DEditor(p_node) );
+ p_node->get_gui_base()->add_child(collision_shape_2d_editor);
+}
+
+CollisionShape2DEditorPlugin::~CollisionShape2DEditorPlugin() {
+
+}
diff --git a/editor/plugins/collision_shape_2d_editor_plugin.h b/editor/plugins/collision_shape_2d_editor_plugin.h
new file mode 100644
index 000000000..65dd72923
--- /dev/null
+++ b/editor/plugins/collision_shape_2d_editor_plugin.h
@@ -0,0 +1,101 @@
+/*************************************************************************/
+/* collision_shape_2d_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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_SHAPE_2D_EDITOR_PLUGIN_H
+#define COLLISION_SHAPE_2D_EDITOR_PLUGIN_H
+
+#include "editor/editor_plugin.h"
+#include "editor/editor_node.h"
+
+#include "scene/2d/collision_shape_2d.h"
+
+class CanvasItemEditor;
+
+class CollisionShape2DEditor : public Control {
+ OBJ_TYPE(CollisionShape2DEditor, Control);
+
+ enum ShapeType {
+ CAPSULE_SHAPE,
+ CIRCLE_SHAPE,
+ CONCAVE_POLYGON_SHAPE,
+ CONVEX_POLYGON_SHAPE,
+ LINE_SHAPE,
+ RAY_SHAPE,
+ RECTANGLE_SHAPE,
+ SEGMENT_SHAPE
+ };
+
+ EditorNode* editor;
+ UndoRedo* undo_redo;
+ CanvasItemEditor* canvas_item_editor;
+ CollisionShape2D* node;
+
+ Vector<Point2> handles;
+
+ int shape_type;
+ int edit_handle;
+ bool pressed;
+ Variant original;
+
+ Variant get_handle_value(int idx) const;
+ void set_handle(int idx, Point2& p_point);
+ void commit_handle(int idx, Variant& p_org);
+
+ void _get_current_shape_type();
+ void _canvas_draw();
+
+protected:
+ static void _bind_methods();
+
+public:
+ bool forward_input_event(const InputEvent& p_event);
+ void edit(Node* p_node);
+
+ CollisionShape2DEditor(EditorNode* p_editor);
+};
+
+class CollisionShape2DEditorPlugin : public EditorPlugin {
+ OBJ_TYPE(CollisionShape2DEditorPlugin, EditorPlugin);
+
+ CollisionShape2DEditor* collision_shape_2d_editor;
+ EditorNode* editor;
+
+public:
+ virtual bool forward_input_event(const InputEvent& p_event) { return collision_shape_2d_editor->forward_input_event(p_event); }
+
+ virtual String get_name() const { return "CollisionShape2D"; }
+ bool has_main_screen() const { return false; }
+ virtual void edit(Object* p_obj);
+ virtual bool handles(Object* p_obj) const;
+ virtual void make_visible(bool visible);
+
+ CollisionShape2DEditorPlugin(EditorNode* p_editor);
+ ~CollisionShape2DEditorPlugin();
+};
+
+#endif //COLLISION_SHAPE_2D_EDITOR_PLUGIN_H
diff --git a/editor/plugins/color_ramp_editor_plugin.cpp b/editor/plugins/color_ramp_editor_plugin.cpp
new file mode 100644
index 000000000..ad0ed7cef
--- /dev/null
+++ b/editor/plugins/color_ramp_editor_plugin.cpp
@@ -0,0 +1,111 @@
+/*************************************************************************/
+/* color_ramp_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "color_ramp_editor_plugin.h"
+#include "spatial_editor_plugin.h"
+#include "canvas_item_editor_plugin.h"
+
+ColorRampEditorPlugin::ColorRampEditorPlugin(EditorNode *p_node) {
+
+ editor=p_node;
+ ramp_editor = memnew( ColorRampEdit );
+
+
+ add_control_to_container(CONTAINER_PROPERTY_EDITOR_BOTTOM,ramp_editor);
+
+ ramp_editor->set_custom_minimum_size(Size2(100, 48));
+ ramp_editor->hide();
+ ramp_editor->connect("ramp_changed", this, "ramp_changed");
+}
+
+void ColorRampEditorPlugin::edit(Object *p_object) {
+
+ ColorRamp* color_ramp = p_object->cast_to<ColorRamp>();
+ if (!color_ramp)
+ return;
+ color_ramp_ref = Ref<ColorRamp>(color_ramp);
+ ramp_editor->set_points(color_ramp_ref->get_points());
+}
+
+bool ColorRampEditorPlugin::handles(Object *p_object) const {
+
+ return p_object->is_type("ColorRamp");
+
+}
+
+void ColorRampEditorPlugin::make_visible(bool p_visible) {
+
+ if (p_visible) {
+ ramp_editor->show();
+ } else {
+ ramp_editor->hide();
+ }
+
+}
+
+void ColorRampEditorPlugin::_ramp_changed() {
+
+ if(color_ramp_ref.is_valid())
+ {
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+
+ //Not sure if I should convert this data to DVector
+ Vector<float> new_offsets=ramp_editor->get_offsets();
+ Vector<Color> new_colors=ramp_editor->get_colors();
+ Vector<float> old_offsets=color_ramp_ref->get_offsets();
+ Vector<Color> old_colors=color_ramp_ref->get_colors();
+
+ if (old_offsets.size()!=new_offsets.size())
+ ur->create_action(TTR("Add/Remove Color Ramp Point"));
+ else
+ ur->create_action(TTR("Modify Color Ramp"),UndoRedo::MERGE_ENDS);
+ ur->add_do_method(this,"undo_redo_color_ramp",new_offsets,new_colors);
+ ur->add_undo_method(this,"undo_redo_color_ramp",old_offsets,old_colors);
+ ur->commit_action();
+
+ //color_ramp_ref->set_points(ramp_editor->get_points());
+ }
+}
+
+void ColorRampEditorPlugin::_undo_redo_color_ramp(const Vector<float>& offsets,
+ const Vector<Color>& colors) {
+
+ color_ramp_ref->set_offsets(offsets);
+ color_ramp_ref->set_colors(colors);
+ ramp_editor->set_points(color_ramp_ref->get_points());
+ ramp_editor->update();
+}
+
+ColorRampEditorPlugin::~ColorRampEditorPlugin(){
+}
+
+void ColorRampEditorPlugin::_bind_methods() {
+ ObjectTypeDB::bind_method(_MD("ramp_changed"),&ColorRampEditorPlugin::_ramp_changed);
+ ObjectTypeDB::bind_method(_MD("undo_redo_color_ramp","offsets","colors"),&ColorRampEditorPlugin::_undo_redo_color_ramp);
+}
diff --git a/editor/plugins/color_ramp_editor_plugin.h b/editor/plugins/color_ramp_editor_plugin.h
new file mode 100644
index 000000000..c72abdb60
--- /dev/null
+++ b/editor/plugins/color_ramp_editor_plugin.h
@@ -0,0 +1,62 @@
+/*************************************************************************/
+/* color_ramp_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 TOOLS_EDITOR_PLUGINS_COLOR_RAMP_EDITOR_PLUGIN_H_
+#define TOOLS_EDITOR_PLUGINS_COLOR_RAMP_EDITOR_PLUGIN_H_
+
+#include "editor/editor_plugin.h"
+#include "editor/editor_node.h"
+#include "scene/gui/color_ramp_edit.h"
+
+class ColorRampEditorPlugin : public EditorPlugin {
+
+ OBJ_TYPE( ColorRampEditorPlugin, EditorPlugin );
+
+ bool _2d;
+ Ref<ColorRamp> color_ramp_ref;
+ ColorRampEdit *ramp_editor;
+ EditorNode *editor;
+
+protected:
+ static void _bind_methods();
+ void _ramp_changed();
+ void _undo_redo_color_ramp(const Vector<float>& offsets, const Vector<Color>& colors);
+
+public:
+ virtual String get_name() const { return "ColorRamp"; }
+ 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);
+
+ ColorRampEditorPlugin(EditorNode *p_node);
+ ~ColorRampEditorPlugin();
+
+};
+
+#endif /* TOOLS_EDITOR_PLUGINS_COLOR_RAMP_EDITOR_PLUGIN_H_ */
diff --git a/editor/plugins/cube_grid_theme_editor_plugin.cpp b/editor/plugins/cube_grid_theme_editor_plugin.cpp
new file mode 100644
index 000000000..e4c041840
--- /dev/null
+++ b/editor/plugins/cube_grid_theme_editor_plugin.cpp
@@ -0,0 +1,356 @@
+/*************************************************************************/
+/* cube_grid_theme_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "editor/editor_node.h"
+#include "main/main.h"
+#include "editor/editor_settings.h"
+#include "scene/3d/navigation_mesh.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);
+ }
+ Ref<NavigationMesh> navmesh;
+ for(int j=0;j<mi->get_child_count();j++) {
+ Node *child2 = mi->get_child(j);
+ if (!child2->cast_to<NavigationMeshInstance>())
+ continue;
+ NavigationMeshInstance *sb = child2->cast_to<NavigationMeshInstance>();
+ navmesh=sb->get_navigation_mesh();
+ if (!navmesh.is_null())
+ break;
+ }
+ if(!navmesh.is_null()){
+ p_library->set_item_navmesh(id, navmesh);
+ }
+ }
+
+ //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",TTR("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(TTR("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(vformat(TTR("Remove item %d?"),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( EditorFileDialog );
+ file->set_mode(EditorFileDialog::MODE_OPEN_FILE);
+ //not for now?
+ List<String> extensions;
+ ResourceLoader::get_recognized_extensions_for_type("PackedScene",&extensions);
+ file->clear_filters();
+ file->set_title(TTR("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(TTR("Add Item"),MENU_OPTION_ADD_ITEM);
+ options->get_popup()->add_item(TTR("Remove Selected Item"),MENU_OPTION_REMOVE_ITEM);
+ options->get_popup()->add_separator();
+ options->get_popup()->add_item(TTR("Import from Scene"),MENU_OPTION_IMPORT_FROM_SCENE);
+ options->get_popup()->add_item(TTR("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/editor/plugins/cube_grid_theme_editor_plugin.h b/editor/plugins/cube_grid_theme_editor_plugin.h
new file mode 100644
index 000000000..c964fdf19
--- /dev/null
+++ b/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-2017 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 "editor/editor_node.h"
+
+
+class MeshLibraryEditor : public Control {
+
+ OBJ_TYPE( MeshLibraryEditor, Control );
+
+ Ref<MeshLibrary> theme;
+
+ EditorNode *editor;
+ MenuButton *menu;
+ ConfirmationDialog *cd;
+ EditorFileDialog *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/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp
new file mode 100644
index 000000000..fd2374672
--- /dev/null
+++ b/editor/plugins/editor_preview_plugins.cpp
@@ -0,0 +1,903 @@
+/*************************************************************************/
+/* editor_preview_plugins.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "editor_preview_plugins.h"
+#include "io/resource_loader.h"
+#include "editor/editor_settings.h"
+#include "io/file_access_memory.h"
+#include "os/os.h"
+#include "scene/resources/material.h"
+#include "scene/resources/sample.h"
+#include "scene/resources/mesh.h"
+#include "scene/resources/bit_mask.h"
+#include "editor/editor_scale.h"
+
+bool EditorTexturePreviewPlugin::handles(const String& p_type) const {
+
+ return (ObjectTypeDB::is_type(p_type,"ImageTexture") || ObjectTypeDB::is_type(p_type, "AtlasTexture"));
+}
+
+Ref<Texture> EditorTexturePreviewPlugin::generate(const RES& p_from) {
+
+ Image img;
+ Ref<AtlasTexture> atex = p_from;
+ if (atex.is_valid()) {
+ Ref<ImageTexture> tex = atex->get_atlas();
+ if (!tex.is_valid()) {
+ return Ref<Texture>();
+ }
+ Image atlas = tex->get_data();
+ img = atlas.get_rect(atex->get_region());
+ }
+ else {
+ Ref<ImageTexture> tex = p_from;
+ img = tex->get_data();
+ }
+
+ if (img.empty())
+ return Ref<Texture>();
+
+ img.clear_mipmaps();
+
+ int thumbnail_size = EditorSettings::get_singleton()->get("file_dialog/thumbnail_size");
+ thumbnail_size*=EDSCALE;
+ if (img.is_compressed()) {
+ if (img.decompress()!=OK)
+ return Ref<Texture>();
+ } else if (img.get_format()!=Image::FORMAT_RGB && img.get_format()!=Image::FORMAT_RGBA) {
+ img.convert(Image::FORMAT_RGBA);
+ }
+
+ int width,height;
+ if (img.get_width() > thumbnail_size && img.get_width() >= img.get_height()) {
+
+ width=thumbnail_size;
+ height = img.get_height() * thumbnail_size / img.get_width();
+ } else if (img.get_height() > thumbnail_size && img.get_height() >= img.get_width()) {
+
+ height=thumbnail_size;
+ width = img.get_width() * thumbnail_size / img.get_height();
+ } else {
+
+ width=img.get_width();
+ height=img.get_height();
+ }
+
+ img.resize(width,height);
+
+ Ref<ImageTexture> ptex = Ref<ImageTexture>( memnew( ImageTexture ));
+
+ ptex->create_from_image(img,0);
+ return ptex;
+
+}
+
+EditorTexturePreviewPlugin::EditorTexturePreviewPlugin() {
+
+
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+bool EditorBitmapPreviewPlugin::handles(const String& p_type) const {
+
+ return ObjectTypeDB::is_type(p_type,"BitMap");
+}
+
+Ref<Texture> EditorBitmapPreviewPlugin::generate(const RES& p_from) {
+
+ Ref<BitMap> bm =p_from;
+
+ if (bm->get_size()==Size2()) {
+ return Ref<Texture>();
+ }
+
+ DVector<uint8_t> data;
+
+ data.resize(bm->get_size().width*bm->get_size().height);
+
+ {
+ DVector<uint8_t>::Write w=data.write();
+
+ for(int i=0;i<bm->get_size().width;i++) {
+ for(int j=0;j<bm->get_size().height;j++) {
+ if (bm->get_bit(Point2i(i,j))) {
+ w[j*bm->get_size().width+i]=255;
+ } else {
+ w[j*bm->get_size().width+i]=0;
+
+ }
+ }
+
+ }
+ }
+
+
+ Image img(bm->get_size().width,bm->get_size().height,0,Image::FORMAT_GRAYSCALE,data);
+
+ int thumbnail_size = EditorSettings::get_singleton()->get("file_dialog/thumbnail_size");
+ thumbnail_size*=EDSCALE;
+ if (img.is_compressed()) {
+ if (img.decompress()!=OK)
+ return Ref<Texture>();
+ } else if (img.get_format()!=Image::FORMAT_RGB && img.get_format()!=Image::FORMAT_RGBA) {
+ img.convert(Image::FORMAT_RGBA);
+ }
+
+ int width,height;
+ if (img.get_width() > thumbnail_size && img.get_width() >= img.get_height()) {
+
+ width=thumbnail_size;
+ height = img.get_height() * thumbnail_size / img.get_width();
+ } else if (img.get_height() > thumbnail_size && img.get_height() >= img.get_width()) {
+
+ height=thumbnail_size;
+ width = img.get_width() * thumbnail_size / img.get_height();
+ } else {
+
+ width=img.get_width();
+ height=img.get_height();
+ }
+
+ img.resize(width,height);
+
+ Ref<ImageTexture> ptex = Ref<ImageTexture>( memnew( ImageTexture ));
+
+ ptex->create_from_image(img,0);
+ return ptex;
+
+}
+
+EditorBitmapPreviewPlugin::EditorBitmapPreviewPlugin() {
+
+
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+
+Ref<Texture> EditorPackedScenePreviewPlugin::_gen_from_imd(Ref<ResourceImportMetadata> p_imd) {
+
+ if (p_imd.is_null()) {
+ return Ref<Texture>();
+ }
+
+ if (!p_imd->has_option("thumbnail"))
+ return Ref<Texture>();
+
+ Variant tn = p_imd->get_option("thumbnail");
+ //print_line(Variant::get_type_name(tn.get_type()));
+ DVector<uint8_t> thumbnail = tn;
+
+ int len = thumbnail.size();
+ if (len==0)
+ return Ref<Texture>();
+
+
+ DVector<uint8_t>::Read r = thumbnail.read();
+
+ Image img(r.ptr(),len);
+ if (img.empty())
+ return Ref<Texture>();
+
+ Ref<ImageTexture> ptex = Ref<ImageTexture>( memnew( ImageTexture ));
+ ptex->create_from_image(img,0);
+ return ptex;
+
+}
+
+bool EditorPackedScenePreviewPlugin::handles(const String& p_type) const {
+
+ return ObjectTypeDB::is_type(p_type,"PackedScene");
+}
+Ref<Texture> EditorPackedScenePreviewPlugin::generate(const RES& p_from) {
+
+ Ref<ResourceImportMetadata> imd = p_from->get_import_metadata();
+ return _gen_from_imd(imd);
+}
+
+Ref<Texture> EditorPackedScenePreviewPlugin::generate_from_path(const String& p_path) {
+
+ Ref<ResourceImportMetadata> imd = ResourceLoader::load_import_metadata(p_path);
+ return _gen_from_imd(imd);
+}
+
+EditorPackedScenePreviewPlugin::EditorPackedScenePreviewPlugin() {
+
+}
+
+//////////////////////////////////////////////////////////////////
+
+bool EditorMaterialPreviewPlugin::handles(const String& p_type) const {
+
+ return ObjectTypeDB::is_type(p_type,"Material"); //any material
+}
+
+Ref<Texture> EditorMaterialPreviewPlugin::generate(const RES& p_from) {
+
+ Ref<Material> material = p_from;
+ ERR_FAIL_COND_V(material.is_null(),Ref<Texture>());
+
+ VS::get_singleton()->mesh_surface_set_material(sphere,0,material->get_rid());
+
+ VS::get_singleton()->viewport_queue_screen_capture(viewport);
+ VS::get_singleton()->viewport_set_render_target_update_mode(viewport,VS::RENDER_TARGET_UPDATE_ONCE); //once used for capture
+// print_line("queue capture!");
+ Image img;
+
+ int timeout=1000;
+ while(timeout) {
+ //print_line("try capture?");
+ OS::get_singleton()->delay_usec(10);
+ img = VS::get_singleton()->viewport_get_screen_capture(viewport);
+ if (!img.empty())
+ break;
+ timeout--;
+ }
+
+ //print_line("captured!");
+ VS::get_singleton()->mesh_surface_set_material(sphere,0,RID());
+
+ int thumbnail_size = EditorSettings::get_singleton()->get("file_dialog/thumbnail_size");
+ thumbnail_size*=EDSCALE;
+ img.resize(thumbnail_size,thumbnail_size);
+
+ Ref<ImageTexture> ptex = Ref<ImageTexture>( memnew( ImageTexture ));
+ ptex->create_from_image(img,0);
+ return ptex;
+}
+
+EditorMaterialPreviewPlugin::EditorMaterialPreviewPlugin() {
+
+ scenario = VS::get_singleton()->scenario_create();
+
+ viewport = VS::get_singleton()->viewport_create();
+ VS::get_singleton()->viewport_set_as_render_target(viewport,true);
+ VS::get_singleton()->viewport_set_render_target_update_mode(viewport,VS::RENDER_TARGET_UPDATE_DISABLED);
+ VS::get_singleton()->viewport_set_scenario(viewport,scenario);
+ VS::ViewportRect vr;
+ vr.x=0;
+ vr.y=0;
+ vr.width=128;
+ vr.height=128;
+ VS::get_singleton()->viewport_set_rect(viewport,vr);
+
+ camera = VS::get_singleton()->camera_create();
+ VS::get_singleton()->viewport_attach_camera(viewport,camera);
+ VS::get_singleton()->camera_set_transform(camera,Transform(Matrix3(),Vector3(0,0,3)));
+ VS::get_singleton()->camera_set_perspective(camera,45,0.1,10);
+
+ light = VS::get_singleton()->light_create(VS::LIGHT_DIRECTIONAL);
+ light_instance = VS::get_singleton()->instance_create2(light,scenario);
+ VS::get_singleton()->instance_set_transform(light_instance,Transform().looking_at(Vector3(-1,-1,-1),Vector3(0,1,0)));
+
+ light2 = VS::get_singleton()->light_create(VS::LIGHT_DIRECTIONAL);
+ VS::get_singleton()->light_set_color(light2,VS::LIGHT_COLOR_DIFFUSE,Color(0.7,0.7,0.7));
+ VS::get_singleton()->light_set_color(light2,VS::LIGHT_COLOR_SPECULAR,Color(0.0,0.0,0.0));
+ light_instance2 = VS::get_singleton()->instance_create2(light2,scenario);
+
+ VS::get_singleton()->instance_set_transform(light_instance2,Transform().looking_at(Vector3(0,1,0),Vector3(0,0,1)));
+
+ sphere = VS::get_singleton()->mesh_create();
+ sphere_instance = VS::get_singleton()->instance_create2(sphere,scenario);
+
+ int lats=32;
+ int lons=32;
+ float radius=1.0;
+
+ DVector<Vector3> vertices;
+ DVector<Vector3> normals;
+ DVector<Vector2> uvs;
+ DVector<float> tangents;
+ Matrix3 tt = Matrix3(Vector3(0,1,0),Math_PI*0.5);
+
+ for(int i = 1; i <= lats; i++) {
+ double lat0 = Math_PI * (-0.5 + (double) (i - 1) / lats);
+ double z0 = Math::sin(lat0);
+ double zr0 = Math::cos(lat0);
+
+ double lat1 = Math_PI * (-0.5 + (double) i / lats);
+ double z1 = Math::sin(lat1);
+ double zr1 = Math::cos(lat1);
+
+ for(int j = lons; j >= 1; j--) {
+
+ double lng0 = 2 * Math_PI * (double) (j - 1) / lons;
+ double x0 = Math::cos(lng0);
+ double y0 = Math::sin(lng0);
+
+ double lng1 = 2 * Math_PI * (double) (j) / lons;
+ double x1 = Math::cos(lng1);
+ double y1 = Math::sin(lng1);
+
+
+ Vector3 v[4]={
+ Vector3(x1 * zr0, z0, y1 *zr0),
+ Vector3(x1 * zr1, z1, y1 *zr1),
+ Vector3(x0 * zr1, z1, y0 *zr1),
+ Vector3(x0 * zr0, z0, y0 *zr0)
+ };
+
+#define ADD_POINT(m_idx) \
+ normals.push_back(v[m_idx]);\
+ vertices.push_back(v[m_idx]*radius);\
+ { Vector2 uv(Math::atan2(v[m_idx].x,v[m_idx].z),Math::atan2(-v[m_idx].y,v[m_idx].z));\
+ uv/=Math_PI;\
+ uv*=4.0;\
+ uv=uv*0.5+Vector2(0.5,0.5);\
+ uvs.push_back(uv);\
+ }\
+ { Vector3 t = tt.xform(v[m_idx]);\
+ tangents.push_back(t.x);\
+ tangents.push_back(t.y);\
+ tangents.push_back(t.z);\
+ tangents.push_back(1.0);\
+ }
+
+
+
+ ADD_POINT(0);
+ ADD_POINT(1);
+ ADD_POINT(2);
+
+ ADD_POINT(2);
+ ADD_POINT(3);
+ ADD_POINT(0);
+ }
+ }
+
+ Array arr;
+ arr.resize(VS::ARRAY_MAX);
+ arr[VS::ARRAY_VERTEX]=vertices;
+ arr[VS::ARRAY_NORMAL]=normals;
+ arr[VS::ARRAY_TANGENT]=tangents;
+ arr[VS::ARRAY_TEX_UV]=uvs;
+ VS::get_singleton()->mesh_add_surface(sphere,VS::PRIMITIVE_TRIANGLES,arr);
+
+}
+
+EditorMaterialPreviewPlugin::~EditorMaterialPreviewPlugin() {
+
+ VS::get_singleton()->free(sphere);
+ VS::get_singleton()->free(sphere_instance);
+ VS::get_singleton()->free(viewport);
+ VS::get_singleton()->free(light);
+ VS::get_singleton()->free(light_instance);
+ VS::get_singleton()->free(light2);
+ VS::get_singleton()->free(light_instance2);
+ VS::get_singleton()->free(camera);
+ VS::get_singleton()->free(scenario);
+
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+static bool _is_text_char(CharType c) {
+
+ return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_';
+}
+
+bool EditorScriptPreviewPlugin::handles(const String& p_type) const {
+
+ return ObjectTypeDB::is_type(p_type,"Script");
+}
+
+Ref<Texture> EditorScriptPreviewPlugin::generate(const RES& p_from) {
+
+
+ Ref<Script> scr = p_from;
+ if (scr.is_null())
+ return Ref<Texture>();
+
+ String code = scr->get_source_code().strip_edges();
+ if (code=="")
+ return Ref<Texture>();
+
+ List<String> kwors;
+ scr->get_language()->get_reserved_words(&kwors);
+
+ Set<String> keywords;
+
+ for(List<String>::Element *E=kwors.front();E;E=E->next()) {
+
+ keywords.insert(E->get());
+
+ }
+
+
+ int line = 0;
+ int col=0;
+ int thumbnail_size = EditorSettings::get_singleton()->get("file_dialog/thumbnail_size");
+ thumbnail_size*=EDSCALE;
+ Image img(thumbnail_size,thumbnail_size,0,Image::FORMAT_RGBA);
+
+
+
+ Color bg_color = EditorSettings::get_singleton()->get("text_editor/background_color");
+ bg_color.a=1.0;
+ Color keyword_color = EditorSettings::get_singleton()->get("text_editor/keyword_color");
+ Color text_color = EditorSettings::get_singleton()->get("text_editor/text_color");
+ Color symbol_color = EditorSettings::get_singleton()->get("text_editor/symbol_color");
+
+
+ for(int i=0;i<thumbnail_size;i++) {
+ for(int j=0;j<thumbnail_size;j++) {
+ img.put_pixel(i,j,bg_color);
+ }
+
+ }
+
+ bool prev_is_text=false;
+ bool in_keyword=false;
+ for(int i=0;i<code.length();i++) {
+
+ CharType c = code[i];
+ if (c>32) {
+ if (col<thumbnail_size) {
+ Color color = text_color;
+
+ if (c!='_' && ((c>='!' && c<='/') || (c>=':' && c<='@') || (c>='[' && c<='`') || (c>='{' && c<='~') || c=='\t')) {
+ //make symbol a little visible
+ color=symbol_color;
+ in_keyword=false;
+ } else if (!prev_is_text && _is_text_char(c)) {
+ int pos = i;
+
+ while(_is_text_char(code[pos])) {
+ pos++;
+ }
+ ///print_line("from "+itos(i)+" to "+itos(pos));
+ String word = code.substr(i,pos-i);
+ //print_line("found word: "+word);
+ if (keywords.has(word))
+ in_keyword=true;
+
+ } else if (!_is_text_char(c)) {
+ in_keyword=false;
+ }
+
+ if (in_keyword)
+ color=keyword_color;
+
+ Color ul=color;
+ ul.a*=0.5;
+ img.put_pixel(col,line*2,bg_color.blend(ul));
+ img.put_pixel(col,line*2+1,color);
+
+ prev_is_text=_is_text_char(c);
+ }
+ } else {
+
+ prev_is_text=false;
+ in_keyword=false;
+
+ if (c=='\n') {
+ col=0;
+ line++;
+ if (line>=thumbnail_size/2)
+ break;
+ } else if (c=='\t') {
+ col+=3;
+ }
+ }
+ col++;
+ }
+
+ Ref<ImageTexture> ptex = Ref<ImageTexture>( memnew( ImageTexture));
+
+ ptex->create_from_image(img,0);
+ return ptex;
+
+}
+
+EditorScriptPreviewPlugin::EditorScriptPreviewPlugin() {
+
+
+}
+///////////////////////////////////////////////////////////////////
+
+bool EditorSamplePreviewPlugin::handles(const String& p_type) const {
+
+ return ObjectTypeDB::is_type(p_type,"Sample");
+}
+
+Ref<Texture> EditorSamplePreviewPlugin::generate(const RES& p_from) {
+
+ Ref<Sample> smp =p_from;
+ ERR_FAIL_COND_V(smp.is_null(),Ref<Texture>());
+
+
+ int thumbnail_size = EditorSettings::get_singleton()->get("file_dialog/thumbnail_size");
+ thumbnail_size*=EDSCALE;
+ DVector<uint8_t> img;
+ int w = thumbnail_size;
+ int h = thumbnail_size;
+ img.resize(w*h*3);
+
+ DVector<uint8_t>::Write imgdata = img.write();
+ uint8_t * imgw = imgdata.ptr();
+ DVector<uint8_t> data = smp->get_data();
+ DVector<uint8_t>::Read sampledata = data.read();
+ const uint8_t *sdata=sampledata.ptr();
+
+ bool stereo = smp->is_stereo();
+ bool _16=smp->get_format()==Sample::FORMAT_PCM16;
+ int len = smp->get_length();
+
+ if (len<1)
+ return Ref<Texture>();
+
+ if (smp->get_format()==Sample::FORMAT_IMA_ADPCM) {
+
+ struct IMA_ADPCM_State {
+
+ int16_t step_index;
+ int32_t predictor;
+ /* values at loop point */
+ int16_t loop_step_index;
+ int32_t loop_predictor;
+ int32_t last_nibble;
+ int32_t loop_pos;
+ int32_t window_ofs;
+ const uint8_t *ptr;
+ } ima_adpcm;
+
+ ima_adpcm.step_index=0;
+ ima_adpcm.predictor=0;
+ ima_adpcm.loop_step_index=0;
+ ima_adpcm.loop_predictor=0;
+ ima_adpcm.last_nibble=-1;
+ ima_adpcm.loop_pos=0x7FFFFFFF;
+ ima_adpcm.window_ofs=0;
+ ima_adpcm.ptr=NULL;
+
+
+ for(int i=0;i<w;i++) {
+
+ float max[2]={-1e10,-1e10};
+ float min[2]={1e10,1e10};
+ int from = i*len/w;
+ int to = (i+1)*len/w;
+ if (to>=len)
+ to=len-1;
+
+ for(int j=from;j<to;j++) {
+
+ while(j>ima_adpcm.last_nibble) {
+
+ static const int16_t _ima_adpcm_step_table[89] = {
+ 7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
+ 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
+ 50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
+ 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
+ 337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
+ 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
+ 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
+ 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
+ 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
+ };
+
+ static const int8_t _ima_adpcm_index_table[16] = {
+ -1, -1, -1, -1, 2, 4, 6, 8,
+ -1, -1, -1, -1, 2, 4, 6, 8
+ };
+
+ int16_t nibble,diff,step;
+
+ ima_adpcm.last_nibble++;
+ const uint8_t *src_ptr=sdata;
+
+ int ofs = ima_adpcm.last_nibble>>1;
+
+ if (stereo)
+ ofs*=2;
+
+
+ nibble = (ima_adpcm.last_nibble&1)?
+ (src_ptr[ofs]>>4):(src_ptr[ofs]&0xF);
+ step=_ima_adpcm_step_table[ima_adpcm.step_index];
+
+ ima_adpcm.step_index += _ima_adpcm_index_table[nibble];
+ if (ima_adpcm.step_index<0)
+ ima_adpcm.step_index=0;
+ if (ima_adpcm.step_index>88)
+ ima_adpcm.step_index=88;
+
+ diff = step >> 3 ;
+ if (nibble & 1)
+ diff += step >> 2 ;
+ if (nibble & 2)
+ diff += step >> 1 ;
+ if (nibble & 4)
+ diff += step ;
+ if (nibble & 8)
+ diff = -diff ;
+
+ ima_adpcm.predictor+=diff;
+ if (ima_adpcm.predictor<-0x8000)
+ ima_adpcm.predictor=-0x8000;
+ else if (ima_adpcm.predictor>0x7FFF)
+ ima_adpcm.predictor=0x7FFF;
+
+
+ /* store loop if there */
+ if (ima_adpcm.last_nibble==ima_adpcm.loop_pos) {
+
+ ima_adpcm.loop_step_index = ima_adpcm.step_index;
+ ima_adpcm.loop_predictor = ima_adpcm.predictor;
+ }
+
+ }
+
+ float v=ima_adpcm.predictor/32767.0;
+ if (v>max[0])
+ max[0]=v;
+ if (v<min[0])
+ min[0]=v;
+ }
+ max[0]*=0.8;
+ min[0]*=0.8;
+
+ for(int j=0;j<h;j++) {
+ float v = (j/(float)h) * 2.0 - 1.0;
+ uint8_t* imgofs = &imgw[(uint64_t(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 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 = uint64_t(i)*len/w;
+ int to = (uint64_t(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[uint64_t(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[uint64_t(k)*c+j]/128.0;
+ if (v>max[j])
+ max[j]=v;
+ if (v<min[j])
+ min[j]=v;
+ }
+
+ }
+ }
+
+ max[0]*=0.8;
+ max[1]*=0.8;
+ min[0]*=0.8;
+ min[1]*=0.8;
+
+ 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;
+ float v;
+ if (j<(h/2)) {
+ half=0;
+ v = (j/(float)(h/2)) * 2.0 - 1.0;
+ } else {
+ half=1;
+ if( (float)(h/2) != 0 ) {
+ v = ((j-(h/2))/(float)(h/2)) * 2.0 - 1.0;
+ } else {
+ v = ((j-(h/2))/(float)(1/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();
+
+ Ref<ImageTexture> ptex = Ref<ImageTexture>( memnew( ImageTexture));
+ ptex->create_from_image(Image(w,h,0,Image::FORMAT_RGB,img),0);
+ return ptex;
+
+}
+
+EditorSamplePreviewPlugin::EditorSamplePreviewPlugin() {
+
+
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+bool EditorMeshPreviewPlugin::handles(const String& p_type) const {
+
+ return ObjectTypeDB::is_type(p_type,"Mesh"); //any Mesh
+}
+
+Ref<Texture> EditorMeshPreviewPlugin::generate(const RES& p_from) {
+
+ Ref<Mesh> mesh = p_from;
+ ERR_FAIL_COND_V(mesh.is_null(),Ref<Texture>());
+
+ VS::get_singleton()->instance_set_base(mesh_instance,mesh->get_rid());
+
+ AABB aabb= mesh->get_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.125);
+ xform.basis = Matrix3().rotated(Vector3(1,0,0),-Math_PI*0.125)*xform.basis;
+ AABB rot_aabb = xform.xform(aabb);
+ float m = MAX(rot_aabb.size.x,rot_aabb.size.y)*0.5;
+ if (m==0)
+ return Ref<Texture>();
+ 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;
+ VS::get_singleton()->instance_set_transform(mesh_instance,xform);
+
+
+
+ VS::get_singleton()->viewport_queue_screen_capture(viewport);
+ VS::get_singleton()->viewport_set_render_target_update_mode(viewport,VS::RENDER_TARGET_UPDATE_ONCE); //once used for capture
+// print_line("queue capture!");
+ Image img;
+
+ int timeout=1000;
+ while(timeout) {
+ //print_line("try capture?");
+ OS::get_singleton()->delay_usec(10);
+ img = VS::get_singleton()->viewport_get_screen_capture(viewport);
+ if (!img.empty())
+ break;
+ timeout--;
+ }
+
+ //print_line("captured!");
+ VS::get_singleton()->instance_set_base(mesh_instance,RID());
+
+ int thumbnail_size = EditorSettings::get_singleton()->get("file_dialog/thumbnail_size");
+ thumbnail_size*=EDSCALE;
+ img.resize(thumbnail_size,thumbnail_size);
+
+ Ref<ImageTexture> ptex = Ref<ImageTexture>( memnew( ImageTexture ));
+ ptex->create_from_image(img,0);
+ return ptex;
+}
+
+EditorMeshPreviewPlugin::EditorMeshPreviewPlugin() {
+
+ scenario = VS::get_singleton()->scenario_create();
+ viewport = VS::get_singleton()->viewport_create();
+ VS::get_singleton()->viewport_set_as_render_target(viewport,true);
+ VS::get_singleton()->viewport_set_render_target_update_mode(viewport,VS::RENDER_TARGET_UPDATE_DISABLED);
+ VS::get_singleton()->viewport_set_scenario(viewport,scenario);
+ VS::ViewportRect vr;
+ vr.x=0;
+ vr.y=0;
+ vr.width=128;
+ vr.height=128;
+ VS::get_singleton()->viewport_set_rect(viewport,vr);
+
+ camera = VS::get_singleton()->camera_create();
+ VS::get_singleton()->viewport_attach_camera(viewport,camera);
+ VS::get_singleton()->camera_set_transform(camera,Transform(Matrix3(),Vector3(0,0,3)));
+// VS::get_singleton()->camera_set_perspective(camera,45,0.1,10);
+ VS::get_singleton()->camera_set_orthogonal(camera,1.0,0.01,1000.0);
+
+ light = VS::get_singleton()->light_create(VS::LIGHT_DIRECTIONAL);
+ light_instance = VS::get_singleton()->instance_create2(light,scenario);
+ VS::get_singleton()->instance_set_transform(light_instance,Transform().looking_at(Vector3(-1,-1,-1),Vector3(0,1,0)));
+
+ light2 = VS::get_singleton()->light_create(VS::LIGHT_DIRECTIONAL);
+ VS::get_singleton()->light_set_color(light2,VS::LIGHT_COLOR_DIFFUSE,Color(0.7,0.7,0.7));
+ VS::get_singleton()->light_set_color(light2,VS::LIGHT_COLOR_SPECULAR,Color(0.0,0.0,0.0));
+ light_instance2 = VS::get_singleton()->instance_create2(light2,scenario);
+
+ VS::get_singleton()->instance_set_transform(light_instance2,Transform().looking_at(Vector3(0,1,0),Vector3(0,0,1)));
+
+// sphere = VS::get_singleton()->mesh_create();
+ mesh_instance = VS::get_singleton()->instance_create();
+ VS::get_singleton()->instance_set_scenario(mesh_instance,scenario);
+
+
+
+}
+
+EditorMeshPreviewPlugin::~EditorMeshPreviewPlugin() {
+
+ //VS::get_singleton()->free(sphere);
+ VS::get_singleton()->free(mesh_instance);
+ VS::get_singleton()->free(viewport);
+ VS::get_singleton()->free(light);
+ VS::get_singleton()->free(light_instance);
+ VS::get_singleton()->free(light2);
+ VS::get_singleton()->free(light_instance2);
+ VS::get_singleton()->free(camera);
+ VS::get_singleton()->free(scenario);
+
+}
diff --git a/editor/plugins/editor_preview_plugins.h b/editor/plugins/editor_preview_plugins.h
new file mode 100644
index 000000000..2bfd0f674
--- /dev/null
+++ b/editor/plugins/editor_preview_plugins.h
@@ -0,0 +1,127 @@
+/*************************************************************************/
+/* editor_preview_plugins.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 EDITORPREVIEWPLUGINS_H
+#define EDITORPREVIEWPLUGINS_H
+
+#include "editor/editor_resource_preview.h"
+
+class EditorTexturePreviewPlugin : public EditorResourcePreviewGenerator {
+public:
+
+ virtual bool handles(const String& p_type) const;
+ virtual Ref<Texture> generate(const RES& p_from);
+
+ EditorTexturePreviewPlugin();
+};
+
+
+class EditorBitmapPreviewPlugin : public EditorResourcePreviewGenerator {
+public:
+
+ virtual bool handles(const String& p_type) const;
+ virtual Ref<Texture> generate(const RES& p_from);
+
+ EditorBitmapPreviewPlugin();
+};
+
+
+
+class EditorPackedScenePreviewPlugin : public EditorResourcePreviewGenerator {
+
+ Ref<Texture> _gen_from_imd(Ref<ResourceImportMetadata> p_imd);
+public:
+
+ virtual bool handles(const String& p_type) const;
+ virtual Ref<Texture> generate(const RES& p_from);
+ virtual Ref<Texture> generate_from_path(const String& p_path);
+
+ EditorPackedScenePreviewPlugin();
+};
+
+class EditorMaterialPreviewPlugin : public EditorResourcePreviewGenerator {
+
+ RID scenario;
+ RID sphere;
+ RID sphere_instance;
+ RID viewport;
+ RID light;
+ RID light_instance;
+ RID light2;
+ RID light_instance2;
+ RID camera;
+public:
+
+ virtual bool handles(const String& p_type) const;
+ virtual Ref<Texture> generate(const RES& p_from);
+
+ EditorMaterialPreviewPlugin();
+ ~EditorMaterialPreviewPlugin();
+};
+
+class EditorScriptPreviewPlugin : public EditorResourcePreviewGenerator {
+public:
+
+ virtual bool handles(const String& p_type) const;
+ virtual Ref<Texture> generate(const RES& p_from);
+
+ EditorScriptPreviewPlugin();
+};
+
+
+class EditorSamplePreviewPlugin : public EditorResourcePreviewGenerator {
+public:
+
+ virtual bool handles(const String& p_type) const;
+ virtual Ref<Texture> generate(const RES& p_from);
+
+ EditorSamplePreviewPlugin();
+};
+
+
+class EditorMeshPreviewPlugin : public EditorResourcePreviewGenerator {
+
+ RID scenario;
+ RID mesh_instance;
+ RID viewport;
+ RID light;
+ RID light_instance;
+ RID light2;
+ RID light_instance2;
+ RID camera;
+public:
+
+ virtual bool handles(const String& p_type) const;
+ virtual Ref<Texture> generate(const RES& p_from);
+
+ EditorMeshPreviewPlugin();
+ ~EditorMeshPreviewPlugin();
+};
+
+
+#endif // EDITORPREVIEWPLUGINS_H
diff --git a/editor/plugins/item_list_editor_plugin.cpp b/editor/plugins/item_list_editor_plugin.cpp
new file mode 100644
index 000000000..ea29e9ef0
--- /dev/null
+++ b/editor/plugins/item_list_editor_plugin.cpp
@@ -0,0 +1,381 @@
+/*************************************************************************/
+/* item_list_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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=="id")
+ set_item_id(idx,p_value);
+ else if (what=="enabled")
+ set_item_enabled(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=="id")
+ r_ret=get_item_id(idx);
+ else if (what=="enabled")
+ r_ret=is_item_enabled(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") );
+
+ int flags = get_flags();
+
+ if (flags&FLAG_CHECKABLE) {
+ p_list->push_back( PropertyInfo(Variant::BOOL,base+"checkable") );
+ p_list->push_back( PropertyInfo(Variant::BOOL,base+"checked") );
+ }
+
+ if (flags&FLAG_ID)
+ p_list->push_back( PropertyInfo(Variant::INT,base+"id",PROPERTY_HINT_RANGE,"-1,4096") );
+
+ if (flags&FLAG_ENABLE)
+ p_list->push_back( PropertyInfo(Variant::BOOL,base+"enabled") );
+
+ if (flags&FLAG_SEPARATOR)
+ p_list->push_back( PropertyInfo(Variant::BOOL,base+"separator") );
+ }
+}
+
+///////////////////////////////////////////////////////////////
+///////////////////////// PLUGINS /////////////////////////////
+///////////////////////////////////////////////////////////////
+
+void ItemListOptionButtonPlugin::set_object(Object *p_object) {
+
+ ob = p_object->cast_to<OptionButton>();
+}
+
+bool ItemListOptionButtonPlugin::handles(Object *p_object) const {
+
+ return p_object->is_type("OptionButton");
+}
+
+int ItemListOptionButtonPlugin::get_flags() const {
+
+ return FLAG_ICON|FLAG_ID|FLAG_ENABLE;
+}
+
+void ItemListOptionButtonPlugin::add_item() {
+
+ ob->add_item( vformat(TTR("Item %d"),ob->get_item_count()));
+ _change_notify();
+}
+
+int ItemListOptionButtonPlugin::get_item_count() const {
+
+ return ob->get_item_count();
+}
+
+void ItemListOptionButtonPlugin::erase(int p_idx) {
+
+ ob->remove_item(p_idx);
+ _change_notify();
+}
+
+ItemListOptionButtonPlugin::ItemListOptionButtonPlugin() {
+
+ ob=NULL;
+}
+
+///////////////////////////////////////////////////////////////
+
+void ItemListPopupMenuPlugin::set_object(Object *p_object) {
+
+ if (p_object->is_type("MenuButton"))
+ pp = p_object->cast_to<MenuButton>()->get_popup();
+ else
+ pp = p_object->cast_to<PopupMenu>();
+}
+
+bool ItemListPopupMenuPlugin::handles(Object *p_object) const {
+
+ return p_object->is_type("PopupMenu") || p_object->is_type("MenuButton");
+}
+
+int ItemListPopupMenuPlugin::get_flags() const {
+
+ return FLAG_ICON|FLAG_CHECKABLE|FLAG_ID|FLAG_ENABLE|FLAG_SEPARATOR;
+}
+
+void ItemListPopupMenuPlugin::add_item() {
+
+ pp->add_item( vformat(TTR("Item %d"),pp->get_item_count()));
+ _change_notify();
+}
+
+int ItemListPopupMenuPlugin::get_item_count() const {
+
+ return pp->get_item_count();
+}
+
+void ItemListPopupMenuPlugin::erase(int p_idx) {
+
+ pp->remove_item(p_idx);
+ _change_notify();
+}
+
+ItemListPopupMenuPlugin::ItemListPopupMenuPlugin() {
+
+ pp=NULL;
+}
+
+///////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////
+
+void ItemListEditor::_node_removed(Node *p_node) {
+
+ if(p_node==item_list) {
+ item_list=NULL;
+ hide();
+ dialog->hide();
+ }
+}
+
+void ItemListEditor::_notification(int p_notification) {
+
+ if (p_notification==NOTIFICATION_ENTER_TREE) {
+
+ add_button->set_icon(get_icon("Add","EditorIcons"));
+ del_button->set_icon(get_icon("Remove","EditorIcons"));
+ }
+}
+
+void ItemListEditor::_add_pressed() {
+
+ if (selected_idx==-1)
+ return;
+
+ item_plugins[selected_idx]->add_item();
+}
+
+void ItemListEditor::_delete_pressed() {
+
+ TreeItem *ti = tree->get_selected();
+
+ if (!ti)
+ return;
+
+ if (ti->get_parent()!=tree->get_root())
+ return;
+
+ int idx = ti->get_text(0).to_int();
+
+ if (selected_idx==-1)
+ return;
+
+ item_plugins[selected_idx]->erase(idx);
+}
+
+void ItemListEditor::_edit_items() {
+
+ dialog->popup_centered(Vector2(300, 400));
+}
+
+void ItemListEditor::edit(Node *p_item_list) {
+
+ item_list=p_item_list;
+
+ if (!item_list) {
+ selected_idx=-1;
+ property_editor->edit(NULL);
+ return;
+ }
+
+ 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);
+ property_editor->edit(item_plugins[i]);
+
+ if (has_icon(item_list->get_type(), "EditorIcons"))
+ toolbar_button->set_icon(get_icon(item_list->get_type(), "EditorIcons"));
+ else
+ toolbar_button->set_icon(Ref<Texture>());
+
+ selected_idx=i;
+ return;
+ }
+ }
+
+ selected_idx=-1;
+ property_editor->edit(NULL);
+}
+
+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;
+}
+
+void ItemListEditor::_bind_methods() {
+
+ ObjectTypeDB::bind_method("_edit_items",&ItemListEditor::_edit_items);
+ ObjectTypeDB::bind_method("_add_button",&ItemListEditor::_add_pressed);
+ ObjectTypeDB::bind_method("_delete_button",&ItemListEditor::_delete_pressed);
+}
+
+ItemListEditor::ItemListEditor() {
+
+ selected_idx=-1;
+
+ add_child( memnew( VSeparator ) );
+
+ toolbar_button = memnew( ToolButton );
+ toolbar_button->set_text(TTR("Items"));
+ add_child(toolbar_button);
+ toolbar_button->connect("pressed",this,"_edit_items");
+
+ dialog = memnew( AcceptDialog );
+ dialog->set_title(TTR("Item List Editor"));
+ add_child( dialog );
+
+ VBoxContainer *vbc = memnew( VBoxContainer );
+ dialog->add_child(vbc);
+ dialog->set_child_rect(vbc);
+
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->set_h_size_flags(SIZE_EXPAND_FILL);
+ vbc->add_child(hbc);
+
+ add_button = memnew( Button );
+ add_button->set_text(TTR("Add"));
+ hbc->add_child(add_button);
+ add_button->connect("pressed",this,"_add_button");
+
+ hbc->add_spacer();
+
+ del_button = memnew( Button );
+ del_button->set_text(TTR("Delete"));
+ hbc->add_child(del_button);
+ del_button->connect("pressed",this,"_delete_button");
+
+ property_editor = memnew( PropertyEditor );
+ property_editor->hide_top_label();
+ property_editor->set_subsection_selectable(true);
+ vbc->add_child(property_editor);
+ property_editor->set_v_size_flags(SIZE_EXPAND_FILL);
+
+ tree = property_editor->get_scene_tree();
+}
+
+ItemListEditor::~ItemListEditor() {
+
+ for(int i=0;i<item_plugins.size();i++)
+ memdelete( item_plugins[i] );
+}
+
+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);
+ }
+}
+
+ItemListEditorPlugin::ItemListEditorPlugin(EditorNode *p_node) {
+
+ editor=p_node;
+ item_list_editor = memnew( ItemListEditor );
+ CanvasItemEditor::get_singleton()->add_control_to_menu_panel(item_list_editor);
+
+ item_list_editor->hide();
+ item_list_editor->add_plugin( memnew( ItemListOptionButtonPlugin ) );
+ item_list_editor->add_plugin( memnew( ItemListPopupMenuPlugin ) );
+}
+
+ItemListEditorPlugin::~ItemListEditorPlugin()
+{
+}
+
+
diff --git a/editor/plugins/item_list_editor_plugin.h b/editor/plugins/item_list_editor_plugin.h
new file mode 100644
index 000000000..d5f542eee
--- /dev/null
+++ b/editor/plugins/item_list_editor_plugin.h
@@ -0,0 +1,230 @@
+/*************************************************************************/
+/* item_list_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "editor/editor_plugin.h"
+#include "editor/editor_node.h"
+#include "canvas_item_editor_plugin.h"
+
+#include "scene/gui/option_button.h"
+#include "scene/gui/menu_button.h"
+#include "scene/gui/popup_menu.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_ID=4,
+ FLAG_ENABLE=8,
+ FLAG_SEPARATOR=16
+ };
+
+ 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 String get_item_text(int p_idx) const{ return ""; };
+
+ virtual void set_item_icon(int p_idx, const Ref<Texture>& p_tex) {}
+ virtual Ref<Texture> get_item_icon(int p_idx) const{ return Ref<Texture>(); };
+
+ virtual void set_item_checkable(int p_idx, bool p_check) {}
+ virtual bool is_item_checkable(int p_idx) const{ return false; };
+
+ virtual void set_item_checked(int p_idx, bool p_checked) {}
+ virtual bool is_item_checked(int p_idx) const{ return false; };
+
+ virtual void set_item_enabled(int p_idx, int p_enabled) {}
+ virtual bool is_item_enabled(int p_idx) const{ return false; };
+
+ virtual void set_item_id(int p_idx, int p_id) {}
+ virtual int get_item_id(int p_idx) const{ return -1; };
+
+ virtual void set_item_separator(int p_idx, bool p_separator) {}
+ 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 ItemListOptionButtonPlugin : public ItemListPlugin {
+
+ OBJ_TYPE(ItemListOptionButtonPlugin,ItemListPlugin);
+
+ OptionButton *ob;
+public:
+
+ virtual void set_object(Object *p_object);
+ virtual bool handles(Object *p_object) const;
+ virtual int get_flags() const;
+
+ virtual void set_item_text(int p_idx, const String& p_text) { ob->set_item_text(p_idx,p_text); }
+ virtual String get_item_text(int p_idx) const { return ob->get_item_text(p_idx); }
+
+ virtual void set_item_icon(int p_idx, const Ref<Texture>& p_tex) { ob->set_item_icon(p_idx, p_tex); }
+ virtual Ref<Texture> get_item_icon(int p_idx) const { return ob->get_item_icon(p_idx); }
+
+ virtual void set_item_enabled(int p_idx, int p_enabled) { ob->set_item_disabled(p_idx, !p_enabled); }
+ virtual bool is_item_enabled(int p_idx) const { return !ob->is_item_disabled(p_idx); }
+
+ virtual void set_item_id(int p_idx, int p_id) { ob->set_item_ID(p_idx,p_id); }
+ virtual int get_item_id(int p_idx) const { return ob->get_item_ID(p_idx); }
+
+ virtual void add_item();
+ virtual int get_item_count() const;
+ virtual void erase(int p_idx);
+
+ ItemListOptionButtonPlugin();
+};
+
+class ItemListPopupMenuPlugin : public ItemListPlugin {
+
+ OBJ_TYPE(ItemListPopupMenuPlugin,ItemListPlugin);
+
+ PopupMenu *pp;
+public:
+
+ virtual void set_object(Object *p_object);
+ virtual bool handles(Object *p_object) const;
+ virtual int get_flags() const;
+
+ virtual void set_item_text(int p_idx, const String& p_text) { pp->set_item_text(p_idx,p_text); }
+ virtual String get_item_text(int p_idx) const { return pp->get_item_text(p_idx); }
+
+ virtual void set_item_icon(int p_idx, const Ref<Texture>& p_tex) { pp->set_item_icon(p_idx,p_tex); }
+ virtual Ref<Texture> get_item_icon(int p_idx) const { return pp->get_item_icon(p_idx); }
+
+ virtual void set_item_checkable(int p_idx, bool p_check) { pp->set_item_as_checkable(p_idx,p_check); }
+ virtual bool is_item_checkable(int p_idx) const { return pp->is_item_checkable(p_idx); }
+
+ virtual void set_item_checked(int p_idx, bool p_checked) { pp->set_item_checked(p_idx,p_checked); }
+ virtual bool is_item_checked(int p_idx) const { return pp->is_item_checked(p_idx); }
+
+ virtual void set_item_enabled(int p_idx, int p_enabled) { pp->set_item_disabled(p_idx,!p_enabled); }
+ virtual bool is_item_enabled(int p_idx) const { return !pp->is_item_disabled(p_idx); }
+
+ virtual void set_item_id(int p_idx, int p_id) { pp->set_item_ID(p_idx,p_idx); }
+ virtual int get_item_id(int p_idx) const { return pp->get_item_ID(p_idx); }
+
+ virtual void set_item_separator(int p_idx, bool p_separator) { pp->set_item_as_separator(p_idx,p_separator); }
+ virtual bool is_item_separator(int p_idx) const { return pp->is_item_separator(p_idx); }
+
+ virtual void add_item();
+ virtual int get_item_count() const;
+ virtual void erase(int p_idx);
+
+ ItemListPopupMenuPlugin();
+};
+
+///////////////////////////////////////////////////////////////
+
+class ItemListEditor : public HBoxContainer {
+
+ OBJ_TYPE(ItemListEditor,HBoxContainer);
+
+ Node *item_list;
+
+ ToolButton *toolbar_button;
+
+ AcceptDialog *dialog;
+ PropertyEditor *property_editor;
+ Tree *tree;
+ Button *add_button;
+ Button *del_button;
+
+ int selected_idx;
+
+ Vector<ItemListPlugin*> item_plugins;
+
+ void _edit_items();
+
+ void _add_pressed();
+ void _delete_pressed();
+
+ void _node_removed(Node *p_node);
+
+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/editor/plugins/light_occluder_2d_editor_plugin.cpp b/editor/plugins/light_occluder_2d_editor_plugin.cpp
new file mode 100644
index 000000000..8575533b8
--- /dev/null
+++ b/editor/plugins/light_occluder_2d_editor_plugin.cpp
@@ -0,0 +1,518 @@
+/*************************************************************************/
+/* light_occluder_2d_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "light_occluder_2d_editor_plugin.h"
+
+#include "canvas_item_editor_plugin.h"
+#include "os/file_access.h"
+#include "editor/editor_settings.h"
+
+void LightOccluder2DEditor::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_READY: {
+
+ button_create->set_icon( get_icon("Edit","EditorIcons"));
+ button_edit->set_icon( get_icon("MovePoint","EditorIcons"));
+ button_edit->set_pressed(true);
+ get_tree()->connect("node_removed",this,"_node_removed");
+ create_poly->connect("confirmed",this,"_create_poly");
+
+ } break;
+ case NOTIFICATION_FIXED_PROCESS: {
+
+
+ } break;
+ }
+
+}
+void LightOccluder2DEditor::_node_removed(Node *p_node) {
+
+ if(p_node==node) {
+ node=NULL;
+ hide();
+ canvas_item_editor->get_viewport_control()->update();
+ }
+
+}
+
+
+void LightOccluder2DEditor::_menu_option(int p_option) {
+
+ switch(p_option) {
+
+ case MODE_CREATE: {
+
+ mode=MODE_CREATE;
+ button_create->set_pressed(true);
+ button_edit->set_pressed(false);
+ } break;
+ case MODE_EDIT: {
+
+ mode=MODE_EDIT;
+ button_create->set_pressed(false);
+ button_edit->set_pressed(true);
+ } break;
+
+ }
+}
+
+void LightOccluder2DEditor::_wip_close(bool p_closed) {
+
+ undo_redo->create_action(TTR("Create Poly"));
+ undo_redo->add_undo_method(node->get_occluder_polygon().ptr(),"set_polygon",node->get_occluder_polygon()->get_polygon());
+ undo_redo->add_do_method(node->get_occluder_polygon().ptr(),"set_polygon",wip);
+ undo_redo->add_undo_method(node->get_occluder_polygon().ptr(),"set_closed",node->get_occluder_polygon()->is_closed());
+ undo_redo->add_do_method(node->get_occluder_polygon().ptr(),"set_closed",p_closed);
+
+ undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+ wip.clear();
+ wip_active=false;
+ mode=MODE_EDIT;
+ button_edit->set_pressed(true);
+ button_create->set_pressed(false);
+ edited_point=-1;
+}
+
+bool LightOccluder2DEditor::forward_input_event(const InputEvent& p_event) {
+
+
+ if (!node)
+ return false;
+
+ if (node->get_occluder_polygon().is_null()) {
+ if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1 && p_event.mouse_button.pressed) {
+ create_poly->set_text("No OccluderPolygon2D resource on this node.\nCreate and assign one?");
+ create_poly->popup_centered_minsize();
+ }
+ return (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1);
+ }
+ switch(p_event.type) {
+
+ case InputEvent::MOUSE_BUTTON: {
+
+ const InputEventMouseButton &mb=p_event.mouse_button;
+
+ Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
+
+
+ Vector2 gpoint = Point2(mb.x,mb.y);
+ Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint);
+ cpoint=canvas_item_editor->snap_point(cpoint);
+ cpoint = node->get_global_transform().affine_inverse().xform(cpoint);
+
+ Vector<Vector2> poly = Variant(node->get_occluder_polygon()->get_polygon());
+
+ //first check if a point is to be added (segment split)
+ real_t grab_treshold=EDITOR_DEF("poly_editor/point_grab_radius",8);
+
+ switch(mode) {
+
+
+ case MODE_CREATE: {
+
+ if (mb.button_index==BUTTON_LEFT && mb.pressed) {
+
+
+ if (!wip_active) {
+
+ wip.clear();
+ wip.push_back( cpoint );
+ wip_active=true;
+ edited_point_pos=cpoint;
+ canvas_item_editor->get_viewport_control()->update();
+ edited_point=1;
+ return true;
+ } else {
+
+
+ if (wip.size()>1 && xform.xform(wip[0]).distance_to(gpoint)<grab_treshold) {
+ //wip closed
+ _wip_close(true);
+
+ return true;
+ } else if (wip.size()>1 && xform.xform(wip[wip.size()-1]).distance_to(gpoint)<grab_treshold) {
+ //wip closed
+ _wip_close(false);
+ return true;
+
+ } else {
+
+ wip.push_back( cpoint );
+ edited_point=wip.size();
+ canvas_item_editor->get_viewport_control()->update();
+ return true;
+
+ //add wip point
+ }
+ }
+ } else if (mb.button_index==BUTTON_RIGHT && mb.pressed && wip_active) {
+ _wip_close(true);
+ }
+
+
+
+ } break;
+
+ case MODE_EDIT: {
+
+ if (mb.button_index==BUTTON_LEFT) {
+ if (mb.pressed) {
+
+ if (mb.mod.control) {
+
+
+ if (poly.size() < 3) {
+
+ undo_redo->create_action(TTR("Edit Poly"));
+ undo_redo->add_undo_method(node->get_occluder_polygon().ptr(),"set_polygon",poly);
+ poly.push_back(cpoint);
+ undo_redo->add_do_method(node->get_occluder_polygon().ptr(),"set_polygon",poly);
+ undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+ return true;
+ }
+
+ //search edges
+ int closest_idx=-1;
+ Vector2 closest_pos;
+ real_t closest_dist=1e10;
+ for(int i=0;i<poly.size();i++) {
+
+ Vector2 points[2] ={ xform.xform(poly[i]),
+ xform.xform(poly[(i+1)%poly.size()]) };
+
+ Vector2 cp = Geometry::get_closest_point_to_segment_2d(gpoint,points);
+ if (cp.distance_squared_to(points[0])<CMP_EPSILON2 || cp.distance_squared_to(points[1])<CMP_EPSILON2)
+ continue; //not valid to reuse point
+
+ real_t d = cp.distance_to(gpoint);
+ if (d<closest_dist && d<grab_treshold) {
+ closest_dist=d;
+ closest_pos=cp;
+ closest_idx=i;
+ }
+
+
+ }
+
+ if (closest_idx>=0) {
+
+ pre_move_edit=poly;
+ poly.insert(closest_idx+1,xform.affine_inverse().xform(closest_pos));
+ edited_point=closest_idx+1;
+ edited_point_pos=xform.affine_inverse().xform(closest_pos);
+ node->get_occluder_polygon()->set_polygon(Variant(poly));
+ canvas_item_editor->get_viewport_control()->update();
+ return true;
+ }
+ } else {
+
+ //look for points to move
+
+ int closest_idx=-1;
+ Vector2 closest_pos;
+ real_t closest_dist=1e10;
+ for(int i=0;i<poly.size();i++) {
+
+ Vector2 cp =xform.xform(poly[i]);
+
+ real_t d = cp.distance_to(gpoint);
+ if (d<closest_dist && d<grab_treshold) {
+ closest_dist=d;
+ closest_pos=cp;
+ closest_idx=i;
+ }
+
+ }
+
+ if (closest_idx>=0) {
+
+ pre_move_edit=poly;
+ edited_point=closest_idx;
+ edited_point_pos=xform.affine_inverse().xform(closest_pos);
+ canvas_item_editor->get_viewport_control()->update();
+ return true;
+ }
+ }
+ } else {
+
+ if (edited_point!=-1) {
+
+ //apply
+
+ ERR_FAIL_INDEX_V(edited_point,poly.size(),false);
+ poly[edited_point]=edited_point_pos;
+ undo_redo->create_action(TTR("Edit Poly"));
+ undo_redo->add_do_method(node->get_occluder_polygon().ptr(),"set_polygon",poly);
+ undo_redo->add_undo_method(node->get_occluder_polygon().ptr(),"set_polygon",pre_move_edit);
+ undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+
+ edited_point=-1;
+ return true;
+ }
+ }
+ } if (mb.button_index==BUTTON_RIGHT && mb.pressed && edited_point==-1) {
+
+
+
+ int closest_idx=-1;
+ Vector2 closest_pos;
+ real_t closest_dist=1e10;
+ for(int i=0;i<poly.size();i++) {
+
+ Vector2 cp =xform.xform(poly[i]);
+
+ real_t d = cp.distance_to(gpoint);
+ if (d<closest_dist && d<grab_treshold) {
+ closest_dist=d;
+ closest_pos=cp;
+ closest_idx=i;
+ }
+
+ }
+
+ if (closest_idx>=0) {
+
+
+ undo_redo->create_action(TTR("Edit Poly (Remove Point)"));
+ undo_redo->add_undo_method(node->get_occluder_polygon().ptr(),"set_polygon",poly);
+ poly.remove(closest_idx);
+ undo_redo->add_do_method(node->get_occluder_polygon().ptr(),"set_polygon",poly);
+ undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+ return true;
+ }
+
+ }
+
+
+
+ } break;
+ }
+
+
+
+ } break;
+ case InputEvent::MOUSE_MOTION: {
+
+ const InputEventMouseMotion &mm=p_event.mouse_motion;
+
+ if (edited_point!=-1 && (wip_active || mm.button_mask&BUTTON_MASK_LEFT)) {
+
+ Vector2 gpoint = Point2(mm.x,mm.y);
+ Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint);
+ cpoint=canvas_item_editor->snap_point(cpoint);
+ edited_point_pos = node->get_global_transform().affine_inverse().xform(cpoint);
+
+ canvas_item_editor->get_viewport_control()->update();
+
+ }
+
+ } break;
+ }
+
+ return false;
+}
+void LightOccluder2DEditor::_canvas_draw() {
+
+ if (!node || !node->get_occluder_polygon().is_valid())
+ return;
+
+ Control *vpc = canvas_item_editor->get_viewport_control();
+
+ Vector<Vector2> poly;
+
+ if (wip_active)
+ poly=wip;
+ else
+ poly=Variant(node->get_occluder_polygon()->get_polygon());
+
+
+ Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
+ Ref<Texture> handle= get_icon("EditorHandle","EditorIcons");
+
+ for(int i=0;i<poly.size();i++) {
+
+
+ Vector2 p,p2;
+ p = i==edited_point ? edited_point_pos : poly[i];
+ if ((wip_active && i==poly.size()-1) || (((i+1)%poly.size())==edited_point))
+ p2=edited_point_pos;
+ else
+ p2 = poly[(i+1)%poly.size()];
+
+ Vector2 point = xform.xform(p);
+ Vector2 next_point = xform.xform(p2);
+
+ Color col=Color(1,0.3,0.1,0.8);
+
+ if (i==poly.size()-1 && (!node->get_occluder_polygon()->is_closed() || wip_active)) {
+
+ } else {
+ vpc->draw_line(point,next_point,col,2);
+ }
+ vpc->draw_texture(handle,point-handle->get_size()*0.5);
+ }
+}
+
+
+
+void LightOccluder2DEditor::edit(Node *p_collision_polygon) {
+
+ if (!canvas_item_editor) {
+ canvas_item_editor=CanvasItemEditor::get_singleton();
+ }
+
+ if (p_collision_polygon) {
+
+ node=p_collision_polygon->cast_to<LightOccluder2D>();
+ if (!canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw"))
+ canvas_item_editor->get_viewport_control()->connect("draw",this,"_canvas_draw");
+ wip.clear();
+ wip_active=false;
+ edited_point=-1;
+ canvas_item_editor->get_viewport_control()->update();
+ } else {
+ node=NULL;
+
+ if (canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw"))
+ canvas_item_editor->get_viewport_control()->disconnect("draw",this,"_canvas_draw");
+
+ }
+
+}
+
+void LightOccluder2DEditor::_create_poly() {
+
+ if (!node)
+ return;
+ undo_redo->create_action(TTR("Create Occluder Polygon"));
+ undo_redo->add_do_method(node,"set_occluder_polygon",Ref<OccluderPolygon2D>(memnew( OccluderPolygon2D)));
+ undo_redo->add_undo_method(node,"set_occluder_polygon",Variant(REF()));
+ undo_redo->commit_action();
+}
+
+void LightOccluder2DEditor::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("_menu_option"),&LightOccluder2DEditor::_menu_option);
+ ObjectTypeDB::bind_method(_MD("_canvas_draw"),&LightOccluder2DEditor::_canvas_draw);
+ ObjectTypeDB::bind_method(_MD("_node_removed"),&LightOccluder2DEditor::_node_removed);
+ ObjectTypeDB::bind_method(_MD("_create_poly"),&LightOccluder2DEditor::_create_poly);
+
+}
+
+
+LightOccluder2DEditor::LightOccluder2DEditor(EditorNode *p_editor) {
+
+ node=NULL;
+ canvas_item_editor=NULL;
+ editor=p_editor;
+ undo_redo = editor->get_undo_redo();
+
+ add_child( memnew( VSeparator ));
+ button_create = memnew( ToolButton );
+ add_child(button_create);
+ button_create->connect("pressed",this,"_menu_option",varray(MODE_CREATE));
+ button_create->set_toggle_mode(true);
+ button_create->set_tooltip(TTR("Create a new polygon from scratch."));
+
+ button_edit = memnew( ToolButton );
+ add_child(button_edit);
+ button_edit->connect("pressed",this,"_menu_option",varray(MODE_EDIT));
+ button_edit->set_toggle_mode(true);
+ button_edit->set_tooltip(TTR("Edit existing polygon:")+"\n"+TTR("LMB: Move Point.")+"\n"+TTR("Ctrl+LMB: Split Segment.")+"\n"+TTR("RMB: Erase Point."));
+
+ create_poly = memnew( ConfirmationDialog );
+ add_child(create_poly);
+ create_poly->get_ok()->set_text(TTR("Create"));
+
+
+ //add_constant_override("separation",0);
+
+#if 0
+ options = memnew( MenuButton );
+ add_child(options);
+ options->set_area_as_parent_rect();
+ options->set_text("Polygon");
+ //options->get_popup()->add_item("Parse BBCode",PARSE_BBCODE);
+ options->get_popup()->connect("item_pressed", this,"_menu_option");
+#endif
+
+ mode = MODE_EDIT;
+ wip_active=false;
+
+}
+
+
+void LightOccluder2DEditorPlugin::edit(Object *p_object) {
+
+ collision_polygon_editor->edit(p_object->cast_to<Node>());
+}
+
+bool LightOccluder2DEditorPlugin::handles(Object *p_object) const {
+
+ return p_object->is_type("LightOccluder2D");
+}
+
+void LightOccluder2DEditorPlugin::make_visible(bool p_visible) {
+
+ if (p_visible) {
+ collision_polygon_editor->show();
+ } else {
+
+ collision_polygon_editor->hide();
+ collision_polygon_editor->edit(NULL);
+ }
+
+}
+
+LightOccluder2DEditorPlugin::LightOccluder2DEditorPlugin(EditorNode *p_node) {
+
+ editor=p_node;
+ collision_polygon_editor = memnew( LightOccluder2DEditor(p_node) );
+ CanvasItemEditor::get_singleton()->add_control_to_menu_panel(collision_polygon_editor);
+
+ collision_polygon_editor->hide();
+
+
+
+}
+
+
+LightOccluder2DEditorPlugin::~LightOccluder2DEditorPlugin()
+{
+}
+
diff --git a/editor/plugins/light_occluder_2d_editor_plugin.h b/editor/plugins/light_occluder_2d_editor_plugin.h
new file mode 100644
index 000000000..7bcdf9f8a
--- /dev/null
+++ b/editor/plugins/light_occluder_2d_editor_plugin.h
@@ -0,0 +1,115 @@
+/*************************************************************************/
+/* light_occluder_2d_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 LIGHT_OCCLUDER_2D_EDITOR_PLUGIN_H
+#define LIGHT_OCCLUDER_2D_EDITOR_PLUGIN_H
+
+
+
+#include "editor/editor_plugin.h"
+#include "editor/editor_node.h"
+#include "scene/2d/light_occluder_2d.h"
+#include "scene/gui/tool_button.h"
+#include "scene/gui/button_group.h"
+
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+class CanvasItemEditor;
+
+class LightOccluder2DEditor : public HBoxContainer {
+
+ OBJ_TYPE(LightOccluder2DEditor, HBoxContainer );
+
+ UndoRedo *undo_redo;
+ enum Mode {
+
+ MODE_CREATE,
+ MODE_EDIT,
+
+ };
+
+ Mode mode;
+
+ ToolButton *button_create;
+ ToolButton *button_edit;
+
+ CanvasItemEditor *canvas_item_editor;
+ EditorNode *editor;
+ Panel *panel;
+ LightOccluder2D *node;
+ MenuButton *options;
+
+ int edited_point;
+ Vector2 edited_point_pos;
+ Vector<Vector2> pre_move_edit;
+ Vector<Vector2> wip;
+ bool wip_active;
+
+ ConfirmationDialog *create_poly;
+
+ void _wip_close(bool p_closed);
+ void _canvas_draw();
+ void _menu_option(int p_option);
+ void _create_poly();
+
+protected:
+ void _notification(int p_what);
+ void _node_removed(Node *p_node);
+ static void _bind_methods();
+public:
+
+ Vector2 snap_point(const Vector2& p_point) const;
+ bool forward_input_event(const InputEvent& p_event);
+ void edit(Node *p_collision_polygon);
+ LightOccluder2DEditor(EditorNode *p_editor);
+};
+
+class LightOccluder2DEditorPlugin : public EditorPlugin {
+
+ OBJ_TYPE( LightOccluder2DEditorPlugin, EditorPlugin );
+
+ LightOccluder2DEditor *collision_polygon_editor;
+ EditorNode *editor;
+
+public:
+
+ virtual bool forward_input_event(const InputEvent& p_event) { return collision_polygon_editor->forward_input_event(p_event); }
+
+ virtual String get_name() const { return "LightOccluder2D"; }
+ bool has_main_screen() const { return false; }
+ virtual void edit(Object *p_node);
+ virtual bool handles(Object *p_node) const;
+ virtual void make_visible(bool p_visible);
+
+ LightOccluder2DEditorPlugin(EditorNode *p_node);
+ ~LightOccluder2DEditorPlugin();
+
+};
+
+#endif // LIGHT_OCCLUDER_2D_EDITOR_PLUGIN_H
diff --git a/editor/plugins/material_editor_plugin.cpp b/editor/plugins/material_editor_plugin.cpp
new file mode 100644
index 000000000..876fab0d6
--- /dev/null
+++ b/editor/plugins/material_editor_plugin.cpp
@@ -0,0 +1,381 @@
+#include "material_editor_plugin.h"
+#include "scene/main/viewport.h"
+
+void MaterialEditor::_input_event(InputEvent p_event) {
+
+
+}
+
+void MaterialEditor::_notification(int p_what) {
+
+ if (p_what==NOTIFICATION_FIXED_PROCESS) {
+
+ }
+
+
+ if (p_what==NOTIFICATION_READY) {
+
+ //get_scene()->connect("node_removed",this,"_node_removed");
+
+ if (first_enter) {
+ //it's in propertyeditor so.. could be moved around
+
+ light_1_switch->set_normal_texture(get_icon("MaterialPreviewLight1","EditorIcons"));
+ light_1_switch->set_pressed_texture(get_icon("MaterialPreviewLight1Off","EditorIcons"));
+ light_2_switch->set_normal_texture(get_icon("MaterialPreviewLight2","EditorIcons"));
+ light_2_switch->set_pressed_texture(get_icon("MaterialPreviewLight2Off","EditorIcons"));
+
+ sphere_switch->set_normal_texture(get_icon("MaterialPreviewSphereOff","EditorIcons"));
+ sphere_switch->set_pressed_texture(get_icon("MaterialPreviewSphere","EditorIcons"));
+ box_switch->set_normal_texture(get_icon("MaterialPreviewCubeOff","EditorIcons"));
+ box_switch->set_pressed_texture(get_icon("MaterialPreviewCube","EditorIcons"));
+
+ first_enter=false;
+ }
+
+ }
+
+ if (p_what==NOTIFICATION_DRAW) {
+
+
+ Ref<Texture> checkerboard = get_icon("Checkerboard","EditorIcons");
+ Size2 size = get_size();
+
+ draw_texture_rect(checkerboard,Rect2(Point2(),size),true);
+
+ }
+}
+
+
+
+void MaterialEditor::edit(Ref<Material> p_material) {
+
+ material=p_material;
+
+ if (!material.is_null()) {
+ sphere_mesh->surface_set_material(0,material);
+ box_mesh->surface_set_material(0,material);
+ } else {
+
+ hide();
+ }
+
+}
+
+
+void MaterialEditor::_button_pressed(Node* p_button) {
+
+ if (p_button==light_1_switch) {
+ light1->set_enabled(!light_1_switch->is_pressed());
+ }
+
+ if (p_button==light_2_switch) {
+ light2->set_enabled(!light_2_switch->is_pressed());
+ }
+
+ if (p_button==box_switch) {
+ box_instance->show();
+ sphere_instance->hide();
+ box_switch->set_pressed(true);
+ sphere_switch->set_pressed(false);
+ }
+
+ if (p_button==sphere_switch) {
+ box_instance->hide();
+ sphere_instance->show();
+ box_switch->set_pressed(false);
+ sphere_switch->set_pressed(true);
+ }
+
+}
+
+void MaterialEditor::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("_input_event"),&MaterialEditor::_input_event);
+ ObjectTypeDB::bind_method(_MD("_button_pressed"),&MaterialEditor::_button_pressed);
+
+}
+
+MaterialEditor::MaterialEditor() {
+
+ viewport = memnew( Viewport );
+ Ref<World> world;
+ world.instance();
+ viewport->set_world(world); //use own world
+ add_child(viewport);
+ viewport->set_disable_input(true);
+
+ camera = memnew( Camera );
+ camera->set_transform(Transform(Matrix3(),Vector3(0,0,3)));
+ camera->set_perspective(45,0.1,10);
+ viewport->add_child(camera);
+
+ light1 = memnew( DirectionalLight );
+ light1->set_transform(Transform().looking_at(Vector3(-1,-1,-1),Vector3(0,1,0)));
+ viewport->add_child(light1);
+
+ light2 = memnew( DirectionalLight );
+ light2->set_transform(Transform().looking_at(Vector3(0,1,0),Vector3(0,0,1)));
+ light2->set_color(Light::COLOR_DIFFUSE,Color(0.7,0.7,0.7));
+ light2->set_color(Light::COLOR_SPECULAR,Color(0.7,0.7,0.7));
+ viewport->add_child(light2);
+
+ sphere_instance = memnew( MeshInstance );
+ viewport->add_child(sphere_instance);
+
+ box_instance = memnew( MeshInstance );
+ viewport->add_child(box_instance);
+
+ Transform box_xform;
+ box_xform.basis.rotate(Vector3(1,0,0),Math::deg2rad(-25));
+ box_xform.basis = box_xform.basis * Matrix3().rotated(Vector3(0,1,0),Math::deg2rad(-25));
+ box_xform.basis.scale(Vector3(0.8,0.8,0.8));
+ box_instance->set_transform(box_xform);
+
+ {
+
+ sphere_mesh.instance();
+
+
+ int lats=32;
+ int lons=32;
+ float radius=1.0;
+
+ DVector<Vector3> vertices;
+ DVector<Vector3> normals;
+ DVector<Vector2> uvs;
+ DVector<float> tangents;
+ Matrix3 tt = Matrix3(Vector3(0,1,0),Math_PI*0.5);
+
+ for(int i = 1; i <= lats; i++) {
+ double lat0 = Math_PI * (-0.5 + (double) (i - 1) / lats);
+ double z0 = Math::sin(lat0);
+ double zr0 = Math::cos(lat0);
+
+ double lat1 = Math_PI * (-0.5 + (double) i / lats);
+ double z1 = Math::sin(lat1);
+ double zr1 = Math::cos(lat1);
+
+ for(int j = lons; j >= 1; j--) {
+
+ double lng0 = 2 * Math_PI * (double) (j - 1) / lons;
+ double x0 = Math::cos(lng0);
+ double y0 = Math::sin(lng0);
+
+ double lng1 = 2 * Math_PI * (double) (j) / lons;
+ double x1 = Math::cos(lng1);
+ double y1 = Math::sin(lng1);
+
+
+ Vector3 v[4]={
+ Vector3(x1 * zr0, z0, y1 *zr0),
+ Vector3(x1 * zr1, z1, y1 *zr1),
+ Vector3(x0 * zr1, z1, y0 *zr1),
+ Vector3(x0 * zr0, z0, y0 *zr0)
+ };
+
+ #define ADD_POINT(m_idx) \
+ normals.push_back(v[m_idx]);\
+ vertices.push_back(v[m_idx]*radius);\
+ { Vector2 uv(Math::atan2(v[m_idx].x,v[m_idx].z),Math::atan2(-v[m_idx].y,v[m_idx].z));\
+ uv/=Math_PI;\
+ uv*=4.0;\
+ uv=uv*0.5+Vector2(0.5,0.5);\
+ uvs.push_back(uv);\
+ }\
+ { Vector3 t = tt.xform(v[m_idx]);\
+ tangents.push_back(t.x);\
+ tangents.push_back(t.y);\
+ tangents.push_back(t.z);\
+ tangents.push_back(1.0);\
+ }
+
+
+
+ ADD_POINT(0);
+ ADD_POINT(1);
+ ADD_POINT(2);
+
+ ADD_POINT(2);
+ ADD_POINT(3);
+ ADD_POINT(0);
+ }
+ }
+
+ Array arr;
+ arr.resize(VS::ARRAY_MAX);
+ arr[VS::ARRAY_VERTEX]=vertices;
+ arr[VS::ARRAY_NORMAL]=normals;
+ arr[VS::ARRAY_TANGENT]=tangents;
+ arr[VS::ARRAY_TEX_UV]=uvs;
+
+ sphere_mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES,arr);
+
+ sphere_instance->set_mesh(sphere_mesh);
+
+ }
+ {
+
+
+ box_mesh.instance();
+
+ DVector<Vector3> vertices;
+ DVector<Vector3> normals;
+ DVector<float> tangents;
+ DVector<Vector3> uvs;
+
+ int vtx_idx=0;
+ #define ADD_VTX(m_idx);\
+ vertices.push_back( face_points[m_idx] );\
+ normals.push_back( normal_points[m_idx] );\
+ tangents.push_back( normal_points[m_idx][1] );\
+ tangents.push_back( normal_points[m_idx][2] );\
+ tangents.push_back( normal_points[m_idx][0] );\
+ tangents.push_back( 1.0 );\
+ uvs.push_back( Vector3(uv_points[m_idx*2+0],uv_points[m_idx*2+1],0) );\
+ vtx_idx++;\
+
+ for (int i=0;i<6;i++) {
+
+
+ Vector3 face_points[4];
+ Vector3 normal_points[4];
+ float uv_points[8]={0,0,0,1,1,1,1,0};
+
+ for (int j=0;j<4;j++) {
+
+ float v[3];
+ v[0]=1.0;
+ v[1]=1-2*((j>>1)&1);
+ v[2]=v[1]*(1-2*(j&1));
+
+ for (int k=0;k<3;k++) {
+
+ if (i<3)
+ face_points[j][(i+k)%3]=v[k]*(i>=3?-1:1);
+ else
+ face_points[3-j][(i+k)%3]=v[k]*(i>=3?-1:1);
+ }
+ normal_points[j]=Vector3();
+ normal_points[j][i%3]=(i>=3?-1:1);
+ }
+
+ //tri 1
+ ADD_VTX(0);
+ ADD_VTX(1);
+ ADD_VTX(2);
+ //tri 2
+ ADD_VTX(2);
+ ADD_VTX(3);
+ ADD_VTX(0);
+
+ }
+
+
+
+ Array d;
+ d.resize(VS::ARRAY_MAX);
+ d[VisualServer::ARRAY_NORMAL]= normals ;
+ d[VisualServer::ARRAY_TANGENT]= tangents ;
+ d[VisualServer::ARRAY_TEX_UV]= uvs ;
+ d[VisualServer::ARRAY_VERTEX]= vertices ;
+
+ DVector<int> indices;
+ indices.resize(vertices.size());
+ for(int i=0;i<vertices.size();i++)
+ indices.set(i,i);
+ d[VisualServer::ARRAY_INDEX]=indices;
+
+ box_mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES,d);
+ box_instance->set_mesh(box_mesh);
+ box_instance->hide();
+
+
+
+ }
+
+ set_custom_minimum_size(Size2(1,150)*EDSCALE);
+
+ HBoxContainer *hb = memnew( HBoxContainer );
+ add_child(hb);
+ hb->set_area_as_parent_rect(2);
+
+ VBoxContainer *vb_shape = memnew( VBoxContainer );
+ hb->add_child(vb_shape);
+
+ sphere_switch = memnew( TextureButton );
+ sphere_switch->set_toggle_mode(true);
+ sphere_switch->set_pressed(true);
+ vb_shape->add_child(sphere_switch);
+ sphere_switch->connect("pressed",this,"_button_pressed",varray(sphere_switch));
+
+ box_switch = memnew( TextureButton );
+ box_switch->set_toggle_mode(true);
+ box_switch->set_pressed(false);
+ vb_shape->add_child(box_switch);
+ box_switch->connect("pressed",this,"_button_pressed",varray(box_switch));
+
+ hb->add_spacer();
+
+ VBoxContainer *vb_light = memnew( VBoxContainer );
+ hb->add_child(vb_light);
+
+ light_1_switch = memnew( TextureButton );
+ light_1_switch->set_toggle_mode(true);
+ vb_light->add_child(light_1_switch);
+ light_1_switch->connect("pressed",this,"_button_pressed",varray(light_1_switch));
+
+ light_2_switch = memnew( TextureButton );
+ light_2_switch->set_toggle_mode(true);
+ vb_light->add_child(light_2_switch);
+ light_2_switch->connect("pressed",this,"_button_pressed",varray(light_2_switch));
+
+ first_enter=true;
+
+}
+
+
+void MaterialEditorPlugin::edit(Object *p_object) {
+
+ Material * s = p_object->cast_to<Material>();
+ if (!s)
+ return;
+
+ material_editor->edit(Ref<Material>(s));
+}
+
+bool MaterialEditorPlugin::handles(Object *p_object) const {
+
+ return p_object->is_type("Material");
+}
+
+void MaterialEditorPlugin::make_visible(bool p_visible) {
+
+ if (p_visible) {
+ material_editor->show();
+// material_editor->set_process(true);
+ } else {
+
+ material_editor->hide();
+// material_editor->set_process(false);
+ }
+
+}
+
+MaterialEditorPlugin::MaterialEditorPlugin(EditorNode *p_node) {
+
+ editor=p_node;
+ material_editor = memnew( MaterialEditor );
+ add_control_to_container(CONTAINER_PROPERTY_EDITOR_BOTTOM,material_editor);
+ material_editor->hide();
+
+
+
+}
+
+
+MaterialEditorPlugin::~MaterialEditorPlugin()
+{
+}
+
+
diff --git a/editor/plugins/material_editor_plugin.h b/editor/plugins/material_editor_plugin.h
new file mode 100644
index 000000000..09ffeee0e
--- /dev/null
+++ b/editor/plugins/material_editor_plugin.h
@@ -0,0 +1,71 @@
+#ifndef MATERIAL_EDITOR_PLUGIN_H
+#define MATERIAL_EDITOR_PLUGIN_H
+
+#include "editor/editor_plugin.h"
+#include "editor/editor_node.h"
+#include "scene/resources/material.h"
+#include "scene/3d/light.h"
+#include "scene/3d/mesh_instance.h"
+#include "scene/3d/camera.h"
+
+
+class MaterialEditor : public Control {
+
+ OBJ_TYPE(MaterialEditor, Control);
+
+
+ Viewport *viewport;
+ MeshInstance *sphere_instance;
+ MeshInstance *box_instance;
+ DirectionalLight *light1;
+ DirectionalLight *light2;
+ Camera *camera;
+
+ Ref<Mesh> sphere_mesh;
+ Ref<Mesh> box_mesh;
+
+ TextureButton *sphere_switch;
+ TextureButton *box_switch;
+
+ TextureButton *light_1_switch;
+ TextureButton *light_2_switch;
+
+
+ Ref<Material> material;
+
+
+ void _button_pressed(Node* p_button);
+ bool first_enter;
+
+protected:
+ void _notification(int p_what);
+ void _input_event(InputEvent p_event);
+ static void _bind_methods();
+public:
+
+ void edit(Ref<Material> p_material);
+ MaterialEditor();
+};
+
+
+class MaterialEditorPlugin : public EditorPlugin {
+
+ OBJ_TYPE( MaterialEditorPlugin, EditorPlugin );
+
+ MaterialEditor *material_editor;
+ EditorNode *editor;
+
+public:
+
+ virtual String get_name() const { return "Material"; }
+ 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);
+
+ MaterialEditorPlugin(EditorNode *p_node);
+ ~MaterialEditorPlugin();
+
+};
+
+#endif // MATERIAL_EDITOR_PLUGIN_H
diff --git a/editor/plugins/mesh_editor_plugin.cpp b/editor/plugins/mesh_editor_plugin.cpp
new file mode 100644
index 000000000..bfe99004b
--- /dev/null
+++ b/editor/plugins/mesh_editor_plugin.cpp
@@ -0,0 +1,243 @@
+/*************************************************************************/
+/* mesh_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "mesh_editor_plugin.h"
+
+void MeshEditor::_input_event(InputEvent p_event) {
+
+
+ if (p_event.type==InputEvent::MOUSE_MOTION && p_event.mouse_motion.button_mask&BUTTON_MASK_LEFT) {
+
+ rot_x-=p_event.mouse_motion.relative_y*0.01;
+ rot_y-=p_event.mouse_motion.relative_x*0.01;
+ if (rot_x<-Math_PI/2)
+ rot_x=-Math_PI/2;
+ else if (rot_x>Math_PI/2) {
+ rot_x=Math_PI/2;
+ }
+ _update_rotation();
+ }
+}
+
+void MeshEditor::_notification(int p_what) {
+
+ if (p_what==NOTIFICATION_FIXED_PROCESS) {
+
+ }
+
+
+ if (p_what==NOTIFICATION_READY) {
+
+ //get_scene()->connect("node_removed",this,"_node_removed");
+
+ if (first_enter) {
+ //it's in propertyeditor so.. could be moved around
+
+ light_1_switch->set_normal_texture(get_icon("MaterialPreviewLight1","EditorIcons"));
+ light_1_switch->set_pressed_texture(get_icon("MaterialPreviewLight1Off","EditorIcons"));
+ light_2_switch->set_normal_texture(get_icon("MaterialPreviewLight2","EditorIcons"));
+ light_2_switch->set_pressed_texture(get_icon("MaterialPreviewLight2Off","EditorIcons"));
+ first_enter=false;
+ }
+
+ }
+
+ if (p_what==NOTIFICATION_DRAW) {
+
+
+ Ref<Texture> checkerboard = get_icon("Checkerboard","EditorIcons");
+ Size2 size = get_size();
+
+ draw_texture_rect(checkerboard,Rect2(Point2(),size),true);
+
+ }
+}
+
+void MeshEditor::_update_rotation() {
+
+ Transform t;
+ t.basis.rotate(Vector3(0, 1, 0), rot_y);
+ t.basis.rotate(Vector3(1, 0, 0), rot_x);
+ mesh_instance->set_transform(t);
+
+}
+
+void MeshEditor::edit(Ref<Mesh> p_mesh) {
+
+ mesh=p_mesh;
+ mesh_instance->set_mesh(mesh);
+
+ if (mesh.is_null()) {
+
+ hide();
+ } else {
+ rot_x=0;
+ rot_y=0;
+ _update_rotation();
+
+ AABB aabb= mesh->get_aabb();
+ Vector3 ofs = aabb.pos + aabb.size*0.5;
+ aabb.pos-=ofs;
+ float m = MAX(aabb.size.x,aabb.size.y)*0.5;
+ if (m!=0) {
+ m=1.0/m;
+ m*=0.5;
+ //print_line("scale: "+rtos(m));
+ Transform xform;
+ xform.basis.scale(Vector3(m,m,m));
+ xform.origin=-xform.basis.xform(ofs); //-ofs*m;
+ xform.origin.z-=aabb.size.z*2;
+ mesh_instance->set_transform(xform);
+ }
+
+ }
+
+}
+
+
+void MeshEditor::_button_pressed(Node* p_button) {
+
+ if (p_button==light_1_switch) {
+ light1->set_enabled(!light_1_switch->is_pressed());
+ }
+
+ if (p_button==light_2_switch) {
+ light2->set_enabled(!light_2_switch->is_pressed());
+ }
+
+
+}
+
+void MeshEditor::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("_input_event"),&MeshEditor::_input_event);
+ ObjectTypeDB::bind_method(_MD("_button_pressed"),&MeshEditor::_button_pressed);
+
+}
+
+MeshEditor::MeshEditor() {
+
+ viewport = memnew( Viewport );
+ Ref<World> world;
+ world.instance();
+ viewport->set_world(world); //use own world
+ add_child(viewport);
+ viewport->set_disable_input(true);
+
+ camera = memnew( Camera );
+ camera->set_transform(Transform(Matrix3(),Vector3(0,0,3)));
+ camera->set_perspective(45,0.1,10);
+ viewport->add_child(camera);
+
+ light1 = memnew( DirectionalLight );
+ light1->set_transform(Transform().looking_at(Vector3(-1,-1,-1),Vector3(0,1,0)));
+ viewport->add_child(light1);
+
+ light2 = memnew( DirectionalLight );
+ light2->set_transform(Transform().looking_at(Vector3(0,1,0),Vector3(0,0,1)));
+ light2->set_color(Light::COLOR_DIFFUSE,Color(0.7,0.7,0.7));
+ light2->set_color(Light::COLOR_SPECULAR,Color(0.7,0.7,0.7));
+ viewport->add_child(light2);
+
+ mesh_instance = memnew( MeshInstance );
+ viewport->add_child(mesh_instance);
+
+
+
+ set_custom_minimum_size(Size2(1,150)*EDSCALE);
+
+ HBoxContainer *hb = memnew( HBoxContainer );
+ add_child(hb);
+ hb->set_area_as_parent_rect(2);
+
+ hb->add_spacer();
+
+ VBoxContainer *vb_light = memnew( VBoxContainer );
+ hb->add_child(vb_light);
+
+ light_1_switch = memnew( TextureButton );
+ light_1_switch->set_toggle_mode(true);
+ vb_light->add_child(light_1_switch);
+ light_1_switch->connect("pressed",this,"_button_pressed",varray(light_1_switch));
+
+ light_2_switch = memnew( TextureButton );
+ light_2_switch->set_toggle_mode(true);
+ vb_light->add_child(light_2_switch);
+ light_2_switch->connect("pressed",this,"_button_pressed",varray(light_2_switch));
+
+ first_enter=true;
+
+ rot_x=0;
+ rot_y=0;
+
+
+}
+
+
+void MeshEditorPlugin::edit(Object *p_object) {
+
+ Mesh * s = p_object->cast_to<Mesh>();
+ if (!s)
+ return;
+
+ mesh_editor->edit(Ref<Mesh>(s));
+}
+
+bool MeshEditorPlugin::handles(Object *p_object) const {
+
+ return p_object->is_type("Mesh");
+}
+
+void MeshEditorPlugin::make_visible(bool p_visible) {
+
+ if (p_visible) {
+ mesh_editor->show();
+// mesh_editor->set_process(true);
+ } else {
+
+ mesh_editor->hide();
+// mesh_editor->set_process(false);
+ }
+
+}
+
+MeshEditorPlugin::MeshEditorPlugin(EditorNode *p_node) {
+
+ editor=p_node;
+ mesh_editor = memnew( MeshEditor );
+ add_control_to_container(CONTAINER_PROPERTY_EDITOR_BOTTOM,mesh_editor);
+ mesh_editor->hide();
+
+
+
+}
+
+
+MeshEditorPlugin::~MeshEditorPlugin()
+{
+}
diff --git a/editor/plugins/mesh_editor_plugin.h b/editor/plugins/mesh_editor_plugin.h
new file mode 100644
index 000000000..3dc332b20
--- /dev/null
+++ b/editor/plugins/mesh_editor_plugin.h
@@ -0,0 +1,95 @@
+/*************************************************************************/
+/* mesh_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 MESH_EDITOR_PLUGIN_H
+#define MESH_EDITOR_PLUGIN_H
+
+#include "editor/editor_plugin.h"
+#include "editor/editor_node.h"
+#include "scene/resources/material.h"
+#include "scene/3d/light.h"
+#include "scene/3d/mesh_instance.h"
+#include "scene/3d/camera.h"
+
+class MeshEditor : public Control {
+
+ OBJ_TYPE(MeshEditor, Control);
+
+
+
+ float rot_x;
+ float rot_y;
+
+ Viewport *viewport;
+ MeshInstance *mesh_instance;
+ DirectionalLight *light1;
+ DirectionalLight *light2;
+ Camera *camera;
+
+ Ref<Mesh> mesh;
+
+
+ TextureButton *light_1_switch;
+ TextureButton *light_2_switch;
+
+ void _button_pressed(Node* p_button);
+ bool first_enter;
+
+ void _update_rotation();
+protected:
+ void _notification(int p_what);
+ void _input_event(InputEvent p_event);
+ static void _bind_methods();
+public:
+
+ void edit(Ref<Mesh> p_mesh);
+ MeshEditor();
+};
+
+
+class MeshEditorPlugin : public EditorPlugin {
+
+ OBJ_TYPE( MeshEditorPlugin, EditorPlugin );
+
+ MeshEditor *mesh_editor;
+ EditorNode *editor;
+
+public:
+
+ virtual String get_name() const { return "Mesh"; }
+ bool has_main_screen() const { return false; }
+ virtual void edit(Object *p_node);
+ virtual bool handles(Object *p_node) const;
+ virtual void make_visible(bool p_visible);
+
+ MeshEditorPlugin(EditorNode *p_node);
+ ~MeshEditorPlugin();
+
+};
+
+#endif // MESH_EDITOR_PLUGIN_H
diff --git a/editor/plugins/mesh_instance_editor_plugin.cpp b/editor/plugins/mesh_instance_editor_plugin.cpp
new file mode 100644
index 000000000..c952feb1d
--- /dev/null
+++ b/editor/plugins/mesh_instance_editor_plugin.cpp
@@ -0,0 +1,306 @@
+#include "mesh_instance_editor_plugin.h"
+
+#include "scene/3d/physics_body.h"
+#include "scene/3d/body_shape.h"
+#include "scene/gui/box_container.h"
+#include "scene/3d/navigation_mesh.h"
+#include "spatial_editor_plugin.h"
+
+void MeshInstanceEditor::_node_removed(Node *p_node) {
+
+ if(p_node==node) {
+ node=NULL;
+ options->hide();
+ }
+
+}
+
+
+
+void MeshInstanceEditor::edit(MeshInstance *p_mesh) {
+
+ node=p_mesh;
+
+}
+
+void MeshInstanceEditor::_menu_option(int p_option) {
+
+ Ref<Mesh> mesh = node->get_mesh();
+ if (mesh.is_null()) {
+ err_dialog->set_text(TTR("Mesh is empty!"));
+ err_dialog->popup_centered_minsize();
+ return;
+ }
+
+ switch(p_option) {
+ case MENU_OPTION_CREATE_STATIC_TRIMESH_BODY:
+ case MENU_OPTION_CREATE_STATIC_CONVEX_BODY: {
+
+ bool trimesh_shape = (p_option==MENU_OPTION_CREATE_STATIC_TRIMESH_BODY);
+
+ EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection();
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+
+ List<Node*> selection = editor_selection->get_selected_node_list();
+
+ if (selection.empty()) {
+ Ref<Shape> shape = trimesh_shape ? mesh->create_trimesh_shape() : mesh->create_convex_shape();
+ if (shape.is_null())
+ return;
+
+ CollisionShape *cshape = memnew( CollisionShape );
+ cshape->set_shape(shape);
+ StaticBody *body = memnew( StaticBody );
+ body->add_child(cshape);
+
+ Node *owner = node==get_tree()->get_edited_scene_root() ? node : node->get_owner();
+
+ if (trimesh_shape)
+ ur->create_action(TTR("Create Static Trimesh Body"));
+ else
+ ur->create_action(TTR("Create Static Convex Body"));
+
+ ur->add_do_method(node,"add_child",body);
+ ur->add_do_method(body,"set_owner",owner);
+ ur->add_do_method(cshape,"set_owner",owner);
+ ur->add_do_reference(body);
+ ur->add_undo_method(node,"remove_child",body);
+ ur->commit_action();
+ return;
+ }
+
+ if (trimesh_shape)
+ ur->create_action(TTR("Create Static Trimesh Body"));
+ else
+ ur->create_action(TTR("Create Static Convex Body"));
+
+ for (List<Node*>::Element *E=selection.front();E;E=E->next()) {
+
+ MeshInstance *instance = E->get()->cast_to<MeshInstance>();
+ if (!instance)
+ continue;
+
+ Ref<Mesh> m = instance->get_mesh();
+ if (m.is_null())
+ continue;
+
+ Ref<Shape> shape = trimesh_shape ? m->create_trimesh_shape() : m->create_convex_shape();
+ if (shape.is_null())
+ continue;
+
+ CollisionShape *cshape = memnew( CollisionShape );
+ cshape->set_shape(shape);
+ StaticBody *body = memnew( StaticBody );
+ body->add_child(cshape);
+
+ Node *owner = instance==get_tree()->get_edited_scene_root() ? instance : instance->get_owner();
+
+ ur->add_do_method(instance,"add_child",body);
+ ur->add_do_method(body,"set_owner",owner);
+ ur->add_do_method(cshape,"set_owner",owner);
+ ur->add_do_reference(body);
+ ur->add_undo_method(instance,"remove_child",body);
+ }
+
+ ur->commit_action();
+
+ } break;
+
+ case MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE:
+ case MENU_OPTION_CREATE_CONVEX_COLLISION_SHAPE: {
+
+ if (node==get_tree()->get_edited_scene_root()) {
+ err_dialog->set_text(TTR("This doesn't work on scene root!"));
+ err_dialog->popup_centered_minsize();
+ return;
+ }
+
+ bool trimesh_shape = (p_option==MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE);
+
+ Ref<Shape> shape = trimesh_shape ? mesh->create_trimesh_shape() : mesh->create_convex_shape();
+ if (shape.is_null())
+ return;
+
+ CollisionShape *cshape = memnew( CollisionShape );
+ cshape->set_shape(shape);
+
+ Node *owner = node->get_owner();
+
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+
+ if (trimesh_shape)
+ ur->create_action(TTR("Create Trimesh Shape"));
+ else
+ ur->create_action(TTR("Create Convex Shape"));
+
+ ur->add_do_method(node->get_parent(),"add_child",cshape);
+ ur->add_do_method(node->get_parent(),"move_child",cshape,node->get_index()+1);
+ ur->add_do_method(cshape,"set_owner",owner);
+ ur->add_do_reference(cshape);
+ ur->add_undo_method(node->get_parent(),"remove_child",cshape);
+ ur->commit_action();
+
+ } break;
+
+ case MENU_OPTION_CREATE_NAVMESH: {
+
+ Ref<NavigationMesh> nmesh = memnew( NavigationMesh );
+
+ if (nmesh.is_null())
+ return;
+
+ nmesh->create_from_mesh(mesh);
+ NavigationMeshInstance *nmi = memnew( NavigationMeshInstance );
+ nmi->set_navigation_mesh(nmesh);
+
+ Node *owner = node==get_tree()->get_edited_scene_root() ? node : node->get_owner();
+
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Create Navigation Mesh"));
+
+ ur->add_do_method(node,"add_child",nmi);
+ ur->add_do_method(nmi,"set_owner",owner);
+
+ ur->add_do_reference(nmi);
+ ur->add_undo_method(node,"remove_child",nmi);
+ ur->commit_action();
+ } break;
+
+ case MENU_OPTION_CREATE_OUTLINE_MESH: {
+
+ outline_dialog->popup_centered(Vector2(200, 90));
+ } break;
+ }
+
+}
+
+void MeshInstanceEditor::_create_outline_mesh() {
+
+ Ref<Mesh> mesh = node->get_mesh();
+ if (mesh.is_null()) {
+ err_dialog->set_text(TTR("MeshInstance lacks a Mesh!"));
+ err_dialog->popup_centered_minsize();
+ return;
+ }
+
+ if (mesh->get_surface_count() == 0) {
+ err_dialog->set_text(TTR("Mesh has not surface to create outlines from!"));
+ err_dialog->popup_centered_minsize();
+ return;
+ }
+
+ Ref<Mesh> mesho = mesh->create_outline(outline_size->get_val());
+
+ if (mesho.is_null()) {
+ err_dialog->set_text(TTR("Could not create outline!"));
+ err_dialog->popup_centered_minsize();
+ return;
+ }
+
+ MeshInstance *mi = memnew( MeshInstance );
+ mi->set_mesh(mesho);
+ Node *owner=node->get_owner();
+ if (get_tree()->get_edited_scene_root()==node) {
+ owner=node;
+ }
+
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+
+ ur->create_action(TTR("Create Outline"));
+
+ ur->add_do_method(node,"add_child",mi);
+ ur->add_do_method(mi,"set_owner",owner);
+
+ ur->add_do_reference(mi);
+ ur->add_undo_method(node,"remove_child",mi);
+ ur->commit_action();
+}
+
+void MeshInstanceEditor::_bind_methods() {
+
+ ObjectTypeDB::bind_method("_menu_option",&MeshInstanceEditor::_menu_option);
+ ObjectTypeDB::bind_method("_create_outline_mesh",&MeshInstanceEditor::_create_outline_mesh);
+}
+
+MeshInstanceEditor::MeshInstanceEditor() {
+
+
+ options = memnew( MenuButton );
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(options);
+
+ options->set_text(TTR("Mesh"));
+ options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("MeshInstance","EditorIcons"));
+
+ options->get_popup()->add_item(TTR("Create Trimesh Static Body"),MENU_OPTION_CREATE_STATIC_TRIMESH_BODY);
+ options->get_popup()->add_item(TTR("Create Convex Static Body"),MENU_OPTION_CREATE_STATIC_CONVEX_BODY);
+ options->get_popup()->add_separator();
+ options->get_popup()->add_item(TTR("Create Trimesh Collision Sibling"),MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE);
+ options->get_popup()->add_item(TTR("Create Convex Collision Sibling"),MENU_OPTION_CREATE_CONVEX_COLLISION_SHAPE);
+ options->get_popup()->add_separator();
+ options->get_popup()->add_item(TTR("Create Navigation Mesh"),MENU_OPTION_CREATE_NAVMESH);
+ options->get_popup()->add_separator();
+ options->get_popup()->add_item(TTR("Create Outline Mesh.."),MENU_OPTION_CREATE_OUTLINE_MESH);
+
+ options->get_popup()->connect("item_pressed", this,"_menu_option");
+
+ outline_dialog = memnew( ConfirmationDialog );
+ outline_dialog->set_title(TTR("Create Outline Mesh"));
+ outline_dialog->get_ok()->set_text(TTR("Create"));
+
+ VBoxContainer *outline_dialog_vbc = memnew( VBoxContainer );
+ outline_dialog->add_child(outline_dialog_vbc);
+ outline_dialog->set_child_rect(outline_dialog_vbc);
+
+ outline_size = memnew( SpinBox );
+ outline_size->set_min(0.001);
+ outline_size->set_max(1024);
+ outline_size->set_step(0.001);
+ outline_size->set_val(0.05);
+ outline_dialog_vbc->add_margin_child(TTR("Outline Size:"),outline_size);
+
+ add_child(outline_dialog);
+ outline_dialog->connect("confirmed",this,"_create_outline_mesh");
+
+ err_dialog = memnew( AcceptDialog );
+ add_child(err_dialog);
+
+}
+
+
+void MeshInstanceEditorPlugin::edit(Object *p_object) {
+
+ mesh_editor->edit(p_object->cast_to<MeshInstance>());
+}
+
+bool MeshInstanceEditorPlugin::handles(Object *p_object) const {
+
+ return p_object->is_type("MeshInstance");
+}
+
+void MeshInstanceEditorPlugin::make_visible(bool p_visible) {
+
+ if (p_visible) {
+ mesh_editor->options->show();
+ } else {
+
+ mesh_editor->options->hide();
+ mesh_editor->edit(NULL);
+ }
+
+}
+
+MeshInstanceEditorPlugin::MeshInstanceEditorPlugin(EditorNode *p_node) {
+
+ editor=p_node;
+ mesh_editor = memnew( MeshInstanceEditor );
+ editor->get_viewport()->add_child(mesh_editor);
+
+ mesh_editor->options->hide();
+}
+
+
+MeshInstanceEditorPlugin::~MeshInstanceEditorPlugin()
+{
+}
+
+
diff --git a/editor/plugins/mesh_instance_editor_plugin.h b/editor/plugins/mesh_instance_editor_plugin.h
new file mode 100644
index 000000000..efe469958
--- /dev/null
+++ b/editor/plugins/mesh_instance_editor_plugin.h
@@ -0,0 +1,69 @@
+#ifndef MESH_INSTANCE_EDITOR_PLUGIN_H
+#define MESH_INSTANCE_EDITOR_PLUGIN_H
+
+
+#include "editor/editor_plugin.h"
+#include "editor/editor_node.h"
+#include "scene/3d/mesh_instance.h"
+#include "scene/gui/spin_box.h"
+
+
+class MeshInstanceEditor : public Node {
+
+ OBJ_TYPE(MeshInstanceEditor, Node );
+
+
+ enum Menu {
+
+ MENU_OPTION_CREATE_STATIC_TRIMESH_BODY,
+ MENU_OPTION_CREATE_STATIC_CONVEX_BODY,
+ MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE,
+ MENU_OPTION_CREATE_CONVEX_COLLISION_SHAPE,
+ MENU_OPTION_CREATE_NAVMESH,
+ MENU_OPTION_CREATE_OUTLINE_MESH,
+ };
+
+ MeshInstance *node;
+
+ MenuButton *options;
+
+ ConfirmationDialog *outline_dialog;
+ SpinBox *outline_size;
+
+ AcceptDialog *err_dialog;
+
+ void _menu_option(int p_option);
+ void _create_outline_mesh();
+
+friend class MeshInstanceEditorPlugin;
+
+protected:
+ void _node_removed(Node *p_node);
+ static void _bind_methods();
+public:
+
+ void edit(MeshInstance *p_mesh);
+ MeshInstanceEditor();
+};
+
+class MeshInstanceEditorPlugin : public EditorPlugin {
+
+ OBJ_TYPE( MeshInstanceEditorPlugin, EditorPlugin );
+
+ MeshInstanceEditor *mesh_editor;
+ EditorNode *editor;
+
+public:
+
+ virtual String get_name() const { return "MeshInstance"; }
+ 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);
+
+ MeshInstanceEditorPlugin(EditorNode *p_node);
+ ~MeshInstanceEditorPlugin();
+
+};
+
+#endif // MESH_EDITOR_PLUGIN_H
diff --git a/editor/plugins/multimesh_editor_plugin.cpp b/editor/plugins/multimesh_editor_plugin.cpp
new file mode 100644
index 000000000..c099df940
--- /dev/null
+++ b/editor/plugins/multimesh_editor_plugin.cpp
@@ -0,0 +1,459 @@
+/*************************************************************************/
+/* multimesh_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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"
+#include "spatial_editor_plugin.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(TTR("No mesh source specified (and no MultiMesh set in node)."));
+ err_dialog->popup_centered_minsize();
+ return;
+ }
+ if (multimesh->get_mesh().is_null()) {
+
+ err_dialog->set_text(TTR("No mesh source specified (and MultiMesh contains no Mesh)."));
+ err_dialog->popup_centered_minsize();
+ return;
+ }
+
+ mesh = multimesh->get_mesh();
+ } else {
+
+ Node *ms_node = node->get_node(mesh_source->get_text());
+
+ if (!ms_node) {
+
+ err_dialog->set_text(TTR("Mesh source is invalid (invalid path)."));
+ err_dialog->popup_centered_minsize();
+ return;
+ }
+
+ MeshInstance *ms_instance = ms_node->cast_to<MeshInstance>();
+
+ if (!ms_instance) {
+
+ err_dialog->set_text(TTR("Mesh source is invalid (not a MeshInstance)."));
+ err_dialog->popup_centered_minsize();
+ return;
+ }
+
+ mesh=ms_instance->get_mesh();
+
+ if (mesh.is_null()) {
+
+ err_dialog->set_text(TTR("Mesh source is invalid (contains no Mesh resource)."));
+ err_dialog->popup_centered_minsize();
+ return;
+ }
+
+ }
+
+ if (surface_source->get_text()=="") {
+
+ err_dialog->set_text(TTR("No surface source specified."));
+ err_dialog->popup_centered_minsize();
+ return;
+ }
+
+ Node *ss_node = node->get_node(surface_source->get_text());
+
+ if (!ss_node) {
+
+ err_dialog->set_text(TTR("Surface source is invalid (invalid path)."));
+ err_dialog->popup_centered_minsize();
+ return;
+ }
+
+ GeometryInstance *ss_instance = ss_node->cast_to<MeshInstance>();
+
+ if (!ss_instance) {
+
+ err_dialog->set_text(TTR("Surface source is invalid (no geometry)."));
+ err_dialog->popup_centered_minsize();
+ 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(TTR("Surface source is invalid (no faces)."));
+ err_dialog->popup_centered_minsize();
+ 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(TTR("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(TTR("Couldn't map area."));
+ ERR_FAIL_COND(triangle_area_map.size()==0);
+ ERR_EXPLAIN(TTR("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,380));
+
+ } break;
+ }
+}
+
+
+void MultiMeshEditor::edit(MultiMeshInstance *p_multimesh) {
+
+ node=p_multimesh;
+
+}
+
+void MultiMeshEditor::_browse(bool p_source) {
+
+ browsing_source=p_source;
+ std->get_scene_tree()->set_marked(node,false);
+ std->popup_centered_ratio();
+ if (p_source)
+ std->set_title(TTR("Select a Source Mesh:"));
+ else
+ std->set_title(TTR("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 );
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(options);
+
+ options->set_text("MultiMesh");
+ options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("MultiMeshInstance","EditorIcons"));
+
+ options->get_popup()->add_item(TTR("Populate Surface"));
+ options->get_popup()->connect("item_pressed", this,"_menu_option");
+
+ populate_dialog = memnew( ConfirmationDialog );
+ populate_dialog->set_title(TTR("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(TTR("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(TTR("Source Mesh:"),hbc);
+ b->connect("pressed",this,"_browse",make_binds(true));
+
+
+ populate_axis = memnew( OptionButton );
+ populate_axis->add_item(TTR("X-Axis"));
+ populate_axis->add_item(TTR("Y-Axis"));
+ populate_axis->add_item(TTR("Z-Axis"));
+ populate_axis->select(2);
+ vbc->add_margin_child(TTR("Mesh Up Axis:"),populate_axis);
+
+ populate_rotate_random = memnew( HSlider );
+ populate_rotate_random->set_max(1);
+ populate_rotate_random->set_step(0.01);
+ vbc->add_margin_child(TTR("Random Rotation:"),populate_rotate_random);
+
+ populate_tilt_random = memnew( HSlider );
+ populate_tilt_random->set_max(1);
+ populate_tilt_random->set_step(0.01);
+ vbc->add_margin_child(TTR("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(TTR("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(TTR("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(TTR("Amount:"),populate_amount);
+
+ populate_dialog->get_ok()->set_text(TTR("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;
+
+ 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->options->show();
+ } else {
+
+ multimesh_editor->options->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->options->hide();
+}
+
+
+MultiMeshEditorPlugin::~MultiMeshEditorPlugin()
+{
+}
+
+
diff --git a/editor/plugins/multimesh_editor_plugin.h b/editor/plugins/multimesh_editor_plugin.h
new file mode 100644
index 000000000..af11139e5
--- /dev/null
+++ b/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-2017 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 "editor/editor_plugin.h"
+#include "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 );
+
+friend class MultiMeshEditorPlugin;
+
+ AcceptDialog *err_dialog;
+ MenuButton * options;
+ MultiMeshInstance *_last_pp_node;
+ bool browsing_source;
+
+ Panel *panel;
+ MultiMeshInstance *node;
+
+ LineEdit *surface_source;
+ LineEdit *mesh_source;
+
+ SceneTreeDialog *std;
+
+ ConfirmationDialog *populate_dialog;
+ OptionButton *populate_axis;
+ HSlider *populate_rotate_random;
+ HSlider *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/editor/plugins/navigation_polygon_editor_plugin.cpp b/editor/plugins/navigation_polygon_editor_plugin.cpp
new file mode 100644
index 000000000..ab4d96d6e
--- /dev/null
+++ b/editor/plugins/navigation_polygon_editor_plugin.cpp
@@ -0,0 +1,566 @@
+/*************************************************************************/
+/* navigation_polygon_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "navigation_polygon_editor_plugin.h"
+
+#include "canvas_item_editor_plugin.h"
+#include "os/file_access.h"
+#include "editor/editor_settings.h"
+
+void NavigationPolygonEditor::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_READY: {
+
+ button_create->set_icon( get_icon("Edit","EditorIcons"));
+ button_edit->set_icon( get_icon("MovePoint","EditorIcons"));
+ button_edit->set_pressed(true);
+ get_tree()->connect("node_removed",this,"_node_removed");
+ create_nav->connect("confirmed",this,"_create_nav");
+
+ } break;
+ case NOTIFICATION_FIXED_PROCESS: {
+
+
+ } break;
+ }
+
+}
+void NavigationPolygonEditor::_node_removed(Node *p_node) {
+
+ if(p_node==node) {
+ node=NULL;
+ hide();
+ canvas_item_editor->get_viewport_control()->update();
+ }
+
+}
+
+void NavigationPolygonEditor::_create_nav() {
+
+ if (!node)
+ return;
+
+ undo_redo->create_action(TTR("Create Navigation Polygon"));
+ undo_redo->add_do_method(node,"set_navigation_polygon",Ref<NavigationPolygon>(memnew( NavigationPolygon)));
+ undo_redo->add_undo_method(node,"set_navigation_polygon",Variant(REF()));
+ undo_redo->commit_action();
+}
+
+void NavigationPolygonEditor::_menu_option(int p_option) {
+
+ switch(p_option) {
+
+ case MODE_CREATE: {
+
+ mode=MODE_CREATE;
+ button_create->set_pressed(true);
+ button_edit->set_pressed(false);
+ } break;
+ case MODE_EDIT: {
+
+ mode=MODE_EDIT;
+ button_create->set_pressed(false);
+ button_edit->set_pressed(true);
+ } break;
+
+ }
+}
+
+void NavigationPolygonEditor::_wip_close() {
+
+
+ if (wip.size()>=3) {
+
+ undo_redo->create_action(TTR("Create Poly"));
+ undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"remove_outline",node->get_navigation_polygon()->get_outline_count());
+ undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"add_outline",wip);
+ undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines");
+ undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines");
+ undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+ mode=MODE_EDIT;
+ button_edit->set_pressed(true);
+ button_create->set_pressed(false);
+ }
+
+ wip.clear();
+ wip_active=false;
+ edited_point=-1;
+}
+
+bool NavigationPolygonEditor::forward_input_event(const InputEvent& p_event) {
+
+
+ if (!node)
+ return false;
+
+ if (node->get_navigation_polygon().is_null()) {
+ if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1 && p_event.mouse_button.pressed) {
+ create_nav->set_text("No NavigationPolygon resource on this node.\nCreate and assign one?");
+ create_nav->popup_centered_minsize();
+ }
+ return (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1);
+ }
+
+
+ switch(p_event.type) {
+
+ case InputEvent::MOUSE_BUTTON: {
+
+ const InputEventMouseButton &mb=p_event.mouse_button;
+
+ Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
+
+
+ Vector2 gpoint = Point2(mb.x,mb.y);
+ Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint);
+ cpoint=canvas_item_editor->snap_point(cpoint);
+ cpoint = node->get_global_transform().affine_inverse().xform(cpoint);
+
+
+
+ //first check if a point is to be added (segment split)
+ real_t grab_treshold=EDITOR_DEF("poly_editor/point_grab_radius",8);
+
+ switch(mode) {
+
+
+ case MODE_CREATE: {
+
+ if (mb.button_index==BUTTON_LEFT && mb.pressed) {
+
+
+ if (!wip_active) {
+
+ wip.clear();
+ wip.push_back( cpoint );
+ wip_active=true;
+ edited_point_pos=cpoint;
+ edited_outline=-1;
+ canvas_item_editor->get_viewport_control()->update();
+ edited_point=1;
+ return true;
+ } else {
+
+
+ if (wip.size()>1 && xform.xform(wip[0]).distance_to(gpoint)<grab_treshold) {
+ //wip closed
+ _wip_close();
+
+ return true;
+ } else {
+
+ wip.push_back( cpoint );
+ edited_point=wip.size();
+ canvas_item_editor->get_viewport_control()->update();
+ return true;
+
+ //add wip point
+ }
+ }
+ } else if (mb.button_index==BUTTON_RIGHT && mb.pressed && wip_active) {
+ _wip_close();
+ }
+
+
+
+ } break;
+
+ case MODE_EDIT: {
+
+ if (mb.button_index==BUTTON_LEFT) {
+ if (mb.pressed) {
+
+ if (mb.mod.control) {
+
+
+ //search edges
+ int closest_outline=-1;
+ int closest_idx=-1;
+ Vector2 closest_pos;
+ real_t closest_dist=1e10;
+
+ for(int j=0;j<node->get_navigation_polygon()->get_outline_count();j++) {
+
+
+ DVector<Vector2> points=node->get_navigation_polygon()->get_outline(j);
+
+ int pc=points.size();
+ DVector<Vector2>::Read poly=points.read();
+
+ for(int i=0;i<pc;i++) {
+
+ Vector2 points[2] ={ xform.xform(poly[i]),
+ xform.xform(poly[(i+1)%pc]) };
+
+ Vector2 cp = Geometry::get_closest_point_to_segment_2d(gpoint,points);
+ if (cp.distance_squared_to(points[0])<CMP_EPSILON2 || cp.distance_squared_to(points[1])<CMP_EPSILON2)
+ continue; //not valid to reuse point
+
+ real_t d = cp.distance_to(gpoint);
+ if (d<closest_dist && d<grab_treshold) {
+ closest_dist=d;
+ closest_outline=j;
+ closest_pos=cp;
+ closest_idx=i;
+ }
+
+
+ }
+ }
+
+ if (closest_idx>=0) {
+
+ pre_move_edit=node->get_navigation_polygon()->get_outline(closest_outline);
+ DVector<Point2> poly = pre_move_edit;
+ poly.insert(closest_idx+1,xform.affine_inverse().xform(closest_pos));
+ edited_point=closest_idx+1;
+ edited_outline=closest_outline;
+ edited_point_pos=xform.affine_inverse().xform(closest_pos);
+ node->get_navigation_polygon()->set_outline(closest_outline,poly);
+ canvas_item_editor->get_viewport_control()->update();
+ return true;
+ }
+ } else {
+
+ //look for points to move
+ int closest_outline=-1;
+ int closest_idx=-1;
+ Vector2 closest_pos;
+ real_t closest_dist=1e10;
+
+ for(int j=0;j<node->get_navigation_polygon()->get_outline_count();j++) {
+
+
+ DVector<Vector2> points=node->get_navigation_polygon()->get_outline(j);
+
+ int pc=points.size();
+ DVector<Vector2>::Read poly=points.read();
+
+ for(int i=0;i<pc;i++) {
+
+
+ Vector2 cp =xform.xform(poly[i]);
+
+ real_t d = cp.distance_to(gpoint);
+ if (d<closest_dist && d<grab_treshold) {
+ closest_dist=d;
+ closest_pos=cp;
+ closest_outline=j;
+ closest_idx=i;
+ }
+ }
+ }
+
+ if (closest_idx>=0) {
+
+ pre_move_edit=node->get_navigation_polygon()->get_outline(closest_outline);
+ edited_point=closest_idx;
+ edited_outline=closest_outline;
+ edited_point_pos=xform.affine_inverse().xform(closest_pos);
+ canvas_item_editor->get_viewport_control()->update();
+ return true;
+ }
+ }
+ } else {
+
+ if (edited_point!=-1) {
+
+ //apply
+
+ DVector<Vector2> poly = node->get_navigation_polygon()->get_outline(edited_outline);
+ ERR_FAIL_INDEX_V(edited_point,poly.size(),false);
+ poly.set(edited_point,edited_point_pos);
+ undo_redo->create_action(TTR("Edit Poly"));
+ undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"set_outline",edited_outline,poly);
+ undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"set_outline",edited_outline,pre_move_edit);
+ undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines");
+ undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines");
+ undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+
+ edited_point=-1;
+ return true;
+ }
+ }
+ } if (mb.button_index==BUTTON_RIGHT && mb.pressed && edited_point==-1) {
+
+ int closest_outline=-1;
+ int closest_idx=-1;
+ Vector2 closest_pos;
+ real_t closest_dist=1e10;
+
+ for(int j=0;j<node->get_navigation_polygon()->get_outline_count();j++) {
+
+
+ DVector<Vector2> points=node->get_navigation_polygon()->get_outline(j);
+
+ int pc=points.size();
+ DVector<Vector2>::Read poly=points.read();
+
+ for(int i=0;i<pc;i++) {
+
+
+ Vector2 cp =xform.xform(poly[i]);
+
+ real_t d = cp.distance_to(gpoint);
+ if (d<closest_dist && d<grab_treshold) {
+ closest_dist=d;
+ closest_pos=cp;
+ closest_outline=j;
+ closest_idx=i;
+ }
+ }
+ }
+
+ if (closest_idx>=0) {
+
+
+ DVector<Vector2> poly = node->get_navigation_polygon()->get_outline(closest_outline);
+
+ if (poly.size()>3) {
+ undo_redo->create_action(TTR("Edit Poly (Remove Point)"));
+ undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"set_outline",closest_outline,poly);
+ poly.remove(closest_idx);
+ undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"set_outline",closest_outline,poly);
+ undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines");
+ undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines");
+ undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+ } else {
+
+ undo_redo->create_action(TTR("Remove Poly And Point"));
+ undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"add_outline_at_index",poly,closest_outline);
+ poly.remove(closest_idx);
+ undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"remove_outline",closest_outline);
+ undo_redo->add_do_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines");
+ undo_redo->add_undo_method(node->get_navigation_polygon().ptr(),"make_polygons_from_outlines");
+ undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+
+ }
+ return true;
+ }
+ }
+
+
+
+ } break;
+ }
+
+
+
+ } break;
+ case InputEvent::MOUSE_MOTION: {
+
+ const InputEventMouseMotion &mm=p_event.mouse_motion;
+
+ if (edited_point!=-1 && (wip_active || mm.button_mask&BUTTON_MASK_LEFT)) {
+
+ Vector2 gpoint = Point2(mm.x,mm.y);
+ Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint);
+ cpoint=canvas_item_editor->snap_point(cpoint);
+ edited_point_pos = node->get_global_transform().affine_inverse().xform(cpoint);
+
+ canvas_item_editor->get_viewport_control()->update();
+
+ }
+
+ } break;
+ }
+
+ return false;
+}
+void NavigationPolygonEditor::_canvas_draw() {
+
+ if (!node)
+ return;
+
+ Control *vpc = canvas_item_editor->get_viewport_control();
+ if (node->get_navigation_polygon().is_null())
+ return;
+
+ Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
+ Ref<Texture> handle= get_icon("EditorHandle","EditorIcons");
+
+
+
+ for(int j=-1;j<node->get_navigation_polygon()->get_outline_count();j++) {
+ Vector<Vector2> poly;
+
+ if (wip_active && j==edited_outline) {
+ poly=wip;
+ } else {
+ if (j==-1)
+ continue;
+ poly = Variant(node->get_navigation_polygon()->get_outline(j));
+ }
+
+ for(int i=0;i<poly.size();i++) {
+
+
+ Vector2 p,p2;
+ p = (j==edited_outline && i==edited_point) ? edited_point_pos : poly[i];
+ if (j==edited_outline && ((wip_active && i==poly.size()-1) || (((i+1)%poly.size())==edited_point)))
+ p2=edited_point_pos;
+ else
+ p2 = poly[(i+1)%poly.size()];
+
+ Vector2 point = xform.xform(p);
+ Vector2 next_point = xform.xform(p2);
+
+ Color col=Color(1,0.3,0.1,0.8);
+ vpc->draw_line(point,next_point,col,2);
+ vpc->draw_texture(handle,point-handle->get_size()*0.5);
+ }
+ }
+}
+
+
+
+void NavigationPolygonEditor::edit(Node *p_collision_polygon) {
+
+ if (!canvas_item_editor) {
+ canvas_item_editor=CanvasItemEditor::get_singleton();
+ }
+
+ if (p_collision_polygon) {
+
+ node=p_collision_polygon->cast_to<NavigationPolygonInstance>();
+ if (!canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw"))
+ canvas_item_editor->get_viewport_control()->connect("draw",this,"_canvas_draw");
+ wip.clear();
+ wip_active=false;
+ edited_point=-1;
+ canvas_item_editor->get_viewport_control()->update();
+
+ } else {
+ node=NULL;
+
+ if (canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw"))
+ canvas_item_editor->get_viewport_control()->disconnect("draw",this,"_canvas_draw");
+
+ }
+
+}
+
+void NavigationPolygonEditor::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("_menu_option"),&NavigationPolygonEditor::_menu_option);
+ ObjectTypeDB::bind_method(_MD("_canvas_draw"),&NavigationPolygonEditor::_canvas_draw);
+ ObjectTypeDB::bind_method(_MD("_node_removed"),&NavigationPolygonEditor::_node_removed);
+ ObjectTypeDB::bind_method(_MD("_create_nav"),&NavigationPolygonEditor::_create_nav);
+
+}
+
+NavigationPolygonEditor::NavigationPolygonEditor(EditorNode *p_editor) {
+ node=NULL;
+ canvas_item_editor=NULL;
+ editor=p_editor;
+ undo_redo = editor->get_undo_redo();
+
+ add_child( memnew( VSeparator ));
+ button_create = memnew( ToolButton );
+ add_child(button_create);
+ button_create->connect("pressed",this,"_menu_option",varray(MODE_CREATE));
+ button_create->set_toggle_mode(true);
+ button_create->set_tooltip(TTR("Create a new polygon from scratch."));
+
+ button_edit = memnew( ToolButton );
+ add_child(button_edit);
+ button_edit->connect("pressed",this,"_menu_option",varray(MODE_EDIT));
+ button_edit->set_toggle_mode(true);
+ button_edit->set_tooltip(TTR("Edit existing polygon:")+"\n"+TTR("LMB: Move Point.")+"\n"+TTR("Ctrl+LMB: Split Segment.")+"\n"+TTR("RMB: Erase Point."));
+ create_nav = memnew( ConfirmationDialog );
+ add_child(create_nav);
+ create_nav->get_ok()->set_text(TTR("Create"));
+
+
+ //add_constant_override("separation",0);
+
+#if 0
+ options = memnew( MenuButton );
+ add_child(options);
+ options->set_area_as_parent_rect();
+ options->set_text("Polygon");
+ //options->get_popup()->add_item("Parse BBCode",PARSE_BBCODE);
+ options->get_popup()->connect("item_pressed", this,"_menu_option");
+#endif
+
+ mode = MODE_EDIT;
+ wip_active=false;
+ edited_outline=-1;
+
+}
+
+
+void NavigationPolygonEditorPlugin::edit(Object *p_object) {
+
+ collision_polygon_editor->edit(p_object->cast_to<Node>());
+}
+
+bool NavigationPolygonEditorPlugin::handles(Object *p_object) const {
+
+ return p_object->is_type("NavigationPolygonInstance");
+}
+
+void NavigationPolygonEditorPlugin::make_visible(bool p_visible) {
+
+ if (p_visible) {
+ collision_polygon_editor->show();
+ } else {
+
+ collision_polygon_editor->hide();
+ collision_polygon_editor->edit(NULL);
+ }
+
+}
+
+NavigationPolygonEditorPlugin::NavigationPolygonEditorPlugin(EditorNode *p_node) {
+
+ editor=p_node;
+ collision_polygon_editor = memnew( NavigationPolygonEditor(p_node) );
+ CanvasItemEditor::get_singleton()->add_control_to_menu_panel(collision_polygon_editor);
+
+ collision_polygon_editor->hide();
+
+
+
+}
+
+
+NavigationPolygonEditorPlugin::~NavigationPolygonEditorPlugin()
+{
+}
+
diff --git a/editor/plugins/navigation_polygon_editor_plugin.h b/editor/plugins/navigation_polygon_editor_plugin.h
new file mode 100644
index 000000000..188c636bd
--- /dev/null
+++ b/editor/plugins/navigation_polygon_editor_plugin.h
@@ -0,0 +1,118 @@
+/*************************************************************************/
+/* navigation_polygon_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 NAVIGATIONPOLYGONEDITORPLUGIN_H
+#define NAVIGATIONPOLYGONEDITORPLUGIN_H
+
+
+
+#include "editor/editor_plugin.h"
+#include "editor/editor_node.h"
+#include "scene/2d/navigation_polygon.h"
+#include "scene/gui/tool_button.h"
+#include "scene/gui/button_group.h"
+
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+class CanvasItemEditor;
+
+class NavigationPolygonEditor : public HBoxContainer {
+
+ OBJ_TYPE(NavigationPolygonEditor, HBoxContainer );
+
+ UndoRedo *undo_redo;
+ enum Mode {
+
+ MODE_CREATE,
+ MODE_EDIT,
+
+ };
+
+ Mode mode;
+
+ ToolButton *button_create;
+ ToolButton *button_edit;
+
+ ConfirmationDialog *create_nav;
+
+ CanvasItemEditor *canvas_item_editor;
+ EditorNode *editor;
+ Panel *panel;
+ NavigationPolygonInstance *node;
+ MenuButton *options;
+
+ int edited_outline;
+ int edited_point;
+ Vector2 edited_point_pos;
+ DVector<Vector2> pre_move_edit;
+ Vector<Vector2> wip;
+ bool wip_active;
+
+
+ void _wip_close();
+ void _canvas_draw();
+ void _create_nav();
+
+ void _menu_option(int p_option);
+
+protected:
+ void _notification(int p_what);
+ void _node_removed(Node *p_node);
+ static void _bind_methods();
+public:
+
+ bool forward_input_event(const InputEvent& p_event);
+ void edit(Node *p_collision_polygon);
+ NavigationPolygonEditor(EditorNode *p_editor);
+};
+
+class NavigationPolygonEditorPlugin : public EditorPlugin {
+
+ OBJ_TYPE( NavigationPolygonEditorPlugin, EditorPlugin );
+
+ NavigationPolygonEditor *collision_polygon_editor;
+ EditorNode *editor;
+
+public:
+
+ virtual bool forward_input_event(const InputEvent& p_event) { return collision_polygon_editor->forward_input_event(p_event); }
+
+ virtual String get_name() const { return "NavigationPolygonInstance"; }
+ bool has_main_screen() const { return false; }
+ virtual void edit(Object *p_node);
+ virtual bool handles(Object *p_node) const;
+ virtual void make_visible(bool p_visible);
+
+ NavigationPolygonEditorPlugin(EditorNode *p_node);
+ ~NavigationPolygonEditorPlugin();
+
+};
+
+
+#endif // NAVIGATIONPOLYGONEDITORPLUGIN_H
diff --git a/editor/plugins/particles_2d_editor_plugin.cpp b/editor/plugins/particles_2d_editor_plugin.cpp
new file mode 100644
index 000000000..0f46c7fd5
--- /dev/null
+++ b/editor/plugins/particles_2d_editor_plugin.cpp
@@ -0,0 +1,199 @@
+/*************************************************************************/
+/* particles_2d_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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"
+#include "scene/gui/separator.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) {
+
+ toolbar->show();
+ } else {
+
+ toolbar->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(TTR("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(TTR("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(TTR("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(TTR("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_TREE) {
+
+ menu->get_popup()->connect("item_pressed",this,"_menu_callback");
+ menu->set_icon(menu->get_popup()->get_icon("Particles2D","EditorIcons"));
+ 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();
+
+ toolbar = memnew( HBoxContainer );
+ add_control_to_container(CONTAINER_CANVAS_EDITOR_MENU, toolbar);
+ toolbar->hide();
+
+ toolbar->add_child( memnew( VSeparator ) );
+
+ menu = memnew( MenuButton );
+ menu->get_popup()->add_item(TTR("Load Emission Mask"),MENU_LOAD_EMISSION_MASK);
+ menu->get_popup()->add_item(TTR("Clear Emission Mask"),MENU_CLEAR_EMISSION_MASK);
+ menu->set_text("Particles");
+ toolbar->add_child(menu);
+
+ file = memnew( EditorFileDialog );
+ List<String> ext;
+ ImageLoader::get_recognized_extensions(&ext);
+ for(List<String>::Element *E=ext.front();E;E=E->next()) {
+ file->add_filter("*."+E->get()+"; "+E->get().to_upper());
+ }
+ file->set_mode(EditorFileDialog::MODE_OPEN_FILE);
+ toolbar->add_child(file);
+
+ epoints = memnew( SpinBox );
+ epoints->set_min(1);
+ epoints->set_max(8192);
+ epoints->set_step(1);
+ epoints->set_val(512);
+ file->get_vbox()->add_margin_child(TTR("Generated Point Count:"),epoints);
+}
+
+Particles2DEditorPlugin::~Particles2DEditorPlugin()
+{
+}
+
diff --git a/editor/plugins/particles_2d_editor_plugin.h b/editor/plugins/particles_2d_editor_plugin.h
new file mode 100644
index 000000000..dc798d4b5
--- /dev/null
+++ b/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-2017 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 "editor/editor_plugin.h"
+#include "editor/editor_node.h"
+#include "scene/2d/collision_polygon_2d.h"
+
+#include "scene/gui/box_container.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
+ };
+
+ Particles2D *particles;
+
+ EditorFileDialog *file;
+ EditorNode *editor;
+
+ HBoxContainer *toolbar;
+ MenuButton *menu;
+
+ 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/editor/plugins/particles_editor_plugin.cpp b/editor/plugins/particles_editor_plugin.cpp
new file mode 100644
index 000000000..e3484602d
--- /dev/null
+++ b/editor/plugins/particles_editor_plugin.cpp
@@ -0,0 +1,458 @@
+/*************************************************************************/
+/* particles_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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"
+#include "editor/plugins/spatial_editor_plugin.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(TTR("Node does not contain geometry."));
+ err_dialog->popup_centered_minsize();
+ return;
+ }
+
+ geometry = vi->get_faces(VisualInstance::FACES_SOLID);
+
+ if (geometry.size()==0) {
+
+ err_dialog->set_text(TTR("Node does not contain geometry (faces)."));
+ err_dialog->popup_centered_minsize();
+ 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_TREE) {
+ options->set_icon(options->get_popup()->get_icon("Particles","EditorIcons"));
+
+ }
+}
+
+
+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(TTR("Faces contain no area!"));
+ err_dialog->popup_centered_minsize();
+ 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(TTR("No faces!"));
+ err_dialog->popup_centered_minsize();
+ 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 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() {
+
+ particles_editor_hb = memnew ( HBoxContainer );
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(particles_editor_hb);
+ options = memnew( MenuButton );
+ particles_editor_hb->add_child(options);
+ particles_editor_hb->hide();
+
+ options->set_text("Particles");
+ options->get_popup()->add_item(TTR("Generate AABB"),MENU_OPTION_GENERATE_AABB);
+ options->get_popup()->add_separator();
+ options->get_popup()->add_item(TTR("Create Emitter From Mesh"),MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_MESH);
+ options->get_popup()->add_item(TTR("Create Emitter From Node"),MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE);
+ options->get_popup()->add_item(TTR("Clear Emitter"),MENU_OPTION_CLEAR_EMISSION_VOLUME);
+
+ options->get_popup()->connect("item_pressed", this,"_menu_option");
+
+ emission_dialog = memnew( ConfirmationDialog );
+ emission_dialog->set_title(TTR("Create Emitter"));
+ add_child(emission_dialog);
+ Label *l = memnew(Label);
+ l->set_pos(Point2(5,5));
+ l->set_text(TTR("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(TTR("Create"));
+ emission_dialog->connect("confirmed",this,"_generate_emission_points");
+
+ l = memnew(Label);
+ l->set_pos(Point2(5,50));
+ l->set_text(TTR("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(TTR("Surface"));
+ emission_fill->add_item(TTR("Volume"));
+ emission_dialog->add_child(emission_fill);
+
+ err_dialog = memnew( ConfirmationDialog );
+ //err_dialog->get_cancel()->hide();
+ add_child(err_dialog);
+
+
+ emission_file_dialog = memnew( EditorFileDialog );
+ 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(EditorFileDialog::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();
+ particles_editor->particles_editor_hb->show();
+ } else {
+ particles_editor->particles_editor_hb->hide();
+ 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->hide();
+}
+
+
+ParticlesEditorPlugin::~ParticlesEditorPlugin()
+{
+}
+
+
diff --git a/editor/plugins/particles_editor_plugin.h b/editor/plugins/particles_editor_plugin.h
new file mode 100644
index 000000000..6f20958c4
--- /dev/null
+++ b/editor/plugins/particles_editor_plugin.h
@@ -0,0 +1,115 @@
+/*************************************************************************/
+/* particles_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "editor/editor_plugin.h"
+#include "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;
+ HBoxContainer *particles_editor_hb;
+ Particles *node;
+
+
+ EditorFileDialog *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();
+
+friend class ParticlesEditorPlugin;
+
+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/editor/plugins/path_2d_editor_plugin.cpp b/editor/plugins/path_2d_editor_plugin.cpp
new file mode 100644
index 000000000..6472b1fac
--- /dev/null
+++ b/editor/plugins/path_2d_editor_plugin.cpp
@@ -0,0 +1,718 @@
+/*************************************************************************/
+/* path_2d_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "editor/editor_settings.h"
+#include "os/keyboard.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();
+ }
+
+}
+
+
+bool Path2DEditor::forward_input_event(const InputEvent& p_event) {
+
+ if (!node)
+ return false;
+
+ if (!node->is_visible())
+ 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 = !mb.mod.alt? canvas_item_editor->snap_point(xform.affine_inverse().xform(gpoint))
+ : node->get_global_transform().affine_inverse().xform( canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().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 && action==ACTION_NONE ) {
+
+ 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.button_index==BUTTON_LEFT && !mb.mod.shift && mode==MODE_EDIT) {
+
+ action=ACTION_MOVING_POINT;
+ action_point=i;
+ moving_from=curve->get_point_pos(i);
+ moving_screen_from=gpoint;
+ return true;
+ } else if ((mb.button_index==BUTTON_RIGHT && mode==MODE_EDIT) || (mb.button_index==BUTTON_LEFT && mode==MODE_DELETE)) {
+
+ undo_redo->create_action(TTR("Remove Point from Curve"));
+ undo_redo->add_do_method(curve.ptr(),"remove_point",i);
+ undo_redo->add_undo_method(curve.ptr(),"add_point",curve->get_point_pos(i),curve->get_point_in(i),curve->get_point_out(i),i);
+ undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+ return true;
+ } else
+ pointunder=true;
+ }
+ }
+
+ if (mb.button_index==BUTTON_LEFT && 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 && (mode == MODE_EDIT || mode==MODE_EDIT_CURVE) ) {
+
+ action=ACTION_MOVING_OUT;
+ action_point=i;
+ moving_from=curve->get_point_out(i);
+ moving_screen_from=gpoint;
+ return true;
+ }
+ }
+
+ if (mb.button_index==BUTTON_LEFT && i>0) {
+ Point2 p = xform.xform( curve->get_point_pos(i)+curve->get_point_in(i) );
+ if (gpoint.distance_to(p) < grab_treshold && (mode == MODE_EDIT || mode==MODE_EDIT_CURVE)) {
+
+ 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.button_index==BUTTON_LEFT && ((mb.mod.command && mode == MODE_EDIT) || mode == MODE_CREATE)) {
+
+ Ref<Curve2D> curve = node->get_curve();
+
+ undo_redo->create_action(TTR("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->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"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;
+
+ canvas_item_editor->get_viewport_control()->update();
+
+ 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.affine_inverse().basis_xform(gpoint - moving_screen_from);
+ switch(action) {
+
+ case ACTION_MOVING_POINT: {
+
+
+ undo_redo->create_action(TTR("Move Point in Curve"));
+ undo_redo->add_do_method(curve.ptr(),"set_point_pos",action_point,cpoint);
+ undo_redo->add_undo_method(curve.ptr(),"set_point_pos",action_point,moving_from);
+ undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+
+ } break;
+ case ACTION_MOVING_IN: {
+
+ undo_redo->create_action(TTR("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->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+
+ } break;
+ case ACTION_MOVING_OUT: {
+
+ undo_redo->create_action(TTR("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->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"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( canvas_item_editor->snap_point(cpoint) );
+ wip_active=true;
+ edited_point_pos=canvas_item_editor->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( canvas_item_editor->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(TTR("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->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+ return true;
+ }
+
+ //search edges
+ int closest_idx=-1;
+ Vector2 closest_pos;
+ real_t closest_dist=1e10;
+ for(int i=0;i<poly.size();i++) {
+
+ Vector2 points[2] ={ xform.xform(poly[i]),
+ xform.xform(poly[(i+1)%poly.size()]) };
+
+ Vector2 cp = Geometry::get_closest_point_to_segment_2d(gpoint,points);
+ if (cp.distance_squared_to(points[0])<CMP_EPSILON2 || cp.distance_squared_to(points[1])<CMP_EPSILON2)
+ continue; //not valid to reuse point
+
+ real_t d = cp.distance_to(gpoint);
+ if (d<closest_dist && d<grab_treshold) {
+ closest_dist=d;
+ closest_pos=cp;
+ closest_idx=i;
+ }
+
+
+ }
+
+ if (closest_idx>=0) {
+
+ pre_move_edit=poly;
+ poly.insert(closest_idx+1,canvas_item_editor->snap_point(xform.affine_inverse().xform(closest_pos)));
+ edited_point=closest_idx+1;
+ edited_point_pos=canvas_item_editor->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(TTR("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->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+
+ edited_point=-1;
+ return true;
+ }
+ }
+ } if (mb.button_index==BUTTON_RIGHT && mb.pressed && edited_point==-1) {
+
+
+
+ int closest_idx=-1;
+ Vector2 closest_pos;
+ real_t closest_dist=1e10;
+ for(int i=0;i<poly.size();i++) {
+
+ Vector2 cp =xform.xform(poly[i]);
+
+ real_t d = cp.distance_to(gpoint);
+ if (d<closest_dist && d<grab_treshold) {
+ closest_dist=d;
+ closest_pos=cp;
+ closest_idx=i;
+ }
+
+ }
+
+ if (closest_idx>=0) {
+
+
+ undo_redo->create_action(TTR("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->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"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);
+ Vector2 cpoint = !mm.mod.alt? canvas_item_editor->snap_point(xform.affine_inverse().xform(gpoint))
+ : node->get_global_transform().affine_inverse().xform( canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint)) );
+
+ Ref<Curve2D> curve = node->get_curve();
+
+ Vector2 new_pos = moving_from + xform.affine_inverse().basis_xform(gpoint - moving_screen_from);
+
+ switch(action) {
+
+ case ACTION_MOVING_POINT: {
+
+ curve->set_point_pos(action_point,cpoint);
+ } 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->get_viewport_control()->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 = canvas_item_editor->snap_point(xform.affine_inverse().xform(gpoint));
+ canvas_item_editor->update();
+
+ }
+#endif
+ } break;
+ }
+
+ return false;
+}
+void Path2DEditor::_canvas_draw() {
+
+ if (!node)
+ return ;
+
+ if (!node->is_visible())
+ 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");
+ Size2 handle_size = handle->get_size();
+
+ Ref<Curve2D> curve = node->get_curve();
+
+ int len = curve->get_point_count();
+ Control *vpc = canvas_item_editor->get_viewport_control();
+
+
+ for(int i=0;i<len;i++) {
+
+
+ Vector2 point = xform.xform(curve->get_point_pos(i));
+ vpc->draw_texture_rect(handle,Rect2(point-handle_size*0.5,handle_size),false,Color(1,1,1,1));
+
+ if (i<len-1) {
+ Vector2 pointout = xform.xform(curve->get_point_pos(i)+curve->get_point_out(i));
+ vpc->draw_line(point,pointout,Color(0.5,0.5,1.0,0.8),1.0);
+ vpc->draw_texture_rect(handle, Rect2(pointout-handle_size*0.5,handle_size),false,Color(1,0.5,1,0.3));
+ }
+
+ if (i>0) {
+ Vector2 pointin = xform.xform(curve->get_point_pos(i)+curve->get_point_in(i));
+ vpc->draw_line(point,pointin,Color(0.5,0.5,1.0,0.8),1.0);
+ vpc->draw_texture_rect(handle, Rect2(pointin-handle_size*0.5,handle_size),false,Color(1,0.5,1,0.3));
+ }
+
+ }
+
+}
+
+void Path2DEditor::_node_visibility_changed() {
+ if (!node)
+ return;
+
+ canvas_item_editor->get_viewport_control()->update();
+}
+
+void Path2DEditor::edit(Node *p_path2d) {
+
+ if (!canvas_item_editor) {
+ canvas_item_editor=CanvasItemEditor::get_singleton();
+ }
+
+ if (p_path2d) {
+
+ node=p_path2d->cast_to<Path2D>();
+ if (!canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw"))
+ canvas_item_editor->get_viewport_control()->connect("draw",this,"_canvas_draw");
+ if (!node->is_connected("visibility_changed", this, "_node_visibility_changed"))
+ node->connect("visibility_changed", this, "_node_visibility_changed");
+
+
+ } else {
+
+ if (canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw"))
+ canvas_item_editor->get_viewport_control()->disconnect("draw",this,"_canvas_draw");
+
+ // node may have been deleted at this point
+ if (node && node->is_connected("visibility_changed", this, "_node_visibility_changed"))
+ node->disconnect("visibility_changed", this, "_node_visibility_changed");
+ node=NULL;
+ }
+
+}
+
+void Path2DEditor::_bind_methods() {
+
+ //ObjectTypeDB::bind_method(_MD("_menu_option"),&Path2DEditor::_menu_option);
+ ObjectTypeDB::bind_method(_MD("_canvas_draw"),&Path2DEditor::_canvas_draw);
+ ObjectTypeDB::bind_method(_MD("_node_visibility_changed"),&Path2DEditor::_node_visibility_changed);
+ ObjectTypeDB::bind_method(_MD("_mode_selected"),&Path2DEditor::_mode_selected);
+}
+
+void Path2DEditor::_mode_selected(int p_mode) {
+
+ if (p_mode==MODE_CREATE) {
+
+ curve_create->set_pressed(true);
+ curve_edit->set_pressed(false);
+ curve_edit_curve->set_pressed(false);
+ curve_del->set_pressed(false);
+ } else if (p_mode==MODE_EDIT) {
+
+ curve_create->set_pressed(false);
+ curve_edit->set_pressed(true);
+ curve_edit_curve->set_pressed(false);
+ curve_del->set_pressed(false);
+ } else if (p_mode==MODE_EDIT_CURVE) {
+
+ curve_create->set_pressed(false);
+ curve_edit->set_pressed(false);
+ curve_edit_curve->set_pressed(true);
+ curve_del->set_pressed(false);
+ } else if (p_mode==MODE_DELETE) {
+
+ curve_create->set_pressed(false);
+ curve_edit->set_pressed(false);
+ curve_edit_curve->set_pressed(false);
+ curve_del->set_pressed(true);
+ } else if (p_mode==ACTION_CLOSE) {
+
+ //?
+
+ if (!node->get_curve().is_valid())
+ return ;
+ if (node->get_curve()->get_point_count()<3)
+ return;
+
+ Vector2 begin = node->get_curve()->get_point_pos(0);
+ Vector2 end = node->get_curve()->get_point_pos( node->get_curve()->get_point_count() -1 );
+ if (begin.distance_to(end)<CMP_EPSILON)
+ return;
+
+ undo_redo->create_action(TTR("Remove Point from Curve"));
+ undo_redo->add_do_method(node->get_curve().ptr(),"add_point",begin);
+ undo_redo->add_undo_method(node->get_curve().ptr(),"remove_point",node->get_curve()->get_point_count());
+ undo_redo->add_do_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+ return;
+ }
+
+ mode=Mode(p_mode);
+
+}
+
+Path2DEditor::Path2DEditor(EditorNode *p_editor) {
+
+ canvas_item_editor=NULL;
+ editor=p_editor;
+ undo_redo = editor->get_undo_redo();
+
+ mode=MODE_EDIT;
+
+ 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
+
+ base_hb = memnew( HBoxContainer );
+ CanvasItemEditor::get_singleton()->add_control_to_menu_panel(base_hb);
+
+ sep = memnew( VSeparator);
+ base_hb->add_child(sep);
+ curve_edit = memnew( ToolButton );
+ curve_edit->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("CurveEdit","EditorIcons"));
+ curve_edit->set_toggle_mode(true);
+ curve_edit->set_focus_mode(Control::FOCUS_NONE);
+ curve_edit->set_tooltip(TTR("Select Points")+"\n"+TTR("Shift+Drag: Select Control Points")+"\n"+keycode_get_string(KEY_MASK_CMD)+TTR("Click: Add Point")+"\n"+TTR("Right Click: Delete Point"));
+ curve_edit->connect("pressed",this,"_mode_selected",varray(MODE_EDIT));
+ base_hb->add_child(curve_edit);
+ curve_edit_curve = memnew( ToolButton );
+ curve_edit_curve->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("CurveCurve","EditorIcons"));
+ curve_edit_curve->set_toggle_mode(true);
+ curve_edit_curve->set_focus_mode(Control::FOCUS_NONE);
+ curve_edit_curve->set_tooltip(TTR("Select Control Points (Shift+Drag)"));
+ curve_edit_curve->connect("pressed",this,"_mode_selected",varray(MODE_EDIT_CURVE));
+ base_hb->add_child(curve_edit_curve);
+ curve_create = memnew( ToolButton );
+ curve_create->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("CurveCreate","EditorIcons"));
+ curve_create->set_toggle_mode(true);
+ curve_create->set_focus_mode(Control::FOCUS_NONE);
+ curve_create->set_tooltip(TTR("Add Point (in empty space)")+"\n"+TTR("Split Segment (in curve)"));
+ curve_create->connect("pressed",this,"_mode_selected",varray(MODE_CREATE));
+ base_hb->add_child(curve_create);
+ curve_del = memnew( ToolButton );
+ curve_del->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("CurveDelete","EditorIcons"));
+ curve_del->set_toggle_mode(true);
+ curve_del->set_focus_mode(Control::FOCUS_NONE);
+ curve_del->set_tooltip(TTR("Delete Point"));
+ curve_del->connect("pressed",this,"_mode_selected",varray(MODE_DELETE));
+ base_hb->add_child(curve_del);
+ curve_close = memnew( ToolButton );
+ curve_close->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("CurveClose","EditorIcons"));
+ curve_close->set_focus_mode(Control::FOCUS_NONE);
+ curve_close->set_tooltip(TTR("Close Curve"));
+ curve_close->connect("pressed",this,"_mode_selected",varray(ACTION_CLOSE));
+ base_hb->add_child(curve_close);
+ base_hb->hide();
+
+
+
+ curve_edit->set_pressed(true);
+
+
+}
+
+
+void Path2DEditorPlugin::edit(Object *p_object) {
+
+ path2d_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) {
+ path2d_editor->show();
+ path2d_editor->base_hb->show();
+
+ } else {
+
+ path2d_editor->hide();
+ path2d_editor->base_hb->hide();
+ path2d_editor->edit(NULL);
+ }
+
+}
+
+Path2DEditorPlugin::Path2DEditorPlugin(EditorNode *p_node) {
+
+ editor=p_node;
+ path2d_editor = memnew( Path2DEditor(p_node) );
+ CanvasItemEditor::get_singleton()->add_control_to_menu_panel(path2d_editor);
+ path2d_editor->hide();
+
+
+}
+
+
+Path2DEditorPlugin::~Path2DEditorPlugin()
+{
+}
+
diff --git a/editor/plugins/path_2d_editor_plugin.h b/editor/plugins/path_2d_editor_plugin.h
new file mode 100644
index 000000000..5eb79ff91
--- /dev/null
+++ b/editor/plugins/path_2d_editor_plugin.h
@@ -0,0 +1,126 @@
+/*************************************************************************/
+/* path_2d_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "editor/editor_plugin.h"
+#include "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 HBoxContainer {
+
+ OBJ_TYPE(Path2DEditor, HBoxContainer);
+
+ UndoRedo *undo_redo;
+
+ CanvasItemEditor *canvas_item_editor;
+ EditorNode *editor;
+ Panel *panel;
+ Path2D *node;
+
+ HBoxContainer *base_hb;
+ Separator *sep;
+
+ enum Mode {
+ MODE_CREATE,
+ MODE_EDIT,
+ MODE_EDIT_CURVE,
+ MODE_DELETE,
+ ACTION_CLOSE
+ };
+
+ Mode mode;
+ ToolButton *curve_create;
+ ToolButton *curve_edit;
+ ToolButton *curve_edit_curve;
+ ToolButton *curve_del;
+ ToolButton *curve_close;
+
+ 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 _mode_selected(int p_mode);
+
+ void _canvas_draw();
+ void _node_visibility_changed();
+friend class Path2DEditorPlugin;
+protected:
+ void _notification(int p_what);
+ void _node_removed(Node *p_node);
+ static void _bind_methods();
+public:
+
+ bool forward_input_event(const InputEvent& p_event);
+ void edit(Node *p_path2d);
+ Path2DEditor(EditorNode *p_editor);
+};
+
+class Path2DEditorPlugin : public EditorPlugin {
+
+ OBJ_TYPE( Path2DEditorPlugin, EditorPlugin );
+
+ Path2DEditor *path2d_editor;
+ EditorNode *editor;
+
+public:
+
+ virtual bool forward_input_event(const InputEvent& p_event) { return path2d_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/editor/plugins/path_editor_plugin.cpp b/editor/plugins/path_editor_plugin.cpp
new file mode 100644
index 000000000..4135f7868
--- /dev/null
+++ b/editor/plugins/path_editor_plugin.cpp
@@ -0,0 +1,601 @@
+/*************************************************************************/
+/* path_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 TTR("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 = TTR("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)) {
+
+ if(SpatialEditor::get_singleton()->is_snap_enabled())
+ {
+ float snap = SpatialEditor::get_singleton()->get_translate_snap();
+ inters.snap(snap);
+ }
+
+ 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(TTR("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(TTR("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(TTR("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);
+
+
+
+}
+
+Ref<SpatialEditorGizmo> PathEditorPlugin::create_spatial_gizmo(Spatial* p_spatial) {
+
+
+ if (p_spatial->cast_to<Path>()) {
+
+ return memnew( PathSpatialGizmo(p_spatial->cast_to<Path>()));
+ }
+
+ return Ref<SpatialEditorGizmo>();
+}
+
+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(TTR("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(TTR("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(TTR("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_TREE) {
+
+ 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(EditorNode::get_singleton()->get_gui_base()->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(TTR("Select Points")+"\n"+TTR("Shift+Drag: Select Control Points")+"\n"+keycode_get_string(KEY_MASK_CMD)+TTR("Click: Add Point")+"\n"+TTR("Right Click: Delete Point"));
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(curve_edit);
+ curve_create = memnew( ToolButton );
+ curve_create->set_icon(EditorNode::get_singleton()->get_gui_base()->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(TTR("Add Point (in empty space)")+"\n"+TTR("Split Segment (in curve)"));
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(curve_create);
+ curve_del = memnew( ToolButton );
+ curve_del->set_icon(EditorNode::get_singleton()->get_gui_base()->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(TTR("Delete Point"));
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(curve_del);
+ curve_close = memnew( ToolButton );
+ curve_close->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("CurveClose","EditorIcons"));
+ curve_close->hide();
+ curve_close->set_focus_mode(Control::FOCUS_NONE);
+ curve_close->set_tooltip(TTR("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/editor/plugins/path_editor_plugin.h b/editor/plugins/path_editor_plugin.h
new file mode 100644
index 000000000..77f39befb
--- /dev/null
+++ b/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-2017 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 "editor/spatial_editor_gizmos.h"
+#include "scene/3d/path.h"
+class PathSpatialGizmo : public EditorSpatialGizmo {
+
+ OBJ_TYPE(PathSpatialGizmo,EditorSpatialGizmo);
+
+ 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 Ref<SpatialEditorGizmo> 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/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp
new file mode 100644
index 000000000..e823347ff
--- /dev/null
+++ b/editor/plugins/polygon_2d_editor_plugin.cpp
@@ -0,0 +1,1027 @@
+/*************************************************************************/
+/* polygon_2d_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "polygon_2d_editor_plugin.h"
+#include "canvas_item_editor_plugin.h"
+#include "os/file_access.h"
+#include "editor/editor_settings.h"
+#include "os/keyboard.h"
+#include "os/input.h"
+
+void Polygon2DEditor::_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);
+ button_uv->set_icon( get_icon("Uv","EditorIcons"));
+
+ uv_button[UV_MODE_EDIT_POINT]->set_icon(get_icon("ToolSelect","EditorIcons"));
+ uv_button[UV_MODE_MOVE]->set_icon(get_icon("ToolMove","EditorIcons"));
+ uv_button[UV_MODE_ROTATE]->set_icon(get_icon("ToolRotate","EditorIcons"));
+ uv_button[UV_MODE_SCALE]->set_icon(get_icon("ToolScale","EditorIcons"));
+
+ b_snap_grid->set_icon( get_icon("Grid", "EditorIcons"));
+ b_snap_enable->set_icon( get_icon("Snap", "EditorIcons"));
+ uv_icon_zoom->set_texture( get_icon("Zoom", "EditorIcons"));
+
+ get_tree()->connect("node_removed", this, "_node_removed");
+
+ } break;
+ case NOTIFICATION_FIXED_PROCESS: {
+
+
+ } break;
+ }
+
+}
+void Polygon2DEditor::_node_removed(Node *p_node) {
+
+ if(p_node==node) {
+ edit(NULL);
+ hide();
+
+ canvas_item_editor->get_viewport_control()->update();
+ }
+
+}
+
+
+void Polygon2DEditor::_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;
+ case MODE_EDIT_UV: {
+
+ if (node->get_texture().is_null()) {
+
+ error->set_text("No texture in this polygon.\nSet a texture to be able to edit UV.");
+ error->popup_centered_minsize();
+ return;
+ }
+
+
+ DVector<Vector2> points = node->get_polygon();
+ DVector<Vector2> uvs = node->get_uv();
+ if (uvs.size()!=points.size()) {
+ undo_redo->create_action(TTR("Create UV Map"));
+ undo_redo->add_do_method(node,"set_uv",points);
+ undo_redo->add_undo_method(node,"set_uv",uvs);
+ undo_redo->add_do_method(uv_edit_draw,"update");
+ undo_redo->add_undo_method(uv_edit_draw,"update");
+ undo_redo->commit_action();
+
+ }
+
+
+ uv_edit->popup_centered_ratio(0.85);
+ } break;
+ case UVEDIT_POLYGON_TO_UV: {
+
+ DVector<Vector2> points = node->get_polygon();
+ if (points.size()==0)
+ break;
+ DVector<Vector2> uvs = node->get_uv();
+ undo_redo->create_action(TTR("Create UV Map"));
+ undo_redo->add_do_method(node,"set_uv",points);
+ undo_redo->add_undo_method(node,"set_uv",uvs);
+ undo_redo->add_do_method(uv_edit_draw,"update");
+ undo_redo->add_undo_method(uv_edit_draw,"update");
+ undo_redo->commit_action();
+
+
+ } break;
+ case UVEDIT_UV_TO_POLYGON: {
+
+ DVector<Vector2> points = node->get_polygon();
+ DVector<Vector2> uvs = node->get_uv();
+ if (uvs.size()==0)
+ break;
+
+ undo_redo->create_action(TTR("Create UV Map"));
+ undo_redo->add_do_method(node,"set_polygon",uvs);
+ undo_redo->add_undo_method(node,"set_polygon",points);
+ undo_redo->add_do_method(uv_edit_draw,"update");
+ undo_redo->add_undo_method(uv_edit_draw,"update");
+ undo_redo->commit_action();
+
+ } break;
+ case UVEDIT_UV_CLEAR: {
+
+ DVector<Vector2> uvs = node->get_uv();
+ if (uvs.size()==0)
+ break;
+ undo_redo->create_action(TTR("Create UV Map"));
+ undo_redo->add_do_method(node,"set_uv",DVector<Vector2>());
+ undo_redo->add_undo_method(node,"set_uv",uvs);
+ undo_redo->add_do_method(uv_edit_draw,"update");
+ undo_redo->add_undo_method(uv_edit_draw,"update");
+ undo_redo->commit_action();
+
+ } break;
+
+
+ }
+}
+
+void Polygon2DEditor::_set_use_snap(bool p_use)
+{
+ use_snap=p_use;
+}
+
+void Polygon2DEditor::_set_show_grid(bool p_show)
+{
+ snap_show_grid=p_show;
+ uv_edit_draw->update();
+}
+
+void Polygon2DEditor::_set_snap_off_x(float p_val)
+{
+ snap_offset.x=p_val;
+ uv_edit_draw->update();
+}
+
+void Polygon2DEditor::_set_snap_off_y(float p_val)
+{
+ snap_offset.y=p_val;
+ uv_edit_draw->update();
+}
+
+void Polygon2DEditor::_set_snap_step_x(float p_val)
+{
+ snap_step.x=p_val;
+ uv_edit_draw->update();
+}
+
+void Polygon2DEditor::_set_snap_step_y(float p_val)
+{
+ snap_step.y=p_val;
+ uv_edit_draw->update();
+}
+
+void Polygon2DEditor::_wip_close() {
+
+ undo_redo->create_action(TTR("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->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+ wip.clear();
+ wip_active=false;
+ mode=MODE_EDIT;
+ button_edit->set_pressed(true);
+ button_create->set_pressed(false);
+ edited_point=-1;
+}
+
+bool Polygon2DEditor::forward_input_event(const InputEvent& p_event) {
+
+ if (node==NULL)
+ return false;
+
+ switch(p_event.type) {
+
+ case InputEvent::MOUSE_BUTTON: {
+
+ const InputEventMouseButton &mb=p_event.mouse_button;
+
+ Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
+
+
+ Vector2 gpoint = Point2(mb.x,mb.y);
+ Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint);
+ cpoint=canvas_item_editor->snap_point(cpoint);
+ cpoint = node->get_global_transform().affine_inverse().xform(cpoint);
+
+
+ Vector<Vector2> poly = Variant(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( cpoint-node->get_offset() );
+ wip_active=true;
+ edited_point_pos=cpoint;
+ canvas_item_editor->get_viewport_control()->update();
+ edited_point=1;
+ return true;
+ } else {
+
+
+ if (wip.size()>1 && xform.xform(wip[0]+node->get_offset()).distance_to(gpoint)<grab_treshold) {
+ //wip closed
+ _wip_close();
+
+ return true;
+ } else {
+
+ wip.push_back( cpoint-node->get_offset() );
+ edited_point=wip.size();
+ canvas_item_editor->get_viewport_control()->update();
+ return true;
+
+ //add wip point
+ }
+ }
+ } else if (mb.button_index==BUTTON_RIGHT && mb.pressed && wip_active) {
+ _wip_close();
+ }
+
+
+
+ } break;
+
+ case MODE_EDIT: {
+
+ if (mb.button_index==BUTTON_LEFT) {
+ if (mb.pressed) {
+
+ if (mb.mod.control) {
+
+
+ if (poly.size() < 3) {
+
+ undo_redo->create_action(TTR("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->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+ return true;
+ }
+
+ //search edges
+ int closest_idx=-1;
+ Vector2 closest_pos;
+ real_t closest_dist=1e10;
+ for(int i=0;i<poly.size();i++) {
+
+ Vector2 points[2] ={ xform.xform(poly[i]+node->get_offset()),
+ xform.xform(poly[(i+1)%poly.size()]+node->get_offset()) };
+
+ Vector2 cp = Geometry::get_closest_point_to_segment_2d(gpoint,points);
+ if (cp.distance_squared_to(points[0])<CMP_EPSILON2 || cp.distance_squared_to(points[1])<CMP_EPSILON2)
+ continue; //not valid to reuse point
+
+ real_t d = cp.distance_to(gpoint);
+ if (d<closest_dist && d<grab_treshold) {
+ closest_dist=d;
+ closest_pos=cp;
+ closest_idx=i;
+ }
+
+
+ }
+
+ if (closest_idx>=0) {
+
+ pre_move_edit=poly;
+ poly.insert(closest_idx+1,xform.affine_inverse().xform(closest_pos)-node->get_offset());
+ edited_point=closest_idx+1;
+ edited_point_pos=xform.affine_inverse().xform(closest_pos);
+ node->set_polygon(Variant(poly));
+ canvas_item_editor->get_viewport_control()->update();
+ return true;
+ }
+ } else {
+
+ //look for points to move
+
+ int closest_idx=-1;
+ Vector2 closest_pos;
+ real_t closest_dist=1e10;
+ for(int i=0;i<poly.size();i++) {
+
+ Vector2 cp =xform.xform(poly[i]+node->get_offset());
+
+ real_t d = cp.distance_to(gpoint);
+ if (d<closest_dist && d<grab_treshold) {
+ closest_dist=d;
+ closest_pos=cp;
+ closest_idx=i;
+ }
+
+ }
+
+ if (closest_idx>=0) {
+
+ pre_move_edit=poly;
+ edited_point=closest_idx;
+ edited_point_pos=xform.affine_inverse().xform(closest_pos);
+ canvas_item_editor->get_viewport_control()->update();
+ return true;
+ }
+ }
+ } else {
+
+ if (edited_point!=-1) {
+
+ //apply
+
+ ERR_FAIL_INDEX_V(edited_point,poly.size(),false);
+ poly[edited_point]=edited_point_pos-node->get_offset();
+ undo_redo->create_action(TTR("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->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+
+ edited_point=-1;
+ return true;
+ }
+ }
+ } if (mb.button_index==BUTTON_RIGHT && mb.pressed && edited_point==-1) {
+
+
+
+ int closest_idx=-1;
+ Vector2 closest_pos;
+ real_t closest_dist=1e10;
+ for(int i=0;i<poly.size();i++) {
+
+ Vector2 cp =xform.xform(poly[i]+node->get_offset());
+
+ 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(TTR("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->get_viewport_control(),"update");
+ undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(),"update");
+ undo_redo->commit_action();
+ return true;
+ }
+
+ }
+
+
+
+ } break;
+ }
+
+
+
+ } break;
+ case InputEvent::MOUSE_MOTION: {
+
+ const InputEventMouseMotion &mm=p_event.mouse_motion;
+
+ if (edited_point!=-1 && (wip_active || mm.button_mask&BUTTON_MASK_LEFT)) {
+
+ Vector2 gpoint = Point2(mm.x,mm.y);
+ Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint);
+ cpoint=canvas_item_editor->snap_point(cpoint);
+ edited_point_pos = node->get_global_transform().affine_inverse().xform(cpoint);
+
+ canvas_item_editor->get_viewport_control()->update();
+
+ }
+
+ } break;
+ }
+
+ return false;
+}
+void Polygon2DEditor::_canvas_draw() {
+
+ if (!node)
+ return;
+
+ Control *vpc = canvas_item_editor->get_viewport_control();
+
+ Vector<Vector2> poly;
+
+ if (wip_active)
+ poly=wip;
+ else
+ poly=Variant(node->get_polygon());
+
+
+ Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
+ Ref<Texture> handle= get_icon("EditorHandle","EditorIcons");
+
+ for(int i=0;i<poly.size();i++) {
+
+
+ Vector2 p,p2;
+ p = i==edited_point ? edited_point_pos : (poly[i]+node->get_offset());
+ if ((wip_active && i==poly.size()-1) || (((i+1)%poly.size())==edited_point))
+ p2=edited_point_pos;
+ else
+ p2 = poly[(i+1)%poly.size()]+node->get_offset();
+
+ Vector2 point = xform.xform(p);
+ Vector2 next_point = xform.xform(p2);
+
+ Color col=Color(1,0.3,0.1,0.8);
+ vpc->draw_line(point,next_point,col,2);
+ vpc->draw_texture(handle,point-handle->get_size()*0.5);
+ }
+}
+
+
+void Polygon2DEditor::_uv_mode(int p_mode) {
+
+
+ uv_mode=UVMode(p_mode);
+ for(int i=0;i<UV_MODE_MAX;i++) {
+ uv_button[i]->set_pressed(p_mode==i);
+ }
+}
+
+
+void Polygon2DEditor::_uv_input(const InputEvent& p_input) {
+
+
+ Matrix32 mtx;
+ mtx.elements[2]=-uv_draw_ofs;
+ mtx.scale_basis(Vector2(uv_draw_zoom,uv_draw_zoom));
+
+ if (p_input.type==InputEvent::MOUSE_BUTTON) {
+
+
+ const InputEventMouseButton &mb=p_input.mouse_button;
+
+ if (mb.button_index==BUTTON_LEFT) {
+
+
+ if (mb.pressed) {
+
+ uv_drag_from=Vector2(mb.x,mb.y);
+ uv_drag=true;
+ uv_prev=node->get_uv();
+ uv_move_current=uv_mode;
+ if (uv_move_current==UV_MODE_EDIT_POINT) {
+
+ if (mb.mod.shift && mb.mod.command)
+ uv_move_current=UV_MODE_SCALE;
+ else if (mb.mod.shift)
+ uv_move_current=UV_MODE_MOVE;
+ else if (mb.mod.command)
+ uv_move_current=UV_MODE_ROTATE;
+ }
+
+ if (uv_move_current==UV_MODE_EDIT_POINT) {
+
+ uv_drag_index=-1;
+ for(int i=0;i<uv_prev.size();i++) {
+
+ Vector2 tuv=mtx.xform(uv_prev[i]);
+ if (tuv.distance_to(Vector2(mb.x,mb.y))<8) {
+ uv_drag_from=tuv;
+ uv_drag_index=i;
+ }
+ }
+
+ if (uv_drag_index==-1) {
+ uv_drag=false;
+ }
+
+ }
+ } else if (uv_drag) {
+
+ undo_redo->create_action(TTR("Transform UV Map"));
+ undo_redo->add_do_method(node,"set_uv",node->get_uv());
+ undo_redo->add_undo_method(node,"set_uv",uv_prev);
+ undo_redo->add_do_method(uv_edit_draw,"update");
+ undo_redo->add_undo_method(uv_edit_draw,"update");
+ undo_redo->commit_action();
+
+ uv_drag=false;
+ }
+
+ } else if (mb.button_index==BUTTON_RIGHT && mb.pressed) {
+
+ if (uv_drag) {
+
+ uv_drag=false;
+ node->set_uv(uv_prev);
+ uv_edit_draw->update();
+ }
+
+ } else if (mb.button_index==BUTTON_WHEEL_UP && mb.pressed) {
+
+ uv_zoom->set_val( uv_zoom->get_val()/0.9 );
+ } else if (mb.button_index==BUTTON_WHEEL_DOWN && mb.pressed) {
+
+ uv_zoom->set_val( uv_zoom->get_val()*0.9);
+ }
+
+ } else if (p_input.type==InputEvent::MOUSE_MOTION) {
+
+ const InputEventMouseMotion &mm=p_input.mouse_motion;
+
+ if (mm.button_mask&BUTTON_MASK_MIDDLE || Input::get_singleton()->is_key_pressed(KEY_SPACE)) {
+
+ Vector2 drag(mm.relative_x,mm.relative_y);
+ uv_hscroll->set_val( uv_hscroll->get_val()-drag.x );
+ uv_vscroll->set_val( uv_vscroll->get_val()-drag.y );
+
+ } else if (uv_drag) {
+
+ Vector2 uv_drag_to=snap_point(Vector2(mm.x,mm.y));
+ Vector2 drag = mtx.affine_inverse().xform(uv_drag_to) - mtx.affine_inverse().xform(uv_drag_from);
+
+
+ switch(uv_move_current) {
+
+ case UV_MODE_EDIT_POINT: {
+
+ DVector<Vector2> uv_new=uv_prev;
+ uv_new.set( uv_drag_index, uv_new[uv_drag_index]+drag );
+ node->set_uv(uv_new);
+ } break;
+ case UV_MODE_MOVE: {
+
+ DVector<Vector2> uv_new=uv_prev;
+ for(int i=0;i<uv_new.size();i++)
+ uv_new.set( i, uv_new[i]+drag );
+
+ node->set_uv(uv_new);
+
+
+ } break;
+ case UV_MODE_ROTATE: {
+
+ Vector2 center;
+ DVector<Vector2> uv_new=uv_prev;
+
+ for(int i=0;i<uv_new.size();i++)
+ center+=uv_prev[i];
+ center/=uv_new.size();
+
+ float angle = (uv_drag_from - mtx.xform(center)).normalized().angle_to( (uv_drag_to - mtx.xform(center)).normalized() );
+
+ for(int i=0;i<uv_new.size();i++) {
+ Vector2 rel = uv_prev[i] - center;
+ rel=rel.rotated(angle);
+ uv_new.set(i,center+rel);
+ }
+
+ node->set_uv(uv_new);
+
+ } break;
+ case UV_MODE_SCALE: {
+
+ Vector2 center;
+ DVector<Vector2> uv_new=uv_prev;
+
+ for(int i=0;i<uv_new.size();i++)
+ center+=uv_prev[i];
+ center/=uv_new.size();
+
+ float from_dist = uv_drag_from.distance_to(mtx.xform(center));
+ float to_dist = uv_drag_to.distance_to(mtx.xform(center));
+ if (from_dist<2)
+ break;
+
+ float scale = to_dist/from_dist;
+
+
+ for(int i=0;i<uv_new.size();i++) {
+ Vector2 rel = uv_prev[i] - center;
+ rel=rel*scale;
+ uv_new.set(i,center+rel);
+ }
+
+ node->set_uv(uv_new);
+ } break;
+
+
+ }
+ uv_edit_draw->update();
+ }
+
+ }
+
+}
+
+
+void Polygon2DEditor::_uv_scroll_changed(float) {
+
+ if (updating_uv_scroll)
+ return;
+
+ uv_draw_ofs.x=uv_hscroll->get_val();
+ uv_draw_ofs.y=uv_vscroll->get_val();
+ uv_draw_zoom=uv_zoom->get_val();
+ uv_edit_draw->update();
+}
+
+void Polygon2DEditor::_uv_draw() {
+
+ Ref<Texture> base_tex = node->get_texture();
+ if (base_tex.is_null())
+ return;
+
+ Matrix32 mtx;
+ mtx.elements[2]=-uv_draw_ofs;
+ mtx.scale_basis(Vector2(uv_draw_zoom,uv_draw_zoom));
+
+ VS::get_singleton()->canvas_item_set_clip(uv_edit_draw->get_canvas_item(),true);
+ VS::get_singleton()->canvas_item_add_set_transform(uv_edit_draw->get_canvas_item(),mtx);
+ uv_edit_draw->draw_texture(base_tex,Point2());
+ VS::get_singleton()->canvas_item_add_set_transform(uv_edit_draw->get_canvas_item(),Matrix32());
+
+ if (snap_show_grid) {
+ Size2 s = uv_edit_draw->get_size();
+ int last_cell;
+
+ if (snap_step.x!=0) {
+ for(int i=0;i<s.width;i++) {
+ int cell = Math::fast_ftoi(Math::floor((mtx.affine_inverse().xform(Vector2(i,0)).x-snap_offset.x)/snap_step.x));
+ if (i==0)
+ last_cell=cell;
+ if (last_cell!=cell)
+ uv_edit_draw->draw_line(Point2(i,0),Point2(i,s.height),Color(0.3,0.7,1,0.3));
+ last_cell=cell;
+ }
+ }
+
+ if (snap_step.y!=0) {
+ for(int i=0;i<s.height;i++) {
+ int cell = Math::fast_ftoi(Math::floor((mtx.affine_inverse().xform(Vector2(0,i)).y-snap_offset.y)/snap_step.y));
+ if (i==0)
+ last_cell=cell;
+ if (last_cell!=cell)
+ uv_edit_draw->draw_line(Point2(0,i),Point2(s.width,i),Color(0.3,0.7,1,0.3));
+ last_cell=cell;
+ }
+ }
+ }
+
+ DVector<Vector2> uvs = node->get_uv();
+ Ref<Texture> handle = get_icon("EditorHandle","EditorIcons");
+
+ Rect2 rect(Point2(),mtx.basis_xform(base_tex->get_size()));
+ rect.expand_to(mtx.basis_xform(uv_edit_draw->get_size()));
+
+ for(int i=0;i<uvs.size();i++) {
+
+ int next = (i+1)%uvs.size();
+ uv_edit_draw->draw_line(mtx.xform(uvs[i]),mtx.xform(uvs[next]),Color(0.9,0.5,0.5),2);
+ uv_edit_draw->draw_texture(handle,mtx.xform(uvs[i])-handle->get_size()*0.5);
+ rect.expand_to(mtx.basis_xform(uvs[i]));
+ }
+
+ rect=rect.grow(200);
+ updating_uv_scroll=true;
+ uv_hscroll->set_min(rect.pos.x);
+ uv_hscroll->set_max(rect.pos.x+rect.size.x);
+ uv_hscroll->set_page(uv_edit_draw->get_size().x);
+ uv_hscroll->set_val(uv_draw_ofs.x);
+ uv_hscroll->set_step(0.001);
+
+ uv_vscroll->set_min(rect.pos.y);
+ uv_vscroll->set_max(rect.pos.y+rect.size.y);
+ uv_vscroll->set_page(uv_edit_draw->get_size().y);
+ uv_vscroll->set_val(uv_draw_ofs.y);
+ uv_vscroll->set_step(0.001);
+ updating_uv_scroll=false;
+
+}
+
+void Polygon2DEditor::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<Polygon2D>();
+ if (!canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw"))
+ canvas_item_editor->get_viewport_control()->connect("draw",this,"_canvas_draw");
+
+ wip.clear();
+ wip_active=false;
+ edited_point=-1;
+
+ } else {
+
+ node=NULL;
+
+ if (canvas_item_editor->get_viewport_control()->is_connected("draw",this,"_canvas_draw"))
+ canvas_item_editor->get_viewport_control()->disconnect("draw",this,"_canvas_draw");
+
+ }
+
+}
+
+void Polygon2DEditor::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("_menu_option"),&Polygon2DEditor::_menu_option);
+ ObjectTypeDB::bind_method(_MD("_canvas_draw"),&Polygon2DEditor::_canvas_draw);
+ ObjectTypeDB::bind_method(_MD("_uv_mode"),&Polygon2DEditor::_uv_mode);
+ ObjectTypeDB::bind_method(_MD("_uv_draw"),&Polygon2DEditor::_uv_draw);
+ ObjectTypeDB::bind_method(_MD("_uv_input"),&Polygon2DEditor::_uv_input);
+ ObjectTypeDB::bind_method(_MD("_uv_scroll_changed"),&Polygon2DEditor::_uv_scroll_changed);
+ ObjectTypeDB::bind_method(_MD("_node_removed"),&Polygon2DEditor::_node_removed);
+ ObjectTypeDB::bind_method(_MD("_set_use_snap"),&Polygon2DEditor::_set_use_snap);
+ ObjectTypeDB::bind_method(_MD("_set_show_grid"),&Polygon2DEditor::_set_show_grid);
+ ObjectTypeDB::bind_method(_MD("_set_snap_off_x"),&Polygon2DEditor::_set_snap_off_x);
+ ObjectTypeDB::bind_method(_MD("_set_snap_off_y"),&Polygon2DEditor::_set_snap_off_y);
+ ObjectTypeDB::bind_method(_MD("_set_snap_step_x"),&Polygon2DEditor::_set_snap_step_x);
+ ObjectTypeDB::bind_method(_MD("_set_snap_step_y"),&Polygon2DEditor::_set_snap_step_y);
+
+
+}
+
+inline float _snap_scalar(float p_offset, float p_step, float p_target) {
+ return p_step != 0 ? Math::stepify(p_target - p_offset, p_step) + p_offset : p_target;
+}
+
+Vector2 Polygon2DEditor::snap_point(Vector2 p_target) const {
+ if (use_snap) {
+ p_target.x = _snap_scalar(snap_offset.x*uv_draw_zoom-uv_draw_ofs.x, snap_step.x*uv_draw_zoom, p_target.x);
+ p_target.y = _snap_scalar(snap_offset.y*uv_draw_zoom-uv_draw_ofs.y, snap_step.y*uv_draw_zoom, p_target.y);
+ }
+
+ return p_target;
+}
+
+Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) {
+
+ node=NULL;
+ canvas_item_editor=NULL;
+ editor=p_editor;
+ undo_redo = editor->get_undo_redo();
+
+ snap_step=Vector2(10,10);
+ use_snap=false;
+ snap_show_grid=false;
+
+ 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);
+
+ button_uv = memnew( ToolButton );
+ add_child(button_uv);
+ button_uv->connect("pressed",this,"_menu_option",varray(MODE_EDIT_UV));
+
+ //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;
+
+ uv_mode=UV_MODE_EDIT_POINT;
+ uv_edit = memnew( AcceptDialog );
+ add_child(uv_edit);
+ uv_edit->set_title(TTR("Polygon 2D UV Editor"));
+ uv_edit->set_self_opacity(0.9);
+
+ VBoxContainer *uv_main_vb = memnew( VBoxContainer );
+ uv_edit->add_child(uv_main_vb);
+ uv_edit->set_child_rect(uv_main_vb);
+ HBoxContainer *uv_mode_hb = memnew( HBoxContainer );
+ uv_main_vb->add_child(uv_mode_hb);
+ for(int i=0;i<UV_MODE_MAX;i++) {
+
+ uv_button[i]=memnew( ToolButton );
+ uv_button[i]->set_toggle_mode(true);
+ uv_mode_hb->add_child(uv_button[i]);
+ uv_button[i]->connect("pressed",this,"_uv_mode",varray(i));
+ uv_button[i]->set_focus_mode(FOCUS_NONE);
+ }
+
+ uv_button[0]->set_tooltip(TTR("Move Point")+"\n"+TTR("Ctrl: Rotate")+"\n"+TTR("Shift: Move All")+"\n"+TTR("Shift+Ctrl: Scale"));
+ uv_button[1]->set_tooltip(TTR("Move Polygon"));
+ uv_button[2]->set_tooltip(TTR("Rotate Polygon"));
+ uv_button[3]->set_tooltip(TTR("Scale Polygon"));
+
+ uv_button[0]->set_pressed(true);
+ HBoxContainer *uv_main_hb = memnew( HBoxContainer );
+ uv_main_vb->add_child(uv_main_hb);
+ uv_edit_draw = memnew( Control );
+ uv_main_hb->add_child(uv_edit_draw);
+ uv_main_hb->set_v_size_flags(SIZE_EXPAND_FILL);
+ uv_edit_draw->set_h_size_flags(SIZE_EXPAND_FILL);
+ uv_menu = memnew( MenuButton );
+ uv_mode_hb->add_child(uv_menu);
+ uv_menu->set_text(TTR("Edit"));
+ uv_menu->get_popup()->add_item(TTR("Polygon->UV"),UVEDIT_POLYGON_TO_UV);
+ uv_menu->get_popup()->add_item(TTR("UV->Polygon"),UVEDIT_UV_TO_POLYGON);
+ uv_menu->get_popup()->add_separator();
+ uv_menu->get_popup()->add_item(TTR("Clear UV"),UVEDIT_UV_CLEAR);
+ uv_menu->get_popup()->connect("item_pressed",this,"_menu_option");
+
+ uv_mode_hb->add_child( memnew( VSeparator ));
+
+ b_snap_enable = memnew( ToolButton );
+ uv_mode_hb->add_child(b_snap_enable);
+ b_snap_enable->set_text(TTR("Snap"));
+ b_snap_enable->set_focus_mode(FOCUS_NONE);
+ b_snap_enable->set_toggle_mode(true);
+ b_snap_enable->set_pressed(use_snap);
+ b_snap_enable->set_tooltip(TTR("Enable Snap"));
+ b_snap_enable->connect("toggled",this,"_set_use_snap");
+
+ b_snap_grid = memnew( ToolButton );
+ uv_mode_hb->add_child(b_snap_grid);
+ b_snap_grid->set_text(TTR("Grid"));
+ b_snap_grid->set_focus_mode(FOCUS_NONE);
+ b_snap_grid->set_toggle_mode(true);
+ b_snap_grid->set_pressed(snap_show_grid);
+ b_snap_grid->set_tooltip(TTR("Show Grid"));
+ b_snap_grid->connect("toggled",this,"_set_show_grid");
+
+ uv_mode_hb->add_child( memnew( VSeparator ));
+ uv_mode_hb->add_child( memnew( Label(TTR("Grid Offset:")) ) );
+
+ SpinBox *sb_off_x = memnew( SpinBox );
+ sb_off_x->set_min(-256);
+ sb_off_x->set_max(256);
+ sb_off_x->set_step(1);
+ sb_off_x->set_val(snap_offset.x);
+ sb_off_x->set_suffix("px");
+ sb_off_x->connect("value_changed", this, "_set_snap_off_x");
+ uv_mode_hb->add_child(sb_off_x);
+
+ SpinBox *sb_off_y = memnew( SpinBox );
+ sb_off_y->set_min(-256);
+ sb_off_y->set_max(256);
+ sb_off_y->set_step(1);
+ sb_off_y->set_val(snap_offset.y);
+ sb_off_y->set_suffix("px");
+ sb_off_y->connect("value_changed", this, "_set_snap_off_y");
+ uv_mode_hb->add_child(sb_off_y);
+
+ uv_mode_hb->add_child( memnew( VSeparator ));
+ uv_mode_hb->add_child( memnew( Label(TTR("Grid Step:")) ) );
+
+ SpinBox *sb_step_x = memnew( SpinBox );
+ sb_step_x->set_min(-256);
+ sb_step_x->set_max(256);
+ sb_step_x->set_step(1);
+ sb_step_x->set_val(snap_step.x);
+ sb_step_x->set_suffix("px");
+ sb_step_x->connect("value_changed", this, "_set_snap_step_x");
+ uv_mode_hb->add_child(sb_step_x);
+
+ SpinBox *sb_step_y = memnew( SpinBox );
+ sb_step_y->set_min(-256);
+ sb_step_y->set_max(256);
+ sb_step_y->set_step(1);
+ sb_step_y->set_val(snap_step.y);
+ sb_step_y->set_suffix("px");
+ sb_step_y->connect("value_changed", this, "_set_snap_step_y");
+ uv_mode_hb->add_child(sb_step_y);
+
+ uv_mode_hb->add_child( memnew( VSeparator ));
+ uv_icon_zoom = memnew( TextureFrame );
+ uv_mode_hb->add_child( uv_icon_zoom );
+ uv_zoom = memnew( HSlider );
+ uv_zoom->set_min(0.01);
+ uv_zoom->set_max(4);
+ uv_zoom->set_val(1);
+ uv_zoom->set_step(0.01);
+ uv_mode_hb->add_child(uv_zoom);
+ uv_zoom->set_custom_minimum_size(Size2(200,0));
+ uv_zoom_value = memnew( SpinBox );
+ uv_zoom->share(uv_zoom_value);
+ uv_zoom_value->set_custom_minimum_size(Size2(50,0));
+ uv_mode_hb->add_child(uv_zoom_value);
+ uv_zoom->connect("value_changed",this,"_uv_scroll_changed");
+
+
+
+ uv_vscroll = memnew( VScrollBar);
+ uv_main_hb->add_child(uv_vscroll);
+ uv_vscroll->connect("value_changed",this,"_uv_scroll_changed");
+ uv_hscroll = memnew( HScrollBar );
+ uv_main_vb->add_child(uv_hscroll);
+ uv_hscroll->connect("value_changed",this,"_uv_scroll_changed");
+
+ uv_edit_draw->connect("draw",this,"_uv_draw");
+ uv_edit_draw->connect("input_event",this,"_uv_input");
+ uv_draw_zoom=1.0;
+ uv_drag_index=-1;
+ uv_drag=false;
+ updating_uv_scroll=false;
+
+ error = memnew( AcceptDialog);
+ add_child(error);
+
+}
+
+
+void Polygon2DEditorPlugin::edit(Object *p_object) {
+
+
+ collision_polygon_editor->edit(p_object->cast_to<Node>());
+}
+
+bool Polygon2DEditorPlugin::handles(Object *p_object) const {
+
+ return p_object->is_type("Polygon2D");
+}
+
+void Polygon2DEditorPlugin::make_visible(bool p_visible) {
+
+ if (p_visible) {
+ collision_polygon_editor->show();
+ } else {
+
+ collision_polygon_editor->hide();
+ collision_polygon_editor->edit(NULL);
+ }
+
+}
+
+Polygon2DEditorPlugin::Polygon2DEditorPlugin(EditorNode *p_node) {
+
+ editor=p_node;
+ collision_polygon_editor = memnew( Polygon2DEditor(p_node) );
+ CanvasItemEditor::get_singleton()->add_control_to_menu_panel(collision_polygon_editor);
+
+ collision_polygon_editor->hide();
+
+}
+
+
+Polygon2DEditorPlugin::~Polygon2DEditorPlugin()
+{
+}
+
diff --git a/editor/plugins/polygon_2d_editor_plugin.h b/editor/plugins/polygon_2d_editor_plugin.h
new file mode 100644
index 000000000..6052f6aaa
--- /dev/null
+++ b/editor/plugins/polygon_2d_editor_plugin.h
@@ -0,0 +1,167 @@
+/*************************************************************************/
+/* polygon_2d_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 POLYGON_2D_EDITOR_PLUGIN_H
+#define POLYGON_2D_EDITOR_PLUGIN_H
+
+#include "editor/editor_plugin.h"
+#include "editor/editor_node.h"
+#include "scene/2d/polygon_2d.h"
+#include "scene/gui/tool_button.h"
+#include "scene/gui/button_group.h"
+
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+class CanvasItemEditor;
+
+class Polygon2DEditor : public HBoxContainer {
+
+ OBJ_TYPE(Polygon2DEditor, HBoxContainer );
+
+ UndoRedo *undo_redo;
+ enum Mode {
+
+ MODE_CREATE,
+ MODE_EDIT,
+ MODE_EDIT_UV,
+ UVEDIT_POLYGON_TO_UV,
+ UVEDIT_UV_TO_POLYGON,
+ UVEDIT_UV_CLEAR
+
+ };
+
+ enum UVMode {
+ UV_MODE_EDIT_POINT,
+ UV_MODE_MOVE,
+ UV_MODE_ROTATE,
+ UV_MODE_SCALE,
+ UV_MODE_MAX
+ };
+
+ Mode mode;
+
+ UVMode uv_mode;
+ AcceptDialog *uv_edit;
+ ToolButton *uv_button[4];
+ ToolButton *b_snap_enable;
+ ToolButton *b_snap_grid;
+ Control *uv_edit_draw;
+ HSlider *uv_zoom;
+ SpinBox *uv_zoom_value;
+ HScrollBar *uv_hscroll;
+ VScrollBar *uv_vscroll;
+ MenuButton *uv_menu;
+ TextureFrame *uv_icon_zoom;
+
+ Vector2 uv_draw_ofs;
+ float uv_draw_zoom;
+ DVector<Vector2> uv_prev;
+ int uv_drag_index;
+ bool uv_drag;
+ UVMode uv_move_current;
+ Vector2 uv_drag_from;
+ bool updating_uv_scroll;
+
+
+
+ AcceptDialog *error;
+
+ ToolButton *button_create;
+ ToolButton *button_edit;
+ ToolButton *button_uv;
+
+ CanvasItemEditor *canvas_item_editor;
+ EditorNode *editor;
+ Panel *panel;
+ Polygon2D *node;
+ MenuButton *options;
+
+ int edited_point;
+ Vector2 edited_point_pos;
+ Vector<Vector2> pre_move_edit;
+ Vector<Vector2> wip;
+ bool wip_active;
+
+ bool use_snap;
+ bool snap_show_grid;
+ Vector2 snap_offset;
+ Vector2 snap_step;
+
+ void _uv_scroll_changed(float);
+ void _uv_input(const InputEvent& p_input);
+ void _uv_draw();
+ void _uv_mode(int p_mode);
+ void _wip_close();
+ void _canvas_draw();
+ void _menu_option(int p_option);
+
+ void _set_use_snap(bool p_use);
+ void _set_show_grid(bool p_show);
+ void _set_snap_off_x(float p_val);
+ void _set_snap_off_y(float p_val);
+ void _set_snap_step_x(float p_val);
+ void _set_snap_step_y(float p_val);
+
+protected:
+ void _notification(int p_what);
+ void _node_removed(Node *p_node);
+ static void _bind_methods();
+
+ Vector2 snap_point(Vector2 p_target) const;
+
+public:
+
+ bool forward_input_event(const InputEvent& p_event);
+ void edit(Node *p_collision_polygon);
+ Polygon2DEditor(EditorNode *p_editor);
+};
+
+class Polygon2DEditorPlugin : public EditorPlugin {
+
+ OBJ_TYPE( Polygon2DEditorPlugin, EditorPlugin );
+
+ Polygon2DEditor *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 "Polygon2D"; }
+ 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);
+
+ Polygon2DEditorPlugin(EditorNode *p_node);
+ ~Polygon2DEditorPlugin();
+
+};
+
+#endif // POLYGON_2D_EDITOR_PLUGIN_H
diff --git a/editor/plugins/resource_preloader_editor_plugin.cpp b/editor/plugins/resource_preloader_editor_plugin.cpp
new file mode 100644
index 000000000..6cd63451d
--- /dev/null
+++ b/editor/plugins/resource_preloader_editor_plugin.cpp
@@ -0,0 +1,508 @@
+/*************************************************************************/
+/* resource_preloader_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "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_TREE) {
+ 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::_files_load_request(const Vector<String>& p_paths) {
+
+ for(int i=0;i<p_paths.size();i++) {
+
+ String path = p_paths[i];
+
+ RES resource;
+ resource = ResourceLoader::load(path);
+
+ if (resource.is_null()) {
+ dialog->set_text(TTR("ERROR: Couldn't load resource!"));
+ dialog->set_title(TTR("Error!"));
+ //dialog->get_cancel()->set_text("Close");
+ dialog->get_ok()->set_text(TTR("Close"));
+ dialog->popup_centered_minsize();
+ return; ///beh should show an error i guess
+ }
+
+
+ String basename = path.get_file().basename();
+ String name=basename;
+ int counter=1;
+ while(preloader->has_resource(name)) {
+ counter++;
+ name=basename+" "+itos(counter);
+ }
+
+ undo_redo->create_action(TTR("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(EditorFileDialog::MODE_OPEN_FILES);
+
+ 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(TTR("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(TTR("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(TTR("Resource clipboard is empty!"));
+ dialog->set_title(TTR("Error!"));
+ //dialog->get_cancel()->set_text("Close");
+ dialog->get_ok()->set_text(TTR("Close"));
+ dialog->popup_centered_minsize();
+ 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(TTR("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);
+ }
+
+}
+
+
+
+Variant ResourcePreloaderEditor::get_drag_data_fw(const Point2& p_point,Control* p_from) {
+
+ TreeItem*ti =tree->get_item_at_pos(p_point);
+ if (!ti)
+ return Variant();
+
+ String name = ti->get_metadata(0);
+
+ RES res = preloader->get_resource(name);
+ if (!res.is_valid())
+ return Variant();
+
+ return EditorNode::get_singleton()->drag_resource(res,p_from);
+
+}
+
+bool ResourcePreloaderEditor::can_drop_data_fw(const Point2& p_point,const Variant& p_data,Control* p_from) const {
+
+
+
+ Dictionary d = p_data;
+
+ if (!d.has("type"))
+ return false;
+
+ if (d.has("from") && (Object*)(d["from"])==tree)
+ return false;
+
+ if (String(d["type"])=="resource" && d.has("resource")) {
+ RES r=d["resource"];
+
+ return r.is_valid();
+ }
+
+
+ if (String(d["type"])=="files") {
+
+ Vector<String> files = d["files"];
+
+ if (files.size()==0)
+ return false;
+
+ return true;
+
+ }
+ return false;
+}
+
+void ResourcePreloaderEditor::drop_data_fw(const Point2& p_point,const Variant& p_data,Control* p_from) {
+
+ if (!can_drop_data_fw(p_point,p_data,p_from))
+ return;
+
+ Dictionary d = p_data;
+
+ if (!d.has("type"))
+ return;
+
+
+ if (String(d["type"])=="resource" && d.has("resource")) {
+ RES r=d["resource"];
+
+ if (r.is_valid()) {
+
+ String basename;
+ if (r->get_name()!="") {
+ basename=r->get_name();
+ } else if (r->get_path().is_resource_file()) {
+ basename = r->get_path().basename();
+ } else {
+ basename="Resource";
+ }
+
+ String name=basename;
+ int counter=0;
+ while(preloader->has_resource(name)) {
+ counter++;
+ name=basename+"_"+itos(counter);
+ }
+
+ undo_redo->create_action(TTR("Add 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();
+ }
+ }
+
+
+ if (String(d["type"])=="files") {
+
+ Vector<String> files = d["files"];
+
+ _files_load_request(files);
+ }
+}
+
+
+
+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("_files_load_request"),&ResourcePreloaderEditor::_files_load_request);
+ ObjectTypeDB::bind_method(_MD("_update_library"),&ResourcePreloaderEditor::_update_library);
+
+
+ ObjectTypeDB::bind_method(_MD("get_drag_data_fw"), &ResourcePreloaderEditor::get_drag_data_fw);
+ ObjectTypeDB::bind_method(_MD("can_drop_data_fw"), &ResourcePreloaderEditor::can_drop_data_fw);
+ ObjectTypeDB::bind_method(_MD("drop_data_fw"), &ResourcePreloaderEditor::drop_data_fw);
+
+
+}
+
+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(TTR("Load Resource"));
+ hbc->add_child(load);
+
+
+
+ _delete = memnew( Button );
+ hbc->add_child(_delete);
+
+ paste = memnew( Button );
+ paste->set_text(TTR("Paste"));
+ hbc->add_child(paste);
+
+ file = memnew( EditorFileDialog );
+ 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);
+
+ tree->set_drag_forwarding(this);
+ 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("files_selected", this,"_files_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();
+ button->show();
+ editor->make_bottom_panel_item_visible(preloader_editor);
+// preloader_editor->set_process(true);
+ } else {
+
+ if (preloader_editor->is_visible())
+ editor->hide_bottom_panel();
+ button->hide();
+ //preloader_editor->hide();
+// preloader_editor->set_process(false);
+ }
+
+}
+
+ResourcePreloaderEditorPlugin::ResourcePreloaderEditorPlugin(EditorNode *p_node) {
+
+ editor=p_node;
+ preloader_editor = memnew( ResourcePreloaderEditor );
+ preloader_editor->set_custom_minimum_size(Size2(0,250));
+
+ button=editor->add_bottom_panel_item("ResourcePreloader",preloader_editor);
+ button->hide();
+
+// preloader_editor->set_anchor( MARGIN_TOP, Control::ANCHOR_END);
+// preloader_editor->set_margin( MARGIN_TOP, 120 );
+
+
+
+
+}
+
+
+ResourcePreloaderEditorPlugin::~ResourcePreloaderEditorPlugin()
+{
+}
+
+
diff --git a/editor/plugins/resource_preloader_editor_plugin.h b/editor/plugins/resource_preloader_editor_plugin.h
new file mode 100644
index 000000000..f181de699
--- /dev/null
+++ b/editor/plugins/resource_preloader_editor_plugin.h
@@ -0,0 +1,108 @@
+/*************************************************************************/
+/* resource_preloader_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "editor/editor_plugin.h"
+#include "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;
+
+
+ EditorFileDialog *file;
+
+ AcceptDialog *dialog;
+
+ ResourcePreloader *preloader;
+
+
+ void _load_pressed();
+ void _load_scene_pressed();
+ void _files_load_request(const Vector<String>& p_paths);
+ void _paste_pressed();
+ void _delete_pressed();
+ void _delete_confirm_pressed();
+ void _update_library();
+ void _item_edited();
+
+ UndoRedo *undo_redo;
+
+ Variant get_drag_data_fw(const Point2& p_point,Control* p_from);
+ bool can_drop_data_fw(const Point2& p_point,const Variant& p_data,Control* p_from) const;
+ void drop_data_fw(const Point2& p_point,const Variant& p_data,Control* p_from);
+
+
+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;
+ Button *button;
+
+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/editor/plugins/rich_text_editor_plugin.cpp b/editor/plugins/rich_text_editor_plugin.cpp
new file mode 100644
index 000000000..f5a6bb6b4
--- /dev/null
+++ b/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-2017 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"
+#include "canvas_item_editor_plugin.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);
+ CanvasItemEditor::get_singleton()->add_control_to_menu_panel(options);
+ options->set_area_as_parent_rect();
+
+ options->set_text("RichText");
+ options->get_popup()->add_item(TTR("Parse BBCode"),PARSE_BBCODE);
+ options->get_popup()->add_item(TTR("Clear"),CLEAR);
+
+ options->get_popup()->connect("item_pressed", this,"_menu_option");
+ file_dialog = memnew( EditorFileDialog );
+ add_child(file_dialog);
+ file_dialog->add_filter("*.txt");
+ file_dialog->set_mode(EditorFileDialog::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->options->show();
+ } else {
+
+ rich_text_editor->options->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->options->hide();
+
+}
+
+
+RichTextEditorPlugin::~RichTextEditorPlugin()
+{
+}
+
diff --git a/editor/plugins/rich_text_editor_plugin.h b/editor/plugins/rich_text_editor_plugin.h
new file mode 100644
index 000000000..1e168c378
--- /dev/null
+++ b/editor/plugins/rich_text_editor_plugin.h
@@ -0,0 +1,91 @@
+/*************************************************************************/
+/* rich_text_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "editor/editor_plugin.h"
+#include "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 );
+
+ friend class RichTextEditorPlugin;
+
+ enum {
+
+ PARSE_BBCODE,
+ CLEAR
+ };
+
+ Panel *panel;
+ MenuButton *options;
+ RichTextLabel *node;
+ EditorFileDialog *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/editor/plugins/sample_editor_plugin.cpp b/editor/plugins/sample_editor_plugin.cpp
new file mode 100644
index 000000000..3ad75284e
--- /dev/null
+++ b/editor/plugins/sample_editor_plugin.cpp
@@ -0,0 +1,450 @@
+/*************************************************************************/
+/* sample_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "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_TREE) {
+ 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;
+
+ if (p_sample->get_format()==Sample::FORMAT_IMA_ADPCM) {
+
+
+ struct IMA_ADPCM_State {
+
+ int16_t step_index;
+ int32_t predictor;
+ /* values at loop point */
+ int16_t loop_step_index;
+ int32_t loop_predictor;
+ int32_t last_nibble;
+ int32_t loop_pos;
+ int32_t window_ofs;
+ const uint8_t *ptr;
+ } ima_adpcm;
+
+ ima_adpcm.step_index=0;
+ ima_adpcm.predictor=0;
+ ima_adpcm.loop_step_index=0;
+ ima_adpcm.loop_predictor=0;
+ ima_adpcm.last_nibble=-1;
+ ima_adpcm.loop_pos=0x7FFFFFFF;
+ ima_adpcm.window_ofs=0;
+ ima_adpcm.ptr=NULL;
+
+
+ for(int i=0;i<w;i++) {
+
+ float max[2]={-1e10,-1e10};
+ float min[2]={1e10,1e10};
+ int from = i*len/w;
+ int to = (i+1)*len/w;
+ if (to>=len)
+ to=len-1;
+
+ for(int j=from;j<to;j++) {
+
+ while(j>ima_adpcm.last_nibble) {
+
+ static const int16_t _ima_adpcm_step_table[89] = {
+ 7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
+ 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
+ 50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
+ 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
+ 337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
+ 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
+ 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
+ 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
+ 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
+ };
+
+ static const int8_t _ima_adpcm_index_table[16] = {
+ -1, -1, -1, -1, 2, 4, 6, 8,
+ -1, -1, -1, -1, 2, 4, 6, 8
+ };
+
+ int16_t nibble,diff,step;
+
+ ima_adpcm.last_nibble++;
+ const uint8_t *src_ptr=sdata;
+
+ int ofs = ima_adpcm.last_nibble>>1;
+
+ if (stereo)
+ ofs*=2;
+
+ nibble = (ima_adpcm.last_nibble&1)?
+ (src_ptr[ofs]>>4):(src_ptr[ofs]&0xF);
+
+ step=_ima_adpcm_step_table[ima_adpcm.step_index];
+
+ ima_adpcm.step_index += _ima_adpcm_index_table[nibble];
+ if (ima_adpcm.step_index<0)
+ ima_adpcm.step_index=0;
+ if (ima_adpcm.step_index>88)
+ ima_adpcm.step_index=88;
+
+ diff = step >> 3 ;
+ if (nibble & 1)
+ diff += step >> 2 ;
+ if (nibble & 2)
+ diff += step >> 1 ;
+ if (nibble & 4)
+ diff += step ;
+ if (nibble & 8)
+ diff = -diff ;
+
+ ima_adpcm.predictor+=diff;
+ if (ima_adpcm.predictor<-0x8000)
+ ima_adpcm.predictor=-0x8000;
+ else if (ima_adpcm.predictor>0x7FFF)
+ ima_adpcm.predictor=0x7FFF;
+
+
+ /* store loop if there */
+ if (ima_adpcm.last_nibble==ima_adpcm.loop_pos) {
+
+ ima_adpcm.loop_step_index = ima_adpcm.step_index;
+ ima_adpcm.loop_predictor = ima_adpcm.predictor;
+ }
+
+ }
+
+ float v=ima_adpcm.predictor/32767.0;
+ if (v>max[0])
+ max[0]=v;
+ if (v<min[0])
+ min[0]=v;
+ }
+
+ for(int j=0;j<h;j++) {
+ float v = (j/(float)h) * 2.0 - 1.0;
+ uint8_t* imgofs = &imgw[(uint64_t(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 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 = uint64_t(i)*len/w;
+ int to = (uint64_t(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[uint64_t(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[uint64_t(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[(uint64_t(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;
+ float v;
+ if (j<(h/2)) {
+ half=0;
+ v = (j/(float)(h/2)) * 2.0 - 1.0;
+ } else {
+ half=1;
+ v = ((j-(h/2))/(float)(h/2)) * 2.0 - 1.0;
+ }
+
+ uint8_t* imgofs = &imgw[(uint64_t(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();
+
+ generate_preview_texture(sample,peakdisplay);
+ info_label->set_text(TTR("Length:")+" "+String::num(sample->get_length()/(float)sample->get_mix_rate(),2)+"s");
+
+ if (library->has_sample("default"))
+ library->remove_sample("default");
+
+ 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");
+
+ set_custom_minimum_size(Size2(1,150)*EDSCALE);
+
+}
+
+
+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 );
+ add_control_to_container(CONTAINER_PROPERTY_EDITOR_BOTTOM,sample_editor);
+ sample_editor->hide();
+
+
+
+}
+
+
+SampleEditorPlugin::~SampleEditorPlugin()
+{
+}
+
+
diff --git a/editor/plugins/sample_editor_plugin.h b/editor/plugins/sample_editor_plugin.h
new file mode 100644
index 000000000..b932dcedf
--- /dev/null
+++ b/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-2017 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 "editor/editor_plugin.h"
+#include "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/editor/plugins/sample_library_editor_plugin.cpp b/editor/plugins/sample_library_editor_plugin.cpp
new file mode 100644
index 000000000..4be87e390
--- /dev/null
+++ b/editor/plugins/sample_library_editor_plugin.cpp
@@ -0,0 +1,543 @@
+/*************************************************************************/
+/* sample_library_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "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_PROCESS) {
+ if (is_playing && !player->is_active()) {
+ TreeItem *tl=last_sample_playing->cast_to<TreeItem>();
+ tl->set_button(0,0,get_icon("Play","EditorIcons"));
+ is_playing = false;
+ set_process(false);
+ }
+ }
+
+ if (p_what==NOTIFICATION_ENTER_TREE) {
+ load->set_icon( get_icon("Folder","EditorIcons") );
+ load->set_tooltip(TTR("Open Sample File(s)"));
+ }
+
+ if (p_what==NOTIFICATION_READY) {
+
+// NodePath("/root")->connect("node_removed", this,"_node_removed",Vector<Variant>(),true);
+ }
+
+ if (p_what==NOTIFICATION_DRAW) {
+
+ }
+}
+
+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(TTR("ERROR: Couldn't load sample!"));
+ dialog->set_title(TTR("Error!"));
+ //dialog->get_cancel()->set_text("Close");
+ dialog->get_ok()->set_text(TTR("Close"));
+ dialog->popup_centered_minsize();
+ 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(TTR("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) { // Play/Stop
+
+ String btn_type;
+ if(!is_playing) {
+ is_playing = true;
+ btn_type = TTR("Stop");
+ player->play(name,true);
+ last_sample_playing = p_item;
+ set_process(true);
+ } else {
+ player->stop_all();
+ if(last_sample_playing != p_item){
+ TreeItem *tl=last_sample_playing->cast_to<TreeItem>();
+ tl->set_button(p_column,0,get_icon("Play","EditorIcons"));
+ btn_type = TTR("Stop");
+ player->play(name,true);
+ last_sample_playing = p_item;
+ } else {
+ btn_type = TTR("Play");
+ is_playing = false;
+ }
+ }
+ ti->set_button(p_column,0,get_icon(btn_type,"EditorIcons"));
+ } else if (p_column==1) { // Edit
+
+ get_tree()->get_root()->get_child(0)->call("_resource_selected",sample_library->get_sample(name));
+ } else if (p_column==5) { // Delete
+
+ ti->select(0);
+ _delete_pressed();
+ }
+
+
+}
+
+
+
+
+
+void SampleLibraryEditor::_item_edited() {
+
+ if (!tree->get_selected())
+ return;
+
+ TreeItem *s = tree->get_selected();
+
+ if (tree->get_selected_column()==0) { // Name
+ // 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(TTR("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) { // Volume dB
+
+ StringName n = s->get_text(0);
+ sample_library->sample_set_volume_db(n,s->get_range(3));
+
+ } else if (tree->get_selected_column()==4) { // Pitch scale
+
+ StringName n = s->get_text(0);
+ sample_library->sample_set_pitch_scale(n,s->get_range(4));
+
+ }
+
+
+}
+
+void SampleLibraryEditor::_delete_pressed() {
+
+ if (!tree->get_selected())
+ return;
+
+ String to_remove = tree->get_selected()->get_text(0);
+ undo_redo->create_action(TTR("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::_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);
+ names.sort_custom<StringName::AlphCompare>();
+
+ for(List<StringName>::Element *E=names.front();E;E=E->next()) {
+
+ TreeItem *ti = tree->create_item(root);
+
+ // Name + Play/Stop
+ 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"));
+
+ Ref<Sample> smp = sample_library->get_sample(E->get());
+
+ // Preview/edit
+ 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->add_button(1,get_icon("Edit","EditorIcons"));
+
+ // Format
+ ti->set_cell_mode(2,TreeItem::CELL_MODE_STRING);
+ ti->set_editable(2,false);
+ ti->set_selectable(2,false);
+ ti->set_text(2,String()+(smp->get_format()==Sample::FORMAT_PCM16?TTR("16 Bits")+", ":(smp->get_format()==Sample::FORMAT_PCM8?TTR("8 Bits")+", ":"IMA-ADPCM,"))+(smp->is_stereo()?TTR("Stereo"):TTR("Mono")));
+
+ // Volume dB
+ 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()));
+
+ // Pitch scale
+ 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()));
+
+ // Delete
+ ti->set_cell_mode(5,TreeItem::CELL_MODE_STRING);
+ ti->add_button(5,get_icon("Remove","EditorIcons"));
+
+ }
+
+ //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();
+ }
+
+}
+
+Variant SampleLibraryEditor::get_drag_data_fw(const Point2& p_point,Control* p_from) {
+
+ TreeItem*ti =tree->get_item_at_pos(p_point);
+ if (!ti)
+ return Variant();
+
+ String name = ti->get_metadata(0);
+
+ RES res = sample_library->get_sample(name);
+ if (!res.is_valid())
+ return Variant();
+
+ return EditorNode::get_singleton()->drag_resource(res,p_from);
+
+
+}
+
+bool SampleLibraryEditor::can_drop_data_fw(const Point2& p_point,const Variant& p_data,Control* p_from) const {
+
+
+
+ Dictionary d = p_data;
+
+ if (!d.has("type"))
+ return false;
+
+ if (d.has("from") && (Object*)(d["from"])==tree)
+ return false;
+
+ if (String(d["type"])=="resource" && d.has("resource")) {
+ RES r=d["resource"];
+
+ Ref<Sample> sample = r;
+
+ if (sample.is_valid()) {
+
+ return true;
+ }
+ }
+
+
+ if (String(d["type"])=="files") {
+
+ Vector<String> files = d["files"];
+
+ if (files.size()==0)
+ return false;
+
+ for(int i=0;i<files.size();i++) {
+ String file = files[0];
+ String ftype = EditorFileSystem::get_singleton()->get_file_type(file);
+
+ if (ftype!="Sample") {
+ return false;
+ }
+
+ }
+
+ return true;
+
+ }
+ return false;
+}
+
+void SampleLibraryEditor::drop_data_fw(const Point2& p_point,const Variant& p_data,Control* p_from) {
+
+ if (!can_drop_data_fw(p_point,p_data,p_from))
+ return;
+
+ Dictionary d = p_data;
+
+ if (!d.has("type"))
+ return;
+
+
+ if (String(d["type"])=="resource" && d.has("resource")) {
+ RES r=d["resource"];
+
+ Ref<Sample> sample = r;
+
+ if (sample.is_valid()) {
+
+ String basename;
+ if (sample->get_name()!="") {
+ basename=sample->get_name();
+ } else if (sample->get_path().is_resource_file()) {
+ basename = sample->get_path().basename();
+ } else {
+ basename="Sample";
+ }
+
+ String name=basename;
+ int counter=0;
+ while(sample_library->has_sample(name)) {
+ counter++;
+ name=basename+"_"+itos(counter);
+ }
+
+ undo_redo->create_action(TTR("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();
+ }
+ }
+
+
+ if (String(d["type"])=="files") {
+
+ DVector<String> files = d["files"];
+
+ _file_load_request(files);
+
+ }
+
+}
+
+
+void SampleLibraryEditor::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("_input_event"),&SampleLibraryEditor::_input_event);
+ 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("_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);
+
+ ObjectTypeDB::bind_method(_MD("get_drag_data_fw"), &SampleLibraryEditor::get_drag_data_fw);
+ ObjectTypeDB::bind_method(_MD("can_drop_data_fw"), &SampleLibraryEditor::can_drop_data_fw);
+ ObjectTypeDB::bind_method(_MD("drop_data_fw"), &SampleLibraryEditor::drop_data_fw);
+
+}
+
+SampleLibraryEditor::SampleLibraryEditor() {
+
+ player = memnew(SamplePlayer);
+ add_child(player);
+ add_style_override("panel", get_stylebox("panel","Panel"));
+
+
+ load = memnew( Button );
+ load->set_pos(Point2( 5, 5 ));
+ load->set_size( Size2(1,1 ) );
+ add_child(load);
+
+ file = memnew( EditorFileDialog );
+ 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(EditorFileDialog::MODE_OPEN_FILES);
+
+ tree = memnew( Tree );
+ tree->set_columns(6);
+ 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,TTR("Name"));
+ tree->set_column_title(1,TTR("Preview"));
+ tree->set_column_title(2,TTR("Format"));
+ tree->set_column_title(3,"dB");
+ tree->set_column_title(4,TTR("Pitch"));
+ tree->set_column_title(5,"");
+
+ 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_min_width(5,32);
+ tree->set_column_expand(1,false);
+ tree->set_column_expand(2,false);
+ tree->set_column_expand(3,false);
+ tree->set_column_expand(4,false);
+ tree->set_column_expand(5,false);
+
+ tree->set_drag_forwarding(this);
+
+ dialog = memnew( ConfirmationDialog );
+ add_child( dialog );
+
+ tree->connect("button_pressed",this,"_button_pressed");
+ load->connect("pressed", this,"_load_pressed");
+ file->connect("files_selected", this,"_file_load_request");
+ tree->connect("item_edited", this,"_item_edited");
+
+ is_playing = false;
+}
+
+
+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();
+ button->show();
+ editor->make_bottom_panel_item_visible(sample_library_editor);
+// sample_library_editor->set_process(true);
+ } else {
+
+ if (sample_library_editor->is_visible())
+ editor->hide_bottom_panel();
+ button->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_custom_minimum_size(Size2(0,250));
+ button=p_node->add_bottom_panel_item("SampleLibrary",sample_library_editor);
+ button->hide();
+
+ //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/editor/plugins/sample_library_editor_plugin.h b/editor/plugins/sample_library_editor_plugin.h
new file mode 100644
index 000000000..48d67c144
--- /dev/null
+++ b/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-2017 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 "editor/editor_plugin.h"
+#include "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 *load;
+ Tree *tree;
+ bool is_playing;
+ Object *last_sample_playing;
+
+ EditorFileDialog *file;
+
+ ConfirmationDialog *dialog;
+
+
+ void _load_pressed();
+ void _file_load_request(const DVector<String>& p_path);
+ void _delete_pressed();
+ void _update_library();
+ void _item_edited();
+
+ UndoRedo *undo_redo;
+
+ void _button_pressed(Object *p_item,int p_column, int p_id);
+
+ Variant get_drag_data_fw(const Point2& p_point,Control* p_from);
+ bool can_drop_data_fw(const Point2& p_point,const Variant& p_data,Control* p_from) const;
+ void drop_data_fw(const Point2& p_point,const Variant& p_data,Control* p_from);
+
+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;
+ Button *button;
+
+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/editor/plugins/sample_player_editor_plugin.cpp b/editor/plugins/sample_player_editor_plugin.cpp
new file mode 100644
index 000000000..8d2765aca
--- /dev/null
+++ b/editor/plugins/sample_player_editor_plugin.cpp
@@ -0,0 +1,198 @@
+/*************************************************************************/
+/* sample_player_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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_TREE) {
+ 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() ));
+ stop->set_pressed(false);
+ play->set_pressed(true);
+}
+
+void SamplePlayerEditor::_stop() {
+
+ if (!node)
+ return;
+ if (samples->get_item_count()<=0)
+ return;
+
+ node->call("stop_all");
+ print_line("STOP ALL!!");
+ stop->set_pressed(true);
+ play->set_pressed(false);
+
+}
+
+
+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);
+ samplenames.sort_custom<StringName::AlphCompare>();
+ 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/editor/plugins/sample_player_editor_plugin.h b/editor/plugins/sample_player_editor_plugin.h
new file mode 100644
index 000000000..ae8f24f46
--- /dev/null
+++ b/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-2017 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 "editor/editor_plugin.h"
+#include "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/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
new file mode 100644
index 000000000..4709cb843
--- /dev/null
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -0,0 +1,3190 @@
+/*************************************************************************/
+/* script_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "editor/editor_settings.h"
+#include "io/resource_loader.h"
+#include "io/resource_saver.h"
+#include "os/keyboard.h"
+#include "os/os.h"
+#include "editor/editor_node.h"
+#include "editor/script_editor_debugger.h"
+#include "globals.h"
+#include "os/file_access.h"
+#include "scene/main/viewport.h"
+#include "os/keyboard.h"
+#include "os/input.h"
+
+/*** SCRIPT EDITOR ****/
+
+
+static bool _can_open_in_editor(Script* p_script) {
+
+ String path = p_script->get_path();
+
+ if (path.find("::")!=-1) {
+ //refuse handling this if it can't be edited
+
+ bool valid=false;
+ for(int i=0;i<EditorNode::get_singleton()->get_editor_data().get_edited_scene_count();i++) {
+ if (path.begins_with(EditorNode::get_singleton()->get_editor_data().get_scene_path(i))) {
+ valid=true;
+ break;
+ }
+ }
+
+ return valid;
+ }
+
+ return true;
+}
+
+
+class EditorScriptCodeCompletionCache : public ScriptCodeCompletionCache {
+
+
+ struct Cache {
+ uint64_t time_loaded;
+ RES cache;
+ };
+
+ Map<String,Cache> cached;
+
+
+public:
+
+ uint64_t max_time_cache;
+ int max_cache_size;
+
+ void cleanup() {
+
+ List< Map<String,Cache>::Element * > to_clean;
+
+
+ Map<String,Cache>::Element *I=cached.front();
+ while(I) {
+ if ((OS::get_singleton()->get_ticks_msec()-I->get().time_loaded)>max_time_cache) {
+ to_clean.push_back(I);
+ }
+ I=I->next();
+ }
+
+ while(to_clean.front()) {
+ cached.erase(to_clean.front()->get());
+ to_clean.pop_front();
+ }
+ }
+
+ RES get_cached_resource(const String& p_path) {
+
+ Map<String,Cache>::Element *E=cached.find(p_path);
+ if (!E) {
+
+ Cache c;
+ c.cache=ResourceLoader::load(p_path);
+ E=cached.insert(p_path,c);
+ }
+
+ E->get().time_loaded=OS::get_singleton()->get_ticks_msec();
+
+ if (cached.size()>max_cache_size) {
+ uint64_t older;
+ Map<String,Cache>::Element *O=cached.front();
+ older=O->get().time_loaded;
+ Map<String,Cache>::Element *I=O;
+ while(I) {
+ if (I->get().time_loaded<older) {
+ older = I->get().time_loaded;
+ O=I;
+ }
+ I=I->next();
+ }
+
+ if (O!=E) {//should never heppane..
+ cached.erase(O);
+ }
+ }
+
+ return E->get().cache;
+ }
+
+
+ EditorScriptCodeCompletionCache() {
+
+ max_cache_size=128;
+ max_time_cache=5*60*1000; //minutes, five
+ }
+
+};
+
+#define SORT_SCRIPT_LIST
+
+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_TREE) {
+
+ 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(TTR("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(TTR("Matches:"),search_options,true);
+ get_ok()->set_text(TTR("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;
+// print_line("applying code");
+ script->set_source_code(get_text_edit()->get_text());
+ script->update_exports();
+}
+
+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("completion_background_color", EDITOR_DEF("text_editor/completion_background_color", Color(0,0,0,0)));
+ get_text_edit()->add_color_override("completion_selected_color", EDITOR_DEF("text_editor/completion_selected_color", Color::html("434244")));
+ get_text_edit()->add_color_override("completion_existing_color", EDITOR_DEF("text_editor/completion_existing_color", Color::html("21dfdfdf")));
+ get_text_edit()->add_color_override("completion_scroll_color", EDITOR_DEF("text_editor/completion_scroll_color", Color::html("ffffff")));
+ get_text_edit()->add_color_override("completion_font_color", EDITOR_DEF("text_editor/completion_font_color", Color::html("aaaaaa")));
+ get_text_edit()->add_color_override("font_color",EDITOR_DEF("text_editor/text_color",Color(0,0,0)));
+ get_text_edit()->add_color_override("line_number_color",EDITOR_DEF("text_editor/line_number_color",Color(0,0,0)));
+ get_text_edit()->add_color_override("caret_color",EDITOR_DEF("text_editor/caret_color",Color(0,0,0)));
+ get_text_edit()->add_color_override("caret_background_color",EDITOR_DEF("text_editor/caret_background_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)));
+ get_text_edit()->add_color_override("brace_mismatch_color",EDITOR_DEF("text_editor/brace_mismatch_color",Color(1,0.2,0.2)));
+ get_text_edit()->add_color_override("current_line_color",EDITOR_DEF("text_editor/current_line_color",Color(0.3,0.5,0.8,0.15)));
+ get_text_edit()->add_color_override("word_highlighted_color",EDITOR_DEF("text_editor/word_highlighted_color",Color(0.8,0.9,0.9,0.15)));
+ get_text_edit()->add_color_override("number_color",EDITOR_DEF("text_editor/number_color",Color(0.9,0.6,0.0,2)));
+ get_text_edit()->add_color_override("function_color",EDITOR_DEF("text_editor/function_color",Color(0.4,0.6,0.8)));
+ get_text_edit()->add_color_override("member_variable_color",EDITOR_DEF("text_editor/member_variable_color",Color(0.9,0.3,0.3)));
+ get_text_edit()->add_color_override("mark_color", EDITOR_DEF("text_editor/mark_color", Color(1.0,0.4,0.4,0.4)));
+ get_text_edit()->add_color_override("breakpoint_color", EDITOR_DEF("text_editor/breakpoint_color", Color(0.8,0.8,0.4,0.2)));
+ get_text_edit()->add_color_override("search_result_color",EDITOR_DEF("text_editor/search_result_color",Color(0.05,0.25,0.05,1)));
+ get_text_edit()->add_color_override("search_result_border_color",EDITOR_DEF("text_editor/search_result_border_color",Color(0.1,0.45,0.1,1)));
+ get_text_edit()->add_constant_override("line_spacing", EDITOR_DEF("text_editor/line_spacing",4));
+
+ Color keyword_color= EDITOR_DEF("text_editor/keyword_color",Color(0.5,0.0,0.2));
+
+ 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);
+ get_text_edit()->add_keyword_color("Rect2",basetype_color);
+ get_text_edit()->add_keyword_color("NodePath",basetype_color);
+
+ //colorize engine types
+ Color type_color= EDITOR_DEF("text_editor/engine_type_color",Color(0.0,0.2,0.4));
+
+ List<StringName> types;
+ ObjectTypeDB::get_type_list(&types);
+
+ for(List<StringName>::Element *E=types.front();E;E=E->next()) {
+
+ String n = E->get();
+ if (n.begins_with("_"))
+ n = n.substr(1, n.length());
+
+ get_text_edit()->add_keyword_color(n,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()) ;
+
+ TextEdit *te = get_text_edit();
+ int column = te->cursor_get_column();
+ int row = te->cursor_get_line();
+ int h = te->get_h_scroll();
+ int v = te->get_v_scroll();
+
+ te->set_text(script->get_source_code());
+ te->clear_undo_history();
+ te->cursor_set_line(row);
+ te->cursor_set_column(column);
+ te->set_h_scroll(h);
+ te->set_v_scroll(v);
+
+ te->tag_saved_version();
+
+ _line_col_changed();
+
+}
+
+void ScriptTextEditor::_notification(int p_what) {
+
+ if (p_what==NOTIFICATION_READY) {
+
+ //emit_signal("name_changed");
+ }
+}
+
+
+bool ScriptTextEditor::is_unsaved() {
+
+ return get_text_edit()->get_version()!=get_text_edit()->get_saved_version();
+}
+
+String ScriptTextEditor::get_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())+")";
+
+ return name;
+
+}
+
+Ref<Texture> ScriptTextEditor::get_icon() {
+
+ if (get_parent_control() && get_parent_control()->has_icon(script->get_type(),"EditorIcons")) {
+ return get_parent_control()->get_icon(script->get_type(),"EditorIcons");
+ }
+
+ return Ref<Texture>();
+}
+
+
+
+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();
+
+
+ emit_signal("name_changed");
+ _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->update_exports();
+ //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);
+ }
+
+ emit_signal("name_changed");
+}
+
+
+static Node* _find_node_for_script(Node* p_base, Node*p_current, const Ref<Script>& p_script) {
+
+ if (p_current->get_owner()!=p_base && p_base!=p_current)
+ return NULL;
+ Ref<Script> c = p_current->get_script();
+ if (c==p_script)
+ return p_current;
+ for(int i=0;i<p_current->get_child_count();i++) {
+ Node *found = _find_node_for_script(p_base,p_current->get_child(i),p_script);
+ if (found)
+ return found;
+ }
+
+ return NULL;
+}
+
+static void _find_changed_scripts_for_external_editor(Node* p_base, Node*p_current, Set<Ref<Script> > &r_scripts) {
+
+ if (p_current->get_owner()!=p_base && p_base!=p_current)
+ return;
+ Ref<Script> c = p_current->get_script();
+
+ if (c.is_valid())
+ r_scripts.insert(c);
+
+ for(int i=0;i<p_current->get_child_count();i++) {
+ _find_changed_scripts_for_external_editor(p_base,p_current->get_child(i),r_scripts);
+ }
+
+}
+
+void ScriptEditor::_update_modified_scripts_for_external_editor(Ref<Script> p_for_script) {
+
+ if (!bool(EditorSettings::get_singleton()->get("external_editor/use_external_editor")))
+ return;
+
+ Set<Ref<Script> > scripts;
+
+ Node *base = get_tree()->get_edited_scene_root();
+ if (base) {
+ _find_changed_scripts_for_external_editor(base,base,scripts);
+ }
+
+ for (Set<Ref<Script> >::Element *E=scripts.front();E;E=E->next()) {
+
+ Ref<Script> script = E->get();
+
+ if (p_for_script.is_valid() && p_for_script!=script)
+ continue;
+
+ if (script->get_path()=="" || script->get_path().find("local://")!=-1 || script->get_path().find("::")!=-1) {
+
+ continue; //internal script, who cares, though weird
+ }
+
+ uint64_t last_date = script->get_last_modified_time();
+ uint64_t date = FileAccess::get_modified_time(script->get_path());
+
+ if (last_date!=date) {
+
+ 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->update_exports();
+ }
+
+ }
+}
+
+
+
+void ScriptTextEditor::_code_complete_script(const String& p_code, List<String>* r_options) {
+
+ Node *base = get_tree()->get_edited_scene_root();
+ if (base) {
+ base = _find_node_for_script(base,base,script);
+ }
+ String hint;
+ Error err = script->get_language()->complete_code(p_code,script->get_path().get_base_dir(),base,r_options,hint);
+ if (hint!="") {
+ get_text_edit()->set_code_hint(hint);
+ }
+
+}
+void ScriptTextEditor::_bind_methods() {
+
+ ADD_SIGNAL(MethodInfo("name_changed"));
+}
+
+ScriptTextEditor::ScriptTextEditor() {
+}
+
+/*** 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) {
+
+ if (bool(EditorSettings::get_singleton()->get("text_editor/external/use_external_editor"))) {
+ return;
+ }
+
+ 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::_script_created(Ref<Script> p_script) {
+ editor->push_item(p_script.operator->());
+}
+
+void ScriptEditor::_trim_trailing_whitespace(TextEdit *tx) {
+
+ bool trimed_whitespace = false;
+ for (int i = 0; i < tx->get_line_count(); i++) {
+ String line = tx->get_line(i);
+ if (line.ends_with(" ") || line.ends_with("\t")) {
+
+ if (!trimed_whitespace) {
+ tx->begin_complex_operation();
+ trimed_whitespace = true;
+ }
+
+ int end = 0;
+ for (int j = line.length() - 1; j > -1; j--) {
+ if (line[j] != ' ' && line[j] != '\t') {
+ end = j+1;
+ break;
+ }
+ }
+ tx->set_line(i, line.substr(0, end));
+ }
+ }
+ if (trimed_whitespace) {
+ tx->end_complex_operation();
+ tx->update();
+ }
+}
+
+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::_update_history_arrows() {
+
+ script_back->set_disabled( history_pos<=0 );
+ script_forward->set_disabled( history_pos>=history.size()-1 );
+}
+
+
+void ScriptEditor::_go_to_tab(int p_idx) {
+
+ Node *cn = tab_container->get_child(p_idx);
+ if (!cn)
+ return;
+ Control *c = cn->cast_to<Control>();
+ if (!c)
+ return;
+
+ if (history_pos>=0 && history_pos<history.size() && history[history_pos].control==tab_container->get_current_tab_control()) {
+
+ Node *n = tab_container->get_current_tab_control();
+
+ if (n->cast_to<ScriptTextEditor>()) {
+
+ history[history_pos].scroll_pos=n->cast_to<ScriptTextEditor>()->get_text_edit()->get_v_scroll();
+ history[history_pos].cursor_column=n->cast_to<ScriptTextEditor>()->get_text_edit()->cursor_get_column();
+ history[history_pos].cursor_row=n->cast_to<ScriptTextEditor>()->get_text_edit()->cursor_get_line();
+ }
+ if (n->cast_to<EditorHelp>()) {
+
+ history[history_pos].scroll_pos=n->cast_to<EditorHelp>()->get_scroll();
+ }
+ }
+
+ history.resize(history_pos+1);
+ ScriptHistory sh;
+ sh.control=c;
+ sh.scroll_pos=0;
+
+ history.push_back(sh);
+ history_pos++;
+
+
+ tab_container->set_current_tab(p_idx);
+
+ c = tab_container->get_current_tab_control();
+
+ if (c->cast_to<ScriptTextEditor>()) {
+
+ script_name_label->set_text(c->cast_to<ScriptTextEditor>()->get_name());
+ script_icon->set_texture(c->cast_to<ScriptTextEditor>()->get_icon());
+ if (is_visible())
+ c->cast_to<ScriptTextEditor>()->get_text_edit()->grab_focus();
+ }
+ if (c->cast_to<EditorHelp>()) {
+
+ script_name_label->set_text(c->cast_to<EditorHelp>()->get_class_name());
+ script_icon->set_texture(get_icon("Help","EditorIcons"));
+ if (is_visible())
+ c->cast_to<EditorHelp>()->set_focused();
+ }
+
+
+
+ c->set_meta("__editor_pass",++edit_pass);
+ _update_history_arrows();
+ _update_script_colors();
+}
+
+void ScriptEditor::_close_tab(int p_idx) {
+
+ int selected = p_idx;
+ if (selected<0 || selected>=tab_container->get_child_count())
+ return;
+
+ Node *tselected = tab_container->get_child(selected);
+ ScriptTextEditor *current = tab_container->get_child(selected)->cast_to<ScriptTextEditor>();
+ if (current) {
+ apply_scripts();
+ }
+
+ //remove from history
+ history.resize(history_pos+1);
+
+ for(int i=0;i<history.size();i++) {
+ if (history[i].control==tselected) {
+ history.remove(i);
+ i--;
+ history_pos--;
+ }
+ }
+
+ if (history_pos>=history.size()) {
+ history_pos=history.size()-1;
+ }
+
+ int idx = tab_container->get_current_tab();
+ memdelete(tselected);
+ if (idx>=tab_container->get_child_count())
+ idx=tab_container->get_child_count()-1;
+ if (idx>=0) {
+
+ if (history_pos>=0) {
+ idx = history[history_pos].control->get_index();
+ }
+ tab_container->set_current_tab(idx);
+
+ //script_list->select(idx);
+ }
+
+
+ _update_history_arrows();
+
+
+
+ _update_script_names();
+ _save_layout();
+}
+
+void ScriptEditor::_close_current_tab() {
+
+ _close_tab(tab_container->get_current_tab());
+
+}
+
+void ScriptEditor::_close_docs_tab() {
+
+ int child_count = tab_container->get_child_count();
+ for (int i = child_count-1; i>=0; i--) {
+
+ EditorHelp *ste = tab_container->get_child(i)->cast_to<EditorHelp>();
+
+ if (ste) {
+ _close_tab(i);
+ }
+
+ }
+
+}
+
+
+
+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
+
+ if (trim_trailing_whitespace_on_save) {
+ _trim_trailing_whitespace(ste->get_text_edit());
+ }
+ 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
+ }
+
+
+ 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) {
+ continue;
+ }
+
+
+ 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();
+ _update_script_names();
+
+}
+
+
+
+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();
+ }
+
+ }
+
+ _update_script_names();
+
+
+ if (!pending_auto_reload && auto_reload_running_scripts) {
+ call_deferred("_live_auto_reload_running_scripts");
+ pending_auto_reload=true;
+ }
+}
+
+void ScriptEditor::_live_auto_reload_running_scripts() {
+ pending_auto_reload=false;
+ debugger->reload_scripts();
+}
+
+
+bool ScriptEditor::_test_script_times_on_disk(Ref<Script> p_for_script) {
+
+
+ disk_changed_list->clear();
+ TreeItem *r = disk_changed_list->create_item();
+ disk_changed_list->set_hide_root(true);
+
+ bool need_ask=false;
+ bool need_reload=false;
+ bool use_autoreload=bool(EDITOR_DEF("text_editor/auto_reload_scripts_on_external_change",false));
+
+
+
+ for(int i=0;i<tab_container->get_child_count();i++) {
+
+ ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>();
+ if (ste) {
+
+ Ref<Script> script = ste->get_edited_script();
+
+ if (p_for_script.is_valid() && p_for_script!=script)
+ continue;
+
+ 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());
+
+ if (!use_autoreload || ste->is_unsaved()) {
+ need_ask=true;
+ }
+ need_reload=true;
+ //r->set_metadata(0,);
+ }
+ }
+ }
+
+
+
+ if (need_reload) {
+ if (!need_ask) {
+ script_editor->_reload_scripts();
+ need_reload=false;
+ } else {
+ disk_changed->call_deferred("popup_centered_ratio",0.5);
+ }
+ }
+
+ return need_reload;
+}
+
+void ScriptEditor::swap_lines(TextEdit *tx, int line1, int line2)
+{
+ String tmp = tx->get_line(line1);
+ String tmp2 = tx->get_line(line2);
+ tx->set_line(line2, tmp);
+ tx->set_line(line1, tmp2);
+
+ tx->cursor_set_line(line2);
+}
+
+void ScriptEditor::_breakpoint_toggled(const int p_row) {
+ 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) {
+ get_debugger()->set_breakpoint(current->get_edited_script()->get_path(),p_row+1,current->get_text_edit()->is_line_set_as_breakpoint(p_row));
+ }
+}
+
+void ScriptEditor::_file_dialog_action(String p_file) {
+
+ switch (file_dialog_option) {
+ case FILE_SAVE_THEME_AS: {
+ if(!EditorSettings::get_singleton()->save_text_editor_theme_as(p_file)) {
+ editor->show_warning(TTR("Error while saving theme"), TTR("Error saving"));
+ }
+ } break;
+ case FILE_IMPORT_THEME: {
+ if(!EditorSettings::get_singleton()->import_text_editor_theme(p_file)) {
+ editor->show_warning(TTR("Error importing theme"), TTR("Error importing"));
+ }
+ } break;
+ }
+ file_dialog_option = -1;
+}
+
+void ScriptEditor::_menu_option(int p_option) {
+
+
+ switch(p_option) {
+ case FILE_NEW: {
+ script_create_dialog->config("Node", ".gd");
+ script_create_dialog->popup_centered(Size2(300, 300)*EDSCALE);
+ } break;
+ case FILE_OPEN: {
+
+ editor->open_resource("Script");
+ return;
+ } break;
+ case FILE_SAVE_ALL: {
+
+ if (!_test_script_times_on_disk())
+ return;
+
+ save_all_scripts();
+
+#if 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;
+
+
+ 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 );
+ }
+
+#endif
+ } break;
+ case FILE_IMPORT_THEME: {
+ file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE);
+ file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
+ file_dialog_option = FILE_IMPORT_THEME;
+ file_dialog->clear_filters();
+ file_dialog->add_filter("*.tet");
+ file_dialog->popup_centered_ratio();
+ file_dialog->set_title(TTR("Import Theme"));
+ } break;
+ case FILE_RELOAD_THEME: {
+ EditorSettings::get_singleton()->load_text_editor_theme();
+ } break;
+ case FILE_SAVE_THEME: {
+ if(!EditorSettings::get_singleton()->save_text_editor_theme()) {
+ editor->show_warning(TTR("Error while saving theme"), TTR("Error saving"));
+ }
+ } break;
+ case FILE_SAVE_THEME_AS: {
+ file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE);
+ file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
+ file_dialog_option = FILE_SAVE_THEME_AS;
+ file_dialog->clear_filters();
+ file_dialog->add_filter("*.tet");
+ file_dialog->set_current_path(EditorSettings::get_singleton()->get_settings_path() + "/text_editor_themes/" + EditorSettings::get_singleton()->get("text_editor/color_theme"));
+ file_dialog->popup_centered_ratio();
+ file_dialog->set_title(TTR("Save Theme As.."));
+ } break;
+ case SEARCH_HELP: {
+
+ help_search_dialog->popup();
+ } break;
+ case SEARCH_CLASSES: {
+
+ String current;
+
+ if (tab_container->get_tab_count()>0) {
+ EditorHelp *eh = tab_container->get_child( tab_container->get_current_tab() )->cast_to<EditorHelp>();
+ if (eh) {
+ current=eh->get_class_name();
+ }
+ }
+
+ help_index->popup();
+
+ if (current!="") {
+ help_index->call_deferred("select_class",current);
+ }
+ } break;
+ case SEARCH_WEBSITE: {
+
+ OS::get_singleton()->shell_open("http://docs.godotengine.org/");
+ } break;
+
+ case WINDOW_NEXT: {
+
+ _history_forward();
+ } break;
+ case WINDOW_PREV: {
+ _history_back();
+ } 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 DEBUG_SHOW_KEEP_OPEN: {
+ bool visible = debug_menu->get_popup()->is_item_checked( debug_menu->get_popup()->get_item_index(DEBUG_SHOW_KEEP_OPEN) );
+ if (debugger)
+ debugger->set_hide_on_stop(visible);
+ debug_menu->get_popup()->set_item_checked( debug_menu->get_popup()->get_item_index(DEBUG_SHOW_KEEP_OPEN), !visible);
+ } break;
+ }
+
+
+ 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) {
+
+ switch(p_option) {
+ case FILE_NEW: {
+ script_create_dialog->config("Node", ".gd");
+ script_create_dialog->popup_centered(Size2(300, 300)*EDSCALE);
+ } break;
+ case FILE_SAVE: {
+
+ if (_test_script_times_on_disk())
+ return;
+
+ if (trim_trailing_whitespace_on_save) {
+ _trim_trailing_whitespace(current->get_text_edit());
+ }
+ editor->save_resource( current->get_edited_script() );
+
+ } break;
+ case FILE_SAVE_AS: {
+
+ if (trim_trailing_whitespace_on_save) {
+ _trim_trailing_whitespace(current->get_text_edit());
+ }
+ editor->push_item(current->get_edited_script()->cast_to<Object>());
+ editor->save_resource_as( current->get_edited_script() );
+
+ } break;
+ case EDIT_UNDO: {
+ current->get_text_edit()->undo();
+ current->get_text_edit()->call_deferred("grab_focus");
+ } break;
+ case EDIT_REDO: {
+ current->get_text_edit()->redo();
+ current->get_text_edit()->call_deferred("grab_focus");
+ } break;
+ case EDIT_CUT: {
+
+ current->get_text_edit()->cut();
+ current->get_text_edit()->call_deferred("grab_focus");
+ } break;
+ case EDIT_COPY: {
+ current->get_text_edit()->copy();
+ current->get_text_edit()->call_deferred("grab_focus");
+
+ } break;
+ case EDIT_PASTE: {
+ current->get_text_edit()->paste();
+ current->get_text_edit()->call_deferred("grab_focus");
+
+ } break;
+ case EDIT_SELECT_ALL: {
+
+ current->get_text_edit()->select_all();
+ current->get_text_edit()->call_deferred("grab_focus");
+
+ } break;
+ case EDIT_MOVE_LINE_UP: {
+
+ TextEdit *tx = current->get_text_edit();
+ Ref<Script> scr = current->get_edited_script();
+ if (scr.is_null())
+ return;
+
+ tx->begin_complex_operation();
+ if (tx->is_selection_active())
+ {
+ int from_line = tx->get_selection_from_line();
+ int from_col = tx->get_selection_from_column();
+ int to_line = tx->get_selection_to_line();
+ int to_column = tx->get_selection_to_column();
+
+ for (int i = from_line; i <= to_line; i++)
+ {
+ int line_id = i;
+ int next_id = i - 1;
+
+ if (line_id == 0 || next_id < 0)
+ return;
+
+ swap_lines(tx, line_id, next_id);
+ }
+ int from_line_up = from_line > 0 ? from_line-1 : from_line;
+ int to_line_up = to_line > 0 ? to_line-1 : to_line;
+ tx->select(from_line_up, from_col, to_line_up, to_column);
+ }
+ else
+ {
+ int line_id = tx->cursor_get_line();
+ int next_id = line_id - 1;
+
+ if (line_id == 0 || next_id < 0)
+ return;
+
+ swap_lines(tx, line_id, next_id);
+ }
+ tx->end_complex_operation();
+ tx->update();
+
+ } break;
+ case EDIT_MOVE_LINE_DOWN: {
+
+ TextEdit *tx = current->get_text_edit();
+ Ref<Script> scr = current->get_edited_script();
+ if (scr.is_null())
+ return;
+
+ tx->begin_complex_operation();
+ if (tx->is_selection_active())
+ {
+ int from_line = tx->get_selection_from_line();
+ int from_col = tx->get_selection_from_column();
+ int to_line = tx->get_selection_to_line();
+ int to_column = tx->get_selection_to_column();
+
+ for (int i = to_line; i >= from_line; i--)
+ {
+ int line_id = i;
+ int next_id = i + 1;
+
+ if (line_id == tx->get_line_count()-1 || next_id > tx->get_line_count())
+ return;
+
+ swap_lines(tx, line_id, next_id);
+ }
+ int from_line_down = from_line < tx->get_line_count() ? from_line+1 : from_line;
+ int to_line_down = to_line < tx->get_line_count() ? to_line+1 : to_line;
+ tx->select(from_line_down, from_col, to_line_down, to_column);
+ }
+ else
+ {
+ int line_id = tx->cursor_get_line();
+ int next_id = line_id + 1;
+
+ if (line_id == tx->get_line_count()-1 || next_id > tx->get_line_count())
+ return;
+
+ swap_lines(tx, line_id, next_id);
+ }
+ tx->end_complex_operation();
+ tx->update();
+
+ } break;
+ case EDIT_INDENT_LEFT: {
+
+ TextEdit *tx = current->get_text_edit();
+ Ref<Script> scr = current->get_edited_script();
+ if (scr.is_null())
+ return;
+
+ tx->begin_complex_operation();
+ if (tx->is_selection_active())
+ {
+ tx->indent_selection_left();
+ }
+ else
+ {
+ int begin = tx->cursor_get_line();
+ String line_text = tx->get_line(begin);
+ // begins with tab
+ if (line_text.begins_with("\t"))
+ {
+ line_text = line_text.substr(1, line_text.length());
+ tx->set_line(begin, line_text);
+ }
+ // begins with 4 spaces
+ else if (line_text.begins_with(" "))
+ {
+ line_text = line_text.substr(4, line_text.length());
+ tx->set_line(begin, line_text);
+ }
+ }
+ tx->end_complex_operation();
+ tx->update();
+ //tx->deselect();
+
+ } break;
+ case EDIT_INDENT_RIGHT: {
+
+ TextEdit *tx = current->get_text_edit();
+ Ref<Script> scr = current->get_edited_script();
+ if (scr.is_null())
+ return;
+
+ tx->begin_complex_operation();
+ if (tx->is_selection_active())
+ {
+ tx->indent_selection_right();
+ }
+ else
+ {
+ int begin = tx->cursor_get_line();
+ String line_text = tx->get_line(begin);
+ line_text = '\t' + line_text;
+ tx->set_line(begin, line_text);
+ }
+ tx->end_complex_operation();
+ tx->update();
+ //tx->deselect();
+
+ } break;
+ case EDIT_CLONE_DOWN: {
+
+ TextEdit *tx = current->get_text_edit();
+ Ref<Script> scr = current->get_edited_script();
+ if (scr.is_null())
+ return;
+
+ int from_line = tx->cursor_get_line();
+ int to_line = tx->cursor_get_line();
+ int column = tx->cursor_get_column();
+
+ if (tx->is_selection_active()) {
+ from_line = tx->get_selection_from_line();
+ to_line = tx->get_selection_to_line();
+ column = tx->cursor_get_column();
+ }
+ int next_line = to_line + 1;
+
+ tx->begin_complex_operation();
+ for (int i = from_line; i <= to_line; i++) {
+
+ if (i >= tx->get_line_count() - 1) {
+ tx->set_line(i, tx->get_line(i) + "\n");
+ }
+ String line_clone = tx->get_line(i);
+ tx->insert_at(line_clone, next_line);
+ next_line++;
+ }
+
+ tx->cursor_set_column(column);
+ if (tx->is_selection_active()) {
+ tx->select(to_line + 1, tx->get_selection_from_column(), next_line - 1, tx->get_selection_to_column());
+ }
+
+ tx->end_complex_operation();
+ tx->update();
+
+ } break;
+ case EDIT_TOGGLE_COMMENT: {
+
+ TextEdit *tx = current->get_text_edit();
+ Ref<Script> scr = current->get_edited_script();
+ if (scr.is_null())
+ return;
+
+
+ tx->begin_complex_operation();
+ if (tx->is_selection_active())
+ {
+ int begin = tx->get_selection_from_line();
+ int end = tx->get_selection_to_line();
+
+ // End of selection ends on the first column of the last line, ignore it.
+ if(tx->get_selection_to_column() == 0)
+ end -= 1;
+
+ for (int i = begin; i <= end; i++)
+ {
+ String line_text = tx->get_line(i);
+
+ if (line_text.begins_with("#"))
+ line_text = line_text.substr(1, line_text.length());
+ else
+ line_text = "#" + line_text;
+ tx->set_line(i, line_text);
+ }
+ }
+ else
+ {
+ int begin = tx->cursor_get_line();
+ String line_text = tx->get_line(begin);
+
+ if (line_text.begins_with("#"))
+ line_text = line_text.substr(1, line_text.length());
+ else
+ line_text = "#" + line_text;
+ tx->set_line(begin, line_text);
+ }
+ tx->end_complex_operation();
+ tx->update();
+ //tx->deselect();
+
+ } break;
+ case EDIT_COMPLETE: {
+
+ current->get_text_edit()->query_code_comple();
+
+ } break;
+ case EDIT_AUTO_INDENT: {
+
+ TextEdit *te = current->get_text_edit();
+ String text = te->get_text();
+ Ref<Script> scr = current->get_edited_script();
+ if (scr.is_null())
+ return;
+ int begin,end;
+ if (te->is_selection_active()) {
+ begin=te->get_selection_from_line();
+ end=te->get_selection_to_line();
+ } else {
+ begin=0;
+ end=te->get_line_count()-1;
+ }
+ scr->get_language()->auto_indent_code(text,begin,end);
+ te->set_text(text);
+
+
+ } break;
+ case FILE_TOOL_RELOAD:
+ case FILE_TOOL_RELOAD_SOFT: {
+
+ TextEdit *te = current->get_text_edit();
+ Ref<Script> scr = current->get_edited_script();
+ if (scr.is_null())
+ return;
+ scr->set_source_code(te->get_text());
+ bool soft = p_option==FILE_TOOL_RELOAD_SOFT || scr->get_instance_base_type()=="EditorPlugin"; //always soft-reload editor plugins
+
+ scr->get_language()->reload_tool_script(scr,soft);
+ } break;
+ case EDIT_TRIM_TRAILING_WHITESAPCE: {
+ _trim_trailing_whitespace(current->get_text_edit());
+ } break;
+ case SEARCH_FIND: {
+
+ current->get_find_replace_bar()->popup_search();
+ } break;
+ case SEARCH_FIND_NEXT: {
+
+ current->get_find_replace_bar()->search_next();
+ } break;
+ case SEARCH_FIND_PREV: {
+
+ current->get_find_replace_bar()->search_prev();
+ } break;
+ case SEARCH_REPLACE: {
+
+ current->get_find_replace_bar()->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);
+ get_debugger()->set_breakpoint(current->get_edited_script()->get_path(),line+1,dobreak);
+ } break;
+ case DEBUG_REMOVE_ALL_BREAKPOINTS: {
+ List<int> bpoints;
+ current->get_text_edit()->get_breakpoints(&bpoints);
+
+ for(List<int>::Element *E=bpoints.front();E;E=E->next()) {
+ int line = E->get();
+ bool dobreak = !current->get_text_edit()->is_line_set_as_breakpoint(line);
+ current->get_text_edit()->set_line_as_breakpoint(line,dobreak);
+ get_debugger()->set_breakpoint(current->get_edited_script()->get_path(),line+1,dobreak);
+ }
+ }
+ case DEBUG_GOTO_NEXT_BREAKPOINT: {
+ List<int> bpoints;
+ current->get_text_edit()->get_breakpoints(&bpoints);
+ if (bpoints.size() <= 0) {
+ return;
+ }
+
+ int line=current->get_text_edit()->cursor_get_line();
+ // wrap around
+ if (line >= bpoints[bpoints.size() - 1]) {
+ current->get_text_edit()->cursor_set_line(bpoints[0]);
+ } else {
+ for(List<int>::Element *E=bpoints.front();E;E=E->next()) {
+ int bline = E->get();
+ if (bline > line) {
+ current->get_text_edit()->cursor_set_line(bline);
+ return;
+ }
+ }
+ }
+
+ } break;
+ case DEBUG_GOTO_PREV_BREAKPOINT: {
+ List<int> bpoints;
+ current->get_text_edit()->get_breakpoints(&bpoints);
+ if (bpoints.size() <= 0) {
+ return;
+ }
+
+ int line=current->get_text_edit()->cursor_get_line();
+ // wrap around
+ if (line <= bpoints[0]) {
+ current->get_text_edit()->cursor_set_line(bpoints[bpoints.size() - 1]);
+ } else {
+ for(List<int>::Element *E=bpoints.back();E;E=E->prev()) {
+ int bline = E->get();
+ if (bline < line) {
+ current->get_text_edit()->cursor_set_line(bline);
+ return;
+ }
+ }
+ }
+
+ } 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 HELP_CONTEXTUAL: {
+ String text = current->get_text_edit()->get_selection_text();
+ if (text == "")
+ text = current->get_text_edit()->get_word_under_cursor();
+ if (text != "")
+ help_search_dialog->popup(text);
+ } break;
+ case FILE_CLOSE: {
+ if (current->get_text_edit()->get_version()!=current->get_text_edit()->get_saved_version()) {
+ erase_tab_confirm->set_text("Close and save changes?\n\""+current->get_name()+"\"");
+ erase_tab_confirm->popup_centered_minsize();
+ } else {
+ _close_current_tab();
+ }
+ } break;
+ case CLOSE_DOCS: {
+ _close_docs_tab();
+ } 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);
+ script_list->call_deferred("select",tab_container->get_current_tab()-1);
+ tab_container->move_child(current,tab_container->get_current_tab()-1);
+ _update_script_names();
+ }
+ } 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);
+ script_list->call_deferred("select",tab_container->get_current_tab()+1);
+ tab_container->move_child(current,tab_container->get_current_tab()+1);
+ _update_script_names();
+ }
+
+
+ } break;
+
+ default: {
+
+ if (p_option>=WINDOW_SELECT_BASE) {
+
+ tab_container->set_current_tab(p_option-WINDOW_SELECT_BASE);
+ script_list->select(p_option-WINDOW_SELECT_BASE);
+
+ }
+ }
+ }
+ }
+
+ EditorHelp *help = tab_container->get_current_tab_control()->cast_to<EditorHelp>();
+ if (help) {
+
+ switch(p_option) {
+
+ case SEARCH_FIND: {
+ help->popup_search();
+ } break;
+ case SEARCH_FIND_NEXT: {
+ help->search_again();
+ } break;
+ case FILE_CLOSE: {
+ _close_current_tab();
+ } break;
+ case CLOSE_DOCS: {
+ _close_docs_tab();
+ } break;
+
+
+ }
+ }
+
+
+}
+
+void ScriptEditor::_tab_changed(int p_which) {
+
+ ensure_select_current();
+}
+
+void ScriptEditor::_notification(int p_what) {
+
+ if (p_what==NOTIFICATION_ENTER_TREE) {
+
+ 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");
+ script_list->connect("item_selected",this,"_script_selected");
+ script_split->connect("dragged",this,"_script_split_dragged");
+ autosave_timer->connect("timeout",this,"_autosave_scripts");
+ {
+ float autosave_time = EditorSettings::get_singleton()->get("text_editor/autosave_interval_secs");
+ if (autosave_time>0) {
+ autosave_timer->set_wait_time(autosave_time);
+ autosave_timer->start();
+ } else {
+ autosave_timer->stop();
+ }
+ }
+
+ EditorSettings::get_singleton()->connect("settings_changed",this,"_editor_settings_changed");
+ help_search->set_icon(get_icon("Help","EditorIcons"));
+ site_search->set_icon(get_icon("Godot","EditorIcons"));
+ class_search->set_icon(get_icon("ClassList","EditorIcons"));
+
+ script_forward->set_icon(get_icon("Forward","EditorIcons"));
+ script_back->set_icon(get_icon("Back","EditorIcons"));
+
+
+
+
+ }
+
+ if (p_what==NOTIFICATION_READY) {
+
+ get_tree()->connect("tree_changed",this,"_tree_changed");
+ editor->connect("request_help",this,"_request_help");
+ }
+
+ if (p_what==NOTIFICATION_EXIT_TREE) {
+
+ 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();
+ _update_modified_scripts_for_external_editor();
+ }
+
+ if (p_what==NOTIFICATION_PROCESS) {
+
+ }
+
+}
+
+
+void ScriptEditor::close_builtin_scripts_from_scene(const String& p_scene) {
+
+
+
+ for(int i=0;i<tab_container->get_child_count();i++) {
+
+ ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>();
+
+ if (ste) {
+
+ Ref<Script> script = ste->get_edited_script();
+ if (!script.is_valid())
+ continue;
+
+ if (script->get_path().find("::")!=-1 && script->get_path().begins_with(p_scene)) { //is an internal script and belongs to scene being closed
+ _close_tab(i);
+ i--;
+
+ }
+ }
+
+ }
+
+
+}
+
+void ScriptEditor::edited_scene_changed() {
+
+ _update_modified_scripts_for_external_editor();
+
+}
+
+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;
+#if 0
+ 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_tree()->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;
+
+#endif
+ return state;
+}
+void ScriptEditor::set_state(const Dictionary& p_state) {
+
+#if 0
+ 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_tree()->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"]);
+ }
+#endif
+
+}
+void ScriptEditor::clear() {
+#if 0
+ 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);
+ script_list->select( script_list->find_metadata(idx) );
+ }
+
+#endif
+
+
+}
+
+
+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::ensure_focus_current() {
+
+ if (!is_inside_tree())
+ return;
+
+ 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::_script_selected(int p_idx) {
+
+ grab_focus_block = !Input::get_singleton()->is_mouse_button_pressed(1); //amazing hack, simply amazing
+
+ _go_to_tab(script_list->get_item_metadata(p_idx));
+ grab_focus_block=false;
+}
+
+void ScriptEditor::ensure_select_current() {
+
+
+ if (tab_container->get_child_count() && tab_container->get_current_tab()>=0) {
+
+ Node *current = tab_container->get_child(tab_container->get_current_tab());
+
+
+ ScriptTextEditor *ste = current->cast_to<ScriptTextEditor>();
+ if (ste) {
+
+ Ref<Script> script = ste->get_edited_script();
+
+ if (!grab_focus_block && is_visible())
+ ste->get_text_edit()->grab_focus();
+
+ edit_menu->show();
+ search_menu->show();
+ script_search_menu->hide();
+
+
+ }
+
+ EditorHelp *eh = current->cast_to<EditorHelp>();
+
+ if (eh) {
+ edit_menu->hide();
+ search_menu->hide();
+ script_search_menu->show();
+
+ }
+ }
+
+
+
+
+
+}
+
+void ScriptEditor::_find_scripts(Node* p_base, Node* p_current, Set<Ref<Script> > &used) {
+ if (p_current!=p_base && p_current->get_owner()!=p_base)
+ return;
+
+ if (p_current->get_script_instance()) {
+ Ref<Script> scr = p_current->get_script();
+ if (scr.is_valid())
+ used.insert(scr);
+ }
+
+ for(int i=0;i<p_current->get_child_count();i++) {
+ _find_scripts(p_base,p_current->get_child(i),used);
+ }
+
+}
+
+struct _ScriptEditorItemData {
+
+ String name;
+ String sort_key;
+ Ref<Texture> icon;
+ int index;
+ String tooltip;
+ bool used;
+ int category;
+
+
+ bool operator<(const _ScriptEditorItemData& id) const {
+
+ return category==id.category?sort_key<id.sort_key:category<id.category;
+ }
+
+};
+
+
+void ScriptEditor::_update_script_colors() {
+
+ bool script_temperature_enabled = EditorSettings::get_singleton()->get("text_editor/script_temperature_enabled");
+ bool highlight_current = EditorSettings::get_singleton()->get("text_editor/highlight_current_script");
+
+ int hist_size = EditorSettings::get_singleton()->get("text_editor/script_temperature_history_size");
+ Color hot_color=EditorSettings::get_singleton()->get("text_editor/script_temperature_hot_color");
+ Color cold_color=EditorSettings::get_singleton()->get("text_editor/script_temperature_cold_color");
+
+ for(int i=0;i<script_list->get_item_count();i++) {
+
+ int c = script_list->get_item_metadata(i);
+ Node *n = tab_container->get_child(c);
+ if (!n)
+ continue;
+
+ script_list->set_item_custom_bg_color(i,Color(0,0,0,0));
+
+ bool current = tab_container->get_current_tab() == c;
+ if (current && highlight_current) {
+ script_list->set_item_custom_bg_color(i, EditorSettings::get_singleton()->get("text_editor/current_script_background_color"));
+
+ } else if (script_temperature_enabled) {
+
+ if (!n->has_meta("__editor_pass")) {
+ continue;
+ }
+
+ int pass=n->get_meta("__editor_pass");
+ int h = edit_pass - pass;
+ if (h>hist_size) {
+ continue;
+ }
+ int non_zero_hist_size = ( hist_size == 0 ) ? 1 : hist_size;
+ float v = Math::ease((edit_pass-pass)/float(non_zero_hist_size),0.4);
+
+ script_list->set_item_custom_bg_color(i,hot_color.linear_interpolate(cold_color,v));
+ }
+ }
+}
+
+void ScriptEditor::_update_script_names() {
+
+ if (restoring_layout)
+ return;
+
+ waiting_update_names=false;
+ Set<Ref<Script> > used;
+ Node* edited = EditorNode::get_singleton()->get_edited_scene();
+ if (edited) {
+ _find_scripts(edited,edited,used);
+ }
+
+ script_list->clear();
+ bool split_script_help = EditorSettings::get_singleton()->get("text_editor/group_help_pages");
+ ScriptSortBy sort_by = (ScriptSortBy) (int) EditorSettings::get_singleton()->get("text_editor/sort_scripts_by");
+ ScriptListName display_as = (ScriptListName) (int) EditorSettings::get_singleton()->get("text_editor/list_script_names_as");
+
+ Vector<_ScriptEditorItemData> sedata;
+
+ for(int i=0;i<tab_container->get_child_count();i++) {
+
+
+ ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>();
+ if (ste) {
+
+ String name = ste->get_name();
+ Ref<Texture> icon = ste->get_icon();
+ String path = ste->get_edited_script()->get_path();
+
+ _ScriptEditorItemData sd;
+ sd.icon=icon;
+ sd.name=name;
+ sd.tooltip=path;
+ sd.index=i;
+ sd.used=used.has(ste->get_edited_script());
+ sd.category=0;
+
+ switch (sort_by) {
+ case SORT_BY_NAME: {
+ sd.sort_key=name.to_lower();
+ } break;
+ case SORT_BY_PATH: {
+ sd.sort_key=path;
+ } break;
+ }
+
+ switch (display_as) {
+ case DISPLAY_NAME: {
+ sd.name=name;
+ } break;
+ case DISPLAY_DIR_AND_NAME: {
+ if (!path.get_base_dir().get_file().empty()) {
+ sd.name=path.get_base_dir().get_file() + "/" + name;
+ } else {
+ sd.name=name;
+ }
+ } break;
+ case DISPLAY_FULL_PATH: {
+ sd.name=path;
+ } break;
+ }
+
+
+ sedata.push_back(sd);
+ }
+
+ EditorHelp *eh = tab_container->get_child(i)->cast_to<EditorHelp>();
+ if (eh) {
+
+ String name = eh->get_class_name();
+ Ref<Texture> icon = get_icon("Help","EditorIcons");
+ String tooltip = name+" Class Reference";
+
+ _ScriptEditorItemData sd;
+ sd.icon=icon;
+ sd.name=name;
+ sd.sort_key=name;
+ sd.tooltip=tooltip;
+ sd.index=i;
+ sd.used=false;
+ sd.category=split_script_help?1:0;
+ sedata.push_back(sd);
+
+ }
+
+ }
+
+ sedata.sort();
+
+ for(int i=0;i<sedata.size();i++) {
+
+ script_list->add_item(sedata[i].name,sedata[i].icon);
+ int index = script_list->get_item_count()-1;
+ script_list->set_item_tooltip(index,sedata[i].tooltip);
+ script_list->set_item_metadata(index,sedata[i].index);
+ if (sedata[i].used) {
+
+ script_list->set_item_custom_bg_color(index,Color(88/255.0,88/255.0,60/255.0));
+ }
+ if (tab_container->get_current_tab()==sedata[i].index) {
+ script_list->select(index);
+ script_name_label->set_text(sedata[i].name);
+ script_icon->set_texture(sedata[i].icon);
+
+ }
+ }
+
+ _update_script_colors();
+
+
+
+
+}
+
+
+
+void ScriptEditor::edit(const Ref<Script>& p_script) {
+
+ if (p_script.is_null())
+ return;
+
+ // refuse to open built-in if scene is not loaded
+
+
+
+
+ // see if already has it
+
+ bool open_dominant = EditorSettings::get_singleton()->get("text_editor/open_dominant_script_on_scene_change");
+
+ 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 (open_dominant || !EditorNode::get_singleton()->is_changing_scene()) {
+ if (tab_container->get_current_tab()!=i) {
+ _go_to_tab(i);
+ script_list->select( script_list->find_metadata(i) );
+ }
+ if (is_visible())
+ ste->get_text_edit()->grab_focus();
+ }
+ return;
+ }
+ }
+
+ // doesn't have it, make a new one
+
+ ScriptTextEditor *ste = memnew( ScriptTextEditor );
+ ste->update_editor_settings();
+ ste->set_edited_script(p_script);
+ ste->get_text_edit()->set_tooltip_request_func(this,"_get_debug_tooltip",ste);
+ ste->get_text_edit()->set_callhint_settings(
+ EditorSettings::get_singleton()->get("text_editor/put_callhint_tooltip_below_current_line"),
+ EditorSettings::get_singleton()->get("text_editor/callhint_tooltip_offset"));
+ ste->get_text_edit()->connect("breakpoint_toggled", this, "_breakpoint_toggled");
+ tab_container->add_child(ste);
+ _go_to_tab(tab_container->get_tab_count()-1);
+
+
+
+
+ _update_script_names();
+ _save_layout();
+ ste->connect("name_changed",this,"_update_script_names");
+
+ //test for modification, maybe the script was not edited but was loaded
+
+ _test_script_times_on_disk(p_script);
+ _update_modified_scripts_for_external_editor(p_script);
+
+}
+
+void ScriptEditor::save_all_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;
+
+
+ if (trim_trailing_whitespace_on_save) {
+ _trim_trailing_whitespace(ste->get_text_edit());
+ }
+ if (ste->get_text_edit()->get_version()==ste->get_text_edit()->get_saved_version())
+ continue;
+
+ Ref<Script> script = ste->get_edited_script();
+ if (script.is_valid())
+ ste->apply_code();
+
+ 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()->grab_focus();
+ 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::_add_callback(Object *p_obj, const String& p_function, const StringArray& p_args) {
+
+ //print_line("add callback! hohoho"); kinda sad to remove this
+ 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(p_function,code);
+ if (pos==-1) {
+ //does not exist
+ ste->get_text_edit()->deselect();
+ 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);
+ }
+
+ _go_to_tab(i);
+ ste->get_text_edit()->cursor_set_line(pos);
+ ste->get_text_edit()->cursor_set_column(1);
+
+ script_list->select( script_list->find_metadata(i) );
+
+ break;
+
+ }
+
+}
+
+void ScriptEditor::_save_layout() {
+
+ if (restoring_layout) {
+ return;
+ }
+
+ editor->save_layout();
+}
+
+void ScriptEditor::_editor_settings_changed() {
+
+ trim_trailing_whitespace_on_save = EditorSettings::get_singleton()->get("text_editor/trim_trailing_whitespace_on_save");
+ float autosave_time = EditorSettings::get_singleton()->get("text_editor/autosave_interval_secs");
+ if (autosave_time>0) {
+ autosave_timer->set_wait_time(autosave_time);
+ autosave_timer->start();
+ } else {
+ autosave_timer->stop();
+ }
+
+ if (current_theme == "") {
+ current_theme = EditorSettings::get_singleton()->get("text_editor/color_theme");
+ } else if (current_theme != EditorSettings::get_singleton()->get("text_editor/color_theme")) {
+ current_theme = EditorSettings::get_singleton()->get("text_editor/color_theme");
+ EditorSettings::get_singleton()->load_text_editor_theme();
+ }
+
+ 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->update_editor_settings();
+ }
+ _update_script_colors();
+ _update_script_names();
+
+ ScriptServer::set_reload_scripts_on_save(EDITOR_DEF("text_editor/auto_reload_and_parse_scripts_on_save",true));
+
+}
+
+void ScriptEditor::_autosave_scripts() {
+
+ save_all_scripts();
+}
+
+void ScriptEditor::_tree_changed() {
+
+ if (waiting_update_names)
+ return;
+
+ waiting_update_names=true;
+ call_deferred("_update_script_names");
+}
+
+void ScriptEditor::_script_split_dragged(float) {
+
+ _save_layout();
+}
+
+void ScriptEditor::_unhandled_input(const InputEvent& p_event) {
+ if (p_event.key.pressed || !is_visible()) return;
+ if (ED_IS_SHORTCUT("script_editor/next_script", p_event)) {
+ int next_tab = script_list->get_current() + 1;
+ next_tab %= script_list->get_item_count();
+ _go_to_tab(script_list->get_item_metadata(next_tab));
+ _update_script_names();
+ }
+ if (ED_IS_SHORTCUT("script_editor/prev_script", p_event)) {
+ int next_tab = script_list->get_current() - 1;
+ next_tab = next_tab >= 0 ? next_tab : script_list->get_item_count() - 1;
+ _go_to_tab(script_list->get_item_metadata(next_tab));
+ _update_script_names();
+ }
+}
+
+void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) {
+
+ if (!bool(EDITOR_DEF("text_editor/restore_scripts_on_load",true))) {
+ return;
+ }
+
+ if (!p_layout->has_section_key("ScriptEditor","open_scripts") && !p_layout->has_section_key("ScriptEditor","open_help"))
+ return;
+
+ Array scripts = p_layout->get_value("ScriptEditor","open_scripts");
+ Array helps;
+ if (p_layout->has_section_key("ScriptEditor","open_help"))
+ helps=p_layout->get_value("ScriptEditor","open_help");
+
+ restoring_layout=true;
+
+ for(int i=0;i<scripts.size();i++) {
+
+ String path = scripts[i];
+ if (!FileAccess::exists(path))
+ continue;
+ Ref<Script> scr = ResourceLoader::load(path);
+ if (scr.is_valid()) {
+ edit(scr);
+ }
+ }
+
+ for(int i=0;i<helps.size();i++) {
+
+ String path = helps[i];
+ _help_class_open(path);
+ }
+
+ for(int i=0;i<tab_container->get_child_count();i++) {
+ tab_container->get_child(i)->set_meta("__editor_pass",Variant());
+ }
+
+
+ if (p_layout->has_section_key("ScriptEditor","split_offset")) {
+ script_split->set_split_offset(p_layout->get_value("ScriptEditor","split_offset"));
+ }
+
+ restoring_layout=false;
+
+ _update_script_names();
+}
+
+void ScriptEditor::get_window_layout(Ref<ConfigFile> p_layout) {
+
+ Array scripts;
+ Array helps;
+
+ for(int i=0;i<tab_container->get_child_count();i++) {
+
+ ScriptTextEditor *ste = tab_container->get_child(i)->cast_to<ScriptTextEditor>();
+ if (ste) {
+
+ String path = ste->get_edited_script()->get_path();
+ if (!path.is_resource_file())
+ continue;
+
+ scripts.push_back(path);
+ }
+
+ EditorHelp *eh = tab_container->get_child(i)->cast_to<EditorHelp>();
+
+ if (eh) {
+
+ helps.push_back(eh->get_class_name());
+ }
+
+
+ }
+
+ p_layout->set_value("ScriptEditor","open_scripts",scripts);
+ p_layout->set_value("ScriptEditor","open_help",helps);
+ p_layout->set_value("ScriptEditor","split_offset",script_split->get_split_offset());
+}
+
+
+void ScriptEditor::_help_class_open(const String& p_class) {
+
+
+ for(int i=0;i<tab_container->get_child_count();i++) {
+
+ EditorHelp *eh = tab_container->get_child(i)->cast_to<EditorHelp>();
+
+ if (eh && eh->get_class_name()==p_class) {
+
+ _go_to_tab(i);
+ _update_script_names();
+ return;
+ }
+ }
+
+ EditorHelp * eh = memnew( EditorHelp );
+
+
+ eh->set_name(p_class);
+ tab_container->add_child(eh);
+ _go_to_tab(tab_container->get_tab_count()-1);
+ eh->go_to_class(p_class,0);
+ eh->connect("go_to_help",this,"_help_class_goto");
+ _update_script_names();
+ _save_layout();
+}
+
+void ScriptEditor::_help_class_goto(const String& p_desc) {
+
+
+ String cname=p_desc.get_slice(":",1);
+
+ for(int i=0;i<tab_container->get_child_count();i++) {
+
+ EditorHelp *eh = tab_container->get_child(i)->cast_to<EditorHelp>();
+
+ if (eh && eh->get_class_name()==cname) {
+
+ _go_to_tab(i);
+ eh->go_to_help(p_desc);
+ _update_script_names();
+ return;
+ }
+ }
+
+ EditorHelp * eh = memnew( EditorHelp );
+
+ eh->set_name(cname);
+ tab_container->add_child(eh);
+ _go_to_tab(tab_container->get_tab_count()-1);
+ eh->go_to_help(p_desc);
+ eh->connect("go_to_help",this,"_help_class_goto");
+ _update_script_names();
+ _save_layout();
+}
+
+void ScriptEditor::_update_history_pos(int p_new_pos) {
+
+ Node *n = tab_container->get_current_tab_control();
+
+ if (n->cast_to<ScriptTextEditor>()) {
+
+ history[history_pos].scroll_pos=n->cast_to<ScriptTextEditor>()->get_text_edit()->get_v_scroll();
+ history[history_pos].cursor_column=n->cast_to<ScriptTextEditor>()->get_text_edit()->cursor_get_column();
+ history[history_pos].cursor_row=n->cast_to<ScriptTextEditor>()->get_text_edit()->cursor_get_line();
+ }
+ if (n->cast_to<EditorHelp>()) {
+
+ history[history_pos].scroll_pos=n->cast_to<EditorHelp>()->get_scroll();
+ }
+
+ history_pos=p_new_pos;
+ tab_container->set_current_tab(history[history_pos].control->get_index());
+
+ n = history[history_pos].control;
+
+ if (n->cast_to<ScriptTextEditor>()) {
+
+ n->cast_to<ScriptTextEditor>()->get_text_edit()->set_v_scroll(history[history_pos].scroll_pos);
+ n->cast_to<ScriptTextEditor>()->get_text_edit()->cursor_set_column( history[history_pos].cursor_column );
+ n->cast_to<ScriptTextEditor>()->get_text_edit()->cursor_set_line( history[history_pos].cursor_row );
+ n->cast_to<ScriptTextEditor>()->get_text_edit()->grab_focus();
+ }
+
+ if (n->cast_to<EditorHelp>()) {
+
+ n->cast_to<EditorHelp>()->set_scroll(history[history_pos].scroll_pos);
+ n->cast_to<EditorHelp>()->set_focused();
+ }
+
+ n->set_meta("__editor_pass",++edit_pass);
+ _update_script_names();
+ _update_history_arrows();
+
+}
+
+void ScriptEditor::_history_forward() {
+
+ if (history_pos<history.size()-1) {
+ _update_history_pos(history_pos+1);
+ }
+}
+
+void ScriptEditor::_history_back(){
+
+ if (history_pos>0) {
+ _update_history_pos(history_pos-1);
+ }
+
+}
+void ScriptEditor::set_scene_root_script( Ref<Script> p_script ) {
+
+ bool open_dominant = EditorSettings::get_singleton()->get("text_editor/open_dominant_script_on_scene_change");
+
+ if (bool(EditorSettings::get_singleton()->get("external_editor/use_external_editor")))
+ return;
+
+ if (open_dominant && p_script.is_valid() && _can_open_in_editor(p_script.ptr())) {
+ edit(p_script);
+ }
+}
+
+bool ScriptEditor::script_go_to_method(Ref<Script> p_script, const String& p_method) {
+
+ Vector<String> functions;
+ bool found=false;
+
+ for (int i=0;i<tab_container->get_child_count();i++) {
+ ScriptTextEditor *current = tab_container->get_child(i)->cast_to<ScriptTextEditor>();
+
+ if (current && current->get_edited_script()==p_script) {
+ functions=current->get_functions();
+ found=true;
+ break;
+ }
+ }
+
+ if (!found) {
+ String errortxt;
+ int line=-1,col;
+ String text=p_script->get_source_code();
+ List<String> fnc;
+
+ if (p_script->get_language()->validate(text,line,col,errortxt,p_script->get_path(),&fnc)) {
+
+ for (List<String>::Element *E=fnc.front();E;E=E->next())
+ functions.push_back(E->get());
+ }
+ }
+
+ String method_search = p_method + ":";
+
+ for (int i=0;i<functions.size();i++) {
+ String function=functions[i];
+
+ if (function.begins_with(method_search)) {
+
+ edit(p_script);
+ int line=function.get_slice(":",1).to_int();
+ _goto_script_line2(line-1);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void ScriptEditor::set_live_auto_reload_running_scripts(bool p_enabled) {
+
+ auto_reload_running_scripts=p_enabled;
+}
+
+void ScriptEditor::_bind_methods() {
+
+ ObjectTypeDB::bind_method("_file_dialog_action",&ScriptEditor::_file_dialog_action);
+ 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("_close_docs_tab", &ScriptEditor::_close_docs_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("_breakpoint_toggled", &ScriptEditor::_breakpoint_toggled);
+ ObjectTypeDB::bind_method("_breaked",&ScriptEditor::_breaked);
+ ObjectTypeDB::bind_method("_show_debugger",&ScriptEditor::_show_debugger);
+ ObjectTypeDB::bind_method("_get_debug_tooltip",&ScriptEditor::_get_debug_tooltip);
+ ObjectTypeDB::bind_method("_autosave_scripts",&ScriptEditor::_autosave_scripts);
+ ObjectTypeDB::bind_method("_editor_settings_changed",&ScriptEditor::_editor_settings_changed);
+ ObjectTypeDB::bind_method("_update_script_names",&ScriptEditor::_update_script_names);
+ ObjectTypeDB::bind_method("_tree_changed",&ScriptEditor::_tree_changed);
+ ObjectTypeDB::bind_method("_script_selected",&ScriptEditor::_script_selected);
+ ObjectTypeDB::bind_method("_script_created",&ScriptEditor::_script_created);
+ ObjectTypeDB::bind_method("_script_split_dragged",&ScriptEditor::_script_split_dragged);
+ ObjectTypeDB::bind_method("_help_class_open",&ScriptEditor::_help_class_open);
+ ObjectTypeDB::bind_method("_help_class_goto",&ScriptEditor::_help_class_goto);
+ ObjectTypeDB::bind_method("_request_help",&ScriptEditor::_help_class_open);
+ ObjectTypeDB::bind_method("_history_forward",&ScriptEditor::_history_forward);
+ ObjectTypeDB::bind_method("_history_back",&ScriptEditor::_history_back);
+ ObjectTypeDB::bind_method("_live_auto_reload_running_scripts",&ScriptEditor::_live_auto_reload_running_scripts);
+ ObjectTypeDB::bind_method("_unhandled_input",&ScriptEditor::_unhandled_input);
+
+}
+
+ScriptEditor::ScriptEditor(EditorNode *p_editor) {
+
+ current_theme = "";
+
+ completion_cache = memnew( EditorScriptCodeCompletionCache );
+ restoring_layout=false;
+ waiting_update_names=false;
+ pending_auto_reload=false;
+ auto_reload_running_scripts=false;
+ editor=p_editor;
+
+ menu_hb = memnew( HBoxContainer );
+ add_child(menu_hb);
+
+
+ script_split = memnew( HSplitContainer );
+ add_child(script_split);
+ script_split->set_v_size_flags(SIZE_EXPAND_FILL);
+
+ script_list = memnew( ItemList );
+ script_split->add_child(script_list);
+ script_list->set_custom_minimum_size(Size2(0,0));
+ script_split->set_split_offset(140);
+
+ tab_container = memnew( TabContainer );
+ tab_container->set_tabs_visible(false);
+ script_split->add_child(tab_container);
+
+
+ tab_container->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ ED_SHORTCUT("script_editor/next_script", TTR("Next script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_GREATER);
+ ED_SHORTCUT("script_editor/prev_script", TTR("Previous script"), KEY_MASK_CMD | KEY_LESS);
+ set_process_unhandled_input(true);
+
+ file_menu = memnew( MenuButton );
+ menu_hb->add_child(file_menu);
+ file_menu->set_text(TTR("File"));
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/new", TTR("New")), FILE_NEW);
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/open", TTR("Open")), FILE_OPEN);
+ file_menu->get_popup()->add_separator();
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save", TTR("Save"), KEY_MASK_ALT|KEY_MASK_CMD|KEY_S), FILE_SAVE);
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_as", TTR("Save As..")), FILE_SAVE_AS);
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_all", TTR("Save All"), KEY_MASK_CMD|KEY_MASK_SHIFT|KEY_MASK_ALT|KEY_S), FILE_SAVE_ALL);
+ file_menu->get_popup()->add_separator();
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_previous", TTR("History Prev"), KEY_MASK_CTRL|KEY_MASK_ALT|KEY_LEFT), WINDOW_PREV);
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_next", TTR("History Next"), KEY_MASK_CTRL|KEY_MASK_ALT|KEY_RIGHT), WINDOW_NEXT);
+ file_menu->get_popup()->add_separator();
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/import_theme", TTR("Import Theme")), FILE_IMPORT_THEME);
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/reload_theme", TTR("Reload Theme")), FILE_RELOAD_THEME);
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_theme", TTR("Save Theme")), FILE_SAVE_THEME);
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_theme_as", TTR("Save Theme As")), FILE_SAVE_THEME_AS);
+ file_menu->get_popup()->add_separator();
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/close_docs", TTR("Close Docs")), CLOSE_DOCS);
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/close_file", TTR("Close"), KEY_MASK_CMD | KEY_W), FILE_CLOSE);
+ file_menu->get_popup()->connect("item_pressed", this,"_menu_option");
+
+ edit_menu = memnew( MenuButton );
+ menu_hb->add_child(edit_menu);
+ edit_menu->set_text(TTR("Edit"));
+ edit_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/undo", TTR("Undo"), KEY_MASK_CMD|KEY_Z), EDIT_UNDO);
+ edit_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/redo", TTR("Redo"), KEY_MASK_CMD|KEY_Y), EDIT_REDO);
+ edit_menu->get_popup()->add_separator();
+ edit_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/cut", TTR("Cut"), KEY_MASK_CMD|KEY_X), EDIT_CUT);
+ edit_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/copy", TTR("Copy"), KEY_MASK_CMD|KEY_C), EDIT_COPY);
+ edit_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/paste", TTR("Paste"), KEY_MASK_CMD|KEY_V), EDIT_PASTE);
+ edit_menu->get_popup()->add_separator();
+ edit_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/select_all", TTR("Select All"), KEY_MASK_CMD|KEY_A), EDIT_SELECT_ALL);
+ edit_menu->get_popup()->add_separator();
+ edit_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/move_up", TTR("Move Up"), KEY_MASK_ALT|KEY_UP), EDIT_MOVE_LINE_UP);
+ edit_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/move_down", TTR("Move Down"), KEY_MASK_ALT|KEY_DOWN), EDIT_MOVE_LINE_DOWN);
+ edit_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/indent_left", TTR("Indent Left"), KEY_MASK_ALT|KEY_LEFT), EDIT_INDENT_LEFT);
+ edit_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/indent_right", TTR("Indent Right"), KEY_MASK_ALT|KEY_RIGHT), EDIT_INDENT_RIGHT);
+ edit_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/toggle_comment", TTR("Toggle Comment"), KEY_MASK_CMD|KEY_K), EDIT_TOGGLE_COMMENT);
+ edit_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/clone_down", TTR("Clone Down"), KEY_MASK_CMD|KEY_B), EDIT_CLONE_DOWN);
+ edit_menu->get_popup()->add_separator();
+#ifdef OSX_ENABLED
+ edit_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/complete_symbol", TTR("Complete Symbol"), KEY_MASK_CTRL|KEY_SPACE), EDIT_COMPLETE);
+#else
+ edit_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/complete_symbol", TTR("Complete Symbol"), KEY_MASK_CMD|KEY_SPACE), EDIT_COMPLETE);
+#endif
+ edit_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/trim_trailing_whitespace", TTR("Trim Trailing Whitespace"), KEY_MASK_CTRL|KEY_MASK_ALT|KEY_T), EDIT_TRIM_TRAILING_WHITESAPCE);
+ edit_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/auto_indent", TTR("Auto Indent"), KEY_MASK_CMD|KEY_I), EDIT_AUTO_INDENT);
+ edit_menu->get_popup()->connect("item_pressed", this,"_menu_option");
+ edit_menu->get_popup()->add_separator();
+ edit_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/reload_script_soft", TTR("Soft Reload Script"), KEY_MASK_CMD|KEY_MASK_SHIFT|KEY_R), FILE_TOOL_RELOAD_SOFT);
+
+
+ search_menu = memnew( MenuButton );
+ menu_hb->add_child(search_menu);
+ search_menu->set_text(TTR("Search"));
+ search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find", TTR("Find.."), KEY_MASK_CMD|KEY_F), SEARCH_FIND);
+ search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_next", TTR("Find Next"), KEY_F3), SEARCH_FIND_NEXT);
+ search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_previous", TTR("Find Previous"), KEY_MASK_SHIFT|KEY_F3), SEARCH_FIND_PREV);
+ search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/replace", TTR("Replace.."), KEY_MASK_CMD|KEY_R), SEARCH_REPLACE);
+ search_menu->get_popup()->add_separator();
+ search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/goto_function", TTR("Goto Function.."), KEY_MASK_SHIFT|KEY_MASK_CMD|KEY_F), SEARCH_LOCATE_FUNCTION);
+ search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/goto_line", TTR("Goto Line.."), KEY_MASK_CMD|KEY_L), SEARCH_GOTO_LINE);
+ search_menu->get_popup()->connect("item_pressed", this,"_menu_option");
+
+ script_search_menu = memnew( MenuButton );
+ menu_hb->add_child(script_search_menu);
+ script_search_menu->set_text(TTR("Search"));
+ script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find", TTR("Find.."), KEY_MASK_CMD|KEY_F), SEARCH_FIND);
+ script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_next", TTR("Find Next"), KEY_F3), SEARCH_FIND_NEXT);
+ script_search_menu->get_popup()->connect("item_pressed", this,"_menu_option");
+ script_search_menu->hide();
+
+
+ debug_menu = memnew( MenuButton );
+ menu_hb->add_child(debug_menu);
+ debug_menu->set_text(TTR("Debug"));
+ debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/toggle_breakpoint", TTR("Toggle Breakpoint"), KEY_F9), DEBUG_TOGGLE_BREAKPOINT);
+ debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/remove_all_breakpoints", TTR("Remove All Breakpoints"), KEY_MASK_CTRL|KEY_MASK_SHIFT|KEY_F9), DEBUG_REMOVE_ALL_BREAKPOINTS);
+ debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/goto_next_breakpoint", TTR("Goto Next Breakpoint"), KEY_MASK_CTRL|KEY_PERIOD), DEBUG_GOTO_NEXT_BREAKPOINT);
+ debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/goto_previous_breakpoint", TTR("Goto Previous Breakpoint"), KEY_MASK_CTRL|KEY_COMMA), DEBUG_GOTO_PREV_BREAKPOINT);
+ debug_menu->get_popup()->add_separator();
+ debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/step_over", TTR("Step Over"), KEY_F10), DEBUG_NEXT);
+ debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/step_into", TTR("Step Into"), KEY_F11), DEBUG_STEP);
+ debug_menu->get_popup()->add_separator();
+ debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/break", TTR("Break")), DEBUG_BREAK);
+ debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/continue", TTR("Continue")), DEBUG_CONTINUE);
+ debug_menu->get_popup()->add_separator();
+ //debug_menu->get_popup()->add_check_item("Show Debugger",DEBUG_SHOW);
+ debug_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("debugger/keep_debugger_open", TTR("Keep Debugger Open")), DEBUG_SHOW_KEEP_OPEN);
+ 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 );
+
+
+#if 0
+ window_menu = memnew( MenuButton );
+ menu_hb->add_child(window_menu);
+ window_menu->set_text(TTR("Window"));
+ window_menu->get_popup()->add_item(TTR("Close"),WINDOW_CLOSE,KEY_MASK_CMD|KEY_W);
+ window_menu->get_popup()->add_separator();
+ window_menu->get_popup()->add_item(TTR("Move Left"),WINDOW_MOVE_LEFT,KEY_MASK_CMD|KEY_LEFT);
+ window_menu->get_popup()->add_item(TTR("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");
+
+#endif
+
+ help_menu = memnew( MenuButton );
+ menu_hb->add_child(help_menu);
+ help_menu->set_text(TTR("Help"));
+ help_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/Contextual", TTR("Contextual Help"), KEY_MASK_SHIFT|KEY_F1), HELP_CONTEXTUAL);
+ help_menu->get_popup()->connect("item_pressed", this,"_menu_option");
+
+ menu_hb->add_spacer();
+
+
+ script_icon = memnew( TextureFrame );
+ menu_hb->add_child(script_icon);
+ script_name_label = memnew( Label );
+ menu_hb->add_child(script_name_label);
+
+ script_icon->hide();
+ script_name_label->hide();
+
+ menu_hb->add_spacer();
+
+ site_search = memnew( ToolButton );
+ site_search->set_text(TTR("Tutorials"));
+ site_search->connect("pressed",this,"_menu_option",varray(SEARCH_WEBSITE));
+ menu_hb->add_child(site_search);
+ site_search->set_tooltip(TTR("Open https://godotengine.org at tutorials section."));
+
+ class_search = memnew( ToolButton );
+ class_search->set_text(TTR("Classes"));
+ class_search->connect("pressed",this,"_menu_option",varray(SEARCH_CLASSES));
+ menu_hb->add_child(class_search);
+ class_search->set_tooltip(TTR("Search the class hierarchy."));
+
+ help_search = memnew( ToolButton );
+ help_search->set_text(TTR("Search Help"));
+ help_search->connect("pressed",this,"_menu_option",varray(SEARCH_HELP));
+ menu_hb->add_child(help_search);
+ help_search->set_tooltip(TTR("Search the reference documentation."));
+
+ menu_hb->add_child( memnew( VSeparator) );
+
+ script_back = memnew( ToolButton );
+ script_back->connect("pressed",this,"_history_back");
+ menu_hb->add_child(script_back);
+ script_back->set_disabled(true);
+ script_back->set_tooltip(TTR("Go to previous edited document."));
+
+ script_forward = memnew( ToolButton );
+ script_forward->connect("pressed",this,"_history_forward");
+ menu_hb->add_child(script_forward);
+ script_forward->set_disabled(true);
+ script_forward->set_tooltip(TTR("Go to next edited document."));
+
+
+
+ tab_container->connect("tab_changed", this,"_tab_changed");
+
+ erase_tab_confirm = memnew( ConfirmationDialog );
+ add_child(erase_tab_confirm);
+ erase_tab_confirm->connect("confirmed", this,"_close_current_tab");
+
+ script_create_dialog = memnew(ScriptCreateDialog);
+ script_create_dialog->set_title(TTR("Create Script"));
+ add_child(script_create_dialog);
+ script_create_dialog->connect("script_created", this, "_script_created");
+
+ file_dialog_option = -1;
+ file_dialog = memnew( EditorFileDialog );
+ add_child(file_dialog);
+ file_dialog->connect("file_selected", this,"_file_dialog_action");
+
+ 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(TTR("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(TTR("Reload"));
+
+ disk_changed->add_button(TTR("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");
+
+
+ Button *db = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Debugger"),debugger);
+ debugger->set_tool_button(db);
+
+
+ debugger->connect("breaked",this,"_breaked");
+
+ autosave_timer = memnew( Timer );
+ autosave_timer->set_one_shot(false);
+ add_child(autosave_timer);
+
+ grab_focus_block=false;
+
+ help_search_dialog = memnew( EditorHelpSearch );
+ add_child(help_search_dialog);
+ help_search_dialog->connect("go_to_help",this,"_help_class_goto");
+
+
+ help_index = memnew( EditorHelpIndex );
+ add_child(help_index);
+ help_index->connect("open_class",this,"_help_class_open");
+
+ history_pos=-1;
+// debugger_gui->hide();
+
+ edit_pass=0;
+ trim_trailing_whitespace_on_save = false;
+}
+
+
+ScriptEditor::~ScriptEditor() {
+
+ memdelete(completion_cache);
+}
+
+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 {
+
+ if (p_object->cast_to<Script>()) {
+
+ bool valid = _can_open_in_editor(p_object->cast_to<Script>());
+
+ if (!valid) { //user tried to open it by clicking
+ EditorNode::get_singleton()->show_warning(TTR("Built-in scripts can only be edited when the scene they belong to is loaded"));
+ }
+ return valid;
+ }
+
+ 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_all_scripts();
+}
+
+void ScriptEditorPlugin::apply_changes() {
+
+ script_editor->apply_scripts();
+}
+
+void ScriptEditorPlugin::restore_global_state() {
+
+
+}
+
+void ScriptEditorPlugin::save_global_state() {
+
+}
+
+void ScriptEditorPlugin::set_window_layout(Ref<ConfigFile> p_layout) {
+
+ script_editor->set_window_layout(p_layout);
+}
+
+void ScriptEditorPlugin::get_window_layout(Ref<ConfigFile> p_layout){
+
+ script_editor->get_window_layout(p_layout);
+}
+
+
+void ScriptEditorPlugin::get_breakpoints(List<String> *p_breakpoints) {
+
+
+ return script_editor->get_breakpoints(p_breakpoints);
+}
+
+void ScriptEditorPlugin::edited_scene_changed() {
+
+ script_editor->edited_scene_changed();
+}
+
+
+
+ScriptEditorPlugin::ScriptEditorPlugin(EditorNode *p_node) {
+
+ editor=p_node;
+ script_editor = memnew( ScriptEditor(p_node) );
+ editor->get_viewport()->add_child(script_editor);
+ script_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+
+ script_editor->hide();
+
+ EDITOR_DEF("text_editor/auto_reload_scripts_on_external_change",true);
+ ScriptServer::set_reload_scripts_on_save(EDITOR_DEF("text_editor/auto_reload_and_parse_scripts_on_save",true));
+ EDITOR_DEF("text_editor/open_dominant_script_on_scene_change",true);
+ EDITOR_DEF("external_editor/use_external_editor",false);
+ EDITOR_DEF("external_editor/exec_path","");
+ EDITOR_DEF("text_editor/script_temperature_enabled",true);
+ EDITOR_DEF("text_editor/highlight_current_script", true);
+ EDITOR_DEF("text_editor/script_temperature_history_size",15);
+ EDITOR_DEF("text_editor/script_temperature_hot_color",Color(1,0,0,0.3));
+ EDITOR_DEF("text_editor/script_temperature_cold_color",Color(0,0,1,0.3));
+ EDITOR_DEF("text_editor/current_script_background_color",Color(0.81,0.81,0.14,0.63));
+ EDITOR_DEF("text_editor/group_help_pages",true);
+ EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT,"text_editor/sort_scripts_by",PROPERTY_HINT_ENUM,"Name,Path"));
+ EDITOR_DEF("text_editor/sort_scripts_by",0);
+ EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT,"text_editor/list_script_names_as",PROPERTY_HINT_ENUM,"Name,Parent Directory And Name,Full Path"));
+ EDITOR_DEF("text_editor/list_script_names_as",0);
+ EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,"external_editor/exec_path",PROPERTY_HINT_GLOBAL_FILE));
+ EDITOR_DEF("external_editor/exec_flags","");
+
+}
+
+
+ScriptEditorPlugin::~ScriptEditorPlugin()
+{
+}
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
new file mode 100644
index 000000000..a5dddf88e
--- /dev/null
+++ b/editor/plugins/script_editor_plugin.h
@@ -0,0 +1,400 @@
+/*************************************************************************/
+/* script_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "editor/editor_plugin.h"
+#include "editor/script_create_dialog.h"
+#include "scene/gui/tab_container.h"
+#include "scene/gui/text_edit.h"
+#include "scene/gui/menu_button.h"
+#include "scene/gui/tool_button.h"
+#include "scene/gui/tree.h"
+#include "scene/main/timer.h"
+#include "script_language.h"
+#include "editor/code_editor.h"
+#include "scene/gui/split_container.h"
+#include "scene/gui/item_list.h"
+#include "editor/editor_help.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, List<String>* r_options);
+ virtual void _load_theme_settings();
+ void _notification(int p_what);
+ static void _bind_methods();
+
+
+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();
+ String get_name() ;
+ Ref<Texture> get_icon() ;
+ bool is_unsaved();
+ ScriptTextEditor();
+
+};
+
+class EditorScriptCodeCompletionCache;
+
+class ScriptEditor : public VBoxContainer {
+
+ OBJ_TYPE(ScriptEditor, VBoxContainer );
+
+
+ EditorNode *editor;
+ enum {
+ FILE_NEW,
+ FILE_OPEN,
+ FILE_SAVE,
+ FILE_SAVE_AS,
+ FILE_SAVE_ALL,
+ FILE_IMPORT_THEME,
+ FILE_RELOAD_THEME,
+ FILE_SAVE_THEME,
+ FILE_SAVE_THEME_AS,
+ FILE_CLOSE,
+ CLOSE_DOCS,
+ EDIT_UNDO,
+ EDIT_REDO,
+ EDIT_CUT,
+ EDIT_COPY,
+ EDIT_PASTE,
+ EDIT_SELECT_ALL,
+ EDIT_COMPLETE,
+ EDIT_AUTO_INDENT,
+ EDIT_TRIM_TRAILING_WHITESAPCE,
+ EDIT_TOGGLE_COMMENT,
+ EDIT_MOVE_LINE_UP,
+ EDIT_MOVE_LINE_DOWN,
+ EDIT_INDENT_RIGHT,
+ EDIT_INDENT_LEFT,
+ EDIT_CLONE_DOWN,
+ FILE_TOOL_RELOAD,
+ FILE_TOOL_RELOAD_SOFT,
+ SEARCH_FIND,
+ SEARCH_FIND_NEXT,
+ SEARCH_FIND_PREV,
+ SEARCH_REPLACE,
+ SEARCH_LOCATE_FUNCTION,
+ SEARCH_GOTO_LINE,
+ SEARCH_HELP,
+ SEARCH_CLASSES,
+ SEARCH_WEBSITE,
+ DEBUG_TOGGLE_BREAKPOINT,
+ DEBUG_REMOVE_ALL_BREAKPOINTS,
+ DEBUG_GOTO_NEXT_BREAKPOINT,
+ DEBUG_GOTO_PREV_BREAKPOINT,
+ DEBUG_NEXT,
+ DEBUG_STEP,
+ DEBUG_BREAK,
+ DEBUG_CONTINUE,
+ DEBUG_SHOW,
+ DEBUG_SHOW_KEEP_OPEN,
+ HELP_CONTEXTUAL,
+ WINDOW_MOVE_LEFT,
+ WINDOW_MOVE_RIGHT,
+ WINDOW_NEXT,
+ WINDOW_PREV,
+ WINDOW_SELECT_BASE=100
+ };
+
+ enum ScriptSortBy {
+ SORT_BY_NAME,
+ SORT_BY_PATH,
+ };
+
+ enum ScriptListName {
+ DISPLAY_NAME,
+ DISPLAY_DIR_AND_NAME,
+ DISPLAY_FULL_PATH,
+ };
+
+ HBoxContainer *menu_hb;
+ MenuButton *file_menu;
+ MenuButton *edit_menu;
+ MenuButton *search_menu;
+ MenuButton *script_search_menu;
+ MenuButton *debug_menu;
+ MenuButton *help_menu;
+ Timer *autosave_timer;
+ uint64_t idle;
+
+ Button *help_search;
+ Button *site_search;
+ Button *class_search;
+ EditorHelpSearch *help_search_dialog;
+
+ ItemList *script_list;
+ HSplitContainer *script_split;
+ TabContainer *tab_container;
+ EditorFileDialog *file_dialog;
+ GotoLineDialog *goto_line_dialog;
+ ConfirmationDialog *erase_tab_confirm;
+ ScriptCreateDialog *script_create_dialog;
+ ScriptEditorDebugger* debugger;
+ ToolButton *scripts_visible;
+
+ String current_theme;
+
+ TextureFrame *script_icon;
+ Label *script_name_label;
+
+ ToolButton *script_back;
+ ToolButton *script_forward;
+
+
+ struct ScriptHistory {
+
+ Control *control;
+ int scroll_pos;
+ int cursor_column;
+ int cursor_row;
+ };
+
+ Vector<ScriptHistory> history;
+ int history_pos;
+
+
+ EditorHelpIndex *help_index;
+
+ void _tab_changed(int p_which);
+ void _menu_option(int p_optin);
+
+ Tree *disk_changed_list;
+ ConfirmationDialog *disk_changed;
+
+ bool restoring_layout;
+
+ 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(Ref<Script> p_for_script=Ref<Script>());
+
+ void _close_tab(int p_idx);
+
+ void _close_current_tab();
+ void _close_docs_tab();
+
+ bool grab_focus_block;
+
+ bool pending_auto_reload;
+ bool auto_reload_running_scripts;
+ void _live_auto_reload_running_scripts();
+
+ ScriptEditorQuickOpen *quick_open;
+
+ EditorScriptCodeCompletionCache *completion_cache;
+
+ void _editor_play();
+ void _editor_pause();
+ void _editor_stop();
+
+ int edit_pass;
+
+ void _add_callback(Object *p_obj, const String& p_function, const StringArray& p_args);
+ void _res_saved_callback(const Ref<Resource>& p_res);
+
+ bool trim_trailing_whitespace_on_save;
+
+ void _trim_trailing_whitespace(TextEdit *tx);
+
+ 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();
+ void _script_created(Ref<Script> p_script);
+
+ void _save_layout();
+ void _editor_settings_changed();
+ void _autosave_scripts();
+
+ void _update_script_names();
+
+ void _script_selected(int p_idx);
+
+ void _find_scripts(Node* p_base, Node* p_current,Set<Ref<Script> >& used);
+
+ void _tree_changed();
+
+ void _script_split_dragged(float);
+
+ void _unhandled_input(const InputEvent& p_event);
+
+
+ void _history_forward();
+ void _history_back();
+
+ bool waiting_update_names;
+
+ void _help_class_open(const String& p_class);
+ void _help_class_goto(const String& p_desc);
+ void _update_history_arrows();
+ void _go_to_tab(int p_idx);
+ void _update_history_pos(int p_new_pos);
+ void _update_script_colors();
+ void _update_modified_scripts_for_external_editor(Ref<Script> p_for_script=Ref<Script>());
+
+ int file_dialog_option;
+ void _file_dialog_action(String p_file);
+
+ static ScriptEditor *script_editor;
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+ static ScriptEditor *get_singleton() { return script_editor; }
+
+ 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 swap_lines(TextEdit *tx, int line1, int line2);
+ void _breakpoint_toggled(const int p_row);
+
+ void save_all_scripts();
+
+ void set_window_layout(Ref<ConfigFile> p_layout);
+ void get_window_layout(Ref<ConfigFile> p_layout);
+
+ void set_scene_root_script( Ref<Script> p_script );
+
+ bool script_go_to_method(Ref<Script> p_script, const String& p_method);
+
+ virtual void edited_scene_changed();
+
+ void close_builtin_scripts_from_scene(const String& p_scene);
+
+ void goto_help(const String& p_desc) { _help_class_goto(p_desc); }
+
+ ScriptEditorDebugger *get_debugger() { return debugger; }
+ void set_live_auto_reload_running_scripts(bool p_enabled);
+
+ ScriptEditor(EditorNode *p_editor);
+ ~ScriptEditor();
+};
+
+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 set_window_layout(Ref<ConfigFile> p_layout);
+ virtual void get_window_layout(Ref<ConfigFile> p_layout);
+
+ virtual void get_breakpoints(List<String> *p_breakpoints);
+
+
+ virtual void edited_scene_changed();
+
+ ScriptEditorPlugin(EditorNode *p_node);
+ ~ScriptEditorPlugin();
+
+};
+
+#endif // SCRIPT_EDITOR_PLUGIN_H
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
new file mode 100644
index 000000000..28846ee84
--- /dev/null
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -0,0 +1,624 @@
+/*************************************************************************/
+/* shader_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "editor/editor_settings.h"
+
+#include "spatial_editor_plugin.h"
+#include "scene/resources/shader_graph.h"
+#include "io/resource_loader.h"
+#include "io/resource_saver.h"
+#include "os/keyboard.h"
+#include "editor/editor_node.h"
+#include "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_LIGHT || p_type==ShaderLanguage::SHADER_CANVAS_ITEM_LIGHT)
+ get_text_edit()->set_text(shader->get_light_code());
+ else if (p_type==ShaderLanguage::SHADER_MATERIAL_VERTEX || p_type==ShaderLanguage::SHADER_CANVAS_ITEM_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("completion_background_color", EDITOR_DEF("text_editor/completion_background_color", Color(0,0,0,0)));
+ get_text_edit()->add_color_override("completion_selected_color", EDITOR_DEF("text_editor/completion_selected_color", Color::html("434244")));
+ get_text_edit()->add_color_override("completion_existing_color", EDITOR_DEF("text_editor/completion_existing_color", Color::html("21dfdfdf")));
+ get_text_edit()->add_color_override("completion_scroll_color", EDITOR_DEF("text_editor/completion_scroll_color", Color::html("ffffff")));
+ get_text_edit()->add_color_override("completion_font_color", EDITOR_DEF("text_editor/completion_font_color", Color::html("aaaaaa")));
+ get_text_edit()->add_color_override("font_color",EDITOR_DEF("text_editor/text_color",Color(0,0,0)));
+ get_text_edit()->add_color_override("line_number_color",EDITOR_DEF("text_editor/line_number_color",Color(0,0,0)));
+ get_text_edit()->add_color_override("caret_color",EDITOR_DEF("text_editor/caret_color",Color(0,0,0)));
+ get_text_edit()->add_color_override("caret_background_color",EDITOR_DEF("text_editor/caret_background_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)));
+ get_text_edit()->add_color_override("brace_mismatch_color",EDITOR_DEF("text_editor/brace_mismatch_color",Color(1,0.2,0.2)));
+ get_text_edit()->add_color_override("current_line_color",EDITOR_DEF("text_editor/current_line_color",Color(0.3,0.5,0.8,0.15)));
+ get_text_edit()->add_color_override("word_highlighted_color",EDITOR_DEF("text_editor/word_highlighted_color",Color(0.8,0.9,0.9,0.15)));
+ get_text_edit()->add_color_override("number_color",EDITOR_DEF("text_editor/number_color",Color(0.9,0.6,0.0,2)));
+ get_text_edit()->add_color_override("function_color",EDITOR_DEF("text_editor/function_color",Color(0.4,0.6,0.8)));
+ get_text_edit()->add_color_override("member_variable_color",EDITOR_DEF("text_editor/member_variable_color",Color(0.9,0.3,0.3)));
+ get_text_edit()->add_color_override("mark_color", EDITOR_DEF("text_editor/mark_color", Color(1.0,0.4,0.4,0.4)));
+ get_text_edit()->add_color_override("breakpoint_color", EDITOR_DEF("text_editor/breakpoint_color", Color(0.8,0.8,0.4,0.2)));
+ get_text_edit()->add_color_override("search_result_color",EDITOR_DEF("text_editor/search_result_color",Color(0.05,0.25,0.05,1)));
+ get_text_edit()->add_color_override("search_result_border_color",EDITOR_DEF("text_editor/search_result_border_color",Color(0.1,0.45,0.1,1)));
+
+ Color keyword_color= EDITOR_DEF("text_editor/keyword_color",Color(0.5,0.0,0.2));
+
+
+ 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=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+1)+","+itos(col+1)+"): "+errortxt;
+ set_error(error_text);
+ get_text_edit()->set_line_as_marked(line,true);
+
+ } else {
+ for(int i=0;i<get_text_edit()->get_line_count();i++)
+ get_text_edit()->set_line_as_marked(i,false);
+ 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) {
+
+
+ ShaderTextEditor *current = tab_container->get_current_tab_control()->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: {
+
+ current->get_find_replace_bar()->popup_search();
+ } break;
+ case SEARCH_FIND_NEXT: {
+
+ current->get_find_replace_bar()->search_next();
+ } break;
+ case SEARCH_FIND_PREV: {
+
+ current->get_find_replace_bar()->search_prev();
+ } break;
+ case SEARCH_REPLACE: {
+
+ current->get_find_replace_bar()->popup_replace();
+ } break;
+// case SEARCH_LOCATE_SYMBOL: {
+
+// } break;
+ case SEARCH_GOTO_LINE: {
+
+ goto_line_dialog->popup_find_line(current->get_text_edit());
+ } break;
+
+ }
+}
+
+void ShaderEditor::_tab_changed(int p_which) {
+
+ ShaderTextEditor *shader_editor = tab_container->get_tab_control(p_which)->cast_to<ShaderTextEditor>();
+
+ if (shader_editor && is_inside_tree())
+ shader_editor->get_text_edit()->grab_focus();
+
+ ensure_select_current();
+}
+
+void ShaderEditor::_notification(int p_what) {
+
+ if (p_what==NOTIFICATION_ENTER_TREE) {
+
+ 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();
+ light_editor->_validate_script();
+}
+
+void ShaderEditor::_editor_settings_changed() {
+
+ vertex_editor->update_editor_settings();
+ fragment_editor->update_editor_settings();
+ light_editor->update_editor_settings();
+}
+
+void ShaderEditor::_bind_methods() {
+
+ ObjectTypeDB::bind_method("_editor_settings_changed",&ShaderEditor::_editor_settings_changed);
+ 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) {
+ vertex_editor->set_edited_shader(p_shader,ShaderLanguage::SHADER_MATERIAL_VERTEX);
+ fragment_editor->set_edited_shader(p_shader,ShaderLanguage::SHADER_MATERIAL_FRAGMENT);
+ light_editor->set_edited_shader(shader,ShaderLanguage::SHADER_MATERIAL_LIGHT);
+ } else if (shader->get_mode()==Shader::MODE_CANVAS_ITEM) {
+
+ vertex_editor->set_edited_shader(p_shader,ShaderLanguage::SHADER_CANVAS_ITEM_VERTEX);
+ fragment_editor->set_edited_shader(p_shader,ShaderLanguage::SHADER_CANVAS_ITEM_FRAGMENT);
+ light_editor->set_edited_shader(shader,ShaderLanguage::SHADER_CANVAS_ITEM_LIGHT);
+ }
+
+ //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(),light_editor->get_text_edit()->get_text(),0,0);
+ shader->set_edited(true);
+ }
+}
+
+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(TTR("Edit"));
+ edit_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/undo", TTR("Undo"), KEY_MASK_CMD|KEY_Z), EDIT_UNDO);
+ edit_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/redo", TTR("Redo"), KEY_MASK_CMD|KEY_Y), EDIT_REDO);
+ edit_menu->get_popup()->add_separator();
+ edit_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/cut", TTR("Cut"), KEY_MASK_CMD|KEY_X), EDIT_CUT);
+ edit_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/copy", TTR("Copy"), KEY_MASK_CMD|KEY_C), EDIT_COPY);
+ edit_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/paste", TTR("Paste"), KEY_MASK_CMD|KEY_V), EDIT_PASTE);
+ edit_menu->get_popup()->add_separator();
+ edit_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/select_all", TTR("Select All"), KEY_MASK_CMD|KEY_A), EDIT_SELECT_ALL);
+ 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(TTR("Search"));
+ search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find", TTR("Find.."), KEY_MASK_CMD|KEY_F), SEARCH_FIND);
+ search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_next", TTR("Find Next"), KEY_F3), SEARCH_FIND_NEXT);
+ search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_previous", TTR("Find Previous"), KEY_MASK_SHIFT|KEY_F3), SEARCH_FIND_PREV);
+ search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/replace", TTR("Replace.."), KEY_MASK_CMD|KEY_R), SEARCH_REPLACE);
+ 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_shortcut(ED_SHORTCUT("script_editor/goto_line", TTR("Goto Line.."), KEY_MASK_CMD|KEY_L), SEARCH_GOTO_LINE);
+ search_menu->get_popup()->connect("item_pressed", this,"_menu_option");
+
+
+ tab_container->connect("tab_changed", this,"_tab_changed");
+
+ 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(TTR("Vertex"));
+
+ fragment_editor = memnew( ShaderTextEditor );
+ tab_container->add_child(fragment_editor);
+ fragment_editor->set_name(TTR("Fragment"));
+
+ light_editor = memnew( ShaderTextEditor );
+ tab_container->add_child(light_editor);
+ light_editor->set_name(TTR("Lighting"));
+
+ tab_container->set_current_tab(1);
+
+
+ vertex_editor->connect("script_changed", this,"apply_shaders");
+ fragment_editor->connect("script_changed", this,"apply_shaders");
+ light_editor->connect("script_changed", this,"apply_shaders");
+ EditorSettings::get_singleton()->connect("settings_changed",this,"_editor_settings_changed");
+
+ _editor_settings_changed();
+}
+
+
+void ShaderEditorPlugin::edit(Object *p_object) {
+
+ Shader* s = p_object->cast_to<Shader>();
+ if (!s || s->cast_to<ShaderGraph>()) {
+ shader_editor->hide(); //Dont edit ShaderGraph
+ return;
+ }
+
+ if (_2d && s->get_mode()==Shader::MODE_CANVAS_ITEM)
+ shader_editor->edit(s);
+ else if (!_2d && s->get_mode()==Shader::MODE_MATERIAL)
+ shader_editor->edit(s);
+
+}
+
+bool ShaderEditorPlugin::handles(Object *p_object) const {
+
+ bool handles = true;
+ Shader *shader=p_object->cast_to<Shader>();
+ if (!shader || shader->cast_to<ShaderGraph>()) // Dont handle ShaderGraph's
+ handles = false;
+ if (handles && _2d)
+ handles = shader->get_mode()==Shader::MODE_CANVAS_ITEM;
+ else if (handles && !_2d)
+ return shader->get_mode()==Shader::MODE_MATERIAL;
+
+ if (!handles)
+ shader_editor->hide();
+ return handles;
+}
+
+void ShaderEditorPlugin::make_visible(bool p_visible) {
+
+ if (p_visible) {
+ shader_editor->show();
+ } else {
+ shader_editor->apply_shaders();
+ }
+
+}
+
+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, bool p_2d) {
+
+ editor=p_node;
+ shader_editor = memnew( ShaderEditor );
+ _2d=p_2d;
+ if (p_2d)
+ add_control_to_container(CONTAINER_CANVAS_EDITOR_BOTTOM,shader_editor);
+ else
+ add_control_to_container(CONTAINER_SPATIAL_EDITOR_BOTTOM,shader_editor);
+// editor->get_viewport()->add_child(shader_editor);
+// shader_editor->set_area_as_parent_rect();
+
+ shader_editor->hide();
+
+}
+
+
+ShaderEditorPlugin::~ShaderEditorPlugin() {
+}
+
diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h
new file mode 100644
index 000000000..7d30afa35
--- /dev/null
+++ b/editor/plugins/shader_editor_plugin.h
@@ -0,0 +1,157 @@
+/*************************************************************************/
+/* shader_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "editor/code_editor.h"
+#include "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_FIND_PREV,
+ SEARCH_REPLACE,
+ //SEARCH_LOCATE_SYMBOL,
+ SEARCH_GOTO_LINE,
+
+ };
+
+ MenuButton *edit_menu;
+ MenuButton *search_menu;
+ MenuButton *settings_menu;
+ uint64_t idle;
+
+ TabContainer *tab_container;
+ GotoLineDialog *goto_line_dialog;
+ ConfirmationDialog *erase_tab_confirm;
+
+ TextureButton *close;
+
+ ShaderTextEditor *vertex_editor;
+ ShaderTextEditor *fragment_editor;
+ ShaderTextEditor *light_editor;
+
+ void _tab_changed(int p_which);
+ void _menu_option(int p_optin);
+ void _params_changed();
+ mutable Ref<Shader> shader;
+
+ void _close_callback();
+
+ void _editor_settings_changed();
+
+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 );
+
+ bool _2d;
+ 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,bool p_2d);
+ ~ShaderEditorPlugin();
+
+};
+#endif
diff --git a/editor/plugins/shader_graph_editor_plugin.cpp b/editor/plugins/shader_graph_editor_plugin.cpp
new file mode 100644
index 000000000..daec2bf50
--- /dev/null
+++ b/editor/plugins/shader_graph_editor_plugin.cpp
@@ -0,0 +1,2941 @@
+/*************************************************************************/
+/* shader_graph_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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"
+
+
+#include "scene/gui/check_box.h"
+#include "scene/gui/menu_button.h"
+#include "scene/gui/panel.h"
+#include "spatial_editor_plugin.h"
+#include "os/keyboard.h"
+#include "canvas_item_editor_plugin.h"
+
+void GraphColorRampEdit::_input_event(const InputEvent& p_event) {
+
+ if (p_event.type==InputEvent::KEY && p_event.key.pressed && p_event.key.scancode==KEY_DELETE && grabbed!=-1) {
+
+ points.remove(grabbed);
+ grabbed=-1;
+ update();
+ emit_signal("ramp_changed");
+ accept_event();
+ }
+
+ if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1 && p_event.mouse_button.pressed) {
+
+ update();
+ int x = p_event.mouse_button.x;
+ int total_w = get_size().width-get_size().height-3;
+ if (x>total_w+3) {
+
+ if (grabbed==-1)
+ return;
+ Size2 ms = Size2(350, picker->get_combined_minimum_size().height+10);
+ picker->set_color(points[grabbed].color);
+ popup->set_pos(get_global_pos()-Size2(0,ms.height));
+ popup->set_size(ms);
+ popup->popup();
+ return;
+ }
+
+
+ float ofs = CLAMP(x/float(total_w),0,1);
+
+ grabbed=-1;
+ grabbing=true;
+ int pos=-1;
+ for(int i=0;i<points.size();i++) {
+
+ if (ABS(x-points[i].offset*total_w)<4) {
+ grabbed=i;
+ }
+ if (points[i].offset<ofs)
+ pos=i;
+ }
+
+ grabbed_at=ofs;
+ //grab or select
+ if (grabbed!=-1) {
+ return;
+ }
+ //insert
+
+
+ Point p;
+ p.offset=ofs;
+
+ Point prev;
+ Point next;
+
+ if (pos==-1) {
+
+ prev.color=Color(0,0,0);
+ prev.offset=0;
+ if (points.size()) {
+ next=points[0];
+ } else {
+ next.color=Color(1,1,1);
+ next.offset=1.0;
+ }
+ } else {
+
+ if (pos==points.size()-1) {
+ next.color=Color(1,1,1);
+ next.offset=1.0;
+ } else {
+ next=points[pos+1];
+ }
+ prev=points[pos];
+
+ }
+
+ p.color=prev.color.linear_interpolate(next.color,(p.offset-prev.offset)/(next.offset-prev.offset));
+
+ points.push_back(p);
+ points.sort();
+ for(int i=0;i<points.size();i++) {
+ if (points[i].offset==ofs) {
+ grabbed=i;
+ break;
+ }
+ }
+
+ emit_signal("ramp_changed");
+
+ }
+
+ if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1 && !p_event.mouse_button.pressed) {
+
+ if (grabbing) {
+ grabbing=false;
+ emit_signal("ramp_changed");
+ }
+ update();
+ }
+
+ if (p_event.type==InputEvent::MOUSE_MOTION && grabbing) {
+
+ int total_w = get_size().width-get_size().height-3;
+
+ int x = p_event.mouse_motion.x;
+ float newofs = CLAMP(x/float(total_w),0,1);
+
+ bool valid=true;
+ for(int i=0;i<points.size();i++) {
+
+ if (points[i].offset==newofs && i!=grabbed) {
+ valid=false;
+ }
+ }
+
+ if (!valid)
+ return;
+
+ points[grabbed].offset=newofs;
+
+ points.sort();
+ for(int i=0;i<points.size();i++) {
+ if (points[i].offset==newofs) {
+ grabbed=i;
+ break;
+ }
+ }
+
+ emit_signal("ramp_changed");
+
+ update();
+ }
+}
+
+void GraphColorRampEdit::_notification(int p_what){
+
+ if (p_what==NOTIFICATION_ENTER_TREE) {
+ if (!picker->is_connected("color_changed",this,"_color_changed")) {
+ picker->connect("color_changed",this,"_color_changed");
+ }
+ }
+ if (p_what==NOTIFICATION_DRAW) {
+
+
+ Point prev;
+ prev.offset=0;
+ prev.color=Color(0,0,0);
+
+ int h = get_size().y;
+ int total_w = get_size().width-get_size().height-3;
+
+ for(int i=-1;i<points.size();i++) {
+
+ Point next;
+ if (i+1==points.size()) {
+ next.color=Color(1,1,1);
+ next.offset=1;
+ } else {
+ next=points[i+1];
+ }
+
+ if (prev.offset==next.offset) {
+ prev=next;
+ continue;
+ }
+
+ Vector<Vector2> points;
+ Vector<Color> colors;
+ points.push_back(Vector2(prev.offset*total_w,h));
+ points.push_back(Vector2(prev.offset*total_w,0));
+ points.push_back(Vector2(next.offset*total_w,0));
+ points.push_back(Vector2(next.offset*total_w,h));
+ colors.push_back(prev.color);
+ colors.push_back(prev.color);
+ colors.push_back(next.color);
+ colors.push_back(next.color);
+ draw_primitive(points,colors,Vector<Point2>());
+ prev=next;
+ }
+
+ for(int i=0;i<points.size();i++) {
+
+ Color col=i==grabbed?Color(1,0.0,0.0,0.9):Color(1,1,1,0.8);
+
+ draw_line(Vector2(points[i].offset*total_w,0),Vector2(points[i].offset*total_w,h-1),Color(0,0,0,0.7));
+ draw_line(Vector2(points[i].offset*total_w-1,h/2),Vector2(points[i].offset*total_w-1,h-1),col);
+ draw_line(Vector2(points[i].offset*total_w+1,h/2),Vector2(points[i].offset*total_w+1,h-1),col);
+ draw_line(Vector2(points[i].offset*total_w-1,h/2),Vector2(points[i].offset*total_w+1,h/2),col);
+ draw_line(Vector2(points[i].offset*total_w-1,h-1),Vector2(points[i].offset*total_w+1,h-1),col);
+
+ }
+
+ if (grabbed!=-1) {
+
+ draw_rect(Rect2(total_w+3,0,h,h),points[grabbed].color);
+ }
+
+ if (has_focus()) {
+
+ draw_line(Vector2(-1,-1),Vector2(total_w+1,-1),Color(1,1,1,0.6));
+ draw_line(Vector2(total_w+1,-1),Vector2(total_w+1,h+1),Color(1,1,1,0.6));
+ draw_line(Vector2(total_w+1,h+1),Vector2(-1,h+1),Color(1,1,1,0.6));
+ draw_line(Vector2(-1,-1),Vector2(-1,h+1),Color(1,1,1,0.6));
+ }
+
+ }
+}
+
+Size2 GraphColorRampEdit::get_minimum_size() const {
+
+ return Vector2(0,16);
+}
+
+
+void GraphColorRampEdit::_color_changed(const Color& p_color) {
+
+ if (grabbed==-1)
+ return;
+ points[grabbed].color=p_color;
+ update();
+ emit_signal("ramp_changed");
+
+}
+
+void GraphColorRampEdit::set_ramp(const Vector<float>& p_offsets,const Vector<Color>& p_colors) {
+
+ ERR_FAIL_COND(p_offsets.size()!=p_colors.size());
+ points.clear();
+ for(int i=0;i<p_offsets.size();i++) {
+ Point p;
+ p.offset=p_offsets[i];
+ p.color=p_colors[i];
+ points.push_back(p);
+ }
+
+ points.sort();
+ update();
+}
+
+Vector<float> GraphColorRampEdit::get_offsets() const{
+ Vector<float> ret;
+ for(int i=0;i<points.size();i++)
+ ret.push_back(points[i].offset);
+ return ret;
+}
+Vector<Color> GraphColorRampEdit::get_colors() const{
+
+ Vector<Color> ret;
+ for(int i=0;i<points.size();i++)
+ ret.push_back(points[i].color);
+ return ret;
+}
+
+
+void GraphColorRampEdit::_bind_methods(){
+
+ ObjectTypeDB::bind_method(_MD("_input_event"),&GraphColorRampEdit::_input_event);
+ ObjectTypeDB::bind_method(_MD("_color_changed"),&GraphColorRampEdit::_color_changed);
+ ADD_SIGNAL(MethodInfo("ramp_changed"));
+}
+
+GraphColorRampEdit::GraphColorRampEdit(){
+
+ grabbed=-1;
+ grabbing=false;
+ set_focus_mode(FOCUS_ALL);
+
+ popup = memnew( PopupPanel );
+ picker = memnew( ColorPicker );
+ popup->add_child(picker);
+ popup->set_child_rect(picker);
+ add_child(popup);
+
+}
+////////////
+
+void GraphCurveMapEdit::_input_event(const InputEvent& p_event) {
+
+ if (p_event.type==InputEvent::KEY && p_event.key.pressed && p_event.key.scancode==KEY_DELETE && grabbed!=-1) {
+
+ points.remove(grabbed);
+ grabbed=-1;
+ update();
+ emit_signal("curve_changed");
+ accept_event();
+ }
+
+ if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1 && p_event.mouse_button.pressed) {
+
+ update();
+ Point2 p = Vector2(p_event.mouse_button.x,p_event.mouse_button.y)/get_size();
+ p.y=1.0-p.y;
+ grabbed=-1;
+ grabbing=true;
+
+ for(int i=0;i<points.size();i++) {
+
+ Vector2 ps = p*get_size();
+ Vector2 pt = Vector2(points[i].offset,points[i].height)*get_size();
+ if (ps.distance_to(pt)<4) {
+ grabbed=i;
+ }
+
+ }
+
+
+ //grab or select
+ if (grabbed!=-1) {
+ return;
+ }
+ //insert
+
+
+ Point np;
+ np.offset=p.x;
+ np.height=p.y;
+
+ points.push_back(np);
+ points.sort();
+ for(int i=0;i<points.size();i++) {
+ if (points[i].offset==p.x && points[i].height==p.y) {
+ grabbed=i;
+ break;
+ }
+ }
+
+ emit_signal("curve_changed");
+
+ }
+
+ if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1 && !p_event.mouse_button.pressed) {
+
+ if (grabbing) {
+ grabbing=false;
+ emit_signal("curve_changed");
+ }
+ update();
+ }
+
+ if (p_event.type==InputEvent::MOUSE_MOTION && grabbing && grabbed != -1) {
+
+ Point2 p = Vector2(p_event.mouse_button.x,p_event.mouse_button.y)/get_size();
+ p.y=1.0-p.y;
+
+ p.x = CLAMP(p.x,0.0,1.0);
+ p.y = CLAMP(p.y,0.0,1.0);
+
+ bool valid=true;
+
+ for(int i=0;i<points.size();i++) {
+
+ if (points[i].offset==p.x && points[i].height==p.y && i!=grabbed) {
+ valid=false;
+ }
+ }
+
+ if (!valid)
+ return;
+
+ points[grabbed].offset=p.x;
+ points[grabbed].height=p.y;
+
+ points.sort();
+ for(int i=0;i<points.size();i++) {
+ if (points[i].offset==p.x && points[i].height==p.y) {
+ grabbed=i;
+ break;
+ }
+ }
+
+ emit_signal("curve_changed");
+
+ update();
+ }
+}
+
+void GraphCurveMapEdit::_plot_curve(const Vector2& p_a,const Vector2& p_b,const Vector2& p_c,const Vector2& p_d) {
+
+ float geometry[4][4];
+ float tmp1[4][4];
+ float tmp2[4][4];
+ float deltas[4][4];
+ double x, dx, dx2, dx3;
+ double y, dy, dy2, dy3;
+ double d, d2, d3;
+ int lastx, lasty;
+ int newx, newy;
+ int ntimes;
+ int i,j;
+
+ int xmax=get_size().x;
+ int ymax=get_size().y;
+
+ /* construct the geometry matrix from the segment */
+ for (i = 0; i < 4; i++) {
+ geometry[i][2] = 0;
+ geometry[i][3] = 0;
+ }
+
+ geometry[0][0] = (p_a[0] * xmax);
+ geometry[1][0] = (p_b[0] * xmax);
+ geometry[2][0] = (p_c[0] * xmax);
+ geometry[3][0] = (p_d[0] * xmax);
+
+ geometry[0][1] = (p_a[1] * ymax);
+ geometry[1][1] = (p_b[1] * ymax);
+ geometry[2][1] = (p_c[1] * ymax);
+ geometry[3][1] = (p_d[1] * ymax);
+
+ /* subdivide the curve ntimes (1000) times */
+ ntimes = 4 * xmax;
+ /* ntimes can be adjusted to give a finer or coarser curve */
+ d = 1.0 / ntimes;
+ d2 = d * d;
+ d3 = d * d * d;
+
+ /* construct a temporary matrix for determining the forward differencing deltas */
+ tmp2[0][0] = 0; tmp2[0][1] = 0; tmp2[0][2] = 0; tmp2[0][3] = 1;
+ tmp2[1][0] = d3; tmp2[1][1] = d2; tmp2[1][2] = d; tmp2[1][3] = 0;
+ tmp2[2][0] = 6*d3; tmp2[2][1] = 2*d2; tmp2[2][2] = 0; tmp2[2][3] = 0;
+ tmp2[3][0] = 6*d3; tmp2[3][1] = 0; tmp2[3][2] = 0; tmp2[3][3] = 0;
+
+ /* compose the basis and geometry matrices */
+
+ static const float CR_basis[4][4] = {
+ { -0.5, 1.5, -1.5, 0.5 },
+ { 1.0, -2.5, 2.0, -0.5 },
+ { -0.5, 0.0, 0.5, 0.0 },
+ { 0.0, 1.0, 0.0, 0.0 },
+ };
+
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 0; j < 4; j++)
+ {
+ tmp1[i][j] = (CR_basis[i][0] * geometry[0][j] +
+ CR_basis[i][1] * geometry[1][j] +
+ CR_basis[i][2] * geometry[2][j] +
+ CR_basis[i][3] * geometry[3][j]);
+ }
+ }
+ /* compose the above results to get the deltas matrix */
+
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 0; j < 4; j++)
+ {
+ deltas[i][j] = (tmp2[i][0] * tmp1[0][j] +
+ tmp2[i][1] * tmp1[1][j] +
+ tmp2[i][2] * tmp1[2][j] +
+ tmp2[i][3] * tmp1[3][j]);
+ }
+ }
+
+
+ /* extract the x deltas */
+ x = deltas[0][0];
+ dx = deltas[1][0];
+ dx2 = deltas[2][0];
+ dx3 = deltas[3][0];
+
+ /* extract the y deltas */
+ y = deltas[0][1];
+ dy = deltas[1][1];
+ dy2 = deltas[2][1];
+ dy3 = deltas[3][1];
+
+
+ lastx = CLAMP (x, 0, xmax);
+ lasty = CLAMP (y, 0, ymax);
+
+ /* if (fix255)
+ {
+ cd->curve[cd->outline][lastx] = lasty;
+ }
+ else
+ {
+ cd->curve_ptr[cd->outline][lastx] = lasty;
+ if(gb_debug) printf("bender_plot_curve xmax:%d ymax:%d\n", (int)xmax, (int)ymax);
+ }
+*/
+ /* loop over the curve */
+ for (i = 0; i < ntimes; i++)
+ {
+ /* increment the x values */
+ x += dx;
+ dx += dx2;
+ dx2 += dx3;
+
+ /* increment the y values */
+ y += dy;
+ dy += dy2;
+ dy2 += dy3;
+
+ newx = CLAMP ((Math::round (x)), 0, xmax);
+ newy = CLAMP ((Math::round (y)), 0, ymax);
+
+ /* if this point is different than the last one...then draw it */
+ if ((lastx != newx) || (lasty != newy))
+ {
+#if 0
+ if(fix255)
+ {
+ /* use fixed array size (for the curve graph) */
+ cd->curve[cd->outline][newx] = newy;
+ }
+ else
+ {
+ /* use dynamic allocated curve_ptr (for the real curve) */
+ cd->curve_ptr[cd->outline][newx] = newy;
+
+ if(gb_debug) printf("outline: %d cX: %d cY: %d\n", (int)cd->outline, (int)newx, (int)newy);
+ }
+#endif
+ draw_line(Vector2(lastx,ymax-lasty),Vector2(newx,ymax-newy),Color(0.8,0.8,0.8,0.8),2.0);
+ }
+
+ lastx = newx;
+ lasty = newy;
+ }
+}
+
+
+void GraphCurveMapEdit::_notification(int p_what){
+
+ if (p_what==NOTIFICATION_DRAW) {
+
+ draw_style_box(get_stylebox("bg","Tree"),Rect2(Point2(),get_size()));
+
+ int w = get_size().x;
+ int h = get_size().y;
+
+ Vector2 prev=Vector2(0,0);
+ Vector2 prev2=Vector2(0,0);
+
+ for(int i=-1;i<points.size();i++) {
+
+ Vector2 next;
+ Vector2 next2;
+ if (i+1>=points.size()) {
+ next=Vector2(1,1);
+ } else {
+ next=Vector2(points[i+1].offset,points[i+1].height);
+ }
+
+ if (i+2>=points.size()) {
+ next2=Vector2(1,1);
+ } else {
+ next2=Vector2(points[i+2].offset,points[i+2].height);
+ }
+
+ /*if (i==-1 && prev.offset==next.offset) {
+ prev=next;
+ continue;
+ }*/
+
+ _plot_curve(prev2,prev,next,next2);
+
+ prev2=prev;
+ prev=next;
+ }
+
+ for(int i=0;i<points.size();i++) {
+
+ Color col=i==grabbed?Color(1,0.0,0.0,0.9):Color(1,1,1,0.8);
+
+
+ draw_rect(Rect2( Vector2(points[i].offset,1.0-points[i].height)*get_size()-Vector2(2,2),Vector2(5,5)),col);
+ }
+
+ /* if (grabbed!=-1) {
+
+ draw_rect(Rect2(total_w+3,0,h,h),points[grabbed].color);
+ }
+*/
+ if (has_focus()) {
+
+ draw_line(Vector2(-1,-1),Vector2(w+1,-1),Color(1,1,1,0.6));
+ draw_line(Vector2(w+1,-1),Vector2(w+1,h+1),Color(1,1,1,0.6));
+ draw_line(Vector2(w+1,h+1),Vector2(-1,h+1),Color(1,1,1,0.6));
+ draw_line(Vector2(-1,-1),Vector2(-1,h+1),Color(1,1,1,0.6));
+ }
+
+ }
+}
+
+Size2 GraphCurveMapEdit::get_minimum_size() const {
+
+ return Vector2(64,64);
+}
+
+
+
+void GraphCurveMapEdit::set_points(const Vector<Vector2>& p_points) {
+
+
+ points.clear();
+ for(int i=0;i<p_points.size();i++) {
+ Point p;
+ p.offset=p_points[i].x;
+ p.height=p_points[i].y;
+ points.push_back(p);
+ }
+
+ points.sort();
+ update();
+}
+
+Vector<Vector2> GraphCurveMapEdit::get_points() const {
+ Vector<Vector2> ret;
+ for(int i=0;i<points.size();i++)
+ ret.push_back(Vector2(points[i].offset,points[i].height));
+ return ret;
+}
+
+void GraphCurveMapEdit::_bind_methods(){
+
+ ObjectTypeDB::bind_method(_MD("_input_event"),&GraphCurveMapEdit::_input_event);
+ ADD_SIGNAL(MethodInfo("curve_changed"));
+}
+
+GraphCurveMapEdit::GraphCurveMapEdit(){
+
+ grabbed=-1;
+ grabbing=false;
+ set_focus_mode(FOCUS_ALL);
+
+}
+
+
+////cbacks
+///
+void ShaderGraphView::_scalar_const_changed(double p_value,int p_id) {
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change Scalar Constant"),UndoRedo::MERGE_ENDS);
+ ur->add_do_method(graph.ptr(),"scalar_const_node_set_value",type,p_id,p_value);
+ ur->add_undo_method(graph.ptr(),"scalar_const_node_set_value",type,p_id,graph->scalar_const_node_get_value(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
+}
+
+void ShaderGraphView::_vec_const_changed(double p_value, int p_id,Array p_arr){
+
+ Vector3 val;
+ for(int i=0;i<p_arr.size();i++) {
+ val[i]=p_arr[i].call("get_val");
+ }
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change Vec Constant"),UndoRedo::MERGE_ENDS);
+ ur->add_do_method(graph.ptr(),"vec_const_node_set_value",type,p_id,val);
+ ur->add_undo_method(graph.ptr(),"vec_const_node_set_value",type,p_id,graph->vec_const_node_get_value(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
+
+}
+void ShaderGraphView::_rgb_const_changed(const Color& p_color, int p_id){
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change RGB Constant"),UndoRedo::MERGE_ENDS);
+ ur->add_do_method(graph.ptr(),"rgb_const_node_set_value",type,p_id,p_color);
+ ur->add_undo_method(graph.ptr(),"rgb_const_node_set_value",type,p_id,graph->rgb_const_node_get_value(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
+
+}
+void ShaderGraphView::_scalar_op_changed(int p_op, int p_id){
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change Scalar Operator"));
+ ur->add_do_method(graph.ptr(),"scalar_op_node_set_op",type,p_id,p_op);
+ ur->add_undo_method(graph.ptr(),"scalar_op_node_set_op",type,p_id,graph->scalar_op_node_get_op(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
+
+}
+void ShaderGraphView::_vec_op_changed(int p_op, int p_id){
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change Vec Operator"));
+ ur->add_do_method(graph.ptr(),"vec_op_node_set_op",type,p_id,p_op);
+ ur->add_undo_method(graph.ptr(),"vec_op_node_set_op",type,p_id,graph->vec_op_node_get_op(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
+}
+void ShaderGraphView::_vec_scalar_op_changed(int p_op, int p_id){
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change Vec Scalar Operator"));
+ ur->add_do_method(graph.ptr(),"vec_scalar_op_node_set_op",type,p_id,p_op);
+ ur->add_undo_method(graph.ptr(),"vec_scalar_op_node_set_op",type,p_id,graph->vec_scalar_op_node_get_op(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
+
+}
+void ShaderGraphView::_rgb_op_changed(int p_op, int p_id){
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change RGB Operator"));
+ ur->add_do_method(graph.ptr(),"rgb_op_node_set_op",type,p_id,p_op);
+ ur->add_undo_method(graph.ptr(),"rgb_op_node_set_op",type,p_id,graph->rgb_op_node_get_op(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
+}
+void ShaderGraphView::_xform_inv_rev_changed(bool p_enabled, int p_id){
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Toggle Rot Only"));
+ ur->add_do_method(graph.ptr(),"xform_vec_mult_node_set_no_translation",type,p_id,p_enabled);
+ ur->add_undo_method(graph.ptr(),"xform_vec_mult_node_set_no_translation",type,p_id,graph->xform_vec_mult_node_get_no_translation(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
+}
+void ShaderGraphView::_scalar_func_changed(int p_func, int p_id){
+
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change Scalar Function"));
+ ur->add_do_method(graph.ptr(),"scalar_func_node_set_function",type,p_id,p_func);
+ ur->add_undo_method(graph.ptr(),"scalar_func_node_set_function",type,p_id,graph->scalar_func_node_get_function(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
+}
+void ShaderGraphView::_vec_func_changed(int p_func, int p_id){
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change Vec Function"));
+ ur->add_do_method(graph.ptr(),"vec_func_node_set_function",type,p_id,p_func);
+ ur->add_undo_method(graph.ptr(),"vec_func_node_set_function",type,p_id,graph->vec_func_node_get_function(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
+
+}
+void ShaderGraphView::_scalar_input_changed(double p_value,int p_id){
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change Scalar Uniform"),UndoRedo::MERGE_ENDS);
+ ur->add_do_method(graph.ptr(),"scalar_input_node_set_value",type,p_id,p_value);
+ ur->add_undo_method(graph.ptr(),"scalar_input_node_set_value",type,p_id,graph->scalar_input_node_get_value(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
+
+}
+void ShaderGraphView::_vec_input_changed(double p_value, int p_id,Array p_arr){
+
+ Vector3 val;
+ for(int i=0;i<p_arr.size();i++) {
+ val[i]=p_arr[i].call("get_val");
+ }
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change Vec Uniform"),UndoRedo::MERGE_ENDS);
+ ur->add_do_method(graph.ptr(),"vec_input_node_set_value",type,p_id,val);
+ ur->add_undo_method(graph.ptr(),"vec_input_node_set_value",type,p_id,graph->vec_input_node_get_value(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
+
+}
+void ShaderGraphView::_xform_input_changed(int p_id, Node *p_button){
+
+
+ ToolButton *tb = p_button->cast_to<ToolButton>();
+ ped_popup->set_pos(tb->get_global_pos()+Vector2(0,tb->get_size().height));
+ ped_popup->set_size(tb->get_size());
+ edited_id=p_id;
+ edited_def=-1;
+ ped_popup->edit(NULL,"",Variant::TRANSFORM,graph->xform_input_node_get_value(type,p_id),PROPERTY_HINT_NONE,"");
+ ped_popup->popup();
+
+}
+void ShaderGraphView::_xform_const_changed(int p_id, Node *p_button){
+
+ ToolButton *tb = p_button->cast_to<ToolButton>();
+ ped_popup->set_pos(tb->get_global_pos()+Vector2(0,tb->get_size().height));
+ ped_popup->set_size(tb->get_size());
+ edited_id=p_id;
+ edited_def=-1;
+ ped_popup->edit(NULL,"",Variant::TRANSFORM,graph->xform_const_node_get_value(type,p_id),PROPERTY_HINT_NONE,"");
+ ped_popup->popup();
+
+}
+
+void ShaderGraphView::_rgb_input_changed(const Color& p_color, int p_id){
+
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change RGB Uniform"),UndoRedo::MERGE_ENDS);
+ ur->add_do_method(graph.ptr(),"rgb_input_node_set_value",type,p_id,p_color);
+ ur->add_undo_method(graph.ptr(),"rgb_input_node_set_value",type,p_id,graph->rgb_input_node_get_value(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
+}
+void ShaderGraphView::_tex_input_change(int p_id, Node *p_button){
+
+
+}
+void ShaderGraphView::_cube_input_change(int p_id){
+
+
+}
+
+void ShaderGraphView::_variant_edited() {
+
+ if (edited_def != -1) {
+
+ Variant v = ped_popup->get_variant();
+ Variant v2 = graph->default_get_value(type,edited_id,edited_def);
+ if (v2.get_type() == Variant::NIL)
+ switch (v.get_type()) {
+ case Variant::VECTOR3:
+ v2=Vector3();
+ break;
+ case Variant::REAL:
+ v2=0.0;
+ break;
+ case Variant::TRANSFORM:
+ v2=Transform();
+ break;
+ case Variant::COLOR:
+ v2=Color();
+ break;
+ }
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change Default Value"));
+ ur->add_do_method(graph.ptr(),"default_set_value",type,edited_id,edited_def, v);
+ ur->add_undo_method(graph.ptr(),"default_set_value",type,edited_id,edited_def, v2);
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ ur->commit_action();
+ return;
+ }
+
+ if (graph->node_get_type(type,edited_id)==ShaderGraph::NODE_XFORM_CONST) {
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change XForm Uniform"));
+ ur->add_do_method(graph.ptr(),"xform_const_node_set_value",type,edited_id,ped_popup->get_variant());
+ ur->add_undo_method(graph.ptr(),"xform_const_node_set_value",type,edited_id,graph->xform_const_node_get_value(type,edited_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ ur->commit_action();
+ }
+
+
+ if (graph->node_get_type(type,edited_id)==ShaderGraph::NODE_XFORM_INPUT) {
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change XForm Uniform"));
+ ur->add_do_method(graph.ptr(),"xform_input_node_set_value",type,edited_id,ped_popup->get_variant());
+ ur->add_undo_method(graph.ptr(),"xform_input_node_set_value",type,edited_id,graph->xform_input_node_get_value(type,edited_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ ur->commit_action();
+ }
+
+ if (graph->node_get_type(type,edited_id)==ShaderGraph::NODE_TEXTURE_INPUT) {
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change Texture Uniform"));
+ ur->add_do_method(graph.ptr(),"texture_input_node_set_value",type,edited_id,ped_popup->get_variant());
+ ur->add_undo_method(graph.ptr(),"texture_input_node_set_value",type,edited_id,graph->texture_input_node_get_value(type,edited_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ ur->commit_action();
+ }
+
+ if (graph->node_get_type(type,edited_id)==ShaderGraph::NODE_CUBEMAP_INPUT) {
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change Cubemap Uniform"));
+ ur->add_do_method(graph.ptr(),"cubemap_input_node_set_value",type,edited_id,ped_popup->get_variant());
+ ur->add_undo_method(graph.ptr(),"cubemap_input_node_set_value",type,edited_id,graph->cubemap_input_node_get_value(type,edited_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ ur->commit_action();
+ }
+
+}
+
+void ShaderGraphView::_comment_edited(int p_id,Node* p_button) {
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ TextEdit *te=p_button->cast_to<TextEdit>();
+ ur->create_action(TTR("Change Comment"),UndoRedo::MERGE_ENDS);
+ ur->add_do_method(graph.ptr(),"comment_node_set_text",type,p_id,te->get_text());
+ ur->add_undo_method(graph.ptr(),"comment_node_set_text",type,p_id,graph->comment_node_get_text(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
+
+}
+
+void ShaderGraphView::_color_ramp_changed(int p_id,Node* p_ramp) {
+
+ GraphColorRampEdit *cr=p_ramp->cast_to<GraphColorRampEdit>();
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+
+
+ Vector<float> offsets=cr->get_offsets();
+ Vector<Color> colors=cr->get_colors();
+
+ DVector<float> new_offsets;
+ DVector<Color> new_colors;
+ {
+ new_offsets.resize(offsets.size());
+ new_colors.resize(colors.size());
+ DVector<float>::Write ow=new_offsets.write();
+ DVector<Color>::Write cw=new_colors.write();
+ for(int i=0;i<new_offsets.size();i++) {
+ ow[i]=offsets[i];
+ cw[i]=colors[i];
+ }
+
+ }
+
+
+ DVector<float> old_offsets=graph->color_ramp_node_get_offsets(type,p_id);
+ DVector<Color> old_colors=graph->color_ramp_node_get_colors(type,p_id);
+
+ if (old_offsets.size()!=new_offsets.size())
+ ur->create_action(TTR("Add/Remove to Color Ramp"));
+ else
+ ur->create_action(TTR("Modify Color Ramp"),UndoRedo::MERGE_ENDS);
+
+ ur->add_do_method(graph.ptr(),"color_ramp_node_set_ramp",type,p_id,new_colors,new_offsets);
+ ur->add_undo_method(graph.ptr(),"color_ramp_node_set_ramp",type,p_id,old_colors,old_offsets);
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
+}
+
+void ShaderGraphView::_curve_changed(int p_id,Node* p_curve) {
+
+ GraphCurveMapEdit *cr=p_curve->cast_to<GraphCurveMapEdit>();
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+
+
+ Vector<Point2> points=cr->get_points();
+
+ DVector<Vector2> new_points;
+ {
+ new_points.resize(points.size());
+ DVector<Vector2>::Write ow=new_points.write();
+ for(int i=0;i<new_points.size();i++) {
+ ow[i]=points[i];
+ }
+
+ }
+
+
+ DVector<Vector2> old_points=graph->curve_map_node_get_points(type,p_id);
+
+ if (old_points.size()!=new_points.size())
+ ur->create_action(TTR("Add/Remove to Curve Map"));
+ else
+ ur->create_action(TTR("Modify Curve Map"),UndoRedo::MERGE_ENDS);
+
+ ur->add_do_method(graph.ptr(),"curve_map_node_set_points",type,p_id,new_points);
+ ur->add_undo_method(graph.ptr(),"curve_map_node_set_points",type,p_id,old_points);
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
+}
+
+
+void ShaderGraphView::_input_name_changed(const String& p_name, int p_id, Node *p_line_edit) {
+
+ LineEdit *le=p_line_edit->cast_to<LineEdit>();
+ ERR_FAIL_COND(!le);
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change Input Name"));
+ ur->add_do_method(graph.ptr(),"input_node_set_name",type,p_id,p_name);
+ ur->add_undo_method(graph.ptr(),"input_node_set_name",type,p_id,graph->input_node_get_name(type,p_id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ block_update=true;
+ ur->commit_action();
+ block_update=false;
+ le->set_text(graph->input_node_get_name(type,p_id));
+}
+
+void ShaderGraphView::_tex_edited(int p_id,Node* p_button) {
+
+ ToolButton *tb = p_button->cast_to<ToolButton>();
+ ped_popup->set_pos(tb->get_global_pos()+Vector2(0,tb->get_size().height));
+ ped_popup->set_size(tb->get_size());
+ edited_id=p_id;
+ edited_def=-1;
+ ped_popup->edit(NULL,"",Variant::OBJECT,graph->texture_input_node_get_value(type,p_id),PROPERTY_HINT_RESOURCE_TYPE,"Texture");
+}
+
+void ShaderGraphView::_cube_edited(int p_id,Node* p_button) {
+
+ ToolButton *tb = p_button->cast_to<ToolButton>();
+ ped_popup->set_pos(tb->get_global_pos()+Vector2(0,tb->get_size().height));
+ ped_popup->set_size(tb->get_size());
+ edited_id=p_id;
+ edited_def=-1;
+ ped_popup->edit(NULL,"",Variant::OBJECT,graph->cubemap_input_node_get_value(type,p_id),PROPERTY_HINT_RESOURCE_TYPE,"CubeMap");
+}
+
+
+//////////////view/////////////
+
+
+void ShaderGraphView::_connection_request(const String& p_from, int p_from_slot,const String& p_to,int p_to_slot) {
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+
+ int from_idx=-1;
+ int to_idx=-1;
+ for (Map<int,GraphNode*>::Element *E=node_map.front();E;E=E->next()) {
+
+ if (p_from==E->get()->get_name())
+ from_idx=E->key();
+ if (p_to==E->get()->get_name())
+ to_idx=E->key();
+ }
+
+ ERR_FAIL_COND(from_idx==-1);
+ ERR_FAIL_COND(to_idx==-1);
+
+ ur->create_action(TTR("Connect Graph Nodes"));
+
+ List<ShaderGraph::Connection> conns;
+
+ graph->get_node_connections(type,&conns);
+ //disconnect/reconnect dependencies
+ ur->add_undo_method(graph.ptr(),"disconnect_node",type,from_idx,p_from_slot,to_idx,p_to_slot);
+ for(List<ShaderGraph::Connection>::Element *E=conns.front();E;E=E->next()) {
+
+ if (E->get().dst_id==to_idx && E->get().dst_slot==p_to_slot) {
+ ur->add_do_method(graph.ptr(),"disconnect_node",type,E->get().src_id,E->get().src_slot,E->get().dst_id,E->get().dst_slot);
+ ur->add_undo_method(graph.ptr(),"connect_node",type,E->get().src_id,E->get().src_slot,E->get().dst_id,E->get().dst_slot);
+ }
+ }
+ ur->add_do_method(graph.ptr(),"connect_node",type,from_idx,p_from_slot,to_idx,p_to_slot);
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ ur->commit_action();
+
+
+}
+
+void ShaderGraphView::_disconnection_request(const String& p_from, int p_from_slot,const String& p_to,int p_to_slot) {
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+
+ int from_idx=-1;
+ int to_idx=-1;
+ for (Map<int,GraphNode*>::Element *E=node_map.front();E;E=E->next()) {
+
+ if (p_from==E->get()->get_name())
+ from_idx=E->key();
+ if (p_to==E->get()->get_name())
+ to_idx=E->key();
+ }
+
+ ERR_FAIL_COND(from_idx==-1);
+ ERR_FAIL_COND(to_idx==-1);
+
+ if (!graph->is_node_connected(type,from_idx,p_from_slot,to_idx,p_to_slot))
+ return; //nothing to disconnect
+
+ ur->create_action(TTR("Disconnect Graph Nodes"));
+
+ List<ShaderGraph::Connection> conns;
+
+ graph->get_node_connections(type,&conns);
+ //disconnect/reconnect dependencies
+ ur->add_do_method(graph.ptr(),"disconnect_node",type,from_idx,p_from_slot,to_idx,p_to_slot);
+ ur->add_undo_method(graph.ptr(),"connect_node",type,from_idx,p_from_slot,to_idx,p_to_slot);
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ ur->commit_action();
+
+
+}
+
+void ShaderGraphView::_node_removed(int p_id) {
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Remove Shader Graph Node"));
+
+ ur->add_do_method(graph.ptr(),"node_remove",type,p_id);
+ ur->add_undo_method(graph.ptr(),"node_add",type,graph->node_get_type(type,p_id),p_id);
+ ur->add_undo_method(graph.ptr(),"node_set_state",type,p_id,graph->node_get_state(type,p_id));
+ List<ShaderGraph::Connection> conns;
+
+ graph->get_node_connections(type,&conns);
+ for(List<ShaderGraph::Connection>::Element *E=conns.front();E;E=E->next()) {
+
+ if (E->get().dst_id==p_id || E->get().src_id==p_id) {
+ ur->add_undo_method(graph.ptr(),"connect_node",type,E->get().src_id,E->get().src_slot,E->get().dst_id,E->get().dst_slot);
+ }
+ }
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ ur->commit_action();
+
+}
+
+void ShaderGraphView::_begin_node_move()
+{
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Move Shader Graph Node"));
+}
+
+void ShaderGraphView::_node_moved(const Vector2& p_from, const Vector2& p_to,int p_id) {
+
+
+ ERR_FAIL_COND(!node_map.has(p_id));
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->add_do_method(this,"_move_node",p_id,p_to);
+ ur->add_undo_method(this,"_move_node",p_id,p_from);
+}
+
+void ShaderGraphView::_end_node_move()
+{
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->commit_action();
+}
+
+void ShaderGraphView::_move_node(int p_id,const Vector2& p_to) {
+
+ ERR_FAIL_COND(!node_map.has(p_id));
+ node_map[p_id]->set_offset(p_to);
+ graph->node_set_pos(type,p_id,p_to);
+}
+
+void ShaderGraphView::_duplicate_nodes_request()
+{
+ Array s_id;
+
+ for(Map<int,GraphNode*>::Element *E=node_map.front();E;E=E->next()) {
+ ShaderGraph::NodeType t=graph->node_get_type(type, E->key());
+ if (t==ShaderGraph::NODE_OUTPUT || t==ShaderGraph::NODE_INPUT)
+ continue;
+ GraphNode *gn = E->get();
+ if (gn && gn->is_selected())
+ s_id.push_back(E->key());
+ }
+
+ if (s_id.size()==0)
+ return;
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Duplicate Graph Node(s)"));
+ ur->add_do_method(this,"_duplicate_nodes",s_id);
+ List<int> n_ids = graph->generate_ids(type, s_id.size());
+ for (List<int>::Element *E=n_ids.front();E;E=E->next())
+ ur->add_undo_method(graph.ptr(),"node_remove",type,E->get());
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ ur->commit_action();
+
+}
+
+void ShaderGraphView::_duplicate_nodes(const Array &p_nodes)
+{
+ List<int> n = List<int>();
+ for (int i=0; i<p_nodes.size();i++)
+ n.push_back(p_nodes.get(i));
+ graph->duplicate_nodes(type, n);
+ call_deferred("_update_graph");
+}
+
+void ShaderGraphView::_delete_nodes_request()
+{
+ List<int> s_id=List<int>();
+
+ for(Map<int,GraphNode*>::Element *E=node_map.front();E;E=E->next()) {
+ ShaderGraph::NodeType t=graph->node_get_type(type, E->key());
+ if (t==ShaderGraph::NODE_OUTPUT)
+ continue;
+ GraphNode *gn = E->get();
+ if (gn && gn->is_selected())
+ s_id.push_back(E->key());
+ }
+
+ if (s_id.size()==0)
+ return;
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Delete Shader Graph Node(s)"));
+
+ for (List<int>::Element *N=s_id.front();N;N=N->next()) {
+ ur->add_do_method(graph.ptr(),"node_remove",type,N->get());
+ ur->add_undo_method(graph.ptr(),"node_add",type,graph->node_get_type(type,N->get()),N->get());
+ ur->add_undo_method(graph.ptr(),"node_set_state",type,N->get(),graph->node_get_state(type,N->get()));
+ List<ShaderGraph::Connection> conns;
+
+ graph->get_node_connections(type,&conns);
+ for(List<ShaderGraph::Connection>::Element *E=conns.front();E;E=E->next()) {
+
+ if (E->get().dst_id==N->get() || E->get().src_id==N->get()) {
+ ur->add_undo_method(graph.ptr(),"connect_node",type,E->get().src_id,E->get().src_slot,E->get().dst_id,E->get().dst_slot);
+ }
+ }
+ }
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ ur->commit_action();
+
+}
+
+void ShaderGraphView::_default_changed(int p_id, Node *p_button, int p_param, int v_type, String p_hint)
+{
+ ToolButton *tb = p_button->cast_to<ToolButton>();
+ ped_popup->set_pos(tb->get_global_pos()+Vector2(0,tb->get_size().height));
+ ped_popup->set_size(tb->get_size());
+ edited_id=p_id;
+ edited_def=p_param;
+ Variant::Type vt = (Variant::Type)v_type;
+ Variant v = graph->default_get_value(type,p_id,edited_def);
+ int h=PROPERTY_HINT_NONE;
+ if (v.get_type() == Variant::NIL)
+ switch (vt) {
+ case Variant::VECTOR3:
+ v=Vector3();
+ break;
+ case Variant::REAL:
+ h=PROPERTY_HINT_RANGE;
+ v=0.0;
+ break;
+ case Variant::TRANSFORM:
+ v=Transform();
+ break;
+ case Variant::COLOR:
+ h=PROPERTY_HINT_COLOR_NO_ALPHA;
+ v=Color();
+ break;
+ }
+
+ ped_popup->edit(NULL,"",vt,v,h,p_hint);
+
+ ped_popup->popup();
+}
+
+ToolButton *ShaderGraphView::make_label(String text, Variant::Type v_type) {
+ ToolButton *l = memnew( ToolButton );
+ l->set_text(text);
+ l->set_text_align(ToolButton::ALIGN_LEFT);
+ l->add_style_override("hover", l->get_stylebox("normal", "ToolButton"));
+ l->add_style_override("pressed", l->get_stylebox("normal", "ToolButton"));
+ l->add_style_override("focus", l->get_stylebox("normal", "ToolButton"));
+ switch (v_type) {
+ case Variant::REAL:
+ l->set_icon(ped_popup->get_icon("Real", "EditorIcons"));
+ break;
+ case Variant::VECTOR3:
+ l->set_icon(ped_popup->get_icon("Vector", "EditorIcons"));
+ break;
+ case Variant::TRANSFORM:
+ l->set_icon(ped_popup->get_icon("Matrix", "EditorIcons"));
+ break;
+ case Variant::COLOR:
+ l->set_icon(ped_popup->get_icon("Color", "EditorIcons"));
+ }
+ return l;
+}
+
+ToolButton *ShaderGraphView::make_editor(String text,GraphNode* gn,int p_id,int param,Variant::Type v_type, String p_hint) {
+ ToolButton *edit = memnew( ToolButton );
+ edit->set_text(text);
+ edit->set_text_align(ToolButton::ALIGN_LEFT);
+ edit->set_flat(false);
+ edit->add_style_override("normal", gn->get_stylebox("defaultframe", "GraphNode"));
+ edit->add_style_override("hover", gn->get_stylebox("defaultframe", "GraphNode"));
+ edit->add_style_override("pressed", gn->get_stylebox("defaultframe", "GraphNode"));
+ edit->add_style_override("focus", gn->get_stylebox("defaultfocus", "GraphNode"));
+ edit->connect("pressed",this,"_default_changed",varray(p_id,edit,param,v_type,p_hint));
+
+ switch (v_type) {
+ case Variant::REAL:
+ edit->set_icon(ped_popup->get_icon("Real", "EditorIcons"));
+ break;
+ case Variant::VECTOR3:
+ edit->set_icon(ped_popup->get_icon("Vector", "EditorIcons"));
+ break;
+ case Variant::TRANSFORM:
+ edit->set_icon(ped_popup->get_icon("Matrix", "EditorIcons"));
+ break;
+ case Variant::COLOR:
+ Image icon_color = Image(15,15,false,Image::FORMAT_RGB);
+ Color c = graph->default_get_value(type,p_id,param);
+ for (int x=1;x<14;x++)
+ for (int y=1;y<14;y++)
+ icon_color.put_pixel(x,y,c);
+ Ref<ImageTexture> t;
+ t.instance();
+ t->create_from_image(icon_color);
+ edit->set_icon(t);
+ break;
+ }
+ return edit;
+}
+
+void ShaderGraphView::_create_node(int p_id) {
+
+
+ GraphNode *gn = memnew( GraphNode );
+ gn->set_show_close_button(true);
+ Color typecol[4]={
+ Color(0.9,0.4,1),
+ Color(0.8,1,0.2),
+ Color(1,0.2,0.2),
+ Color(0,1,1)
+ };
+
+ const String hint_spin = "-65536,65535,0.001";
+ const String hint_slider = "0.0,1.0,0.01,slider";
+
+
+ switch(graph->node_get_type(type,p_id)) {
+
+ case ShaderGraph::NODE_INPUT: {
+
+ gn->set_title("Input");
+
+ List<ShaderGraph::SlotInfo> si;
+ ShaderGraph::get_input_output_node_slot_info(graph->get_mode(),type,&si);
+
+ int idx=0;
+ for (List<ShaderGraph::SlotInfo>::Element *E=si.front();E;E=E->next()) {
+ ShaderGraph::SlotInfo& s=E->get();
+ if (s.dir==ShaderGraph::SLOT_IN) {
+
+ Label *l= memnew( Label );
+ l->set_text(s.name);
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child(l);
+ gn->set_slot(idx,false,0,Color(),true,s.type,typecol[s.type]);
+ idx++;
+ }
+ }
+
+ } break; // all inputs (case Shader type dependent)
+ case ShaderGraph::NODE_SCALAR_CONST: {
+ gn->set_title("Scalar");
+ SpinBox *sb = memnew( SpinBox );
+ sb->set_min(-100000);
+ sb->set_max(100000);
+ sb->set_step(0.001);
+ sb->set_val(graph->scalar_const_node_get_value(type,p_id));
+ sb->connect("value_changed",this,"_scalar_const_changed",varray(p_id));
+ gn->add_child(sb);
+ gn->set_slot(0,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+
+ } break; //scalar constant
+ case ShaderGraph::NODE_VEC_CONST: {
+
+ gn->set_title("Vector");
+ Array v3p(true);
+ for(int i=0;i<3;i++) {
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ Label *l = memnew( Label );
+ l->set_text(String::chr('X'+i));
+ hbc->add_child(l);
+ SpinBox *sb = memnew( SpinBox );
+ sb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ sb->set_min(-100000);
+ sb->set_max(100000);
+ sb->set_step(0.001);
+ sb->set_val(graph->vec_const_node_get_value(type,p_id)[i]);
+ sb->connect("value_changed",this,"_vec_const_changed",varray(p_id,v3p));
+ v3p.push_back(sb);
+ hbc->add_child(sb);
+ gn->add_child(hbc);
+ }
+ gn->set_slot(0,false,0,Color(),true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+
+ } break; //vec3 constant
+ case ShaderGraph::NODE_RGB_CONST: {
+
+ gn->set_title("Color");
+ ColorPickerButton *cpb = memnew( ColorPickerButton );
+ cpb->set_color(graph->rgb_const_node_get_value(type,p_id));
+ cpb->connect("color_changed",this,"_rgb_const_changed",varray(p_id));
+ gn->add_child(cpb);
+ Label *l = memnew( Label );
+ l->set_text("RGB");
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child(l);
+ l = memnew( Label );
+ l->set_text("Alpha");
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child(l);
+
+ gn->set_slot(1,false,0,Color(),true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(2,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+
+ } break; //rgb constant (shows a color picker instead)
+ case ShaderGraph::NODE_XFORM_CONST: {
+ gn->set_title("XForm");
+ ToolButton *edit = memnew( ToolButton );
+ edit->set_text("edit..");
+ edit->connect("pressed",this,"_xform_const_changed",varray(p_id,edit));
+ gn->add_child(edit);
+ gn->set_slot(0,false,0,Color(),true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM]);
+
+ } break; // 4x4 matrix constant
+ case ShaderGraph::NODE_TIME: {
+
+ gn->set_title("Time");
+ Label *l = memnew( Label );
+ l->set_text("(s)");
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child(l);
+ gn->set_slot(0,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+
+ } break; // time in seconds
+ case ShaderGraph::NODE_SCREEN_TEX: {
+
+ gn->set_title("ScreenTex");
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (!graph->is_slot_connected(type,p_id,0)) {
+ Vector3 v = graph->default_get_value(type, p_id, 0);
+ hbc->add_child(make_editor("UV: " + v,gn,p_id,0,Variant::VECTOR3));
+ } else {
+ hbc->add_child(make_label("UV",Variant::VECTOR3));
+ }
+ hbc->add_spacer();
+ hbc->add_child( memnew(Label("RGB")));
+ gn->add_child(hbc);
+ gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+
+ } break; // screen texture sampler (takes UV) (only usable in fragment case Shader)
+ case ShaderGraph::NODE_SCALAR_OP: {
+
+ gn->set_title("ScalarOp");
+ static const char* op_name[ShaderGraph::SCALAR_MAX_OP]={
+ ("Add"),
+ ("Sub"),
+ ("Mul"),
+ ("Div"),
+ ("Mod"),
+ ("Pow"),
+ ("Max"),
+ ("Min"),
+ ("Atan2")
+ };
+
+ OptionButton *ob = memnew( OptionButton );
+ for(int i=0;i<ShaderGraph::SCALAR_MAX_OP;i++) {
+
+ ob->add_item(op_name[i],i);
+ }
+
+ ob->select(graph->scalar_op_node_get_op(type,p_id));
+ ob->connect("item_selected",this,"_scalar_op_changed",varray(p_id));
+ gn->add_child(ob);
+
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("a",Variant::REAL));
+ } else {
+ float v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("a: ")+Variant(v),gn,p_id,0,Variant::REAL,hint_spin));
+ }
+ hbc->add_spacer();
+ hbc->add_child( memnew(Label("out")));
+ gn->add_child(hbc);
+ if (graph->is_slot_connected(type, p_id, 1)) {
+ gn->add_child(make_label("b",Variant::REAL));
+ } else {
+ float v = graph->default_get_value(type,p_id,1);
+ gn->add_child(make_editor(String("b: ")+Variant(v),gn,p_id,1,Variant::REAL,hint_spin));
+ }
+
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+ gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],false,0,Color());
+
+
+ } break; // scalar vs scalar op (mul: { } break; add: { } break; div: { } break; etc)
+ case ShaderGraph::NODE_VEC_OP: {
+
+ gn->set_title("VecOp");
+ static const char* op_name[ShaderGraph::VEC_MAX_OP]={
+ ("Add"),
+ ("Sub"),
+ ("Mul"),
+ ("Div"),
+ ("Mod"),
+ ("Pow"),
+ ("Max"),
+ ("Min"),
+ ("Cross")
+ };
+
+ OptionButton *ob = memnew( OptionButton );
+ for(int i=0;i<ShaderGraph::VEC_MAX_OP;i++) {
+
+ ob->add_item(op_name[i],i);
+ }
+
+ ob->select(graph->vec_op_node_get_op(type,p_id));
+ ob->connect("item_selected",this,"_vec_op_changed",varray(p_id));
+ gn->add_child(ob);
+
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("a",Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("a: ")+v,gn,p_id,0,Variant::VECTOR3));
+ }
+ hbc->add_spacer();
+ hbc->add_child( memnew(Label("out")));
+ gn->add_child(hbc);
+ if (graph->is_slot_connected(type, p_id, 1)) {
+ gn->add_child(make_label("b",Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,1);
+ gn->add_child(make_editor(String("b: ")+v,gn,p_id,1,Variant::VECTOR3));
+ }
+
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],false,0,Color());
+
+
+ } break; // vec3 vs vec3 op (mul: { } break;ad: { } break;div: { } break;crossprod: { } break;etc)
+ case ShaderGraph::NODE_VEC_SCALAR_OP: {
+
+ gn->set_title("VecScalarOp");
+ static const char* op_name[ShaderGraph::VEC_SCALAR_MAX_OP]={
+ ("Mul"),
+ ("Div"),
+ ("Pow"),
+ };
+
+ OptionButton *ob = memnew( OptionButton );
+ for(int i=0;i<ShaderGraph::VEC_SCALAR_MAX_OP;i++) {
+
+ ob->add_item(op_name[i],i);
+ }
+
+ ob->select(graph->vec_scalar_op_node_get_op(type,p_id));
+ ob->connect("item_selected",this,"_vec_scalar_op_changed",varray(p_id));
+ gn->add_child(ob);
+
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("a",Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("a: ")+v,gn,p_id,0,Variant::VECTOR3));
+ }
+ hbc->add_spacer();
+ hbc->add_child( memnew(Label("out")));
+ gn->add_child(hbc);
+
+ if (graph->is_slot_connected(type, p_id, 1)) {
+ gn->add_child(make_label("b",Variant::REAL));
+ } else {
+ float v = graph->default_get_value(type,p_id,1);
+ gn->add_child(make_editor(String("b: ")+Variant(v),gn,p_id,1,Variant::REAL,hint_spin));
+ }
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],false,0,Color());
+
+
+ } break; // vec3 vs scalar op (mul: { } break; add: { } break; div: { } break; etc)
+ case ShaderGraph::NODE_RGB_OP: {
+
+ gn->set_title("RGB Op");
+ static const char* op_name[ShaderGraph::RGB_MAX_OP]={
+ ("Screen"),
+ ("Difference"),
+ ("Darken"),
+ ("Lighten"),
+ ("Overlay"),
+ ("Dodge"),
+ ("Burn"),
+ ("SoftLight"),
+ ("HardLight")
+ };
+
+ OptionButton *ob = memnew( OptionButton );
+ for(int i=0;i<ShaderGraph::RGB_MAX_OP;i++) {
+
+ ob->add_item(op_name[i],i);
+ }
+
+ ob->select(graph->rgb_op_node_get_op(type,p_id));
+ ob->connect("item_selected",this,"_rgb_op_changed",varray(p_id));
+ gn->add_child(ob);
+
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("a",Variant::COLOR));
+ } else {
+ hbc->add_child(make_editor(String("a: "),gn,p_id,0,Variant::COLOR));
+ }
+ hbc->add_spacer();
+ hbc->add_child( memnew(Label("out")));
+ gn->add_child(hbc);
+ if (graph->is_slot_connected(type, p_id, 1)) {
+ gn->add_child(make_label("b",Variant::COLOR));
+ } else {
+ gn->add_child(make_editor(String("b: "),gn,p_id,1,Variant::COLOR));
+ }
+
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],false,0,Color());
+
+ } break; // vec3 vs vec3 rgb op (with scalar amount): { } break; like brighten: { } break; darken: { } break; burn: { } break; dodge: { } break; multiply: { } break; etc.
+ case ShaderGraph::NODE_XFORM_MULT: {
+
+ gn->set_title("XFMult");
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("a",Variant::TRANSFORM));
+ } else {
+ hbc->add_child(make_editor(String("a: edit..."),gn,p_id,0,Variant::TRANSFORM));
+ }
+ hbc->add_spacer();
+ hbc->add_child( memnew(Label("out")));
+ gn->add_child(hbc);
+ if (graph->is_slot_connected(type, p_id, 1)) {
+ gn->add_child(make_label("b",Variant::TRANSFORM));
+ } else {
+ gn->add_child(make_editor(String("b: edit..."),gn,p_id,1,Variant::TRANSFORM));
+ }
+
+ gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM],true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM]);
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM],false,0,Color());
+
+
+ } break; // mat4 x mat4
+ case ShaderGraph::NODE_XFORM_VEC_MULT: {
+
+ gn->set_title("XFVecMult");
+
+ CheckBox *button = memnew (CheckBox("RotOnly"));
+ button->set_pressed(graph->xform_vec_mult_node_get_no_translation(type,p_id));
+ button->connect("toggled",this,"_xform_inv_rev_changed",varray(p_id));
+
+ gn->add_child(button);
+
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("xf",Variant::TRANSFORM));
+ } else {
+ hbc->add_child(make_editor(String("xf: edit..."),gn,p_id,0,Variant::TRANSFORM));
+ }
+ hbc->add_spacer();
+ Label *l = memnew(Label("out"));
+ l->set_align(Label::ALIGN_RIGHT);
+ hbc->add_child( l);
+ gn->add_child(hbc);
+ if (graph->is_slot_connected(type, p_id, 1)) {
+ gn->add_child(make_label("a",Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,1);
+ gn->add_child(make_editor(String("a: ")+v,gn,p_id,1,Variant::VECTOR3));
+ }
+
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],false,0,Color());
+
+ } break;
+ case ShaderGraph::NODE_XFORM_VEC_INV_MULT: {
+
+ gn->set_title("XFVecInvMult");
+
+
+ CheckBox *button = memnew( CheckBox("RotOnly"));
+ button->set_pressed(graph->xform_vec_mult_node_get_no_translation(type,p_id));
+ button->connect("toggled",this,"_xform_inv_rev_changed",varray(p_id));
+
+ gn->add_child(button);
+
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ gn->add_child(make_label("a",Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,0);
+ gn->add_child(make_editor(String("a: ")+v,gn,p_id,0,Variant::VECTOR3));
+ }
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 1)) {
+ hbc->add_child(make_label("xf", Variant::TRANSFORM));
+ } else {
+ hbc->add_child(make_editor(String("xf: edit..."),gn,p_id,1,Variant::TRANSFORM));
+ }
+ hbc->add_spacer();
+ Label *l = memnew(Label("out"));
+ l->set_align(Label::ALIGN_RIGHT);
+ hbc->add_child( l);
+ gn->add_child(hbc);
+
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],false,0,Color());
+ gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+
+
+ } break; // mat4 x vec3 inverse mult (with no-translation option)
+ case ShaderGraph::NODE_SCALAR_FUNC: {
+
+ gn->set_title("ScalarFunc");
+ static const char* func_name[ShaderGraph::SCALAR_MAX_FUNC]={
+ ("Sin"),
+ ("Cos"),
+ ("Tan"),
+ ("ASin"),
+ ("ACos"),
+ ("ATan"),
+ ("SinH"),
+ ("CosH"),
+ ("TanH"),
+ ("Log"),
+ ("Exp"),
+ ("Sqrt"),
+ ("Abs"),
+ ("Sign"),
+ ("Floor"),
+ ("Round"),
+ ("Ceil"),
+ ("Frac"),
+ ("Satr"),
+ ("Neg")
+ };
+
+ OptionButton *ob = memnew( OptionButton );
+ for(int i=0;i<ShaderGraph::SCALAR_MAX_FUNC;i++) {
+
+ ob->add_item(func_name[i],i);
+ }
+
+ ob->select(graph->scalar_func_node_get_function(type,p_id));
+ ob->connect("item_selected",this,"_scalar_func_changed",varray(p_id));
+ gn->add_child(ob);
+
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("in", Variant::REAL));
+ } else {
+ float v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("in: ")+Variant(v),gn,p_id,0,Variant::REAL,hint_spin));
+ }
+ hbc->add_spacer();
+ hbc->add_child( memnew(Label("out")));
+ gn->add_child(hbc);
+
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+
+
+ } break; // scalar function (sin: { } break; cos: { } break; etc)
+ case ShaderGraph::NODE_VEC_FUNC: {
+
+
+
+ gn->set_title("VecFunc");
+ static const char* func_name[ShaderGraph::VEC_MAX_FUNC]={
+ ("Normalize"),
+ ("Saturate"),
+ ("Negate"),
+ ("Reciprocal"),
+ ("RGB to HSV"),
+ ("HSV to RGB"),
+ };
+
+ OptionButton *ob = memnew( OptionButton );
+ for(int i=0;i<ShaderGraph::VEC_MAX_FUNC;i++) {
+
+ ob->add_item(func_name[i],i);
+ }
+
+ ob->select(graph->vec_func_node_get_function(type,p_id));
+ ob->connect("item_selected",this,"_vec_func_changed",varray(p_id));
+ gn->add_child(ob);
+
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("in", Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("in: ")+v,gn,p_id,0,Variant::VECTOR3));
+ }
+ hbc->add_spacer();
+ hbc->add_child( memnew(Label("out")));
+ gn->add_child(hbc);
+
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+
+ } break; // vector function (normalize: { } break; negate: { } break; reciprocal: { } break; rgb2hsv: { } break; hsv2rgb: { } break; etc: { } break; etc)
+ case ShaderGraph::NODE_VEC_LEN: {
+ gn->set_title("VecLength");
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("in", Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("in: ")+v,gn,p_id,0,Variant::VECTOR3));
+ }
+ hbc->add_spacer();
+ hbc->add_child( memnew(Label("len")));
+ gn->add_child(hbc);
+
+ gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+
+ } break; // vec3 length
+ case ShaderGraph::NODE_DOT_PROD: {
+
+ gn->set_title("DotProduct");
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("a", Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("a: ")+v,gn,p_id,0,Variant::VECTOR3));
+ }
+ hbc->add_spacer();
+ hbc->add_child( memnew(Label("dp")));
+ gn->add_child(hbc);
+ if (graph->is_slot_connected(type, p_id, 1)) {
+ gn->add_child(make_label("b", Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,1);
+ gn->add_child(make_editor(String("b: ")+v,gn,p_id,1,Variant::VECTOR3));
+ }
+
+ gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],false,0,Color());
+
+ } break; // vec3 . vec3 (dot product -> scalar output)
+ case ShaderGraph::NODE_VEC_TO_SCALAR: {
+
+ gn->set_title("Vec2Scalar");
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("vec", Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("vec: ")+v,gn,p_id,0,Variant::VECTOR3));
+ }
+ hbc->add_spacer();
+ Label *l=memnew(Label("x"));
+ l->set_align(Label::ALIGN_RIGHT);
+ hbc->add_child( l);
+ gn->add_child(hbc);
+ l=memnew(Label("y"));
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child( l );
+ l=memnew(Label("z"));
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child( l);
+
+ gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+ gn->set_slot(1,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+ gn->set_slot(2,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+
+
+
+
+ } break; // 1 vec3 input: { } break; 3 scalar outputs
+ case ShaderGraph::NODE_SCALAR_TO_VEC: {
+
+ gn->set_title("Scalar2Vec");
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("x", Variant::REAL));
+ } else {
+ float v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("x: ")+Variant(v),gn,p_id,0,Variant::REAL));
+ }
+ hbc->add_spacer();
+ hbc->add_child( memnew(Label("vec")));
+ gn->add_child(hbc);
+ if (graph->is_slot_connected(type, p_id, 1)) {
+ gn->add_child(make_label("y", Variant::REAL));
+ } else {
+ float v = graph->default_get_value(type,p_id,1);
+ gn->add_child(make_editor(String("y: ")+Variant(v),gn,p_id,1,Variant::REAL));
+ }
+ if (graph->is_slot_connected(type, p_id, 2)) {
+ gn->add_child(make_label("in", Variant::REAL));
+ } else {
+ float v = graph->default_get_value(type,p_id,2);
+ gn->add_child(make_editor(String("in: ")+Variant(v),gn,p_id,2,Variant::REAL));
+ }
+
+ gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],false,0,Color());
+ gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],false,0,Color());
+
+ } break; // 3 scalar input: { } break; 1 vec3 output
+ case ShaderGraph::NODE_VEC_TO_XFORM: {
+
+ gn->set_title("Vec2XForm");
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("x", Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("x: ")+v,gn,p_id,0,Variant::VECTOR3));
+ }
+ hbc->add_spacer();
+ hbc->add_child( memnew(Label("xf")));
+ gn->add_child(hbc);
+ if (graph->is_slot_connected(type, p_id, 1)) {
+ gn->add_child(make_label("y", Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,1);
+ gn->add_child(make_editor(String("y: ")+v,gn,p_id,1,Variant::VECTOR3));
+ }
+ if (graph->is_slot_connected(type, p_id, 2)) {
+ gn->add_child(make_label("z", Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,2);
+ gn->add_child(make_editor(String("z: ")+v,gn,p_id,2,Variant::VECTOR3));
+ }
+ if (graph->is_slot_connected(type, p_id, 3)) {
+ gn->add_child(make_label("ofs", Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,3);
+ gn->add_child(make_editor(String("ofs: ")+v,gn,p_id,3,Variant::VECTOR3));
+ }
+
+ gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM]);
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],false,0,Color());
+ gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],false,0,Color());
+ gn->set_slot(3,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],false,0,Color());
+
+ } break; // 3 vec input: { } break; 1 xform output
+ case ShaderGraph::NODE_XFORM_TO_VEC: {
+
+ gn->set_title("XForm2Vec");
+
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("fx", Variant::TRANSFORM));
+ } else {
+ hbc->add_child(make_editor(String("fx: edit..."),gn,p_id,0,Variant::TRANSFORM));
+ }
+ hbc->add_spacer();
+ Label *l=memnew(Label("x"));
+ l->set_align(Label::ALIGN_RIGHT);
+ hbc->add_child( l);
+ gn->add_child(hbc);
+ l=memnew(Label("y"));
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child( l );
+ l=memnew(Label("z"));
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child( l);
+ l=memnew(Label("ofs"));
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child( l);
+
+ gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(1,false,0,Color(),true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(2,false,0,Color(),true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(3,false,0,Color(),true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+
+ } break; // 3 vec input: { } break; 1 xform output
+ case ShaderGraph::NODE_SCALAR_INTERP: {
+
+ gn->set_title("ScalarInterp");
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("a", Variant::REAL));
+ } else {
+ float v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("a: ")+Variant(v),gn,p_id,0,Variant::REAL,hint_spin));
+ }
+ hbc->add_spacer();
+ hbc->add_child( memnew(Label("interp")));
+ gn->add_child(hbc);
+ if (graph->is_slot_connected(type, p_id, 1)) {
+ gn->add_child(make_label("b", Variant::REAL));
+ } else {
+ float v = graph->default_get_value(type,p_id,1);
+ gn->add_child(make_editor(String("b: ")+Variant(v),gn,p_id,1,Variant::REAL,hint_spin));
+ }
+ if (graph->is_slot_connected(type, p_id, 2)) {
+ gn->add_child(make_label("c", Variant::REAL));
+ } else {
+ float v = graph->default_get_value(type,p_id,2);
+ gn->add_child(make_editor(String("c: ")+Variant(v),gn,p_id,2,Variant::REAL,hint_slider));
+ }
+
+ gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],false,0,Color());
+ gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],false,0,Color());
+
+
+ } break; // scalar interpolation (with optional curve)
+ case ShaderGraph::NODE_VEC_INTERP: {
+
+ gn->set_title("VecInterp");
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("a", Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("a: ")+v,gn,p_id,0,Variant::VECTOR3));
+ }
+ hbc->add_spacer();
+ hbc->add_child( memnew(Label("interp")));
+ gn->add_child(hbc);
+ if (graph->is_slot_connected(type, p_id, 1)) {
+ gn->add_child(make_label("b", Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,1);
+ gn->add_child(make_editor(String("b: ")+v,gn,p_id,1,Variant::VECTOR3));
+ }
+ if (graph->is_slot_connected(type, p_id, 2)) {
+ gn->add_child(make_label("c", Variant::REAL));
+ } else {
+ float v = graph->default_get_value(type,p_id,2);
+ gn->add_child(make_editor(String("c: ")+Variant(v),gn,p_id,2,Variant::REAL,hint_slider));
+ }
+
+ gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],false,0,Color());
+ gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],false,0,Color());
+
+ } break; // vec3 interpolation (with optional curve)
+ case ShaderGraph::NODE_COLOR_RAMP: {
+
+ gn->set_title("ColorRamp");
+ GraphColorRampEdit * ramp = memnew( GraphColorRampEdit );
+
+ DVector<real_t> offsets = graph->color_ramp_node_get_offsets(type,p_id);
+ DVector<Color> colors = graph->color_ramp_node_get_colors(type,p_id);
+
+ int oc = offsets.size();
+
+ if (oc) {
+ DVector<real_t>::Read rofs = offsets.read();
+ DVector<Color>::Read rcol = colors.read();
+
+ Vector<float> ofsv;
+ Vector<Color> colorv;
+ for(int i=0;i<oc;i++) {
+ ofsv.push_back(rofs[i]);
+ colorv.push_back(rcol[i]);
+ }
+
+ ramp->set_ramp(ofsv,colorv);
+
+ }
+
+ ramp->connect("ramp_changed",this,"_color_ramp_changed",varray(p_id,ramp));
+ ramp->set_custom_minimum_size(Size2(128,1));
+ gn->add_child(ramp);
+
+
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("c", Variant::REAL));
+ } else {
+ float v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("c: ")+Variant(v),gn,p_id,0,Variant::REAL,hint_slider));
+ }
+ hbc->add_spacer();
+ Label *l=memnew(Label("rgb"));
+ l->set_align(Label::ALIGN_RIGHT);
+ hbc->add_child( l);
+ gn->add_child(hbc);
+ l=memnew(Label("alpha"));
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child( l);
+
+
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(2,false,ShaderGraph::SLOT_MAX,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+
+
+ } break; // scalar interpolation (with optional curve)
+ case ShaderGraph::NODE_CURVE_MAP: {
+
+ gn->set_title("CurveMap");
+ GraphCurveMapEdit * map = memnew( GraphCurveMapEdit );
+
+ DVector<Vector2> points = graph->curve_map_node_get_points(type,p_id);
+
+ int oc = points.size();
+
+ if (oc) {
+ DVector<Vector2>::Read rofs = points.read();
+
+
+ Vector<Vector2> ofsv;
+ for(int i=0;i<oc;i++) {
+ ofsv.push_back(rofs[i]);
+ }
+
+ map->set_points(ofsv);
+
+ }
+ map->connect("curve_changed",this,"_curve_changed",varray(p_id,map));
+
+ //map->connect("map_changed",this,"_curve_map_changed",varray(p_id,map));
+ map->set_custom_minimum_size(Size2(128,64));
+ gn->add_child(map);
+
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("c", Variant::REAL));
+ } else {
+ float v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("c: ")+Variant(v),gn,p_id,0,Variant::REAL,hint_slider));
+ }
+ hbc->add_spacer();
+ Label *l=memnew(Label("cmap"));
+ l->set_align(Label::ALIGN_RIGHT);
+ hbc->add_child( l);
+ gn->add_child(hbc);
+
+
+ gn->set_slot(1,true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR],true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+
+
+ } break; // scalar interpolation (with optional curve)
+
+ case ShaderGraph::NODE_SCALAR_INPUT: {
+
+ gn->set_title("ScalarUniform");
+ LineEdit *le = memnew( LineEdit );
+ gn->add_child(le);
+ le->set_text(graph->input_node_get_name(type,p_id));
+ le->connect("text_entered",this,"_input_name_changed",varray(p_id,le));
+ SpinBox *sb = memnew( SpinBox );
+ sb->set_min(-100000);
+ sb->set_max(100000);
+ sb->set_step(0.001);
+ sb->set_val(graph->scalar_input_node_get_value(type,p_id));
+ sb->connect("value_changed",this,"_scalar_input_changed",varray(p_id));
+ gn->add_child(sb);
+ gn->set_slot(1,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+
+ } break; // scalar uniform (assignable in material)
+ case ShaderGraph::NODE_VEC_INPUT: {
+
+ gn->set_title("VectorUniform");
+ LineEdit *le = memnew( LineEdit );
+ gn->add_child(le);
+ le->set_text(graph->input_node_get_name(type,p_id));
+ le->connect("text_entered",this,"_input_name_changed",varray(p_id,le));
+ Array v3p(true);
+ for(int i=0;i<3;i++) {
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ Label *l = memnew( Label );
+ l->set_text(String::chr('X'+i));
+ hbc->add_child(l);
+ SpinBox *sb = memnew( SpinBox );
+ sb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ sb->set_min(-100000);
+ sb->set_max(100000);
+ sb->set_step(0.001);
+ sb->set_val(graph->vec_input_node_get_value(type,p_id)[i]);
+ sb->connect("value_changed",this,"_vec_input_changed",varray(p_id,v3p));
+ v3p.push_back(sb);
+ hbc->add_child(sb);
+ gn->add_child(hbc);
+ }
+ gn->set_slot(1,false,0,Color(),true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+
+ } break; // vec3 uniform (assignable in material)
+ case ShaderGraph::NODE_RGB_INPUT: {
+
+ gn->set_title("ColorUniform");
+ LineEdit *le = memnew( LineEdit );
+ gn->add_child(le);
+ le->set_text(graph->input_node_get_name(type,p_id));
+ le->connect("text_entered",this,"_input_name_changed",varray(p_id,le));
+ ColorPickerButton *cpb = memnew( ColorPickerButton );
+ cpb->set_color(graph->rgb_input_node_get_value(type,p_id));
+ cpb->connect("color_changed",this,"_rgb_input_changed",varray(p_id));
+ gn->add_child(cpb);
+ Label *l = memnew( Label );
+ l->set_text("RGB");
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child(l);
+ l = memnew( Label );
+ l->set_text("Alpha");
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child(l);
+
+ gn->set_slot(2,false,0,Color(),true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(3,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+
+
+ } break; // color uniform (assignable in material)
+ case ShaderGraph::NODE_XFORM_INPUT: {
+ gn->set_title("XFUniform");
+ LineEdit *le = memnew( LineEdit );
+ gn->add_child(le);
+ le->set_text(graph->input_node_get_name(type,p_id));
+ le->connect("text_entered",this,"_input_name_changed",varray(p_id,le));
+ ToolButton *edit = memnew( ToolButton );
+ edit->set_text("edit..");
+ edit->connect("pressed",this,"_xform_input_changed",varray(p_id,edit));
+ gn->add_child(edit);
+ gn->set_slot(1,false,0,Color(),true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM]);
+
+ } break; // mat4 uniform (assignable in material)
+ case ShaderGraph::NODE_TEXTURE_INPUT: {
+
+ gn->set_title("TexUniform");
+ LineEdit *le = memnew( LineEdit );
+ gn->add_child(le);
+ le->set_text(graph->input_node_get_name(type,p_id));
+ le->connect("text_entered",this,"_input_name_changed",varray(p_id,le));
+ TextureFrame *tex = memnew( TextureFrame );
+ tex->set_expand(true);
+ tex->set_custom_minimum_size(Size2(80,80));
+ tex->set_drag_forwarding(this);
+ gn->add_child(tex);
+ tex->set_ignore_mouse(false);
+ tex->set_texture(graph->texture_input_node_get_value(type,p_id));
+ ToolButton *edit = memnew( ToolButton );
+ edit->set_text("edit..");
+ edit->connect("pressed",this,"_tex_edited",varray(p_id,edit));
+ gn->add_child(edit);
+
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("UV", Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("UV: ")+v,gn,p_id,0,Variant::VECTOR3));
+ }
+ hbc->add_spacer();
+ Label *l=memnew(Label("RGB"));
+ l->set_align(Label::ALIGN_RIGHT);
+ hbc->add_child(l);
+ gn->add_child(hbc);
+ l = memnew( Label );
+ l->set_text("Alpha");
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child(l);
+
+ gn->set_slot(3,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(4,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+
+ } break; // texture input (assignable in material)
+ case ShaderGraph::NODE_CUBEMAP_INPUT: {
+
+ gn->set_title("TexUniform");
+ LineEdit *le = memnew( LineEdit );
+ gn->add_child(le);
+ le->set_text(graph->input_node_get_name(type,p_id));
+ le->connect("text_entered",this,"_input_name_changed",varray(p_id,le));
+
+ ToolButton *edit = memnew( ToolButton );
+ edit->set_text("edit..");
+ edit->connect("pressed",this,"_cube_edited",varray(p_id,edit));
+ gn->add_child(edit);
+
+
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("UV", Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("UV: ")+v,gn,p_id,0,Variant::VECTOR3));
+ }
+ hbc->add_spacer();
+ Label *l=memnew(Label("RGB"));
+ l->set_align(Label::ALIGN_RIGHT);
+ hbc->add_child(l);
+ gn->add_child(hbc);
+ l = memnew( Label );
+ l->set_text("Alpha");
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child(l);
+
+ gn->set_slot(2,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(3,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+
+ } break; // cubemap input (assignable in material)
+ case ShaderGraph::NODE_DEFAULT_TEXTURE: {
+
+ gn->set_title("CanvasItemTex");
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ hbc->add_constant_override("separation",0);
+ if (graph->is_slot_connected(type, p_id, 0)) {
+ hbc->add_child(make_label("UV", Variant::VECTOR3));
+ } else {
+ Vector3 v = graph->default_get_value(type,p_id,0);
+ hbc->add_child(make_editor(String("UV: ")+v,gn,p_id,0,Variant::VECTOR3));
+ }
+ hbc->add_spacer();
+ Label *l=memnew(Label("RGB"));
+ l->set_align(Label::ALIGN_RIGHT);
+ hbc->add_child(l);
+ gn->add_child(hbc);
+ l = memnew( Label );
+ l->set_text("Alpha");
+ l->set_align(Label::ALIGN_RIGHT);
+ gn->add_child(l);
+
+ gn->set_slot(0,true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC],true,ShaderGraph::SLOT_TYPE_VEC,typecol[ShaderGraph::SLOT_TYPE_VEC]);
+ gn->set_slot(1,false,0,Color(),true,ShaderGraph::SLOT_TYPE_SCALAR,typecol[ShaderGraph::SLOT_TYPE_SCALAR]);
+
+
+ } break; // screen texture sampler (takes UV) (only usable in fragment case Shader)
+
+ case ShaderGraph::NODE_OUTPUT: {
+ gn->set_title("Output");
+ gn->set_show_close_button(false);
+
+ List<ShaderGraph::SlotInfo> si;
+ ShaderGraph::get_input_output_node_slot_info(graph->get_mode(),type,&si);
+
+ Array colors;
+ colors.push_back("Color");
+ colors.push_back("LightColor");
+ colors.push_back("Light");
+ colors.push_back("ShadowColor");
+ colors.push_back("Diffuse");
+ colors.push_back("Specular");
+ colors.push_back("Emmision");
+ Array reals;
+ reals.push_back("Alpha");
+ reals.push_back("DiffuseAlpha");
+ reals.push_back("NormalMapDepth");
+ reals.push_back("SpecExp");
+ reals.push_back("Glow");
+ reals.push_back("ShadeParam");
+ reals.push_back("SpecularExp");
+ reals.push_back("LightAlpha");
+ reals.push_back("ShadowAlpha");
+ reals.push_back("PointSize");
+ reals.push_back("Discard");
+
+ int idx=0;
+ for (List<ShaderGraph::SlotInfo>::Element *E=si.front();E;E=E->next()) {
+ ShaderGraph::SlotInfo& s=E->get();
+ if (s.dir==ShaderGraph::SLOT_OUT) {
+ Variant::Type v;
+ if (colors.find(s.name)>=0)
+ v=Variant::COLOR;
+ else if (reals.find(s.name)>=0)
+ v=Variant::REAL;
+ else
+ v=Variant::VECTOR3;
+ gn->add_child(make_label(s.name, v));
+ gn->set_slot(idx,true,s.type,typecol[s.type],false,0,Color());
+ idx++;
+ }
+ }
+
+ } break; // output (case Shader type dependent)
+ case ShaderGraph::NODE_COMMENT: {
+ gn->set_title("Comment");
+ TextEdit *te = memnew(TextEdit);
+ te->set_custom_minimum_size(Size2(100,100));
+ gn->add_child(te);
+ te->set_text(graph->comment_node_get_text(type,p_id));
+ te->connect("text_changed",this,"_comment_edited",varray(p_id,te));
+
+ } break; // comment
+
+
+
+ }
+
+ gn->connect("dragged",this,"_node_moved",varray(p_id));
+ gn->connect("close_request",this,"_node_removed",varray(p_id),CONNECT_DEFERRED);
+ graph_edit->add_child(gn);
+ node_map[p_id]=gn;
+ gn->set_offset(graph->node_get_pos(type,p_id));
+
+
+}
+
+void ShaderGraphView::_update_graph() {
+
+
+ if (block_update)
+ return;
+
+ for (Map<int,GraphNode*>::Element *E=node_map.front();E;E=E->next()) {
+
+ memdelete(E->get());
+ }
+
+ node_map.clear();
+
+ if (!graph.is_valid())
+ return;
+
+
+ List<int> nl;
+ graph->get_node_list(type,&nl);
+
+ for(List<int>::Element *E=nl.front();E;E=E->next()) {
+
+ _create_node(E->get());
+ }
+ graph_edit->clear_connections();
+
+ List<ShaderGraph::Connection> connections;
+ graph->get_node_connections(type,&connections);
+ for(List<ShaderGraph::Connection>::Element *E=connections.front();E;E=E->next()) {
+
+ ERR_CONTINUE(!node_map.has(E->get().src_id) || !node_map.has(E->get().dst_id));
+ graph_edit->connect_node(node_map[E->get().src_id]->get_name(),E->get().src_slot,node_map[E->get().dst_id]->get_name(),E->get().dst_slot);
+ }
+
+}
+
+void ShaderGraphView::_sg_updated() {
+
+ if (!graph.is_valid())
+ return;
+ switch(graph->get_graph_error(type)) {
+ case ShaderGraph::GRAPH_OK: status->set_text(""); break;
+ case ShaderGraph::GRAPH_ERROR_CYCLIC: status->set_text(TTR("Error: Cyclic Connection Link")); break;
+ case ShaderGraph::GRAPH_ERROR_MISSING_CONNECTIONS: status->set_text(TTR("Error: Missing Input Connections")); break;
+ }
+}
+
+Variant ShaderGraphView::get_drag_data_fw(const Point2 &p_point, Control *p_from)
+{
+ TextureFrame* frame = p_from->cast_to<TextureFrame>();
+ if (!frame)
+ return Variant();
+
+ if (!frame->get_texture().is_valid())
+ return Variant();
+
+ RES res = frame->get_texture();
+ return EditorNode::get_singleton()->drag_resource(res,p_from);
+
+ return Variant();
+}
+
+bool ShaderGraphView::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const
+{
+ if (p_data.get_type() != Variant::DICTIONARY)
+ return false;
+
+ Dictionary d = p_data;
+
+ if (d.has("type")){
+ if (d["type"] == "resource" && d.has("resource")) {
+ Variant val = d["resource"];
+
+ if (val.get_type()==Variant::OBJECT) {
+ RES res = val;
+ if (res.is_valid() && res->cast_to<Texture>())
+ return true;
+ }
+ }
+ else if (d["type"] == "files" && d.has("files")) {
+ Vector<String> files = d["files"];
+ if (files.size() != 1)
+ return false;
+ return (ResourceLoader::get_resource_type(files[0]) == "ImageTexture");
+ }
+ }
+
+ return false;
+}
+
+void ShaderGraphView::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from)
+{
+ if (!can_drop_data_fw(p_point, p_data, p_from))
+ return;
+
+ TextureFrame *frame = p_from->cast_to<TextureFrame>();
+ if (!frame)
+ return;
+
+ Dictionary d = p_data;
+ Ref<Texture> tex;
+
+ if (d.has("type")) {
+ if (d["type"] == "resource" && d.has("resource")){
+ Variant val = d["resource"];
+
+ if (val.get_type()==Variant::OBJECT) {
+ RES res = val;
+ if (res.is_valid())
+ tex = Ref<Texture>(res->cast_to<Texture>());
+ }
+ }
+ else if (d["type"] == "files" && d.has("files")) {
+ Vector<String> files = d["files"];
+ RES res = ResourceLoader::load(files[0]);
+ if (res.is_valid())
+ tex = Ref<Texture>(res->cast_to<Texture>());
+ }
+ }
+
+ if (!tex.is_valid()) return;
+
+ GraphNode *gn = frame->get_parent()->cast_to<GraphNode>();
+ if (!gn) return;
+
+ int id = -1;
+ for(Map<int,GraphNode*>::Element *E = node_map.front();E;E=E->next())
+ if (E->get() == gn) {
+ id = E->key();
+ break;
+ }
+ print_line(String::num(double(id)));
+ if (id < 0) return;
+
+ if (graph->node_get_type(type,id)==ShaderGraph::NODE_TEXTURE_INPUT) {
+
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change Texture Uniform"));
+ ur->add_do_method(graph.ptr(),"texture_input_node_set_value",type,id,tex);
+ ur->add_undo_method(graph.ptr(),"texture_input_node_set_value",type,id,graph->texture_input_node_get_value(type,id));
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ ur->commit_action();
+ }
+}
+
+void ShaderGraphView::set_graph(Ref<ShaderGraph> p_graph){
+
+
+ if (graph.is_valid()) {
+ graph->disconnect("updated",this,"_sg_updated");
+ }
+ graph=p_graph;
+ if (graph.is_valid()) {
+ graph->connect("updated",this,"_sg_updated");
+ }
+ _update_graph();
+ _sg_updated();
+
+}
+
+void ShaderGraphView::_notification(int p_what) {
+
+ if (p_what==NOTIFICATION_ENTER_TREE) {
+
+ ped_popup->connect("variant_changed",this,"_variant_edited");
+ }
+}
+
+void ShaderGraphView::add_node(int p_type, const Vector2 &location) {
+
+ if (p_type==ShaderGraph::NODE_INPUT && graph->node_count(type, p_type)>0)
+ return;
+
+ List<int> existing;
+ graph->get_node_list(type,&existing);
+ existing.sort();
+ int newid=1;
+ for(List<int>::Element *E=existing.front();E;E=E->next()) {
+ if (!E->next() || (E->get()+1!=E->next()->get())){
+ newid=E->get()+1;
+ break;
+ }
+ }
+
+ Vector2 init_ofs = location;
+ while(true) {
+ bool valid=true;
+ for(List<int>::Element *E=existing.front();E;E=E->next()) {
+ Vector2 pos = graph->node_get_pos(type,E->get());
+ if (init_ofs==pos) {
+ init_ofs+=Vector2(20,20);
+ valid=false;
+ break;
+
+ }
+ }
+
+ if (valid)
+ break;
+ }
+ UndoRedo *ur=EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Add Shader Graph Node"));
+ ur->add_do_method(graph.ptr(),"node_add",type,p_type,newid);
+ ur->add_do_method(graph.ptr(),"node_set_pos",type,newid,init_ofs);
+ ur->add_undo_method(graph.ptr(),"node_remove",type,newid);
+ ur->add_do_method(this,"_update_graph");
+ ur->add_undo_method(this,"_update_graph");
+ ur->commit_action();
+
+}
+
+void ShaderGraphView::_bind_methods() {
+
+ ObjectTypeDB::bind_method("_update_graph",&ShaderGraphView::_update_graph);
+ ObjectTypeDB::bind_method("_begin_node_move", &ShaderGraphView::_begin_node_move);
+ ObjectTypeDB::bind_method("_node_moved",&ShaderGraphView::_node_moved);
+ ObjectTypeDB::bind_method("_end_node_move", &ShaderGraphView::_end_node_move);
+ ObjectTypeDB::bind_method("_move_node",&ShaderGraphView::_move_node);
+ ObjectTypeDB::bind_method("_node_removed",&ShaderGraphView::_node_removed);
+ ObjectTypeDB::bind_method("_connection_request",&ShaderGraphView::_connection_request);
+ ObjectTypeDB::bind_method("_disconnection_request",&ShaderGraphView::_disconnection_request);
+ ObjectTypeDB::bind_method("_duplicate_nodes_request", &ShaderGraphView::_duplicate_nodes_request);
+ ObjectTypeDB::bind_method("_duplicate_nodes", &ShaderGraphView::_duplicate_nodes);
+ ObjectTypeDB::bind_method("_delete_nodes_request", &ShaderGraphView::_delete_nodes_request);
+
+ ObjectTypeDB::bind_method("_default_changed",&ShaderGraphView::_default_changed);
+ ObjectTypeDB::bind_method("_scalar_const_changed",&ShaderGraphView::_scalar_const_changed);
+ ObjectTypeDB::bind_method("_vec_const_changed",&ShaderGraphView::_vec_const_changed);
+ ObjectTypeDB::bind_method("_rgb_const_changed",&ShaderGraphView::_rgb_const_changed);
+ ObjectTypeDB::bind_method("_xform_const_changed",&ShaderGraphView::_xform_const_changed);
+ ObjectTypeDB::bind_method("_scalar_op_changed",&ShaderGraphView::_scalar_op_changed);
+ ObjectTypeDB::bind_method("_vec_op_changed",&ShaderGraphView::_vec_op_changed);
+ ObjectTypeDB::bind_method("_vec_scalar_op_changed",&ShaderGraphView::_vec_scalar_op_changed);
+ ObjectTypeDB::bind_method("_rgb_op_changed",&ShaderGraphView::_rgb_op_changed);
+ ObjectTypeDB::bind_method("_xform_inv_rev_changed",&ShaderGraphView::_xform_inv_rev_changed);
+ ObjectTypeDB::bind_method("_scalar_func_changed",&ShaderGraphView::_scalar_func_changed);
+ ObjectTypeDB::bind_method("_vec_func_changed",&ShaderGraphView::_vec_func_changed);
+ ObjectTypeDB::bind_method("_scalar_input_changed",&ShaderGraphView::_scalar_input_changed);
+ ObjectTypeDB::bind_method("_vec_input_changed",&ShaderGraphView::_vec_input_changed);
+ ObjectTypeDB::bind_method("_xform_input_changed",&ShaderGraphView::_xform_input_changed);
+ ObjectTypeDB::bind_method("_rgb_input_changed",&ShaderGraphView::_rgb_input_changed);
+ ObjectTypeDB::bind_method("_tex_input_change",&ShaderGraphView::_tex_input_change);
+ ObjectTypeDB::bind_method("_cube_input_change",&ShaderGraphView::_cube_input_change);
+ ObjectTypeDB::bind_method("_input_name_changed",&ShaderGraphView::_input_name_changed);
+ ObjectTypeDB::bind_method("_tex_edited",&ShaderGraphView::_tex_edited);
+ ObjectTypeDB::bind_method("_variant_edited",&ShaderGraphView::_variant_edited);
+ ObjectTypeDB::bind_method("_cube_edited",&ShaderGraphView::_cube_edited);
+ ObjectTypeDB::bind_method("_comment_edited",&ShaderGraphView::_comment_edited);
+ ObjectTypeDB::bind_method("_color_ramp_changed",&ShaderGraphView::_color_ramp_changed);
+ ObjectTypeDB::bind_method("_curve_changed",&ShaderGraphView::_curve_changed);
+
+ ObjectTypeDB::bind_method(_MD("get_drag_data_fw"), &ShaderGraphView::get_drag_data_fw);
+ ObjectTypeDB::bind_method(_MD("can_drop_data_fw"), &ShaderGraphView::can_drop_data_fw);
+ ObjectTypeDB::bind_method(_MD("drop_data_fw"), &ShaderGraphView::drop_data_fw);
+
+ ObjectTypeDB::bind_method("_sg_updated",&ShaderGraphView::_sg_updated);
+}
+
+ShaderGraphView::ShaderGraphView(ShaderGraph::ShaderType p_type) {
+
+ type=p_type;
+ graph_edit = memnew( GraphEdit );
+ block_update=false;
+ ped_popup = memnew( CustomPropertyEditor );
+ graph_edit->add_child(ped_popup);
+ status = memnew( Label );
+ graph_edit->get_top_layer()->add_child(status);
+ graph_edit->connect("_begin_node_move", this, "_begin_node_move");
+ graph_edit->connect("_end_node_move", this, "_end_node_move");
+ status->set_pos(Vector2(5,5));
+ status->add_color_override("font_color_shadow",Color(0,0,0));
+ status->add_color_override("font_color",Color(1,0.4,0.3));
+ status->add_constant_override("shadow_as_outline",1);
+ status->add_constant_override("shadow_offset_x",2);
+ status->add_constant_override("shadow_offset_y",2);
+ status->set_text("");
+}
+
+
+//////////////edit//////////////
+void ShaderGraphEditor::edit(Ref<ShaderGraph> p_shader) {
+
+ for(int i=0;i<ShaderGraph::SHADER_TYPE_MAX;i++) {
+ graph_edits[i]->set_graph(p_shader);
+ }
+}
+
+void ShaderGraphEditor::_add_node(int p_type) {
+
+ ShaderGraph::ShaderType shader_type=ShaderGraph::ShaderType(tabs->get_current_tab());
+ graph_edits[shader_type]->add_node(p_type, next_location);
+}
+
+void ShaderGraphEditor::_popup_requested(const Vector2 &p_position)
+{
+ Vector2 scroll_ofs=graph_edits[tabs->get_current_tab()]->get_graph_edit()->get_scroll_ofs();
+ next_location = get_local_mouse_pos() + scroll_ofs;
+ popup->set_global_pos(p_position);
+ popup->set_size( Size2( 200, 0) );
+ popup->popup();
+ popup->call_deferred("grab_click_focus");
+ popup->set_invalidate_click_until_motion();
+}
+
+void ShaderGraphEditor::_notification(int p_what) {
+ if (p_what==NOTIFICATION_ENTER_TREE) {
+
+ for(int i=0;i<ShaderGraph::NODE_TYPE_MAX;i++) {
+
+ if (i==ShaderGraph::NODE_OUTPUT)
+ continue;
+ if (!_2d && i==ShaderGraph::NODE_DEFAULT_TEXTURE)
+ continue;
+
+ String nn = node_names[i];
+ String ic = nn.get_slice(":",0);
+ String v = nn.get_slice(":",1);
+ bool addsep=false;
+ if (nn.ends_with(":")) {
+ addsep=true;
+ }
+ popup->add_icon_item(get_icon(ic,"EditorIcons"),v,i);
+ if (addsep)
+ popup->add_separator();
+ }
+ popup->connect("item_pressed",this,"_add_node");
+
+
+ }
+}
+
+void ShaderGraphEditor::_bind_methods() {
+
+ ObjectTypeDB::bind_method("_add_node",&ShaderGraphEditor::_add_node);
+ ObjectTypeDB::bind_method("_popup_requested",&ShaderGraphEditor::_popup_requested);
+}
+
+
+const char* ShaderGraphEditor::node_names[ShaderGraph::NODE_TYPE_MAX]={
+ ("GraphInput:Input"), // all inputs (shader type dependent)
+ ("GraphScalar:Scalar Constant"), //scalar constant
+ ("GraphVector:Vector Constant"), //vec3 constant
+ ("GraphRgb:RGB Constant"), //rgb constant (shows a color picker instead)
+ ("GraphXform:XForm Constant"), // 4x4 matrix constant
+ ("GraphTime:Time:"), // time in seconds
+ ("GraphTexscreen:Screen Sample"), // screen texture sampler (takes uv) (only usable in fragment shader)
+ ("GraphScalarOp:Scalar Operator"), // scalar vs scalar op (mul", add", div", etc)
+ ("GraphVecOp:Vector Operator"), // vec3 vs vec3 op (mul",ad",div",crossprod",etc)
+ ("GraphVecScalarOp:Scalar+Vector Operator"), // vec3 vs scalar op (mul", add", div", etc)
+ ("GraphRgbOp:RGB Operator:"), // vec3 vs vec3 rgb op (with scalar amount)", like brighten", darken", burn", dodge", multiply", etc.
+ ("GraphXformMult:XForm Multiply"), // mat4 x mat4
+ ("GraphXformVecMult:XForm+Vector Multiply"), // mat4 x vec3 mult (with no-translation option)
+ ("GraphXformVecImult:Form+Vector InvMultiply:"), // mat4 x vec3 inverse mult (with no-translation option)
+ ("GraphXformScalarFunc:Scalar Function"), // scalar function (sin", cos", etc)
+ ("GraphXformVecFunc:Vector Function"), // vector function (normalize", negate", reciprocal", rgb2hsv", hsv2rgb", etc", etc)
+ ("GraphVecLength:Vector Length"), // vec3 length
+ ("GraphVecDp:Dot Product:"), // vec3 . vec3 (dot product -> scalar output)
+ ("GraphVecToScalars:Vector -> Scalars"), // 1 vec3 input", 3 scalar outputs
+ ("GraphScalarsToVec:Scalars -> Vector"), // 3 scalar input", 1 vec3 output
+ ("GraphXformToVecs:XForm -> Vectors"), // 3 vec input", 1 xform output
+ ("GraphVecsToXform:Vectors -> XForm:"), // 3 vec input", 1 xform output
+ ("GraphScalarInterp:Scalar Interpolate"), // scalar interpolation (with optional curve)
+ ("GraphVecInterp:Vector Interpolate:"), // vec3 interpolation (with optional curve)
+ ("GraphColorRamp:Color Ramp"), // vec3 interpolation (with optional curve)
+ ("GraphCurveMap:Curve Remap:"), // vec3 interpolation (with optional curve)
+ ("GraphScalarUniform:Scalar Uniform"), // scalar uniform (assignable in material)
+ ("GraphVectorUniform:Vector Uniform"), // vec3 uniform (assignable in material)
+ ("GraphRgbUniform:RGB Uniform"), // color uniform (assignable in material)
+ ("GraphXformUniform:XForm Uniform"), // mat4 uniform (assignable in material)
+ ("GraphTextureUniform:Texture Uniform"), // texture input (assignable in material)
+ ("GraphCubeUniform:CubeMap Uniform:"), // cubemap input (assignable in material)
+ ("GraphDefaultTexture:CanvasItem Texture:"), // cubemap input (assignable in material)
+ ("Output"), // output (shader type dependent)
+ ("GraphComment:Comment"), // comment
+
+
+};
+ShaderGraphEditor::ShaderGraphEditor(bool p_2d) {
+ _2d=p_2d;
+
+ popup = memnew( PopupMenu );
+ add_child(popup);
+
+
+ tabs = memnew(TabContainer);
+ tabs->set_v_size_flags(SIZE_EXPAND_FILL);
+ add_child(tabs);
+ const char* sname[ShaderGraph::SHADER_TYPE_MAX]={
+ "Vertex",
+ "Fragment",
+ "Light"
+ };
+ for(int i=0;i<ShaderGraph::SHADER_TYPE_MAX;i++) {
+
+ graph_edits[i]= memnew( ShaderGraphView(ShaderGraph::ShaderType(i)) );
+ add_child(graph_edits[i]);
+ graph_edits[i]->get_graph_edit()->set_name(sname[i]);
+ tabs->add_child(graph_edits[i]->get_graph_edit());
+ graph_edits[i]->get_graph_edit()->connect("connection_request",graph_edits[i],"_connection_request");
+ graph_edits[i]->get_graph_edit()->connect("disconnection_request",graph_edits[i],"_disconnection_request");
+ graph_edits[i]->get_graph_edit()->connect("duplicate_nodes_request", graph_edits[i], "_duplicate_nodes_request");
+ graph_edits[i]->get_graph_edit()->connect("popup_request",this,"_popup_requested");
+ graph_edits[i]->get_graph_edit()->connect("delete_nodes_request",graph_edits[i],"_delete_nodes_request");
+ graph_edits[i]->get_graph_edit()->set_right_disconnects(true);
+ }
+
+ tabs->set_current_tab(1);
+
+ set_custom_minimum_size(Size2(100,300));
+}
+
+
+void ShaderGraphEditorPlugin::edit(Object *p_object) {
+
+ shader_editor->edit(p_object->cast_to<ShaderGraph>());
+}
+
+bool ShaderGraphEditorPlugin::handles(Object *p_object) const {
+
+ ShaderGraph *shader=p_object->cast_to<ShaderGraph>();
+ if (!shader)
+ return false;
+ if (_2d)
+ return shader->get_mode()==Shader::MODE_CANVAS_ITEM;
+ else
+ return shader->get_mode()==Shader::MODE_MATERIAL;
+}
+
+void ShaderGraphEditorPlugin::make_visible(bool p_visible) {
+
+ if (p_visible) {
+ shader_editor->show();
+ } else {
+
+ shader_editor->hide();
+ }
+
+}
+
+ShaderGraphEditorPlugin::ShaderGraphEditorPlugin(EditorNode *p_node, bool p_2d) {
+
+ _2d=p_2d;
+ editor=p_node;
+ shader_editor = memnew( ShaderGraphEditor(p_2d) );
+ shader_editor->hide();
+ if (p_2d)
+ CanvasItemEditor::get_singleton()->get_bottom_split()->add_child(shader_editor);
+ else
+ SpatialEditor::get_singleton()->get_shader_split()->add_child(shader_editor);
+
+
+ // editor->get_viewport()->add_child(shader_editor);
+ // shader_editor->set_area_as_parent_rect();
+ // shader_editor->hide();
+
+}
+
+
+ShaderGraphEditorPlugin::~ShaderGraphEditorPlugin()
+{
+}
+
+
+
diff --git a/editor/plugins/shader_graph_editor_plugin.h b/editor/plugins/shader_graph_editor_plugin.h
new file mode 100644
index 000000000..115242fb9
--- /dev/null
+++ b/editor/plugins/shader_graph_editor_plugin.h
@@ -0,0 +1,242 @@
+/*************************************************************************/
+/* shader_graph_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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
+
+
+#include "editor/editor_plugin.h"
+#include "editor/editor_node.h"
+#include "scene/resources/shader.h"
+#include "scene/gui/tree.h"
+#include "scene/gui/button.h"
+#include "scene/gui/graph_edit.h"
+#include "scene/gui/popup.h"
+#include "editor/property_editor.h"
+#include "scene/resources/shader_graph.h"
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+
+
+class GraphColorRampEdit : public Control {
+
+ OBJ_TYPE(GraphColorRampEdit,Control);
+
+
+ struct Point {
+
+ float offset;
+ Color color;
+ bool operator<(const Point& p_ponit) const {
+ return offset<p_ponit.offset;
+ }
+ };
+
+ PopupPanel *popup;
+ ColorPicker *picker;
+
+
+ bool grabbing;
+ int grabbed;
+ float grabbed_at;
+ Vector<Point> points;
+
+ void _color_changed(const Color& p_color);
+
+protected:
+ void _input_event(const InputEvent& p_event);
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+ void set_ramp(const Vector<float>& p_offsets,const Vector<Color>& p_colors);
+ Vector<float> get_offsets() const;
+ Vector<Color> get_colors() const;
+ virtual Size2 get_minimum_size() const;
+ GraphColorRampEdit();
+};
+
+
+class GraphCurveMapEdit : public Control {
+
+ OBJ_TYPE(GraphCurveMapEdit,Control);
+
+
+ struct Point {
+
+ float offset;
+ float height;
+ bool operator<(const Point& p_ponit) const {
+ return offset<p_ponit.offset;
+ }
+ };
+
+
+ bool grabbing;
+ int grabbed;
+ Vector<Point> points;
+
+ void _plot_curve(const Vector2& p_a,const Vector2& p_b,const Vector2& p_c,const Vector2& p_d);
+protected:
+ void _input_event(const InputEvent& p_event);
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+ void set_points(const Vector<Vector2>& p_points);
+ Vector<Vector2> get_points() const;
+ virtual Size2 get_minimum_size() const;
+ GraphCurveMapEdit();
+};
+
+class ShaderGraphView : public Control {
+
+ OBJ_TYPE(ShaderGraphView,Control);
+
+
+
+ CustomPropertyEditor *ped_popup;
+ bool block_update;
+
+ Label *status;
+ GraphEdit *graph_edit;
+ Ref<ShaderGraph> graph;
+ int edited_id;
+ int edited_def;
+
+ ShaderGraph::ShaderType type;
+
+ void _update_graph();
+ void _create_node(int p_id);
+
+
+ ToolButton *make_label(String text, Variant::Type v_type = Variant::NIL);
+ ToolButton *make_editor(String text, GraphNode* gn, int p_id, int param, Variant::Type type, String p_hint="");
+
+ void _connection_request(const String& p_from, int p_from_slot,const String& p_to,int p_to_slot);
+ void _disconnection_request(const String& p_from, int p_from_slot,const String& p_to,int p_to_slot);
+
+ void _node_removed(int p_id);
+ void _begin_node_move();
+ void _node_moved(const Vector2& p_from, const Vector2& p_to,int p_id);
+ void _end_node_move();
+ void _move_node(int p_id,const Vector2& p_to);
+ void _duplicate_nodes_request();
+ void _duplicate_nodes(const Array &p_nodes);
+ void _delete_nodes_request();
+
+
+ void _default_changed(int p_id, Node* p_button, int p_param, int v_type, String p_hint);
+
+ void _scalar_const_changed(double p_value,int p_id);
+ void _vec_const_changed(double p_value, int p_id, Array p_arr);
+ void _rgb_const_changed(const Color& p_color, int p_id);
+ void _xform_const_changed(int p_id,Node* p_button);
+ void _scalar_op_changed(int p_op, int p_id);
+ void _vec_op_changed(int p_op, int p_id);
+ void _vec_scalar_op_changed(int p_op, int p_id);
+ void _rgb_op_changed(int p_op, int p_id);
+ void _xform_inv_rev_changed(bool p_enabled, int p_id);
+ void _scalar_func_changed(int p_func, int p_id);
+ void _vec_func_changed(int p_func, int p_id);
+ void _scalar_input_changed(double p_value,int p_id);
+ void _vec_input_changed(double p_value, int p_id, Array p_arr);
+ void _xform_input_changed(int p_id,Node* p_button);
+ void _rgb_input_changed(const Color& p_color, int p_id);
+ void _tex_input_change(int p_id,Node* p_button);
+ void _cube_input_change(int p_id);
+ void _input_name_changed(const String& p_name,int p_id,Node* p_line_edit);
+ void _tex_edited(int p_id,Node* p_button);
+ void _cube_edited(int p_id,Node* p_button);
+ void _variant_edited();
+ void _comment_edited(int p_id,Node* p_button);
+ void _color_ramp_changed(int p_id,Node* p_ramp);
+ void _curve_changed(int p_id,Node* p_curve);
+ void _sg_updated();
+ Map<int,GraphNode*> node_map;
+
+ Variant get_drag_data_fw(const Point2& p_point,Control* p_from);
+ bool can_drop_data_fw(const Point2& p_point,const Variant& p_data,Control* p_from) const;
+ void drop_data_fw(const Point2& p_point,const Variant& p_data,Control* p_from);
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+ void add_node(int p_type, const Vector2 &location);
+ GraphEdit *get_graph_edit() { return graph_edit; }
+ void set_graph(Ref<ShaderGraph> p_graph);
+
+ ShaderGraphView(ShaderGraph::ShaderType p_type=ShaderGraph::SHADER_TYPE_FRAGMENT);
+};
+
+class ShaderGraphEditor : public VBoxContainer {
+
+ OBJ_TYPE(ShaderGraphEditor,VBoxContainer);
+
+ PopupMenu *popup;
+ TabContainer *tabs;
+ ShaderGraphView *graph_edits[ShaderGraph::SHADER_TYPE_MAX];
+ static const char* node_names[ShaderGraph::NODE_TYPE_MAX];
+ Vector2 next_location;
+
+ bool _2d;
+ void _add_node(int p_type);
+ void _popup_requested(const Vector2 &p_position);
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+ void edit(Ref<ShaderGraph> p_shader);
+ ShaderGraphEditor(bool p_2d);
+};
+
+class ShaderGraphEditorPlugin : public EditorPlugin {
+
+ OBJ_TYPE( ShaderGraphEditorPlugin, EditorPlugin );
+
+ bool _2d;
+ ShaderGraphEditor *shader_editor;
+ EditorNode *editor;
+
+public:
+
+ virtual String get_name() const { return "ShaderGraph"; }
+ 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);
+
+ ShaderGraphEditorPlugin(EditorNode *p_node,bool p_2d);
+ ~ShaderGraphEditorPlugin();
+
+};
+#endif
+
diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp
new file mode 100644
index 000000000..f5d29b506
--- /dev/null
+++ b/editor/plugins/spatial_editor_plugin.cpp
@@ -0,0 +1,4187 @@
+/*************************************************************************/
+/* spatial_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "editor/editor_node.h"
+#include "editor/editor_settings.h"
+#include "scene/resources/surface_tool.h"
+#include "editor/spatial_editor_gizmos.h"
+#include "globals.h"
+#include "editor/plugins/animation_player_editor_plugin.h"
+#include "editor/animation_editor.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() {
+ if (orthogonal) {
+ //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();
+ }
+}
+
+String SpatialEditorGizmo::get_handle_name(int p_idx) const {
+
+ if (get_script_instance() && get_script_instance()->has_method("get_handle_name"))
+ return get_script_instance()->call("get_handle_name", p_idx);
+
+ return "";
+}
+
+Variant SpatialEditorGizmo::get_handle_value(int p_idx) const{
+
+ if (get_script_instance() && get_script_instance()->has_method("get_handle_value"))
+ return get_script_instance()->call("get_handle_value", p_idx);
+
+ return Variant();
+}
+
+void SpatialEditorGizmo::set_handle(int p_idx,Camera *p_camera, const Point2& p_point) {
+
+ if (get_script_instance() && get_script_instance()->has_method("set_handle"))
+ get_script_instance()->call("set_handle", p_idx, p_camera, p_point);
+}
+
+void SpatialEditorGizmo::commit_handle(int p_idx,const Variant& p_restore,bool p_cancel){
+
+ if (get_script_instance() && get_script_instance()->has_method("commit_handle"))
+ get_script_instance()->call("commit_handle", p_idx, p_restore, 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(){
+
+ selected=false;
+}
+
+
+
+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);
+ }
+
+ }
+
+}
+
+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_tree()->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;
+
+ 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,NULL,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();
+
+}
+
+void SpatialEditorViewport::_find_items_at_pos(const Point2& p_pos,bool &r_includes_current,Vector<_RayResult> &results,bool p_alt_select) {
+
+ 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_tree()->get_root()->get_world()->get_scenario() );
+ Set<Ref<SpatialEditorGizmo> > found_gizmos;
+
+ r_includes_current=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;
+
+ 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,NULL,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;
+
+ results.sort();
+}
+
+
+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_tree()->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::_update_name() {
+
+ String ortho = orthogonal?TTR("Orthogonal"):TTR("Perspective");
+
+ if (name!="")
+ view_menu->set_text("[ "+name+" "+ortho+" ]");
+ else
+ view_menu->set_text("[ "+ortho+" ]");
+}
+
+
+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;
+}
+
+SpatialEditorViewport::NavigationScheme SpatialEditorViewport::_get_navigation_schema(const String& p_property) {
+ switch(EditorSettings::get_singleton()->get(p_property).operator int()) {
+ case 0: return NAVIGATION_GODOT;
+ case 1: return NAVIGATION_MAYA;
+ case 2: return NAVIGATION_MODO;
+ }
+ return NAVIGATION_GODOT;
+}
+
+SpatialEditorViewport::NavigationZoomStyle SpatialEditorViewport::_get_navigation_zoom_style(const String& p_property) {
+ switch(EditorSettings::get_singleton()->get(p_property).operator int()) {
+ case 0: return NAVIGATION_ZOOM_VERTICAL;
+ case 1: return NAVIGATION_ZOOM_HORIZONTAL;
+ }
+ return NAVIGATION_ZOOM_VERTICAL;
+}
+
+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 ) );
+
+ Transform gt = spatial_editor->get_gizmo_transform();
+ float gs=gizmo_scale;
+
+ 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() {
+
+ if (!surface->has_focus() && (!get_focus_owner() || !get_focus_owner()->is_text_field()))
+ surface->grab_focus();
+}
+
+void SpatialEditorViewport::_list_select(InputEventMouseButton b) {
+
+ _find_items_at_pos(Vector2( b.x, b.y ),clicked_includes_current,selection_results,b.mod.shift);
+
+ Node *scene=editor->get_edited_scene();
+
+ for(int i=0;i<selection_results.size();i++) {
+ Spatial *item=selection_results[i].item;
+ if (item!=scene && item->get_owner()!=scene && !scene->is_editable_instance(item->get_owner())) {
+ //invalid result
+ selection_results.remove(i);
+ i--;
+ }
+
+ }
+
+
+ clicked_wants_append=b.mod.shift;
+
+ if (selection_results.size() == 1) {
+
+ clicked=selection_results[0].item->get_instance_ID();
+ selection_results.clear();
+
+ if (clicked) {
+ _select_clicked(clicked_wants_append,true);
+ clicked=0;
+ }
+
+ } else if (!selection_results.empty()) {
+
+ NodePath root_path = get_tree()->get_edited_scene_root()->get_path();
+ StringName root_name = root_path.get_name(root_path.get_name_count()-1);
+
+ for (int i = 0; i < selection_results.size(); i++) {
+
+ Spatial *spat=selection_results[i].item;
+
+ Ref<Texture> icon;
+ if (spat->has_meta("_editor_icon"))
+ icon=spat->get_meta("_editor_icon");
+ else
+ icon=get_icon( has_icon(spat->get_type(),"EditorIcons")?spat->get_type():String("Object"),"EditorIcons");
+
+ String node_path="/"+root_name+"/"+root_path.rel_path_to(spat->get_path());
+
+ selection_menu->add_item(spat->get_name());
+ selection_menu->set_item_icon(i, icon );
+ selection_menu->set_item_metadata(i, node_path);
+ selection_menu->set_item_tooltip(i,String(spat->get_name())+
+ "\nType: "+spat->get_type()+"\nPath: "+node_path);
+ }
+
+ selection_menu->set_global_pos(Vector2( b.global_x, b.global_y ));
+ selection_menu->popup();
+ selection_menu->call_deferred("grab_click_focus");
+ selection_menu->set_invalidate_click_until_motion();
+
+
+
+ }
+}
+void SpatialEditorViewport::_sinput(const InputEvent &p_event) {
+
+ if (previewing)
+ return; //do NONE
+
+
+ {
+
+ EditorNode *en = editor;
+ EditorPluginList *over_plugin_list = en->get_editor_plugins_over();
+
+ if (!over_plugin_list->empty()) {
+ bool discard = over_plugin_list->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;
+ if (cursor.distance<0.001)
+ cursor.distance=0.001;
+
+ } break;
+ case BUTTON_WHEEL_DOWN: {
+
+ if (cursor.distance<0.001)
+ cursor.distance=0.001;
+ cursor.distance*=1.08;
+
+ } break;
+ case BUTTON_RIGHT: {
+
+ NavigationScheme nav_scheme = _get_navigation_schema("3d_editor/navigation_scheme");
+
+ 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_tree()->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 (b.mod.alt) {
+
+ if (nav_scheme == NAVIGATION_MAYA)
+ break;
+
+ _list_select(b);
+ return;
+
+ }
+ }
+
+ 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(TTR("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(TTR("X-Axis Transform."),2);
+ name="";
+ _update_name();
+ } break;
+ case TRANSFORM_X_AXIS: {
+
+ _edit.plane=TRANSFORM_Y_AXIS;
+ set_message(TTR("Y-Axis Transform."),2);
+
+ } break;
+ case TRANSFORM_Y_AXIS: {
+
+ _edit.plane=TRANSFORM_Z_AXIS;
+ set_message(TTR("Z-Axis Transform."),2);
+
+ } break;
+ case TRANSFORM_Z_AXIS: {
+
+ _edit.plane=TRANSFORM_VIEW;
+ set_message(TTR("View Plane Transform."),2);
+
+ } break;
+ }
+
+ }
+ } break;
+ case BUTTON_LEFT: {
+
+ if (b.pressed) {
+
+ NavigationScheme nav_scheme = _get_navigation_schema("3d_editor/navigation_scheme");
+ if ( (nav_scheme==NAVIGATION_MAYA || nav_scheme==NAVIGATION_MODO) && b.mod.alt) {
+ break;
+ }
+
+ if (spatial_editor->get_tool_mode()==SpatialEditor::TOOL_MODE_LIST_SELECT) {
+ _list_select(b);
+ break;
+ }
+
+ _edit.mouse_pos=Point2(b.x,b.y);
+ _edit.snap=false;
+ _edit.mode=TRANSFORM_NONE;
+
+
+ //gizmo has priority over everything
+
+ bool can_select_gizmos=true;
+
+ {
+ int idx = view_menu->get_popup()->get_item_index(VIEW_GIZMOS);
+ can_select_gizmos = view_menu->get_popup()->is_item_checked( idx );
+ }
+
+
+
+ if (can_select_gizmos && spatial_editor->get_selected()) {
+
+ Ref<SpatialEditorGizmo> seg = spatial_editor->get_selected()->get_gizmo();
+ if (seg.is_valid()) {
+ int handle=-1;
+ Vector3 point;
+ Vector3 normal;
+ bool inters = seg->intersect_ray(camera,_edit.mouse_pos,point,normal,&handle,b.mod.shift);
+ if (inters && handle!=-1) {
+
+ _edit.gizmo=seg;
+ _edit.gizmo_handle=handle;
+ //_edit.gizmo_initial_pos=seg->get_handle_pos(gizmo_handle);
+ _edit.gizmo_initial_value=seg->get_handle_value(handle);
+ break;
+
+ }
+ }
+ }
+
+
+
+ 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 (spatial_editor->get_selected()) {
+
+
+ Ref<SpatialEditorGizmo> seg = spatial_editor->get_selected()->get_gizmo();
+ if (seg.is_valid()) {
+
+ int selected_handle=-1;
+
+ int handle=-1;
+ Vector3 point;
+ Vector3 normal;
+ bool inters = seg->intersect_ray(camera,_edit.mouse_pos,point,normal,&handle,false);
+ if (inters && handle!=-1) {
+
+ selected_handle=handle;
+
+ }
+
+ if (selected_handle!=spatial_editor->get_over_gizmo_handle()) {
+ spatial_editor->set_over_gizmo_handle(selected_handle);
+ spatial_editor->get_selected()->update_gizmo();
+ if (selected_handle!=-1)
+ spatial_editor->select_gizmo_hilight_axis(-1);
+ }
+ }
+ }
+
+ if (spatial_editor->get_over_gizmo_handle()==-1 && !(m.button_mask&1) && !_edit.gizmo.is_valid()) {
+
+ _gizmo_select(_edit.mouse_pos,true);
+
+ }
+
+ NavigationScheme nav_scheme = _get_navigation_schema("3d_editor/navigation_scheme");
+ NavigationMode nav_mode = NAVIGATION_NONE;
+
+ if (_edit.gizmo.is_valid()) {
+
+ _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 (nav_scheme == NAVIGATION_MAYA && m.mod.alt) {
+ nav_mode = NAVIGATION_ORBIT;
+ } else if (nav_scheme == NAVIGATION_MODO && m.mod.alt && m.mod.shift) {
+ nav_mode = NAVIGATION_PAN;
+ } else if (nav_scheme == NAVIGATION_MODO && m.mod.alt && m.mod.control) {
+ nav_mode = NAVIGATION_ZOOM;
+ } else if (nav_scheme == NAVIGATION_MODO && m.mod.alt) {
+ nav_mode = NAVIGATION_ORBIT;
+ } else {
+ 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 && nav_mode == NAVIGATION_NONE) {
+
+ cursor.region_end=Point2(m.x,m.y);
+ surface->update();
+ return;
+ }
+
+ if (_edit.mode==TRANSFORM_NONE && nav_mode == NAVIGATION_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(vformat(TTR("Scaling to %s%%."),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(vformat(TTR("Rotating %s degrees."),rtos(angle)));
+ angle=Math::deg2rad(angle);
+ } else
+ set_message(vformat(TTR("Rotating %s degrees."),rtos(Math::rad2deg(angle))));
+
+ } else {
+ set_message(vformat(TTR("Rotating %s degrees."),rtos(Math::rad2deg(angle))));
+ }
+
+
+
+
+ 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&2) {
+
+ if (nav_scheme == NAVIGATION_MAYA && m.mod.alt) {
+ nav_mode = NAVIGATION_ZOOM;
+ }
+
+ } else if (m.button_mask&4) {
+
+ if (nav_scheme == NAVIGATION_GODOT) {
+
+ 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"))
+ nav_mode = NAVIGATION_PAN;
+ else if (mod == _get_key_modifier("3d_editor/zoom_modifier"))
+ nav_mode = NAVIGATION_ZOOM;
+ else if (mod == _get_key_modifier("3d_editor/orbit_modifier"))
+ nav_mode = NAVIGATION_ORBIT;
+
+ } else if (nav_scheme == NAVIGATION_MAYA) {
+ if (m.mod.alt)
+ nav_mode = NAVIGATION_PAN;
+ }
+
+ } else if (EditorSettings::get_singleton()->get("3d_editor/emulate_3_button_mouse")) {
+ // Handle trackpad (no external mouse) use case
+ 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){
+ if (mod == _get_key_modifier("3d_editor/pan_modifier"))
+ nav_mode = NAVIGATION_PAN;
+ else if (mod == _get_key_modifier("3d_editor/zoom_modifier"))
+ nav_mode = NAVIGATION_ZOOM;
+ else if (mod == _get_key_modifier("3d_editor/orbit_modifier"))
+ nav_mode = NAVIGATION_ORBIT;
+ }
+ }
+
+ switch(nav_mode) {
+ case NAVIGATION_PAN:{
+
+ real_t pan_speed = 1/150.0;
+ int pan_speed_modifier = 10;
+ if (nav_scheme==NAVIGATION_MAYA && m.mod.shift)
+ pan_speed *= pan_speed_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*pan_speed,m.relative_y*pan_speed,0);
+ translation*=cursor.distance/DISTANCE_DEFAULT;
+ camera_transform.translate(translation);
+ cursor.pos=camera_transform.origin;
+
+ } break;
+
+ case NAVIGATION_ZOOM: {
+ real_t zoom_speed = 1/80.0;
+ int zoom_speed_modifier = 10;
+ if (nav_scheme==NAVIGATION_MAYA && m.mod.shift)
+ zoom_speed *= zoom_speed_modifier;
+
+ NavigationZoomStyle zoom_style = _get_navigation_zoom_style("3d_editor/zoom_style");
+ if (zoom_style == NAVIGATION_ZOOM_HORIZONTAL) {
+ if ( m.relative_x > 0)
+ cursor.distance*=1-m.relative_x*zoom_speed;
+ else if (m.relative_x < 0)
+ cursor.distance/=1+m.relative_x*zoom_speed;
+ }
+ else {
+ if ( m.relative_y > 0)
+ cursor.distance*=1+m.relative_y*zoom_speed;
+ else if (m.relative_y < 0)
+ cursor.distance/=1-m.relative_y*zoom_speed;
+ }
+
+ } break;
+
+ case NAVIGATION_ORBIT: {
+ 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;
+ name="";
+ _update_name();
+ } break;
+
+ default: {}
+ }
+ } break;
+ case InputEvent::KEY: {
+ const InputEventKey &k = p_event.key;
+ if (!k.pressed)
+ break;
+
+ if (ED_IS_SHORTCUT("spatial_editor/snap", p_event)) {
+ if (_edit.mode != TRANSFORM_NONE) {
+ _edit.snap=true;
+ }
+ }
+ if (ED_IS_SHORTCUT("spatial_editor/bottom_view", p_event)) {
+ cursor.y_rot = 0;
+ cursor.x_rot = -Math_PI/2.0;
+ set_message(TTR("Bottom View."),2);
+ name = TTR("Bottom");
+ _update_name();
+ }
+ if (ED_IS_SHORTCUT("spatial_editor/top_view", p_event)) {
+ cursor.y_rot = 0;
+ cursor.x_rot = Math_PI/2.0;
+ set_message(TTR("Top View."),2);
+ name = TTR("Top");
+ _update_name();
+ }
+ if (ED_IS_SHORTCUT("spatial_editor/rear_view", p_event)) {
+ cursor.x_rot = 0;
+ cursor.y_rot = Math_PI;
+ set_message(TTR("Rear View."),2);
+ name = TTR("Rear");
+ _update_name();
+ }
+ if (ED_IS_SHORTCUT("spatial_editor/front_view", p_event)) {
+ cursor.x_rot = 0;
+ cursor.y_rot=0;
+ set_message(TTR("Front View."),2);
+ name=TTR("Front");
+ _update_name();
+ }
+ if (ED_IS_SHORTCUT("spatial_editor/left_view", p_event)) {
+ cursor.x_rot=0;
+ cursor.y_rot = Math_PI/2.0;
+ set_message(TTR("Left View."),2);
+ name = TTR("Left");
+ _update_name();
+ }
+ if (ED_IS_SHORTCUT("spatial_editor/right_view", p_event)) {
+ cursor.x_rot=0;
+ cursor.y_rot = -Math_PI/2.0;
+ set_message(TTR("Right View."),2);
+ name = TTR("Right");
+ _update_name();
+ }
+ if (ED_IS_SHORTCUT("spatial_editor/switch_perspective_orthogonal", p_event)) {
+ _menu_option(orthogonal?VIEW_PERSPECTIVE:VIEW_ORTHOGONAL);
+ _update_name();
+ }
+ if (ED_IS_SHORTCUT("spatial_editor/insert_anim_key", p_event)) {
+ if (!get_selected_count() || _edit.mode!=TRANSFORM_NONE)
+ break;
+
+ if (!AnimationPlayerEditor::singleton->get_key_editor()->has_keying()) {
+ set_message(TTR("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(TTR("Animation Key Inserted."));
+ }
+
+ if (k.scancode == KEY_SPACE) {
+ if (!k.pressed) emit_signal("toggle_maximize_view", this);
+ }
+
+ } 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 (visible)
+ _update_camera();
+
+ call_deferred("update_transform_gizmo_view");
+ }
+
+ if (p_what==NOTIFICATION_RESIZED) {
+
+ call_deferred("update_transform_gizmo_view");
+
+ }
+
+ if (p_what==NOTIFICATION_PROCESS) {
+
+
+ //force editr camera
+ /*
+ current_camera=get_root_node()->get_current_camera();
+ if (current_camera!=camera) {
+
+
+ }
+ */
+
+ _update_camera();
+
+ 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;
+
+
+ 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();
+ }
+
+ }
+
+ if (p_what==NOTIFICATION_ENTER_TREE) {
+
+ 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"));
+ _init_gizmo_instance(index);
+ }
+ if (p_what==NOTIFICATION_EXIT_TREE) {
+
+
+ _finish_gizmo_instances();
+
+ }
+
+ 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));
+
+
+ }
+
+ if (previewing) {
+
+
+ Size2 ss = Size2( Globals::get_singleton()->get("display/width"), Globals::get_singleton()->get("display/height") );
+ float aspect = ss.get_aspect();
+ Size2 s = get_size();
+
+ Rect2 draw_rect;
+
+
+ switch(previewing->get_keep_aspect_mode()) {
+ case Camera::KEEP_WIDTH: {
+
+ draw_rect.size = Size2(s.width,s.width/aspect);
+ draw_rect.pos.x=0;
+ draw_rect.pos.y=(s.height-draw_rect.size.y)*0.5;
+
+ } break;
+ case Camera::KEEP_HEIGHT: {
+
+ draw_rect.size = Size2(s.height*aspect,s.height);
+ draw_rect.pos.y=0;
+ draw_rect.pos.x=(s.width-draw_rect.size.x)*0.5;
+
+ } break;
+ }
+
+ draw_rect = Rect2(Vector2(),s).clip(draw_rect);
+
+ surface->draw_line(draw_rect.pos,draw_rect.pos+Vector2(draw_rect.size.x,0),Color(0.6,0.6,0.1,0.5),2.0);
+ surface->draw_line(draw_rect.pos+Vector2(draw_rect.size.x,0),draw_rect.pos+draw_rect.size,Color(0.6,0.6,0.1,0.5),2.0);
+ surface->draw_line(draw_rect.pos+draw_rect.size,draw_rect.pos+Vector2(0,draw_rect.size.y),Color(0.6,0.6,0.1,0.5),2.0);
+ surface->draw_line(draw_rect.pos,draw_rect.pos+Vector2(0,draw_rect.size.y),Color(0.6,0.6,0.1,0.5),2.0);
+ }
+
+}
+
+
+void SpatialEditorViewport::_menu_option(int p_option) {
+
+ switch(p_option) {
+
+ case VIEW_TOP: {
+
+ cursor.x_rot=Math_PI/2.0;
+ cursor.y_rot=0;
+ name=TTR("Top");
+ _update_name();
+ } break;
+ case VIEW_BOTTOM: {
+
+ cursor.x_rot=-Math_PI/2.0;
+ cursor.y_rot=0;
+ name=TTR("Bottom");
+ _update_name();
+
+ } break;
+ case VIEW_LEFT: {
+
+ cursor.y_rot=Math_PI/2.0;
+ cursor.x_rot=0;
+ name=TTR("Left");
+ _update_name();
+
+ } break;
+ case VIEW_RIGHT: {
+
+ cursor.y_rot=-Math_PI/2.0;
+ cursor.x_rot=0;
+ name=TTR("Right");
+ _update_name();
+
+ } break;
+ case VIEW_FRONT: {
+
+ cursor.y_rot=0;
+ cursor.x_rot=0;
+ name=TTR("Front");
+ _update_name();
+
+ } break;
+ case VIEW_REAR: {
+
+ cursor.y_rot=Math_PI;
+ cursor.x_rot=0;
+ name=TTR("Rear");
+ _update_name();
+
+ } break;
+ case VIEW_CENTER_TO_ORIGIN: {
+
+ cursor.pos = Vector3(0,0,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++;
+ }
+
+ if( count != 0 ) {
+ center/=float(count);
+ }
+
+ cursor.pos=center;
+ } break;
+ case VIEW_ALIGN_SELECTION_WITH_VIEW: {
+
+ if (!get_selected_count())
+ break;
+
+ Transform camera_transform = camera->get_global_transform();
+
+ List<Node*> &selection = editor_selection->get_selected_node_list();
+
+ undo_redo->create_action(TTR("Align with view"));
+ 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 xform = camera_transform;
+ xform.scale_basis(sp->get_scale());
+
+ undo_redo->add_do_method(sp,"set_global_transform",xform);
+ undo_redo->add_undo_method(sp,"set_global_transform",sp->get_global_transform());
+ }
+ undo_redo->commit_action();
+ } 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;
+ call_deferred("update_transform_gizmo_view");
+ _update_name();
+
+ } 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;
+ call_deferred("update_transform_gizmo_view");
+ _update_name();
+
+ } break;
+ case VIEW_AUDIO_LISTENER: {
+
+ int idx = view_menu->get_popup()->get_item_index(VIEW_AUDIO_LISTENER);
+ bool current = view_menu->get_popup()->is_item_checked( idx );
+ current=!current;
+ viewport->set_as_audio_listener(current);
+ view_menu->get_popup()->set_item_checked( idx, current );
+
+ } break;
+ case VIEW_GIZMOS: {
+
+ int idx = view_menu->get_popup()->get_item_index(VIEW_GIZMOS);
+ bool current = view_menu->get_popup()->is_item_checked( idx );
+ current=!current;
+ if (current)
+ camera->set_visible_layers( ((1<<20)-1)|(1<<(GIZMO_BASE_LAYER+index))|(1<<GIZMO_EDIT_LAYER)|(1<<GIZMO_GRID_LAYER) );
+ else
+ camera->set_visible_layers( ((1<<20)-1)|(1<<(GIZMO_BASE_LAYER+index))|(1<<GIZMO_GRID_LAYER) );
+ view_menu->get_popup()->set_item_checked( idx, current );
+
+ } break;
+
+ }
+
+}
+
+
+void SpatialEditorViewport::_preview_exited_scene() {
+
+ preview_camera->set_pressed(false);
+ _toggle_camera_preview(false);
+ view_menu->show();
+}
+
+
+void SpatialEditorViewport::_init_gizmo_instance(int p_idx) {
+
+ uint32_t layer=1<<(GIZMO_BASE_LAYER+p_idx);//|(1<<GIZMO_GRID_LAYER);
+
+ for(int i=0;i<3;i++) {
+ move_gizmo_instance[i]=VS::get_singleton()->instance_create();
+ VS::get_singleton()->instance_set_base(move_gizmo_instance[i],spatial_editor->get_move_gizmo(i)->get_rid());
+ VS::get_singleton()->instance_set_scenario(move_gizmo_instance[i],get_tree()->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_cast_shadows_setting(move_gizmo_instance[i], VS::SHADOW_CASTING_SETTING_OFF);
+ VS::get_singleton()->instance_set_layer_mask(move_gizmo_instance[i],layer);
+
+ rotate_gizmo_instance[i]=VS::get_singleton()->instance_create();
+ VS::get_singleton()->instance_set_base(rotate_gizmo_instance[i],spatial_editor->get_rotate_gizmo(i)->get_rid());
+ VS::get_singleton()->instance_set_scenario(rotate_gizmo_instance[i],get_tree()->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_cast_shadows_setting(rotate_gizmo_instance[i], VS::SHADOW_CASTING_SETTING_OFF);
+ VS::get_singleton()->instance_set_layer_mask(rotate_gizmo_instance[i],layer);
+ }
+
+}
+
+
+void SpatialEditorViewport::_finish_gizmo_instances() {
+
+
+ for(int i=0;i<3;i++) {
+ VS::get_singleton()->free(move_gizmo_instance[i]);
+ VS::get_singleton()->free(rotate_gizmo_instance[i]);
+ }
+
+}
+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_tree",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();
+ surface->update();
+
+ } else {
+
+ previewing=preview;
+ previewing->connect("exit_tree",this,"_preview_exited_scene");
+ VS::get_singleton()->viewport_attach_camera( viewport->get_viewport(), preview->get_camera() ); //replace
+ view_menu->hide();
+ surface->update();
+
+ }
+}
+
+void SpatialEditorViewport::_selection_result_pressed(int p_result) {
+
+ if (selection_results.size() <= p_result)
+ return;
+
+ clicked=selection_results[p_result].item->get_instance_ID();
+
+ if (clicked) {
+ _select_clicked(clicked_wants_append,true);
+ clicked=0;
+ }
+}
+
+void SpatialEditorViewport::_selection_menu_hide() {
+
+ selection_results.clear();
+ selection_menu->clear();
+ selection_menu->set_size(Vector2(0, 0));
+}
+
+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::update_transform_gizmo_view() {
+
+ if (!is_visible())
+ return;
+
+ Transform xform = spatial_editor->get_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;
+
+ float gsize = EditorSettings::get_singleton()->get("3d_editor/manipulator_gizmo_size");
+ gizmo_scale=(gsize/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,spatial_editor->is_gizmo_visible()&& (spatial_editor->get_tool_mode()==SpatialEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode()==SpatialEditor::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,spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode()==SpatialEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode()==SpatialEditor::TOOL_MODE_ROTATE) );
+ }
+
+}
+
+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);
+ if (p_state.has("listener")) {
+ bool listener = p_state["listener"];
+
+ int idx = view_menu->get_popup()->get_item_index(VIEW_AUDIO_LISTENER);
+ viewport->set_as_audio_listener(listener);
+ view_menu->get_popup()->set_item_checked( idx, listener );
+ }
+
+ if (p_state.has("previewing")) {
+ Node *pv = EditorNode::get_singleton()->get_edited_scene()->get_node(p_state["previewing"]);
+ if (pv && pv->cast_to<Camera>()) {
+ previewing=pv->cast_to<Camera>();
+ previewing->connect("exit_tree",this,"_preview_exited_scene");
+ VS::get_singleton()->viewport_attach_camera( viewport->get_viewport(), previewing->get_camera() ); //replace
+ view_menu->hide();
+ surface->update();
+ preview_camera->set_pressed(true);
+ preview_camera->show();
+ }
+ }
+}
+
+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;
+ d["listener"]=viewport->is_audio_listener();
+ if (previewing) {
+ d["previewing"]=EditorNode::get_singleton()->get_edited_scene()->get_path_to(previewing);
+ }
+
+ 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);
+ ObjectTypeDB::bind_method(_MD("update_transform_gizmo_view"),&SpatialEditorViewport::update_transform_gizmo_view);
+ ObjectTypeDB::bind_method(_MD("_selection_result_pressed"),&SpatialEditorViewport::_selection_result_pressed);
+ ObjectTypeDB::bind_method(_MD("_selection_menu_hide"),&SpatialEditorViewport::_selection_menu_hide);
+
+ ADD_SIGNAL( MethodInfo("toggle_maximize_view", PropertyInfo(Variant::OBJECT, "viewport")) );
+}
+
+
+void SpatialEditorViewport::reset() {
+
+ orthogonal=false;
+ message_time=0;
+ message="";
+ last_message="";
+ name="";
+
+ cursor.x_rot=0.5;
+ cursor.y_rot=0.5;
+ cursor.distance=4;
+ cursor.region_select=false;
+ _update_name();
+}
+
+SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, EditorNode *p_editor, int p_index) {
+
+ _edit.mode=TRANSFORM_NONE;
+ _edit.plane=TRANSFORM_VIEW;
+ _edit.edited_gizmo=0;
+ _edit.snap=1;
+ _edit.gizmo_handle=0;
+
+
+
+ index=p_index;
+ 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 );
+ viewport->set_disable_input(true);
+ 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_visible_layers( ((1<<20)-1)|(1<<(GIZMO_BASE_LAYER+p_index))|(1<<GIZMO_EDIT_LAYER)|(1<<GIZMO_GRID_LAYER) );
+ //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_self_opacity(0.5);
+ view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/top_view"), VIEW_TOP);
+ view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/bottom_view"), VIEW_BOTTOM);
+ view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/left_view"), VIEW_LEFT);
+ view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/right_view"), VIEW_RIGHT);
+ view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/front_view"), VIEW_FRONT);
+ view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/rear_view"), VIEW_REAR);
+ view_menu->get_popup()->add_separator();
+ view_menu->get_popup()->add_check_item(TTR("Perspective") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_PERSPECTIVE);
+ view_menu->get_popup()->add_check_item(TTR("Orthogonal") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", 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_shortcut(ED_SHORTCUT("spatial_editor/view_environment", TTR("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_check_shortcut(ED_SHORTCUT("spatial_editor/view_audio_listener", TTR("Audio Listener")), VIEW_AUDIO_LISTENER);
+ view_menu->get_popup()->add_separator();
+ view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_gizmos", TTR("Gizmos")),VIEW_GIZMOS);
+ view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(VIEW_GIZMOS),true);
+
+ view_menu->get_popup()->add_separator();
+ view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/focus_origin"), VIEW_CENTER_TO_ORIGIN);
+ view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/focus_selection"), VIEW_CENTER_TO_SELECTION);
+ view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/align_selection_with_view"), VIEW_ALIGN_SELECTION_WITH_VIEW);
+ 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;
+ gizmo_scale=1.0;
+
+ selection_menu = memnew( PopupMenu );
+ add_child(selection_menu);
+ selection_menu->set_custom_minimum_size(Vector2(100, 0));
+ selection_menu->connect("item_pressed", this, "_selection_result_pressed");
+ selection_menu->connect("popup_hide", this, "_selection_menu_hide");
+
+ if (p_index==0) {
+ view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUDIO_LISTENER),true);
+ viewport->set_as_audio_listener(true);
+ }
+
+
+ name=TTR("Top");
+ _update_name();
+
+ EditorSettings::get_singleton()->connect("settings_changed",this,"update_transform_gizmo_view");
+
+}
+
+
+
+
+
+
+
+SpatialEditor *SpatialEditor::singleton=NULL;
+
+SpatialEditorSelectedItem::~SpatialEditorSelectedItem() {
+
+ if (sbox_instance.is_valid())
+ VisualServer::get_singleton()->free(sbox_instance);
+}
+
+
+
+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;
+
+ for(int i=0;i<4;i++) {
+ viewports[i]->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_cast_shadows_setting(si->sbox_instance, VS::SHADOW_CASTING_SETTING_OFF);
+
+ if (get_tree()->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;
+
+ d["snap_enabled"]=snap_enabled;
+ d["translate_snap"]=get_translate_snap();
+ d["rotate_snap"]=get_rotate_snap();
+ d["scale_snap"]=get_scale_snap();
+
+ int local_coords_index=transform_menu->get_popup()->get_item_index(MENU_TRANSFORM_LOCAL_COORDS);
+ d["local_coords"]=transform_menu->get_popup()->is_item_checked( local_coords_index );
+
+ 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;
+ else if (view_menu->get_popup()->is_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT) ))
+ vc=5;
+ else if (view_menu->get_popup()->is_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT) ))
+ vc=6;
+
+ 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["ambient_light_color"]=settings_ambient_color->get_color();
+
+ d["default_srgb"]=view_menu->get_popup()->is_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_DEFAULT_SRGB) );
+ 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();
+ d["deflight_rot_x"]=settings_default_light_rot_x;
+ d["deflight_rot_y"]=settings_default_light_rot_y;
+
+ return d;
+}
+void SpatialEditor::set_state(const Dictionary& p_state) {
+
+ Dictionary d = p_state;
+
+ if (d.has("snap_enabled")) {
+ snap_enabled=d["snap_enabled"];
+ int snap_enabled_idx=transform_menu->get_popup()->get_item_index(MENU_TRANSFORM_USE_SNAP);
+ transform_menu->get_popup()->set_item_checked( snap_enabled_idx, snap_enabled );
+ }
+
+ if (d.has("translate_snap"))
+ snap_translate->set_text(d["translate_snap"]);
+
+ if (d.has("rotate_snap"))
+ snap_rotate->set_text(d["rotate_snap"]);
+
+ if (d.has("scale_snap"))
+ snap_scale->set_text(d["scale_snap"]);
+
+ if (d.has("local_coords")) {
+ int local_coords_idx=transform_menu->get_popup()->get_item_index(MENU_TRANSFORM_LOCAL_COORDS);
+ transform_menu->get_popup()->set_item_checked( local_coords_idx, d["local_coords"] );
+ update_transform_gizmo();
+ }
+
+ if (d.has("viewport_mode")) {
+ 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);
+ else if (vc==5)
+ _menu_item_pressed(MENU_VIEW_USE_2_VIEWPORTS_ALT);
+ else if (vc==6)
+ _menu_item_pressed(MENU_VIEW_USE_3_VIEWPORTS_ALT);
+ }
+
+ if (d.has("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_val(float(d["zfar"]));
+ if (d.has("znear"))
+ settings_znear->set_val(float(d["znear"]));
+ if (d.has("fov"))
+ settings_fov->set_val(float(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_tree()->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("ambient_light_color")) {
+ settings_ambient_color->set_color(d["ambient_light_color"]);
+ viewport_environment->fx_set_param(Environment::FX_PARAM_AMBIENT_LIGHT_COLOR,d["ambient_light_color"]);
+ }
+
+ if (d.has("default_srgb")) {
+ bool use = d["default_srgb"];
+
+ viewport_environment->set_enable_fx(Environment::FX_SRGB,use);
+ view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_DEFAULT_SRGB), use );
+ }
+ 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))) {
+ _menu_item_pressed(MENU_VIEW_GRID);
+ }
+ }
+
+ 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);
+ }
+ }
+
+ if (d.has("deflight_rot_x"))
+ settings_default_light_rot_x=d["deflight_rot_x"];
+ if (d.has("deflight_rot_y"))
+ settings_default_light_rot_y=d["deflight_rot_y"];
+
+ _update_default_light_angle();
+
+
+}
+
+
+void SpatialEditor::edit(Spatial *p_spatial) {
+
+ if (p_spatial!=selected) {
+ if (selected) {
+
+ Ref<SpatialEditorGizmo> seg = selected->get_gizmo();
+ if (seg.is_valid()) {
+ seg->set_selected(false);
+ selected->update_gizmo();
+ }
+ }
+
+ selected=p_spatial;
+ over_gizmo_handle=-1;
+
+ if (selected) {
+
+ Ref<SpatialEditorGizmo> seg = selected->get_gizmo();
+ if (seg.is_valid()) {
+ seg->set_selected(true);
+ selected->update_gizmo();
+ }
+ }
+ }
+
+ 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(TTR("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:
+ case MENU_TOOL_LIST_SELECT: {
+
+ for(int i=0;i<TOOL_MAX;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.","List Selection 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,180));
+ } 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_tree()->get_root()->get_world()->get_scenario());
+ VisualServer::get_singleton()->instance_set_transform(light_instance,light_transform);
+
+ _update_default_light_angle();
+ }
+
+ 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_DEFAULT_SRGB: {
+
+ bool is_checked = view_menu->get_popup()->is_item_checked( view_menu->get_popup()->get_item_index(p_option) );
+
+ if (is_checked) {
+ viewport_environment->set_enable_fx(Environment::FX_SRGB,false);
+ } else {
+ viewport_environment->set_enable_fx(Environment::FX_SRGB,true);
+ }
+
+ is_checked = ! is_checked;
+
+ view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(p_option), is_checked );
+
+
+ } 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 );
+ view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), false );
+ view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), 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 );
+ view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), false );
+ view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), false );
+
+ } break;
+ case MENU_VIEW_USE_2_VIEWPORTS_ALT: {
+
+ 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_RIGHT,ANCHOR_RATIO,0.5);
+ viewports[2]->set_area_as_parent_rect();
+ viewports[2]->set_anchor_and_margin(MARGIN_LEFT,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), false );
+ view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), true );
+ view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), 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 );
+ view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), false );
+ view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), false );
+
+ } break;
+ case MENU_VIEW_USE_3_VIEWPORTS_ALT: {
+
+ 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[0]->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_RATIO,0.5);
+ viewports[2]->set_area_as_parent_rect();
+ viewports[2]->set_anchor_and_margin(MARGIN_TOP,ANCHOR_RATIO,0.5);
+ viewports[2]->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_RATIO,0.5);
+ viewports[3]->set_area_as_parent_rect();
+ viewports[3]->set_anchor_and_margin(MARGIN_LEFT,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), false );
+ view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), false );
+ view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), true );
+
+ } 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 );
+ view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), false );
+ view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), false );
+
+ } break;
+ case MENU_VIEW_DISPLAY_NORMAL: {
+
+
+ VisualServer::get_singleton()->scenario_set_debug( get_tree()->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 );
+ view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_SHADELESS), false );
+
+ } break;
+ case MENU_VIEW_DISPLAY_WIREFRAME: {
+
+ VisualServer::get_singleton()->scenario_set_debug( get_tree()->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 );
+ view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_SHADELESS), false );
+
+ } break;
+ case MENU_VIEW_DISPLAY_OVERDRAW: {
+
+ VisualServer::get_singleton()->scenario_set_debug( get_tree()->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 );
+ view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_SHADELESS), false );
+
+ } break;
+ case MENU_VIEW_DISPLAY_SHADELESS: {
+
+ VisualServer::get_singleton()->scenario_set_debug( get_tree()->get_root()->get_world()->get_scenario(), VisualServer::SCENARIO_DEBUG_SHADELESS );
+ 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), false );
+ view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_SHADELESS), 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;
+
+ for(int i=0;i<3;++i) {
+ if (grid_enable[i]) {
+ VisualServer::get_singleton()->instance_geometry_set_flag(grid_instance[i],VS::INSTANCE_FLAG_VISIBLE,grid_enabled);
+ grid_visible[i]=grid_enabled;
+ }
+ }
+
+ 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(settings_vbc->get_combined_minimum_size()+Size2(50,50));
+ } 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_tree()->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);
+
+
+ {
+
+ 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;
+
+ Color grid_color = EditorSettings::get_singleton()->get("3d_editor/grid_color");
+
+ 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(grid_color);
+ grid_colors[i].push_back(grid_color);
+ grid_colors[i].push_back(grid_color);
+ grid_colors[i].push_back(grid_color);
+ 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_tree()->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_cast_shadows_setting(grid_instance[i], VS::SHADOW_CASTING_SETTING_OFF);
+ VS::get_singleton()->instance_set_layer_mask(grid_instance[i], 1 << SpatialEditorViewport::GIZMO_GRID_LAYER);
+
+
+ }
+
+ 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);
+
+
+// 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_tree()->get_root()->get_world()->get_scenario());
+ VS::get_singleton()->instance_set_layer_mask(origin_instance,1<<SpatialEditorViewport::GIZMO_GRID_LAYER);
+
+ VisualServer::get_singleton()->instance_geometry_set_cast_shadows_setting(origin_instance, VS::SHADOW_CASTING_SETTING_OFF);
+
+
+
+ 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));
+ cursor_material=VisualServer::get_singleton()->fixed_material_create();
+ VisualServer::get_singleton()->fixed_material_set_param(cursor_material,VS::FIXED_MATERIAL_PARAM_DIFFUSE,Color(0,1,1));
+ VisualServer::get_singleton()->material_set_flag( cursor_material, VisualServer::MATERIAL_FLAG_UNSHADED, true );
+ VisualServer::get_singleton()->fixed_material_set_flag(cursor_material, VisualServer::FIXED_MATERIAL_FLAG_USE_ALPHA,true);
+ VisualServer::get_singleton()->fixed_material_set_flag(cursor_material, 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,cursor_material);
+
+ cursor_instance = VisualServer::get_singleton()->instance_create2(cursor_mesh,get_tree()->get_root()->get_world()->get_scenario());
+ VS::get_singleton()->instance_set_layer_mask(cursor_instance,1<<SpatialEditorViewport::GIZMO_GRID_LAYER);
+
+ VisualServer::get_singleton()->instance_geometry_set_cast_shadows_setting(cursor_instance, VS::SHADOW_CASTING_SETTING_OFF);
+
+
+ }
+
+
+ {
+
+ //move gizmo
+
+
+ float gizmo_alph = EditorSettings::get_singleton()->get("3d_editor/manipulator_gizmo_opacity");
+
+ 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,gizmo_alph+0.2f));
+
+ for(int i=0;i<3;i++) {
+
+ move_gizmo[i]=Ref<Mesh>( memnew( Mesh ) );
+ rotate_gizmo[i]=Ref<Mesh>( memnew( Mesh ) );
+
+
+ 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= gizmo_alph;
+ 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 k = 0; k < 7 ; k++) {
+
+
+ Matrix3 ma(ivec,Math_PI*2*float(k)/arrow_sides);
+ Matrix3 mb(ivec,Math_PI*2*float(k+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]);
+
+ }
+
+
+ }
+ }
+
+ /*for(int i=0;i<4;i++) {
+
+ viewports[i]->init_gizmo_instance(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);
+ VisualServer::get_singleton()->free(indicator_mat);
+ VisualServer::get_singleton()->free(cursor_material);
+}
+
+void SpatialEditor::_instance_scene() {
+#if 0
+ EditorNode *en = get_scene()->get_root_node()->cast_to<EditorNode>();
+ ERR_FAIL_COND(!en);
+ String path = en->get_filesystem_dock()->get_selected_path();
+ if (path=="") {
+ set_message(TTR("No scene selected to instance!"));
+ return;
+ }
+
+ undo_redo->create_action(TTR("Instance at Cursor"));
+
+ Node* scene = en->request_instance_scene(path);
+
+ if (!scene) {
+ set_message(TTR("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::_unhandled_key_input(InputEvent p_event) {
+
+ if (!is_visible() || get_viewport()->gui_has_modal_stack())
+ return;
+
+ {
+
+ EditorNode *en = editor;
+ EditorPluginList *over_plugin_list = en->get_editor_plugins_over();
+
+ if (!over_plugin_list->empty() && over_plugin_list->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;
+
+ case KEY_Z: {
+ if (k.mod.shift || k.mod.control || k.mod.command)
+ break;
+
+ if (view_menu->get_popup()->is_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_DISPLAY_WIREFRAME))) {
+ _menu_item_pressed(MENU_VIEW_DISPLAY_NORMAL);
+ } else {
+ _menu_item_pressed(MENU_VIEW_DISPLAY_WIREFRAME);
+ }
+ } 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") );
+ tool_button[SpatialEditor::TOOL_MODE_LIST_SELECT]->set_icon( get_icon("ListSelect","EditorIcons") );
+ instance_button->set_icon( get_icon("SpatialAdd","EditorIcons") );
+ instance_button->hide();
+
+
+ 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_2_VIEWPORTS_ALT),get_icon("Panels2Alt","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_3_VIEWPORTS_ALT),get_icon("Panels3Alt","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_tree()->connect("node_removed",this,"_node_removed");
+ VS::get_singleton()->scenario_set_fallback_environment(get_viewport()->find_world()->get_scenario(),viewport_environment->get_rid());
+
+ }
+
+ if (p_what==NOTIFICATION_ENTER_TREE) {
+
+ gizmos = memnew( SpatialEditorGizmos );
+ _init_indicators();
+ _update_default_light_angle();
+ }
+
+ if (p_what==NOTIFICATION_EXIT_TREE) {
+
+ _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() || editor->get_edited_scene()->is_editable_instance(sp->get_owner()))) {
+
+ Ref<SpatialEditorGizmo> seg;
+
+ for(int i=0;i<EditorNode::get_singleton()->get_editor_data().get_editor_plugin_count();i++) {
+
+ seg = EditorNode::get_singleton()->get_editor_data().get_editor_plugin(i)->create_spatial_gizmo(sp);
+ if (seg.is_valid())
+ break;
+ }
+
+ if (!seg.is_valid()) {
+ seg = gizmos->get_gizmo(sp);
+ }
+
+ if (seg.is_valid()) {
+ sp->set_gizmo(seg);
+ }
+
+
+ if (seg.is_valid() && sp==selected) {
+ seg->set_selected(true);
+ selected->update_gizmo();
+ }
+
+ }
+}
+
+void SpatialEditor::_toggle_maximize_view(Object* p_viewport) {
+ if (!p_viewport) return;
+ SpatialEditorViewport *current_viewport = p_viewport->cast_to<SpatialEditorViewport>();
+ if (!current_viewport) return;
+
+ int index=-1;
+ bool maximized = false;
+ for(int i=0;i<4;i++) {
+ if (viewports[i]==current_viewport) {
+ index=i;
+ if ( current_viewport->get_global_rect() == viewport_base->get_global_rect() )
+ maximized=true;
+ break;
+ }
+ }
+ if (index==-1) return;
+
+ if (!maximized) {
+
+ for(int i=0;i<4;i++) {
+ if (i==index)
+ viewports[i]->set_area_as_parent_rect();
+ else
+ viewports[i]->hide();
+ }
+ } else {
+
+ for(int i=0;i<4;i++)
+ viewports[i]->show();
+
+ if (view_menu->get_popup()->is_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT) ))
+ _menu_item_pressed(MENU_VIEW_USE_1_VIEWPORT);
+ else if (view_menu->get_popup()->is_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS) ))
+ _menu_item_pressed(MENU_VIEW_USE_2_VIEWPORTS);
+ else if (view_menu->get_popup()->is_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT) ))
+ _menu_item_pressed(MENU_VIEW_USE_2_VIEWPORTS_ALT);
+ else if (view_menu->get_popup()->is_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS) ))
+ _menu_item_pressed(MENU_VIEW_USE_3_VIEWPORTS);
+ else if (view_menu->get_popup()->is_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT) ))
+ _menu_item_pressed(MENU_VIEW_USE_3_VIEWPORTS_ALT);
+ else if (view_menu->get_popup()->is_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS) ))
+ _menu_item_pressed(MENU_VIEW_USE_4_VIEWPORTS);
+ }
+
+}
+
+
+void SpatialEditor::_node_removed(Node* p_node) {
+
+ if (p_node==selected)
+ selected=NULL;
+}
+
+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("_get_editor_data",&SpatialEditor::_get_editor_data);
+ ObjectTypeDB::bind_method("_request_gizmo",&SpatialEditor::_request_gizmo);
+ ObjectTypeDB::bind_method("_default_light_angle_input",&SpatialEditor::_default_light_angle_input);
+ ObjectTypeDB::bind_method("_update_ambient_light_color",&SpatialEditor::_update_ambient_light_color);
+ ObjectTypeDB::bind_method("_toggle_maximize_view",&SpatialEditor::_toggle_maximize_view);
+
+ ADD_SIGNAL( MethodInfo("transform_key_request") );
+
+
+}
+
+void SpatialEditor::clear() {
+
+ settings_fov->set_val(EDITOR_DEF("3d_editor/default_fov",60.0));
+ settings_znear->set_val(EDITOR_DEF("3d_editor/default_z_near",0.1));
+ settings_zfar->set_val(EDITOR_DEF("3d_editor/default_z_far",1500.0));
+
+ for(int i=0;i<4;i++) {
+ viewports[i]->reset();
+ }
+
+ _menu_item_pressed(MENU_VIEW_USE_1_VIEWPORT);
+ _menu_item_pressed(MENU_VIEW_DISPLAY_NORMAL);
+
+
+ VisualServer::get_singleton()->instance_geometry_set_flag(origin_instance,VS::INSTANCE_FLAG_VISIBLE,true);
+ view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_ORIGIN), true);
+ for(int i=0;i<3;++i) {
+ if (grid_enable[i]) {
+ VisualServer::get_singleton()->instance_geometry_set_flag(grid_instance[i],VS::INSTANCE_FLAG_VISIBLE,true);
+ grid_visible[i]=true;
+ }
+ }
+
+ for(int i=0;i<4;i++) {
+
+ viewports[i]->view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(SpatialEditorViewport::VIEW_AUDIO_LISTENER),i==0);
+ viewports[i]->viewport->set_as_audio_listener(i==0);
+ }
+
+ view_menu->get_popup()->set_item_checked( view_menu->get_popup()->get_item_index(MENU_VIEW_GRID), true );
+
+ settings_default_light_rot_x=Math_PI*0.3;
+ settings_default_light_rot_y=Math_PI*0.2;
+
+ viewport_environment->fx_set_param(Environment::FX_PARAM_AMBIENT_LIGHT_COLOR,Color(0.15,0.15,0.15));
+ settings_ambient_color->set_color(Color(0.15,0.15,0.15));
+ if (!light_instance.is_valid())
+ _menu_item_pressed(MENU_VIEW_USE_DEFAULT_LIGHT);
+
+ _update_default_light_angle();
+
+
+}
+
+
+void SpatialEditor::_update_ambient_light_color(const Color& p_color) {
+
+ viewport_environment->fx_set_param(Environment::FX_PARAM_AMBIENT_LIGHT_COLOR,settings_ambient_color->get_color());
+
+}
+
+void SpatialEditor::_update_default_light_angle() {
+
+ Transform t;
+ t.basis.rotate(Vector3(1,0,0),settings_default_light_rot_x);
+ t.basis.rotate(Vector3(0,1,0),settings_default_light_rot_y);
+ settings_dlight->set_transform(t);
+ if (light_instance.is_valid()) {
+ VS::get_singleton()->instance_set_transform(light_instance,t);
+ }
+
+}
+
+void SpatialEditor::_default_light_angle_input(const InputEvent& p_event) {
+
+
+ if (p_event.type==InputEvent::MOUSE_MOTION && p_event.mouse_motion.button_mask&(0x1|0x2|0x4)) {
+
+ settings_default_light_rot_y = Math::fposmod(settings_default_light_rot_y - p_event.mouse_motion.relative_x*0.01,Math_PI*2.0);
+ settings_default_light_rot_x = Math::fposmod(settings_default_light_rot_x - p_event.mouse_motion.relative_y*0.01,Math_PI*2.0);
+ _update_default_light_angle();
+ }
+
+}
+
+
+SpatialEditor::SpatialEditor(EditorNode *p_editor) {
+
+ gizmo.visible=true;
+ gizmo.scale=1.0;
+
+ 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);
+
+ 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)\n"+keycode_get_string(KEY_MASK_CMD)+"Drag: Rotate\nAlt+Drag: Move\nAlt+RMB: Depth list selection");
+
+
+ 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(TTR("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(TTR("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(TTR("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");
+ instance_button->hide();
+
+ VSeparator *vs = memnew( VSeparator );
+ hbc_menu->add_child(vs);
+
+ tool_button[TOOL_MODE_LIST_SELECT] = memnew( ToolButton );
+ hbc_menu->add_child( tool_button[TOOL_MODE_LIST_SELECT] );
+ tool_button[TOOL_MODE_LIST_SELECT]->set_toggle_mode(true);
+ tool_button[TOOL_MODE_LIST_SELECT]->set_flat(true);
+ button_binds[0]=MENU_TOOL_LIST_SELECT;
+ tool_button[TOOL_MODE_LIST_SELECT]->connect("pressed", this,"_menu_item_pressed",button_binds);
+ tool_button[TOOL_MODE_LIST_SELECT]->set_tooltip(TTR("Show a list of all objects at the position clicked\n(same as Alt+RMB in select mode)."));
+
+ vs = memnew( VSeparator );
+ hbc_menu->add_child(vs);
+
+
+ ED_SHORTCUT("spatial_editor/bottom_view", TTR("Bottom View"), KEY_MASK_ALT+KEY_KP_7);
+ ED_SHORTCUT("spatial_editor/top_view", TTR("Top View"), KEY_KP_7);
+ ED_SHORTCUT("spatial_editor/rear_view", TTR("Rear View"), KEY_MASK_ALT+KEY_KP_1);
+ ED_SHORTCUT("spatial_editor/front_view", TTR("Front View"), KEY_KP_1);
+ ED_SHORTCUT("spatial_editor/left_view", TTR("Left View"), KEY_MASK_ALT+KEY_KP_3);
+ ED_SHORTCUT("spatial_editor/right_view", TTR("Right View"), KEY_KP_3);
+ ED_SHORTCUT("spatial_editor/switch_perspective_orthogonal", TTR("Switch Perspective/Orthogonal view"), KEY_KP_5);
+ ED_SHORTCUT("spatial_editor/snap", TTR("Snap"), KEY_S);
+ ED_SHORTCUT("spatial_editor/insert_anim_key", TTR("Insert Animation Key"), KEY_K);
+ ED_SHORTCUT("spatial_editor/focus_origin", TTR("Focus Origin"), KEY_O);
+ ED_SHORTCUT("spatial_editor/focus_selection", TTR("Focus Selection"), KEY_F);
+ ED_SHORTCUT("spatial_editor/align_selection_with_view", TTR("Align Selection With View"), KEY_MASK_ALT+KEY_MASK_CMD+KEY_F);
+
+
+ PopupMenu *p;
+
+ transform_menu = memnew( MenuButton );
+ transform_menu->set_text(TTR("Transform"));
+ hbc_menu->add_child( transform_menu );
+
+ p = transform_menu->get_popup();
+ p->add_check_shortcut(ED_SHORTCUT("spatial_editor/use_snap", TTR("Use Snap")), MENU_TRANSFORM_USE_SNAP);
+ p->add_shortcut(ED_SHORTCUT("spatial_editor/configure_snap", TTR("Configure Snap..")), MENU_TRANSFORM_CONFIGURE_SNAP);
+ p->add_separator();
+ p->add_check_shortcut(ED_SHORTCUT("spatial_editor/local_coords", TTR("Local Coords")), MENU_TRANSFORM_LOCAL_COORDS);
+ //p->set_item_checked(p->get_item_count()-1,true);
+ p->add_separator();
+ p->add_shortcut(ED_SHORTCUT("spatial_editor/transform_dialog", TTR("Transform Dialog..")), MENU_TRANSFORM_DIALOG);
+
+ p->connect("item_pressed", this,"_menu_item_pressed");
+
+ view_menu = memnew( MenuButton );
+ view_menu->set_text(TTR("View"));
+ view_menu->set_pos( Point2( 212,0) );
+ hbc_menu->add_child( view_menu );
+
+ p = view_menu->get_popup();
+
+ p->add_check_shortcut(ED_SHORTCUT("spatial_editor/use_default_light", TTR("Use Default Light")), MENU_VIEW_USE_DEFAULT_LIGHT);
+ p->add_check_shortcut(ED_SHORTCUT("spatial_editor/use_default_srgb", TTR("Use Default sRGB")), MENU_VIEW_USE_DEFAULT_SRGB);
+ p->add_separator();
+
+ p->add_check_shortcut(ED_SHORTCUT("spatial_editor/1_viewport", TTR("1 Viewport"), KEY_MASK_CMD+KEY_1), MENU_VIEW_USE_1_VIEWPORT);
+ p->add_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports", TTR("2 Viewports"), KEY_MASK_CMD+KEY_2), MENU_VIEW_USE_2_VIEWPORTS);
+ p->add_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports_alt", TTR("2 Viewports (Alt)"), KEY_MASK_ALT+KEY_MASK_CMD+KEY_2), MENU_VIEW_USE_2_VIEWPORTS_ALT);
+ p->add_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports", TTR("3 Viewports"),KEY_MASK_CMD+KEY_3), MENU_VIEW_USE_3_VIEWPORTS);
+ p->add_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports_alt", TTR("3 Viewports (Alt)"), KEY_MASK_ALT+KEY_MASK_CMD+KEY_3), MENU_VIEW_USE_3_VIEWPORTS_ALT);
+ p->add_check_shortcut(ED_SHORTCUT("spatial_editor/4_viewports", TTR("4 Viewports"), KEY_MASK_CMD+KEY_4), MENU_VIEW_USE_4_VIEWPORTS);
+ p->add_separator();
+
+ p->add_check_shortcut(ED_SHORTCUT("spatial_editor/display_normal", TTR("Display Normal")), MENU_VIEW_DISPLAY_NORMAL);
+ p->add_check_shortcut(ED_SHORTCUT("spatial_editor/display_wireframe", TTR("Display Wireframe")), MENU_VIEW_DISPLAY_WIREFRAME);
+ p->add_check_shortcut(ED_SHORTCUT("spatial_editor/display_overdraw", TTR("Display Overdraw")), MENU_VIEW_DISPLAY_OVERDRAW);
+ p->add_check_shortcut(ED_SHORTCUT("spatial_editor/display_shadeless", TTR("Display Shadeless")), MENU_VIEW_DISPLAY_SHADELESS);
+ p->add_separator();
+ p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_origin", TTR("View Origin")), MENU_VIEW_ORIGIN);
+ p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_grid", TTR("View Grid")), MENU_VIEW_GRID);
+ p->add_separator();
+ p->add_shortcut(ED_SHORTCUT("spatial_editor/settings", TTR("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,i) );
+ viewports[i]->connect("toggle_maximize_view",this,"_toggle_maximize_view");
+ viewport_base->add_child(viewports[i]);
+ }
+ //vbc->add_child(viewport_base);
+
+
+
+
+ /* SNAP DIALOG */
+
+ snap_dialog = memnew( ConfirmationDialog );
+ snap_dialog->set_title(TTR("Snap Settings"));
+ add_child(snap_dialog);
+
+ VBoxContainer *snap_dialog_vbc = memnew( VBoxContainer );
+ snap_dialog->add_child(snap_dialog_vbc);
+ snap_dialog->set_child_rect(snap_dialog_vbc);
+
+ snap_translate = memnew( LineEdit );
+ snap_translate->set_text("1");
+ snap_dialog_vbc->add_margin_child(TTR("Translate Snap:"),snap_translate);
+
+ snap_rotate = memnew( LineEdit );
+ snap_rotate->set_text("5");
+ snap_dialog_vbc->add_margin_child(TTR("Rotate Snap (deg.):"),snap_rotate);
+
+ snap_scale = memnew( LineEdit );
+ snap_scale->set_text("5");
+ snap_dialog_vbc->add_margin_child(TTR("Scale Snap (%):"),snap_scale);
+
+ /* SETTINGS DIALOG */
+
+ settings_dialog = memnew( ConfirmationDialog );
+ settings_dialog->set_title(TTR("Viewport Settings"));
+ add_child(settings_dialog);
+ settings_vbc = memnew( VBoxContainer );
+ settings_vbc->set_custom_minimum_size(Size2(200,0));
+ settings_dialog->add_child(settings_vbc);
+ settings_dialog->set_child_rect(settings_vbc);
+
+
+
+ settings_light_base = memnew( Control );
+ settings_light_base->set_custom_minimum_size(Size2(128,128));
+ settings_light_base->connect("input_event",this,"_default_light_angle_input");
+ settings_vbc->add_margin_child(TTR("Default Light Normal:"),settings_light_base);
+ settings_light_vp = memnew( Viewport );
+ settings_light_vp->set_disable_input(true);
+ settings_light_vp->set_use_own_world(true);
+ settings_light_base->add_child(settings_light_vp);
+
+ settings_dlight = memnew( DirectionalLight );
+ settings_light_vp->add_child(settings_dlight);
+ settings_sphere = memnew( ImmediateGeometry );
+ settings_sphere->begin(Mesh::PRIMITIVE_TRIANGLES,Ref<Texture>());
+ settings_sphere->set_color(Color(1,1,1));
+ settings_sphere->add_sphere(32,16,1);
+ settings_sphere->end();
+ settings_light_vp->add_child(settings_sphere);
+ settings_camera = memnew( Camera );
+ settings_light_vp->add_child(settings_camera);
+ settings_camera->set_translation(Vector3(0,0,2));
+ settings_camera->set_orthogonal(2.1,0.1,5);
+
+ settings_default_light_rot_x=Math_PI*0.3;
+ settings_default_light_rot_y=Math_PI*0.2;
+
+
+
+ settings_ambient_color = memnew( ColorPickerButton );
+ settings_vbc->add_margin_child(TTR("Ambient Light Color:"),settings_ambient_color);
+ settings_ambient_color->connect("color_changed",this,"_update_ambient_light_color");
+
+ viewport_environment->set_enable_fx(Environment::FX_AMBIENT_LIGHT,true);
+ viewport_environment->fx_set_param(Environment::FX_PARAM_AMBIENT_LIGHT_COLOR,Color(0.15,0.15,0.15));
+ settings_ambient_color->set_color(Color(0.15,0.15,0.15));
+
+
+ settings_fov = memnew( SpinBox );
+ settings_fov->set_max(179);
+ settings_fov->set_min(1);
+ settings_fov->set_step(0.01);
+ settings_fov->set_val(EDITOR_DEF("3d_editor/default_fov",60.0));
+ settings_vbc->add_margin_child(TTR("Perspective FOV (deg.):"),settings_fov);
+
+ settings_znear = memnew( SpinBox );
+ settings_znear->set_max(10000);
+ settings_znear->set_min(0.1);
+ settings_znear->set_step(0.01);
+ settings_znear->set_val(EDITOR_DEF("3d_editor/default_z_near",0.1));
+ settings_vbc->add_margin_child(TTR("View Z-Near:"),settings_znear);
+
+ settings_zfar = memnew( SpinBox );
+ settings_zfar->set_max(10000);
+ settings_zfar->set_min(0.1);
+ settings_zfar->set_step(0.01);
+ settings_zfar->set_val(EDITOR_DEF("3d_editor/default_z_far",1500));
+ settings_vbc->add_margin_child(TTR("View Z-Far:"),settings_zfar);
+
+ //settings_dialog->get_cancel()->hide();
+ /* XFORM DIALOG */
+
+ xform_dialog = memnew( ConfirmationDialog );
+ xform_dialog->set_title(TTR("Transform Change"));
+ add_child(xform_dialog);
+ Label *l = memnew(Label);
+ l->set_text(TTR("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(TTR("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(TTR("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(TTR("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(TTR("Pre"));
+ xform_type->add_item(TTR("Post"));
+ xform_dialog->add_child(xform_type);
+
+ xform_dialog->connect("confirmed", this,"_xform_dialog_action");
+
+ scenario_debug=VisualServer::SCENARIO_DEBUG_DISABLED;
+
+ selected=NULL;
+
+ set_process_unhandled_key_input(true);
+ add_to_group("_spatial_editor_group");
+
+ EDITOR_DEF("3d_editor/manipulator_gizmo_size",80);
+ EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT,"3d_editor/manipulator_gizmo_size",PROPERTY_HINT_RANGE,"16,1024,1"));
+ EDITOR_DEF("3d_editor/manipulator_gizmo_opacity",0.2);
+
+ over_gizmo_handle=-1;
+}
+
+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) );
+ spatial_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ 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/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h
new file mode 100644
index 000000000..69bcdf528
--- /dev/null
+++ b/editor/plugins/spatial_editor_plugin.h
@@ -0,0 +1,573 @@
+/*************************************************************************/
+/* spatial_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "editor/editor_plugin.h"
+#include "editor/editor_node.h"
+#include "scene/3d/visual_instance.h"
+#include "scene/3d/immediate_geometry.h"
+#include "scene/3d/light.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);
+
+ bool selected;
+public:
+
+ void set_selected(bool p_selected) { selected=p_selected; }
+ bool is_selected() const { return selected; }
+
+ 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 );
+friend class SpatialEditor;
+ enum {
+
+ VIEW_TOP,
+ VIEW_BOTTOM,
+ VIEW_LEFT,
+ VIEW_RIGHT,
+ VIEW_FRONT,
+ VIEW_REAR,
+ VIEW_CENTER_TO_ORIGIN,
+ VIEW_CENTER_TO_SELECTION,
+ VIEW_ALIGN_SELECTION_WITH_VIEW,
+ VIEW_PERSPECTIVE,
+ VIEW_ENVIRONMENT,
+ VIEW_ORTHOGONAL,
+ VIEW_AUDIO_LISTENER,
+ VIEW_GIZMOS,
+ };
+public:
+ enum {
+ GIZMO_BASE_LAYER=27,
+ GIZMO_EDIT_LAYER=26,
+ GIZMO_GRID_LAYER=25
+ };
+private:
+ int index;
+ String name;
+ void _menu_option(int p_option);
+ Size2 prev_size;
+
+ EditorNode *editor;
+ EditorSelection *editor_selection;
+ UndoRedo *undo_redo;
+
+ Button *preview_camera;
+
+ MenuButton *view_menu;
+
+ Control *surface;
+ Viewport *viewport;
+ Camera *camera;
+ bool transforming;
+ bool orthogonal;
+ float gizmo_scale;
+
+ struct _RayResult {
+
+ Spatial* item;
+ float depth;
+ int handle;
+ _FORCE_INLINE_ bool operator<(const _RayResult& p_rr) const { return depth<p_rr.depth; }
+ };
+
+ void _update_name();
+ 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);
+ void _find_items_at_pos(const Point2& p_pos,bool &r_includes_current,Vector<_RayResult> &results,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();
+ 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;
+ Vector<_RayResult> selection_results;
+ bool clicked_includes_current;
+ bool clicked_wants_append;
+
+ PopupMenu *selection_menu;
+
+ enum NavigationScheme {
+ NAVIGATION_GODOT,
+ NAVIGATION_MAYA,
+ NAVIGATION_MODO,
+ };
+ NavigationScheme _get_navigation_schema(const String& p_property);
+
+ enum NavigationZoomStyle {
+ NAVIGATION_ZOOM_VERTICAL,
+ NAVIGATION_ZOOM_HORIZONTAL
+ };
+ NavigationZoomStyle _get_navigation_zoom_style(const String& p_property);
+
+ enum NavigationMode {
+ NAVIGATION_NONE,
+ NAVIGATION_PAN,
+ NAVIGATION_ZOOM,
+ NAVIGATION_ORBIT
+ };
+ 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.5; distance=4; region_select=false; }
+ } cursor;
+
+ RID move_gizmo_instance[3], rotate_gizmo_instance[3];
+
+
+ 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);
+ void _init_gizmo_instance(int p_idx);
+ void _finish_gizmo_instances();
+ void _selection_result_pressed(int);
+ void _selection_menu_hide();
+ void _list_select(InputEventMouseButton b);
+
+
+protected:
+
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+ void update_transform_gizmo_view();
+
+ void set_can_preview(Camera* p_preview);
+ void set_state(const Dictionary& p_state);
+ Dictionary get_state() const;
+ void reset();
+ Viewport *get_viewport_node() { return viewport; }
+
+
+ SpatialEditorViewport(SpatialEditor *p_spatial_editor,EditorNode *p_editor,int p_index);
+};
+
+
+
+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,
+ TOOL_MODE_LIST_SELECT,
+ TOOL_MAX
+
+ };
+
+
+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];
+ Ref<FixedMaterial> gizmo_color[3];
+ Ref<FixedMaterial> gizmo_hl;
+
+
+ int over_gizmo_handle;
+
+
+
+ Ref<Mesh> selection_box;
+ RID indicators;
+ RID indicators_instance;
+ RID cursor_mesh;
+ RID cursor_instance;
+ RID indicator_mat;
+ RID cursor_material;
+
+/*
+ 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_TOOL_LIST_SELECT,
+ 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_2_VIEWPORTS_ALT,
+ MENU_VIEW_USE_3_VIEWPORTS,
+ MENU_VIEW_USE_3_VIEWPORTS_ALT,
+ MENU_VIEW_USE_4_VIEWPORTS,
+ MENU_VIEW_USE_DEFAULT_LIGHT,
+ MENU_VIEW_USE_DEFAULT_SRGB,
+ MENU_VIEW_DISPLAY_NORMAL,
+ MENU_VIEW_DISPLAY_WIREFRAME,
+ MENU_VIEW_DISPLAY_OVERDRAW,
+ MENU_VIEW_DISPLAY_SHADELESS,
+ MENU_VIEW_ORIGIN,
+ MENU_VIEW_GRID,
+ MENU_VIEW_CAMERA_SETTINGS,
+
+ };
+
+
+ Button *tool_button[TOOL_MAX];
+ 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;
+
+ VBoxContainer *settings_vbc;
+ SpinBox *settings_fov;
+ SpinBox *settings_znear;
+ SpinBox *settings_zfar;
+ DirectionalLight *settings_dlight;
+ ImmediateGeometry *settings_sphere;
+ Camera *settings_camera;
+ float settings_default_light_rot_x;
+ float settings_default_light_rot_y;
+
+ Control *settings_light_base;
+ Viewport *settings_light_vp;
+ ColorPickerButton *settings_ambient_color;
+ Image settings_light_dir_image;
+
+
+ void _xform_dialog_action();
+ void _menu_item_pressed(int p_option);
+
+ HBoxContainer *hbc_menu;
+
+
+
+//
+//
+ void _generate_selection_box();
+ UndoRedo *undo_redo;
+
+ void _instance_scene();
+ void _init_indicators();
+ void _finish_indicators();
+
+ void _toggle_maximize_view(Object* p_viewport);
+
+ Node *custom_camera;
+
+ Object *_get_editor_data(Object *p_what);
+
+ Ref<Environment> viewport_environment;
+
+ Spatial *selected;
+
+ void _request_gizmo(Object* p_obj);
+
+ static SpatialEditor *singleton;
+
+ void _node_removed(Node* p_node);
+ SpatialEditorGizmos *gizmos;
+ SpatialEditor();
+
+ void _update_ambient_light_color(const Color& p_color);
+ void _update_default_light_angle();
+ void _default_light_angle_input(const InputEvent& p_event);
+
+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_val(); }
+ float get_zfar() const { return settings_zfar->get_val(); }
+ float get_fov() const { return settings_fov->get_val(); }
+
+ 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(); }
+
+ Ref<Mesh> get_move_gizmo(int idx) const { return move_gizmo[idx]; }
+ Ref<Mesh> get_rotate_gizmo(int idx) const { return rotate_gizmo[idx]; }
+
+ 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_control_to_menu_panel(Control *p_control);
+
+ VSplitContainer *get_shader_split();
+ HSplitContainer *get_palette_split();
+
+ Spatial *get_selected() { return selected; }
+
+ int get_over_gizmo_handle() const { return over_gizmo_handle; }
+ void set_over_gizmo_handle(int idx) { over_gizmo_handle=idx; }
+
+ void set_can_preview(Camera* p_preview);
+
+ SpatialEditorViewport *get_editor_viewport(int p_idx) {
+ ERR_FAIL_INDEX_V(p_idx,4,NULL);
+ return viewports[p_idx];
+ }
+
+ Camera *get_camera() { return NULL; }
+ void edit(Spatial *p_spatial);
+ void clear();
+ 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);
+ virtual void clear() { spatial_editor->clear(); }
+
+
+ SpatialEditorPlugin(EditorNode *p_node);
+ ~SpatialEditorPlugin();
+
+};
+
+#endif
diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp
new file mode 100644
index 000000000..004869d15
--- /dev/null
+++ b/editor/plugins/sprite_frames_editor_plugin.cpp
@@ -0,0 +1,969 @@
+/*************************************************************************/
+/* sprite_frames_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "editor/editor_settings.h"
+#include "scene/3d/sprite_3d.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_TREE) {
+ load->set_icon( get_icon("Folder","EditorIcons") );
+ _delete->set_icon( get_icon("Del","EditorIcons") );
+ new_anim->set_icon( get_icon("New","EditorIcons") );
+ remove_anim->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,int p_at_pos) {
+
+ ERR_FAIL_COND(!frames->has_animation(edited_anim));
+
+ 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(TTR("ERROR: Couldn't load frame resource!"));
+ dialog->set_title(TTR("Error!"));
+ //dialog->get_cancel()->set_text("Close");
+ dialog->get_ok()->set_text(TTR("Close"));
+ dialog->popup_centered_minsize();
+ return; ///beh should show an error i guess
+ }
+
+ resources.push_back(resource);
+ }
+
+
+ if (resources.empty()) {
+ //print_line("added frames!");
+ return;
+ }
+
+ undo_redo->create_action(TTR("Add Frame"));
+ int fc=frames->get_frame_count(edited_anim);
+
+ int count=0;
+
+ for(List< Ref<Texture> >::Element *E=resources.front();E;E=E->next() ) {
+
+ undo_redo->add_do_method(frames,"add_frame",edited_anim,E->get(),p_at_pos==-1?-1:p_at_pos+count);
+ undo_redo->add_undo_method(frames,"remove_frame",edited_anim,p_at_pos==-1?fc:p_at_pos);
+ count++;
+
+ }
+ 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() {
+
+ ERR_FAIL_COND(!frames->has_animation(edited_anim));
+ 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(EditorFileDialog::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() {
+
+ ERR_FAIL_COND(!frames->has_animation(edited_anim));
+
+ if (tree->get_current()<0)
+ return;
+
+ sel-=1;
+ if (sel<0 && frames->get_frame_count(edited_anim))
+ sel=0;
+
+ int to_remove = tree->get_current();
+ sel=to_remove;
+ Ref<Texture> r = frames->get_frame(edited_anim,to_remove);
+ undo_redo->create_action(TTR("Delete Resource"));
+ undo_redo->add_do_method(frames,"remove_frame",edited_anim,to_remove);
+ undo_redo->add_undo_method(frames,"add_frame",edited_anim,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() {
+
+ ERR_FAIL_COND(!frames->has_animation(edited_anim));
+
+ Ref<Texture> r=EditorSettings::get_singleton()->get_resource_clipboard();
+ if (!r.is_valid()) {
+ dialog->set_text(TTR("Resource clipboard is empty or not a texture!"));
+ dialog->set_title(TTR("Error!"));
+ //dialog->get_cancel()->set_text("Close");
+ dialog->get_ok()->set_text(TTR("Close"));
+ dialog->popup_centered_minsize();
+ return; ///beh should show an error i guess
+ }
+
+
+ undo_redo->create_action(TTR("Paste Frame"));
+ undo_redo->add_do_method(frames,"add_frame",edited_anim,r);
+ undo_redo->add_undo_method(frames,"remove_frame",edited_anim,frames->get_frame_count(edited_anim));
+ undo_redo->add_do_method(this,"_update_library");
+ undo_redo->add_undo_method(this,"_update_library");
+ undo_redo->commit_action();
+
+}
+
+void SpriteFramesEditor::_empty_pressed() {
+
+ ERR_FAIL_COND(!frames->has_animation(edited_anim));
+
+ int from=-1;
+
+ if (tree->get_current()>=0) {
+
+ from = tree->get_current();
+ sel=from;
+
+ } else {
+ from=frames->get_frame_count(edited_anim);
+ }
+
+
+
+ Ref<Texture> r;
+
+ undo_redo->create_action(TTR("Add Empty"));
+ undo_redo->add_do_method(frames,"add_frame",edited_anim,r,from);
+ undo_redo->add_undo_method(frames,"remove_frame",edited_anim,from);
+ undo_redo->add_do_method(this,"_update_library");
+ undo_redo->add_undo_method(this,"_update_library");
+ undo_redo->commit_action();
+
+}
+
+void SpriteFramesEditor::_empty2_pressed() {
+
+ ERR_FAIL_COND(!frames->has_animation(edited_anim));
+
+ int from=-1;
+
+ if (tree->get_current()>=0) {
+
+ from = tree->get_current();
+ sel=from;
+
+ } else {
+ from=frames->get_frame_count(edited_anim);
+ }
+
+
+
+ Ref<Texture> r;
+
+ undo_redo->create_action(TTR("Add Empty"));
+ undo_redo->add_do_method(frames,"add_frame",edited_anim,r,from+1);
+ undo_redo->add_undo_method(frames,"remove_frame",edited_anim,from+1);
+ undo_redo->add_do_method(this,"_update_library");
+ undo_redo->add_undo_method(this,"_update_library");
+ undo_redo->commit_action();
+
+}
+
+void SpriteFramesEditor::_up_pressed() {
+
+ ERR_FAIL_COND(!frames->has_animation(edited_anim));
+
+ if (tree->get_current()<0)
+ return;
+
+ int to_move = tree->get_current();
+ if (to_move<1)
+ return;
+
+ sel=to_move;
+ sel-=1;
+
+ Ref<Texture> r = frames->get_frame(edited_anim,to_move);
+ undo_redo->create_action(TTR("Delete Resource"));
+ undo_redo->add_do_method(frames,"set_frame",edited_anim,to_move,frames->get_frame(edited_anim,to_move-1));
+ undo_redo->add_do_method(frames,"set_frame",edited_anim,to_move-1,frames->get_frame(edited_anim,to_move));
+ undo_redo->add_undo_method(frames,"set_frame",edited_anim,to_move,frames->get_frame(edited_anim,to_move));
+ undo_redo->add_undo_method(frames,"set_frame",edited_anim,to_move-1,frames->get_frame(edited_anim,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() {
+
+ ERR_FAIL_COND(!frames->has_animation(edited_anim));
+
+ if (tree->get_current()<0)
+ return;
+
+ int to_move = tree->get_current();
+ if (to_move<0 || to_move>=frames->get_frame_count(edited_anim)-1)
+ return;
+
+ sel=to_move;
+ sel+=1;
+
+ Ref<Texture> r = frames->get_frame(edited_anim,to_move);
+ undo_redo->create_action(TTR("Delete Resource"));
+ undo_redo->add_do_method(frames,"set_frame",edited_anim,to_move,frames->get_frame(edited_anim,to_move+1));
+ undo_redo->add_do_method(frames,"set_frame",edited_anim,to_move+1,frames->get_frame(edited_anim,to_move));
+ undo_redo->add_undo_method(frames,"set_frame",edited_anim,to_move,frames->get_frame(edited_anim,to_move));
+ undo_redo->add_undo_method(frames,"set_frame",edited_anim,to_move+1,frames->get_frame(edited_anim,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_current()<0)
+ 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::_animation_select() {
+
+ if (updating)
+ return;
+
+ TreeItem *selected = animations->get_selected();
+ ERR_FAIL_COND(!selected);
+ edited_anim=selected->get_text(0);
+ _update_library(true);
+
+}
+
+
+static void _find_anim_sprites(Node* p_node,List<Node*> *r_nodes,Ref<SpriteFrames> p_sfames) {
+
+ Node *edited = EditorNode::get_singleton()->get_edited_scene();
+ if (!edited)
+ return;
+ if (p_node!=edited && p_node->get_owner()!=edited)
+ return;
+
+ {
+ AnimatedSprite *as = p_node->cast_to<AnimatedSprite>();
+ if (as && as->get_sprite_frames()==p_sfames) {
+ r_nodes->push_back(p_node);
+ }
+ }
+
+ {
+ AnimatedSprite3D *as = p_node->cast_to<AnimatedSprite3D>();
+ if (as && as->get_sprite_frames()==p_sfames) {
+ r_nodes->push_back(p_node);
+ }
+ }
+
+ for(int i=0;i<p_node->get_child_count();i++) {
+ _find_anim_sprites(p_node->get_child(i),r_nodes,p_sfames);
+ }
+
+}
+
+void SpriteFramesEditor::_animation_name_edited(){
+
+ if (updating)
+ return;
+
+ if (!frames->has_animation(edited_anim))
+ return;
+
+ TreeItem *edited = animations->get_edited();
+ if (!edited)
+ return;
+
+ String new_name = edited->get_text(0);
+
+ if (new_name==String(edited_anim))
+ return;
+
+ new_name=new_name.replace("/","_").replace(","," ");
+
+ String name=new_name;
+ int counter=0;
+ while(frames->has_animation(name)) {
+ counter++;
+ name=new_name+" "+itos(counter);
+ }
+
+ List<Node*> nodes;
+ _find_anim_sprites(EditorNode::get_singleton()->get_edited_scene(),&nodes,Ref<SpriteFrames>(frames));
+
+ undo_redo->create_action(TTR("Rename Animation"));
+ undo_redo->add_do_method(frames,"rename_animation",edited_anim,name);
+ undo_redo->add_undo_method(frames,"rename_animation",name,edited_anim);
+
+ for(List<Node*>::Element *E=nodes.front();E;E=E->next()) {
+
+ String current = E->get()->call("get_animation");
+ if (current!=edited_anim)
+ continue;
+
+ undo_redo->add_do_method(E->get(),"set_animation",name);
+ undo_redo->add_undo_method(E->get(),"set_animation",edited_anim);
+
+ }
+
+ undo_redo->add_do_method(this,"_update_library");
+ undo_redo->add_undo_method(this,"_update_library");
+
+ edited_anim=new_name;
+
+ undo_redo->commit_action();
+
+
+
+}
+void SpriteFramesEditor::_animation_add(){
+
+
+ String new_name = "New Anim";
+
+ String name=new_name;
+ int counter=0;
+ while(frames->has_animation(name)) {
+ counter++;
+ name=new_name+" "+itos(counter);
+ }
+
+ List<Node*> nodes;
+ _find_anim_sprites(EditorNode::get_singleton()->get_edited_scene(),&nodes,Ref<SpriteFrames>(frames));
+
+
+ undo_redo->create_action(TTR("Add Animation"));
+ undo_redo->add_do_method(frames,"add_animation",name);
+ undo_redo->add_undo_method(frames,"remove_animation",name);
+ undo_redo->add_do_method(this,"_update_library");
+ undo_redo->add_undo_method(this,"_update_library");
+
+
+ for(List<Node*>::Element *E=nodes.front();E;E=E->next()) {
+
+ String current = E->get()->call("get_animation");
+ if (frames->has_animation(current))
+ continue;
+
+ undo_redo->add_do_method(E->get(),"set_animation",name);
+ undo_redo->add_undo_method(E->get(),"set_animation",current);
+
+ }
+
+ edited_anim=new_name;
+
+ undo_redo->commit_action();
+
+}
+void SpriteFramesEditor::_animation_remove(){
+
+ //fuck everything
+ if (updating)
+ return;
+
+ if (!frames->has_animation(edited_anim))
+ return;
+
+
+ undo_redo->create_action(TTR("Remove Animation"));
+ undo_redo->add_do_method(frames,"remove_animation",edited_anim);
+ undo_redo->add_undo_method(frames,"add_animation",edited_anim);
+ undo_redo->add_undo_method(frames,"set_animation_speed",edited_anim,frames->get_animation_speed(edited_anim));
+ undo_redo->add_undo_method(frames,"set_animation_loop",edited_anim,frames->get_animation_loop(edited_anim));
+ int fc = frames->get_frame_count(edited_anim);
+ for(int i=0;i<fc;i++) {
+ Ref<Texture> frame = frames->get_frame(edited_anim,i);
+ undo_redo->add_undo_method(frames,"add_frame",edited_anim,frame);
+ }
+ undo_redo->add_do_method(this,"_update_library");
+ undo_redo->add_undo_method(this,"_update_library");
+
+ undo_redo->commit_action();
+
+}
+
+
+void SpriteFramesEditor::_animation_loop_changed() {
+
+ if (updating)
+ return;
+
+ undo_redo->create_action(TTR("Change Animation Loop"));
+ undo_redo->add_do_method(frames,"set_animation_loop",edited_anim,anim_loop->is_pressed());
+ undo_redo->add_undo_method(frames,"set_animation_loop",edited_anim,frames->get_animation_loop(edited_anim));
+ undo_redo->add_do_method(this,"_update_library",true);
+ undo_redo->add_undo_method(this,"_update_library",true);
+ undo_redo->commit_action();
+
+}
+
+void SpriteFramesEditor::_animation_fps_changed(double p_value) {
+
+ if (updating)
+ return;
+
+ undo_redo->create_action(TTR("Change Animation FPS"),UndoRedo::MERGE_ENDS);
+ undo_redo->add_do_method(frames,"set_animation_speed",edited_anim,p_value);
+ undo_redo->add_undo_method(frames,"set_animation_speed",edited_anim,frames->get_animation_speed(edited_anim));
+ undo_redo->add_do_method(this,"_update_library",true);
+ undo_redo->add_undo_method(this,"_update_library",true);
+
+ undo_redo->commit_action();
+
+}
+
+void SpriteFramesEditor::_update_library(bool p_skip_selector) {
+
+ updating=true;
+
+ if (!p_skip_selector) {
+ animations->clear();
+
+ TreeItem *anim_root=animations->create_item();
+
+ List<StringName> anim_names;
+
+ anim_names.sort_custom<StringName::AlphCompare>();
+
+ frames->get_animation_list(&anim_names);
+
+ anim_names.sort_custom<StringName::AlphCompare>();
+
+ for(List<StringName>::Element *E=anim_names.front();E;E=E->next()) {
+
+ String name = E->get();
+
+ TreeItem *it = animations->create_item(anim_root);
+
+ it->set_metadata(0,name);
+
+ it->set_text(0,name);
+ it->set_editable(0,true);
+
+ if (E->get()==edited_anim) {
+ it->select(0);
+ }
+ }
+ }
+
+
+ tree->clear();
+
+ if (!frames->has_animation(edited_anim)) {
+ updating=false;
+ return;
+ }
+
+
+ if (sel>=frames->get_frame_count(edited_anim))
+ sel=frames->get_frame_count(edited_anim)-1;
+ else if (sel<0 && frames->get_frame_count(edited_anim))
+ sel=0;
+
+ for(int i=0;i<frames->get_frame_count(edited_anim);i++) {
+
+
+ String name;
+ Ref<Texture> icon;
+
+
+ if (frames->get_frame(edited_anim,i).is_null()) {
+
+ name=itos(i)+": "+TTR("(empty)");
+
+ } else {
+ name=itos(i)+": "+frames->get_frame(edited_anim,i)->get_name();
+ icon=frames->get_frame(edited_anim,i);
+ }
+
+ tree->add_item(name,icon);
+ if (frames->get_frame(edited_anim,i).is_valid())
+ tree->set_item_tooltip(tree->get_item_count()-1,frames->get_frame(edited_anim,i)->get_path());
+ if (sel==i)
+ tree->select(tree->get_item_count()-1);
+ }
+
+ anim_speed->set_val(frames->get_animation_speed(edited_anim));
+ anim_loop->set_pressed(frames->get_animation_loop(edited_anim));
+
+ updating=false;
+ //player->add_resource("default",resource);
+}
+
+
+
+void SpriteFramesEditor::edit(SpriteFrames* p_frames) {
+
+ if (frames==p_frames)
+ return;
+
+ frames=p_frames;
+
+
+ if (p_frames) {
+
+ if (!p_frames->has_animation(edited_anim)) {
+
+ List<StringName> anim_names;
+ frames->get_animation_list(&anim_names);
+ anim_names.sort_custom<StringName::AlphCompare>();
+ if (anim_names.size()) {
+ edited_anim=anim_names.front()->get();
+ } else {
+ edited_anim=StringName();
+ }
+
+ }
+
+ _update_library();
+ } else {
+
+ hide();
+ //set_fixed_process(false);
+ }
+
+}
+
+
+Variant SpriteFramesEditor::get_drag_data_fw(const Point2& p_point,Control* p_from) {
+
+ if (!frames->has_animation(edited_anim))
+ return false;
+
+ int idx = tree->get_item_at_pos(p_point,true);
+
+ if (idx<0 || idx>=frames->get_frame_count(edited_anim))
+ return Variant();
+
+ RES frame = frames->get_frame(edited_anim,idx);
+
+ if (frame.is_null())
+ return Variant();
+
+ return EditorNode::get_singleton()->drag_resource(frame,p_from);
+
+
+}
+
+bool SpriteFramesEditor::can_drop_data_fw(const Point2& p_point,const Variant& p_data,Control* p_from) const{
+
+ Dictionary d = p_data;
+
+ if (!d.has("type"))
+ return false;
+
+ if (d.has("from") && (Object*)(d["from"])==tree)
+ return false;
+
+ if (String(d["type"])=="resource" && d.has("resource")) {
+ RES r=d["resource"];
+
+ Ref<Texture> texture = r;
+
+ if (texture.is_valid()) {
+
+ return true;
+ }
+ }
+
+
+ if (String(d["type"])=="files") {
+
+ Vector<String> files = d["files"];
+
+ if (files.size()==0)
+ return false;
+
+ for(int i=0;i<files.size();i++) {
+ String file = files[0];
+ String ftype = EditorFileSystem::get_singleton()->get_file_type(file);
+
+ if (!ObjectTypeDB::is_type(ftype,"Texture")) {
+ return false;
+ }
+
+ }
+
+ return true;
+
+ }
+ return false;
+}
+
+void SpriteFramesEditor::drop_data_fw(const Point2& p_point,const Variant& p_data,Control* p_from){
+
+ if (!can_drop_data_fw(p_point,p_data,p_from))
+ return;
+
+ Dictionary d = p_data;
+
+ if (!d.has("type"))
+ return;
+
+ int at_pos = tree->get_item_at_pos(p_point,true);
+
+ if (String(d["type"])=="resource" && d.has("resource")) {
+ RES r=d["resource"];
+
+ Ref<Texture> texture = r;
+
+ if (texture.is_valid()) {
+
+ undo_redo->create_action(TTR("Add Frame"));
+ undo_redo->add_do_method(frames,"add_frame",edited_anim,texture,at_pos==-1?-1:at_pos);
+ undo_redo->add_undo_method(frames,"remove_frame",edited_anim,at_pos==-1?frames->get_frame_count(edited_anim):at_pos);
+ undo_redo->add_do_method(this,"_update_library");
+ undo_redo->add_undo_method(this,"_update_library");
+ undo_redo->commit_action();
+
+ }
+ }
+
+
+ if (String(d["type"])=="files") {
+
+ DVector<String> files = d["files"];
+
+ _file_load_request(files,at_pos);
+ }
+
+}
+
+
+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("_empty2_pressed"),&SpriteFramesEditor::_empty2_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","files","atpos"),&SpriteFramesEditor::_file_load_request,DEFVAL(-1));
+ ObjectTypeDB::bind_method(_MD("_update_library","skipsel"),&SpriteFramesEditor::_update_library,DEFVAL(false));
+ ObjectTypeDB::bind_method(_MD("_up_pressed"),&SpriteFramesEditor::_up_pressed);
+ ObjectTypeDB::bind_method(_MD("_down_pressed"),&SpriteFramesEditor::_down_pressed);
+ ObjectTypeDB::bind_method(_MD("_animation_select"),&SpriteFramesEditor::_animation_select);
+ ObjectTypeDB::bind_method(_MD("_animation_name_edited"),&SpriteFramesEditor::_animation_name_edited);
+ ObjectTypeDB::bind_method(_MD("_animation_add"),&SpriteFramesEditor::_animation_add);
+ ObjectTypeDB::bind_method(_MD("_animation_remove"),&SpriteFramesEditor::_animation_remove);
+ ObjectTypeDB::bind_method(_MD("_animation_loop_changed"),&SpriteFramesEditor::_animation_loop_changed);
+ ObjectTypeDB::bind_method(_MD("_animation_fps_changed"),&SpriteFramesEditor::_animation_fps_changed);
+ ObjectTypeDB::bind_method(_MD("get_drag_data_fw"), &SpriteFramesEditor::get_drag_data_fw);
+ ObjectTypeDB::bind_method(_MD("can_drop_data_fw"), &SpriteFramesEditor::can_drop_data_fw);
+ ObjectTypeDB::bind_method(_MD("drop_data_fw"), &SpriteFramesEditor::drop_data_fw);
+
+
+}
+
+
+SpriteFramesEditor::SpriteFramesEditor() {
+
+ //add_style_override("panel", get_stylebox("panel","Panel"));
+
+ split = memnew( HSplitContainer );
+ add_child(split);
+
+ VBoxContainer *vbc_animlist = memnew( VBoxContainer );
+ split->add_child(vbc_animlist);
+ vbc_animlist->set_custom_minimum_size(Size2(150,0));
+ //vbc_animlist->set_v_size_flags(SIZE_EXPAND_FILL);
+
+
+ VBoxContainer *sub_vb = memnew( VBoxContainer );
+ vbc_animlist->add_margin_child(TTR("Animations"),sub_vb,true);
+ sub_vb->set_v_size_flags(SIZE_EXPAND_FILL);
+
+ HBoxContainer *hbc_animlist = memnew( HBoxContainer );
+ sub_vb->add_child(hbc_animlist);
+
+ new_anim = memnew( Button );
+ hbc_animlist->add_child(new_anim);
+ new_anim->connect("pressed",this,"_animation_add");
+
+
+ hbc_animlist->add_spacer();
+
+ remove_anim = memnew( Button );
+ hbc_animlist->add_child(remove_anim);
+ remove_anim->connect("pressed",this,"_animation_remove");
+
+ animations = memnew( Tree );
+ sub_vb->add_child(animations);
+ animations->set_v_size_flags(SIZE_EXPAND_FILL);
+ animations->set_hide_root(true);
+ animations->connect("cell_selected",this,"_animation_select");
+ animations->connect("item_edited",this,"_animation_name_edited");
+ animations->set_single_select_cell_editing_only_when_already_selected(true);
+
+
+ anim_speed = memnew( SpinBox);
+ vbc_animlist->add_margin_child(TTR("Speed (FPS):"),anim_speed);
+ anim_speed->set_min(0);
+ anim_speed->set_max(100);
+ anim_speed->set_step(0.01);
+ anim_speed->connect("value_changed",this,"_animation_fps_changed");
+
+ anim_loop = memnew( CheckButton );
+ anim_loop->set_text(TTR("Loop"));
+ vbc_animlist->add_child(anim_loop);
+ anim_loop->connect("pressed",this,"_animation_loop_changed");
+
+ VBoxContainer *vbc = memnew( VBoxContainer );
+ split->add_child(vbc);
+ vbc->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ sub_vb = memnew( VBoxContainer );
+ vbc->add_margin_child(TTR("Animation Frames"),sub_vb,true);
+
+
+ HBoxContainer *hbc = memnew( HBoxContainer );
+ sub_vb->add_child(hbc);
+
+ //animations = memnew( ItemList );
+
+
+ load = memnew( Button );
+ load->set_tooltip(TTR("Load Resource"));
+ hbc->add_child(load);
+
+ paste = memnew( Button );
+ paste->set_text(TTR("Paste"));
+ hbc->add_child(paste);
+
+ empty = memnew( Button );
+ empty->set_text(TTR("Insert Empty (Before)"));
+ hbc->add_child(empty);
+
+ empty2 = memnew( Button );
+ empty2->set_text(TTR("Insert Empty (After)"));
+ hbc->add_child(empty2);
+
+ move_up = memnew( Button );
+ move_up->set_text(TTR("Up"));
+ hbc->add_child(move_up);
+
+ move_down = memnew( Button );
+ move_down->set_text(TTR("Down"));
+ hbc->add_child(move_down);
+
+ _delete = memnew( Button );
+ hbc->add_child(_delete);
+
+ file = memnew( EditorFileDialog );
+ add_child(file);
+
+
+ tree = memnew( ItemList );
+ tree->set_v_size_flags(SIZE_EXPAND_FILL);
+ tree->set_icon_mode(ItemList::ICON_MODE_TOP);
+
+ int thumbnail_size = 96;
+ tree->set_max_columns(0);
+ tree->set_icon_mode(ItemList::ICON_MODE_TOP);
+ tree->set_fixed_column_width(thumbnail_size*3/2);
+ tree->set_max_text_lines(2);
+ tree->set_fixed_icon_size(Size2(thumbnail_size,thumbnail_size));
+ //tree->set_min_icon_size(Size2(thumbnail_size,thumbnail_size));
+ tree->set_drag_forwarding(this);
+
+
+
+ sub_vb->add_child(tree);
+
+ 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");
+ empty2->connect("pressed", this,"_empty2_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_selected", this,"_item_edited");
+ loading_scene=false;
+ sel=-1;
+
+ updating=false;
+
+ edited_anim="default";
+
+}
+
+
+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) {
+ button->show();
+ editor->make_bottom_panel_item_visible(frames_editor);
+// frames_editor->set_process(true);
+ } else {
+
+ button->hide();
+ if (frames_editor->is_visible())
+ editor->hide_bottom_panel();
+
+// frames_editor->set_process(false);
+ }
+
+}
+
+SpriteFramesEditorPlugin::SpriteFramesEditorPlugin(EditorNode *p_node) {
+
+ editor=p_node;
+ frames_editor = memnew( SpriteFramesEditor );
+ frames_editor->set_custom_minimum_size(Size2(0,300));
+ button=editor->add_bottom_panel_item("SpriteFrames",frames_editor);
+ button->hide();
+
+
+
+}
+
+
+SpriteFramesEditorPlugin::~SpriteFramesEditorPlugin()
+{
+}
+
+
diff --git a/editor/plugins/sprite_frames_editor_plugin.h b/editor/plugins/sprite_frames_editor_plugin.h
new file mode 100644
index 000000000..a104fa4f9
--- /dev/null
+++ b/editor/plugins/sprite_frames_editor_plugin.h
@@ -0,0 +1,138 @@
+/*************************************************************************/
+/* sprite_frames_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "editor/editor_plugin.h"
+#include "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"
+#include "scene/gui/split_container.h"
+
+
+class SpriteFramesEditor : public PanelContainer {
+
+ OBJ_TYPE(SpriteFramesEditor, PanelContainer );
+
+ Button *load;
+ Button *_delete;
+ Button *paste;
+ Button *empty;
+ Button *empty2;
+ Button *move_up;
+ Button *move_down;
+ ItemList *tree;
+ bool loading_scene;
+ int sel;
+
+ HSplitContainer *split;
+ Button *new_anim;
+ Button *remove_anim;
+
+
+ Tree *animations;
+ SpinBox *anim_speed;
+ CheckButton *anim_loop;
+
+ EditorFileDialog *file;
+
+ AcceptDialog *dialog;
+
+ SpriteFrames *frames;
+
+ StringName edited_anim;
+
+ void _load_pressed();
+ void _load_scene_pressed();
+ void _file_load_request(const DVector<String>& p_path, int p_at_pos=-1);
+ void _paste_pressed();
+ void _empty_pressed();
+ void _empty2_pressed();
+ void _delete_pressed();
+ void _delete_confirm_pressed();
+ void _up_pressed();
+ void _down_pressed();
+ void _update_library(bool p_skip_selector=false);
+ void _item_edited();
+
+
+
+ void _animation_select();
+ void _animation_name_edited();
+ void _animation_add();
+ void _animation_remove();
+ void _animation_loop_changed();
+ void _animation_fps_changed(double p_value);
+
+ bool updating;
+
+ UndoRedo *undo_redo;
+
+ bool _is_drop_valid(const Dictionary& p_drag_data, const Dictionary& p_item_data) const;
+ Variant get_drag_data_fw(const Point2& p_point,Control* p_from);
+ bool can_drop_data_fw(const Point2& p_point,const Variant& p_data,Control* p_from) const;
+ void drop_data_fw(const Point2& p_point,const Variant& p_data,Control* p_from);
+
+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;
+ Button *button;
+
+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/editor/plugins/stream_editor_plugin.cpp b/editor/plugins/stream_editor_plugin.cpp
new file mode 100644
index 000000000..06ae4c33c
--- /dev/null
+++ b/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-2017 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_TREE) {
+ 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/editor/plugins/stream_editor_plugin.h b/editor/plugins/stream_editor_plugin.h
new file mode 100644
index 000000000..a28dce2f1
--- /dev/null
+++ b/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-2017 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 "editor/editor_plugin.h"
+#include "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/editor/plugins/style_box_editor_plugin.cpp b/editor/plugins/style_box_editor_plugin.cpp
new file mode 100644
index 000000000..b14046ff0
--- /dev/null
+++ b/editor/plugins/style_box_editor_plugin.cpp
@@ -0,0 +1,115 @@
+/*************************************************************************/
+/* style_box_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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(TTR("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) {
+ button->show();
+ EditorNode::get_singleton()->make_bottom_panel_item_visible(stylebox_editor);
+
+ } else {
+ if (stylebox_editor->is_visible())
+ EditorNode::get_singleton()->hide_bottom_panel();
+ button->hide();
+ }
+}
+
+StyleBoxEditorPlugin::StyleBoxEditorPlugin(EditorNode *p_node) {
+
+ stylebox_editor = memnew( StyleBoxEditor );
+ stylebox_editor->set_custom_minimum_size(Size2(0,250));
+
+ //p_node->get_viewport()->add_child(stylebox_editor);
+ button = p_node->add_bottom_panel_item("StyleBox",stylebox_editor);
+ button->hide();
+
+
+}
+
diff --git a/editor/plugins/style_box_editor_plugin.h b/editor/plugins/style_box_editor_plugin.h
new file mode 100644
index 000000000..ad8ea80c1
--- /dev/null
+++ b/editor/plugins/style_box_editor_plugin.h
@@ -0,0 +1,83 @@
+/*************************************************************************/
+/* style_box_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "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;
+ Button *button;
+
+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/editor/plugins/texture_editor_plugin.cpp b/editor/plugins/texture_editor_plugin.cpp
new file mode 100644
index 000000000..fc20750ed
--- /dev/null
+++ b/editor/plugins/texture_editor_plugin.cpp
@@ -0,0 +1,141 @@
+#include "texture_editor_plugin.h"
+
+#include "io/resource_loader.h"
+#include "globals.h"
+#include "editor/editor_settings.h"
+
+void TextureEditor::_input_event(InputEvent p_event) {
+
+
+}
+
+void TextureEditor::_notification(int p_what) {
+
+ if (p_what==NOTIFICATION_FIXED_PROCESS) {
+
+ }
+
+
+ if (p_what==NOTIFICATION_READY) {
+
+ //get_scene()->connect("node_removed",this,"_node_removed");
+
+ }
+
+ if (p_what==NOTIFICATION_DRAW) {
+
+
+ Ref<Texture> checkerboard = get_icon("Checkerboard","EditorIcons");
+ Size2 size = get_size();
+
+ draw_texture_rect(checkerboard,Rect2(Point2(),size),true);
+
+ int tex_width = texture->get_width() * size.height / texture ->get_height();
+ int tex_height = size.height;
+
+ if (tex_width>size.width) {
+ tex_width=size.width;
+ tex_height=texture->get_height() * tex_width / texture->get_width();
+ }
+
+ int ofs_x = (size.width - tex_width)/2;
+ int ofs_y = (size.height - tex_height)/2;
+
+ draw_texture_rect(texture,Rect2(ofs_x,ofs_y,tex_width,tex_height));
+
+ Ref<Font> font = get_font("font","Label");
+
+ String format;
+ if (texture->cast_to<ImageTexture>()) {
+ format = Image::get_format_name(texture->cast_to<ImageTexture>()->get_format());
+ } else {
+ format=texture->get_type();
+ }
+ String text = itos(texture->get_width())+"x"+itos(texture->get_height())+" "+format;
+
+ Size2 rect = font->get_string_size(text);
+
+ Vector2 draw_from = size-rect+Size2(-2,font->get_ascent()-2);
+ if (draw_from.x<0)
+ draw_from.x=0;
+
+ draw_string(font,draw_from+Vector2(2,2),text,Color(0,0,0,0.5),size.width);
+ draw_string(font,draw_from-Vector2(2,2),text,Color(0,0,0,0.5),size.width);
+ draw_string(font,draw_from,text,Color(1,1,1,1),size.width);
+ }
+}
+
+
+
+void TextureEditor::edit(Ref<Texture> p_texture) {
+
+ texture=p_texture;
+
+ if (!texture.is_null())
+ update();
+ else {
+
+ hide();
+ }
+
+}
+
+
+
+void TextureEditor::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("_input_event"),&TextureEditor::_input_event);
+
+}
+
+TextureEditor::TextureEditor() {
+
+ set_custom_minimum_size(Size2(1,150));
+
+}
+
+
+void TextureEditorPlugin::edit(Object *p_object) {
+
+ Texture * s = p_object->cast_to<Texture>();
+ if (!s)
+ return;
+
+ texture_editor->edit(Ref<Texture>(s));
+}
+
+bool TextureEditorPlugin::handles(Object *p_object) const {
+
+ return p_object->is_type("Texture");
+}
+
+void TextureEditorPlugin::make_visible(bool p_visible) {
+
+ if (p_visible) {
+ texture_editor->show();
+// texture_editor->set_process(true);
+ } else {
+
+ texture_editor->hide();
+// texture_editor->set_process(false);
+ }
+
+}
+
+TextureEditorPlugin::TextureEditorPlugin(EditorNode *p_node) {
+
+ editor=p_node;
+ texture_editor = memnew( TextureEditor );
+ add_control_to_container(CONTAINER_PROPERTY_EDITOR_BOTTOM,texture_editor);
+ texture_editor->hide();
+
+
+
+}
+
+
+TextureEditorPlugin::~TextureEditorPlugin()
+{
+}
+
+
diff --git a/editor/plugins/texture_editor_plugin.h b/editor/plugins/texture_editor_plugin.h
new file mode 100644
index 000000000..af2ce92fe
--- /dev/null
+++ b/editor/plugins/texture_editor_plugin.h
@@ -0,0 +1,49 @@
+#ifndef TEXTURE_EDITOR_PLUGIN_H
+#define TEXTURE_EDITOR_PLUGIN_H
+
+
+
+#include "editor/editor_plugin.h"
+#include "editor/editor_node.h"
+#include "scene/resources/texture.h"
+
+
+class TextureEditor : public Control {
+
+ OBJ_TYPE(TextureEditor, Control);
+
+
+ Ref<Texture> texture;
+
+protected:
+ void _notification(int p_what);
+ void _input_event(InputEvent p_event);
+ static void _bind_methods();
+public:
+
+ void edit(Ref<Texture> p_texture);
+ TextureEditor();
+};
+
+
+class TextureEditorPlugin : public EditorPlugin {
+
+ OBJ_TYPE( TextureEditorPlugin, EditorPlugin );
+
+ TextureEditor *texture_editor;
+ EditorNode *editor;
+
+public:
+
+ virtual String get_name() const { return "Texture"; }
+ bool has_main_screen() const { return false; }
+ virtual void edit(Object *p_node);
+ virtual bool handles(Object *p_node) const;
+ virtual void make_visible(bool p_visible);
+
+ TextureEditorPlugin(EditorNode *p_node);
+ ~TextureEditorPlugin();
+
+};
+
+#endif // TEXTURE_EDITOR_PLUGIN_H
diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp
new file mode 100644
index 000000000..3b9b38700
--- /dev/null
+++ b/editor/plugins/texture_region_editor_plugin.cpp
@@ -0,0 +1,1010 @@
+/*************************************************************************/
+/* texture_region_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Author: Mariano Suligoy */
+/* */
+/* 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 "core/core_string_names.h"
+#include "texture_region_editor_plugin.h"
+#include "scene/gui/check_box.h"
+#include "os/input.h"
+#include "os/keyboard.h"
+
+void draw_margin_line(Control *edit_draw, Vector2 from, Vector2 to){
+ Vector2 line = (to-from).normalized() * 10;
+ while ((to - from).length_squared() > 200) {
+ edit_draw->draw_line(from, from + line,Color(0.97, 0.2, 0.2),2);
+ from += line*2;
+ }
+}
+
+void TextureRegionEditor::_region_draw()
+{
+ Ref<Texture> base_tex = NULL;
+ if(node_sprite)
+ base_tex = node_sprite->get_texture();
+ else if(node_patch9)
+ base_tex = node_patch9->get_texture();
+ else if(obj_styleBox.is_valid())
+ base_tex = obj_styleBox->get_texture();
+ else if(atlas_tex.is_valid())
+ base_tex = atlas_tex->get_atlas();
+ if (base_tex.is_null())
+ return;
+
+ Matrix32 mtx;
+ mtx.elements[2]=-draw_ofs;
+ mtx.scale_basis(Vector2(draw_zoom,draw_zoom));
+
+ VS::get_singleton()->canvas_item_set_clip(edit_draw->get_canvas_item(),true);
+ VS::get_singleton()->canvas_item_add_set_transform(edit_draw->get_canvas_item(),mtx);
+ edit_draw->draw_texture(base_tex,Point2());
+ VS::get_singleton()->canvas_item_add_set_transform(edit_draw->get_canvas_item(),Matrix32());
+
+ if (snap_mode == SNAP_GRID) {
+ Size2 s = edit_draw->get_size();
+ int last_cell;
+
+ if (snap_step.x!=0) {
+ if (snap_separation.x == 0)
+ for(int i=0;i<s.width;i++) {
+ int cell = Math::fast_ftoi(Math::floor((mtx.affine_inverse().xform(Vector2(i,0)).x-snap_offset.x)/snap_step.x));
+ if (i==0)
+ last_cell=cell;
+ if (last_cell!=cell)
+ edit_draw->draw_line(Point2(i,0),Point2(i,s.height),Color(0.3,0.7,1,0.3));
+ last_cell=cell;
+ }
+ else
+ for(int i=0;i<s.width;i++) {
+ int cell = Math::fast_ftoi(Math::floor((mtx.affine_inverse().xform(Vector2(i,0)).x-snap_offset.x)/(snap_step.x+snap_separation.x)));
+ if (i==0)
+ last_cell=cell;
+ if (last_cell!=cell)
+ edit_draw->draw_rect(Rect2(i-snap_separation.x*draw_zoom,0,snap_separation.x*draw_zoom,s.height),Color(0.3,0.7,1,0.3));
+ last_cell=cell;
+ }
+ }
+
+ if (snap_step.y!=0) {
+ if (snap_separation.y == 0)
+ for(int i=0;i<s.height;i++) {
+ int cell = Math::fast_ftoi(Math::floor((mtx.affine_inverse().xform(Vector2(0,i)).y-snap_offset.y)/snap_step.y));
+ if (i==0)
+ last_cell=cell;
+ if (last_cell!=cell)
+ edit_draw->draw_line(Point2(0,i),Point2(s.width,i),Color(0.3,0.7,1,0.3));
+ last_cell=cell;
+ }
+ else
+ for(int i=0;i<s.height;i++) {
+ int cell = Math::fast_ftoi(Math::floor((mtx.affine_inverse().xform(Vector2(0,i)).y-snap_offset.y)/(snap_step.y+snap_separation.y)));
+ if (i==0)
+ last_cell=cell;
+ if (last_cell!=cell)
+ edit_draw->draw_rect(Rect2(0,i-snap_separation.y*draw_zoom,s.width,snap_separation.y*draw_zoom),Color(0.3,0.7,1,0.3));
+ last_cell=cell;
+ }
+ }
+ } else if (snap_mode == SNAP_AUTOSLICE) {
+ for (List<Rect2>::Element *E = autoslice_cache.front();E;E=E->next()) {
+ Rect2 r = E->get();
+ Vector2 endpoints[4]={
+ mtx.basis_xform(r.pos),
+ mtx.basis_xform(r.pos+Vector2(r.size.x,0)),
+ mtx.basis_xform(r.pos+r.size),
+ mtx.basis_xform(r.pos+Vector2(0,r.size.y))
+ };
+ for(int i=0;i<4;i++) {
+ int next = (i+1)%4;
+ edit_draw->draw_line(endpoints[i]-draw_ofs, endpoints[next]-draw_ofs, Color(0.3,0.7,1,1) , 2);
+ }
+ }
+ }
+
+ Ref<Texture> select_handle = get_icon("EditorHandle","EditorIcons");
+
+ Rect2 scroll_rect(Point2(),mtx.basis_xform(base_tex->get_size()));
+ scroll_rect.expand_to(mtx.basis_xform(edit_draw->get_size()));
+
+ Vector2 endpoints[4]={
+ mtx.basis_xform(rect.pos),
+ mtx.basis_xform(rect.pos+Vector2(rect.size.x,0)),
+ mtx.basis_xform(rect.pos+rect.size),
+ mtx.basis_xform(rect.pos+Vector2(0,rect.size.y))
+ };
+ Color color(0.9,0.5,0.5);
+ 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);
+
+ edit_draw->draw_line(endpoints[i]-draw_ofs, endpoints[next]-draw_ofs, color , 2);
+
+ if (snap_mode != SNAP_AUTOSLICE)
+ edit_draw->draw_texture(select_handle,(endpoints[i]+ofs-(select_handle->get_size()/2)).floor()-draw_ofs);
+
+ ofs = (endpoints[next]-endpoints[i])/2;
+ ofs += (endpoints[next]-endpoints[i]).tangent().normalized()*(select_handle->get_size().width/2);
+
+ if (snap_mode != SNAP_AUTOSLICE)
+ edit_draw->draw_texture(select_handle,(endpoints[i]+ofs-(select_handle->get_size()/2)).floor()-draw_ofs);
+
+ scroll_rect.expand_to(endpoints[i]);
+ }
+
+ scroll_rect=scroll_rect.grow(200);
+ updating_scroll=true;
+ hscroll->set_min(scroll_rect.pos.x);
+ hscroll->set_max(scroll_rect.pos.x+scroll_rect.size.x);
+ hscroll->set_page(edit_draw->get_size().x);
+ hscroll->set_val(draw_ofs.x);
+ hscroll->set_step(0.001);
+
+ vscroll->set_min(scroll_rect.pos.y);
+ vscroll->set_max(scroll_rect.pos.y+scroll_rect.size.y);
+ vscroll->set_page(edit_draw->get_size().y);
+ vscroll->set_val(draw_ofs.y);
+ vscroll->set_step(0.001);
+ updating_scroll=false;
+
+ float margins[4];
+ if (node_patch9 || obj_styleBox.is_valid()) {
+ if (node_patch9) {
+ margins[0] = node_patch9->get_patch_margin(MARGIN_TOP);
+ margins[1] = node_patch9->get_patch_margin(MARGIN_BOTTOM);
+ margins[2] = node_patch9->get_patch_margin(MARGIN_LEFT);
+ margins[3] = node_patch9->get_patch_margin(MARGIN_RIGHT);
+ } else if (obj_styleBox.is_valid()) {
+ margins[0] = obj_styleBox->get_margin_size(MARGIN_TOP);
+ margins[1] = obj_styleBox->get_margin_size(MARGIN_BOTTOM);
+ margins[2] = obj_styleBox->get_margin_size(MARGIN_LEFT);
+ margins[3] = obj_styleBox->get_margin_size(MARGIN_RIGHT);
+ }
+ Vector2 pos[4] = {
+ mtx.basis_xform(Vector2(0,margins[0]))+Vector2(0,endpoints[0].y-draw_ofs.y),
+ -mtx.basis_xform(Vector2(0,margins[1]))+Vector2(0,endpoints[2].y-draw_ofs.y),
+ mtx.basis_xform(Vector2(margins[2],0))+Vector2(endpoints[0].x-draw_ofs.x,0),
+ -mtx.basis_xform(Vector2(margins[3],0))+Vector2(endpoints[2].x-draw_ofs.x,0)};
+
+ draw_margin_line(edit_draw,pos[0],pos[0]+Vector2(edit_draw->get_size().x,0));
+ draw_margin_line(edit_draw,pos[1],pos[1]+Vector2(edit_draw->get_size().x,0));
+ draw_margin_line(edit_draw,pos[2],pos[2]+Vector2(0,edit_draw->get_size().y));
+ draw_margin_line(edit_draw,pos[3],pos[3]+Vector2(0,edit_draw->get_size().y));
+ }
+}
+
+void TextureRegionEditor::_region_input(const InputEvent& p_input)
+{
+ Matrix32 mtx;
+ mtx.elements[2]=-draw_ofs;
+ mtx.scale_basis(Vector2(draw_zoom,draw_zoom));
+
+ Vector2 endpoints[8]={
+ mtx.xform(rect.pos)+Vector2(-4,-4),
+ mtx.xform(rect.pos+Vector2(rect.size.x/2,0))+Vector2(0,-4),
+ mtx.xform(rect.pos+Vector2(rect.size.x,0))+Vector2(4,-4),
+ mtx.xform(rect.pos+Vector2(rect.size.x,rect.size.y/2))+Vector2(4,0),
+ mtx.xform(rect.pos+rect.size)+Vector2(4,4),
+ mtx.xform(rect.pos+Vector2(rect.size.x/2,rect.size.y))+Vector2(0,4),
+ mtx.xform(rect.pos+Vector2(0,rect.size.y))+Vector2(-4,4),
+ mtx.xform(rect.pos+Vector2(0,rect.size.y/2))+Vector2(-4,0)
+ };
+
+ if (p_input.type==InputEvent::MOUSE_BUTTON) {
+
+ const InputEventMouseButton &mb=p_input.mouse_button;
+
+ if (mb.button_index==BUTTON_LEFT) {
+
+ if (mb.pressed) {
+ if (node_patch9 || obj_styleBox.is_valid()) {
+ edited_margin = -1;
+ float margins[4];
+ if (node_patch9) {
+ margins[0] = node_patch9->get_patch_margin(MARGIN_TOP);
+ margins[1] = node_patch9->get_patch_margin(MARGIN_BOTTOM);
+ margins[2] = node_patch9->get_patch_margin(MARGIN_LEFT);
+ margins[3] = node_patch9->get_patch_margin(MARGIN_RIGHT);
+ } else if (obj_styleBox.is_valid()) {
+ margins[0] = obj_styleBox->get_margin_size(MARGIN_TOP);
+ margins[1] = obj_styleBox->get_margin_size(MARGIN_BOTTOM);
+ margins[2] = obj_styleBox->get_margin_size(MARGIN_LEFT);
+ margins[3] = obj_styleBox->get_margin_size(MARGIN_RIGHT);
+ }
+ Vector2 pos[4] = {
+ mtx.basis_xform(rect.pos+Vector2(0,margins[0]))-draw_ofs,
+ mtx.basis_xform(rect.pos+rect.size-Vector2(0,margins[1]))-draw_ofs,
+ mtx.basis_xform(rect.pos+Vector2(margins[2],0))-draw_ofs,
+ mtx.basis_xform(rect.pos+rect.size-Vector2(margins[3],0))-draw_ofs};
+ if (Math::abs(mb.y - pos[0].y) < 8) {
+ edited_margin = 0;
+ prev_margin = margins[0];
+ } else if (Math::abs(mb.y - pos[1].y) < 8) {
+ edited_margin = 1;
+ prev_margin = margins[1];
+ } else if (Math::abs(mb.x - pos[2].x) < 8) {
+ edited_margin = 2;
+ prev_margin = margins[2];
+ } else if (Math::abs(mb.x - pos[3].x) < 8) {
+ edited_margin = 3;
+ prev_margin = margins[3];
+ }
+ if (edited_margin >= 0) {
+ drag_from=Vector2(mb.x,mb.y);
+ drag=true;
+ }
+ }
+ if ( edited_margin < 0 && snap_mode == SNAP_AUTOSLICE) {
+ Vector2 point = mtx.affine_inverse().xform(Vector2(mb.x,mb.y));
+ for (List<Rect2>::Element *E=autoslice_cache.front();E;E=E->next()) {
+ if (E->get().has_point(point)) {
+ rect = E->get();
+ if (Input::get_singleton()->is_key_pressed(KEY_CONTROL)&&!(Input::get_singleton()->is_key_pressed(KEY_SHIFT|KEY_ALT))) {
+ Rect2 r;
+ if(node_sprite )
+ r=node_sprite->get_region_rect();
+ else if(node_patch9)
+ r=node_patch9->get_region_rect();
+ else if(obj_styleBox.is_valid())
+ r=obj_styleBox->get_region_rect();
+ else if(atlas_tex.is_valid())
+ r =atlas_tex->get_region();
+ rect.expand_to(r.pos);
+ rect.expand_to(r.pos+r.size);
+ }
+ undo_redo->create_action("Set Region Rect");
+ if(node_sprite){
+ undo_redo->add_do_method(node_sprite ,"set_region_rect",rect);
+ undo_redo->add_undo_method(node_sprite,"set_region_rect",node_sprite->get_region_rect());
+ } else if(node_patch9){
+ undo_redo->add_do_method(node_patch9 ,"set_region_rect",rect);
+ undo_redo->add_undo_method(node_patch9,"set_region_rect",node_patch9->get_region_rect());
+ } else if (obj_styleBox.is_valid()) {
+ undo_redo->add_do_method(obj_styleBox.ptr(),"set_region_rect",rect);
+ undo_redo->add_undo_method(obj_styleBox.ptr(),"set_region_rect",obj_styleBox->get_region_rect());
+ } else if (atlas_tex.is_valid()) {
+ undo_redo->add_do_method(atlas_tex.ptr(),"set_region",rect);
+ undo_redo->add_undo_method(atlas_tex.ptr(),"set_region",atlas_tex->get_region());
+ }
+ undo_redo->add_do_method(edit_draw,"update");
+ undo_redo->add_undo_method(edit_draw,"update");
+ undo_redo->commit_action();
+ break;
+ }
+ }
+ } else if (edited_margin < 0) {
+ drag_from=mtx.affine_inverse().xform(Vector2(mb.x,mb.y));
+ if (snap_mode == SNAP_PIXEL)
+ drag_from = drag_from.snapped(Vector2(1,1));
+ else if (snap_mode == SNAP_GRID)
+ drag_from=snap_point(drag_from);
+ drag=true;
+ if(node_sprite )
+ rect_prev=node_sprite->get_region_rect();
+ else if(node_patch9)
+ rect_prev=node_patch9->get_region_rect();
+ else if(obj_styleBox.is_valid())
+ rect_prev=obj_styleBox->get_region_rect();
+ else if(atlas_tex.is_valid())
+ rect_prev=atlas_tex->get_region();
+
+ for (int i=0; i<8;i++) {
+ Vector2 tuv=endpoints[i];
+ if (tuv.distance_to(Vector2(mb.x,mb.y))<8) {
+ drag_index=i;
+ }
+ }
+
+ if (drag_index==-1) {
+ creating = true;
+ rect = Rect2(drag_from,Size2());
+ }
+ }
+
+ } else if (drag) {
+ if (edited_margin >= 0) {
+ undo_redo->create_action("Set Margin");
+ static Margin m[4] = {MARGIN_TOP,MARGIN_BOTTOM,MARGIN_LEFT,MARGIN_RIGHT};
+ if (node_patch9) {
+ undo_redo->add_do_method(node_patch9 ,"set_patch_margin",m[edited_margin],node_patch9->get_patch_margin(m[edited_margin]));
+ undo_redo->add_undo_method(node_patch9,"set_patch_margin",m[edited_margin],prev_margin);
+ } else if (obj_styleBox.is_valid()) {
+ undo_redo->add_do_method(obj_styleBox.ptr() ,"set_margin_size",m[edited_margin],obj_styleBox->get_margin_size(m[edited_margin]));
+ undo_redo->add_undo_method(obj_styleBox.ptr(),"set_margin_size",m[edited_margin],prev_margin);
+ obj_styleBox->emit_signal(CoreStringNames::get_singleton()->changed);
+ }
+ edited_margin = -1;
+ } else {
+ undo_redo->create_action("Set Region Rect");
+ if(node_sprite){
+ undo_redo->add_do_method(node_sprite ,"set_region_rect",node_sprite->get_region_rect());
+ undo_redo->add_undo_method(node_sprite,"set_region_rect",rect_prev);
+ }
+ else if(atlas_tex.is_valid()){
+ undo_redo->add_do_method(atlas_tex.ptr() ,"set_region",atlas_tex->get_region());
+ undo_redo->add_undo_method(atlas_tex.ptr(),"set_region",rect_prev);
+ }
+ else if(node_patch9){
+ } else if(node_patch9){
+ undo_redo->add_do_method(node_patch9 ,"set_region_rect",node_patch9->get_region_rect());
+ undo_redo->add_undo_method(node_patch9,"set_region_rect",rect_prev);
+ } else if (obj_styleBox.is_valid()) {
+ undo_redo->add_do_method(obj_styleBox.ptr() ,"set_region_rect",obj_styleBox->get_region_rect());
+ undo_redo->add_undo_method(obj_styleBox.ptr(),"set_region_rect",rect_prev);
+ }
+ drag_index = -1;
+ }
+ undo_redo->add_do_method(edit_draw,"update");
+ undo_redo->add_undo_method(edit_draw,"update");
+ undo_redo->commit_action();
+ drag=false;
+ creating = false;
+ }
+
+ } else if (mb.button_index==BUTTON_RIGHT && mb.pressed) {
+
+ if (drag) {
+ drag=false;
+ if (edited_margin >= 0) {
+ static Margin m[4] = {MARGIN_TOP,MARGIN_BOTTOM,MARGIN_LEFT,MARGIN_RIGHT};
+ if (node_patch9)
+ node_patch9->set_patch_margin(m[edited_margin],prev_margin);
+ if (obj_styleBox.is_valid())
+ obj_styleBox->set_margin_size(m[edited_margin],prev_margin);
+ edited_margin = -1;
+ } else {
+ apply_rect(rect_prev);
+ rect=rect_prev;
+ edit_draw->update();
+ drag_index = -1;
+ }
+ }
+ } else if (mb.button_index == BUTTON_WHEEL_UP && mb.pressed) {
+ _zoom_in();
+ } else if (mb.button_index == BUTTON_WHEEL_DOWN && mb.pressed) {
+ _zoom_out();
+ }
+ } else if (p_input.type==InputEvent::MOUSE_MOTION) {
+
+ const InputEventMouseMotion &mm=p_input.mouse_motion;
+
+ if (mm.button_mask&BUTTON_MASK_MIDDLE || Input::get_singleton()->is_key_pressed(KEY_SPACE)) {
+
+ Vector2 draged(mm.relative_x,mm.relative_y);
+ hscroll->set_val( hscroll->get_val()-draged.x );
+ vscroll->set_val( vscroll->get_val()-draged.y );
+
+ } else if (drag) {
+
+ if (edited_margin >= 0) {
+ float new_margin;
+ if (edited_margin == 0)
+ new_margin = prev_margin + (mm.y-drag_from.y) / draw_zoom;
+ else if (edited_margin == 1)
+ new_margin = prev_margin - (mm.y-drag_from.y) / draw_zoom;
+ else if (edited_margin == 2)
+ new_margin = prev_margin + (mm.x-drag_from.x) / draw_zoom;
+ else if (edited_margin == 3)
+ new_margin = prev_margin - (mm.x-drag_from.x) / draw_zoom;
+ if (new_margin < 0)
+ new_margin = 0;
+ static Margin m[4] = {MARGIN_TOP,MARGIN_BOTTOM,MARGIN_LEFT,MARGIN_RIGHT};
+ if (node_patch9)
+ node_patch9->set_patch_margin(m[edited_margin],new_margin);
+ if (obj_styleBox.is_valid())
+ obj_styleBox->set_margin_size(m[edited_margin],new_margin);
+ } else {
+ Vector2 new_pos = mtx.affine_inverse().xform(Vector2(mm.x,mm.y));
+ if (snap_mode == SNAP_PIXEL)
+ new_pos = new_pos.snapped(Vector2(1,1));
+ else if (snap_mode == SNAP_GRID)
+ new_pos=snap_point(new_pos);
+
+ if (creating) {
+ rect = Rect2(drag_from,Size2());
+ rect.expand_to(new_pos);
+ apply_rect(rect);
+ edit_draw->update();
+ return;
+ }
+
+ switch(drag_index) {
+ case 0: {
+ Vector2 p=rect_prev.pos+rect_prev.size;
+ rect = Rect2(p,Size2());
+ rect.expand_to(new_pos);
+ apply_rect(rect);
+ } break;
+ case 1: {
+ Vector2 p=rect_prev.pos+Vector2(0,rect_prev.size.y);
+ rect = Rect2(p,Size2(rect_prev.size.x,0));
+ rect.expand_to(new_pos);
+ apply_rect(rect);
+ } break;
+ case 2: {
+ Vector2 p=rect_prev.pos+Vector2(0,rect_prev.size.y);
+ rect = Rect2(p,Size2());
+ rect.expand_to(new_pos);
+ apply_rect(rect);
+ } break;
+ case 3: {
+ Vector2 p=rect_prev.pos;
+ rect = Rect2(p,Size2(0,rect_prev.size.y));
+ rect.expand_to(new_pos);
+ apply_rect(rect);
+ } break;
+ case 4: {
+ Vector2 p=rect_prev.pos;
+ rect = Rect2(p,Size2());
+ rect.expand_to(new_pos);
+ apply_rect(rect);
+ } break;
+ case 5: {
+ Vector2 p=rect_prev.pos;
+ rect = Rect2(p,Size2(rect_prev.size.x,0));
+ rect.expand_to(new_pos);
+ apply_rect(rect);
+ } break;
+ case 6: {
+ Vector2 p=rect_prev.pos+Vector2(rect_prev.size.x,0);
+ rect = Rect2(p,Size2());
+ rect.expand_to(new_pos);
+ apply_rect(rect);
+ } break;
+ case 7: {
+ Vector2 p=rect_prev.pos+Vector2(rect_prev.size.x,0);
+ rect = Rect2(p,Size2(0,rect_prev.size.y));
+ rect.expand_to(new_pos);
+ apply_rect(rect);
+ } break;
+
+ }
+ }
+ edit_draw->update();
+ }
+
+ }
+}
+
+void TextureRegionEditor::_scroll_changed(float)
+{
+ if (updating_scroll)
+ return;
+
+ draw_ofs.x=hscroll->get_val();
+ draw_ofs.y=vscroll->get_val();
+ edit_draw->update();
+}
+
+void TextureRegionEditor::_set_snap_mode(int p_mode)
+{
+ snap_mode_button->get_popup()->set_item_checked(snap_mode,false);
+ snap_mode = p_mode;
+ snap_mode_button->set_text(snap_mode_button->get_popup()->get_item_text(p_mode));
+ snap_mode_button->get_popup()->set_item_checked(snap_mode,true);
+
+ if (snap_mode == SNAP_GRID)
+ hb_grid->show();
+ else
+ hb_grid->hide();
+
+ edit_draw->update();
+}
+
+void TextureRegionEditor::_set_snap_off_x(float p_val)
+{
+ snap_offset.x=p_val;
+ edit_draw->update();
+}
+
+void TextureRegionEditor::_set_snap_off_y(float p_val)
+{
+ snap_offset.y=p_val;
+ edit_draw->update();
+}
+
+void TextureRegionEditor::_set_snap_step_x(float p_val)
+{
+ snap_step.x=p_val;
+ edit_draw->update();
+}
+
+void TextureRegionEditor::_set_snap_step_y(float p_val)
+{
+ snap_step.y=p_val;
+ edit_draw->update();
+}
+
+void TextureRegionEditor::_set_snap_sep_x(float p_val)
+{
+ snap_separation.x = p_val;
+ edit_draw->update();
+}
+
+void TextureRegionEditor::_set_snap_sep_y(float p_val)
+{
+ snap_separation.y = p_val;
+ edit_draw->update();
+}
+
+void TextureRegionEditor::_zoom_in()
+{
+ if (draw_zoom < 8) {
+ draw_zoom *= 2;
+ edit_draw->update();
+ }
+}
+
+void TextureRegionEditor::_zoom_reset()
+{
+ if (draw_zoom == 1) return;
+ draw_zoom = 1;
+ edit_draw->update();
+}
+
+void TextureRegionEditor::_zoom_out()
+{
+ if (draw_zoom > 0.25) {
+ draw_zoom /= 2;
+ edit_draw->update();
+ }
+}
+
+void TextureRegionEditor::apply_rect(const Rect2& rect){
+ if(node_sprite)
+ node_sprite->set_region_rect(rect);
+ else if(node_patch9)
+ node_patch9->set_region_rect(rect);
+ else if(obj_styleBox.is_valid())
+ obj_styleBox->set_region_rect(rect);
+ else if(atlas_tex.is_valid())
+ atlas_tex->set_region(rect);
+}
+
+void TextureRegionEditor::_notification(int p_what)
+{
+ switch(p_what) {
+ case NOTIFICATION_READY: {
+ zoom_out->set_icon(get_icon("ZoomLess", "EditorIcons"));
+ zoom_reset->set_icon(get_icon("ZoomReset", "EditorIcons"));
+ zoom_in->set_icon(get_icon("ZoomMore", "EditorIcons"));
+ icon_zoom->set_texture( get_icon("Zoom", "EditorIcons"));
+ } break;
+ }
+}
+
+void TextureRegionEditor::_node_removed(Object *p_obj)
+{
+ if(p_obj == node_sprite || p_obj == node_patch9 || p_obj == obj_styleBox.ptr() || p_obj == atlas_tex.ptr()) {
+ node_patch9 = NULL;
+ node_sprite = NULL;
+ obj_styleBox = Ref<StyleBox>(NULL);
+ atlas_tex = Ref<AtlasTexture>(NULL);
+ hide();
+ }
+}
+
+void TextureRegionEditor::_bind_methods()
+{
+ ObjectTypeDB::bind_method(_MD("_edit_region"),&TextureRegionEditor::_edit_region);
+ ObjectTypeDB::bind_method(_MD("_region_draw"),&TextureRegionEditor::_region_draw);
+ ObjectTypeDB::bind_method(_MD("_region_input"),&TextureRegionEditor::_region_input);
+ ObjectTypeDB::bind_method(_MD("_scroll_changed"),&TextureRegionEditor::_scroll_changed);
+ ObjectTypeDB::bind_method(_MD("_node_removed"),&TextureRegionEditor::_node_removed);
+ ObjectTypeDB::bind_method(_MD("_set_snap_mode"),&TextureRegionEditor::_set_snap_mode);
+ ObjectTypeDB::bind_method(_MD("_set_snap_off_x"),&TextureRegionEditor::_set_snap_off_x);
+ ObjectTypeDB::bind_method(_MD("_set_snap_off_y"),&TextureRegionEditor::_set_snap_off_y);
+ ObjectTypeDB::bind_method(_MD("_set_snap_step_x"),&TextureRegionEditor::_set_snap_step_x);
+ ObjectTypeDB::bind_method(_MD("_set_snap_step_y"),&TextureRegionEditor::_set_snap_step_y);
+ ObjectTypeDB::bind_method(_MD("_set_snap_sep_x"),&TextureRegionEditor::_set_snap_sep_x);
+ ObjectTypeDB::bind_method(_MD("_set_snap_sep_y"),&TextureRegionEditor::_set_snap_sep_y);
+ ObjectTypeDB::bind_method(_MD("_zoom_in"),&TextureRegionEditor::_zoom_in);
+ ObjectTypeDB::bind_method(_MD("_zoom_reset"),&TextureRegionEditor::_zoom_reset);
+ ObjectTypeDB::bind_method(_MD("_zoom_out"),&TextureRegionEditor::_zoom_out);
+}
+
+void TextureRegionEditor::edit(Object *p_obj)
+{
+ if (node_sprite && node_sprite->is_connected("texture_changed",this,"_edit_region"))
+ node_sprite->disconnect("texture_changed",this,"_edit_region");
+ if (node_patch9 && node_patch9->is_connected("texture_changed",this,"_edit_region"))
+ node_patch9->disconnect("texture_changed",this,"_edit_region");
+ if (obj_styleBox.is_valid() && obj_styleBox->is_connected("texture_changed",this,"_edit_region"))
+ obj_styleBox->disconnect("texture_changed",this,"_edit_region");
+ if (atlas_tex.is_valid() && atlas_tex->is_connected("atlas_changed",this,"_edit_region"))
+ atlas_tex->disconnect("atlas_changed",this,"_edit_region");
+ if (p_obj) {
+ node_sprite = p_obj->cast_to<Sprite>();
+ node_patch9 = p_obj->cast_to<Patch9Frame>();
+ if (p_obj->cast_to<StyleBoxTexture>())
+ obj_styleBox = Ref<StyleBoxTexture>(p_obj->cast_to<StyleBoxTexture>());
+ if (p_obj->cast_to<AtlasTexture>()) {
+ atlas_tex = Ref<AtlasTexture>(p_obj->cast_to<AtlasTexture>());
+ atlas_tex->connect("atlas_changed",this,"_edit_region");
+ } else {
+ p_obj->connect("texture_changed",this,"_edit_region");
+ }
+ p_obj->add_change_receptor(this);
+ p_obj->connect("exit_tree",this,"_node_removed",varray(p_obj),CONNECT_ONESHOT);
+ _edit_region();
+ } else {
+ if(node_sprite)
+ node_sprite->disconnect("exit_tree",this,"_node_removed");
+ else if(node_patch9)
+ node_patch9->disconnect("exit_tree",this,"_node_removed");
+ else if(obj_styleBox.is_valid())
+ obj_styleBox->disconnect("exit_tree",this,"_node_removed");
+ else if(atlas_tex.is_valid())
+ atlas_tex->disconnect("exit_tree",this,"_node_removed");
+
+ node_sprite = NULL;
+ node_patch9 = NULL;
+ obj_styleBox = Ref<StyleBoxTexture>(NULL);
+ atlas_tex = Ref<AtlasTexture>(NULL);
+ }
+ edit_draw->update();
+}
+
+void TextureRegionEditor::_changed_callback(Object *p_changed, const char *p_prop) {
+ if ((String)p_prop == "region_rect") {
+ _edit_region();
+ }
+}
+
+void TextureRegionEditor::_edit_region()
+{
+ Ref<Texture> texture = NULL;
+ if(node_sprite )
+ texture = node_sprite->get_texture();
+ else if(node_patch9 )
+ texture = node_patch9->get_texture();
+ else if(obj_styleBox.is_valid())
+ texture = obj_styleBox->get_texture();
+ else if(atlas_tex.is_valid())
+ texture = atlas_tex->get_atlas();
+
+ if (texture.is_null()) {
+ return;
+ }
+
+ autoslice_cache.clear();
+ Image i;
+ if (i.load(texture->get_path()) == OK) {
+ BitMap bm;
+ bm.create_from_image_alpha(i);
+ for (int y = 0; y < i.get_height(); y++) {
+ for (int x = 0; x < i.get_width(); x++) {
+ if (bm.get_bit(Point2(x,y))) {
+ bool found = false;
+ for (List<Rect2>::Element *E = autoslice_cache.front(); E; E=E->next()) {
+ Rect2 grown = E->get().grow(1.5);
+ if (grown.has_point(Point2(x,y))) {
+ E->get().expand_to(Point2(x,y));
+ E->get().expand_to(Point2(x+1,y+1));
+ x = E->get().pos.x+E->get().size.x-1;
+ bool merged = true;
+ while (merged) {
+ merged = false;
+ bool queue_erase = false;
+ for (List<Rect2>::Element *F = autoslice_cache.front(); F; F=F->next()) {
+ if (queue_erase){
+ autoslice_cache.erase(F->prev());
+ queue_erase = false;
+ }
+ if (F==E)
+ continue;
+ if (E->get().grow(1).intersects(F->get())) {
+ E->get().expand_to(F->get().pos);
+ E->get().expand_to(F->get().pos+F->get().size);
+ if (F->prev()) {
+ F=F->prev();
+ autoslice_cache.erase(F->next());
+ } else {
+ queue_erase = true;
+ //Cant delete the first rect in the list.
+ }
+ merged = true;
+ }
+ }
+ }
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ Rect2 new_rect(x,y,1,1);
+ autoslice_cache.push_back(new_rect);
+ }
+ }
+ }
+ }
+ }
+
+
+ if(node_sprite )
+ rect = node_sprite->get_region_rect();
+ else if(node_patch9 )
+ rect = node_patch9->get_region_rect();
+ else if(obj_styleBox.is_valid())
+ rect = obj_styleBox->get_region_rect();
+ else if (atlas_tex.is_valid())
+ rect = atlas_tex->get_region();
+
+ edit_draw->update();
+}
+
+inline float _snap_scalar(float p_offset, float p_step, float separation, float p_target) {
+ if (p_step != 0) {
+ float a = Math::stepify(p_target - p_offset, p_step+separation) + p_offset;
+ float b = a;
+ if (p_target >= 0)
+ b -= separation;
+ else
+ b += p_step;
+ return (Math::abs(p_target-a) < Math::abs(p_target-b)) ? a : b;
+ }
+ return p_target;
+}
+
+Vector2 TextureRegionEditor::snap_point(Vector2 p_target) const {
+ if (snap_mode == SNAP_GRID) {
+ p_target.x = _snap_scalar(snap_offset.x, snap_step.x, snap_separation.x, p_target.x);
+ p_target.y = _snap_scalar(snap_offset.y, snap_step.y, snap_separation.y, p_target.y);
+ }
+
+ return p_target;
+}
+
+TextureRegionEditor::TextureRegionEditor(EditorNode* p_editor)
+{
+ node_sprite = NULL;
+ node_patch9 = NULL;
+ obj_styleBox = Ref<StyleBoxTexture>(NULL);
+ atlas_tex = Ref<AtlasTexture>(NULL);
+ editor=p_editor;
+ undo_redo = editor->get_undo_redo();
+
+ snap_step=Vector2(10,10);
+ snap_separation = Vector2(0,0);
+ edited_margin = -1;
+ drag_index = -1;
+ drag=false;
+
+ VBoxContainer *main_vb = memnew( VBoxContainer );
+ add_child(main_vb);
+ main_vb->set_area_as_parent_rect(0);
+ HBoxContainer *hb_tools = memnew( HBoxContainer );
+ main_vb->add_child(hb_tools);
+
+ hb_tools->add_child(memnew( Label(TTR("Snap Mode:")) ));
+
+ snap_mode_button = memnew( MenuButton );
+ hb_tools->add_child(snap_mode_button);
+ snap_mode_button->set_text(TTR("<None>"));
+ PopupMenu *p = snap_mode_button->get_popup();
+ p->add_item(TTR("<None>"),0);
+ p->add_item(TTR("Pixel Snap"),1);
+ p->add_item(TTR("Grid Snap"),2);
+ p->add_item(TTR("Auto Slice"),3);
+ for (int i = 0; i < 4; i++)
+ p->set_item_as_checkable(i,true);
+ p->set_item_checked(0,true);
+ p->connect("item_pressed", this, "_set_snap_mode");
+ hb_grid = memnew( HBoxContainer );
+ hb_tools->add_child(hb_grid);
+ hb_grid->add_child( memnew( VSeparator ));
+
+ hb_grid->add_child( memnew( Label(TTR("Offset:")) ) );
+
+ sb_off_x = memnew( SpinBox );
+ sb_off_x->set_min(-256);
+ sb_off_x->set_max(256);
+ sb_off_x->set_step(1);
+ sb_off_x->set_val(snap_offset.x);
+ sb_off_x->set_suffix("px");
+ sb_off_x->connect("value_changed", this, "_set_snap_off_x");
+ hb_grid->add_child(sb_off_x);
+
+ sb_off_y = memnew( SpinBox );
+ sb_off_y->set_min(-256);
+ sb_off_y->set_max(256);
+ sb_off_y->set_step(1);
+ sb_off_y->set_val(snap_offset.y);
+ sb_off_y->set_suffix("px");
+ sb_off_y->connect("value_changed", this, "_set_snap_off_y");
+ hb_grid->add_child(sb_off_y);
+
+ hb_grid->add_child( memnew( VSeparator ));
+ hb_grid->add_child( memnew( Label(TTR("Step:")) ) );
+
+ sb_step_x = memnew( SpinBox );
+ sb_step_x->set_min(-256);
+ sb_step_x->set_max(256);
+ sb_step_x->set_step(1);
+ sb_step_x->set_val(snap_step.x);
+ sb_step_x->set_suffix("px");
+ sb_step_x->connect("value_changed", this, "_set_snap_step_x");
+ hb_grid->add_child(sb_step_x);
+
+ sb_step_y = memnew( SpinBox );
+ sb_step_y->set_min(-256);
+ sb_step_y->set_max(256);
+ sb_step_y->set_step(1);
+ sb_step_y->set_val(snap_step.y);
+ sb_step_y->set_suffix("px");
+ sb_step_y->connect("value_changed", this, "_set_snap_step_y");
+ hb_grid->add_child(sb_step_y);
+
+ hb_grid->add_child( memnew( VSeparator ));
+ hb_grid->add_child( memnew( Label(TTR("Separation:")) ) );
+
+ sb_sep_x = memnew( SpinBox );
+ sb_sep_x->set_min(0);
+ sb_sep_x->set_max(256);
+ sb_sep_x->set_step(1);
+ sb_sep_x->set_val(snap_separation.x);
+ sb_sep_x->set_suffix("px");
+ sb_sep_x->connect("value_changed", this, "_set_snap_sep_x");
+ hb_grid->add_child(sb_sep_x);
+
+ sb_sep_y = memnew( SpinBox );
+ sb_sep_y->set_min(0);
+ sb_sep_y->set_max(256);
+ sb_sep_y->set_step(1);
+ sb_sep_y->set_val(snap_separation.y);
+ sb_sep_y->set_suffix("px");
+ sb_sep_y->connect("value_changed", this, "_set_snap_sep_y");
+ hb_grid->add_child(sb_sep_y);
+
+ hb_grid->hide();
+
+ HBoxContainer *main_hb = memnew( HBoxContainer );
+ main_vb->add_child(main_hb);
+ edit_draw = memnew( Control );
+ main_hb->add_child(edit_draw);
+ main_hb->set_v_size_flags(SIZE_EXPAND_FILL);
+ edit_draw->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ Control * separator = memnew( Control );
+ separator->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ hb_tools->add_child(separator);
+
+ icon_zoom = memnew( TextureFrame );
+ hb_tools->add_child(icon_zoom);
+
+ zoom_out = memnew( ToolButton );
+ zoom_out->connect("pressed", this, "_zoom_out");
+ hb_tools->add_child(zoom_out);
+
+ zoom_reset = memnew( ToolButton );
+ zoom_reset->connect("pressed", this, "_zoom_reset");
+ hb_tools->add_child(zoom_reset);
+
+ zoom_in = memnew( ToolButton );
+ zoom_in->connect("pressed", this, "_zoom_in");
+ hb_tools->add_child(zoom_in);
+
+ vscroll = memnew( VScrollBar);
+ main_hb->add_child(vscroll);
+ vscroll->connect("value_changed",this,"_scroll_changed");
+ hscroll = memnew( HScrollBar );
+ main_vb->add_child(hscroll);
+ hscroll->connect("value_changed",this,"_scroll_changed");
+
+ edit_draw->connect("draw",this,"_region_draw");
+ edit_draw->connect("input_event",this,"_region_input");
+ draw_zoom=1.0;
+ updating_scroll=false;
+
+}
+
+void TextureRegionEditorPlugin::edit(Object *p_node)
+{
+ region_editor->edit(p_node);
+}
+
+bool TextureRegionEditorPlugin::handles(Object *p_obj) const
+{
+ return p_obj->is_type("Sprite") || p_obj->is_type("Patch9Frame") || p_obj->is_type("StyleBoxTexture") || p_obj->is_type("AtlasTexture");
+}
+
+void TextureRegionEditorPlugin::make_visible(bool p_visible)
+{
+ if (p_visible) {
+ region_button->show();
+ if (region_button->is_pressed())
+ region_editor->show();
+ } else {
+ region_button->hide();
+ region_editor->edit(NULL);
+ region_editor->hide();
+ }
+}
+
+
+Dictionary TextureRegionEditorPlugin::get_state() const {
+
+ Dictionary state;
+ state["zoom"]=region_editor->draw_zoom;
+ state["snap_offset"]=region_editor->snap_offset;
+ state["snap_step"]=region_editor->snap_step;
+ state["snap_separation"]=region_editor->snap_separation;
+ state["snap_mode"]=region_editor->snap_mode;
+ return state;
+}
+
+void TextureRegionEditorPlugin::set_state(const Dictionary& p_state){
+
+ Dictionary state=p_state;
+ if (state.has("zoom")) {
+ region_editor->draw_zoom = p_state["zoom"];
+ }
+
+ if (state.has("snap_step")) {
+ Vector2 s = state["snap_step"];
+ region_editor->sb_step_x->set_val(s.x);
+ region_editor->sb_step_y->set_val(s.y);
+ region_editor->snap_step = s;
+ }
+
+ if (state.has("snap_offset")) {
+ Vector2 ofs = state["snap_offset"];
+ region_editor->sb_off_x->set_val(ofs.x);
+ region_editor->sb_off_y->set_val(ofs.y);
+ region_editor->snap_offset = ofs;
+ }
+
+ if (state.has("snap_separation")) {
+ Vector2 sep = state["snap_separation"];
+ region_editor->sb_sep_x->set_val(sep.x);
+ region_editor->sb_sep_y->set_val(sep.y);
+ region_editor->snap_separation = sep;
+ }
+
+ if (state.has("snap_mode")) {
+ region_editor->_set_snap_mode(state["snap_mode"]);
+ }
+
+}
+
+TextureRegionEditorPlugin::TextureRegionEditorPlugin(EditorNode *p_node)
+{
+ editor = p_node;
+ region_editor = memnew ( TextureRegionEditor(p_node) );
+
+ region_button = p_node->add_bottom_panel_item(TTR("Texture Region"), region_editor);
+ region_button->set_tooltip(TTR("Texture Region Editor"));
+
+ region_editor->set_custom_minimum_size(Size2(0,200));
+ region_editor->hide();
+ region_button->hide();
+}
diff --git a/editor/plugins/texture_region_editor_plugin.h b/editor/plugins/texture_region_editor_plugin.h
new file mode 100644
index 000000000..45751f528
--- /dev/null
+++ b/editor/plugins/texture_region_editor_plugin.h
@@ -0,0 +1,153 @@
+/*************************************************************************/
+/* texture_region_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Author: Mariano Suligoy */
+/* */
+/* 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 TEXTURE_REGION_EDITOR_PLUGIN_H
+#define TEXTURE_REGION_EDITOR_PLUGIN_H
+
+#include "canvas_item_editor_plugin.h"
+#include "editor/editor_plugin.h"
+#include "editor/editor_node.h"
+#include "scene/2d/sprite.h"
+#include "scene/gui/patch_9_frame.h"
+#include "scene/resources/style_box.h"
+#include "scene/resources/texture.h"
+
+class TextureRegionEditor : public Control {
+
+ OBJ_TYPE(TextureRegionEditor, Control );
+
+ enum SnapMode {
+ SNAP_NONE,
+ SNAP_PIXEL,
+ SNAP_GRID,
+ SNAP_AUTOSLICE
+ };
+
+ friend class TextureRegionEditorPlugin;
+ MenuButton *snap_mode_button;
+ TextureFrame *icon_zoom;
+ ToolButton *zoom_in;
+ ToolButton *zoom_reset;
+ ToolButton *zoom_out;
+ HBoxContainer * hb_grid; //For showing/hiding the grid controls when changing the SnapMode
+ SpinBox *sb_step_y;
+ SpinBox *sb_step_x;
+ SpinBox *sb_off_y;
+ SpinBox *sb_off_x;
+ SpinBox *sb_sep_y;
+ SpinBox *sb_sep_x;
+ Control *edit_draw;
+
+ VScrollBar *vscroll;
+ HScrollBar *hscroll;
+
+ EditorNode *editor;
+ UndoRedo* undo_redo;
+
+ Vector2 draw_ofs;
+ float draw_zoom;
+ bool updating_scroll;
+
+ int snap_mode;
+ Vector2 snap_offset;
+ Vector2 snap_step;
+ Vector2 snap_separation;
+
+ Patch9Frame *node_patch9;
+ Sprite *node_sprite;
+ Ref<StyleBoxTexture> obj_styleBox;
+ Ref<AtlasTexture> atlas_tex;
+
+ Rect2 rect;
+ Rect2 rect_prev;
+ float prev_margin;
+ int edited_margin;
+ List<Rect2> autoslice_cache;
+
+ bool drag;
+ bool creating;
+ Vector2 drag_from;
+ int drag_index;
+
+ void _set_snap_mode(int p_mode);
+ void _set_snap_off_x(float p_val);
+ void _set_snap_off_y(float p_val);
+ void _set_snap_step_x(float p_val);
+ void _set_snap_step_y(float p_val);
+ void _set_snap_sep_x(float p_val);
+ void _set_snap_sep_y(float p_val);
+ void _zoom_in();
+ void _zoom_reset();
+ void _zoom_out();
+ void apply_rect(const Rect2& rect);
+protected:
+
+ void _notification(int p_what);
+ void _node_removed(Object *p_obj);
+ static void _bind_methods();
+
+ Vector2 snap_point(Vector2 p_target) const;
+
+ virtual void _changed_callback(Object *p_changed, const char *p_prop);
+
+public:
+
+ void _edit_region();
+ void _region_draw();
+ void _region_input(const InputEvent &p_input);
+ void _scroll_changed(float);
+
+ void edit(Object *p_obj);
+ TextureRegionEditor(EditorNode* p_editor);
+
+};
+
+class TextureRegionEditorPlugin : public EditorPlugin
+{
+ OBJ_TYPE( TextureRegionEditorPlugin, EditorPlugin );
+
+ Button *region_button;
+ TextureRegionEditor *region_editor;
+ EditorNode *editor;
+public:
+
+ virtual String get_name() const { return "TextureRegion"; }
+ 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);
+ void set_state(const Dictionary &p_state);
+ Dictionary get_state() const;
+
+ TextureRegionEditorPlugin(EditorNode *p_node);
+};
+
+#endif // TEXTURE_REGION_EDITOR_PLUGIN_H
diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp
new file mode 100644
index 000000000..2d115ca88
--- /dev/null
+++ b/editor/plugins/theme_editor_plugin.cpp
@@ -0,0 +1,996 @@
+/*************************************************************************/
+/* theme_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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;
+ main_vb->set_theme(p_theme);
+
+}
+
+
+void ThemeEditor::_propagate_redraw(Control *p_at) {
+
+ p_at->notification(NOTIFICATION_THEME_CHANGED);
+ p_at->minimum_size_changed();
+ p_at->update();
+ for(int i=0;i<p_at->get_child_count();i++) {
+ Control *a = p_at->get_child(i)->cast_to<Control>();
+ if (a)
+ _propagate_redraw(a);
+
+ }
+}
+
+void ThemeEditor::_refresh_interval() {
+
+ _propagate_redraw(main_vb);
+
+}
+
+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(TTR("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,Ref<Texture>());
+
+ }
+
+ }
+ {
+ 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,Ref<StyleBox>());
+
+ }
+
+ }
+ {
+ 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,Ref<Font>());
+
+ }
+ }
+ {
+ 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;
+ case POPUP_CLASS_REMOVE: {
+ 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->clear_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->clear_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->clear_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->clear_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->clear_constant(E->get(),fromtype);
+
+ }
+ }
+
+ }break;
+ }
+
+}
+
+void ThemeEditor::_theme_menu_cbk(int p_option) {
+
+
+ if (p_option==POPUP_CREATE_EMPTY || p_option==POPUP_CREATE_EDITOR_EMPTY) {
+
+
+ Ref<Theme> base_theme;
+
+ if (p_option==POPUP_CREATE_EMPTY) {
+ base_theme = Theme::get_default();
+ } else {
+ base_theme = EditorNode::get_singleton()->get_theme_base()->get_theme();
+ }
+
+
+ {
+
+ List<StringName> types;
+ base_theme->get_type_list(&types);
+
+
+ for (List<StringName>::Element *T=types.front();T;T=T->next()) {
+ StringName type = T->get();
+
+ List<StringName> icons;
+ base_theme->get_icon_list(type,&icons);
+
+ for (List<StringName>::Element *E=icons.front();E;E=E->next()) {
+ theme->set_icon(E->get(),type,Ref<Texture>());
+ }
+
+ List<StringName> shaders;
+ base_theme->get_shader_list(type,&shaders);
+
+ for (List<StringName>::Element *E=shaders.front();E;E=E->next()) {
+ theme->set_shader(E->get(),type,Ref<Shader>());
+ }
+
+ List<StringName> styleboxs;
+ base_theme->get_stylebox_list(type,&styleboxs);
+
+ for (List<StringName>::Element *E=styleboxs.front();E;E=E->next()) {
+ theme->set_stylebox(E->get(),type,Ref<StyleBox>());
+ }
+
+ List<StringName> fonts;
+ base_theme->get_font_list(type,&fonts);
+
+ for (List<StringName>::Element *E=fonts.front();E;E=E->next()) {
+ theme->set_font(E->get(),type,Ref<Font>());
+ }
+
+ List<StringName> colors;
+ base_theme->get_color_list(type,&colors);
+
+ for (List<StringName>::Element *E=colors.front();E;E=E->next()) {
+ theme->set_color(E->get(),type,Color());
+ }
+
+
+ List<StringName> constants;
+ base_theme->get_constant_list(type,&constants);
+
+ for (List<StringName>::Element *E=constants.front();E;E=E->next()) {
+ theme->set_constant(E->get(),type,base_theme->get_constant(type,E->get()));
+ }
+
+ }
+
+ }
+ 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(TTR("Add Item"));
+ add_del_dialog->get_ok()->set_text(TTR("Add"));
+ add_del_dialog->popup_centered(Size2(490,85)*EDSCALE);
+
+ base_theme=Theme::get_default();
+
+ } else if (p_option==POPUP_CLASS_ADD) {//add
+
+ add_del_dialog->set_title(TTR("Add All Items"));
+ add_del_dialog->get_ok()->set_text(TTR("Add All"));
+ add_del_dialog->popup_centered(Size2(240,85)*EDSCALE);
+
+ 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(TTR("Remove Item"));
+ add_del_dialog->get_ok()->set_text(TTR("Remove"));
+ add_del_dialog->popup_centered(Size2(490,85)*EDSCALE);
+
+ base_theme=theme;
+
+ } else if (p_option==POPUP_CLASS_REMOVE) {
+
+ add_del_dialog->set_title("Remove All Items");
+ add_del_dialog->get_ok()->set_text("Remove All");
+ add_del_dialog->popup_centered(Size2(240,85)*EDSCALE);
+
+ base_theme=Theme::get_default();
+
+ type_select->hide();
+ name_select_label->hide();
+ type_select_label->hide();
+ name_edit->hide();
+ name_menu->hide();
+ }
+ 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();
+ types.sort_custom<StringName::AlphCompare>();
+ for(List<StringName>::Element *E=types.front();E;E=E->next()) {
+
+ type_menu->get_popup()->add_item( E->get() );
+ }
+
+}
+
+void ThemeEditor::_notification(int p_what) {
+
+ if (p_what==NOTIFICATION_PROCESS) {
+
+ time_left-=get_process_delta_time();
+ if (time_left<0) {
+ time_left=1.5;
+ _refresh_interval();
+ }
+ }
+}
+
+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() {
+
+ time_left=0;
+
+ scroll = memnew( ScrollContainer );
+ add_child(scroll);
+ scroll->set_area_as_parent_rect(3);
+ scroll->set_margin(MARGIN_TOP,30*EDSCALE);
+ //scroll->set_enable_h_scroll(true);
+ scroll->set_enable_v_scroll(true);
+ scroll->set_enable_h_scroll(false);
+
+ Panel * panel = memnew( Panel );
+ scroll->add_child(panel);
+ panel->set_custom_minimum_size(Size2(500,800)*EDSCALE);
+ panel->set_theme(Theme::get_default());
+ panel->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ main_vb= memnew( VBoxContainer );
+ panel->add_child(main_vb);
+ main_vb->set_area_as_parent_rect(4*EDSCALE);
+
+
+ HBoxContainer *hb_menu = memnew(HBoxContainer);
+ main_vb->add_child(hb_menu);
+
+
+
+ theme_menu = memnew( MenuButton );
+ theme_menu->set_text("Theme");
+ theme_menu->get_popup()->add_item(TTR("Add Item"),POPUP_ADD);
+ theme_menu->get_popup()->add_item(TTR("Add Class Items"),POPUP_CLASS_ADD);
+ theme_menu->get_popup()->add_item(TTR("Remove Item"),POPUP_REMOVE);
+ theme_menu->get_popup()->add_item(TTR("Remove Class Items"),POPUP_CLASS_REMOVE);
+ theme_menu->get_popup()->add_separator();
+ theme_menu->get_popup()->add_item(TTR("Create Empty Template"),POPUP_CREATE_EMPTY);
+ theme_menu->get_popup()->add_item(TTR("Create Empty Editor Template"),POPUP_CREATE_EDITOR_EMPTY);
+
+ add_child(theme_menu);
+ theme_menu->set_pos(Vector2(3,3)*EDSCALE);
+ theme_menu->get_popup()->connect("item_pressed", this,"_theme_menu_cbk");
+
+
+ HBoxContainer *main_hb = memnew( HBoxContainer );
+ main_vb->add_child(main_hb);
+
+
+
+ VBoxContainer *first_vb = memnew( VBoxContainer);
+ first_vb->set_h_size_flags(SIZE_EXPAND_FILL);
+ main_hb->add_child(first_vb);
+
+
+
+// main_panel->add_child(panel);
+// panel->set_area_as_parent_rect();
+// panel->set_margin( MARGIN_TOP,20 );
+
+ first_vb->add_child(memnew( Label("Label") ));
+
+ first_vb->add_child(memnew( Button("Button")) );
+ ToolButton *tb = memnew( ToolButton );
+ tb->set_text("ToolButton");
+ first_vb->add_child(tb );
+ CheckButton *cb = memnew( CheckButton );
+ cb->set_text("CheckButton");
+ first_vb->add_child(cb );
+ CheckBox *cbx = memnew( CheckBox );
+ cbx->set_text("CheckBox");
+ first_vb->add_child(cbx );
+
+
+ ButtonGroup *bg = memnew( ButtonGroup );
+ bg->set_v_size_flags(SIZE_EXPAND_FILL);
+ VBoxContainer *gbvb = memnew( VBoxContainer );
+ gbvb->set_v_size_flags(SIZE_EXPAND_FILL);
+ CheckBox *rbx1 = memnew( CheckBox );
+ rbx1->set_text(TTR("CheckBox Radio1"));
+ rbx1->set_pressed(true);
+ gbvb->add_child(rbx1);
+ CheckBox *rbx2 = memnew( CheckBox );
+ rbx2->set_text(TTR("CheckBox Radio2"));
+ gbvb->add_child(rbx2);
+ bg->add_child(gbvb);
+ first_vb->add_child(bg);
+
+ MenuButton* test_menu_button = memnew( MenuButton );
+ test_menu_button->set_text("MenuButton");
+ test_menu_button->get_popup()->add_item(TTR("Item"));
+ test_menu_button->get_popup()->add_separator();
+ test_menu_button->get_popup()->add_check_item(TTR("Check Item"));
+ test_menu_button->get_popup()->add_check_item(TTR("Checked Item"));
+ test_menu_button->get_popup()->set_item_checked(2,true);
+ first_vb->add_child(test_menu_button);
+
+ OptionButton *test_option_button = memnew( OptionButton );
+ test_option_button->add_item("OptionButton");
+ test_option_button->add_separator();
+ test_option_button->add_item(TTR("Has"));
+ test_option_button->add_item(TTR("Many"));
+ test_option_button->add_item(TTR("Options"));
+ first_vb->add_child(test_option_button);
+
+ ColorPickerButton *cpb = memnew( ColorPickerButton );
+ first_vb->add_child(cpb );
+
+ first_vb->add_child( memnew( HSeparator ));
+ first_vb->add_child( memnew( HSlider ));
+ first_vb->add_child( memnew( HScrollBar ));
+ first_vb->add_child( memnew( SpinBox ));
+ ProgressBar *pb=memnew( ProgressBar );
+ pb->set_val(50);
+ first_vb->add_child( pb);
+ Panel *pn=memnew( Panel );
+ pn->set_custom_minimum_size(Size2(40,40)*EDSCALE);
+ first_vb->add_child( pn);
+ first_vb->add_constant_override("separation",10*EDSCALE);
+
+ VBoxContainer *second_vb = memnew( VBoxContainer );
+ second_vb->set_h_size_flags(SIZE_EXPAND_FILL);
+ main_hb->add_child(second_vb);
+ second_vb->add_constant_override("separation",10*EDSCALE);
+ LineEdit *le = memnew( LineEdit );
+ le->set_text("LineEdit");
+ second_vb->add_child(le);
+ TextEdit *te = memnew( TextEdit );
+ te->set_text("TextEdit");
+ //te->set_v_size_flags(SIZE_EXPAND_FILL);
+ te->set_custom_minimum_size(Size2(0,160)*EDSCALE);
+ second_vb->add_child(te);
+
+ Tree *test_tree = memnew(Tree);
+ second_vb->add_child(test_tree);
+ test_tree->set_custom_minimum_size(Size2(0,160)*EDSCALE);
+
+
+ TreeItem *item = test_tree->create_item();
+ item->set_editable(0,true);
+ item->set_text(0,"Tree");
+ item = test_tree->create_item( test_tree->get_root() );
+ item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+ item->set_editable(0,true);
+ item->set_text(0,"check");
+ item = test_tree->create_item( test_tree->get_root() );
+ item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE);
+ item->set_editable(0,true);
+ item->set_range_config(0,0,20,0.1);
+ item->set_range(0,2);
+ item = test_tree->create_item( test_tree->get_root() );
+ item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE);
+ item->set_editable(0,true);
+ item->set_text(0,TTR("Have,Many,Several,Options!"));
+ item->set_range(0,2);
+
+ VBoxContainer *third_vb = memnew( VBoxContainer );
+ third_vb->set_h_size_flags(SIZE_EXPAND_FILL);
+ third_vb->add_constant_override("separation",10);
+
+ main_hb->add_child(third_vb);
+
+ HBoxContainer *vhb = memnew( HBoxContainer );
+ vhb->set_custom_minimum_size(Size2(0,160)*EDSCALE);
+ vhb->add_child(memnew(VSeparator));
+ vhb->add_child(memnew(VSlider));
+ vhb->add_child(memnew(VScrollBar));
+ third_vb->add_child(vhb);
+
+ TabContainer *tc = memnew( TabContainer );
+ third_vb->add_child(tc);
+ tc->set_custom_minimum_size(Size2(0,160)*EDSCALE);
+ Control *tcc = memnew( Control );
+ tcc->set_name(TTR("Tab 1"));
+ tc->add_child(tcc);
+ tcc = memnew( Control );
+ tcc->set_name(TTR("Tab 2"));
+ tc->add_child(tcc);
+ tcc = memnew( Control );
+ tcc->set_name(TTR("Tab 3"));
+ tc->add_child(tcc);
+
+ main_hb->add_constant_override("separation",20*EDSCALE);
+
+
+
+
+/*
+ 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( EditorFileDialog );
+ panel->add_child(test_file_dialog);
+
+ fd_button->connect("pressed", this,"_open_file_dialog");
+*/
+
+ add_del_dialog = memnew(ConfirmationDialog);
+ add_del_dialog->hide();
+ add_child(add_del_dialog);
+
+
+ Label *l = memnew( Label );
+ l->set_pos( Point2(5,5)*EDSCALE );
+ l->set_text(TTR("Type:"));
+ add_del_dialog->add_child(l);
+ dtype_select_label=l;
+
+
+ type_edit = memnew( LineEdit );
+ type_edit->set_pos(Point2(5,25)*EDSCALE);
+ type_edit->set_size(Point2(150,5)*EDSCALE);
+ add_del_dialog->add_child(type_edit);
+ type_menu = memnew( MenuButton );
+ type_menu->set_pos(Point2(160,25)*EDSCALE);
+ type_menu->set_size(Point2(30,5)*EDSCALE);
+ 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)*EDSCALE );
+ l->set_text(TTR("Name:"));
+ add_del_dialog->add_child(l);
+ name_select_label=l;
+
+ name_edit = memnew( LineEdit );
+ name_edit->set_pos(Point2(200,25)*EDSCALE);
+ name_edit->set_size(Point2(150,5)*EDSCALE);
+ add_del_dialog->add_child(name_edit);
+ name_menu = memnew( MenuButton );
+ name_menu->set_pos(Point2(360,25)*EDSCALE);
+ name_menu->set_size(Point2(30,5)*EDSCALE);
+ 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)*EDSCALE );
+ type_select_label->set_text(TTR("Data Type:"));
+ add_del_dialog->add_child(type_select_label);
+
+ type_select = memnew( OptionButton );
+ type_select->add_item(TTR("Icon"));
+ type_select->add_item(TTR("Style"));
+ type_select->add_item(TTR("Font"));
+ type_select->add_item(TTR("Color"));
+ type_select->add_item(TTR("Constant"));
+ type_select->set_pos( Point2( 400,25 )*EDSCALE );
+ type_select->set_size( Point2( 80,5 )*EDSCALE );
+
+
+ add_del_dialog->add_child(type_select);
+
+ add_del_dialog->get_ok()->connect("pressed", this,"_dialog_cbk");
+
+
+ file_dialog = memnew( EditorFileDialog );
+ 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->show();
+ theme_editor->edit( p_node->cast_to<Theme>() );
+ } else {
+ theme_editor->edit( Ref<Theme>() );
+ 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->set_process(true);
+ button->show();
+ editor->make_bottom_panel_item_visible(theme_editor);
+
+ } else {
+ theme_editor->set_process(false);
+ if (theme_editor->is_visible())
+ editor->hide_bottom_panel();
+ button->hide();
+ }
+}
+
+ThemeEditorPlugin::ThemeEditorPlugin(EditorNode *p_node) {
+
+ editor=p_node;
+ theme_editor = memnew( ThemeEditor );
+ theme_editor->set_custom_minimum_size(Size2(0,200));
+
+// p_node->get_viewport()->add_child(theme_editor);
+ button=editor->add_bottom_panel_item("Theme",theme_editor);
+ button->hide();
+
+
+}
+
diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h
new file mode 100644
index 000000000..d00c22c9d
--- /dev/null
+++ b/editor/plugins/theme_editor_plugin.h
@@ -0,0 +1,125 @@
+/*************************************************************************/
+/* theme_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "scene/gui/check_box.h"
+#include "scene/gui/button_group.h"
+#include "scene/gui/scroll_container.h"
+
+#include "editor/editor_node.h"
+
+
+
+
+class ThemeEditor : public Control {
+
+ OBJ_TYPE( ThemeEditor, Control );
+
+
+ ScrollContainer *scroll;
+ VBoxContainer *main_vb;
+ Ref<Theme> theme;
+
+ EditorFileDialog *file_dialog;
+
+ double time_left;
+
+ 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_CLASS_REMOVE,
+ POPUP_CREATE_EMPTY,
+ POPUP_CREATE_EDITOR_EMPTY
+ };
+
+ 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 _propagate_redraw(Control *p_at);
+ void _refresh_interval();
+
+
+protected:
+ void _notification(int p_what);
+ 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;
+ Button *button;
+
+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/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp
new file mode 100644
index 000000000..d0e1f6095
--- /dev/null
+++ b/editor/plugins/tile_map_editor_plugin.cpp
@@ -0,0 +1,1595 @@
+/*************************************************************************/
+/* tile_map_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "os/keyboard.h"
+#include "os/input.h"
+
+#include "canvas_item_editor_plugin.h"
+#include "editor/editor_settings.h"
+#include "editor/editor_scale.h"
+
+void TileMapEditor::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_ENTER_TREE: {
+
+ transp->set_icon(get_icon("Transpose","EditorIcons"));
+ mirror_x->set_icon(get_icon("MirrorX","EditorIcons"));
+ mirror_y->set_icon(get_icon("MirrorY","EditorIcons"));
+ rotate_0->set_icon(get_icon("Rotate0","EditorIcons"));
+ rotate_90->set_icon(get_icon("Rotate90","EditorIcons"));
+ rotate_180->set_icon(get_icon("Rotate180","EditorIcons"));
+ rotate_270->set_icon(get_icon("Rotate270","EditorIcons"));
+
+ } break;
+ case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+
+ if (is_visible()) {
+ _update_palette();
+ }
+ } break;
+ }
+}
+
+void TileMapEditor::_menu_option(int p_option) {
+
+ switch(p_option) {
+
+ case OPTION_BUCKET: {
+
+ tool=TOOL_BUCKET;
+
+ canvas_item_editor->update();
+ } break;
+ case OPTION_PICK_TILE: {
+
+ tool=TOOL_PICKING;
+
+ canvas_item_editor->update();
+ } break;
+ case OPTION_SELECT: {
+
+ tool=TOOL_SELECTING;
+ selection_active=false;
+
+ canvas_item_editor->update();
+ } break;
+ case OPTION_DUPLICATE: {
+
+ _update_copydata();
+
+ if (selection_active) {
+ tool=TOOL_DUPLICATING;
+
+ canvas_item_editor->update();
+ }
+ } break;
+ case OPTION_ERASE_SELECTION: {
+
+ if (!selection_active)
+ return;
+
+ undo_redo->create_action("Erase Selection");
+ for (int i=rectangle.pos.y;i<=rectangle.pos.y+rectangle.size.y;i++) {
+ for (int j=rectangle.pos.x;j<=rectangle.pos.x+rectangle.size.x;j++) {
+
+ _set_cell(Point2i(j, i), TileMap::INVALID_CELL, false, false, false, true);
+ }
+ }
+ undo_redo->commit_action();
+
+ selection_active=false;
+ copydata.clear();
+
+ canvas_item_editor->update();
+ } 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 {
+
+ int item = palette->get_current();
+
+ if (item==-1)
+ return TileMap::INVALID_CELL;
+
+ return palette->get_item_metadata(item);
+}
+
+void TileMapEditor::set_selected_tile(int p_tile) {
+
+ int idx = palette->find_metadata(p_tile);
+
+ if (idx >= 0) {
+ palette->select(idx, true);
+ palette->ensure_current_is_visible();
+ }
+}
+
+void TileMapEditor::_set_cell(const Point2i& p_pos,int p_value,bool p_flip_h, bool p_flip_v, bool p_transpose,bool p_with_undo) {
+
+ ERR_FAIL_COND(!node);
+
+ int prev_val=node->get_cell(p_pos.x,p_pos.y);
+
+ bool prev_flip_h=node->is_cell_x_flipped(p_pos.x,p_pos.y);
+ bool prev_flip_v=node->is_cell_y_flipped(p_pos.x,p_pos.y);
+ bool prev_transpose=node->is_cell_transposed(p_pos.x,p_pos.y);
+
+ if (p_value==prev_val && p_flip_h==prev_flip_h && p_flip_v==prev_flip_v && p_transpose==prev_transpose)
+ return; //check that it's actually different
+
+ if (p_with_undo) {
+
+ undo_redo->add_do_method(node,"set_cellv",Point2(p_pos),p_value,p_flip_h,p_flip_v,p_transpose);
+ undo_redo->add_undo_method(node,"set_cellv",Point2(p_pos),prev_val,prev_flip_h,prev_flip_v,prev_transpose);
+ } else {
+
+ node->set_cell(p_pos.x,p_pos.y,p_value,p_flip_h,p_flip_v,p_transpose);
+ }
+
+}
+
+void TileMapEditor::_text_entered(const String& p_text) {
+
+ canvas_item_editor->grab_focus();
+}
+
+void TileMapEditor::_text_changed(const String& p_text) {
+
+ _update_palette();
+}
+
+void TileMapEditor::_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 ) ) {
+
+ palette->call("_input_event", p_ie);
+ search_box->accept_event();
+ }
+}
+
+void TileMapEditor::_update_palette() {
+
+ if (!node)
+ return;
+
+ int selected = get_selected_tile();
+ palette->clear();
+
+ Ref<TileSet> tileset=node->get_tileset();
+ if (tileset.is_null())
+ return;
+
+ List<int> tiles;
+ tileset->get_tile_list(&tiles);
+
+ if (tiles.empty())
+ return;
+
+ float min_size = EDITOR_DEF("tile_map/preview_size", 64);
+ min_size *= EDSCALE;
+ int hseparation = EDITOR_DEF("tile_map/palette_item_hseparation",8);
+ bool show_tile_names = bool(EDITOR_DEF("tile_map/show_tile_names", true));
+
+ palette->add_constant_override("hseparation", hseparation*EDSCALE);
+ palette->add_constant_override("vseparation", 8*EDSCALE);
+
+ palette->set_fixed_icon_size(Size2(min_size, min_size));
+ palette->set_fixed_column_width(min_size * MAX(size_slider->get_val(), 1));
+
+ String filter = search_box->get_text().strip_edges();
+
+ for (List<int>::Element *E=tiles.front();E;E=E->next()) {
+
+ String name;
+
+ if (tileset->tile_get_name(E->get())!="") {
+ name = itos(E->get())+" - "+tileset->tile_get_name(E->get());
+ } else {
+ name = "#"+itos(E->get());
+ }
+
+ if (filter != "" && !filter.is_subsequence_ofi(name))
+ continue;
+
+ if (show_tile_names) {
+ palette->add_item(name);
+ } else {
+ palette->add_item(String());
+ }
+
+ Ref<Texture> tex = tileset->tile_get_texture(E->get());
+
+ if (tex.is_valid()) {
+ Rect2 region = tileset->tile_get_region(E->get());
+
+ if (!region.has_no_area())
+ palette->set_item_icon_region(palette->get_item_count()-1, region);
+
+ palette->set_item_icon(palette->get_item_count()-1, tex);
+ }
+
+ palette->set_item_metadata(palette->get_item_count()-1, E->get());
+ }
+
+ palette->set_same_column_width(true);
+
+ if (selected != -1)
+ set_selected_tile(selected);
+ else
+ palette->select(0);
+}
+
+void TileMapEditor::_pick_tile(const Point2& p_pos) {
+
+ int id = node->get_cell(p_pos.x, p_pos.y);
+
+ if (id==TileMap::INVALID_CELL)
+ return;
+
+ if (search_box->get_text().strip_edges() != "") {
+
+ search_box->set_text("");
+ _update_palette();
+ }
+
+ set_selected_tile(id);
+
+ mirror_x->set_pressed(node->is_cell_x_flipped(p_pos.x, p_pos.y));
+ mirror_y->set_pressed(node->is_cell_y_flipped(p_pos.x, p_pos.y));
+ transp->set_pressed(node->is_cell_transposed(p_pos.x, p_pos.y));
+
+ _update_transform_buttons();
+ canvas_item_editor->update();
+}
+
+DVector<Vector2> TileMapEditor::_bucket_fill(const Point2i& p_start, bool erase, bool preview) {
+
+ int prev_id = node->get_cell(p_start.x, p_start.y);
+ int id = TileMap::INVALID_CELL;
+ if (!erase) {
+ id = get_selected_tile();
+
+ if (id == TileMap::INVALID_CELL)
+ return DVector<Vector2>();
+ }
+
+ Rect2i r = node->get_item_rect();
+ r.pos = r.pos/node->get_cell_size();
+ r.size = r.size/node->get_cell_size();
+
+ int area = r.get_area();
+ if(preview) {
+ // Test if we can re-use the result from preview bucket fill
+ bool invalidate_cache = false;
+ // Area changed
+ if(r != bucket_cache_rect)
+ _clear_bucket_cache();
+ // Cache grid is not initialized
+ if(bucket_cache_visited == 0) {
+ bucket_cache_visited = new bool[area];
+ invalidate_cache = true;
+ }
+ // Tile ID changed or position wasn't visited by the previous fill
+ int loc = (p_start.x - r.get_pos().x) + (p_start.y - r.get_pos().y) * r.get_size().x;
+ if(prev_id != bucket_cache_tile || !bucket_cache_visited[loc]) {
+ invalidate_cache = true;
+ }
+ if(invalidate_cache) {
+ for(int i = 0; i < area; ++i)
+ bucket_cache_visited[i] = false;
+ bucket_cache = DVector<Vector2>();
+ bucket_cache_tile = prev_id;
+ bucket_cache_rect = r;
+ }
+ else {
+ return bucket_cache;
+ }
+ }
+
+ DVector<Vector2> points;
+
+ List<Point2i> queue;
+ queue.push_back(p_start);
+
+ while (queue.size()) {
+
+ Point2i n = queue.front()->get();
+ queue.pop_front();
+
+ if (!r.has_point(n))
+ continue;
+
+ if (node->get_cell(n.x, n.y) == prev_id) {
+
+ if(preview) {
+ int loc = (n.x - r.get_pos().x) + (n.y - r.get_pos().y) * r.get_size().x;
+ if(bucket_cache_visited[loc])
+ continue;
+ bucket_cache_visited[loc] = true;
+ bucket_cache.push_back(n);
+ }
+ else {
+ node->set_cellv(n, id, flip_h, flip_v, transpose);
+ points.push_back(n);
+ }
+
+ queue.push_back(n + Point2i(0, 1));
+ queue.push_back(n + Point2i(0, -1));
+ queue.push_back(n + Point2i(1, 0));
+ queue.push_back(n + Point2i(-1, 0));
+ }
+ }
+
+ return preview ? bucket_cache : points;
+}
+
+void TileMapEditor::_fill_points(const DVector<Vector2> p_points, const Dictionary& p_op) {
+
+ int len = p_points.size();
+ DVector<Vector2>::Read pr = p_points.read();
+
+ int id = p_op["id"];
+ bool xf = p_op["flip_h"];
+ bool yf = p_op["flip_v"];
+ bool tr = p_op["transpose"];
+
+ for (int i=0;i<len;i++) {
+
+ _set_cell(pr[i], id, xf, yf, tr);
+ }
+}
+
+void TileMapEditor::_erase_points(const DVector<Vector2> p_points) {
+
+ int len = p_points.size();
+ DVector<Vector2>::Read pr = p_points.read();
+
+ for (int i=0;i<len;i++) {
+
+ _set_cell(pr[i], TileMap::INVALID_CELL);
+ }
+}
+
+void TileMapEditor::_select(const Point2i& p_from, const Point2i& p_to) {
+
+ Point2i begin=p_from;
+ Point2i end=p_to;
+
+ if (begin.x > end.x) {
+
+ SWAP( begin.x, end.x);
+ }
+ if (begin.y > end.y) {
+
+ SWAP( begin.y, end.y);
+ }
+
+ rectangle.pos=begin;
+ rectangle.size=end-begin;
+
+ canvas_item_editor->update();
+}
+
+void TileMapEditor::_draw_cell(int p_cell, const Point2i& p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Matrix32& p_xform) {
+
+ Ref<Texture> t = node->get_tileset()->tile_get_texture(p_cell);
+
+ if (t.is_null())
+ return;
+
+ Vector2 tile_ofs = node->get_tileset()->tile_get_texture_offset(p_cell);
+
+ Rect2 r = node->get_tileset()->tile_get_region(p_cell);
+ Size2 sc = p_xform.get_scale();
+
+ Rect2 rect = Rect2();
+ rect.pos = node->map_to_world(p_point) + node->get_cell_draw_offset();
+
+ if (r.has_no_area()) {
+ rect.size = t->get_size();
+ } else {
+ rect.size = r.size;
+ }
+
+ if (rect.size.y > rect.size.x) {
+ if ((p_flip_h && (p_flip_v || p_transpose)) || (p_flip_v && !p_transpose))
+ tile_ofs.y += rect.size.y - rect.size.x;
+ } else if (rect.size.y < rect.size.x) {
+ if ((p_flip_v && (p_flip_h || p_transpose)) || (p_flip_h && !p_transpose))
+ tile_ofs.x += rect.size.x - rect.size.y;
+ }
+
+ if (p_transpose) {
+ SWAP(tile_ofs.x, tile_ofs.y);
+ }
+ if (p_flip_h) {
+ sc.x*=-1.0;
+ tile_ofs.x*=-1.0;
+ }
+ if (p_flip_v) {
+ sc.y*=-1.0;
+ tile_ofs.y*=-1.0;
+ }
+
+ if (node->get_tile_origin()==TileMap::TILE_ORIGIN_TOP_LEFT) {
+
+ rect.pos+=tile_ofs;
+ } else if (node->get_tile_origin()==TileMap::TILE_ORIGIN_BOTTOM_LEFT) {
+ Size2 cell_size = node->get_cell_size();
+
+ rect.pos+=tile_ofs;
+
+ if(p_transpose)
+ {
+ if(p_flip_h)
+ rect.pos.x-=cell_size.x;
+ else
+ rect.pos.x+=cell_size.x;
+ } else {
+ if(p_flip_v)
+ rect.pos.y-=cell_size.y;
+ else
+ rect.pos.y+=cell_size.y;
+ }
+
+ } else if (node->get_tile_origin()==TileMap::TILE_ORIGIN_CENTER) {
+ rect.pos+=node->get_cell_size()/2;
+ Vector2 s = r.size;
+
+ Vector2 center = (s/2) - tile_ofs;
+
+ if (p_flip_h)
+ rect.pos.x-=s.x-center.x;
+ else
+ rect.pos.x-=center.x;
+
+ if (p_flip_v)
+ rect.pos.y-=s.y-center.y;
+ else
+ rect.pos.y-=center.y;
+ }
+
+ rect.pos=p_xform.xform(rect.pos);
+ rect.size*=sc;
+
+ if (r.has_no_area())
+ canvas_item_editor->draw_texture_rect(t, rect, false, Color(1,1,1,0.5), p_transpose);
+ else
+ canvas_item_editor->draw_texture_rect_region(t, rect, r, Color(1,1,1,0.5), p_transpose);
+}
+
+void TileMapEditor::_draw_fill_preview(int p_cell, const Point2i& p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Matrix32& p_xform) {
+
+ DVector<Vector2> points = _bucket_fill(p_point, false, true);
+ DVector<Vector2>::Read pr = points.read();
+ int len = points.size();
+ int time_after = OS::get_singleton()->get_ticks_msec();
+
+ for(int i = 0; i < len; ++i) {
+ _draw_cell(p_cell, pr[i], p_flip_h, p_flip_v, p_transpose, p_xform);
+ }
+}
+
+void TileMapEditor::_clear_bucket_cache() {
+ if(bucket_cache_visited) {
+ delete[] bucket_cache_visited;
+ bucket_cache_visited = 0;
+ }
+}
+
+void TileMapEditor::_update_copydata() {
+
+ copydata.clear();
+
+ if (!selection_active)
+ return;
+
+ for (int i=rectangle.pos.y;i<=rectangle.pos.y+rectangle.size.y;i++) {
+
+ for (int j=rectangle.pos.x;j<=rectangle.pos.x+rectangle.size.x;j++) {
+
+ TileData tcd;
+
+ tcd.cell=node->get_cell(j, i);
+
+ if (tcd.cell!=TileMap::INVALID_CELL) {
+ tcd.pos=Point2i(j, i);
+ tcd.flip_h=node->is_cell_x_flipped(j,i);
+ tcd.flip_v=node->is_cell_y_flipped(j,i);
+ tcd.transpose=node->is_cell_transposed(j,i);
+ }
+
+ copydata.push_back(tcd);
+ }
+ }
+}
+
+static inline Vector<Point2i> line(int x0, int x1, int y0, int y1) {
+
+ Vector<Point2i> points;
+
+ float dx = ABS(x1 - x0);
+ float dy = ABS(y1 - y0);
+
+ int x = x0;
+ int y = y0;
+
+ int sx = x0 > x1 ? -1 : 1;
+ int sy = y0 > y1 ? -1 : 1;
+
+ if (dx > dy) {
+ float err = dx/2;
+
+ for (; x != x1; x += sx) {
+ points.push_back(Vector2(x, y));
+
+ err -= dy;
+ if (err < 0) {
+ y += sy;
+ err += dx;
+ }
+ }
+ } else {
+ float err = dy/2;
+
+ for (; y != y1; y += sy) {
+ points.push_back(Vector2(x, y));
+
+ err -= dx;
+ if (err < 0) {
+ x += sx;
+ err += dy;
+ }
+ }
+ }
+
+ points.push_back(Vector2(x, y));
+
+ return points;
+}
+
+bool TileMapEditor::forward_input_event(const InputEvent& p_event) {
+
+ if (!node || !node->get_tileset().is_valid() || !node->is_visible())
+ return false;
+
+ Matrix32 xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * node->get_global_transform();
+ Matrix32 xform_inv = xform.affine_inverse();
+
+ switch(p_event.type) {
+
+ case InputEvent::MOUSE_BUTTON: {
+
+ const InputEventMouseButton &mb=p_event.mouse_button;
+
+ if (mb.button_index==BUTTON_LEFT) {
+
+ if (mb.pressed) {
+
+ if (Input::get_singleton()->is_key_pressed(KEY_SPACE))
+ return false; //drag
+
+ if (tool==TOOL_NONE) {
+
+ if (mb.mod.shift) {
+
+ if (mb.mod.control)
+ tool=TOOL_RECTANGLE_PAINT;
+ else
+ tool=TOOL_LINE_PAINT;
+
+ selection_active=false;
+ rectangle_begin=over_tile;
+
+ return true;
+ }
+
+ if (mb.mod.control) {
+
+ tool=TOOL_PICKING;
+ _pick_tile(over_tile);
+
+ return true;
+ }
+
+ tool=TOOL_PAINTING;
+ }
+
+ if (tool==TOOL_PAINTING) {
+
+ int id = get_selected_tile();
+
+ if (id!=TileMap::INVALID_CELL) {
+
+ tool=TOOL_PAINTING;
+
+ paint_undo.clear();
+ paint_undo[over_tile]=_get_op_from_cell(over_tile);
+
+ _set_cell(over_tile, id, flip_h, flip_v, transpose);
+ }
+ } else if (tool==TOOL_PICKING) {
+
+ _pick_tile(over_tile);
+ } else if (tool==TOOL_SELECTING) {
+
+ selection_active=true;
+ rectangle_begin=over_tile;
+ }
+
+ return true;
+
+ } else {
+
+ if (tool!=TOOL_NONE) {
+
+ if (tool==TOOL_PAINTING) {
+
+ int id=get_selected_tile();
+
+ if (id!=TileMap::INVALID_CELL && paint_undo.size()) {
+
+ undo_redo->create_action(TTR("Paint TileMap"));
+ for (Map<Point2i,CellOp>::Element *E=paint_undo.front();E;E=E->next()) {
+
+ Point2 p=E->key();
+ undo_redo->add_do_method(node,"set_cellv",p,id,flip_h,flip_v,transpose);
+ undo_redo->add_undo_method(node,"set_cellv",p,E->get().idx,E->get().xf,E->get().yf,E->get().tr);
+ }
+ undo_redo->commit_action();
+
+ paint_undo.clear();
+ }
+ } else if (tool==TOOL_LINE_PAINT) {
+
+ int id=get_selected_tile();
+
+ if (id!=TileMap::INVALID_CELL) {
+
+ undo_redo->create_action("Line Draw");
+ for (Map<Point2i,CellOp>::Element *E=paint_undo.front();E;E=E->next()) {
+
+ _set_cell(E->key(), id, flip_h, flip_v, transpose, true);
+ }
+ undo_redo->commit_action();
+
+ paint_undo.clear();
+
+ canvas_item_editor->update();
+ }
+ } else if (tool==TOOL_RECTANGLE_PAINT) {
+
+ int id=get_selected_tile();
+
+ if (id!=TileMap::INVALID_CELL) {
+
+ undo_redo->create_action("Rectangle Paint");
+ for (int i=rectangle.pos.y;i<=rectangle.pos.y+rectangle.size.y;i++) {
+ for (int j=rectangle.pos.x;j<=rectangle.pos.x+rectangle.size.x;j++) {
+
+ _set_cell(Point2i(j, i), id, flip_h, flip_v, transpose, true);
+ }
+ }
+ undo_redo->commit_action();
+
+ canvas_item_editor->update();
+ }
+ } else if (tool==TOOL_DUPLICATING) {
+
+ Point2 ofs = over_tile-rectangle.pos;
+
+ undo_redo->create_action(TTR("Duplicate"));
+ for (List<TileData>::Element *E=copydata.front();E;E=E->next()) {
+
+ _set_cell(E->get().pos+ofs,E->get().cell,E->get().flip_h,E->get().flip_v,E->get().transpose,true);
+ }
+ undo_redo->commit_action();
+
+ copydata.clear();
+
+ canvas_item_editor->update();
+
+ } else if (tool==TOOL_SELECTING) {
+
+ canvas_item_editor->update();
+
+ } else if (tool==TOOL_BUCKET) {
+
+ Dictionary pop;
+ pop["id"] = node->get_cell(over_tile.x, over_tile.y);
+ pop["flip_h"] = node->is_cell_x_flipped(over_tile.x, over_tile.y);
+ pop["flip_v"] = node->is_cell_y_flipped(over_tile.x, over_tile.y);
+ pop["transpose"] = node->is_cell_transposed(over_tile.x, over_tile.y);
+
+ DVector<Vector2> points = _bucket_fill(over_tile);
+
+ if (points.size() == 0)
+ return false;
+
+ Dictionary op;
+ op["id"] = get_selected_tile();
+ op["flip_h"] = flip_h;
+ op["flip_v"] = flip_v;
+ op["transpose"] = transpose;
+
+ undo_redo->create_action("Bucket Fill");
+
+ undo_redo->add_do_method(this, "_fill_points", points, op);
+ undo_redo->add_undo_method(this, "_fill_points", points, pop);
+
+ undo_redo->commit_action();
+ }
+
+ tool=TOOL_NONE;
+
+ return true;
+ }
+ }
+ } else if (mb.button_index==BUTTON_RIGHT) {
+
+ if (mb.pressed) {
+
+ if (tool==TOOL_SELECTING || selection_active) {
+
+ tool=TOOL_NONE;
+ selection_active=false;
+
+ canvas_item_editor->update();
+
+ return true;
+ }
+
+ if (tool==TOOL_DUPLICATING) {
+
+ tool=TOOL_NONE;
+ copydata.clear();
+
+ canvas_item_editor->update();
+
+ return true;
+ }
+
+ if (tool==TOOL_NONE) {
+
+ paint_undo.clear();
+
+ Point2 local = node->world_to_map(xform_inv.xform(Point2(mb.x, mb.y)));
+
+ if (mb.mod.shift) {
+
+ if (mb.mod.control)
+ tool=TOOL_RECTANGLE_ERASE;
+ else
+ tool=TOOL_LINE_ERASE;
+
+ selection_active=false;
+ rectangle_begin=local;
+ } else {
+
+ tool=TOOL_ERASING;
+
+ paint_undo[local]=_get_op_from_cell(local);
+ _set_cell(local, TileMap::INVALID_CELL);
+ }
+
+ return true;
+ }
+
+ } else {
+ if (tool==TOOL_ERASING || tool==TOOL_RECTANGLE_ERASE || tool==TOOL_LINE_ERASE) {
+
+ if (paint_undo.size()) {
+ undo_redo->create_action(TTR("Erase TileMap"));
+ for (Map<Point2i,CellOp>::Element *E=paint_undo.front();E;E=E->next()) {
+
+ Point2 p=E->key();
+ undo_redo->add_do_method(node,"set_cellv",p,TileMap::INVALID_CELL,false,false,false);
+ undo_redo->add_undo_method(node,"set_cellv",p,E->get().idx,E->get().xf,E->get().yf,E->get().tr);
+ }
+
+ undo_redo->commit_action();
+ paint_undo.clear();
+ }
+
+ if (tool==TOOL_RECTANGLE_ERASE || tool==TOOL_LINE_ERASE) {
+ canvas_item_editor->update();
+ }
+
+ tool=TOOL_NONE;
+
+ return true;
+
+ } else if (tool==TOOL_BUCKET) {
+
+ Dictionary pop;
+ pop["id"] = node->get_cell(over_tile.x, over_tile.y);
+ pop["flip_h"] = node->is_cell_x_flipped(over_tile.x, over_tile.y);
+ pop["flip_v"] = node->is_cell_y_flipped(over_tile.x, over_tile.y);
+ pop["transpose"] = node->is_cell_transposed(over_tile.x, over_tile.y);
+
+ DVector<Vector2> points = _bucket_fill(over_tile, true);
+
+ if (points.size() == 0)
+ return false;
+
+ undo_redo->create_action("Bucket Fill");
+
+ undo_redo->add_do_method(this, "_erase_points", points);
+ undo_redo->add_undo_method(this, "_fill_points", points, pop);
+
+ undo_redo->commit_action();
+ }
+ }
+ }
+ } break;
+ case InputEvent::MOUSE_MOTION: {
+
+ const InputEventMouseMotion &mm=p_event.mouse_motion;
+
+ Point2i new_over_tile = node->world_to_map(xform_inv.xform(Point2(mm.x,mm.y)));
+
+ if (new_over_tile!=over_tile) {
+
+ over_tile=new_over_tile;
+ canvas_item_editor->update();
+ }
+
+ int tile_under = node->get_cell(over_tile.x, over_tile.y);
+ String tile_name = "none";
+
+ if (node->get_tileset()->has_tile(tile_under))
+ tile_name = node->get_tileset()->tile_get_name(tile_under);
+ tile_info->set_text(String::num(over_tile.x)+", "+String::num(over_tile.y)+" ["+tile_name+"]");
+
+ if (tool==TOOL_PAINTING) {
+
+ int id = get_selected_tile();
+ if (id!=TileMap::INVALID_CELL) {
+
+ if (!paint_undo.has(over_tile)) {
+ paint_undo[over_tile]=_get_op_from_cell(over_tile);
+ }
+
+ _set_cell(over_tile, id, flip_h, flip_v, transpose);
+
+ return true;
+ }
+ }
+
+ if (tool==TOOL_SELECTING) {
+
+ _select(rectangle_begin, over_tile);
+
+ return true;
+ }
+
+ if (tool==TOOL_LINE_PAINT || tool==TOOL_LINE_ERASE) {
+
+ int id = get_selected_tile();
+ bool erasing = (tool==TOOL_LINE_ERASE);
+
+ if (erasing && paint_undo.size()) {
+
+ for (Map<Point2i, CellOp>::Element *E=paint_undo.front();E;E=E->next()) {
+
+ _set_cell(E->key(), E->get().idx, E->get().xf, E->get().yf, E->get().tr);
+ }
+ }
+
+ paint_undo.clear();
+
+ if (id!=TileMap::INVALID_CELL) {
+
+ Vector<Point2i> points = line(rectangle_begin.x, over_tile.x, rectangle_begin.y, over_tile.y);
+
+ for (int i=0;i<points.size();i++) {
+
+ paint_undo[points[i]]=_get_op_from_cell(points[i]);
+
+ if (erasing)
+ _set_cell(points[i], TileMap::INVALID_CELL);
+ }
+
+ canvas_item_editor->update();
+ }
+
+ return true;
+ }
+ if (tool==TOOL_RECTANGLE_PAINT || tool==TOOL_RECTANGLE_ERASE) {
+
+ _select(rectangle_begin, over_tile);
+
+ if (tool==TOOL_RECTANGLE_ERASE) {
+
+ if (paint_undo.size()) {
+
+ for (Map<Point2i, CellOp>::Element *E=paint_undo.front();E;E=E->next()) {
+
+ _set_cell(E->key(), E->get().idx, E->get().xf, E->get().yf, E->get().tr);
+ }
+ }
+
+ paint_undo.clear();
+
+ for (int i=rectangle.pos.y;i<=rectangle.pos.y+rectangle.size.y;i++) {
+ for (int j=rectangle.pos.x;j<=rectangle.pos.x+rectangle.size.x;j++) {
+
+ Point2i tile = Point2i(j, i);
+ paint_undo[tile]=_get_op_from_cell(tile);
+
+ _set_cell(tile, TileMap::INVALID_CELL);
+ }
+ }
+ }
+
+ return true;
+ }
+ if (tool==TOOL_ERASING) {
+
+ if (!paint_undo.has(over_tile)) {
+ paint_undo[over_tile]=_get_op_from_cell(over_tile);
+ }
+
+ _set_cell(over_tile, TileMap::INVALID_CELL);
+
+ return true;
+ }
+ if (tool==TOOL_PICKING && Input::get_singleton()->is_mouse_button_pressed(BUTTON_LEFT)) {
+
+ _pick_tile(over_tile);
+
+ return true;
+ }
+ } break;
+ case InputEvent::KEY: {
+
+ const InputEventKey &k = p_event.key;
+
+ if (!k.pressed)
+ break;
+
+ if (k.scancode==KEY_ESCAPE) {
+
+ if (tool==TOOL_DUPLICATING)
+ copydata.clear();
+ else if (tool==TOOL_SELECTING || selection_active)
+ selection_active=false;
+
+ tool=TOOL_NONE;
+
+ canvas_item_editor->update();
+
+ return true;
+ }
+
+ if (tool!=TOOL_NONE || !mouse_over)
+ return false;
+
+ if (ED_IS_SHORTCUT("tile_map_editor/erase_selection", p_event)) {
+ _menu_option(OPTION_ERASE_SELECTION);
+
+ return true;
+ }
+ if (ED_IS_SHORTCUT("tile_map_editor/select", p_event)) {
+ tool=TOOL_SELECTING;
+ selection_active=false;
+
+ canvas_item_editor->update();
+
+ return true;
+ }
+ if (ED_IS_SHORTCUT("tile_map_editor/duplicate_selection", p_event)) {
+ _update_copydata();
+
+ if (selection_active) {
+ tool=TOOL_DUPLICATING;
+
+ canvas_item_editor->update();
+
+ return true;
+ }
+ }
+ if (ED_IS_SHORTCUT("tile_map_editor/find_tile", p_event)) {
+ search_box->select_all();
+ search_box->grab_focus();
+
+ return true;
+ }
+ if (ED_IS_SHORTCUT("tile_map_editor/mirror_x", p_event)) {
+ flip_h=!flip_h;
+ mirror_x->set_pressed(flip_h);
+ canvas_item_editor->update();
+ return true;
+ }
+ if (ED_IS_SHORTCUT("tile_map_editor/mirror_y", p_event)) {
+ flip_v=!flip_v;
+ mirror_y->set_pressed(flip_v);
+ canvas_item_editor->update();
+ return true;
+ }
+ if (ED_IS_SHORTCUT("tile_map_editor/transpose", p_event)) {
+ transpose = !transpose;
+ transp->set_pressed(transpose);
+ canvas_item_editor->update();
+ return true;
+ }
+ } break;
+ }
+
+ return false;
+}
+
+void TileMapEditor::_canvas_draw() {
+
+ if (!node)
+ return;
+
+ Matrix32 cell_xf = node->get_cell_transform();
+
+ 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=node->world_to_map(xform_inv.xform(Vector2()));
+ aabb.expand_to(node->world_to_map(xform_inv.xform(Vector2(0,screen_size.height))));
+ aabb.expand_to(node->world_to_map(xform_inv.xform(Vector2(screen_size.width,0))));
+ aabb.expand_to(node->world_to_map(xform_inv.xform(screen_size)));
+ Rect2i si=aabb.grow(1.0);
+
+ if (node->get_half_offset()!=TileMap::HALF_OFFSET_X) {
+
+ int max_lines=2000; //avoid crash if size too smal
+
+ for (int i=(si.pos.x)-1;i<=(si.pos.x+si.size.x);i++) {
+
+ Vector2 from = xform.xform(node->map_to_world(Vector2(i,si.pos.y)));
+ Vector2 to = xform.xform(node->map_to_world(Vector2(i,si.pos.y+si.size.y+1)));
+
+ 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(from,to,col,1);
+ if (max_lines--==0)
+ break;
+ }
+ } else {
+
+ int max_lines=10000; //avoid crash if size too smal
+
+ for (int i=(si.pos.x)-1;i<=(si.pos.x+si.size.x);i++) {
+
+ for (int j=(si.pos.y)-1;j<=(si.pos.y+si.size.y);j++) {
+
+ Vector2 ofs;
+ if (ABS(j)&1) {
+ ofs=cell_xf[0]*0.5;
+ }
+
+ Vector2 from = xform.xform(node->map_to_world(Vector2(i,j),true)+ofs);
+ Vector2 to = xform.xform(node->map_to_world(Vector2(i,j+1),true)+ofs);
+ Color col=i==0?Color(1,0.8,0.2,0.5):Color(1,0.3,0.1,0.2);
+ canvas_item_editor->draw_line(from,to,col,1);
+
+ if (max_lines--==0)
+ break;
+
+ }
+
+ }
+ }
+
+ int max_lines=10000; //avoid crash if size too smal
+
+ if (node->get_half_offset()!=TileMap::HALF_OFFSET_Y) {
+
+ for (int i=(si.pos.y)-1;i<=(si.pos.y+si.size.y);i++) {
+
+ Vector2 from = xform.xform(node->map_to_world(Vector2(si.pos.x,i)));
+ Vector2 to = xform.xform(node->map_to_world(Vector2(si.pos.x+si.size.x+1,i)));
+
+ 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(from,to,col,1);
+
+ if (max_lines--==0)
+ break;
+
+ }
+ } else {
+
+
+ for (int i=(si.pos.y)-1;i<=(si.pos.y+si.size.y);i++) {
+
+ for (int j=(si.pos.x)-1;j<=(si.pos.x+si.size.x);j++) {
+
+ Vector2 ofs;
+ if (ABS(j)&1) {
+ ofs=cell_xf[1]*0.5;
+ }
+
+ Vector2 from = xform.xform(node->map_to_world(Vector2(j,i),true)+ofs);
+ Vector2 to = xform.xform(node->map_to_world(Vector2(j+1,i),true)+ofs);
+ Color col=i==0?Color(1,0.8,0.2,0.5):Color(1,0.3,0.1,0.2);
+ canvas_item_editor->draw_line(from,to,col,1);
+
+ if (max_lines--==0)
+ break;
+
+ }
+ }
+ }
+ }
+
+ if (selection_active) {
+
+ Vector<Vector2> points;
+ points.push_back( xform.xform( node->map_to_world(( rectangle.pos ) )));
+ points.push_back( xform.xform( node->map_to_world((rectangle.pos+Point2(rectangle.size.x+1,0)) ) ));
+ points.push_back( xform.xform( node->map_to_world((rectangle.pos+Point2(rectangle.size.x+1,rectangle.size.y+1)) ) ));
+ points.push_back( xform.xform( node->map_to_world((rectangle.pos+Point2(0,rectangle.size.y+1)) ) ));
+
+ canvas_item_editor->draw_colored_polygon(points, Color(0.2,0.8,1,0.4));
+ }
+
+ if (mouse_over){
+
+ Vector2 endpoints[4]={
+ node->map_to_world(over_tile, true),
+ node->map_to_world((over_tile+Point2(1,0)), true),
+ node->map_to_world((over_tile+Point2(1,1)), true),
+ node->map_to_world((over_tile+Point2(0,1)), true)
+ };
+
+ for (int i=0;i<4;i++) {
+ if (node->get_half_offset()==TileMap::HALF_OFFSET_X && ABS(over_tile.y)&1)
+ endpoints[i]+=cell_xf[0]*0.5;
+ if (node->get_half_offset()==TileMap::HALF_OFFSET_Y && ABS(over_tile.x)&1)
+ endpoints[i]+=cell_xf[1]*0.5;
+ endpoints[i]=xform.xform(endpoints[i]);
+ }
+ 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);
+
+
+ bool bucket_preview = EditorSettings::get_singleton()->get("tile_map/bucket_fill_preview");
+ if (tool==TOOL_SELECTING || tool==TOOL_PICKING || !bucket_preview) {
+ return;
+ }
+
+ if (tool==TOOL_LINE_PAINT) {
+
+ if (paint_undo.empty())
+ return;
+
+ int id = get_selected_tile();
+
+ if (id==TileMap::INVALID_CELL)
+ return;
+
+ for (Map<Point2i, CellOp>::Element *E=paint_undo.front();E;E=E->next()) {
+
+ _draw_cell(id, E->key(), flip_h, flip_v, transpose, xform);
+ }
+
+ } else if (tool==TOOL_RECTANGLE_PAINT) {
+
+ int id = get_selected_tile();
+
+ if (id==TileMap::INVALID_CELL)
+ return;
+
+ for (int i=rectangle.pos.y;i<=rectangle.pos.y+rectangle.size.y;i++) {
+ for (int j=rectangle.pos.x;j<=rectangle.pos.x+rectangle.size.x;j++) {
+
+ _draw_cell(id, Point2i(j, i), flip_h, flip_v, transpose, xform);
+ }
+ }
+ } else if (tool==TOOL_DUPLICATING) {
+
+ if (copydata.empty())
+ return;
+
+ Ref<TileSet> ts = node->get_tileset();
+
+ if (ts.is_null())
+ return;
+
+ Point2 ofs = over_tile-rectangle.pos;
+
+ for (List<TileData>::Element *E=copydata.front();E;E=E->next()) {
+
+ if (!ts->has_tile(E->get().cell))
+ continue;
+
+ TileData tcd = E->get();
+
+ _draw_cell(tcd.cell, tcd.pos+ofs, tcd.flip_h, tcd.flip_v, tcd.transpose, xform);
+ }
+
+ Rect2i duplicate=rectangle;
+ duplicate.pos=over_tile;
+
+ Vector<Vector2> points;
+ points.push_back( xform.xform( node->map_to_world(duplicate.pos ) ));
+ points.push_back( xform.xform( node->map_to_world((duplicate.pos+Point2(duplicate.size.x+1,0)) ) ));
+ points.push_back( xform.xform( node->map_to_world((duplicate.pos+Point2(duplicate.size.x+1,duplicate.size.y+1))) ));
+ points.push_back( xform.xform( node->map_to_world((duplicate.pos+Point2(0,duplicate.size.y+1))) ));
+
+ canvas_item_editor->draw_colored_polygon(points, Color(0.2,1.0,0.8,0.2));
+
+ } else if(tool == TOOL_BUCKET) {
+
+ int tile = get_selected_tile();
+ _draw_fill_preview(tile, over_tile, flip_h, flip_v, transpose, xform);
+
+ } else {
+
+ int st = get_selected_tile();
+
+ if (st==TileMap::INVALID_CELL)
+ return;
+
+ _draw_cell(st, over_tile, flip_h, flip_v, transpose, xform);
+ }
+ }
+}
+
+void TileMapEditor::edit(Node *p_tile_map) {
+
+ search_box->set_text("");
+
+ if (!canvas_item_editor) {
+ canvas_item_editor=CanvasItemEditor::get_singleton()->get_viewport_control();
+ }
+
+ if (node)
+ node->disconnect("settings_changed",this,"_tileset_settings_changed");
+ 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();
+ }
+
+ if (node)
+ node->connect("settings_changed",this,"_tileset_settings_changed");
+
+ _clear_bucket_cache();
+
+}
+
+void TileMapEditor::_tileset_settings_changed() {
+
+ _update_palette();
+
+ if (canvas_item_editor)
+ canvas_item_editor->update();
+}
+
+void TileMapEditor::_icon_size_changed(float p_value) {
+ if (node) {
+ palette->set_icon_scale(p_value);
+ _update_palette();
+ }
+}
+
+void TileMapEditor::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("_text_entered"),&TileMapEditor::_text_entered);
+ ObjectTypeDB::bind_method(_MD("_text_changed"),&TileMapEditor::_text_changed);
+ ObjectTypeDB::bind_method(_MD("_sbox_input"),&TileMapEditor::_sbox_input);
+ ObjectTypeDB::bind_method(_MD("_menu_option"),&TileMapEditor::_menu_option);
+ ObjectTypeDB::bind_method(_MD("_canvas_draw"),&TileMapEditor::_canvas_draw);
+ ObjectTypeDB::bind_method(_MD("_canvas_mouse_enter"),&TileMapEditor::_canvas_mouse_enter);
+ ObjectTypeDB::bind_method(_MD("_canvas_mouse_exit"),&TileMapEditor::_canvas_mouse_exit);
+ ObjectTypeDB::bind_method(_MD("_tileset_settings_changed"),&TileMapEditor::_tileset_settings_changed);
+ ObjectTypeDB::bind_method(_MD("_update_transform_buttons"),&TileMapEditor::_update_transform_buttons);
+
+ ObjectTypeDB::bind_method(_MD("_fill_points"),&TileMapEditor::_fill_points);
+ ObjectTypeDB::bind_method(_MD("_erase_points"),&TileMapEditor::_erase_points);
+
+ ObjectTypeDB::bind_method(_MD("_icon_size_changed"), &TileMapEditor::_icon_size_changed);
+}
+
+TileMapEditor::CellOp TileMapEditor::_get_op_from_cell(const Point2i& p_pos)
+{
+ CellOp op;
+ op.idx = node->get_cell(p_pos.x,p_pos.y);
+ if (op.idx!=TileMap::INVALID_CELL) {
+ if (node->is_cell_x_flipped(p_pos.x,p_pos.y))
+ op.xf=true;
+ if (node->is_cell_y_flipped(p_pos.x,p_pos.y))
+ op.yf=true;
+ if (node->is_cell_transposed(p_pos.x,p_pos.y))
+ op.tr=true;
+ }
+ return op;
+}
+
+void TileMapEditor::_update_transform_buttons(Object *p_button) {
+ //ERR_FAIL_NULL(p_button);
+ ToolButton *b=p_button->cast_to<ToolButton>();
+ //ERR_FAIL_COND(!b);
+
+ if (b == rotate_0) {
+ mirror_x->set_pressed(false);
+ mirror_y->set_pressed(false);
+ transp->set_pressed(false);
+ }
+ else if (b == rotate_90) {
+ mirror_x->set_pressed(true);
+ mirror_y->set_pressed(false);
+ transp->set_pressed(true);
+ }
+ else if (b == rotate_180) {
+ mirror_x->set_pressed(true);
+ mirror_y->set_pressed(true);
+ transp->set_pressed(false);
+ }
+ else if (b == rotate_270) {
+ mirror_x->set_pressed(false);
+ mirror_y->set_pressed(true);
+ transp->set_pressed(true);
+ }
+
+ flip_h=mirror_x->is_pressed();
+ flip_v=mirror_y->is_pressed();
+ transpose=transp->is_pressed();
+
+ rotate_0->set_pressed(!flip_h && !flip_v && !transpose);
+ rotate_90->set_pressed(flip_h && !flip_v && transpose);
+ rotate_180->set_pressed(flip_h && flip_v && !transpose);
+ rotate_270->set_pressed(!flip_h && flip_v && transpose);
+}
+
+TileMapEditor::TileMapEditor(EditorNode *p_editor) {
+
+ node=NULL;
+ canvas_item_editor=NULL;
+ editor=p_editor;
+ undo_redo=editor->get_undo_redo();
+
+ tool=TOOL_NONE;
+ selection_active=false;
+ mouse_over=false;
+
+ flip_h=false;
+ flip_v=false;
+ transpose=false;
+
+ bucket_cache_tile = -1;
+ bucket_cache_visited = 0;
+
+ ED_SHORTCUT("tile_map_editor/erase_selection", TTR("Erase selection"), KEY_DELETE);
+ ED_SHORTCUT("tile_map_editor/find_tile", TTR("Find tile"), KEY_MASK_CMD+KEY_F);
+ ED_SHORTCUT("tile_map_editor/transpose", TTR("Transpose"));
+ ED_SHORTCUT("tile_map_editor/mirror_x", TTR("Mirror X"), KEY_A);
+ ED_SHORTCUT("tile_map_editor/mirror_y", TTR("Mirror Y"), KEY_S);
+
+ search_box = memnew( LineEdit );
+ search_box->set_h_size_flags(SIZE_EXPAND_FILL);
+ search_box->connect("text_entered", this, "_text_entered");
+ search_box->connect("text_changed", this, "_text_changed");
+ search_box->connect("input_event", this, "_sbox_input");
+ add_child(search_box);
+
+ size_slider = memnew( HSlider );
+ size_slider->set_h_size_flags(SIZE_EXPAND_FILL);
+ size_slider->set_min(0.1f);
+ size_slider->set_max(4.0f);
+ size_slider->set_step(0.1f);
+ size_slider->set_val(1.0f);
+ size_slider->connect("value_changed", this, "_icon_size_changed");
+ add_child(size_slider);
+
+ int mw = EDITOR_DEF("tile_map/palette_min_width", 80);
+
+ // Add tile palette
+ palette = memnew( ItemList );
+ palette->set_v_size_flags(SIZE_EXPAND_FILL);
+ palette->set_custom_minimum_size(Size2(mw,0));
+ palette->set_max_columns(0);
+ palette->set_icon_mode(ItemList::ICON_MODE_TOP);
+ palette->set_max_text_lines(2);
+ add_child(palette);
+
+ // Add menu items
+ toolbar = memnew( HBoxContainer );
+ toolbar->set_h_size_flags(SIZE_EXPAND_FILL);
+ toolbar->set_alignment(BoxContainer::ALIGN_END);
+ CanvasItemEditor::get_singleton()->add_control_to_menu_panel(toolbar);
+
+ // Tile position
+ tile_info = memnew( Label );
+ toolbar->add_child(tile_info);
+
+ options = memnew( MenuButton );
+ options->set_text("Tile Map");
+ options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("TileMap", "EditorIcons"));
+ options->set_process_unhandled_key_input(false);
+
+ PopupMenu *p = options->get_popup();
+
+ p->add_item(TTR("Bucket"), OPTION_BUCKET);
+ p->add_separator();
+ p->add_item(TTR("Pick Tile"), OPTION_PICK_TILE, KEY_CONTROL);
+ p->add_separator();
+ p->add_shortcut(ED_SHORTCUT("tile_map_editor/select", TTR("Select"), KEY_MASK_CMD+KEY_B), OPTION_SELECT);
+ p->add_shortcut(ED_SHORTCUT("tile_map_editor/duplicate_selection", TTR("Duplicate Selection"), KEY_MASK_CMD+KEY_D), OPTION_DUPLICATE);
+ p->add_shortcut(ED_GET_SHORTCUT("tile_map_editor/erase_selection"), OPTION_ERASE_SELECTION);
+
+ p->connect("item_pressed", this, "_menu_option");
+
+ toolbar->add_child(options);
+
+ toolbar->add_child( memnew( VSeparator ) );
+
+ transp = memnew( ToolButton );
+ transp->set_toggle_mode(true);
+ transp->set_tooltip(TTR("Transpose") + " ("+ED_GET_SHORTCUT("tile_map_editor/transpose")->get_as_text()+")");
+ transp->set_focus_mode(FOCUS_NONE);
+ transp->connect("pressed", this, "_update_transform_buttons", make_binds(transp));
+ toolbar->add_child(transp);
+ mirror_x = memnew( ToolButton );
+ mirror_x->set_toggle_mode(true);
+ mirror_x->set_tooltip(TTR("Mirror X") + " ("+ED_GET_SHORTCUT("tile_map_editor/mirror_x")->get_as_text()+")");
+ mirror_x->set_focus_mode(FOCUS_NONE);
+ mirror_x->connect("pressed", this, "_update_transform_buttons", make_binds(mirror_x));
+ toolbar->add_child(mirror_x);
+ mirror_y = memnew( ToolButton );
+ mirror_y->set_toggle_mode(true);
+ mirror_y->set_tooltip(TTR("Mirror Y") + " ("+ED_GET_SHORTCUT("tile_map_editor/mirror_y")->get_as_text()+")");
+ mirror_y->set_focus_mode(FOCUS_NONE);
+ mirror_y->connect("pressed", this, "_update_transform_buttons", make_binds(mirror_y));
+ toolbar->add_child(mirror_y);
+
+ toolbar->add_child( memnew( VSeparator ) );
+
+ rotate_0 = memnew( ToolButton );
+ rotate_0->set_toggle_mode(true);
+ rotate_0->set_tooltip(TTR("Rotate 0 degrees"));
+ rotate_0->set_focus_mode(FOCUS_NONE);
+ rotate_0->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_0));
+ toolbar->add_child(rotate_0);
+ rotate_90 = memnew( ToolButton );
+ rotate_90->set_toggle_mode(true);
+ rotate_90->set_tooltip(TTR("Rotate 90 degrees"));
+ rotate_90->set_focus_mode(FOCUS_NONE);
+ rotate_90->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_90));
+ toolbar->add_child(rotate_90);
+ rotate_180 = memnew( ToolButton );
+ rotate_180->set_toggle_mode(true);
+ rotate_180->set_tooltip(TTR("Rotate 180 degrees"));
+ rotate_180->set_focus_mode(FOCUS_NONE);
+ rotate_180->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_180));
+ toolbar->add_child(rotate_180);
+ rotate_270 = memnew( ToolButton );
+ rotate_270->set_toggle_mode(true);
+ rotate_270->set_tooltip(TTR("Rotate 270 degrees"));
+ rotate_270->set_focus_mode(FOCUS_NONE);
+ rotate_270->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_270));
+ toolbar->add_child(rotate_270);
+ toolbar->hide();
+
+ rotate_0->set_pressed(true);
+}
+
+TileMapEditor::~TileMapEditor() {
+ _clear_bucket_cache();
+}
+
+///////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////
+
+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->get_toolbar()->show();
+ } else {
+
+ tile_map_editor->hide();
+ tile_map_editor->get_toolbar()->hide();
+ tile_map_editor->edit(NULL);
+ }
+}
+
+TileMapEditorPlugin::TileMapEditorPlugin(EditorNode *p_node) {
+
+ EDITOR_DEF("tile_map/preview_size",64);
+ EDITOR_DEF("tile_map/palette_item_hseparation",8);
+ EDITOR_DEF("tile_map/show_tile_names", true);
+ EDITOR_DEF("tile_map/bucket_fill_preview", true);
+
+ tile_map_editor = memnew( TileMapEditor(p_node) );
+ add_control_to_container(CONTAINER_CANVAS_EDITOR_SIDE, tile_map_editor);
+ tile_map_editor->hide();
+}
+
+TileMapEditorPlugin::~TileMapEditorPlugin()
+{
+}
+
diff --git a/editor/plugins/tile_map_editor_plugin.h b/editor/plugins/tile_map_editor_plugin.h
new file mode 100644
index 000000000..6989536d5
--- /dev/null
+++ b/editor/plugins/tile_map_editor_plugin.h
@@ -0,0 +1,207 @@
+/*************************************************************************/
+/* tile_map_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "editor/editor_plugin.h"
+#include "editor/editor_node.h"
+
+#include "scene/2d/tile_map.h"
+#include "scene/gui/line_edit.h"
+#include "scene/gui/tool_button.h"
+#include "scene/gui/menu_button.h"
+#include "scene/gui/label.h"
+
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+
+class TileMapEditor : public VBoxContainer {
+
+ OBJ_TYPE(TileMapEditor, VBoxContainer );
+
+ enum Tool {
+
+ TOOL_NONE,
+ TOOL_PAINTING,
+ TOOL_ERASING,
+ TOOL_RECTANGLE_PAINT,
+ TOOL_RECTANGLE_ERASE,
+ TOOL_LINE_PAINT,
+ TOOL_LINE_ERASE,
+ TOOL_SELECTING,
+ TOOL_BUCKET,
+ TOOL_PICKING,
+ TOOL_DUPLICATING,
+ };
+
+ enum Options {
+
+ OPTION_BUCKET,
+ OPTION_PICK_TILE,
+ OPTION_SELECT,
+ OPTION_DUPLICATE,
+ OPTION_ERASE_SELECTION
+ };
+
+ TileMap *node;
+
+ EditorNode *editor;
+ UndoRedo *undo_redo;
+ Control *canvas_item_editor;
+
+ LineEdit *search_box;
+ HSlider *size_slider;
+ ItemList *palette;
+
+ HBoxContainer *toolbar;
+
+ Label *tile_info;
+ MenuButton *options;
+ ToolButton *transp;
+ ToolButton *mirror_x;
+ ToolButton *mirror_y;
+ ToolButton *rotate_0;
+ ToolButton *rotate_90;
+ ToolButton *rotate_180;
+ ToolButton *rotate_270;
+
+ Tool tool;
+
+ bool selection_active;
+ bool mouse_over;
+
+ bool flip_h;
+ bool flip_v;
+ bool transpose;
+
+ Point2i rectangle_begin;
+ Rect2i rectangle;
+
+ Point2i over_tile;
+
+ bool * bucket_cache_visited;
+ Rect2i bucket_cache_rect;
+ int bucket_cache_tile;
+ DVector<Vector2> bucket_cache;
+
+ struct CellOp {
+ int idx;
+ bool xf;
+ bool yf;
+ bool tr;
+
+ CellOp() { idx=-1; xf=false; yf=false; tr=false; }
+ };
+
+ Map<Point2i, CellOp> paint_undo;
+
+ struct TileData {
+ Point2i pos;
+ int cell;
+ bool flip_h;
+ bool flip_v;
+ bool transpose;
+ };
+
+ List<TileData> copydata;
+
+ void _pick_tile(const Point2& p_pos);
+
+ DVector<Vector2> _bucket_fill(const Point2i& p_start, bool erase=false, bool preview=false);
+
+ void _fill_points(const DVector<Vector2> p_points, const Dictionary& p_op);
+ void _erase_points(const DVector<Vector2> p_points);
+
+ void _select(const Point2i& p_from, const Point2i& p_to);
+
+ void _draw_cell(int p_cell, const Point2i& p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Matrix32& p_xform);
+ void _draw_fill_preview(int p_cell, const Point2i& p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Matrix32& p_xform);
+ void _clear_bucket_cache();
+
+ void _update_copydata();
+
+ int get_selected_tile() const;
+ void set_selected_tile(int p_tile);
+
+ void _text_entered(const String& p_text);
+ void _text_changed(const String& p_text);
+ void _sbox_input(const InputEvent& p_ie);
+ void _update_palette();
+ void _canvas_draw();
+ void _menu_option(int p_option);
+
+ void _set_cell(const Point2i& p_pos, int p_value, bool p_flip_h=false, bool p_flip_v=false, bool p_transpose=false, bool p_with_undo=false);
+
+ void _canvas_mouse_enter();
+ void _canvas_mouse_exit();
+ void _tileset_settings_changed();
+ void _icon_size_changed(float p_value);
+
+protected:
+
+ void _notification(int p_what);
+ static void _bind_methods();
+ CellOp _get_op_from_cell(const Point2i& p_pos);
+ void _update_transform_buttons(Object *p_button=NULL);
+
+public:
+
+ HBoxContainer *get_toolbar() const { return toolbar; }
+
+ bool forward_input_event(const InputEvent& p_event);
+ void edit(Node *p_tile_map);
+
+ TileMapEditor(EditorNode *p_editor);
+ ~TileMapEditor();
+};
+
+class TileMapEditorPlugin : public EditorPlugin {
+
+ OBJ_TYPE( TileMapEditorPlugin, EditorPlugin );
+
+ TileMapEditor *tile_map_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/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp
new file mode 100644
index 000000000..dd6edaf82
--- /dev/null
+++ b/editor/plugins/tile_set_editor_plugin.cpp
@@ -0,0 +1,295 @@
+/*************************************************************************/
+/* tile_set_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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();
+ Ref<CanvasItemMaterial> material=mi->get_material();
+
+ 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);
+ p_library->tile_set_material(id,material);
+
+ p_library->tile_set_modulate(id,mi->get_modulate());
+
+ Vector2 phys_offset;
+ Size2 s;
+
+ if (mi->is_region()) {
+ s=mi->get_region_rect().size;
+ p_library->tile_set_region(id,mi->get_region_rect());
+ } else {
+ const int frame = mi->get_frame();
+ const int hframes = mi->get_hframes();
+ s=texture->get_size()/Size2(hframes,mi->get_vframes());
+ p_library->tile_set_region(id,Rect2(Vector2(frame%hframes,frame/hframes)*s,s));
+ }
+
+ if (mi->is_centered()) {
+ phys_offset+=-s/2;
+ }
+
+ Vector<Ref<Shape2D> >collisions;
+ Ref<NavigationPolygon> nav_poly;
+ Ref<OccluderPolygon2D> occluder;
+
+ for(int j=0;j<mi->get_child_count();j++) {
+
+ Node *child2 = mi->get_child(j);
+
+ if (child2->cast_to<NavigationPolygonInstance>())
+ nav_poly=child2->cast_to<NavigationPolygonInstance>()->get_navigation_polygon();
+
+ if (child2->cast_to<LightOccluder2D>())
+ occluder=child2->cast_to<LightOccluder2D>()->get_occluder_polygon();
+
+ if (!child2->cast_to<StaticBody2D>())
+ continue;
+ StaticBody2D *sb = child2->cast_to<StaticBody2D>();
+ int shape_count = sb->get_shape_count();
+ if (shape_count==0)
+ continue;
+ for (int shape_index=0; shape_index<shape_count; ++shape_index)
+ {
+ Ref<Shape2D> collision=sb->get_shape(shape_index);
+ if (collision.is_valid()) {
+ collisions.push_back(collision);
+ }
+ }
+ }
+
+ if (collisions.size()) {
+
+ p_library->tile_set_shapes(id,collisions);
+ p_library->tile_set_shape_offset(id,-phys_offset);
+ } else {
+ p_library->tile_set_shape_offset(id,Vector2());
+
+ }
+
+ p_library->tile_set_texture_offset(id,mi->get_offset());
+ p_library->tile_set_navigation_polygon(id,nav_poly);
+ p_library->tile_set_light_occluder(id,occluder);
+ p_library->tile_set_occluder_offset(id,-phys_offset);
+ p_library->tile_set_navigation_polygon_offset(id,-phys_offset);
+
+
+ }
+}
+
+void TileSetEditor::_menu_confirm() {
+
+ switch(option) {
+
+ 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::_name_dialog_confirm(const String& name) {
+
+ switch (option) {
+
+ case MENU_OPTION_REMOVE_ITEM: {
+
+ int id=tileset->find_tile_by_name(name);
+
+ if (id<0 && name.is_valid_integer())
+ id=name.to_int();
+
+ if (tileset->has_tile(id)) {
+ tileset->remove_tile(id);
+ } else {
+ err_dialog->set_text(TTR("Could not find tile:")+" " + name);
+ err_dialog->popup_centered(Size2(300, 60));
+ }
+ } 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: {
+
+ nd->set_title(TTR("Remove Item"));
+ nd->set_text(TTR("Item name or ID:"));
+ nd->popup_centered(Size2(300, 95));
+ } break;
+ case MENU_OPTION_CREATE_FROM_SCENE: {
+
+ cd->set_text(TTR("Create from scene?"));
+ cd->popup_centered(Size2(300,60));
+ } break;
+ case MENU_OPTION_MERGE_FROM_SCENE: {
+
+ cd->set_text(TTR("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);
+ ObjectTypeDB::bind_method("_name_dialog_confirm",&TileSetEditor::_name_dialog_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(TTR("Add Item"),MENU_OPTION_ADD_ITEM);
+ options->get_popup()->add_item(TTR("Remove Item"),MENU_OPTION_REMOVE_ITEM);
+ options->get_popup()->add_separator();
+ options->get_popup()->add_item(TTR("Create from Scene"),MENU_OPTION_CREATE_FROM_SCENE);
+ options->get_popup()->add_item(TTR("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");
+
+ nd = memnew(EditorNameDialog);
+ add_child(nd);
+ nd->set_hide_on_ok(true);
+ nd->get_line_edit()->set_margin(MARGIN_TOP,28);
+ nd->connect("name_confirmed", this,"_name_dialog_confirm");
+
+ err_dialog = memnew(AcceptDialog);
+ add_child(err_dialog);
+ err_dialog->set_title(TTR("Error"));
+}
+
+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/editor/plugins/tile_set_editor_plugin.h b/editor/plugins/tile_set_editor_plugin.h
new file mode 100644
index 000000000..ffdc24192
--- /dev/null
+++ b/editor/plugins/tile_set_editor_plugin.h
@@ -0,0 +1,101 @@
+/*************************************************************************/
+/* tile_set_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 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 "editor/editor_node.h"
+#include "editor/editor_name_dialog.h"
+
+
+class TileSetEditor : public Control {
+
+ OBJ_TYPE( TileSetEditor, Control );
+
+ Ref<TileSet> tileset;
+
+ EditorNode *editor;
+ MenuButton *menu;
+ ConfirmationDialog *cd;
+ EditorNameDialog *nd;
+ AcceptDialog *err_dialog;
+
+ 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();
+ void _name_dialog_confirm(const String& name);
+
+ 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