1/**************************************************************************/
2/* animation_tree_editor_plugin.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "animation_tree_editor_plugin.h"
32
33#include "animation_blend_space_1d_editor.h"
34#include "animation_blend_space_2d_editor.h"
35#include "animation_blend_tree_editor_plugin.h"
36#include "animation_state_machine_editor.h"
37#include "core/config/project_settings.h"
38#include "core/input/input.h"
39#include "core/io/resource_loader.h"
40#include "core/math/delaunay_2d.h"
41#include "core/os/keyboard.h"
42#include "editor/editor_node.h"
43#include "editor/editor_scale.h"
44#include "editor/gui/editor_file_dialog.h"
45#include "scene/animation/animation_blend_tree.h"
46#include "scene/animation/animation_player.h"
47#include "scene/gui/menu_button.h"
48#include "scene/gui/panel.h"
49#include "scene/main/window.h"
50#include "scene/scene_string_names.h"
51
52void AnimationTreeEditor::edit(AnimationTree *p_tree) {
53 if (p_tree && !p_tree->is_connected("animation_player_changed", callable_mp(this, &AnimationTreeEditor::_animation_list_changed))) {
54 p_tree->connect("animation_player_changed", callable_mp(this, &AnimationTreeEditor::_animation_list_changed), CONNECT_DEFERRED);
55 }
56
57 if (tree == p_tree) {
58 return;
59 }
60
61 if (tree && tree->is_connected("animation_player_changed", callable_mp(this, &AnimationTreeEditor::_animation_list_changed))) {
62 tree->disconnect("animation_player_changed", callable_mp(this, &AnimationTreeEditor::_animation_list_changed));
63 }
64
65 tree = p_tree;
66
67 Vector<String> path;
68 if (tree) {
69 edit_path(path);
70 }
71}
72
73void AnimationTreeEditor::_node_removed(Node *p_node) {
74 if (p_node == tree) {
75 tree = nullptr;
76 _clear_editors();
77 }
78}
79
80void AnimationTreeEditor::_path_button_pressed(int p_path) {
81 edited_path.clear();
82 for (int i = 0; i <= p_path; i++) {
83 edited_path.push_back(button_path[i]);
84 }
85}
86
87void AnimationTreeEditor::_animation_list_changed() {
88 AnimationNodeBlendTreeEditor *bte = AnimationNodeBlendTreeEditor::get_singleton();
89 if (bte) {
90 bte->update_graph();
91 }
92}
93
94void AnimationTreeEditor::_update_path() {
95 while (path_hb->get_child_count() > 1) {
96 memdelete(path_hb->get_child(1));
97 }
98
99 Ref<ButtonGroup> group;
100 group.instantiate();
101
102 Button *b = memnew(Button);
103 b->set_text(TTR("Root"));
104 b->set_toggle_mode(true);
105 b->set_button_group(group);
106 b->set_pressed(true);
107 b->set_focus_mode(FOCUS_NONE);
108 b->connect("pressed", callable_mp(this, &AnimationTreeEditor::_path_button_pressed).bind(-1));
109 path_hb->add_child(b);
110 for (int i = 0; i < button_path.size(); i++) {
111 b = memnew(Button);
112 b->set_text(button_path[i]);
113 b->set_toggle_mode(true);
114 b->set_button_group(group);
115 path_hb->add_child(b);
116 b->set_pressed(true);
117 b->set_focus_mode(FOCUS_NONE);
118 b->connect("pressed", callable_mp(this, &AnimationTreeEditor::_path_button_pressed).bind(i));
119 }
120}
121
122void AnimationTreeEditor::edit_path(const Vector<String> &p_path) {
123 button_path.clear();
124
125 Ref<AnimationNode> node = tree->get_tree_root();
126
127 if (node.is_valid()) {
128 current_root = node->get_instance_id();
129
130 for (int i = 0; i < p_path.size(); i++) {
131 Ref<AnimationNode> child = node->get_child_by_name(p_path[i]);
132 ERR_BREAK(child.is_null());
133 node = child;
134 button_path.push_back(p_path[i]);
135 }
136
137 edited_path = button_path;
138
139 for (int i = 0; i < editors.size(); i++) {
140 if (editors[i]->can_edit(node)) {
141 editors[i]->edit(node);
142 editors[i]->show();
143 } else {
144 editors[i]->edit(Ref<AnimationNode>());
145 editors[i]->hide();
146 }
147 }
148 } else {
149 current_root = ObjectID();
150 edited_path = button_path;
151 for (int i = 0; i < editors.size(); i++) {
152 editors[i]->edit(Ref<AnimationNode>());
153 editors[i]->hide();
154 }
155 }
156
157 _update_path();
158}
159
160void AnimationTreeEditor::_clear_editors() {
161 button_path.clear();
162 current_root = ObjectID();
163 edited_path = button_path;
164 for (int i = 0; i < editors.size(); i++) {
165 editors[i]->edit(Ref<AnimationNode>());
166 editors[i]->hide();
167 }
168 _update_path();
169}
170
171Vector<String> AnimationTreeEditor::get_edited_path() const {
172 return button_path;
173}
174
175void AnimationTreeEditor::enter_editor(const String &p_path) {
176 Vector<String> path = edited_path;
177 path.push_back(p_path);
178 edit_path(path);
179}
180
181void AnimationTreeEditor::_notification(int p_what) {
182 switch (p_what) {
183 case NOTIFICATION_ENTER_TREE: {
184 get_tree()->connect("node_removed", callable_mp(this, &AnimationTreeEditor::_node_removed));
185 } break;
186 case NOTIFICATION_PROCESS: {
187 ObjectID root;
188 if (tree && tree->get_tree_root().is_valid()) {
189 root = tree->get_tree_root()->get_instance_id();
190 }
191
192 if (root != current_root) {
193 edit_path(Vector<String>());
194 }
195
196 if (button_path.size() != edited_path.size()) {
197 edit_path(edited_path);
198 }
199 } break;
200 case NOTIFICATION_EXIT_TREE: {
201 get_tree()->disconnect("node_removed", callable_mp(this, &AnimationTreeEditor::_node_removed));
202 } break;
203 }
204}
205
206void AnimationTreeEditor::_bind_methods() {
207}
208
209AnimationTreeEditor *AnimationTreeEditor::singleton = nullptr;
210
211void AnimationTreeEditor::add_plugin(AnimationTreeNodeEditorPlugin *p_editor) {
212 ERR_FAIL_COND(p_editor->get_parent());
213 editor_base->add_child(p_editor);
214 editors.push_back(p_editor);
215 p_editor->set_h_size_flags(SIZE_EXPAND_FILL);
216 p_editor->set_v_size_flags(SIZE_EXPAND_FILL);
217 p_editor->hide();
218}
219
220void AnimationTreeEditor::remove_plugin(AnimationTreeNodeEditorPlugin *p_editor) {
221 ERR_FAIL_COND(p_editor->get_parent() != editor_base);
222 editor_base->remove_child(p_editor);
223 editors.erase(p_editor);
224}
225
226String AnimationTreeEditor::get_base_path() {
227 String path = SceneStringNames::get_singleton()->parameters_base_path;
228 for (int i = 0; i < edited_path.size(); i++) {
229 path += edited_path[i] + "/";
230 }
231 return path;
232}
233
234bool AnimationTreeEditor::can_edit(const Ref<AnimationNode> &p_node) const {
235 for (int i = 0; i < editors.size(); i++) {
236 if (editors[i]->can_edit(p_node)) {
237 return true;
238 }
239 }
240 return false;
241}
242
243Vector<String> AnimationTreeEditor::get_animation_list() {
244 if (!singleton->tree || !singleton->is_visible()) {
245 // When tree is empty, singleton not in the main thread.
246 return Vector<String>();
247 }
248
249 AnimationTree *tree = singleton->tree;
250 if (!tree || !tree->has_node(tree->get_animation_player())) {
251 return Vector<String>();
252 }
253
254 AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(tree->get_node(tree->get_animation_player()));
255
256 if (!ap) {
257 return Vector<String>();
258 }
259
260 List<StringName> anims;
261 ap->get_animation_list(&anims);
262 Vector<String> ret;
263 for (const StringName &E : anims) {
264 ret.push_back(E);
265 }
266
267 return ret;
268}
269
270AnimationTreeEditor::AnimationTreeEditor() {
271 AnimationNodeAnimation::get_editable_animation_list = get_animation_list;
272 path_edit = memnew(ScrollContainer);
273 add_child(path_edit);
274 path_edit->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
275 path_hb = memnew(HBoxContainer);
276 path_edit->add_child(path_hb);
277 path_hb->add_child(memnew(Label(TTR("Path:"))));
278
279 add_child(memnew(HSeparator));
280
281 singleton = this;
282 editor_base = memnew(MarginContainer);
283 editor_base->set_v_size_flags(SIZE_EXPAND_FILL);
284 add_child(editor_base);
285
286 add_plugin(memnew(AnimationNodeBlendTreeEditor));
287 add_plugin(memnew(AnimationNodeBlendSpace1DEditor));
288 add_plugin(memnew(AnimationNodeBlendSpace2DEditor));
289 add_plugin(memnew(AnimationNodeStateMachineEditor));
290}
291
292void AnimationTreeEditorPlugin::edit(Object *p_object) {
293 anim_tree_editor->edit(Object::cast_to<AnimationTree>(p_object));
294}
295
296bool AnimationTreeEditorPlugin::handles(Object *p_object) const {
297 return p_object->is_class("AnimationTree");
298}
299
300void AnimationTreeEditorPlugin::make_visible(bool p_visible) {
301 if (p_visible) {
302 //editor->hide_animation_player_editors();
303 //editor->animation_panel_make_visible(true);
304 button->show();
305 EditorNode::get_singleton()->make_bottom_panel_item_visible(anim_tree_editor);
306 anim_tree_editor->set_process(true);
307 } else {
308 if (anim_tree_editor->is_visible_in_tree()) {
309 EditorNode::get_singleton()->hide_bottom_panel();
310 }
311 button->hide();
312 anim_tree_editor->set_process(false);
313 }
314}
315
316AnimationTreeEditorPlugin::AnimationTreeEditorPlugin() {
317 anim_tree_editor = memnew(AnimationTreeEditor);
318 anim_tree_editor->set_custom_minimum_size(Size2(0, 300) * EDSCALE);
319
320 button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("AnimationTree"), anim_tree_editor);
321 button->hide();
322}
323
324AnimationTreeEditorPlugin::~AnimationTreeEditorPlugin() {
325}
326