diff options
| author | Rémi Verschelde | 2017-03-18 23:45:45 +0100 |
|---|---|---|
| committer | Rémi Verschelde | 2017-03-18 23:45:45 +0100 |
| commit | 1b0e2b0c39f5fe36adaee8aa1a2eee39534850c0 (patch) | |
| tree | ac279269dcb840e5629b0f0fad33ccd43b04c439 /editor/plugins | |
| parent | 9d2c0f6c6e2603fa36fb376f9b0ab7d7d02ff8c8 (diff) | |
| download | godot-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')
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 = ©_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 = ©_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(¶ms); + + 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 |
