1 /*************************************************************************/
2 /*  animation_player_editor_plugin.cpp                                   */
3 /*************************************************************************/
4 /*                       This file is part of:                           */
5 /*                           GODOT ENGINE                                */
6 /*                      https://godotengine.org                          */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
9 /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
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_player_editor_plugin.h"
32 
33 #include "core/io/resource_loader.h"
34 #include "core/io/resource_saver.h"
35 #include "core/os/input.h"
36 #include "core/os/keyboard.h"
37 #include "core/project_settings.h"
38 #include "editor/animation_track_editor.h"
39 #include "editor/editor_scale.h"
40 #include "editor/editor_settings.h"
41 
42 // For onion skinning.
43 #include "editor/plugins/canvas_item_editor_plugin.h"
44 #include "editor/plugins/spatial_editor_plugin.h"
45 #include "scene/main/viewport.h"
46 #include "servers/visual_server.h"
47 
_node_removed(Node * p_node)48 void AnimationPlayerEditor::_node_removed(Node *p_node) {
49 
50 	if (player && player == p_node) {
51 		player = NULL;
52 
53 		set_process(false);
54 
55 		track_editor->set_animation(Ref<Animation>());
56 		track_editor->set_root(NULL);
57 		track_editor->show_select_node_warning(true);
58 		_update_player();
59 	}
60 }
61 
_notification(int p_what)62 void AnimationPlayerEditor::_notification(int p_what) {
63 
64 	switch (p_what) {
65 		case NOTIFICATION_PROCESS: {
66 
67 			if (!player)
68 				return;
69 
70 			updating = true;
71 
72 			if (player->is_playing()) {
73 
74 				{
75 					String animname = player->get_assigned_animation();
76 
77 					if (player->has_animation(animname)) {
78 						Ref<Animation> anim = player->get_animation(animname);
79 						if (!anim.is_null()) {
80 
81 							frame->set_max(anim->get_length());
82 						}
83 					}
84 				}
85 				frame->set_value(player->get_current_animation_position());
86 				track_editor->set_anim_pos(player->get_current_animation_position());
87 				EditorNode::get_singleton()->get_inspector()->refresh();
88 
89 			} else if (!player->is_valid()) {
90 				// Reset timeline when the player has been stopped externally
91 				frame->set_value(0);
92 			} else if (last_active) {
93 				// Need the last frame after it stopped.
94 				frame->set_value(player->get_current_animation_position());
95 			}
96 
97 			last_active = player->is_playing();
98 			updating = false;
99 		} break;
100 		case NOTIFICATION_ENTER_TREE: {
101 
102 			tool_anim->get_popup()->connect("id_pressed", this, "_animation_tool_menu");
103 
104 			onion_skinning->get_popup()->connect("id_pressed", this, "_onion_skinning_menu");
105 
106 			blend_editor.next->connect("item_selected", this, "_blend_editor_next_changed");
107 
108 			get_tree()->connect("node_removed", this, "_node_removed");
109 
110 			add_style_override("panel", editor->get_gui_base()->get_stylebox("panel", "Panel"));
111 		} break;
112 		case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
113 
114 			add_style_override("panel", editor->get_gui_base()->get_stylebox("panel", "Panel"));
115 		} break;
116 		case NOTIFICATION_THEME_CHANGED: {
117 
118 			autoplay->set_icon(get_icon("AutoPlay", "EditorIcons"));
119 
120 			play->set_icon(get_icon("PlayStart", "EditorIcons"));
121 			play_from->set_icon(get_icon("Play", "EditorIcons"));
122 			play_bw->set_icon(get_icon("PlayStartBackwards", "EditorIcons"));
123 			play_bw_from->set_icon(get_icon("PlayBackwards", "EditorIcons"));
124 
125 			autoplay_icon = get_icon("AutoPlay", "EditorIcons");
126 			stop->set_icon(get_icon("Stop", "EditorIcons"));
127 
128 			onion_toggle->set_icon(get_icon("Onion", "EditorIcons"));
129 			onion_skinning->set_icon(get_icon("GuiTabMenuHl", "EditorIcons"));
130 
131 			pin->set_icon(get_icon("Pin", "EditorIcons"));
132 
133 			tool_anim->add_style_override("normal", get_stylebox("normal", "Button"));
134 			track_editor->get_edit_menu()->add_style_override("normal", get_stylebox("normal", "Button"));
135 
136 #define ITEM_ICON(m_item, m_icon) tool_anim->get_popup()->set_item_icon(tool_anim->get_popup()->get_item_index(m_item), get_icon(m_icon, "EditorIcons"))
137 
138 			ITEM_ICON(TOOL_NEW_ANIM, "New");
139 			ITEM_ICON(TOOL_LOAD_ANIM, "Load");
140 			ITEM_ICON(TOOL_SAVE_ANIM, "Save");
141 			ITEM_ICON(TOOL_SAVE_AS_ANIM, "Save");
142 			ITEM_ICON(TOOL_DUPLICATE_ANIM, "Duplicate");
143 			ITEM_ICON(TOOL_RENAME_ANIM, "Rename");
144 			ITEM_ICON(TOOL_EDIT_TRANSITIONS, "Blend");
145 			ITEM_ICON(TOOL_EDIT_RESOURCE, "Edit");
146 			ITEM_ICON(TOOL_REMOVE_ANIM, "Remove");
147 		} break;
148 	}
149 }
150 
_autoplay_pressed()151 void AnimationPlayerEditor::_autoplay_pressed() {
152 
153 	if (updating)
154 		return;
155 	if (animation->get_item_count() == 0) {
156 		return;
157 	}
158 
159 	String current = animation->get_item_text(animation->get_selected());
160 	if (player->get_autoplay() == current) {
161 		//unset
162 		undo_redo->create_action(TTR("Toggle Autoplay"));
163 		undo_redo->add_do_method(player, "set_autoplay", "");
164 		undo_redo->add_undo_method(player, "set_autoplay", player->get_autoplay());
165 		undo_redo->add_do_method(this, "_animation_player_changed", player);
166 		undo_redo->add_undo_method(this, "_animation_player_changed", player);
167 		undo_redo->commit_action();
168 
169 	} else {
170 		//set
171 		undo_redo->create_action(TTR("Toggle Autoplay"));
172 		undo_redo->add_do_method(player, "set_autoplay", current);
173 		undo_redo->add_undo_method(player, "set_autoplay", player->get_autoplay());
174 		undo_redo->add_do_method(this, "_animation_player_changed", player);
175 		undo_redo->add_undo_method(this, "_animation_player_changed", player);
176 		undo_redo->commit_action();
177 	}
178 }
179 
_play_pressed()180 void AnimationPlayerEditor::_play_pressed() {
181 
182 	String current;
183 	if (animation->get_selected() >= 0 && animation->get_selected() < animation->get_item_count()) {
184 
185 		current = animation->get_item_text(animation->get_selected());
186 	}
187 
188 	if (current != "") {
189 
190 		if (current == player->get_assigned_animation())
191 			player->stop(); //so it won't blend with itself
192 		player->play(current);
193 	}
194 
195 	//unstop
196 	stop->set_pressed(false);
197 }
198 
_play_from_pressed()199 void AnimationPlayerEditor::_play_from_pressed() {
200 
201 	String current;
202 	if (animation->get_selected() >= 0 && animation->get_selected() < animation->get_item_count()) {
203 
204 		current = animation->get_item_text(animation->get_selected());
205 	}
206 
207 	if (current != "") {
208 
209 		float time = player->get_current_animation_position();
210 
211 		if (current == player->get_assigned_animation() && player->is_playing()) {
212 
213 			player->stop(); //so it won't blend with itself
214 		}
215 
216 		player->play(current);
217 		player->seek(time);
218 	}
219 
220 	//unstop
221 	stop->set_pressed(false);
222 }
223 
_play_bw_pressed()224 void AnimationPlayerEditor::_play_bw_pressed() {
225 
226 	String current;
227 	if (animation->get_selected() >= 0 && animation->get_selected() < animation->get_item_count()) {
228 
229 		current = animation->get_item_text(animation->get_selected());
230 	}
231 
232 	if (current != "") {
233 
234 		if (current == player->get_assigned_animation())
235 			player->stop(); //so it won't blend with itself
236 		player->play(current, -1, -1, true);
237 	}
238 
239 	//unstop
240 	stop->set_pressed(false);
241 }
242 
_play_bw_from_pressed()243 void AnimationPlayerEditor::_play_bw_from_pressed() {
244 
245 	String current;
246 	if (animation->get_selected() >= 0 && animation->get_selected() < animation->get_item_count()) {
247 
248 		current = animation->get_item_text(animation->get_selected());
249 	}
250 
251 	if (current != "") {
252 
253 		float time = player->get_current_animation_position();
254 		if (current == player->get_assigned_animation())
255 			player->stop(); //so it won't blend with itself
256 
257 		player->play(current, -1, -1, true);
258 		player->seek(time);
259 	}
260 
261 	//unstop
262 	stop->set_pressed(false);
263 }
_stop_pressed()264 void AnimationPlayerEditor::_stop_pressed() {
265 
266 	if (!player) {
267 		return;
268 	}
269 
270 	player->stop(false);
271 	play->set_pressed(false);
272 	stop->set_pressed(true);
273 }
274 
_animation_selected(int p_which)275 void AnimationPlayerEditor::_animation_selected(int p_which) {
276 
277 	if (updating)
278 		return;
279 	// when selecting an animation, the idea is that the only interesting behavior
280 	// ui-wise is that it should play/blend the next one if currently playing
281 	String current;
282 	if (animation->get_selected() >= 0 && animation->get_selected() < animation->get_item_count()) {
283 
284 		current = animation->get_item_text(animation->get_selected());
285 	}
286 
287 	if (current != "") {
288 
289 		player->set_assigned_animation(current);
290 
291 		Ref<Animation> anim = player->get_animation(current);
292 		{
293 
294 			track_editor->set_animation(anim);
295 			Node *root = player->get_node(player->get_root());
296 			if (root) {
297 				track_editor->set_root(root);
298 			}
299 		}
300 		frame->set_max(anim->get_length());
301 
302 	} else {
303 		track_editor->set_animation(Ref<Animation>());
304 		track_editor->set_root(NULL);
305 	}
306 
307 	autoplay->set_pressed(current == player->get_autoplay());
308 
309 	AnimationPlayerEditor::singleton->get_track_editor()->update_keying();
310 	EditorNode::get_singleton()->update_keying();
311 	_animation_key_editor_seek(timeline_position, false);
312 }
313 
_animation_new()314 void AnimationPlayerEditor::_animation_new() {
315 
316 	renaming = false;
317 	name_title->set_text(TTR("New Animation Name:"));
318 
319 	int count = 1;
320 	String base = TTR("New Anim");
321 	while (true) {
322 		String attempt = base;
323 		if (count > 1)
324 			attempt += " (" + itos(count) + ")";
325 		if (player->has_animation(attempt)) {
326 			count++;
327 			continue;
328 		}
329 		base = attempt;
330 		break;
331 	}
332 
333 	name->set_text(base);
334 	name_dialog->popup_centered(Size2(300, 90));
335 	name->select_all();
336 	name->grab_focus();
337 }
_animation_rename()338 void AnimationPlayerEditor::_animation_rename() {
339 
340 	if (animation->get_item_count() == 0)
341 		return;
342 	int selected = animation->get_selected();
343 	String selected_name = animation->get_item_text(selected);
344 
345 	name_title->set_text(TTR("Change Animation Name:"));
346 	name->set_text(selected_name);
347 	renaming = true;
348 	name_dialog->popup_centered(Size2(300, 90));
349 	name->select_all();
350 	name->grab_focus();
351 }
_animation_load()352 void AnimationPlayerEditor::_animation_load() {
353 	ERR_FAIL_COND(!player);
354 	file->set_mode(EditorFileDialog::MODE_OPEN_FILE);
355 	file->clear_filters();
356 	List<String> extensions;
357 
358 	ResourceLoader::get_recognized_extensions_for_type("Animation", &extensions);
359 	for (List<String>::Element *E = extensions.front(); E; E = E->next()) {
360 
361 		file->add_filter("*." + E->get() + " ; " + E->get().to_upper());
362 	}
363 
364 	file->popup_centered_ratio();
365 	current_option = RESOURCE_LOAD;
366 }
367 
_animation_save_in_path(const Ref<Resource> & p_resource,const String & p_path)368 void AnimationPlayerEditor::_animation_save_in_path(const Ref<Resource> &p_resource, const String &p_path) {
369 
370 	int flg = 0;
371 	if (EditorSettings::get_singleton()->get("filesystem/on_save/compress_binary_resources"))
372 		flg |= ResourceSaver::FLAG_COMPRESS;
373 
374 	String path = ProjectSettings::get_singleton()->localize_path(p_path);
375 	Error err = ResourceSaver::save(path, p_resource, flg | ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS);
376 
377 	if (err != OK) {
378 		accept->set_text(TTR("Error saving resource!"));
379 		accept->popup_centered_minsize();
380 		return;
381 	}
382 
383 	((Resource *)p_resource.ptr())->set_path(path);
384 	editor->emit_signal("resource_saved", p_resource);
385 }
386 
_animation_save(const Ref<Resource> & p_resource)387 void AnimationPlayerEditor::_animation_save(const Ref<Resource> &p_resource) {
388 
389 	if (p_resource->get_path().is_resource_file()) {
390 		_animation_save_in_path(p_resource, p_resource->get_path());
391 	} else {
392 		_animation_save_as(p_resource);
393 	}
394 }
395 
_animation_save_as(const Ref<Resource> & p_resource)396 void AnimationPlayerEditor::_animation_save_as(const Ref<Resource> &p_resource) {
397 
398 	file->set_mode(EditorFileDialog::MODE_SAVE_FILE);
399 
400 	List<String> extensions;
401 	ResourceSaver::get_recognized_extensions(p_resource, &extensions);
402 	file->clear_filters();
403 	for (int i = 0; i < extensions.size(); i++) {
404 
405 		file->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper());
406 	}
407 
408 	String path;
409 	//file->set_current_path(current_path);
410 	if (p_resource->get_path() != "") {
411 		path = p_resource->get_path();
412 		if (extensions.size()) {
413 			if (extensions.find(p_resource->get_path().get_extension().to_lower()) == NULL) {
414 				path = p_resource->get_path().get_base_dir() + p_resource->get_name() + "." + extensions.front()->get();
415 			}
416 		}
417 	} else {
418 		if (extensions.size()) {
419 			if (p_resource->get_name() != "") {
420 				path = p_resource->get_name() + "." + extensions.front()->get().to_lower();
421 			} else {
422 				path = "new_" + p_resource->get_class().to_lower() + "." + extensions.front()->get().to_lower();
423 			}
424 		}
425 	}
426 	file->set_current_path(path);
427 	file->popup_centered_ratio();
428 	file->set_title(TTR("Save Resource As..."));
429 	current_option = RESOURCE_SAVE;
430 }
431 
_animation_remove()432 void AnimationPlayerEditor::_animation_remove() {
433 
434 	if (animation->get_item_count() == 0)
435 		return;
436 
437 	delete_dialog->set_text(TTR("Delete Animation?"));
438 	delete_dialog->popup_centered_minsize();
439 }
440 
_animation_remove_confirmed()441 void AnimationPlayerEditor::_animation_remove_confirmed() {
442 
443 	String current = animation->get_item_text(animation->get_selected());
444 	Ref<Animation> anim = player->get_animation(current);
445 
446 	undo_redo->create_action(TTR("Remove Animation"));
447 	if (player->get_autoplay() == current) {
448 		undo_redo->add_do_method(player, "set_autoplay", "");
449 		undo_redo->add_undo_method(player, "set_autoplay", current);
450 		// Avoid having the autoplay icon linger around if there is only one animation in the player.
451 		undo_redo->add_do_method(this, "_animation_player_changed", player);
452 	}
453 	undo_redo->add_do_method(player, "remove_animation", current);
454 	undo_redo->add_undo_method(player, "add_animation", current, anim);
455 	undo_redo->add_do_method(this, "_animation_player_changed", player);
456 	undo_redo->add_undo_method(this, "_animation_player_changed", player);
457 	if (animation->get_item_count() == 1) {
458 		undo_redo->add_do_method(this, "_stop_onion_skinning");
459 		undo_redo->add_undo_method(this, "_start_onion_skinning");
460 	}
461 	undo_redo->commit_action();
462 }
463 
_select_anim_by_name(const String & p_anim)464 void AnimationPlayerEditor::_select_anim_by_name(const String &p_anim) {
465 
466 	int idx = -1;
467 	for (int i = 0; i < animation->get_item_count(); i++) {
468 
469 		if (animation->get_item_text(i) == p_anim) {
470 
471 			idx = i;
472 			break;
473 		}
474 	}
475 
476 	ERR_FAIL_COND(idx == -1);
477 
478 	animation->select(idx);
479 
480 	_animation_selected(idx);
481 }
482 
_get_editor_step() const483 double AnimationPlayerEditor::_get_editor_step() const {
484 
485 	// Returns the effective snapping value depending on snapping modifiers, or 0 if snapping is disabled.
486 	if (track_editor->is_snap_enabled()) {
487 		const String current = player->get_assigned_animation();
488 		const Ref<Animation> anim = player->get_animation(current);
489 		ERR_FAIL_COND_V(!anim.is_valid(), 0.0);
490 
491 		// Use more precise snapping when holding Shift
492 		return Input::get_singleton()->is_key_pressed(KEY_SHIFT) ? anim->get_step() * 0.25 : anim->get_step();
493 	}
494 
495 	return 0.0;
496 }
497 
_animation_name_edited()498 void AnimationPlayerEditor::_animation_name_edited() {
499 
500 	player->stop();
501 
502 	String new_name = name->get_text();
503 	if (new_name == "" || new_name.find(":") != -1 || new_name.find("/") != -1) {
504 		error_dialog->set_text(TTR("Invalid animation name!"));
505 		error_dialog->popup_centered_minsize();
506 		return;
507 	}
508 
509 	if (renaming && animation->get_item_count() > 0 && animation->get_item_text(animation->get_selected()) == new_name) {
510 		name_dialog->hide();
511 		return;
512 	}
513 
514 	if (player->has_animation(new_name)) {
515 		error_dialog->set_text(TTR("Animation name already exists!"));
516 		error_dialog->popup_centered_minsize();
517 		return;
518 	}
519 
520 	if (renaming) {
521 		String current = animation->get_item_text(animation->get_selected());
522 		Ref<Animation> anim = player->get_animation(current);
523 
524 		undo_redo->create_action(TTR("Rename Animation"));
525 		undo_redo->add_do_method(player, "rename_animation", current, new_name);
526 		undo_redo->add_do_method(anim.ptr(), "set_name", new_name);
527 		undo_redo->add_undo_method(player, "rename_animation", new_name, current);
528 		undo_redo->add_undo_method(anim.ptr(), "set_name", current);
529 		undo_redo->add_do_method(this, "_animation_player_changed", player);
530 		undo_redo->add_undo_method(this, "_animation_player_changed", player);
531 		undo_redo->commit_action();
532 
533 		_select_anim_by_name(new_name);
534 
535 	} else {
536 
537 		Ref<Animation> new_anim = Ref<Animation>(memnew(Animation));
538 		new_anim->set_name(new_name);
539 
540 		undo_redo->create_action(TTR("Add Animation"));
541 		undo_redo->add_do_method(player, "add_animation", new_name, new_anim);
542 		undo_redo->add_undo_method(player, "remove_animation", new_name);
543 		undo_redo->add_do_method(this, "_animation_player_changed", player);
544 		undo_redo->add_undo_method(this, "_animation_player_changed", player);
545 		if (animation->get_item_count() == 0) {
546 			undo_redo->add_do_method(this, "_start_onion_skinning");
547 			undo_redo->add_undo_method(this, "_stop_onion_skinning");
548 		}
549 		undo_redo->commit_action();
550 
551 		_select_anim_by_name(new_name);
552 	}
553 
554 	name_dialog->hide();
555 }
556 
_blend_editor_next_changed(const int p_idx)557 void AnimationPlayerEditor::_blend_editor_next_changed(const int p_idx) {
558 
559 	if (animation->get_item_count() == 0)
560 		return;
561 
562 	String current = animation->get_item_text(animation->get_selected());
563 
564 	undo_redo->create_action(TTR("Blend Next Changed"));
565 	undo_redo->add_do_method(player, "animation_set_next", current, blend_editor.next->get_item_text(p_idx));
566 	undo_redo->add_undo_method(player, "animation_set_next", current, player->animation_get_next(current));
567 	undo_redo->add_do_method(this, "_animation_player_changed", player);
568 	undo_redo->add_undo_method(this, "_animation_player_changed", player);
569 	undo_redo->commit_action();
570 }
571 
_animation_blend()572 void AnimationPlayerEditor::_animation_blend() {
573 
574 	if (updating_blends)
575 		return;
576 
577 	blend_editor.tree->clear();
578 
579 	if (animation->get_item_count() == 0)
580 		return;
581 
582 	String current = animation->get_item_text(animation->get_selected());
583 
584 	blend_editor.dialog->popup_centered(Size2(400, 400) * EDSCALE);
585 
586 	blend_editor.tree->set_hide_root(true);
587 	blend_editor.tree->set_column_min_width(0, 10);
588 	blend_editor.tree->set_column_min_width(1, 3);
589 
590 	List<StringName> anims;
591 	player->get_animation_list(&anims);
592 	TreeItem *root = blend_editor.tree->create_item();
593 	updating_blends = true;
594 
595 	int i = 0;
596 	bool anim_found = false;
597 	blend_editor.next->clear();
598 	blend_editor.next->add_item("", i);
599 
600 	for (List<StringName>::Element *E = anims.front(); E; E = E->next()) {
601 
602 		String to = E->get();
603 		TreeItem *blend = blend_editor.tree->create_item(root);
604 		blend->set_editable(0, false);
605 		blend->set_editable(1, true);
606 		blend->set_text(0, to);
607 		blend->set_cell_mode(1, TreeItem::CELL_MODE_RANGE);
608 		blend->set_range_config(1, 0, 3600, 0.001);
609 		blend->set_range(1, player->get_blend_time(current, to));
610 
611 		i++;
612 		blend_editor.next->add_item(to, i);
613 		if (to == player->animation_get_next(current)) {
614 			blend_editor.next->select(i);
615 			anim_found = true;
616 		}
617 	}
618 
619 	// make sure we reset it else it becomes out of sync and could contain a deleted animation
620 	if (!anim_found) {
621 		blend_editor.next->select(0);
622 		player->animation_set_next(current, blend_editor.next->get_item_text(0));
623 	}
624 
625 	updating_blends = false;
626 }
627 
_blend_edited()628 void AnimationPlayerEditor::_blend_edited() {
629 
630 	if (updating_blends)
631 		return;
632 
633 	if (animation->get_item_count() == 0)
634 		return;
635 
636 	String current = animation->get_item_text(animation->get_selected());
637 
638 	TreeItem *selected = blend_editor.tree->get_edited();
639 	if (!selected)
640 		return;
641 
642 	updating_blends = true;
643 	String to = selected->get_text(0);
644 	float blend_time = selected->get_range(1);
645 	float prev_blend_time = player->get_blend_time(current, to);
646 
647 	undo_redo->create_action(TTR("Change Blend Time"));
648 	undo_redo->add_do_method(player, "set_blend_time", current, to, blend_time);
649 	undo_redo->add_undo_method(player, "set_blend_time", current, to, prev_blend_time);
650 	undo_redo->add_do_method(this, "_animation_player_changed", player);
651 	undo_redo->add_undo_method(this, "_animation_player_changed", player);
652 	undo_redo->commit_action();
653 	updating_blends = false;
654 }
655 
ensure_visibility()656 void AnimationPlayerEditor::ensure_visibility() {
657 
658 	if (player && pin->is_pressed())
659 		return; // another player is pinned, don't reset
660 
661 	_animation_edit();
662 }
663 
get_state() const664 Dictionary AnimationPlayerEditor::get_state() const {
665 
666 	Dictionary d;
667 
668 	d["visible"] = is_visible_in_tree();
669 	if (EditorNode::get_singleton()->get_edited_scene() && is_visible_in_tree() && player) {
670 		d["player"] = EditorNode::get_singleton()->get_edited_scene()->get_path_to(player);
671 		d["animation"] = player->get_assigned_animation();
672 		d["track_editor_state"] = track_editor->get_state();
673 	}
674 
675 	return d;
676 }
set_state(const Dictionary & p_state)677 void AnimationPlayerEditor::set_state(const Dictionary &p_state) {
678 
679 	if (!p_state.has("visible") || !p_state["visible"]) {
680 		return;
681 	}
682 	if (!EditorNode::get_singleton()->get_edited_scene()) {
683 		return;
684 	}
685 
686 	if (p_state.has("player")) {
687 
688 		Node *n = EditorNode::get_singleton()->get_edited_scene()->get_node(p_state["player"]);
689 		if (Object::cast_to<AnimationPlayer>(n) && EditorNode::get_singleton()->get_editor_selection()->is_selected(n)) {
690 			player = Object::cast_to<AnimationPlayer>(n);
691 			_update_player();
692 			editor->make_bottom_panel_item_visible(this);
693 			set_process(true);
694 			ensure_visibility();
695 
696 			if (p_state.has("animation")) {
697 				String anim = p_state["animation"];
698 				if (!anim.empty() && player->has_animation(anim)) {
699 					_select_anim_by_name(anim);
700 					_animation_edit();
701 				}
702 			}
703 		}
704 	}
705 
706 	if (p_state.has("track_editor_state")) {
707 		track_editor->set_state(p_state["track_editor_state"]);
708 	}
709 }
710 
_animation_resource_edit()711 void AnimationPlayerEditor::_animation_resource_edit() {
712 
713 	if (animation->get_item_count()) {
714 		String current = animation->get_item_text(animation->get_selected());
715 		Ref<Animation> anim = player->get_animation(current);
716 		editor->edit_resource(anim);
717 	}
718 }
719 
_animation_edit()720 void AnimationPlayerEditor::_animation_edit() {
721 
722 	if (animation->get_item_count()) {
723 		String current = animation->get_item_text(animation->get_selected());
724 		Ref<Animation> anim = player->get_animation(current);
725 		track_editor->set_animation(anim);
726 
727 		Node *root = player->get_node(player->get_root());
728 		if (root) {
729 			track_editor->set_root(root);
730 		}
731 	} else {
732 		track_editor->set_animation(Ref<Animation>());
733 		track_editor->set_root(NULL);
734 	}
735 }
736 
_dialog_action(String p_file)737 void AnimationPlayerEditor::_dialog_action(String p_file) {
738 
739 	switch (current_option) {
740 		case RESOURCE_LOAD: {
741 			ERR_FAIL_COND(!player);
742 
743 			Ref<Resource> res = ResourceLoader::load(p_file, "Animation");
744 			ERR_FAIL_COND_MSG(res.is_null(), "Cannot load Animation from file '" + p_file + "'.");
745 			ERR_FAIL_COND_MSG(!res->is_class("Animation"), "Loaded resource from file '" + p_file + "' is not Animation.");
746 			if (p_file.find_last("/") != -1) {
747 
748 				p_file = p_file.substr(p_file.find_last("/") + 1, p_file.length());
749 			}
750 			if (p_file.find_last("\\") != -1) {
751 
752 				p_file = p_file.substr(p_file.find_last("\\") + 1, p_file.length());
753 			}
754 
755 			if (p_file.find(".") != -1)
756 				p_file = p_file.substr(0, p_file.find("."));
757 
758 			undo_redo->create_action(TTR("Load Animation"));
759 			undo_redo->add_do_method(player, "add_animation", p_file, res);
760 			undo_redo->add_undo_method(player, "remove_animation", p_file);
761 			if (player->has_animation(p_file)) {
762 				undo_redo->add_undo_method(player, "add_animation", p_file, player->get_animation(p_file));
763 			}
764 			undo_redo->add_do_method(this, "_animation_player_changed", player);
765 			undo_redo->add_undo_method(this, "_animation_player_changed", player);
766 			undo_redo->commit_action();
767 			break;
768 		}
769 		case RESOURCE_SAVE: {
770 
771 			String current = animation->get_item_text(animation->get_selected());
772 			if (current != "") {
773 				Ref<Animation> anim = player->get_animation(current);
774 
775 				ERR_FAIL_COND(!Object::cast_to<Resource>(*anim));
776 
777 				RES current_res = RES(Object::cast_to<Resource>(*anim));
778 
779 				_animation_save_in_path(current_res, p_file);
780 			}
781 		}
782 	}
783 }
784 
_scale_changed(const String & p_scale)785 void AnimationPlayerEditor::_scale_changed(const String &p_scale) {
786 
787 	player->set_speed_scale(p_scale.to_double());
788 }
789 
_update_animation()790 void AnimationPlayerEditor::_update_animation() {
791 
792 	// the purpose of _update_animation is to reflect the current state
793 	// of the animation player in the current editor..
794 
795 	updating = true;
796 
797 	if (player->is_playing()) {
798 
799 		play->set_pressed(true);
800 		stop->set_pressed(false);
801 
802 	} else {
803 
804 		play->set_pressed(false);
805 		stop->set_pressed(true);
806 	}
807 
808 	scale->set_text(String::num(player->get_speed_scale(), 2));
809 	String current = player->get_assigned_animation();
810 
811 	for (int i = 0; i < animation->get_item_count(); i++) {
812 
813 		if (animation->get_item_text(i) == current) {
814 			animation->select(i);
815 			break;
816 		}
817 	}
818 
819 	updating = false;
820 }
821 
_update_player()822 void AnimationPlayerEditor::_update_player() {
823 
824 	updating = true;
825 	List<StringName> animlist;
826 	if (player)
827 		player->get_animation_list(&animlist);
828 
829 	animation->clear();
830 
831 #define ITEM_DISABLED(m_item, m_disabled) tool_anim->get_popup()->set_item_disabled(tool_anim->get_popup()->get_item_index(m_item), m_disabled)
832 
833 	ITEM_DISABLED(TOOL_SAVE_ANIM, animlist.size() == 0);
834 	ITEM_DISABLED(TOOL_SAVE_AS_ANIM, animlist.size() == 0);
835 	ITEM_DISABLED(TOOL_DUPLICATE_ANIM, animlist.size() == 0);
836 	ITEM_DISABLED(TOOL_RENAME_ANIM, animlist.size() == 0);
837 	ITEM_DISABLED(TOOL_EDIT_TRANSITIONS, animlist.size() == 0);
838 	ITEM_DISABLED(TOOL_COPY_ANIM, animlist.size() == 0);
839 	ITEM_DISABLED(TOOL_REMOVE_ANIM, animlist.size() == 0);
840 
841 	stop->set_disabled(animlist.size() == 0);
842 	play->set_disabled(animlist.size() == 0);
843 	play_bw->set_disabled(animlist.size() == 0);
844 	play_bw_from->set_disabled(animlist.size() == 0);
845 	play_from->set_disabled(animlist.size() == 0);
846 	frame->set_editable(animlist.size() != 0);
847 	animation->set_disabled(animlist.size() == 0);
848 	autoplay->set_disabled(animlist.size() == 0);
849 	tool_anim->set_disabled(player == NULL);
850 	onion_toggle->set_disabled(animlist.size() == 0);
851 	onion_skinning->set_disabled(animlist.size() == 0);
852 	pin->set_disabled(player == NULL);
853 
854 	if (!player) {
855 		AnimationPlayerEditor::singleton->get_track_editor()->update_keying();
856 		EditorNode::get_singleton()->update_keying();
857 		return;
858 	}
859 
860 	int active_idx = -1;
861 	for (List<StringName>::Element *E = animlist.front(); E; E = E->next()) {
862 
863 		if (player->get_autoplay() == E->get())
864 			animation->add_icon_item(autoplay_icon, E->get());
865 		else
866 			animation->add_item(E->get());
867 
868 		if (player->get_assigned_animation() == E->get())
869 			active_idx = animation->get_item_count() - 1;
870 	}
871 
872 	updating = false;
873 	if (active_idx != -1) {
874 		animation->select(active_idx);
875 		autoplay->set_pressed(animation->get_item_text(active_idx) == player->get_autoplay());
876 		_animation_selected(active_idx);
877 
878 	} else if (animation->get_item_count() > 0) {
879 
880 		animation->select(0);
881 		autoplay->set_pressed(animation->get_item_text(0) == player->get_autoplay());
882 		_animation_selected(0);
883 	} else {
884 		_animation_selected(0);
885 	}
886 
887 	if (animation->get_item_count()) {
888 		String current = animation->get_item_text(animation->get_selected());
889 		Ref<Animation> anim = player->get_animation(current);
890 		track_editor->set_animation(anim);
891 		Node *root = player->get_node(player->get_root());
892 		if (root) {
893 			track_editor->set_root(root);
894 		}
895 	}
896 
897 	_update_animation();
898 }
899 
edit(AnimationPlayer * p_player)900 void AnimationPlayerEditor::edit(AnimationPlayer *p_player) {
901 
902 	if (player && pin->is_pressed())
903 		return; // Ignore, pinned.
904 	player = p_player;
905 
906 	if (player) {
907 		_update_player();
908 
909 		if (onion.enabled) {
910 			if (animation->get_item_count() > 0)
911 				_start_onion_skinning();
912 			else
913 				_stop_onion_skinning();
914 		}
915 
916 		track_editor->show_select_node_warning(false);
917 	} else {
918 		if (onion.enabled)
919 			_stop_onion_skinning();
920 
921 		track_editor->show_select_node_warning(true);
922 	}
923 }
924 
forward_canvas_force_draw_over_viewport(Control * p_overlay)925 void AnimationPlayerEditor::forward_canvas_force_draw_over_viewport(Control *p_overlay) {
926 
927 	if (!onion.can_overlay)
928 		return;
929 
930 	// Can happen on viewport resize, at least.
931 	if (!_are_onion_layers_valid())
932 		return;
933 
934 	RID ci = p_overlay->get_canvas_item();
935 	Rect2 src_rect = p_overlay->get_global_rect();
936 	// Re-flip since captures are already flipped.
937 	src_rect.position.y = onion.capture_size.y - (src_rect.position.y + src_rect.size.y);
938 	src_rect.size.y *= -1;
939 
940 	Rect2 dst_rect = Rect2(Point2(), p_overlay->get_size());
941 
942 	float alpha_step = 1.0 / (onion.steps + 1);
943 
944 	int cidx = 0;
945 	if (onion.past) {
946 		float alpha = 0;
947 		do {
948 			alpha += alpha_step;
949 
950 			if (onion.captures_valid[cidx]) {
951 				VS::get_singleton()->canvas_item_add_texture_rect_region(
952 						ci, dst_rect, VS::get_singleton()->viewport_get_texture(onion.captures[cidx]), src_rect, Color(1, 1, 1, alpha));
953 			}
954 
955 			cidx++;
956 		} while (cidx < onion.steps);
957 	}
958 	if (onion.future) {
959 		float alpha = 1;
960 		int base_cidx = cidx;
961 		do {
962 			alpha -= alpha_step;
963 
964 			if (onion.captures_valid[cidx]) {
965 				VS::get_singleton()->canvas_item_add_texture_rect_region(
966 						ci, dst_rect, VS::get_singleton()->viewport_get_texture(onion.captures[cidx]), src_rect, Color(1, 1, 1, alpha));
967 			}
968 
969 			cidx++;
970 		} while (cidx < base_cidx + onion.steps); // In case there's the present capture at the end, skip it.
971 	}
972 }
973 
_animation_duplicate()974 void AnimationPlayerEditor::_animation_duplicate() {
975 
976 	if (!animation->get_item_count())
977 		return;
978 
979 	String current = animation->get_item_text(animation->get_selected());
980 	Ref<Animation> anim = player->get_animation(current);
981 	if (!anim.is_valid())
982 		return;
983 
984 	Ref<Animation> new_anim = memnew(Animation);
985 	List<PropertyInfo> plist;
986 	anim->get_property_list(&plist);
987 	for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
988 
989 		if (E->get().usage & PROPERTY_USAGE_STORAGE) {
990 
991 			new_anim->set(E->get().name, anim->get(E->get().name));
992 		}
993 	}
994 	new_anim->set_path("");
995 
996 	String new_name = current;
997 	while (player->has_animation(new_name)) {
998 		new_name = new_name + " (copy)";
999 	}
1000 	new_anim->set_name(new_name);
1001 
1002 	undo_redo->create_action(TTR("Duplicate Animation"));
1003 	undo_redo->add_do_method(player, "add_animation", new_name, new_anim);
1004 	undo_redo->add_undo_method(player, "remove_animation", new_name);
1005 	undo_redo->add_do_method(player, "animation_set_next", new_name, player->animation_get_next(current));
1006 	undo_redo->add_do_method(this, "_animation_player_changed", player);
1007 	undo_redo->add_undo_method(this, "_animation_player_changed", player);
1008 	undo_redo->commit_action();
1009 
1010 	for (int i = 0; i < animation->get_item_count(); i++) {
1011 
1012 		if (animation->get_item_text(i) == new_name) {
1013 
1014 			animation->select(i);
1015 			_animation_selected(i);
1016 			return;
1017 		}
1018 	}
1019 }
1020 
_seek_value_changed(float p_value,bool p_set)1021 void AnimationPlayerEditor::_seek_value_changed(float p_value, bool p_set) {
1022 
1023 	if (updating || !player || player->is_playing()) {
1024 		return;
1025 	};
1026 
1027 	updating = true;
1028 	String current = player->get_assigned_animation();
1029 	if (current == "" || !player->has_animation(current)) {
1030 		updating = false;
1031 		current = "";
1032 		return;
1033 	};
1034 
1035 	Ref<Animation> anim;
1036 	anim = player->get_animation(current);
1037 
1038 	float pos = CLAMP(anim->get_length() * (p_value / frame->get_max()), 0, anim->get_length());
1039 	if (track_editor->is_snap_enabled()) {
1040 		pos = Math::stepify(pos, _get_editor_step());
1041 	}
1042 
1043 	if (player->is_valid() && !p_set) {
1044 		float cpos = player->get_current_animation_position();
1045 
1046 		player->seek_delta(pos, pos - cpos);
1047 	} else {
1048 		player->stop(true);
1049 		player->seek(pos, true);
1050 	}
1051 
1052 	track_editor->set_anim_pos(pos);
1053 
1054 	updating = true;
1055 };
1056 
_animation_player_changed(Object * p_pl)1057 void AnimationPlayerEditor::_animation_player_changed(Object *p_pl) {
1058 
1059 	if (player == p_pl && is_visible_in_tree()) {
1060 
1061 		_update_player();
1062 		if (blend_editor.dialog->is_visible_in_tree())
1063 			_animation_blend(); // Update.
1064 	}
1065 }
1066 
_list_changed()1067 void AnimationPlayerEditor::_list_changed() {
1068 
1069 	if (is_visible_in_tree())
1070 		_update_player();
1071 }
1072 
_animation_key_editor_anim_len_changed(float p_len)1073 void AnimationPlayerEditor::_animation_key_editor_anim_len_changed(float p_len) {
1074 
1075 	frame->set_max(p_len);
1076 }
1077 
_animation_key_editor_seek(float p_pos,bool p_drag)1078 void AnimationPlayerEditor::_animation_key_editor_seek(float p_pos, bool p_drag) {
1079 
1080 	timeline_position = p_pos;
1081 
1082 	if (!is_visible_in_tree())
1083 		return;
1084 
1085 	if (!player)
1086 		return;
1087 
1088 	if (player->is_playing())
1089 		return;
1090 
1091 	if (!player->has_animation(player->get_assigned_animation()))
1092 		return;
1093 
1094 	updating = true;
1095 	frame->set_value(Math::stepify(p_pos, _get_editor_step()));
1096 	updating = false;
1097 	_seek_value_changed(p_pos, !p_drag);
1098 
1099 	EditorNode::get_singleton()->get_inspector()->refresh();
1100 }
1101 
_animation_tool_menu(int p_option)1102 void AnimationPlayerEditor::_animation_tool_menu(int p_option) {
1103 
1104 	String current;
1105 	if (animation->get_selected() >= 0 && animation->get_selected() < animation->get_item_count()) {
1106 		current = animation->get_item_text(animation->get_selected());
1107 	}
1108 
1109 	Ref<Animation> anim;
1110 	if (current != String()) {
1111 		anim = player->get_animation(current);
1112 	}
1113 
1114 	switch (p_option) {
1115 
1116 		case TOOL_NEW_ANIM: {
1117 
1118 			_animation_new();
1119 		} break;
1120 		case TOOL_LOAD_ANIM: {
1121 
1122 			_animation_load();
1123 		} break;
1124 		case TOOL_SAVE_ANIM: {
1125 
1126 			if (anim.is_valid()) {
1127 				_animation_save(anim);
1128 			}
1129 		} break;
1130 		case TOOL_SAVE_AS_ANIM: {
1131 
1132 			if (anim.is_valid()) {
1133 				_animation_save_as(anim);
1134 			}
1135 		} break;
1136 		case TOOL_DUPLICATE_ANIM: {
1137 
1138 			_animation_duplicate();
1139 		} break;
1140 		case TOOL_RENAME_ANIM: {
1141 
1142 			_animation_rename();
1143 		} break;
1144 		case TOOL_EDIT_TRANSITIONS: {
1145 
1146 			_animation_blend();
1147 		} break;
1148 		case TOOL_REMOVE_ANIM: {
1149 
1150 			_animation_remove();
1151 		} break;
1152 		case TOOL_COPY_ANIM: {
1153 
1154 			if (!animation->get_item_count()) {
1155 				error_dialog->set_text(TTR("No animation to copy!"));
1156 				error_dialog->popup_centered_minsize();
1157 				return;
1158 			}
1159 
1160 			String current2 = animation->get_item_text(animation->get_selected());
1161 			Ref<Animation> anim2 = player->get_animation(current2);
1162 			EditorSettings::get_singleton()->set_resource_clipboard(anim2);
1163 		} break;
1164 		case TOOL_PASTE_ANIM: {
1165 
1166 			Ref<Animation> anim2 = EditorSettings::get_singleton()->get_resource_clipboard();
1167 			if (!anim2.is_valid()) {
1168 				error_dialog->set_text(TTR("No animation resource on clipboard!"));
1169 				error_dialog->popup_centered_minsize();
1170 				return;
1171 			}
1172 
1173 			String name = anim2->get_name();
1174 			if (name == "") {
1175 				name = TTR("Pasted Animation");
1176 			}
1177 
1178 			int idx = 1;
1179 			String base = name;
1180 			while (player->has_animation(name)) {
1181 
1182 				idx++;
1183 				name = base + " " + itos(idx);
1184 			}
1185 
1186 			undo_redo->create_action(TTR("Paste Animation"));
1187 			undo_redo->add_do_method(player, "add_animation", name, anim2);
1188 			undo_redo->add_undo_method(player, "remove_animation", name);
1189 			undo_redo->add_do_method(this, "_animation_player_changed", player);
1190 			undo_redo->add_undo_method(this, "_animation_player_changed", player);
1191 			undo_redo->commit_action();
1192 
1193 			_select_anim_by_name(name);
1194 		} break;
1195 		case TOOL_EDIT_RESOURCE: {
1196 
1197 			if (!animation->get_item_count()) {
1198 				error_dialog->set_text(TTR("No animation to edit!"));
1199 				error_dialog->popup_centered_minsize();
1200 				return;
1201 			}
1202 
1203 			String current2 = animation->get_item_text(animation->get_selected());
1204 			Ref<Animation> anim2 = player->get_animation(current2);
1205 			editor->edit_resource(anim2);
1206 		} break;
1207 	}
1208 }
1209 
_onion_skinning_menu(int p_option)1210 void AnimationPlayerEditor::_onion_skinning_menu(int p_option) {
1211 
1212 	PopupMenu *menu = onion_skinning->get_popup();
1213 	int idx = menu->get_item_index(p_option);
1214 
1215 	switch (p_option) {
1216 
1217 		case ONION_SKINNING_ENABLE: {
1218 
1219 			onion.enabled = !onion.enabled;
1220 
1221 			if (onion.enabled)
1222 				_start_onion_skinning();
1223 			else
1224 				_stop_onion_skinning();
1225 
1226 		} break;
1227 		case ONION_SKINNING_PAST: {
1228 
1229 			// Ensure at least one of past/future is checked.
1230 			onion.past = onion.future ? !onion.past : true;
1231 			menu->set_item_checked(idx, onion.past);
1232 		} break;
1233 		case ONION_SKINNING_FUTURE: {
1234 
1235 			// Ensure at least one of past/future is checked.
1236 			onion.future = onion.past ? !onion.future : true;
1237 			menu->set_item_checked(idx, onion.future);
1238 		} break;
1239 		case ONION_SKINNING_1_STEP: // Fall-through.
1240 		case ONION_SKINNING_2_STEPS:
1241 		case ONION_SKINNING_3_STEPS: {
1242 
1243 			onion.steps = (p_option - ONION_SKINNING_1_STEP) + 1;
1244 			int one_frame_idx = menu->get_item_index(ONION_SKINNING_1_STEP);
1245 			for (int i = 0; i <= ONION_SKINNING_LAST_STEPS_OPTION - ONION_SKINNING_1_STEP; i++) {
1246 				menu->set_item_checked(one_frame_idx + i, onion.steps == i + 1);
1247 			}
1248 		} break;
1249 		case ONION_SKINNING_DIFFERENCES_ONLY: {
1250 
1251 			onion.differences_only = !onion.differences_only;
1252 			menu->set_item_checked(idx, onion.differences_only);
1253 		} break;
1254 		case ONION_SKINNING_FORCE_WHITE_MODULATE: {
1255 
1256 			onion.force_white_modulate = !onion.force_white_modulate;
1257 			menu->set_item_checked(idx, onion.force_white_modulate);
1258 		} break;
1259 		case ONION_SKINNING_INCLUDE_GIZMOS: {
1260 
1261 			onion.include_gizmos = !onion.include_gizmos;
1262 			menu->set_item_checked(idx, onion.include_gizmos);
1263 		} break;
1264 	}
1265 }
1266 
_unhandled_key_input(const Ref<InputEvent> & p_ev)1267 void AnimationPlayerEditor::_unhandled_key_input(const Ref<InputEvent> &p_ev) {
1268 
1269 	Ref<InputEventKey> k = p_ev;
1270 	if (is_visible_in_tree() && k.is_valid() && k->is_pressed() && !k->is_echo() && !k->get_alt() && !k->get_control() && !k->get_metakey()) {
1271 
1272 		switch (k->get_scancode()) {
1273 
1274 			case KEY_A: {
1275 				if (!k->get_shift())
1276 					_play_bw_from_pressed();
1277 				else
1278 					_play_bw_pressed();
1279 			} break;
1280 			case KEY_S: {
1281 				_stop_pressed();
1282 			} break;
1283 			case KEY_D: {
1284 				if (!k->get_shift())
1285 					_play_from_pressed();
1286 				else
1287 					_play_pressed();
1288 			} break;
1289 		}
1290 	}
1291 }
1292 
_editor_visibility_changed()1293 void AnimationPlayerEditor::_editor_visibility_changed() {
1294 
1295 	if (is_visible() && animation->get_item_count() > 0) {
1296 		_start_onion_skinning();
1297 	}
1298 }
1299 
_are_onion_layers_valid()1300 bool AnimationPlayerEditor::_are_onion_layers_valid() {
1301 
1302 	ERR_FAIL_COND_V(!onion.past && !onion.future, false);
1303 
1304 	Point2 capture_size = get_tree()->get_root()->get_size();
1305 	return onion.captures.size() == onion.get_needed_capture_count() && onion.capture_size == capture_size;
1306 }
1307 
_allocate_onion_layers()1308 void AnimationPlayerEditor::_allocate_onion_layers() {
1309 
1310 	_free_onion_layers();
1311 
1312 	int captures = onion.get_needed_capture_count();
1313 	Point2 capture_size = get_tree()->get_root()->get_size();
1314 
1315 	onion.captures.resize(captures);
1316 	onion.captures_valid.resize(captures);
1317 
1318 	for (int i = 0; i < captures; i++) {
1319 		bool is_present = onion.differences_only && i == captures - 1;
1320 
1321 		// Each capture is a viewport with a canvas item attached that renders a full-size rect with the contents of the main viewport.
1322 		onion.captures.write[i] = VS::get_singleton()->viewport_create();
1323 		VS::get_singleton()->viewport_set_usage(onion.captures[i], VS::VIEWPORT_USAGE_2D);
1324 		VS::get_singleton()->viewport_set_size(onion.captures[i], capture_size.width, capture_size.height);
1325 		VS::get_singleton()->viewport_set_update_mode(onion.captures[i], VS::VIEWPORT_UPDATE_ALWAYS);
1326 		VS::get_singleton()->viewport_set_transparent_background(onion.captures[i], !is_present);
1327 		VS::get_singleton()->viewport_set_vflip(onion.captures[i], true);
1328 		VS::get_singleton()->viewport_attach_canvas(onion.captures[i], onion.capture.canvas);
1329 	}
1330 
1331 	// Reset the capture canvas item to the current root viewport texture (defensive).
1332 	VS::get_singleton()->canvas_item_clear(onion.capture.canvas_item);
1333 	VS::get_singleton()->canvas_item_add_texture_rect(onion.capture.canvas_item, Rect2(Point2(), capture_size), get_tree()->get_root()->get_texture()->get_rid());
1334 
1335 	onion.capture_size = capture_size;
1336 }
1337 
_free_onion_layers()1338 void AnimationPlayerEditor::_free_onion_layers() {
1339 
1340 	for (int i = 0; i < onion.captures.size(); i++) {
1341 		if (onion.captures[i].is_valid()) {
1342 			VS::get_singleton()->free(onion.captures[i]);
1343 		}
1344 	}
1345 	onion.captures.clear();
1346 	onion.captures_valid.clear();
1347 }
1348 
_prepare_onion_layers_1()1349 void AnimationPlayerEditor::_prepare_onion_layers_1() {
1350 
1351 	// This would be called per viewport and we want to act once only.
1352 	int64_t frame = get_tree()->get_frame();
1353 	if (frame == onion.last_frame)
1354 		return;
1355 
1356 	if (!onion.enabled || !is_processing() || !is_visible() || !get_player()) {
1357 		_stop_onion_skinning();
1358 		return;
1359 	}
1360 
1361 	onion.last_frame = frame;
1362 
1363 	// Refresh viewports with no onion layers overlaid.
1364 	onion.can_overlay = false;
1365 	plugin->update_overlays();
1366 
1367 	if (player->is_playing())
1368 		return;
1369 
1370 	// And go to next step afterwards.
1371 	call_deferred("_prepare_onion_layers_2");
1372 }
1373 
_prepare_onion_layers_2()1374 void AnimationPlayerEditor::_prepare_onion_layers_2() {
1375 
1376 	Ref<Animation> anim = player->get_animation(player->get_assigned_animation());
1377 	if (!anim.is_valid())
1378 		return;
1379 
1380 	if (!_are_onion_layers_valid())
1381 		_allocate_onion_layers();
1382 
1383 	// Hide superfluous elements that would make the overlay unnecessary cluttered.
1384 	Dictionary canvas_edit_state;
1385 	Dictionary spatial_edit_state;
1386 	if (SpatialEditor::get_singleton()->is_visible()) {
1387 		// 3D
1388 		spatial_edit_state = SpatialEditor::get_singleton()->get_state();
1389 		Dictionary new_state = spatial_edit_state.duplicate();
1390 		new_state["show_grid"] = false;
1391 		new_state["show_origin"] = false;
1392 		Array orig_vp = spatial_edit_state["viewports"];
1393 		Array vp;
1394 		vp.resize(4);
1395 		for (int i = 0; i < vp.size(); i++) {
1396 			Dictionary d = ((Dictionary)orig_vp[i]).duplicate();
1397 			d["use_environment"] = false;
1398 			d["doppler"] = false;
1399 			d["gizmos"] = onion.include_gizmos ? d["gizmos"] : Variant(false);
1400 			d["information"] = false;
1401 			vp[i] = d;
1402 		}
1403 		new_state["viewports"] = vp;
1404 		// TODO: Save/restore only affected entries.
1405 		SpatialEditor::get_singleton()->set_state(new_state);
1406 	} else { // CanvasItemEditor
1407 		// 2D
1408 		canvas_edit_state = CanvasItemEditor::get_singleton()->get_state();
1409 		Dictionary new_state = canvas_edit_state.duplicate();
1410 		new_state["show_grid"] = false;
1411 		new_state["show_rulers"] = false;
1412 		new_state["show_guides"] = false;
1413 		new_state["show_helpers"] = false;
1414 		new_state["show_zoom_control"] = false;
1415 		// TODO: Save/restore only affected entries.
1416 		CanvasItemEditor::get_singleton()->set_state(new_state);
1417 	}
1418 
1419 	// Tweak the root viewport to ensure it's rendered before our target.
1420 	RID root_vp = get_tree()->get_root()->get_viewport_rid();
1421 	Rect2 root_vp_screen_rect = get_tree()->get_root()->get_attach_to_screen_rect();
1422 	VS::get_singleton()->viewport_attach_to_screen(root_vp, Rect2());
1423 	VS::get_singleton()->viewport_set_update_mode(root_vp, VS::VIEWPORT_UPDATE_ALWAYS);
1424 
1425 	RID present_rid;
1426 	if (onion.differences_only) {
1427 		// Capture present scene as it is.
1428 		VS::get_singleton()->canvas_item_set_material(onion.capture.canvas_item, RID());
1429 		present_rid = onion.captures[onion.captures.size() - 1];
1430 		VS::get_singleton()->viewport_set_active(present_rid, true);
1431 		VS::get_singleton()->viewport_set_parent_viewport(root_vp, present_rid);
1432 		VS::get_singleton()->draw(false);
1433 		VS::get_singleton()->viewport_set_active(present_rid, false);
1434 	}
1435 
1436 	// Backup current animation state.
1437 	AnimatedValuesBackup values_backup = player->backup_animated_values();
1438 	float cpos = player->get_current_animation_position();
1439 
1440 	// Render every past/future step with the capture shader.
1441 
1442 	VS::get_singleton()->canvas_item_set_material(onion.capture.canvas_item, onion.capture.material->get_rid());
1443 	onion.capture.material->set_shader_param("bkg_color", GLOBAL_GET("rendering/environment/default_clear_color"));
1444 	onion.capture.material->set_shader_param("differences_only", onion.differences_only);
1445 	onion.capture.material->set_shader_param("present", onion.differences_only ? VS::get_singleton()->viewport_get_texture(present_rid) : RID());
1446 
1447 	int step_off_a = onion.past ? -onion.steps : 0;
1448 	int step_off_b = onion.future ? onion.steps : 0;
1449 	int cidx = 0;
1450 	onion.capture.material->set_shader_param("dir_color", onion.force_white_modulate ? Color(1, 1, 1) : Color(EDITOR_GET("editors/animation/onion_layers_past_color")));
1451 	for (int step_off = step_off_a; step_off <= step_off_b; step_off++) {
1452 
1453 		if (step_off == 0) {
1454 			// Skip present step and switch to the color of future.
1455 			if (!onion.force_white_modulate)
1456 				onion.capture.material->set_shader_param("dir_color", EDITOR_GET("editors/animation/onion_layers_future_color"));
1457 			continue;
1458 		}
1459 
1460 		float pos = cpos + step_off * anim->get_step();
1461 
1462 		bool valid = anim->has_loop() || (pos >= 0 && pos <= anim->get_length());
1463 		onion.captures_valid.write[cidx] = valid;
1464 		if (valid) {
1465 			player->seek(pos, true);
1466 			get_tree()->flush_transform_notifications(); // Needed for transforms of Spatials.
1467 			values_backup.update_skeletons(); // Needed for Skeletons (2D & 3D).
1468 
1469 			VS::get_singleton()->viewport_set_active(onion.captures[cidx], true);
1470 			VS::get_singleton()->viewport_set_parent_viewport(root_vp, onion.captures[cidx]);
1471 			VS::get_singleton()->draw(false);
1472 			VS::get_singleton()->viewport_set_active(onion.captures[cidx], false);
1473 		}
1474 
1475 		cidx++;
1476 	}
1477 
1478 	// Restore root viewport.
1479 	VS::get_singleton()->viewport_set_parent_viewport(root_vp, RID());
1480 	VS::get_singleton()->viewport_attach_to_screen(root_vp, root_vp_screen_rect);
1481 	VS::get_singleton()->viewport_set_update_mode(root_vp, VS::VIEWPORT_UPDATE_WHEN_VISIBLE);
1482 
1483 	// Restore animation state
1484 	// (Seeking with update=true wouldn't do the trick because the current value of the properties
1485 	// may not match their value for the current point in the animation).
1486 	player->seek(cpos, false);
1487 	player->restore_animated_values(values_backup);
1488 
1489 	// Restore state of main editors.
1490 	if (SpatialEditor::get_singleton()->is_visible()) {
1491 		// 3D
1492 		SpatialEditor::get_singleton()->set_state(spatial_edit_state);
1493 	} else { // CanvasItemEditor
1494 		// 2D
1495 		CanvasItemEditor::get_singleton()->set_state(canvas_edit_state);
1496 	}
1497 
1498 	// Update viewports with skin layers overlaid for the actual engine loop render.
1499 	onion.can_overlay = true;
1500 	plugin->update_overlays();
1501 }
1502 
_start_onion_skinning()1503 void AnimationPlayerEditor::_start_onion_skinning() {
1504 
1505 	// FIXME: Using "idle_frame" makes onion layers update one frame behind the current.
1506 	if (!get_tree()->is_connected("idle_frame", this, "call_deferred")) {
1507 		get_tree()->connect("idle_frame", this, "call_deferred", varray("_prepare_onion_layers_1"));
1508 	}
1509 }
1510 
_stop_onion_skinning()1511 void AnimationPlayerEditor::_stop_onion_skinning() {
1512 
1513 	if (get_tree()->is_connected("idle_frame", this, "call_deferred")) {
1514 
1515 		get_tree()->disconnect("idle_frame", this, "call_deferred");
1516 
1517 		_free_onion_layers();
1518 
1519 		// Clean up the overlay.
1520 		onion.can_overlay = false;
1521 		plugin->update_overlays();
1522 	}
1523 }
1524 
_pin_pressed()1525 void AnimationPlayerEditor::_pin_pressed() {
1526 
1527 	EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor()->update_tree();
1528 }
1529 
_bind_methods()1530 void AnimationPlayerEditor::_bind_methods() {
1531 
1532 	ClassDB::bind_method(D_METHOD("_node_removed"), &AnimationPlayerEditor::_node_removed);
1533 	ClassDB::bind_method(D_METHOD("_play_pressed"), &AnimationPlayerEditor::_play_pressed);
1534 	ClassDB::bind_method(D_METHOD("_play_from_pressed"), &AnimationPlayerEditor::_play_from_pressed);
1535 	ClassDB::bind_method(D_METHOD("_play_bw_pressed"), &AnimationPlayerEditor::_play_bw_pressed);
1536 	ClassDB::bind_method(D_METHOD("_play_bw_from_pressed"), &AnimationPlayerEditor::_play_bw_from_pressed);
1537 	ClassDB::bind_method(D_METHOD("_stop_pressed"), &AnimationPlayerEditor::_stop_pressed);
1538 	ClassDB::bind_method(D_METHOD("_autoplay_pressed"), &AnimationPlayerEditor::_autoplay_pressed);
1539 	ClassDB::bind_method(D_METHOD("_animation_selected"), &AnimationPlayerEditor::_animation_selected);
1540 	ClassDB::bind_method(D_METHOD("_animation_name_edited"), &AnimationPlayerEditor::_animation_name_edited);
1541 	ClassDB::bind_method(D_METHOD("_animation_new"), &AnimationPlayerEditor::_animation_new);
1542 	ClassDB::bind_method(D_METHOD("_animation_rename"), &AnimationPlayerEditor::_animation_rename);
1543 	ClassDB::bind_method(D_METHOD("_animation_load"), &AnimationPlayerEditor::_animation_load);
1544 	ClassDB::bind_method(D_METHOD("_animation_remove"), &AnimationPlayerEditor::_animation_remove);
1545 	ClassDB::bind_method(D_METHOD("_animation_remove_confirmed"), &AnimationPlayerEditor::_animation_remove_confirmed);
1546 	ClassDB::bind_method(D_METHOD("_animation_blend"), &AnimationPlayerEditor::_animation_blend);
1547 	ClassDB::bind_method(D_METHOD("_animation_edit"), &AnimationPlayerEditor::_animation_edit);
1548 	ClassDB::bind_method(D_METHOD("_animation_resource_edit"), &AnimationPlayerEditor::_animation_resource_edit);
1549 	ClassDB::bind_method(D_METHOD("_dialog_action"), &AnimationPlayerEditor::_dialog_action);
1550 	ClassDB::bind_method(D_METHOD("_seek_value_changed"), &AnimationPlayerEditor::_seek_value_changed, DEFVAL(true));
1551 	ClassDB::bind_method(D_METHOD("_animation_player_changed"), &AnimationPlayerEditor::_animation_player_changed);
1552 	ClassDB::bind_method(D_METHOD("_blend_edited"), &AnimationPlayerEditor::_blend_edited);
1553 	ClassDB::bind_method(D_METHOD("_scale_changed"), &AnimationPlayerEditor::_scale_changed);
1554 	ClassDB::bind_method(D_METHOD("_list_changed"), &AnimationPlayerEditor::_list_changed);
1555 	ClassDB::bind_method(D_METHOD("_animation_key_editor_seek"), &AnimationPlayerEditor::_animation_key_editor_seek);
1556 	ClassDB::bind_method(D_METHOD("_animation_key_editor_anim_len_changed"), &AnimationPlayerEditor::_animation_key_editor_anim_len_changed);
1557 	ClassDB::bind_method(D_METHOD("_animation_duplicate"), &AnimationPlayerEditor::_animation_duplicate);
1558 	ClassDB::bind_method(D_METHOD("_blend_editor_next_changed"), &AnimationPlayerEditor::_blend_editor_next_changed);
1559 	ClassDB::bind_method(D_METHOD("_unhandled_key_input"), &AnimationPlayerEditor::_unhandled_key_input);
1560 	ClassDB::bind_method(D_METHOD("_animation_tool_menu"), &AnimationPlayerEditor::_animation_tool_menu);
1561 
1562 	ClassDB::bind_method(D_METHOD("_onion_skinning_menu"), &AnimationPlayerEditor::_onion_skinning_menu);
1563 	ClassDB::bind_method(D_METHOD("_editor_visibility_changed"), &AnimationPlayerEditor::_editor_visibility_changed);
1564 	ClassDB::bind_method(D_METHOD("_prepare_onion_layers_1"), &AnimationPlayerEditor::_prepare_onion_layers_1);
1565 	ClassDB::bind_method(D_METHOD("_prepare_onion_layers_2"), &AnimationPlayerEditor::_prepare_onion_layers_2);
1566 	ClassDB::bind_method(D_METHOD("_start_onion_skinning"), &AnimationPlayerEditor::_start_onion_skinning);
1567 	ClassDB::bind_method(D_METHOD("_stop_onion_skinning"), &AnimationPlayerEditor::_stop_onion_skinning);
1568 
1569 	ClassDB::bind_method(D_METHOD("_pin_pressed"), &AnimationPlayerEditor::_pin_pressed);
1570 }
1571 
1572 AnimationPlayerEditor *AnimationPlayerEditor::singleton = NULL;
1573 
get_player() const1574 AnimationPlayer *AnimationPlayerEditor::get_player() const {
1575 
1576 	return player;
1577 }
1578 
AnimationPlayerEditor(EditorNode * p_editor,AnimationPlayerEditorPlugin * p_plugin)1579 AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlayerEditorPlugin *p_plugin) {
1580 	editor = p_editor;
1581 	plugin = p_plugin;
1582 	singleton = this;
1583 
1584 	updating = false;
1585 
1586 	set_focus_mode(FOCUS_ALL);
1587 
1588 	player = NULL;
1589 
1590 	HBoxContainer *hb = memnew(HBoxContainer);
1591 	add_child(hb);
1592 
1593 	play_bw_from = memnew(ToolButton);
1594 	play_bw_from->set_tooltip(TTR("Play selected animation backwards from current pos. (A)"));
1595 	hb->add_child(play_bw_from);
1596 
1597 	play_bw = memnew(ToolButton);
1598 	play_bw->set_tooltip(TTR("Play selected animation backwards from end. (Shift+A)"));
1599 	hb->add_child(play_bw);
1600 
1601 	stop = memnew(ToolButton);
1602 	stop->set_toggle_mode(true);
1603 	hb->add_child(stop);
1604 	stop->set_tooltip(TTR("Stop animation playback. (S)"));
1605 
1606 	play = memnew(ToolButton);
1607 	play->set_tooltip(TTR("Play selected animation from start. (Shift+D)"));
1608 	hb->add_child(play);
1609 
1610 	play_from = memnew(ToolButton);
1611 	play_from->set_tooltip(TTR("Play selected animation from current pos. (D)"));
1612 	hb->add_child(play_from);
1613 
1614 	frame = memnew(SpinBox);
1615 	hb->add_child(frame);
1616 	frame->set_custom_minimum_size(Size2(60, 0));
1617 	frame->set_stretch_ratio(2);
1618 	frame->set_step(0.0001);
1619 	frame->set_tooltip(TTR("Animation position (in seconds)."));
1620 
1621 	hb->add_child(memnew(VSeparator));
1622 
1623 	scale = memnew(LineEdit);
1624 	hb->add_child(scale);
1625 	scale->set_h_size_flags(SIZE_EXPAND_FILL);
1626 	scale->set_stretch_ratio(1);
1627 	scale->set_tooltip(TTR("Scale animation playback globally for the node."));
1628 	scale->hide();
1629 
1630 	accept = memnew(AcceptDialog);
1631 	add_child(accept);
1632 	accept->connect("confirmed", this, "_menu_confirm_current");
1633 
1634 	delete_dialog = memnew(ConfirmationDialog);
1635 	add_child(delete_dialog);
1636 	delete_dialog->connect("confirmed", this, "_animation_remove_confirmed");
1637 
1638 	tool_anim = memnew(MenuButton);
1639 	tool_anim->set_flat(false);
1640 	tool_anim->set_tooltip(TTR("Animation Tools"));
1641 	tool_anim->set_text(TTR("Animation"));
1642 	tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/new_animation", TTR("New")), TOOL_NEW_ANIM);
1643 	tool_anim->get_popup()->add_separator();
1644 	tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/open_animation", TTR("Load")), TOOL_LOAD_ANIM);
1645 	tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/save_animation", TTR("Save")), TOOL_SAVE_ANIM);
1646 	tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/save_as_animation", TTR("Save As...")), TOOL_SAVE_AS_ANIM);
1647 	tool_anim->get_popup()->add_separator();
1648 	tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/copy_animation", TTR("Copy")), TOOL_COPY_ANIM);
1649 	tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/paste_animation", TTR("Paste")), TOOL_PASTE_ANIM);
1650 	tool_anim->get_popup()->add_separator();
1651 	tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/duplicate_animation", TTR("Duplicate")), TOOL_DUPLICATE_ANIM);
1652 	tool_anim->get_popup()->add_separator();
1653 	tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/rename_animation", TTR("Rename...")), TOOL_RENAME_ANIM);
1654 	tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/edit_transitions", TTR("Edit Transitions...")), TOOL_EDIT_TRANSITIONS);
1655 	tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/open_animation_in_inspector", TTR("Open in Inspector")), TOOL_EDIT_RESOURCE);
1656 	tool_anim->get_popup()->add_separator();
1657 	tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/remove_animation", TTR("Remove")), TOOL_REMOVE_ANIM);
1658 	hb->add_child(tool_anim);
1659 
1660 	animation = memnew(OptionButton);
1661 	hb->add_child(animation);
1662 	animation->set_h_size_flags(SIZE_EXPAND_FILL);
1663 	animation->set_tooltip(TTR("Display list of animations in player."));
1664 	animation->set_clip_text(true);
1665 
1666 	autoplay = memnew(ToolButton);
1667 	hb->add_child(autoplay);
1668 	autoplay->set_tooltip(TTR("Autoplay on Load"));
1669 
1670 	hb->add_child(memnew(VSeparator));
1671 
1672 	track_editor = memnew(AnimationTrackEditor);
1673 
1674 	hb->add_child(track_editor->get_edit_menu());
1675 
1676 	hb->add_child(memnew(VSeparator));
1677 
1678 	onion_toggle = memnew(ToolButton);
1679 	onion_toggle->set_toggle_mode(true);
1680 	onion_toggle->set_tooltip(TTR("Enable Onion Skinning"));
1681 	onion_toggle->connect("pressed", this, "_onion_skinning_menu", varray(ONION_SKINNING_ENABLE));
1682 	hb->add_child(onion_toggle);
1683 
1684 	onion_skinning = memnew(MenuButton);
1685 	onion_skinning->set_tooltip(TTR("Onion Skinning Options"));
1686 	onion_skinning->get_popup()->add_separator(TTR("Directions"));
1687 	onion_skinning->get_popup()->add_check_item(TTR("Past"), ONION_SKINNING_PAST);
1688 	onion_skinning->get_popup()->set_item_checked(onion_skinning->get_popup()->get_item_count() - 1, true);
1689 	onion_skinning->get_popup()->add_check_item(TTR("Future"), ONION_SKINNING_FUTURE);
1690 	onion_skinning->get_popup()->add_separator(TTR("Depth"));
1691 	onion_skinning->get_popup()->add_radio_check_item(TTR("1 step"), ONION_SKINNING_1_STEP);
1692 	onion_skinning->get_popup()->set_item_checked(onion_skinning->get_popup()->get_item_count() - 1, true);
1693 	onion_skinning->get_popup()->add_radio_check_item(TTR("2 steps"), ONION_SKINNING_2_STEPS);
1694 	onion_skinning->get_popup()->add_radio_check_item(TTR("3 steps"), ONION_SKINNING_3_STEPS);
1695 	onion_skinning->get_popup()->add_separator();
1696 	onion_skinning->get_popup()->add_check_item(TTR("Differences Only"), ONION_SKINNING_DIFFERENCES_ONLY);
1697 	onion_skinning->get_popup()->add_check_item(TTR("Force White Modulate"), ONION_SKINNING_FORCE_WHITE_MODULATE);
1698 	onion_skinning->get_popup()->add_check_item(TTR("Include Gizmos (3D)"), ONION_SKINNING_INCLUDE_GIZMOS);
1699 	hb->add_child(onion_skinning);
1700 
1701 	hb->add_child(memnew(VSeparator));
1702 
1703 	pin = memnew(ToolButton);
1704 	pin->set_toggle_mode(true);
1705 	pin->set_tooltip(TTR("Pin AnimationPlayer"));
1706 	hb->add_child(pin);
1707 	pin->connect("pressed", this, "_pin_pressed");
1708 
1709 	file = memnew(EditorFileDialog);
1710 	add_child(file);
1711 
1712 	name_dialog = memnew(ConfirmationDialog);
1713 	name_dialog->set_title(TTR("Create New Animation"));
1714 	name_dialog->set_hide_on_ok(false);
1715 	add_child(name_dialog);
1716 	VBoxContainer *vb = memnew(VBoxContainer);
1717 	name_dialog->add_child(vb);
1718 
1719 	name_title = memnew(Label(TTR("Animation Name:")));
1720 	vb->add_child(name_title);
1721 
1722 	name = memnew(LineEdit);
1723 	vb->add_child(name);
1724 	name_dialog->register_text_enter(name);
1725 
1726 	error_dialog = memnew(ConfirmationDialog);
1727 	error_dialog->get_ok()->set_text(TTR("Close"));
1728 	error_dialog->set_title(TTR("Error!"));
1729 	add_child(error_dialog);
1730 
1731 	name_dialog->connect("confirmed", this, "_animation_name_edited");
1732 
1733 	blend_editor.dialog = memnew(AcceptDialog);
1734 	add_child(blend_editor.dialog);
1735 	blend_editor.dialog->get_ok()->set_text(TTR("Close"));
1736 	blend_editor.dialog->set_hide_on_ok(true);
1737 	VBoxContainer *blend_vb = memnew(VBoxContainer);
1738 	blend_editor.dialog->add_child(blend_vb);
1739 	blend_editor.tree = memnew(Tree);
1740 	blend_editor.tree->set_columns(2);
1741 	blend_vb->add_margin_child(TTR("Blend Times:"), blend_editor.tree, true);
1742 	blend_editor.next = memnew(OptionButton);
1743 	blend_vb->add_margin_child(TTR("Next (Auto Queue):"), blend_editor.next);
1744 	blend_editor.dialog->set_title(TTR("Cross-Animation Blend Times"));
1745 	updating_blends = false;
1746 
1747 	blend_editor.tree->connect("item_edited", this, "_blend_edited");
1748 
1749 	autoplay->connect("pressed", this, "_autoplay_pressed");
1750 	autoplay->set_toggle_mode(true);
1751 	play->connect("pressed", this, "_play_pressed");
1752 	play_from->connect("pressed", this, "_play_from_pressed");
1753 	play_bw->connect("pressed", this, "_play_bw_pressed");
1754 	play_bw_from->connect("pressed", this, "_play_bw_from_pressed");
1755 	stop->connect("pressed", this, "_stop_pressed");
1756 
1757 	animation->connect("item_selected", this, "_animation_selected", Vector<Variant>(), true);
1758 
1759 	file->connect("file_selected", this, "_dialog_action");
1760 	frame->connect("value_changed", this, "_seek_value_changed", Vector<Variant>(), true);
1761 	scale->connect("text_entered", this, "_scale_changed", Vector<Variant>(), true);
1762 
1763 	renaming = false;
1764 	last_active = false;
1765 	timeline_position = 0;
1766 
1767 	set_process_unhandled_key_input(true);
1768 
1769 	add_child(track_editor);
1770 	track_editor->set_v_size_flags(SIZE_EXPAND_FILL);
1771 	track_editor->connect("timeline_changed", this, "_animation_key_editor_seek");
1772 	track_editor->connect("animation_len_changed", this, "_animation_key_editor_anim_len_changed");
1773 
1774 	_update_player();
1775 
1776 	// Onion skinning.
1777 
1778 	track_editor->connect("visibility_changed", this, "_editor_visibility_changed");
1779 
1780 	onion.enabled = false;
1781 	onion.past = true;
1782 	onion.future = false;
1783 	onion.steps = 1;
1784 	onion.differences_only = false;
1785 	onion.force_white_modulate = false;
1786 	onion.include_gizmos = false;
1787 
1788 	onion.last_frame = 0;
1789 	onion.can_overlay = false;
1790 	onion.capture_size = Size2();
1791 	onion.capture.canvas = VS::get_singleton()->canvas_create();
1792 	onion.capture.canvas_item = VS::get_singleton()->canvas_item_create();
1793 	VS::get_singleton()->canvas_item_set_parent(onion.capture.canvas_item, onion.capture.canvas);
1794 
1795 	onion.capture.material = Ref<ShaderMaterial>(memnew(ShaderMaterial));
1796 
1797 	onion.capture.shader = Ref<Shader>(memnew(Shader));
1798 	onion.capture.shader->set_code(" \
1799 		shader_type canvas_item; \
1800 		\
1801         uniform vec4 bkg_color; \
1802 		uniform vec4 dir_color; \
1803 		uniform bool differences_only; \
1804 		uniform sampler2D present; \
1805 		\
1806 		float zero_if_equal(vec4 a, vec4 b) { \
1807 			return smoothstep(0.0, 0.005, length(a.rgb - b.rgb) / sqrt(3.0)); \
1808 		} \
1809 		\
1810 		void fragment() { \
1811 			vec4 capture_samp = texture(TEXTURE, UV); \
1812 			vec4 present_samp = texture(present, UV); \
1813 			float bkg_mask = zero_if_equal(capture_samp, bkg_color); \
1814 			float diff_mask = 1.0 - zero_if_equal(present_samp, bkg_color); \
1815 			diff_mask = min(1.0, diff_mask + float(!differences_only)); \
1816 			COLOR = vec4(capture_samp.rgb * dir_color.rgb, bkg_mask * diff_mask); \
1817 		} \
1818 	");
1819 	VS::get_singleton()->material_set_shader(onion.capture.material->get_rid(), onion.capture.shader->get_rid());
1820 }
1821 
~AnimationPlayerEditor()1822 AnimationPlayerEditor::~AnimationPlayerEditor() {
1823 
1824 	_free_onion_layers();
1825 	VS::get_singleton()->free(onion.capture.canvas);
1826 	VS::get_singleton()->free(onion.capture.canvas_item);
1827 }
1828 
_notification(int p_what)1829 void AnimationPlayerEditorPlugin::_notification(int p_what) {
1830 
1831 	switch (p_what) {
1832 		case NOTIFICATION_ENTER_TREE: {
1833 
1834 			set_force_draw_over_forwarding_enabled();
1835 		} break;
1836 	}
1837 }
1838 
edit(Object * p_object)1839 void AnimationPlayerEditorPlugin::edit(Object *p_object) {
1840 
1841 	anim_editor->set_undo_redo(&get_undo_redo());
1842 	if (!p_object)
1843 		return;
1844 	anim_editor->edit(Object::cast_to<AnimationPlayer>(p_object));
1845 }
1846 
handles(Object * p_object) const1847 bool AnimationPlayerEditorPlugin::handles(Object *p_object) const {
1848 
1849 	return p_object->is_class("AnimationPlayer");
1850 }
1851 
make_visible(bool p_visible)1852 void AnimationPlayerEditorPlugin::make_visible(bool p_visible) {
1853 
1854 	if (p_visible) {
1855 
1856 		editor->make_bottom_panel_item_visible(anim_editor);
1857 		anim_editor->set_process(true);
1858 		anim_editor->ensure_visibility();
1859 	}
1860 }
1861 
AnimationPlayerEditorPlugin(EditorNode * p_node)1862 AnimationPlayerEditorPlugin::AnimationPlayerEditorPlugin(EditorNode *p_node) {
1863 
1864 	editor = p_node;
1865 	anim_editor = memnew(AnimationPlayerEditor(editor, this));
1866 	anim_editor->set_undo_redo(EditorNode::get_undo_redo());
1867 	editor->add_bottom_panel_item(TTR("Animation"), anim_editor);
1868 }
1869 
~AnimationPlayerEditorPlugin()1870 AnimationPlayerEditorPlugin::~AnimationPlayerEditorPlugin() {
1871 }
1872