Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/animation/animation_bezier_editor.cpp
9903 views
1
/**************************************************************************/
2
/* animation_bezier_editor.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_bezier_editor.h"
32
33
#include "editor/animation/animation_player_editor_plugin.h"
34
#include "editor/editor_node.h"
35
#include "editor/editor_string_names.h"
36
#include "editor/editor_undo_redo_manager.h"
37
#include "editor/gui/editor_spin_slider.h"
38
#include "editor/settings/editor_settings.h"
39
#include "editor/themes/editor_scale.h"
40
#include "scene/gui/option_button.h"
41
#include "scene/gui/view_panner.h"
42
#include "scene/resources/text_line.h"
43
44
#include <climits>
45
46
float AnimationBezierTrackEdit::_bezier_h_to_pixel(float p_h) {
47
float h = p_h;
48
h = (h - timeline_v_scroll) / timeline_v_zoom;
49
h = (get_size().height / 2.0) - h;
50
return h;
51
}
52
53
void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) {
54
float scale = timeline->get_zoom_scale();
55
56
int limit = timeline->get_name_limit();
57
int right_limit = get_size().width;
58
59
// Selection may have altered the order of keys.
60
RBMap<real_t, int> key_order;
61
62
for (int i = 0; i < animation->track_get_key_count(p_track); i++) {
63
real_t ofs = animation->track_get_key_time(p_track, i);
64
if (selection.has(IntPair(p_track, i))) {
65
if (moving_selection) {
66
ofs += moving_selection_offset.x;
67
} else if (scaling_selection) {
68
ofs += -scaling_selection_offset.x + (ofs - scaling_selection_pivot.x) * (scaling_selection_scale.x - 1);
69
}
70
}
71
72
key_order[ofs] = i;
73
}
74
75
for (RBMap<real_t, int>::Element *E = key_order.front(); E; E = E->next()) {
76
int i = E->get();
77
78
if (!E->next()) {
79
break;
80
}
81
82
int i_n = E->next()->get();
83
84
float offset = animation->track_get_key_time(p_track, i);
85
float height = animation->bezier_track_get_key_value(p_track, i);
86
Vector2 out_handle = animation->bezier_track_get_key_out_handle(p_track, i);
87
if (p_track == moving_handle_track && (moving_handle == -1 || moving_handle == 1) && moving_handle_key == i) {
88
out_handle = moving_handle_right;
89
}
90
91
if (selection.has(IntPair(p_track, i))) {
92
if (moving_selection) {
93
offset += moving_selection_offset.x;
94
height += moving_selection_offset.y;
95
} else if (scaling_selection) {
96
offset += -scaling_selection_offset.x + (offset - scaling_selection_pivot.x) * (scaling_selection_scale.x - 1);
97
height += -scaling_selection_offset.y + (height - scaling_selection_pivot.y) * (scaling_selection_scale.y - 1);
98
}
99
}
100
101
float offset_n = animation->track_get_key_time(p_track, i_n);
102
float height_n = animation->bezier_track_get_key_value(p_track, i_n);
103
Vector2 in_handle = animation->bezier_track_get_key_in_handle(p_track, i_n);
104
if (p_track == moving_handle_track && (moving_handle == -1 || moving_handle == 1) && moving_handle_key == i_n) {
105
in_handle = moving_handle_left;
106
}
107
108
if (selection.has(IntPair(p_track, i_n))) {
109
if (moving_selection) {
110
offset_n += moving_selection_offset.x;
111
height_n += moving_selection_offset.y;
112
} else if (scaling_selection) {
113
offset_n += -scaling_selection_offset.x + (offset_n - scaling_selection_pivot.x) * (scaling_selection_scale.x - 1);
114
height_n += -scaling_selection_offset.y + (height_n - scaling_selection_pivot.y) * (scaling_selection_scale.y - 1);
115
}
116
}
117
118
if (moving_inserted_key && moving_selection_from_track == p_track) {
119
if (moving_selection_from_key == i) {
120
Animation::HandleMode handle_mode = animation->bezier_track_get_key_handle_mode(p_track, i);
121
if (handle_mode != Animation::HANDLE_MODE_FREE) {
122
float offset_p = offset;
123
float height_p = height;
124
if (E->prev()) {
125
int i_p = E->prev()->get();
126
offset_p = animation->track_get_key_time(p_track, i_p);
127
height_p = animation->bezier_track_get_key_value(p_track, i_p);
128
}
129
130
animation->bezier_track_calculate_handles(offset, offset_p, height_p, offset_n, height_n, handle_mode, Animation::HANDLE_SET_MODE_AUTO, nullptr, &out_handle);
131
}
132
} else if (moving_selection_from_key == i_n) {
133
Animation::HandleMode handle_mode = animation->bezier_track_get_key_handle_mode(p_track, i_n);
134
if (handle_mode != Animation::HANDLE_MODE_FREE) {
135
float offset_nn = offset_n;
136
float height_nn = height_n;
137
if (E->next()->next()) {
138
int i_nn = E->next()->next()->get();
139
offset_nn = animation->track_get_key_time(p_track, i_nn);
140
height_nn = animation->bezier_track_get_key_value(p_track, i_nn);
141
}
142
143
animation->bezier_track_calculate_handles(offset_n, offset, height, offset_nn, height_nn, handle_mode, Animation::HANDLE_SET_MODE_AUTO, &in_handle, nullptr);
144
}
145
}
146
}
147
148
out_handle += Vector2(offset, height);
149
in_handle += Vector2(offset_n, height_n);
150
151
Vector2 start(offset, height);
152
Vector2 end(offset_n, height_n);
153
154
int from_x = (offset - timeline->get_value()) * scale + limit;
155
int point_start = from_x;
156
int to_x = (offset_n - timeline->get_value()) * scale + limit;
157
int point_end = to_x;
158
159
if (from_x > right_limit) { // Not visible.
160
continue;
161
}
162
163
if (to_x < limit) { // Not visible.
164
continue;
165
}
166
167
from_x = MAX(from_x, limit);
168
to_x = MIN(to_x, right_limit);
169
170
Vector<Vector2> lines;
171
172
Vector2 prev_pos;
173
174
for (int j = from_x; j <= to_x; j++) {
175
float t = (j - limit) / scale + timeline->get_value();
176
177
float h;
178
179
if (j == point_end) {
180
h = end.y; // Make sure it always connects.
181
} else if (j == point_start) {
182
h = start.y; // Make sure it always connects.
183
} else { // Custom interpolation, used because it needs to show paths affected by moving the selection or handles.
184
int iterations = 10;
185
float low = 0;
186
float high = 1;
187
188
// Narrow high and low as much as possible.
189
for (int k = 0; k < iterations; k++) {
190
float middle = (low + high) / 2.0;
191
192
Vector2 interp = start.bezier_interpolate(out_handle, in_handle, end, middle);
193
194
if (interp.x < t) {
195
low = middle;
196
} else {
197
high = middle;
198
}
199
}
200
201
// Interpolate the result.
202
Vector2 low_pos = start.bezier_interpolate(out_handle, in_handle, end, low);
203
Vector2 high_pos = start.bezier_interpolate(out_handle, in_handle, end, high);
204
205
float c = (t - low_pos.x) / (high_pos.x - low_pos.x);
206
207
h = low_pos.lerp(high_pos, c).y;
208
}
209
210
h = _bezier_h_to_pixel(h);
211
212
Vector2 pos(j, h);
213
214
if (j > from_x) {
215
lines.push_back(prev_pos);
216
lines.push_back(pos);
217
}
218
prev_pos = pos;
219
}
220
221
if (lines.size() >= 2) {
222
draw_multiline(lines, p_color, Math::round(EDSCALE), true);
223
}
224
}
225
}
226
227
void AnimationBezierTrackEdit::_draw_line_clipped(const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, int p_clip_left, int p_clip_right) {
228
Vector2 from = p_from;
229
Vector2 to = p_to;
230
231
if (from.x == to.x && from.y == to.y) {
232
return;
233
}
234
if (to.x < from.x) {
235
SWAP(to, from);
236
}
237
238
if (to.x < p_clip_left) {
239
return;
240
}
241
242
if (from.x > p_clip_right) {
243
return;
244
}
245
246
if (to.x > p_clip_right) {
247
float c = (p_clip_right - from.x) / (to.x - from.x);
248
to = from.lerp(to, c);
249
}
250
251
if (from.x < p_clip_left) {
252
float c = (p_clip_left - from.x) / (to.x - from.x);
253
from = from.lerp(to, c);
254
}
255
256
draw_line(from, to, p_color, Math::round(EDSCALE), true);
257
}
258
259
void AnimationBezierTrackEdit::_notification(int p_what) {
260
switch (p_what) {
261
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
262
if (EditorSettings::get_singleton()->check_changed_settings_in_group("editors/panning")) {
263
panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/animation_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning")));
264
panner->setup_warped_panning(get_viewport(), EDITOR_GET("editors/panning/warped_mouse_panning"));
265
}
266
} break;
267
268
case NOTIFICATION_ENTER_TREE: {
269
panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/animation_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning")));
270
panner->setup_warped_panning(get_viewport(), EDITOR_GET("editors/panning/warped_mouse_panning"));
271
} break;
272
case NOTIFICATION_THEME_CHANGED: {
273
bezier_icon = get_editor_theme_icon(SNAME("KeyBezierPoint"));
274
bezier_handle_icon = get_editor_theme_icon(SNAME("KeyBezierHandle"));
275
selected_icon = get_editor_theme_icon(SNAME("KeyBezierSelected"));
276
} break;
277
278
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
279
RID ae = get_accessibility_element();
280
ERR_FAIL_COND(ae.is_null());
281
282
//TODO
283
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_STATIC_TEXT);
284
DisplayServer::get_singleton()->accessibility_update_set_value(ae, TTR(vformat("The %s is not accessible at this time.", "Animation bezier track editor")));
285
} break;
286
287
case NOTIFICATION_DRAW: {
288
if (animation.is_null()) {
289
return;
290
}
291
292
int limit = timeline->get_name_limit();
293
294
const Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
295
const int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
296
const Color color = get_theme_color(SceneStringName(font_color), SNAME("Label"));
297
298
const Color h_line_color = get_theme_color(SNAME("h_line_color"), SNAME("AnimationBezierTrackEdit"));
299
const Color v_line_color = get_theme_color(SNAME("v_line_color"), SNAME("AnimationBezierTrackEdit"));
300
const Color focus_color = get_theme_color(SNAME("focus_color"), SNAME("AnimationBezierTrackEdit"));
301
const Color track_focus_color = get_theme_color(SNAME("track_focus_color"), SNAME("AnimationBezierTrackEdit"));
302
303
const int h_separation = get_theme_constant(SNAME("h_separation"), SNAME("AnimationBezierTrackEdit"));
304
const int v_separation = get_theme_constant(SNAME("h_separation"), SNAME("AnimationBezierTrackEdit"));
305
306
if (has_focus()) {
307
draw_rect(Rect2(Point2(), get_size()), focus_color, false, Math::round(EDSCALE));
308
}
309
310
draw_line(Point2(limit, 0), Point2(limit, get_size().height), v_line_color, Math::round(EDSCALE));
311
312
int right_limit = get_size().width;
313
314
track_v_scroll_max = v_separation;
315
316
int vofs = v_separation + track_v_scroll;
317
int margin = 0;
318
319
RBMap<int, Color> subtrack_colors;
320
Color selected_track_color;
321
subtracks.clear();
322
subtrack_icons.clear();
323
324
RBMap<String, Vector<int>> track_indices;
325
int track_count = animation->get_track_count();
326
for (int i = 0; i < track_count; ++i) {
327
if (!_is_track_displayed(i)) {
328
continue;
329
}
330
331
String base_path = String(animation->track_get_path(i));
332
int end = base_path.find_char(':');
333
if (end != -1) {
334
base_path = base_path.substr(0, end + 1);
335
}
336
Vector<int> indices = track_indices.has(base_path) ? track_indices[base_path] : Vector<int>();
337
indices.push_back(i);
338
track_indices[base_path] = indices;
339
}
340
341
for (const KeyValue<String, Vector<int>> &E : track_indices) {
342
String base_path = E.key;
343
344
Vector<int> tracks = E.value;
345
346
// Names and icon.
347
{
348
NodePath path = animation->track_get_path(tracks[0]);
349
350
Node *node = nullptr;
351
352
if (root && root->has_node(path)) {
353
node = root->get_node(path);
354
}
355
356
String text;
357
358
if (node) {
359
int ofs = 0;
360
361
Ref<Texture2D> icon = EditorNode::get_singleton()->get_object_icon(node, "Node");
362
363
text = node->get_name();
364
ofs += h_separation;
365
366
TextLine text_buf = TextLine(text, font, font_size);
367
text_buf.set_width(limit - ofs - icon->get_width() - h_separation);
368
369
int h = MAX(text_buf.get_size().y, icon->get_height());
370
371
draw_texture(icon, Point2(ofs, vofs + int(h - icon->get_height()) / 2.0));
372
ofs += icon->get_width() + h_separation;
373
374
margin = icon->get_width();
375
376
Vector2 string_pos = Point2(ofs, vofs);
377
string_pos = string_pos.floor();
378
text_buf.draw(get_canvas_item(), string_pos, color);
379
380
vofs += h + v_separation;
381
track_v_scroll_max += h + v_separation;
382
}
383
}
384
385
const Color dc = get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor));
386
387
Ref<Texture2D> remove = get_editor_theme_icon(SNAME("Remove"));
388
float remove_hpos = limit - h_separation - remove->get_width();
389
390
Ref<Texture2D> lock = get_editor_theme_icon(SNAME("Lock"));
391
Ref<Texture2D> unlock = get_editor_theme_icon(SNAME("Unlock"));
392
float lock_hpos = remove_hpos - h_separation - lock->get_width();
393
394
Ref<Texture2D> visibility_visible = get_editor_theme_icon(SNAME("GuiVisibilityVisible"));
395
Ref<Texture2D> visibility_hidden = get_editor_theme_icon(SNAME("GuiVisibilityHidden"));
396
float visibility_hpos = lock_hpos - h_separation - visibility_visible->get_width();
397
398
Ref<Texture2D> solo = get_editor_theme_icon(SNAME("AudioBusSolo"));
399
float solo_hpos = visibility_hpos - h_separation - solo->get_width();
400
401
float buttons_width = remove->get_width() + lock->get_width() + visibility_visible->get_width() + solo->get_width() + h_separation * 3;
402
403
for (int i = 0; i < tracks.size(); ++i) {
404
// Related track titles.
405
406
int current_track = tracks[i];
407
408
String path = String(animation->track_get_path(current_track));
409
path = path.replace_first(base_path, "");
410
411
Color cc = color;
412
TextLine text_buf = TextLine(path, font, font_size);
413
text_buf.set_width(limit - margin - buttons_width - h_separation * 2);
414
415
Rect2 rect = Rect2(margin, vofs, solo_hpos - h_separation - solo->get_width(), text_buf.get_size().y + v_separation);
416
417
cc.a *= 0.7;
418
float h;
419
if (path.ends_with(":x")) {
420
h = 0;
421
} else if (path.ends_with(":y")) {
422
h = 0.33f;
423
} else if (path.ends_with(":z")) {
424
h = 0.66f;
425
} else {
426
uint32_t hash = path.hash();
427
hash = ((hash >> 16) ^ hash) * 0x45d9f3b;
428
hash = ((hash >> 16) ^ hash) * 0x45d9f3b;
429
hash = (hash >> 16) ^ hash;
430
h = (hash % 65535) / 65536.0;
431
}
432
433
if (current_track != selected_track) {
434
Color track_color;
435
if (locked_tracks.has(current_track)) {
436
track_color.set_hsv(h, 0, 0.4);
437
} else {
438
track_color.set_hsv(h, 0.2, 0.8);
439
}
440
track_color.a = 0.5;
441
draw_rect(Rect2(0, vofs, margin - h_separation, text_buf.get_size().y * 0.8), track_color);
442
subtrack_colors[current_track] = track_color;
443
444
subtracks[current_track] = rect;
445
} else {
446
draw_rect(rect, track_focus_color);
447
if (locked_tracks.has(selected_track)) {
448
selected_track_color.set_hsv(h, 0.0, 0.4);
449
} else {
450
selected_track_color.set_hsv(h, 0.8, 0.8);
451
}
452
}
453
454
Vector2 string_pos = Point2(margin + h_separation, vofs);
455
text_buf.draw(get_canvas_item(), string_pos, cc);
456
457
float icon_start_height = vofs + rect.size.y / 2.0;
458
Rect2 remove_rect = Rect2(remove_hpos, icon_start_height - remove->get_height() / 2.0, remove->get_width(), remove->get_height());
459
if (read_only) {
460
draw_texture(remove, remove_rect.position, dc);
461
} else {
462
draw_texture(remove, remove_rect.position);
463
}
464
465
Rect2 lock_rect = Rect2(lock_hpos, icon_start_height - lock->get_height() / 2.0, lock->get_width(), lock->get_height());
466
if (locked_tracks.has(current_track)) {
467
draw_texture(lock, lock_rect.position);
468
} else {
469
draw_texture(unlock, lock_rect.position);
470
}
471
472
Rect2 visible_rect = Rect2(visibility_hpos, icon_start_height - visibility_visible->get_height() / 2.0, visibility_visible->get_width(), visibility_visible->get_height());
473
if (hidden_tracks.has(current_track)) {
474
draw_texture(visibility_hidden, visible_rect.position);
475
} else {
476
draw_texture(visibility_visible, visible_rect.position);
477
}
478
479
Rect2 solo_rect = Rect2(solo_hpos, icon_start_height - solo->get_height() / 2.0, solo->get_width(), solo->get_height());
480
draw_texture(solo, solo_rect.position);
481
482
RBMap<int, Rect2> track_icons;
483
track_icons[REMOVE_ICON] = remove_rect;
484
track_icons[LOCK_ICON] = lock_rect;
485
track_icons[VISIBILITY_ICON] = visible_rect;
486
track_icons[SOLO_ICON] = solo_rect;
487
488
subtrack_icons[current_track] = track_icons;
489
490
vofs += text_buf.get_size().y + v_separation;
491
track_v_scroll_max += text_buf.get_size().y + v_separation;
492
}
493
}
494
495
const Color accent = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
496
497
// Guides.
498
{
499
float min_left_scale = font->get_height(font_size) + v_separation;
500
501
float scale = (min_left_scale * 2) * timeline_v_zoom;
502
float step = Math::pow(10.0, Math::round(Math::log(scale / 5.0) / Math::log(10.0))) * 5.0;
503
scale = Math::snapped(scale, step);
504
505
while (scale / timeline_v_zoom < min_left_scale * 2) {
506
scale += step;
507
}
508
509
bool first = true;
510
int prev_iv = 0;
511
for (int i = font->get_height(font_size); i < get_size().height; i++) {
512
float ofs = get_size().height / 2.0 - i;
513
ofs *= timeline_v_zoom;
514
ofs += timeline_v_scroll;
515
516
int iv = int(ofs / scale);
517
if (ofs < 0) {
518
iv -= 1;
519
}
520
if (!first && iv != prev_iv) {
521
Color lc = h_line_color;
522
lc.a *= 0.5;
523
draw_line(Point2(limit, i), Point2(right_limit, i), lc, Math::round(EDSCALE));
524
Color c = color;
525
c.a *= 0.5;
526
draw_string(font, Point2(limit + 8, i - 2), TS->format_number(rtos(Math::snapped((iv + 1) * scale, step))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, c);
527
}
528
529
first = false;
530
prev_iv = iv;
531
}
532
}
533
534
// Draw other curves.
535
{
536
float scale = timeline->get_zoom_scale();
537
Ref<Texture2D> point = get_editor_theme_icon(SNAME("KeyValue"));
538
for (const KeyValue<int, Color> &E : subtrack_colors) {
539
if (hidden_tracks.has(E.key)) {
540
continue;
541
}
542
_draw_track(E.key, E.value);
543
544
for (int i = 0; i < animation->track_get_key_count(E.key); i++) {
545
float offset = animation->track_get_key_time(E.key, i);
546
float value = animation->bezier_track_get_key_value(E.key, i);
547
548
Vector2 pos((offset - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value));
549
550
if (pos.x >= limit && pos.x <= right_limit) {
551
draw_texture(point, pos - point->get_size() / 2.0, E.value);
552
}
553
}
554
}
555
556
if (track_count > 0 && !hidden_tracks.has(selected_track)) {
557
// Draw edited curve.
558
_draw_track(selected_track, selected_track_color);
559
}
560
}
561
562
const bool draw_selection_handles = selection.size() > 1;
563
LocalVector<Point2> selected_pos;
564
565
// Draw editor handles.
566
{
567
edit_points.clear();
568
float scale = timeline->get_zoom_scale();
569
570
for (int i = 0; i < track_count; ++i) {
571
bool draw_track = _is_track_curves_displayed(i) && !locked_tracks.has(i);
572
if (!draw_selection_handles && !draw_track) {
573
continue;
574
}
575
576
int key_count = animation->track_get_key_count(i);
577
for (int j = 0; j < key_count; ++j) {
578
float offset = animation->track_get_key_time(i, j);
579
float value = animation->bezier_track_get_key_value(i, j);
580
bool is_selected = selection.has(IntPair(i, j));
581
582
if (is_selected) {
583
if (moving_selection) {
584
offset += moving_selection_offset.x;
585
value += moving_selection_offset.y;
586
} else if (scaling_selection) {
587
offset += -scaling_selection_offset.x + (offset - scaling_selection_pivot.x) * (scaling_selection_scale.x - 1);
588
value += -scaling_selection_offset.y + (value - scaling_selection_pivot.y) * (scaling_selection_scale.y - 1);
589
}
590
}
591
592
Vector2 pos((offset - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value));
593
594
if (draw_selection_handles && is_selected) {
595
selected_pos.push_back(pos);
596
}
597
598
if (!draw_track) {
599
continue;
600
}
601
602
Vector2 in_vec = animation->bezier_track_get_key_in_handle(i, j);
603
Vector2 out_vec = animation->bezier_track_get_key_out_handle(i, j);
604
605
if ((moving_handle == 1 || moving_handle == -1) && moving_handle_track == i && moving_handle_key == j) {
606
in_vec = moving_handle_left;
607
}
608
609
if ((moving_handle == 1 || moving_handle == -1) && moving_handle_track == i && moving_handle_key == j) {
610
out_vec = moving_handle_right;
611
}
612
613
if (moving_inserted_key && moving_selection_from_key == j) {
614
Animation::HandleMode handle_mode = animation->bezier_track_get_key_handle_mode(i, j);
615
if (handle_mode != Animation::HANDLE_MODE_FREE) {
616
int key_prev = 0;
617
int key_next = moving_selection_from_key;
618
for (int k = 0; k < key_count; k++) {
619
if (k == moving_selection_from_key) {
620
continue;
621
}
622
623
if (animation->track_get_key_time(i, k) < offset) {
624
key_prev = k;
625
} else {
626
key_next = k;
627
break;
628
}
629
}
630
631
float prev_time = offset;
632
float prev_value = value;
633
if (key_prev != moving_selection_from_key) {
634
prev_time = animation->track_get_key_time(i, key_prev);
635
prev_value = animation->bezier_track_get_key_value(i, key_prev);
636
}
637
638
float next_time = offset;
639
float next_value = value;
640
if (key_next != moving_selection_from_key) {
641
next_time = animation->track_get_key_time(i, key_next);
642
next_value = animation->bezier_track_get_key_value(i, key_next);
643
}
644
645
animation->bezier_track_calculate_handles(offset, prev_time, prev_value, next_time, next_value, handle_mode, Animation::HANDLE_SET_MODE_AUTO, &in_vec, &out_vec);
646
}
647
}
648
649
Vector2 pos_in(((offset + in_vec.x) - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value + in_vec.y));
650
Vector2 pos_out(((offset + out_vec.x) - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value + out_vec.y));
651
652
if (i == selected_track || is_selected) {
653
_draw_line_clipped(pos, pos_in, accent, limit, right_limit);
654
_draw_line_clipped(pos, pos_out, accent, limit, right_limit);
655
}
656
657
EditPoint ep;
658
ep.track = i;
659
ep.key = j;
660
if (pos.x >= limit && pos.x <= right_limit) {
661
ep.point_rect.position = (pos - bezier_icon->get_size() / 2.0).floor();
662
ep.point_rect.size = bezier_icon->get_size();
663
if (is_selected) {
664
draw_texture(selected_icon, ep.point_rect.position);
665
draw_string(font, ep.point_rect.position + Vector2(8, -font->get_height(font_size) - 8), TTR("Time:") + " " + TS->format_number(rtos(Math::snapped(offset, 0.0001))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, accent);
666
draw_string(font, ep.point_rect.position + Vector2(8, -8), TTR("Value:") + " " + TS->format_number(rtos(Math::snapped(value, 0.001))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, accent);
667
} else {
668
Color track_color = Color(1, 1, 1, 1);
669
if (i != selected_track) {
670
track_color = subtrack_colors[i];
671
}
672
draw_texture(bezier_icon, ep.point_rect.position, track_color);
673
}
674
ep.point_rect = ep.point_rect.grow(ep.point_rect.size.width * 0.5);
675
}
676
ep.point_rect = ep.point_rect.grow(ep.point_rect.size.width * 0.5);
677
678
if (i == selected_track || is_selected) {
679
if (animation->bezier_track_get_key_handle_mode(i, j) != Animation::HANDLE_MODE_LINEAR) {
680
if (pos_in.x >= limit && pos_in.x <= right_limit) {
681
ep.in_rect.position = (pos_in - bezier_handle_icon->get_size() / 2.0).floor();
682
ep.in_rect.size = bezier_handle_icon->get_size();
683
draw_texture(bezier_handle_icon, ep.in_rect.position);
684
ep.in_rect = ep.in_rect.grow(ep.in_rect.size.width * 0.5);
685
}
686
if (pos_out.x >= limit && pos_out.x <= right_limit) {
687
ep.out_rect.position = (pos_out - bezier_handle_icon->get_size() / 2.0).floor();
688
ep.out_rect.size = bezier_handle_icon->get_size();
689
draw_texture(bezier_handle_icon, ep.out_rect.position);
690
ep.out_rect = ep.out_rect.grow(ep.out_rect.size.width * 0.5);
691
}
692
}
693
}
694
if (!locked_tracks.has(i)) {
695
edit_points.push_back(ep);
696
}
697
}
698
}
699
700
for (int i = 0; i < edit_points.size(); ++i) {
701
if (edit_points[i].track == selected_track) {
702
EditPoint ep = edit_points[i];
703
edit_points.remove_at(i);
704
edit_points.insert(0, ep);
705
}
706
}
707
}
708
709
selection_rect = Rect2();
710
selection_handles_rect = Rect2();
711
// Draw scale handles.
712
if (draw_selection_handles) {
713
selection_rect.position = selected_pos[0];
714
selected_pos.remove_at(0);
715
for (const Point2 &pos : selected_pos) {
716
selection_rect = selection_rect.expand(pos);
717
}
718
719
const int outer_ofs = Math::round(12 * EDSCALE);
720
const int inner_ofs = Math::round(outer_ofs / 2.0);
721
722
// Draw horizontal handles.
723
if (selection_rect.size.height > CMP_EPSILON) {
724
_draw_line_clipped(selection_rect.position - Vector2(inner_ofs, inner_ofs), selection_rect.position + Vector2(selection_rect.size.width + inner_ofs, -inner_ofs), accent, limit, right_limit);
725
_draw_line_clipped(selection_rect.position + Vector2(-inner_ofs, selection_rect.size.height + inner_ofs), selection_rect.position + selection_rect.size + Vector2(inner_ofs, inner_ofs), accent, limit, right_limit);
726
}
727
// Draw vertical handles.
728
if (selection_rect.size.width > CMP_EPSILON) {
729
_draw_line_clipped(selection_rect.position - Vector2(inner_ofs, inner_ofs), selection_rect.position + Vector2(-inner_ofs, selection_rect.size.height + inner_ofs), accent, limit, right_limit);
730
_draw_line_clipped(selection_rect.position + Vector2(selection_rect.size.width + inner_ofs, -inner_ofs), selection_rect.position + selection_rect.size + Vector2(inner_ofs, inner_ofs), accent, limit, right_limit);
731
}
732
733
selection_handles_rect.position = selection_rect.position - Vector2(outer_ofs, outer_ofs);
734
selection_handles_rect.size = selection_rect.size + Vector2(outer_ofs, outer_ofs) * 2;
735
}
736
737
if (box_selecting) {
738
Vector2 bs_from = box_selection_from;
739
Vector2 bs_to = box_selection_to;
740
if (bs_from.x > bs_to.x) {
741
SWAP(bs_from.x, bs_to.x);
742
}
743
if (bs_from.y > bs_to.y) {
744
SWAP(bs_from.y, bs_to.y);
745
}
746
draw_rect(
747
Rect2(bs_from, bs_to - bs_from),
748
get_theme_color(SNAME("box_selection_fill_color"), EditorStringName(Editor)));
749
draw_rect(
750
Rect2(bs_from, bs_to - bs_from),
751
get_theme_color(SNAME("box_selection_stroke_color"), EditorStringName(Editor)),
752
false,
753
Math::round(EDSCALE));
754
}
755
} break;
756
}
757
}
758
759
// Check if a track is displayed in the bezier editor (track type = bezier and track not filtered).
760
bool AnimationBezierTrackEdit::_is_track_displayed(int p_track_index) {
761
if (animation->track_get_type(p_track_index) != Animation::TrackType::TYPE_BEZIER) {
762
return false;
763
}
764
765
if (is_filtered) {
766
String path = String(animation->track_get_path(p_track_index));
767
if (root && root->has_node(path)) {
768
Node *node = root->get_node(path);
769
if (!node) {
770
return false; // No node, no filter.
771
}
772
if (!EditorNode::get_singleton()->get_editor_selection()->is_selected(node)) {
773
return false; // Skip track due to not selected.
774
}
775
}
776
}
777
778
return true;
779
}
780
781
// Check if the curves for a track are displayed in the editor (not hidden). Includes the check on the track visibility.
782
bool AnimationBezierTrackEdit::_is_track_curves_displayed(int p_track_index) {
783
// Is the track is visible in the editor?
784
if (!_is_track_displayed(p_track_index)) {
785
return false;
786
}
787
788
// And curves visible?
789
if (hidden_tracks.has(p_track_index)) {
790
return false;
791
}
792
793
return true;
794
}
795
796
Ref<Animation> AnimationBezierTrackEdit::get_animation() const {
797
return animation;
798
}
799
800
void AnimationBezierTrackEdit::set_animation_and_track(const Ref<Animation> &p_animation, int p_track, bool p_read_only) {
801
animation = p_animation;
802
read_only = p_read_only;
803
selected_track = p_track;
804
queue_redraw();
805
}
806
807
Size2 AnimationBezierTrackEdit::get_minimum_size() const {
808
return Vector2(1, 1);
809
}
810
811
Control::CursorShape AnimationBezierTrackEdit::get_cursor_shape(const Point2 &p_pos) const {
812
// Box selecting or moving a handle
813
if (box_selecting || Math::abs(moving_handle) == 1) {
814
return get_default_cursor_shape();
815
}
816
// Hovering a handle
817
if (!read_only) {
818
for (const EditPoint &edit_point : edit_points) {
819
if (edit_point.in_rect.has_point(p_pos) || edit_point.out_rect.has_point(p_pos)) {
820
return get_default_cursor_shape();
821
}
822
}
823
}
824
// Currently box scaling
825
if (scaling_selection) {
826
if (scaling_selection_handles == Vector2i(1, 1) || scaling_selection_handles == Vector2i(-1, -1)) {
827
return CURSOR_FDIAGSIZE;
828
} else if (scaling_selection_handles == Vector2i(1, -1) || scaling_selection_handles == Vector2i(-1, 1)) {
829
return CURSOR_BDIAGSIZE;
830
} else if (abs(scaling_selection_handles.x) == 1) {
831
return CURSOR_HSIZE;
832
} else if (abs(scaling_selection_handles.y) == 1) {
833
return CURSOR_VSIZE;
834
}
835
}
836
// Hovering the scaling box
837
const Vector2i rel_pos = p_pos - selection_rect.position;
838
if (selection_handles_rect.has_point(p_pos)) {
839
if ((rel_pos.x < 0 && rel_pos.y < 0) || (rel_pos.x > selection_rect.size.width && rel_pos.y > selection_rect.size.height)) {
840
return CURSOR_FDIAGSIZE;
841
} else if ((rel_pos.x < 0 && rel_pos.y > selection_rect.size.height) || (rel_pos.x > selection_rect.size.width && rel_pos.y < 0)) {
842
return CURSOR_BDIAGSIZE;
843
} else if (rel_pos.x < 0 || rel_pos.x > selection_rect.size.width) {
844
return CURSOR_HSIZE;
845
} else if (rel_pos.y < 0 || rel_pos.y > selection_rect.size.height) {
846
return CURSOR_VSIZE;
847
}
848
return CURSOR_MOVE;
849
}
850
return get_default_cursor_shape();
851
}
852
853
void AnimationBezierTrackEdit::set_timeline(AnimationTimelineEdit *p_timeline) {
854
timeline = p_timeline;
855
timeline->connect("zoom_changed", callable_mp(this, &AnimationBezierTrackEdit::_zoom_changed));
856
timeline->connect("name_limit_changed", callable_mp(this, &AnimationBezierTrackEdit::_zoom_changed));
857
}
858
859
void AnimationBezierTrackEdit::set_editor(AnimationTrackEditor *p_editor) {
860
editor = p_editor;
861
connect("clear_selection", callable_mp(editor, &AnimationTrackEditor::_clear_selection).bind(false));
862
connect("select_key", callable_mp(editor, &AnimationTrackEditor::_key_selected), CONNECT_DEFERRED);
863
connect("deselect_key", callable_mp(editor, &AnimationTrackEditor::_key_deselected), CONNECT_DEFERRED);
864
}
865
866
void AnimationBezierTrackEdit::_play_position_draw() {
867
if (animation.is_null() || play_position_pos < 0) {
868
return;
869
}
870
871
float scale = timeline->get_zoom_scale();
872
int h = get_size().height;
873
874
int limit = timeline->get_name_limit();
875
876
int px = (-timeline->get_value() + play_position_pos) * scale + limit;
877
878
if (px >= limit && px < (get_size().width)) {
879
const Color color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
880
play_position->draw_line(Point2(px, 0), Point2(px, h), color, Math::round(2 * EDSCALE));
881
}
882
}
883
884
void AnimationBezierTrackEdit::set_play_position(real_t p_pos) {
885
play_position_pos = p_pos;
886
play_position->queue_redraw();
887
}
888
889
void AnimationBezierTrackEdit::update_play_position() {
890
play_position->queue_redraw();
891
}
892
893
void AnimationBezierTrackEdit::set_root(Node *p_root) {
894
root = p_root;
895
}
896
897
void AnimationBezierTrackEdit::set_filtered(bool p_filtered) {
898
is_filtered = p_filtered;
899
if (animation.is_null()) {
900
return;
901
}
902
String base_path = String(animation->track_get_path(selected_track));
903
if (is_filtered) {
904
if (root && root->has_node(base_path)) {
905
Node *node = root->get_node(base_path);
906
if (!node || !EditorNode::get_singleton()->get_editor_selection()->is_selected(node)) {
907
for (int i = 0; i < animation->get_track_count(); ++i) {
908
if (animation->track_get_type(i) != Animation::TrackType::TYPE_BEZIER) {
909
continue;
910
}
911
912
base_path = String(animation->track_get_path(i));
913
if (root && root->has_node(base_path)) {
914
node = root->get_node(base_path);
915
if (!node) {
916
continue; // No node, no filter.
917
}
918
if (!EditorNode::get_singleton()->get_editor_selection()->is_selected(node)) {
919
continue; // Skip track due to not selected.
920
}
921
922
set_animation_and_track(animation, i, read_only);
923
break;
924
}
925
}
926
}
927
}
928
}
929
queue_redraw();
930
}
931
932
void AnimationBezierTrackEdit::auto_fit_vertically() {
933
int track_count = animation->get_track_count();
934
real_t minimum_value = Math::INF;
935
real_t maximum_value = -Math::INF;
936
937
int nb_track_visible = 0;
938
for (int i = 0; i < track_count; ++i) {
939
if (!_is_track_curves_displayed(i) || locked_tracks.has(i)) {
940
continue;
941
}
942
943
int key_count = animation->track_get_key_count(i);
944
945
for (int j = 0; j < key_count; ++j) {
946
real_t value = animation->bezier_track_get_key_value(i, j);
947
948
minimum_value = MIN(value, minimum_value);
949
maximum_value = MAX(value, maximum_value);
950
951
// We also want to includes the handles...
952
Vector2 in_vec = animation->bezier_track_get_key_in_handle(i, j);
953
Vector2 out_vec = animation->bezier_track_get_key_out_handle(i, j);
954
955
minimum_value = MIN(value + in_vec.y, minimum_value);
956
maximum_value = MAX(value + in_vec.y, maximum_value);
957
minimum_value = MIN(value + out_vec.y, minimum_value);
958
maximum_value = MAX(value + out_vec.y, maximum_value);
959
}
960
961
nb_track_visible++;
962
}
963
964
if (nb_track_visible == 0) {
965
// No visible track... we will not adjust the vertical zoom
966
return;
967
}
968
969
if (Math::is_finite(minimum_value) && Math::is_finite(maximum_value)) {
970
_zoom_vertically(minimum_value, maximum_value);
971
queue_redraw();
972
}
973
}
974
975
void AnimationBezierTrackEdit::_zoom_vertically(real_t p_minimum_value, real_t p_maximum_value) {
976
real_t target_height = p_maximum_value - p_minimum_value;
977
if (target_height <= CMP_EPSILON) {
978
timeline_v_scroll = p_maximum_value;
979
return;
980
}
981
982
timeline_v_scroll = (p_maximum_value + p_minimum_value) / 2.0;
983
timeline_v_zoom = target_height / ((get_size().height - timeline->get_size().height) * 0.9);
984
}
985
986
void AnimationBezierTrackEdit::_zoom_changed() {
987
queue_redraw();
988
play_position->queue_redraw();
989
}
990
991
void AnimationBezierTrackEdit::_update_locked_tracks_after(int p_track) {
992
if (locked_tracks.has(p_track)) {
993
locked_tracks.erase(p_track);
994
}
995
996
Vector<int> updated_locked_tracks;
997
for (const int &E : locked_tracks) {
998
updated_locked_tracks.push_back(E);
999
}
1000
locked_tracks.clear();
1001
for (int i = 0; i < updated_locked_tracks.size(); ++i) {
1002
if (updated_locked_tracks[i] > p_track) {
1003
locked_tracks.insert(updated_locked_tracks[i] - 1);
1004
} else {
1005
locked_tracks.insert(updated_locked_tracks[i]);
1006
}
1007
}
1008
}
1009
1010
void AnimationBezierTrackEdit::_update_hidden_tracks_after(int p_track) {
1011
if (hidden_tracks.has(p_track)) {
1012
hidden_tracks.erase(p_track);
1013
}
1014
1015
Vector<int> updated_hidden_tracks;
1016
for (const int &E : hidden_tracks) {
1017
updated_hidden_tracks.push_back(E);
1018
}
1019
hidden_tracks.clear();
1020
for (int i = 0; i < updated_hidden_tracks.size(); ++i) {
1021
if (updated_hidden_tracks[i] > p_track) {
1022
hidden_tracks.insert(updated_hidden_tracks[i] - 1);
1023
} else {
1024
hidden_tracks.insert(updated_hidden_tracks[i]);
1025
}
1026
}
1027
}
1028
1029
String AnimationBezierTrackEdit::get_tooltip(const Point2 &p_pos) const {
1030
return Control::get_tooltip(p_pos);
1031
}
1032
1033
void AnimationBezierTrackEdit::_clear_selection() {
1034
selection.clear();
1035
emit_signal(SNAME("clear_selection"));
1036
queue_redraw();
1037
}
1038
1039
void AnimationBezierTrackEdit::_change_selected_keys_handle_mode(Animation::HandleMode p_mode, bool p_auto) {
1040
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
1041
undo_redo->create_action(TTR("Update Selected Key Handles"), UndoRedo::MERGE_DISABLE, animation.ptr());
1042
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
1043
const IntPair track_key_pair = E->get();
1044
undo_redo->add_undo_method(editor, "_bezier_track_set_key_handle_mode", animation.ptr(), track_key_pair.first, track_key_pair.second, animation->bezier_track_get_key_handle_mode(track_key_pair.first, track_key_pair.second), Animation::HANDLE_SET_MODE_NONE);
1045
undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_in_handle", track_key_pair.first, track_key_pair.second, animation->bezier_track_get_key_in_handle(track_key_pair.first, track_key_pair.second));
1046
undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", track_key_pair.first, track_key_pair.second, animation->bezier_track_get_key_out_handle(track_key_pair.first, track_key_pair.second));
1047
undo_redo->add_do_method(editor, "_bezier_track_set_key_handle_mode", animation.ptr(), track_key_pair.first, track_key_pair.second, p_mode, p_auto ? Animation::HANDLE_SET_MODE_AUTO : Animation::HANDLE_SET_MODE_RESET);
1048
}
1049
AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton();
1050
if (ape) {
1051
undo_redo->add_do_method(ape, "_animation_update_key_frame");
1052
undo_redo->add_undo_method(ape, "_animation_update_key_frame");
1053
}
1054
undo_redo->commit_action();
1055
}
1056
1057
void AnimationBezierTrackEdit::_clear_selection_for_anim(const Ref<Animation> &p_anim) {
1058
if (!(animation == p_anim) || !is_visible()) {
1059
return;
1060
}
1061
_clear_selection();
1062
}
1063
1064
void AnimationBezierTrackEdit::_select_at_anim(const Ref<Animation> &p_anim, int p_track, real_t p_pos, bool p_single) {
1065
if (!(animation == p_anim) || !is_visible()) {
1066
return;
1067
}
1068
1069
int idx = animation->track_find_key(p_track, p_pos, Animation::FIND_MODE_APPROX);
1070
ERR_FAIL_COND(idx < 0);
1071
1072
selection.insert(IntPair(p_track, idx));
1073
emit_signal(SNAME("select_key"), idx, p_single, p_track);
1074
queue_redraw();
1075
}
1076
1077
void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
1078
ERR_FAIL_COND(p_event.is_null());
1079
1080
if (panner->gui_input(p_event)) {
1081
accept_event();
1082
return;
1083
}
1084
1085
if (p_event->is_pressed()) {
1086
if (ED_IS_SHORTCUT("animation_editor/duplicate_selected_keys", p_event)) {
1087
if (!read_only) {
1088
duplicate_selected_keys(-1.0, false);
1089
}
1090
accept_event();
1091
}
1092
if (ED_IS_SHORTCUT("animation_editor/cut_selected_keys", p_event)) {
1093
if (!read_only) {
1094
copy_selected_keys(true);
1095
}
1096
accept_event();
1097
}
1098
if (ED_IS_SHORTCUT("animation_editor/copy_selected_keys", p_event)) {
1099
if (!read_only) {
1100
copy_selected_keys(false);
1101
}
1102
accept_event();
1103
}
1104
if (ED_IS_SHORTCUT("animation_editor/paste_keys", p_event)) {
1105
if (!read_only) {
1106
paste_keys(-1.0, false);
1107
}
1108
accept_event();
1109
}
1110
if (ED_IS_SHORTCUT("animation_editor/delete_selection", p_event)) {
1111
if (!read_only) {
1112
delete_selection();
1113
}
1114
accept_event();
1115
}
1116
}
1117
1118
Ref<InputEventKey> key_press = p_event;
1119
1120
if (key_press.is_valid() && key_press->is_pressed()) {
1121
if (ED_IS_SHORTCUT("animation_bezier_editor/focus", p_event)) {
1122
SelectionSet focused_keys;
1123
if (selection.is_empty()) {
1124
for (int i = 0; i < edit_points.size(); ++i) {
1125
IntPair key_pair = IntPair(edit_points[i].track, edit_points[i].key);
1126
focused_keys.insert(key_pair);
1127
}
1128
} else {
1129
for (const IntPair &E : selection) {
1130
focused_keys.insert(E);
1131
if (E.second > 0) {
1132
IntPair previous_key = IntPair(E.first, E.second - 1);
1133
focused_keys.insert(previous_key);
1134
}
1135
if (E.second < animation->track_get_key_count(E.first) - 1) {
1136
IntPair next_key = IntPair(E.first, E.second + 1);
1137
focused_keys.insert(next_key);
1138
}
1139
}
1140
}
1141
if (focused_keys.is_empty()) {
1142
accept_event();
1143
return;
1144
}
1145
1146
real_t minimum_time = Math::INF;
1147
real_t maximum_time = -Math::INF;
1148
real_t minimum_value = Math::INF;
1149
real_t maximum_value = -Math::INF;
1150
1151
for (const IntPair &E : focused_keys) {
1152
IntPair key_pair = E;
1153
1154
real_t time = animation->track_get_key_time(key_pair.first, key_pair.second);
1155
real_t value = animation->bezier_track_get_key_value(key_pair.first, key_pair.second);
1156
1157
minimum_time = MIN(time, minimum_time);
1158
maximum_time = MAX(time, maximum_time);
1159
minimum_value = MIN(value, minimum_value);
1160
maximum_value = MAX(value, maximum_value);
1161
}
1162
1163
float width = get_size().width - timeline->get_name_limit() - timeline->get_buttons_width();
1164
float padding = width * 0.1;
1165
float desired_scale = (width - padding / 2.0) / (maximum_time - minimum_time);
1166
minimum_time = MAX(0, minimum_time - (padding / 2.0) / desired_scale);
1167
1168
float zv = Math::pow(100 / desired_scale, 0.125f);
1169
if (zv < 1) {
1170
zv = Math::pow(desired_scale / 100, 0.125f) - 1;
1171
zv = 1 - zv;
1172
}
1173
float zoom_value = timeline->get_zoom()->get_max() - zv;
1174
1175
if (Math::is_finite(minimum_time) && Math::is_finite(maximum_time) && maximum_time - minimum_time > CMP_EPSILON) {
1176
timeline->get_zoom()->set_value(zoom_value);
1177
callable_mp((Range *)timeline, &Range::set_value).call_deferred(minimum_time);
1178
}
1179
1180
if (Math::is_finite(minimum_value) && Math::is_finite(maximum_value)) {
1181
_zoom_vertically(minimum_value, maximum_value);
1182
}
1183
1184
queue_redraw();
1185
accept_event();
1186
return;
1187
} else if (ED_IS_SHORTCUT("animation_bezier_editor/select_all_keys", p_event)) {
1188
for (int i = 0; i < edit_points.size(); ++i) {
1189
_select_at_anim(animation, edit_points[i].track, animation->track_get_key_time(edit_points[i].track, edit_points[i].key), i == 0);
1190
}
1191
1192
queue_redraw();
1193
accept_event();
1194
return;
1195
} else if (ED_IS_SHORTCUT("animation_bezier_editor/deselect_all_keys", p_event)) {
1196
selection.clear();
1197
emit_signal(SNAME("clear_selection"));
1198
1199
queue_redraw();
1200
accept_event();
1201
return;
1202
}
1203
}
1204
1205
Ref<InputEventMouseButton> mb = p_event;
1206
int limit = timeline->get_name_limit();
1207
if (mb.is_valid() && mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) {
1208
menu_insert_key = mb->get_position();
1209
if (menu_insert_key.x >= limit && menu_insert_key.x <= get_size().width) {
1210
if (!read_only) {
1211
Vector2 popup_pos = get_screen_position() + mb->get_position();
1212
1213
bool selected = _try_select_at_ui_pos(mb->get_position(), mb->is_shift_pressed(), false);
1214
1215
menu->clear();
1216
menu->add_icon_item(bezier_icon, TTR("Insert Key Here"), MENU_KEY_INSERT);
1217
if (selected || selection.size()) {
1218
menu->add_separator();
1219
menu->add_icon_item(get_editor_theme_icon(SNAME("Duplicate")), TTR("Duplicate Selected Key(s)"), MENU_KEY_DUPLICATE);
1220
menu->add_icon_item(get_editor_theme_icon(SNAME("ActionCut")), TTR("Cut Selected Key(s)"), MENU_KEY_CUT);
1221
menu->add_icon_item(get_editor_theme_icon(SNAME("ActionCopy")), TTR("Copy Selected Key(s)"), MENU_KEY_COPY);
1222
}
1223
1224
if (editor->is_key_clipboard_active()) {
1225
menu->add_icon_item(get_editor_theme_icon(SNAME("ActionPaste")), TTR("Paste Key(s)"), MENU_KEY_PASTE);
1226
}
1227
1228
if (selected || selection.size()) {
1229
menu->add_separator();
1230
menu->add_icon_item(get_editor_theme_icon(SNAME("Remove")), TTR("Delete Selected Key(s)"), MENU_KEY_DELETE);
1231
menu->add_separator();
1232
menu->add_icon_item(get_editor_theme_icon(SNAME("BezierHandlesFree")), TTR("Make Handles Free"), MENU_KEY_SET_HANDLE_FREE);
1233
menu->add_icon_item(get_editor_theme_icon(SNAME("BezierHandlesLinear")), TTR("Make Handles Linear"), MENU_KEY_SET_HANDLE_LINEAR);
1234
menu->add_icon_item(get_editor_theme_icon(SNAME("BezierHandlesBalanced")), TTR("Make Handles Balanced"), MENU_KEY_SET_HANDLE_BALANCED);
1235
menu->add_icon_item(get_editor_theme_icon(SNAME("BezierHandlesMirror")), TTR("Make Handles Mirrored"), MENU_KEY_SET_HANDLE_MIRRORED);
1236
menu->add_separator();
1237
menu->add_icon_item(get_editor_theme_icon(SNAME("BezierHandlesBalanced")), TTR("Make Handles Balanced (Auto Tangent)"), MENU_KEY_SET_HANDLE_AUTO_BALANCED);
1238
menu->add_icon_item(get_editor_theme_icon(SNAME("BezierHandlesMirror")), TTR("Make Handles Mirrored (Auto Tangent)"), MENU_KEY_SET_HANDLE_AUTO_MIRRORED);
1239
}
1240
1241
if (menu->get_item_count()) {
1242
menu->reset_size();
1243
menu->set_position(popup_pos);
1244
menu->popup();
1245
}
1246
}
1247
}
1248
}
1249
1250
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
1251
Point2 pos = mb->get_position();
1252
bool no_mod_key_pressed = !mb->is_alt_pressed() && !mb->is_shift_pressed() && !mb->is_command_or_control_pressed();
1253
if (mb->is_double_click() && !moving_selection && no_mod_key_pressed) {
1254
int x = pos.x - timeline->get_name_limit();
1255
float ofs = x / timeline->get_zoom_scale() + timeline->get_value();
1256
emit_signal(SNAME("timeline_changed"), ofs, false);
1257
}
1258
for (const KeyValue<int, Rect2> &E : subtracks) {
1259
if (E.value.has_point(mb->get_position())) {
1260
if (!locked_tracks.has(E.key) && !hidden_tracks.has(E.key)) {
1261
set_animation_and_track(animation, E.key, read_only);
1262
_clear_selection();
1263
}
1264
return;
1265
}
1266
}
1267
1268
for (const KeyValue<int, RBMap<int, Rect2>> &E : subtrack_icons) {
1269
int track = E.key;
1270
RBMap<int, Rect2> track_icons = E.value;
1271
for (const KeyValue<int, Rect2> &I : track_icons) {
1272
if (I.value.has_point(mb->get_position())) {
1273
if (I.key == REMOVE_ICON) {
1274
if (!read_only) {
1275
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
1276
undo_redo->create_action("Remove Bezier Track", UndoRedo::MERGE_DISABLE, animation.ptr());
1277
1278
undo_redo->add_do_method(this, "_update_locked_tracks_after", track);
1279
undo_redo->add_do_method(this, "_update_hidden_tracks_after", track);
1280
1281
undo_redo->add_do_method(animation.ptr(), "remove_track", track);
1282
1283
undo_redo->add_undo_method(animation.ptr(), "add_track", Animation::TrackType::TYPE_BEZIER, track);
1284
undo_redo->add_undo_method(animation.ptr(), "track_set_path", track, animation->track_get_path(track));
1285
1286
for (int i = 0; i < animation->track_get_key_count(track); ++i) {
1287
undo_redo->add_undo_method(
1288
this,
1289
"_bezier_track_insert_key_at_anim",
1290
animation,
1291
track,
1292
animation->track_get_key_time(track, i),
1293
animation->bezier_track_get_key_value(track, i),
1294
animation->bezier_track_get_key_in_handle(track, i),
1295
animation->bezier_track_get_key_out_handle(track, i),
1296
animation->bezier_track_get_key_handle_mode(track, i));
1297
}
1298
1299
undo_redo->commit_action();
1300
1301
selected_track = CLAMP(selected_track, 0, animation->get_track_count() - 1);
1302
}
1303
return;
1304
} else if (I.key == LOCK_ICON) {
1305
if (locked_tracks.has(track)) {
1306
locked_tracks.erase(track);
1307
} else {
1308
locked_tracks.insert(track);
1309
if (selected_track == track) {
1310
for (int i = 0; i < animation->get_track_count(); ++i) {
1311
if (!locked_tracks.has(i) && animation->track_get_type(i) == Animation::TrackType::TYPE_BEZIER) {
1312
set_animation_and_track(animation, i, read_only);
1313
break;
1314
}
1315
}
1316
}
1317
}
1318
queue_redraw();
1319
return;
1320
} else if (I.key == VISIBILITY_ICON) {
1321
if (hidden_tracks.has(track)) {
1322
hidden_tracks.erase(track);
1323
} else {
1324
hidden_tracks.insert(track);
1325
if (selected_track == track) {
1326
for (int i = 0; i < animation->get_track_count(); ++i) {
1327
if (!hidden_tracks.has(i) && animation->track_get_type(i) == Animation::TrackType::TYPE_BEZIER) {
1328
set_animation_and_track(animation, i, read_only);
1329
break;
1330
}
1331
}
1332
}
1333
}
1334
1335
Vector<int> visible_tracks;
1336
for (int i = 0; i < animation->get_track_count(); ++i) {
1337
if (!hidden_tracks.has(i) && animation->track_get_type(i) == Animation::TrackType::TYPE_BEZIER) {
1338
visible_tracks.push_back(i);
1339
}
1340
}
1341
1342
if (visible_tracks.size() == 1) {
1343
solo_track = visible_tracks[0];
1344
} else {
1345
solo_track = -1;
1346
}
1347
1348
queue_redraw();
1349
return;
1350
} else if (I.key == SOLO_ICON) {
1351
if (solo_track == track) {
1352
solo_track = -1;
1353
1354
hidden_tracks.clear();
1355
} else {
1356
if (hidden_tracks.has(track)) {
1357
hidden_tracks.erase(track);
1358
}
1359
for (int i = 0; i < animation->get_track_count(); ++i) {
1360
if (animation->track_get_type(i) == Animation::TrackType::TYPE_BEZIER) {
1361
if (i != track && !hidden_tracks.has(i)) {
1362
hidden_tracks.insert(i);
1363
}
1364
}
1365
}
1366
1367
set_animation_and_track(animation, track, read_only);
1368
solo_track = track;
1369
}
1370
queue_redraw();
1371
return;
1372
}
1373
return;
1374
}
1375
}
1376
}
1377
1378
// Check this first, to allow manipulating key handles while ignoring keyframes before scaling/moving.
1379
bool inside_selection_handles_rect = !read_only && selection_handles_rect.has_point(mb->get_position());
1380
1381
// First, check keyframe.
1382
// Command/Control makes it ignore the keyframe, so control point editors can be force-edited.
1383
if (!inside_selection_handles_rect && !mb->is_command_or_control_pressed()) {
1384
if (_try_select_at_ui_pos(mb->get_position(), mb->is_shift_pressed(), true)) {
1385
return;
1386
}
1387
}
1388
// Second, check key handles.
1389
for (int i = 0; i < edit_points.size(); i++) {
1390
if (!read_only) {
1391
if (edit_points[i].in_rect.has_point(mb->get_position())) {
1392
moving_handle = -1;
1393
moving_handle_key = edit_points[i].key;
1394
moving_handle_track = edit_points[i].track;
1395
moving_handle_left = animation->bezier_track_get_key_in_handle(edit_points[i].track, edit_points[i].key);
1396
moving_handle_right = animation->bezier_track_get_key_out_handle(edit_points[i].track, edit_points[i].key);
1397
queue_redraw();
1398
return;
1399
}
1400
1401
if (edit_points[i].out_rect.has_point(mb->get_position())) {
1402
moving_handle = 1;
1403
moving_handle_key = edit_points[i].key;
1404
moving_handle_track = edit_points[i].track;
1405
moving_handle_left = animation->bezier_track_get_key_in_handle(edit_points[i].track, edit_points[i].key);
1406
moving_handle_right = animation->bezier_track_get_key_out_handle(edit_points[i].track, edit_points[i].key);
1407
queue_redraw();
1408
return;
1409
}
1410
}
1411
}
1412
1413
// Box scaling/movement.
1414
if (inside_selection_handles_rect) {
1415
const Vector2i rel_pos = mb->get_position() - selection_rect.position;
1416
scaling_selection_handles = Vector2i();
1417
1418
// Check which scaling handles are available.
1419
if (selection_rect.size.width > CMP_EPSILON) {
1420
if (rel_pos.x <= 0) {
1421
scaling_selection_handles.x = -1;
1422
} else if (rel_pos.x >= selection_rect.size.width) {
1423
scaling_selection_handles.x = 1;
1424
}
1425
}
1426
if (selection_rect.size.height > CMP_EPSILON) {
1427
if (rel_pos.y <= 0) {
1428
scaling_selection_handles.y = -1;
1429
} else if (rel_pos.y >= selection_rect.size.height) {
1430
scaling_selection_handles.y = 1;
1431
}
1432
}
1433
1434
if (scaling_selection_handles != Vector2i()) {
1435
scaling_selection = true;
1436
1437
const float time = ((selection_rect.position.x - limit) / timeline->get_zoom_scale()) + timeline->get_value();
1438
const float h = (get_size().height / 2.0 - selection_rect.position.y) * timeline_v_zoom + timeline_v_scroll;
1439
scaling_selection_pivot = Point2(time, h);
1440
1441
return;
1442
}
1443
1444
// If not scaling, that means we're moving.
1445
moving_selection_attempt = true;
1446
moving_selection = false;
1447
moving_selection_mouse_begin = mb->get_position();
1448
// The pivot will be from the mouse click location, not a specific key.
1449
moving_selection_from_key = -1;
1450
moving_selection_from_track = selected_track;
1451
moving_selection_offset = Vector2();
1452
select_single_attempt = IntPair(-1, -1);
1453
1454
return;
1455
}
1456
1457
// Insert new point.
1458
if (mb->get_position().x >= limit && mb->get_position().x < get_size().width && mb->is_command_or_control_pressed()) {
1459
float h = (get_size().height / 2.0 - mb->get_position().y) * timeline_v_zoom + timeline_v_scroll;
1460
Array new_point = animation->make_default_bezier_key(h);
1461
1462
real_t time = ((mb->get_position().x - limit) / timeline->get_zoom_scale()) + timeline->get_value();
1463
while (animation->track_find_key(selected_track, time, Animation::FIND_MODE_APPROX) != -1) {
1464
time += 0.0001;
1465
}
1466
1467
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
1468
undo_redo->create_action(TTR("Add Bezier Point"));
1469
undo_redo->add_do_method(animation.ptr(), "bezier_track_insert_key", selected_track, time, new_point[0], Vector2(new_point[1], new_point[2]), Vector2(new_point[3], new_point[4]));
1470
undo_redo->add_do_method(editor, "_bezier_track_set_key_handle_mode_at_time", animation.ptr(), selected_track, time, (Animation::HandleMode)editor->bezier_key_mode->get_selected_id(), Animation::HANDLE_SET_MODE_AUTO);
1471
undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", selected_track, time);
1472
undo_redo->commit_action();
1473
1474
// Then attempt to move.
1475
int index = animation->track_find_key(selected_track, time, Animation::FIND_MODE_APPROX);
1476
ERR_FAIL_COND(index == -1);
1477
_clear_selection();
1478
_select_at_anim(animation, selected_track, animation->track_get_key_time(selected_track, index), true);
1479
1480
moving_selection_attempt = true;
1481
moving_inserted_key = true;
1482
moving_selection = false;
1483
moving_selection_mouse_begin = mb->get_position();
1484
moving_selection_from_key = index;
1485
moving_selection_from_track = selected_track;
1486
moving_selection_offset = Vector2();
1487
select_single_attempt = IntPair(-1, -1);
1488
queue_redraw();
1489
1490
return;
1491
}
1492
1493
// Box select.
1494
if (mb->get_position().x >= limit && mb->get_position().x < get_size().width) {
1495
box_selecting_attempt = true;
1496
box_selecting = false;
1497
box_selecting_add = false;
1498
box_selection_from = mb->get_position();
1499
return;
1500
}
1501
}
1502
1503
if (box_selecting_attempt && mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
1504
if (box_selecting) {
1505
// Do actual select.
1506
if (!box_selecting_add) {
1507
_clear_selection();
1508
}
1509
1510
Vector2 bs_from = box_selection_from;
1511
Vector2 bs_to = box_selection_to;
1512
if (bs_from.x > bs_to.x) {
1513
SWAP(bs_from.x, bs_to.x);
1514
}
1515
if (bs_from.y > bs_to.y) {
1516
SWAP(bs_from.y, bs_to.y);
1517
}
1518
Rect2 rect(bs_from, bs_to - bs_from);
1519
1520
bool track_set = false;
1521
int j = 0;
1522
for (int i = 0; i < edit_points.size(); i++) {
1523
if (edit_points[i].point_rect.intersects(rect)) {
1524
_select_at_anim(animation, edit_points[i].track, animation->track_get_key_time(edit_points[i].track, edit_points[i].key), j == 0 && !box_selecting_add);
1525
if (!track_set) {
1526
track_set = true;
1527
set_animation_and_track(animation, edit_points[i].track, read_only);
1528
}
1529
j++;
1530
}
1531
}
1532
} else {
1533
_clear_selection(); // Clicked and nothing happened, so clear the selection.
1534
1535
// Select by clicking on curve.
1536
int track_count = animation->get_track_count();
1537
1538
real_t animation_length = animation->get_length();
1539
animation->set_length(real_t(INT_MAX)); // bezier_track_interpolate doesn't find keys if they exist beyond anim length.
1540
1541
real_t time = ((mb->get_position().x - limit) / timeline->get_zoom_scale()) + timeline->get_value();
1542
1543
for (int i = 0; i < track_count; ++i) {
1544
if (animation->track_get_type(i) != Animation::TrackType::TYPE_BEZIER || hidden_tracks.has(i) || locked_tracks.has(i)) {
1545
continue;
1546
}
1547
1548
float track_h = animation->bezier_track_interpolate(i, time);
1549
float track_height = _bezier_h_to_pixel(track_h);
1550
1551
if (std::abs(mb->get_position().y - track_height) < 10) {
1552
set_animation_and_track(animation, i, read_only);
1553
break;
1554
}
1555
}
1556
1557
animation->set_length(animation_length);
1558
}
1559
1560
box_selecting_attempt = false;
1561
box_selecting = false;
1562
queue_redraw();
1563
}
1564
1565
if (moving_selection_attempt && mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
1566
if (!read_only) {
1567
if (moving_selection && (std::abs(moving_selection_offset.x) > CMP_EPSILON || std::abs(moving_selection_offset.y) > CMP_EPSILON)) {
1568
// Commit it.
1569
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
1570
undo_redo->create_action(TTR("Move Bezier Points"));
1571
1572
List<AnimMoveRestore> to_restore;
1573
List<Animation::HandleMode> to_restore_handle_modes;
1574
// 1 - Remove the keys.
1575
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
1576
undo_redo->add_do_method(animation.ptr(), "track_remove_key", E->get().first, E->get().second);
1577
}
1578
// 2 - Remove overlapped keys.
1579
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
1580
real_t newtime = animation->track_get_key_time(E->get().first, E->get().second) + moving_selection_offset.x;
1581
1582
int idx = animation->track_find_key(E->get().first, newtime, Animation::FIND_MODE_APPROX);
1583
if (idx == -1) {
1584
continue;
1585
}
1586
1587
if (selection.has(IntPair(E->get().first, idx))) {
1588
continue; // Already in selection, don't save.
1589
}
1590
1591
undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", E->get().first, newtime);
1592
AnimMoveRestore amr;
1593
1594
amr.key = animation->track_get_key_value(E->get().first, idx);
1595
amr.track = E->get().first;
1596
amr.time = newtime;
1597
1598
to_restore.push_back(amr);
1599
to_restore_handle_modes.push_back(animation->bezier_track_get_key_handle_mode(E->get().first, idx));
1600
}
1601
1602
// 3 - Move the keys (re-insert them).
1603
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
1604
real_t newpos = animation->track_get_key_time(E->get().first, E->get().second) + moving_selection_offset.x;
1605
Array key = animation->track_get_key_value(E->get().first, E->get().second);
1606
real_t h = key[0];
1607
h += moving_selection_offset.y;
1608
key[0] = h;
1609
1610
Animation::HandleMode handle_mode = animation->bezier_track_get_key_handle_mode(E->get().first, E->get().second);
1611
Animation::HandleSetMode handle_set_mode = Animation::HANDLE_SET_MODE_NONE;
1612
if (moving_inserted_key) {
1613
handle_mode = (Animation::HandleMode)editor->bezier_key_mode->get_selected_id();
1614
handle_set_mode = Animation::HANDLE_SET_MODE_AUTO;
1615
}
1616
1617
undo_redo->add_do_method(
1618
this,
1619
"_bezier_track_insert_key_at_anim",
1620
animation,
1621
E->get().first,
1622
newpos,
1623
key[0],
1624
Vector2(key[1], key[2]),
1625
Vector2(key[3], key[4]),
1626
handle_mode,
1627
handle_set_mode);
1628
}
1629
1630
// 4 - (undo) Remove inserted keys.
1631
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
1632
real_t newpos = animation->track_get_key_time(E->get().first, E->get().second) + moving_selection_offset.x;
1633
undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", E->get().first, newpos);
1634
}
1635
1636
// 5 - (undo) Reinsert keys.
1637
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
1638
real_t oldpos = animation->track_get_key_time(E->get().first, E->get().second);
1639
Array key = animation->track_get_key_value(E->get().first, E->get().second);
1640
undo_redo->add_undo_method(
1641
this,
1642
"_bezier_track_insert_key_at_anim",
1643
animation,
1644
E->get().first,
1645
oldpos,
1646
key[0],
1647
Vector2(key[1], key[2]),
1648
Vector2(key[3], key[4]),
1649
animation->bezier_track_get_key_handle_mode(E->get().first, E->get().second));
1650
}
1651
1652
// 6 - (undo) Reinsert overlapped keys.
1653
List<AnimMoveRestore>::ConstIterator restore_itr = to_restore.begin();
1654
List<Animation::HandleMode>::ConstIterator handle_itr = to_restore_handle_modes.begin();
1655
for (; restore_itr != to_restore.end() && handle_itr != to_restore_handle_modes.end(); ++restore_itr, ++handle_itr) {
1656
const AnimMoveRestore &amr = *restore_itr;
1657
Array key = amr.key;
1658
undo_redo->add_undo_method(animation.ptr(), "track_insert_key", amr.track, amr.time, amr.key, 1);
1659
undo_redo->add_undo_method(
1660
this,
1661
"_bezier_track_insert_key_at_anim",
1662
animation,
1663
amr.track,
1664
amr.time,
1665
key[0],
1666
Vector2(key[1], key[2]),
1667
Vector2(key[3], key[4]),
1668
*handle_itr);
1669
}
1670
1671
undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
1672
undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
1673
1674
// 7 - Reselect.
1675
int i = 0;
1676
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
1677
real_t oldpos = animation->track_get_key_time(E->get().first, E->get().second);
1678
real_t newpos = oldpos + moving_selection_offset.x;
1679
1680
undo_redo->add_do_method(this, "_select_at_anim", animation, E->get().first, newpos, i == 0);
1681
undo_redo->add_undo_method(this, "_select_at_anim", animation, E->get().first, oldpos, i == 0);
1682
i++;
1683
}
1684
1685
AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton();
1686
if (ape) {
1687
undo_redo->add_do_method(ape, "_animation_update_key_frame");
1688
undo_redo->add_undo_method(ape, "_animation_update_key_frame");
1689
}
1690
undo_redo->commit_action();
1691
1692
} else if (select_single_attempt != IntPair(-1, -1)) {
1693
selection.clear();
1694
set_animation_and_track(animation, select_single_attempt.first, read_only);
1695
_select_at_anim(animation, select_single_attempt.first, animation->track_get_key_time(select_single_attempt.first, select_single_attempt.second), true);
1696
}
1697
1698
moving_selection = false;
1699
moving_selection_attempt = false;
1700
moving_inserted_key = false;
1701
moving_selection_mouse_begin = Point2();
1702
queue_redraw();
1703
}
1704
}
1705
1706
if (scaling_selection && mb.is_valid() && !read_only && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
1707
if (std::abs(scaling_selection_scale.x - 1) > CMP_EPSILON || std::abs(scaling_selection_scale.y - 1) > CMP_EPSILON) {
1708
// Scale it.
1709
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
1710
undo_redo->create_action(TTR("Scale Bezier Points"));
1711
1712
List<AnimMoveRestore> to_restore;
1713
List<Animation::HandleMode> to_restore_handle_modes;
1714
// 1 - Remove the keys.
1715
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
1716
undo_redo->add_do_method(animation.ptr(), "track_remove_key", E->get().first, E->get().second);
1717
}
1718
// 2 - Remove overlapped keys.
1719
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
1720
real_t newtime = animation->track_get_key_time(E->get().first, E->get().second);
1721
newtime += -scaling_selection_offset.x + (newtime - scaling_selection_pivot.x) * (scaling_selection_scale.x - 1);
1722
1723
int idx = animation->track_find_key(E->get().first, newtime, Animation::FIND_MODE_APPROX);
1724
if (idx == -1) {
1725
continue;
1726
}
1727
1728
if (selection.has(IntPair(E->get().first, idx))) {
1729
continue; // Already in selection, don't save.
1730
}
1731
1732
undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", E->get().first, newtime);
1733
AnimMoveRestore amr;
1734
1735
amr.key = animation->track_get_key_value(E->get().first, idx);
1736
amr.track = E->get().first;
1737
amr.time = newtime;
1738
1739
to_restore.push_back(amr);
1740
to_restore_handle_modes.push_back(animation->bezier_track_get_key_handle_mode(E->get().first, idx));
1741
}
1742
1743
// 3 - Scale the keys (re-insert them).
1744
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
1745
real_t newpos = animation->track_get_key_time(E->get().first, E->get().second);
1746
newpos += -scaling_selection_offset.x + (newpos - scaling_selection_pivot.x) * (scaling_selection_scale.x - 1);
1747
1748
Array key = animation->track_get_key_value(E->get().first, E->get().second);
1749
real_t h = key[0];
1750
h += -scaling_selection_offset.y + (h - scaling_selection_pivot.y) * (scaling_selection_scale.y - 1);
1751
key[0] = h;
1752
1753
undo_redo->add_do_method(
1754
this,
1755
"_bezier_track_insert_key_at_anim",
1756
animation,
1757
E->get().first,
1758
newpos,
1759
key[0],
1760
Vector2(key[1], key[2]),
1761
Vector2(key[3], key[4]),
1762
animation->bezier_track_get_key_handle_mode(E->get().first, E->get().second));
1763
}
1764
1765
// 4 - (undo) Remove inserted keys.
1766
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
1767
real_t newpos = animation->track_get_key_time(E->get().first, E->get().second);
1768
newpos += -scaling_selection_offset.x + (newpos - scaling_selection_pivot.x) * (scaling_selection_scale.x - 1);
1769
undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", E->get().first, newpos);
1770
}
1771
1772
// 5 - (undo) Reinsert keys.
1773
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
1774
real_t oldpos = animation->track_get_key_time(E->get().first, E->get().second);
1775
Array key = animation->track_get_key_value(E->get().first, E->get().second);
1776
undo_redo->add_undo_method(
1777
this,
1778
"_bezier_track_insert_key_at_anim",
1779
animation,
1780
E->get().first,
1781
oldpos,
1782
key[0],
1783
Vector2(key[1], key[2]),
1784
Vector2(key[3], key[4]),
1785
animation->bezier_track_get_key_handle_mode(E->get().first, E->get().second));
1786
}
1787
1788
// 6 - (undo) Reinsert overlapped keys.
1789
List<AnimMoveRestore>::ConstIterator restore_itr = to_restore.begin();
1790
List<Animation::HandleMode>::ConstIterator handle_itr = to_restore_handle_modes.begin();
1791
for (; restore_itr != to_restore.end() && handle_itr != to_restore_handle_modes.end(); ++restore_itr, ++handle_itr) {
1792
const AnimMoveRestore &amr = *restore_itr;
1793
Array key = amr.key;
1794
undo_redo->add_undo_method(animation.ptr(), "track_insert_key", amr.track, amr.time, amr.key, 1);
1795
undo_redo->add_undo_method(
1796
this,
1797
"_bezier_track_insert_key_at_anim",
1798
animation,
1799
amr.track,
1800
amr.time,
1801
key[0],
1802
Vector2(key[1], key[2]),
1803
Vector2(key[3], key[4]),
1804
*handle_itr);
1805
}
1806
1807
undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
1808
undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
1809
1810
// 7 - Reselect.
1811
int i = 0;
1812
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
1813
real_t oldpos = animation->track_get_key_time(E->get().first, E->get().second);
1814
real_t newpos = animation->track_get_key_time(E->get().first, E->get().second);
1815
newpos += -scaling_selection_offset.x + (newpos - scaling_selection_pivot.x) * (scaling_selection_scale.x - 1);
1816
1817
undo_redo->add_do_method(this, "_select_at_anim", animation, E->get().first, newpos, i == 0);
1818
undo_redo->add_undo_method(this, "_select_at_anim", animation, E->get().first, oldpos, i == 0);
1819
i++;
1820
}
1821
1822
AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton();
1823
if (ape) {
1824
undo_redo->add_do_method(ape, "_animation_update_key_frame");
1825
undo_redo->add_undo_method(ape, "_animation_update_key_frame");
1826
}
1827
undo_redo->commit_action();
1828
}
1829
1830
scaling_selection = false;
1831
scaling_selection_scale = Vector2(1, 1);
1832
scaling_selection_offset = Vector2();
1833
queue_redraw();
1834
}
1835
1836
Ref<InputEventMouseMotion> mm = p_event;
1837
if (moving_selection_attempt && mm.is_valid()) {
1838
Point2 new_pos = mm->get_position();
1839
if (mm->is_alt_pressed()) { // Axis snap key move when alt is pressed
1840
if (Math::abs(new_pos.x - moving_selection_mouse_begin.x) > Math::abs(new_pos.y - moving_selection_mouse_begin.y)) {
1841
new_pos.y = moving_selection_mouse_begin.y;
1842
} else {
1843
new_pos.x = moving_selection_mouse_begin.x;
1844
}
1845
}
1846
1847
if (!moving_selection) {
1848
moving_selection = true;
1849
select_single_attempt = IntPair(-1, -1);
1850
}
1851
1852
if (!read_only) {
1853
float y = (get_size().height / 2.0 - new_pos.y) * timeline_v_zoom + timeline_v_scroll;
1854
float moving_selection_begin_time = ((moving_selection_mouse_begin.x - limit) / timeline->get_zoom_scale()) + timeline->get_value();
1855
float new_time = ((new_pos.x - limit) / timeline->get_zoom_scale()) + timeline->get_value();
1856
float moving_selection_pivot = moving_selection_from_key != -1 ? animation->track_get_key_time(moving_selection_from_track, moving_selection_from_key) : 0;
1857
float time_delta = new_time - moving_selection_begin_time;
1858
1859
float snapped_time = editor->snap_time(moving_selection_pivot + time_delta);
1860
float time_offset = 0.0;
1861
if (std::abs(moving_selection_offset.x) > CMP_EPSILON || (snapped_time > moving_selection_pivot && time_delta > CMP_EPSILON) || (snapped_time < moving_selection_pivot && time_delta < -CMP_EPSILON)) {
1862
time_offset = snapped_time - moving_selection_pivot;
1863
}
1864
1865
float moving_selection_begin_value;
1866
if (moving_selection_from_key == -1) {
1867
moving_selection_begin_value = (get_size().height / 2.0 - moving_selection_mouse_begin.y) * timeline_v_zoom + timeline_v_scroll;
1868
} else {
1869
moving_selection_begin_value = animation->bezier_track_get_key_value(moving_selection_from_track, moving_selection_from_key);
1870
}
1871
1872
float y_offset = y - moving_selection_begin_value;
1873
moving_selection_offset = Vector2(time_offset, y_offset);
1874
}
1875
1876
additional_moving_handle_lefts.clear();
1877
additional_moving_handle_rights.clear();
1878
1879
queue_redraw();
1880
}
1881
1882
if (box_selecting_attempt && mm.is_valid()) {
1883
if (!box_selecting) {
1884
box_selecting = true;
1885
box_selecting_add = mm->is_shift_pressed();
1886
}
1887
1888
box_selection_to = mm->get_position();
1889
queue_redraw();
1890
}
1891
1892
if (scaling_selection && mm.is_valid() && !read_only) {
1893
Point2 mp = mm->get_position();
1894
const int handle_length = Math::round((selection_handles_rect.size.width - selection_rect.size.width) / 4.0);
1895
Point2 rel_pos;
1896
1897
// Calculate the scale according with the distance between the mouse's position (adjusted so that the cursor appears inside the handles)
1898
// and the opposite end of the `selection_rect`.
1899
1900
if (scaling_selection_handles.x != 0) {
1901
if (scaling_selection_handles.x == 1) { // Right Handle
1902
const int handle_adjust = Math::round(mp.x - (scaling_selection_scale.x >= 0 ? selection_rect.position.x : (selection_rect.position.x + selection_rect.size.width)));
1903
mp.x -= MIN(Math::abs(handle_adjust), handle_length) * scaling_selection_handles.x * SIGN(handle_adjust);
1904
1905
if (editor->is_snap_keys_enabled()) {
1906
mp.x = editor->snap_time((mp.x - limit) / timeline->get_zoom_scale(), true) + timeline->get_value();
1907
mp.x = (mp.x - timeline->get_value()) * timeline->get_zoom_scale() + limit;
1908
}
1909
1910
rel_pos.x = scaling_selection_scale.x >= 0 ? (mp.x - selection_rect.position.x) : selection_rect.position.x + selection_rect.size.width - mp.x;
1911
} else { // Left Handle
1912
const int handle_adjust = Math::round((scaling_selection_scale.x >= 0 ? (selection_rect.position.x + selection_rect.size.width) : selection_rect.position.x) - mp.x);
1913
mp.x -= MIN(Math::abs(handle_adjust), handle_length) * scaling_selection_handles.x * SIGN(handle_adjust);
1914
1915
const float x = editor->snap_time((mp.x - limit) / timeline->get_zoom_scale(), true) + timeline->get_value();
1916
if (editor->is_snap_keys_enabled()) {
1917
mp.x = (x - timeline->get_value()) * timeline->get_zoom_scale() + limit;
1918
}
1919
1920
rel_pos.x = scaling_selection_scale.x >= 0 ? (selection_rect.position.x + selection_rect.size.width - mp.x) : (mp.x - selection_rect.position.x);
1921
scaling_selection_offset.x = scaling_selection_pivot.x - x;
1922
}
1923
1924
scaling_selection_scale.x *= rel_pos.x / selection_rect.size.width;
1925
if (scaling_selection_scale.x == 0) {
1926
scaling_selection_scale.x = CMP_EPSILON;
1927
}
1928
}
1929
1930
if (scaling_selection_handles.y != 0) {
1931
if (scaling_selection_handles.y == 1) { // Bottom Handle
1932
const int handle_adjust = Math::round(mp.y - (scaling_selection_scale.y >= 0 ? selection_rect.position.y : (selection_rect.position.y + selection_rect.size.height)));
1933
mp.y -= MIN(Math::abs(handle_adjust), handle_length) * scaling_selection_handles.y * SIGN(handle_adjust);
1934
1935
if (scaling_selection_scale.y >= 0) {
1936
rel_pos.y = mp.y - selection_rect.position.y;
1937
} else {
1938
rel_pos.y = selection_rect.position.y + selection_rect.size.height - mp.y;
1939
}
1940
} else { // Top Handle
1941
const int handle_adjust = Math::round((scaling_selection_scale.y >= 0 ? (selection_rect.position.y + selection_rect.size.height) : selection_rect.position.y) - mp.y);
1942
mp.y -= MIN(Math::abs(handle_adjust), handle_length) * scaling_selection_handles.y * SIGN(handle_adjust);
1943
1944
if (scaling_selection_scale.y >= 0) {
1945
rel_pos.y = selection_rect.position.y + selection_rect.size.height - mp.y;
1946
} else {
1947
rel_pos.y = mp.y - selection_rect.position.y;
1948
}
1949
1950
const float h = (get_size().height / 2.0 - mp.y) * timeline_v_zoom + timeline_v_scroll;
1951
scaling_selection_offset.y = scaling_selection_pivot.y - h;
1952
}
1953
1954
scaling_selection_scale.y *= rel_pos.y / selection_rect.size.height;
1955
if (scaling_selection_scale.y == 0) {
1956
scaling_selection_scale.y = CMP_EPSILON;
1957
}
1958
}
1959
1960
queue_redraw();
1961
}
1962
1963
if ((moving_handle == 1 || moving_handle == -1) && mm.is_valid()) {
1964
float y = (get_size().height / 2.0 - mm->get_position().y) * timeline_v_zoom + timeline_v_scroll;
1965
float x = editor->snap_time((mm->get_position().x - limit) / timeline->get_zoom_scale()) + timeline->get_value();
1966
1967
Vector2 key_pos = Vector2(animation->track_get_key_time(moving_handle_track, moving_handle_key), animation->bezier_track_get_key_value(moving_handle_track, moving_handle_key));
1968
1969
Vector2 moving_handle_value = Vector2(x, y) - key_pos;
1970
1971
moving_handle_left = animation->bezier_track_get_key_in_handle(moving_handle_track, moving_handle_key);
1972
moving_handle_right = animation->bezier_track_get_key_out_handle(moving_handle_track, moving_handle_key);
1973
1974
if (moving_handle == -1) {
1975
moving_handle_left = moving_handle_value;
1976
1977
Animation::HandleMode handle_mode = animation->bezier_track_get_key_handle_mode(moving_handle_track, moving_handle_key);
1978
1979
if (handle_mode == Animation::HANDLE_MODE_BALANCED) {
1980
real_t ratio = timeline->get_zoom_scale() * timeline_v_zoom;
1981
Transform2D xform;
1982
xform.set_scale(Vector2(1.0, 1.0 / ratio));
1983
1984
Vector2 vec_out = xform.xform(moving_handle_right);
1985
Vector2 vec_in = xform.xform(moving_handle_left);
1986
1987
moving_handle_right = xform.affine_inverse().xform(-vec_in.normalized() * vec_out.length());
1988
} else if (handle_mode == Animation::HANDLE_MODE_MIRRORED) {
1989
moving_handle_right = -moving_handle_left;
1990
}
1991
} else if (moving_handle == 1) {
1992
moving_handle_right = moving_handle_value;
1993
1994
Animation::HandleMode handle_mode = animation->bezier_track_get_key_handle_mode(moving_handle_track, moving_handle_key);
1995
1996
if (handle_mode == Animation::HANDLE_MODE_BALANCED) {
1997
real_t ratio = timeline->get_zoom_scale() * timeline_v_zoom;
1998
Transform2D xform;
1999
xform.set_scale(Vector2(1.0, 1.0 / ratio));
2000
2001
Vector2 vec_in = xform.xform(moving_handle_left);
2002
Vector2 vec_out = xform.xform(moving_handle_right);
2003
2004
moving_handle_left = xform.affine_inverse().xform(-vec_out.normalized() * vec_in.length());
2005
} else if (handle_mode == Animation::HANDLE_MODE_MIRRORED) {
2006
moving_handle_left = -moving_handle_right;
2007
}
2008
}
2009
queue_redraw();
2010
}
2011
2012
if ((moving_handle == -1 || moving_handle == 1) && mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
2013
if (!read_only) {
2014
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
2015
undo_redo->create_action(TTR("Move Bezier Points"));
2016
if (moving_handle == -1) {
2017
real_t ratio = timeline->get_zoom_scale() * timeline_v_zoom;
2018
undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_in_handle", moving_handle_track, moving_handle_key, moving_handle_left, ratio);
2019
undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_in_handle", moving_handle_track, moving_handle_key, animation->bezier_track_get_key_in_handle(moving_handle_track, moving_handle_key), ratio);
2020
} else if (moving_handle == 1) {
2021
real_t ratio = timeline->get_zoom_scale() * timeline_v_zoom;
2022
undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_out_handle", moving_handle_track, moving_handle_key, moving_handle_right, ratio);
2023
undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", moving_handle_track, moving_handle_key, animation->bezier_track_get_key_out_handle(moving_handle_track, moving_handle_key), ratio);
2024
}
2025
AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton();
2026
if (ape) {
2027
undo_redo->add_do_method(ape, "_animation_update_key_frame");
2028
undo_redo->add_undo_method(ape, "_animation_update_key_frame");
2029
}
2030
undo_redo->commit_action();
2031
moving_handle = 0;
2032
queue_redraw();
2033
}
2034
}
2035
}
2036
2037
bool AnimationBezierTrackEdit::_try_select_at_ui_pos(const Point2 &p_pos, bool p_aggregate, bool p_deselectable) {
2038
for (int i = 0; i < edit_points.size(); i++) {
2039
// Path 2D editing in the 3D and 2D editors works the same way. (?)
2040
if (edit_points[i].point_rect.has_point(p_pos)) {
2041
IntPair pair = IntPair(edit_points[i].track, edit_points[i].key);
2042
if (p_aggregate) {
2043
// Add to selection.
2044
if (selection.has(pair)) {
2045
if (p_deselectable) {
2046
selection.erase(pair);
2047
emit_signal(SNAME("deselect_key"), edit_points[i].key, edit_points[i].track);
2048
}
2049
} else {
2050
_select_at_anim(animation, edit_points[i].track, animation->track_get_key_time(edit_points[i].track, edit_points[i].key), false);
2051
}
2052
queue_redraw();
2053
select_single_attempt = IntPair(-1, -1);
2054
} else {
2055
if (p_deselectable) {
2056
moving_selection_attempt = true;
2057
moving_selection_from_key = pair.second;
2058
moving_selection_from_track = pair.first;
2059
moving_selection_mouse_begin = p_pos;
2060
moving_selection_offset = Vector2();
2061
moving_handle_track = pair.first;
2062
moving_handle_left = animation->bezier_track_get_key_in_handle(pair.first, pair.second);
2063
moving_handle_right = animation->bezier_track_get_key_out_handle(pair.first, pair.second);
2064
2065
if (selection.has(pair)) {
2066
moving_selection = false;
2067
} else {
2068
moving_selection = true;
2069
}
2070
select_single_attempt = pair;
2071
}
2072
2073
set_animation_and_track(animation, pair.first, read_only);
2074
if (!selection.has(pair)) {
2075
selection.clear();
2076
_select_at_anim(animation, edit_points[i].track, animation->track_get_key_time(edit_points[i].track, edit_points[i].key), true);
2077
}
2078
}
2079
return true;
2080
}
2081
}
2082
return false;
2083
}
2084
2085
void AnimationBezierTrackEdit::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) {
2086
Ref<InputEventMouseMotion> mm = p_event;
2087
if (mm.is_valid()) {
2088
if (mm->get_position().x > timeline->get_name_limit()) {
2089
timeline_v_scroll += p_scroll_vec.y * timeline_v_zoom;
2090
timeline_v_scroll = CLAMP(timeline_v_scroll, -100000, 100000);
2091
timeline->set_value(timeline->get_value() - p_scroll_vec.x / timeline->get_zoom_scale());
2092
} else {
2093
track_v_scroll += p_scroll_vec.y;
2094
if (track_v_scroll < -track_v_scroll_max) {
2095
track_v_scroll = -track_v_scroll_max;
2096
} else if (track_v_scroll > 0) {
2097
track_v_scroll = 0;
2098
}
2099
}
2100
queue_redraw();
2101
}
2102
}
2103
2104
void AnimationBezierTrackEdit::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) {
2105
const float v_zoom_orig = timeline_v_zoom;
2106
Ref<InputEventWithModifiers> iewm = p_event;
2107
if (iewm.is_valid() && iewm->is_alt_pressed()) {
2108
// Alternate zoom (doesn't affect timeline).
2109
timeline_v_zoom = CLAMP(timeline_v_zoom / p_zoom_factor, 0.000001, 100000);
2110
} else {
2111
float zoom_factor = p_zoom_factor > 1.0 ? AnimationTimelineEdit::SCROLL_ZOOM_FACTOR_IN : AnimationTimelineEdit::SCROLL_ZOOM_FACTOR_OUT;
2112
timeline->_zoom_callback(zoom_factor, p_origin, p_event);
2113
}
2114
timeline_v_scroll = timeline_v_scroll + (p_origin.y - get_size().y / 2.0) * (timeline_v_zoom - v_zoom_orig);
2115
queue_redraw();
2116
}
2117
2118
float AnimationBezierTrackEdit::get_bezier_key_value(Array p_bezier_key_array) {
2119
return p_bezier_key_array[0];
2120
}
2121
2122
void AnimationBezierTrackEdit::_menu_selected(int p_index) {
2123
int limit = timeline->get_name_limit();
2124
2125
real_t time = ((menu_insert_key.x - limit) / timeline->get_zoom_scale()) + timeline->get_value();
2126
2127
switch (p_index) {
2128
case MENU_KEY_INSERT: {
2129
if (animation->get_track_count() > 0) {
2130
if (editor->snap_keys->is_pressed() && editor->step->get_value() != 0) {
2131
time = editor->snap_time(time);
2132
}
2133
while (animation->track_find_key(selected_track, time, Animation::FIND_MODE_APPROX) != -1) {
2134
time += 0.001;
2135
}
2136
float h = (get_size().height / 2.0 - menu_insert_key.y) * timeline_v_zoom + timeline_v_scroll;
2137
Array new_point = animation->make_default_bezier_key(h);
2138
Animation::HandleMode handle_mode = (Animation::HandleMode)editor->bezier_key_mode->get_selected_id();
2139
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
2140
undo_redo->create_action(TTR("Add Bezier Point"));
2141
undo_redo->add_do_method(animation.ptr(), "track_insert_key", selected_track, time, new_point);
2142
undo_redo->add_do_method(editor, "_bezier_track_set_key_handle_mode_at_time", animation.ptr(), selected_track, time, handle_mode, Animation::HANDLE_SET_MODE_AUTO);
2143
undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
2144
undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", selected_track, time);
2145
AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton();
2146
if (ape) {
2147
undo_redo->add_do_method(ape, "_animation_update_key_frame");
2148
undo_redo->add_undo_method(ape, "_animation_update_key_frame");
2149
}
2150
undo_redo->commit_action();
2151
queue_redraw();
2152
}
2153
} break;
2154
case MENU_KEY_DUPLICATE: {
2155
duplicate_selected_keys(time, true);
2156
} break;
2157
case MENU_KEY_DELETE: {
2158
delete_selection();
2159
} break;
2160
case MENU_KEY_CUT: {
2161
copy_selected_keys(true);
2162
} break;
2163
case MENU_KEY_COPY: {
2164
copy_selected_keys(false);
2165
} break;
2166
case MENU_KEY_PASTE: {
2167
paste_keys(time, true);
2168
} break;
2169
case MENU_KEY_SET_HANDLE_FREE: {
2170
_change_selected_keys_handle_mode(Animation::HANDLE_MODE_FREE);
2171
} break;
2172
case MENU_KEY_SET_HANDLE_LINEAR: {
2173
_change_selected_keys_handle_mode(Animation::HANDLE_MODE_LINEAR);
2174
} break;
2175
case MENU_KEY_SET_HANDLE_BALANCED: {
2176
_change_selected_keys_handle_mode(Animation::HANDLE_MODE_BALANCED);
2177
} break;
2178
case MENU_KEY_SET_HANDLE_MIRRORED: {
2179
_change_selected_keys_handle_mode(Animation::HANDLE_MODE_MIRRORED);
2180
} break;
2181
case MENU_KEY_SET_HANDLE_AUTO_BALANCED: {
2182
_change_selected_keys_handle_mode(Animation::HANDLE_MODE_BALANCED, true);
2183
} break;
2184
case MENU_KEY_SET_HANDLE_AUTO_MIRRORED: {
2185
_change_selected_keys_handle_mode(Animation::HANDLE_MODE_MIRRORED, true);
2186
} break;
2187
}
2188
}
2189
2190
void AnimationBezierTrackEdit::duplicate_selected_keys(real_t p_ofs, bool p_ofs_valid) {
2191
if (selection.is_empty()) {
2192
return;
2193
}
2194
2195
real_t top_time = 1e10;
2196
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
2197
real_t t = animation->track_get_key_time(E->get().first, E->get().second);
2198
if (t < top_time) {
2199
top_time = t;
2200
}
2201
}
2202
2203
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
2204
undo_redo->create_action(TTR("Animation Duplicate Keys"));
2205
2206
List<Pair<int, real_t>> new_selection_values;
2207
2208
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
2209
real_t t = animation->track_get_key_time(E->get().first, E->get().second);
2210
real_t insert_pos = p_ofs_valid ? p_ofs : timeline->get_play_position();
2211
2212
if (p_ofs_valid) {
2213
if (editor->snap_keys->is_pressed() && editor->step->get_value() != 0) {
2214
insert_pos = editor->snap_time(insert_pos);
2215
}
2216
}
2217
2218
real_t dst_time = t + (insert_pos - top_time);
2219
int existing_idx = animation->track_find_key(E->get().first, dst_time, Animation::FIND_MODE_APPROX);
2220
2221
undo_redo->add_do_method(animation.ptr(), "track_insert_key", E->get().first, dst_time, animation->track_get_key_value(E->get().first, E->get().second), animation->track_get_key_transition(E->get().first, E->get().second));
2222
undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", E->get().first, dst_time);
2223
2224
Pair<int, real_t> p;
2225
p.first = E->get().first;
2226
p.second = dst_time;
2227
new_selection_values.push_back(p);
2228
2229
if (existing_idx != -1) {
2230
undo_redo->add_undo_method(animation.ptr(), "track_insert_key", E->get().first, dst_time, animation->track_get_key_value(E->get().first, existing_idx), animation->track_get_key_transition(E->get().first, existing_idx));
2231
}
2232
}
2233
2234
undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
2235
undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
2236
2237
// Reselect duplicated.
2238
int i = 0;
2239
for (const Pair<int, real_t> &E : new_selection_values) {
2240
undo_redo->add_do_method(this, "_select_at_anim", animation, E.first, E.second, i == 0);
2241
i++;
2242
}
2243
i = 0;
2244
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
2245
real_t time = animation->track_get_key_time(E->get().first, E->get().second);
2246
undo_redo->add_undo_method(this, "_select_at_anim", animation, E->get().first, time, i == 0);
2247
i++;
2248
}
2249
2250
AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton();
2251
if (ape) {
2252
undo_redo->add_do_method(ape, "_animation_update_key_frame");
2253
undo_redo->add_undo_method(ape, "_animation_update_key_frame");
2254
}
2255
undo_redo->add_do_method(this, "queue_redraw");
2256
undo_redo->add_undo_method(this, "queue_redraw");
2257
undo_redo->commit_action();
2258
}
2259
2260
void AnimationBezierTrackEdit::copy_selected_keys(bool p_cut) {
2261
if (selection.is_empty()) {
2262
return;
2263
}
2264
2265
float top_time = 1e10;
2266
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
2267
float t = animation->track_get_key_time(E->get().first, E->get().second);
2268
if (t < top_time) {
2269
top_time = t;
2270
}
2271
}
2272
2273
RBMap<AnimationTrackEditor::SelectedKey, AnimationTrackEditor::KeyInfo> keys;
2274
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
2275
AnimationTrackEditor::SelectedKey sk;
2276
AnimationTrackEditor::KeyInfo ki;
2277
sk.track = E->get().first;
2278
sk.key = E->get().second;
2279
ki.pos = animation->track_get_key_time(E->get().first, E->get().second);
2280
keys.insert(sk, ki);
2281
}
2282
editor->_set_key_clipboard(selected_track, top_time, keys);
2283
2284
if (p_cut) {
2285
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
2286
undo_redo->create_action(TTR("Animation Cut Keys"), UndoRedo::MERGE_DISABLE, animation.ptr());
2287
undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
2288
undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
2289
int i = 0;
2290
for (RBMap<AnimationTrackEditor::SelectedKey, AnimationTrackEditor::KeyInfo>::Element *E = keys.back(); E; E = E->prev()) {
2291
int track_idx = E->key().track;
2292
int key_idx = E->key().key;
2293
float time = E->value().pos;
2294
undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", track_idx, time);
2295
undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track_idx, time, animation->track_get_key_value(track_idx, key_idx), animation->track_get_key_transition(track_idx, key_idx));
2296
undo_redo->add_undo_method(this, "_select_at_anim", animation, track_idx, time, i == 0);
2297
i++;
2298
}
2299
i = 0;
2300
for (RBMap<AnimationTrackEditor::SelectedKey, AnimationTrackEditor::KeyInfo>::Element *E = keys.back(); E; E = E->prev()) {
2301
undo_redo->add_undo_method(this, "_select_at_anim", animation, E->key().track, E->value().pos, i == 0);
2302
i++;
2303
}
2304
2305
AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton();
2306
if (ape) {
2307
undo_redo->add_do_method(ape, "_animation_update_key_frame");
2308
undo_redo->add_undo_method(ape, "_animation_update_key_frame");
2309
}
2310
undo_redo->add_do_method(this, "queue_redraw");
2311
undo_redo->add_undo_method(this, "queue_redraw");
2312
2313
undo_redo->commit_action();
2314
}
2315
}
2316
2317
void AnimationBezierTrackEdit::paste_keys(real_t p_ofs, bool p_ofs_valid) {
2318
if (editor->is_key_clipboard_active() && animation.is_valid() && (selected_track >= 0 && selected_track < animation->get_track_count())) {
2319
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
2320
undo_redo->create_action(TTR("Animation Paste Keys"));
2321
2322
bool same_track = true;
2323
bool all_compatible = true;
2324
2325
for (int i = 0; i < editor->key_clipboard.keys.size(); i++) {
2326
const AnimationTrackEditor::KeyClipboard::Key key = editor->key_clipboard.keys[i];
2327
2328
if (key.track != 0) {
2329
same_track = false;
2330
break;
2331
}
2332
2333
if (!editor->_is_track_compatible(selected_track, key.value.get_type(), key.track_type)) {
2334
all_compatible = false;
2335
break;
2336
}
2337
}
2338
2339
ERR_FAIL_COND_MSG(!all_compatible, "Paste failed: Not all animation keys were compatible with their target tracks");
2340
if (!same_track) {
2341
WARN_PRINT("Pasted animation keys from multiple tracks into single Bezier track");
2342
}
2343
2344
List<Pair<int, float>> new_selection_values;
2345
for (int i = 0; i < editor->key_clipboard.keys.size(); i++) {
2346
const AnimationTrackEditor::KeyClipboard::Key key = editor->key_clipboard.keys[i];
2347
2348
float insert_pos = p_ofs_valid ? p_ofs : timeline->get_play_position();
2349
if (p_ofs_valid) {
2350
if (editor->snap_keys->is_pressed() && editor->step->get_value() != 0) {
2351
insert_pos = editor->snap_time(insert_pos);
2352
}
2353
}
2354
float dst_time = key.time + insert_pos;
2355
2356
int existing_idx = animation->track_find_key(selected_track, dst_time, Animation::FIND_MODE_APPROX);
2357
2358
Variant value = key.value;
2359
if (key.track_type != Animation::TYPE_BEZIER) {
2360
value = animation->make_default_bezier_key(key.value);
2361
}
2362
2363
undo_redo->add_do_method(animation.ptr(), "track_insert_key", selected_track, dst_time, value, key.transition);
2364
undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", selected_track, dst_time);
2365
2366
Pair<int, float> p;
2367
p.first = selected_track;
2368
p.second = dst_time;
2369
new_selection_values.push_back(p);
2370
2371
if (existing_idx != -1) {
2372
undo_redo->add_undo_method(animation.ptr(), "track_insert_key", selected_track, dst_time, animation->track_get_key_value(selected_track, existing_idx), animation->track_get_key_transition(selected_track, existing_idx));
2373
}
2374
}
2375
2376
undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
2377
undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
2378
2379
// Reselect pasted.
2380
int i = 0;
2381
for (const Pair<int, float> &E : new_selection_values) {
2382
undo_redo->add_do_method(this, "_select_at_anim", animation, E.first, E.second, i == 0);
2383
i++;
2384
}
2385
i = 0;
2386
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
2387
undo_redo->add_undo_method(this, "_select_at_anim", animation, E->get().first, animation->track_get_key_time(E->get().first, E->get().second), i == 0);
2388
i++;
2389
}
2390
2391
AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton();
2392
if (ape) {
2393
undo_redo->add_do_method(ape, "_animation_update_key_frame");
2394
undo_redo->add_undo_method(ape, "_animation_update_key_frame");
2395
}
2396
undo_redo->add_do_method(this, "queue_redraw");
2397
undo_redo->add_undo_method(this, "queue_redraw");
2398
2399
undo_redo->commit_action();
2400
}
2401
}
2402
2403
void AnimationBezierTrackEdit::delete_selection() {
2404
if (selection.size()) {
2405
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
2406
undo_redo->create_action(TTR("Animation Delete Keys"));
2407
2408
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
2409
undo_redo->add_do_method(animation.ptr(), "track_remove_key", E->get().first, E->get().second);
2410
undo_redo->add_undo_method(animation.ptr(), "track_insert_key", E->get().first, animation->track_get_key_time(E->get().first, E->get().second), animation->track_get_key_value(E->get().first, E->get().second), 1);
2411
}
2412
undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
2413
undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
2414
AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton();
2415
if (ape) {
2416
undo_redo->add_do_method(ape, "_animation_update_key_frame");
2417
undo_redo->add_undo_method(ape, "_animation_update_key_frame");
2418
}
2419
undo_redo->commit_action();
2420
2421
//selection.clear();
2422
}
2423
}
2424
2425
void AnimationBezierTrackEdit::_bezier_track_insert_key_at_anim(const Ref<Animation> &p_anim, int p_track, double p_time, real_t p_value, const Vector2 &p_in_handle, const Vector2 &p_out_handle, const Animation::HandleMode p_handle_mode, Animation::HandleSetMode p_handle_set_mode) {
2426
int idx = p_anim->bezier_track_insert_key(p_track, p_time, p_value, p_in_handle, p_out_handle);
2427
p_anim->bezier_track_set_key_handle_mode(p_track, idx, p_handle_mode, p_handle_set_mode);
2428
}
2429
2430
void AnimationBezierTrackEdit::_bind_methods() {
2431
ClassDB::bind_method(D_METHOD("_clear_selection"), &AnimationBezierTrackEdit::_clear_selection);
2432
ClassDB::bind_method(D_METHOD("_clear_selection_for_anim"), &AnimationBezierTrackEdit::_clear_selection_for_anim);
2433
ClassDB::bind_method(D_METHOD("_select_at_anim"), &AnimationBezierTrackEdit::_select_at_anim);
2434
ClassDB::bind_method(D_METHOD("_update_hidden_tracks_after"), &AnimationBezierTrackEdit::_update_hidden_tracks_after);
2435
ClassDB::bind_method(D_METHOD("_update_locked_tracks_after"), &AnimationBezierTrackEdit::_update_locked_tracks_after);
2436
ClassDB::bind_method(D_METHOD("_bezier_track_insert_key_at_anim"), &AnimationBezierTrackEdit::_bezier_track_insert_key_at_anim, DEFVAL(Animation::HANDLE_SET_MODE_NONE));
2437
2438
ADD_SIGNAL(MethodInfo("select_key", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::BOOL, "single"), PropertyInfo(Variant::INT, "track")));
2439
ADD_SIGNAL(MethodInfo("deselect_key", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::INT, "track")));
2440
ADD_SIGNAL(MethodInfo("clear_selection"));
2441
ADD_SIGNAL(MethodInfo("timeline_changed", PropertyInfo(Variant::FLOAT, "position"), PropertyInfo(Variant::BOOL, "timeline_only")));
2442
}
2443
2444
AnimationBezierTrackEdit::AnimationBezierTrackEdit() {
2445
panner.instantiate();
2446
panner->set_callbacks(callable_mp(this, &AnimationBezierTrackEdit::_pan_callback), callable_mp(this, &AnimationBezierTrackEdit::_zoom_callback));
2447
2448
play_position = memnew(Control);
2449
play_position->set_mouse_filter(MOUSE_FILTER_PASS);
2450
add_child(play_position);
2451
play_position->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
2452
play_position->connect(SceneStringName(draw), callable_mp(this, &AnimationBezierTrackEdit::_play_position_draw));
2453
set_focus_mode(FOCUS_CLICK);
2454
2455
set_clip_contents(true);
2456
2457
ED_SHORTCUT("animation_bezier_editor/focus", TTRC("Focus"), Key::F);
2458
ED_SHORTCUT("animation_bezier_editor/select_all_keys", TTRC("Select All Keys"), KeyModifierMask::CMD_OR_CTRL | Key::A);
2459
ED_SHORTCUT("animation_bezier_editor/deselect_all_keys", TTRC("Deselect All Keys"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::A);
2460
2461
menu = memnew(PopupMenu);
2462
add_child(menu);
2463
menu->connect(SceneStringName(id_pressed), callable_mp(this, &AnimationBezierTrackEdit::_menu_selected));
2464
}
2465
2466