Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/animation/animation_track_editor_plugins.cpp
21117 views
1
/**************************************************************************/
2
/* animation_track_editor_plugins.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_track_editor_plugins.h"
32
33
#include "editor/audio/audio_stream_preview.h"
34
#include "editor/editor_string_names.h"
35
#include "editor/editor_undo_redo_manager.h"
36
#include "editor/inspector/editor_resource_preview.h"
37
#include "editor/themes/editor_scale.h"
38
#include "scene/2d/animated_sprite_2d.h"
39
#include "scene/2d/sprite_2d.h"
40
#include "scene/3d/sprite_3d.h"
41
#include "scene/animation/animation_player.h"
42
#include "servers/audio/audio_stream.h"
43
44
/// BOOL ///
45
int AnimationTrackEditBool::get_key_height() const {
46
Ref<Texture2D> checked = get_theme_icon(SNAME("checked"), SNAME("CheckBox"));
47
return checked->get_height();
48
}
49
50
Rect2 AnimationTrackEditBool::get_key_rect(int p_index, float p_pixels_sec) {
51
Ref<Texture2D> checked = get_theme_icon(SNAME("checked"), SNAME("CheckBox"));
52
return Rect2(-checked->get_width() / 2, 0, checked->get_width(), get_size().height);
53
}
54
55
bool AnimationTrackEditBool::is_key_selectable_by_distance() const {
56
return false;
57
}
58
59
void AnimationTrackEditBool::draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) {
60
bool checked = get_animation()->track_get_key_value(get_track(), p_index);
61
Ref<Texture2D> icon = get_theme_icon(checked ? "checked" : "unchecked", "CheckBox");
62
63
Vector2 ofs(p_x - icon->get_width() / 2, int(get_size().height - icon->get_height()) / 2);
64
65
if (ofs.x + icon->get_width() / 2 < p_clip_left) {
66
return;
67
}
68
69
if (ofs.x + icon->get_width() / 2 > p_clip_right) {
70
return;
71
}
72
73
draw_texture(icon, ofs);
74
75
if (p_selected) {
76
Color color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
77
draw_rect_clipped(Rect2(ofs, icon->get_size()), color, false);
78
}
79
}
80
81
/// COLOR ///
82
83
int AnimationTrackEditColor::get_key_height() const {
84
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
85
int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
86
return font->get_height(font_size) * 0.8;
87
}
88
89
Rect2 AnimationTrackEditColor::get_key_rect(int p_index, float p_pixels_sec) {
90
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
91
int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
92
int fh = font->get_height(font_size) * 0.8;
93
return Rect2(-fh / 2, 0, fh, get_size().height);
94
}
95
96
bool AnimationTrackEditColor::is_key_selectable_by_distance() const {
97
return false;
98
}
99
100
void AnimationTrackEditColor::draw_key_link(int p_index_from, int p_index_to, float p_pixels_sec, int p_x, int p_next_x, int p_clip_left, int p_clip_right) {
101
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
102
int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
103
int fh = (font->get_height(font_size) * 0.8);
104
105
fh /= 3;
106
107
int x_from = p_x + fh / 2 - 1;
108
int x_to = p_next_x - fh / 2 + 1;
109
x_from = MAX(x_from, p_clip_left);
110
x_to = MIN(x_to, p_clip_right);
111
112
int y_from = (get_size().height - fh) / 2;
113
114
if (x_from > p_clip_right || x_to < p_clip_left) {
115
return;
116
}
117
118
Vector<Color> color_samples;
119
color_samples.append(get_animation()->track_get_key_value(get_track(), p_index_from));
120
121
if (get_animation()->track_get_type(get_track()) == Animation::TYPE_VALUE) {
122
if (get_animation()->track_get_interpolation_type(get_track()) != Animation::INTERPOLATION_NEAREST &&
123
(get_animation()->value_track_get_update_mode(get_track()) == Animation::UPDATE_CONTINUOUS ||
124
get_animation()->value_track_get_update_mode(get_track()) == Animation::UPDATE_CAPTURE) &&
125
!Math::is_zero_approx(get_animation()->track_get_key_transition(get_track(), p_index_from))) {
126
float start_time = get_animation()->track_get_key_time(get_track(), p_index_from);
127
float end_time = get_animation()->track_get_key_time(get_track(), p_index_to);
128
129
Color color_next = get_animation()->value_track_interpolate(get_track(), end_time);
130
131
if (!color_samples[0].is_equal_approx(color_next)) {
132
color_samples.resize(1 + (x_to - x_from) / 64); // Make a color sample every 64 px.
133
for (int i = 1; i < color_samples.size(); i++) {
134
float j = i;
135
color_samples.write[i] = get_animation()->value_track_interpolate(
136
get_track(),
137
Math::lerp(start_time, end_time, j / color_samples.size()));
138
}
139
}
140
color_samples.append(color_next);
141
} else {
142
color_samples.append(color_samples[0]);
143
}
144
} else {
145
color_samples.append(get_animation()->track_get_key_value(get_track(), p_index_to));
146
}
147
148
for (int i = 0; i < color_samples.size() - 1; i++) {
149
Vector<Vector2> points = {
150
Vector2(Math::lerp(x_from, x_to, float(i) / (color_samples.size() - 1)), y_from),
151
Vector2(Math::lerp(x_from, x_to, float(i + 1) / (color_samples.size() - 1)), y_from),
152
Vector2(Math::lerp(x_from, x_to, float(i + 1) / (color_samples.size() - 1)), y_from + fh),
153
Vector2(Math::lerp(x_from, x_to, float(i) / (color_samples.size() - 1)), y_from + fh)
154
};
155
156
Vector<Color> colors = {
157
color_samples[i],
158
color_samples[i + 1],
159
color_samples[i + 1],
160
color_samples[i]
161
};
162
163
draw_primitive(points, colors, Vector<Vector2>());
164
}
165
}
166
167
void AnimationTrackEditColor::draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) {
168
Color color = get_animation()->track_get_key_value(get_track(), p_index);
169
170
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
171
int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
172
int fh = font->get_height(font_size) * 0.8;
173
174
Rect2 rect(Vector2(p_x - fh / 2, int(get_size().height - fh) / 2), Size2(fh, fh));
175
176
draw_rect_clipped(Rect2(rect.position, rect.size / 2), Color(0.4, 0.4, 0.4));
177
draw_rect_clipped(Rect2(rect.position + rect.size / 2, rect.size / 2), Color(0.4, 0.4, 0.4));
178
draw_rect_clipped(Rect2(rect.position + Vector2(rect.size.x / 2, 0), rect.size / 2), Color(0.6, 0.6, 0.6));
179
draw_rect_clipped(Rect2(rect.position + Vector2(0, rect.size.y / 2), rect.size / 2), Color(0.6, 0.6, 0.6));
180
draw_rect_clipped(rect, color);
181
182
if (p_selected) {
183
Color accent = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
184
draw_rect_clipped(rect, accent, false);
185
}
186
}
187
188
/// AUDIO ///
189
190
void AnimationTrackEditAudio::_preview_changed(ObjectID p_which) {
191
Object *object = ObjectDB::get_instance(id);
192
193
if (!object) {
194
return;
195
}
196
197
Ref<AudioStream> stream = object->call("get_stream");
198
199
if (stream.is_valid() && stream->get_instance_id() == p_which) {
200
queue_redraw();
201
}
202
}
203
204
int AnimationTrackEditAudio::get_key_height() const {
205
if (!ObjectDB::get_instance(id)) {
206
return AnimationTrackEdit::get_key_height();
207
}
208
209
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
210
int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
211
return int(font->get_height(font_size) * 1.5);
212
}
213
214
Rect2 AnimationTrackEditAudio::get_key_rect(int p_index, float p_pixels_sec) {
215
Object *object = ObjectDB::get_instance(id);
216
217
if (!object) {
218
return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);
219
}
220
221
Ref<AudioStream> stream = object->call("get_stream");
222
223
if (stream.is_null()) {
224
return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);
225
}
226
227
bool play = get_animation()->track_get_key_value(get_track(), p_index);
228
if (play) {
229
float len = stream->get_length();
230
231
if (len == 0) {
232
Ref<AudioStreamPreview> preview = AudioStreamPreviewGenerator::get_singleton()->generate_preview(stream);
233
len = preview->get_length();
234
}
235
236
if (get_animation()->track_get_key_count(get_track()) > p_index + 1) {
237
len = MIN(len, get_animation()->track_get_key_time(get_track(), p_index + 1) - get_animation()->track_get_key_time(get_track(), p_index));
238
}
239
240
return Rect2(0, 0, len * p_pixels_sec, get_size().height);
241
} else {
242
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
243
int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
244
int fh = font->get_height(font_size) * 0.8;
245
return Rect2(0, 0, fh, get_size().height);
246
}
247
}
248
249
bool AnimationTrackEditAudio::is_key_selectable_by_distance() const {
250
return false;
251
}
252
253
void AnimationTrackEditAudio::draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) {
254
Object *object = ObjectDB::get_instance(id);
255
256
if (!object) {
257
AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);
258
return;
259
}
260
261
Ref<AudioStream> stream = object->call("get_stream");
262
263
if (stream.is_null()) {
264
AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);
265
return;
266
}
267
268
bool play = get_animation()->track_get_key_value(get_track(), p_index);
269
if (play) {
270
float len = stream->get_length();
271
272
Ref<AudioStreamPreview> preview = AudioStreamPreviewGenerator::get_singleton()->generate_preview(stream);
273
274
float preview_len = preview->get_length();
275
276
if (len == 0) {
277
len = preview_len;
278
}
279
280
int pixel_len = len * p_pixels_sec;
281
282
int pixel_begin = p_x;
283
int pixel_end = p_x + pixel_len;
284
285
if (pixel_end < p_clip_left) {
286
return;
287
}
288
289
if (pixel_begin > p_clip_right) {
290
return;
291
}
292
293
int from_x = MAX(pixel_begin, p_clip_left);
294
int to_x = MIN(pixel_end, p_clip_right);
295
296
if (get_animation()->track_get_key_count(get_track()) > p_index + 1) {
297
float limit = MIN(len, get_animation()->track_get_key_time(get_track(), p_index + 1) - get_animation()->track_get_key_time(get_track(), p_index));
298
int limit_x = pixel_begin + limit * p_pixels_sec;
299
to_x = MIN(limit_x, to_x);
300
}
301
302
if (to_x <= from_x) {
303
return;
304
}
305
306
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
307
int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
308
float fh = int(font->get_height(font_size) * 1.5);
309
Rect2 rect = Rect2(from_x, (get_size().height - fh) / 2, to_x - from_x, fh);
310
draw_rect(rect, Color(0.25, 0.25, 0.25));
311
312
Vector<Vector2> points;
313
points.resize((to_x - from_x) * 2);
314
preview_len = preview->get_length();
315
316
for (int i = from_x; i < to_x; i++) {
317
float ofs = (i - pixel_begin) * preview_len / pixel_len;
318
float ofs_n = ((i + 1) - pixel_begin) * preview_len / pixel_len;
319
float max = preview->get_max(ofs, ofs_n) * 0.5 + 0.5;
320
float min = preview->get_min(ofs, ofs_n) * 0.5 + 0.5;
321
322
int idx = i - from_x;
323
points.write[idx * 2 + 0] = Vector2(i, rect.position.y + min * rect.size.y);
324
points.write[idx * 2 + 1] = Vector2(i, rect.position.y + max * rect.size.y);
325
}
326
327
Vector<Color> colors = { Color(0.75, 0.75, 0.75) };
328
329
RS::get_singleton()->canvas_item_add_multiline(get_canvas_item(), points, colors);
330
331
if (p_selected) {
332
Color accent = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
333
draw_rect(rect, accent, false);
334
}
335
} else {
336
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
337
int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
338
int fh = font->get_height(font_size) * 0.8;
339
Rect2 rect(Vector2(p_x, int(get_size().height - fh) / 2), Size2(fh, fh));
340
341
Color color = get_theme_color(SceneStringName(font_color), SNAME("Label"));
342
draw_rect_clipped(rect, color);
343
344
if (p_selected) {
345
Color accent = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
346
draw_rect_clipped(rect, accent, false);
347
}
348
}
349
}
350
351
void AnimationTrackEditAudio::set_node(Object *p_object) {
352
id = p_object->get_instance_id();
353
}
354
355
AnimationTrackEditAudio::AnimationTrackEditAudio() {
356
AudioStreamPreviewGenerator::get_singleton()->connect("preview_updated", callable_mp(this, &AnimationTrackEditAudio::_preview_changed));
357
}
358
359
/// SPRITE FRAME / FRAME_COORDS ///
360
361
int AnimationTrackEditSpriteFrame::get_key_height() const {
362
if (!ObjectDB::get_instance(id)) {
363
return AnimationTrackEdit::get_key_height();
364
}
365
366
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
367
int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
368
return int(font->get_height(font_size) * 2);
369
}
370
371
Rect2 AnimationTrackEditSpriteFrame::get_key_rect(int p_index, float p_pixels_sec) {
372
Object *object = ObjectDB::get_instance(id);
373
374
if (!object) {
375
return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);
376
}
377
378
Size2 size;
379
380
if (Object::cast_to<Sprite2D>(object) || Object::cast_to<Sprite3D>(object)) {
381
Ref<Texture2D> texture = object->call("get_texture");
382
if (texture.is_null()) {
383
return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);
384
}
385
386
size = texture->get_size();
387
388
if (bool(object->call("is_region_enabled"))) {
389
size = Rect2(object->call("get_region_rect")).size;
390
}
391
392
int hframes = object->call("get_hframes");
393
int vframes = object->call("get_vframes");
394
395
if (hframes > 1) {
396
size.x /= hframes;
397
}
398
if (vframes > 1) {
399
size.y /= vframes;
400
}
401
} else if (Object::cast_to<AnimatedSprite2D>(object) || Object::cast_to<AnimatedSprite3D>(object)) {
402
Ref<SpriteFrames> sf = object->call("get_sprite_frames");
403
if (sf.is_null()) {
404
return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);
405
}
406
407
List<StringName> animations;
408
sf->get_animation_list(&animations);
409
410
int frame = get_animation()->track_get_key_value(get_track(), p_index);
411
String animation_name;
412
if (animations.size() == 1) {
413
animation_name = animations.front()->get();
414
} else {
415
// Go through other track to find if animation is set
416
String animation_path = String(get_animation()->track_get_path(get_track()));
417
animation_path = animation_path.replace(":frame", ":animation");
418
int animation_track = get_animation()->find_track(animation_path, get_animation()->track_get_type(get_track()));
419
420
if (animation_track != -1) {
421
float track_time = get_animation()->track_get_key_time(get_track(), p_index);
422
int animation_index = get_animation()->track_find_key(animation_track, track_time);
423
if (animation_index != -1) {
424
animation_name = get_animation()->track_get_key_value(animation_track, animation_index);
425
}
426
}
427
}
428
429
Ref<Texture2D> texture;
430
if (!animation_name.is_empty()) {
431
texture = sf->get_frame_texture(animation_name, frame);
432
}
433
if (texture.is_null()) {
434
return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);
435
}
436
437
size = texture->get_size();
438
}
439
440
size = size.floor();
441
442
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
443
int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
444
int height = int(font->get_height(font_size) * 2);
445
int width = height * size.width / size.height;
446
447
return Rect2(0, 0, width, get_size().height);
448
}
449
450
bool AnimationTrackEditSpriteFrame::is_key_selectable_by_distance() const {
451
return false;
452
}
453
454
void AnimationTrackEditSpriteFrame::draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) {
455
Object *object = ObjectDB::get_instance(id);
456
457
if (!object) {
458
AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);
459
return;
460
}
461
462
Ref<Texture2D> texture;
463
Rect2 region;
464
465
if (Object::cast_to<Sprite2D>(object) || Object::cast_to<Sprite3D>(object)) {
466
texture = object->call("get_texture");
467
if (texture.is_null()) {
468
AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);
469
return;
470
}
471
472
int hframes = object->call("get_hframes");
473
int vframes = object->call("get_vframes");
474
475
Vector2 coords;
476
if (is_coords) {
477
coords = get_animation()->track_get_key_value(get_track(), p_index);
478
} else {
479
int frame = get_animation()->track_get_key_value(get_track(), p_index);
480
coords.x = frame % hframes;
481
coords.y = frame / hframes;
482
}
483
484
region.size = texture->get_size();
485
486
if (bool(object->call("is_region_enabled"))) {
487
region = Rect2(object->call("get_region_rect"));
488
}
489
490
if (hframes > 1) {
491
region.size.x /= hframes;
492
}
493
if (vframes > 1) {
494
region.size.y /= vframes;
495
}
496
497
region.position.x += region.size.x * coords.x;
498
region.position.y += region.size.y * coords.y;
499
500
} else if (Object::cast_to<AnimatedSprite2D>(object) || Object::cast_to<AnimatedSprite3D>(object)) {
501
Ref<SpriteFrames> sf = object->call("get_sprite_frames");
502
if (sf.is_null()) {
503
AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);
504
return;
505
}
506
507
List<StringName> animations;
508
sf->get_animation_list(&animations);
509
510
int frame = get_animation()->track_get_key_value(get_track(), p_index);
511
String animation_name;
512
if (animations.size() == 1) {
513
animation_name = animations.front()->get();
514
} else {
515
// Go through other track to find if animation is set
516
String animation_path = String(get_animation()->track_get_path(get_track()));
517
animation_path = animation_path.replace(":frame", ":animation");
518
int animation_track = get_animation()->find_track(animation_path, get_animation()->track_get_type(get_track()));
519
520
if (animation_track != -1) {
521
float track_time = get_animation()->track_get_key_time(get_track(), p_index);
522
int animation_index = get_animation()->track_find_key(animation_track, track_time);
523
if (animation_index != -1) {
524
animation_name = get_animation()->track_get_key_value(animation_track, animation_index);
525
}
526
}
527
}
528
529
if (!animation_name.is_empty()) {
530
texture = sf->get_frame_texture(animation_name, frame);
531
}
532
533
if (texture.is_null()) {
534
AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);
535
return;
536
}
537
538
region.size = texture->get_size();
539
}
540
541
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
542
int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
543
int height = int(font->get_height(font_size) * 2);
544
545
int width = height * region.size.width / region.size.height;
546
547
Rect2 rect(p_x, int(get_size().height - height) / 2, width, height);
548
549
if (rect.position.x + rect.size.x < p_clip_left) {
550
return;
551
}
552
553
if (rect.position.x > p_clip_right) {
554
return;
555
}
556
557
Color accent = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
558
Color bg = accent;
559
bg.a = 0.15;
560
561
draw_rect_clipped(rect, bg);
562
563
draw_texture_region_clipped(texture, rect, region);
564
565
if (p_selected) {
566
draw_rect_clipped(rect, accent, false);
567
}
568
}
569
570
void AnimationTrackEditSpriteFrame::set_node(Object *p_object) {
571
id = p_object->get_instance_id();
572
}
573
574
void AnimationTrackEditSpriteFrame::set_as_coords() {
575
is_coords = true;
576
}
577
578
/// SUB ANIMATION ///
579
580
int AnimationTrackEditSubAnim::get_key_height() const {
581
if (!ObjectDB::get_instance(id)) {
582
return AnimationTrackEdit::get_key_height();
583
}
584
585
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
586
int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
587
return int(font->get_height(font_size) * 1.5);
588
}
589
590
Rect2 AnimationTrackEditSubAnim::get_key_rect(int p_index, float p_pixels_sec) {
591
Object *object = ObjectDB::get_instance(id);
592
593
if (!object) {
594
return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);
595
}
596
597
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(object);
598
599
if (!ap) {
600
return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);
601
}
602
603
String anim = get_animation()->track_get_key_value(get_track(), p_index);
604
605
if (anim != "[stop]" && ap->has_animation(anim)) {
606
float len = ap->get_animation(anim)->get_length();
607
608
if (get_animation()->track_get_key_count(get_track()) > p_index + 1) {
609
len = MIN(len, get_animation()->track_get_key_time(get_track(), p_index + 1) - get_animation()->track_get_key_time(get_track(), p_index));
610
}
611
612
return Rect2(0, 0, len * p_pixels_sec, get_size().height);
613
} else {
614
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
615
int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
616
int fh = font->get_height(font_size) * 0.8;
617
return Rect2(0, 0, fh, get_size().height);
618
}
619
}
620
621
bool AnimationTrackEditSubAnim::is_key_selectable_by_distance() const {
622
return false;
623
}
624
625
void AnimationTrackEditSubAnim::draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) {
626
Object *object = ObjectDB::get_instance(id);
627
628
if (!object) {
629
AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);
630
return;
631
}
632
633
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(object);
634
635
if (!ap) {
636
AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);
637
return;
638
}
639
640
String anim = get_animation()->track_get_key_value(get_track(), p_index);
641
642
if (anim != "[stop]" && ap->has_animation(anim)) {
643
float len = ap->get_animation(anim)->get_length();
644
645
if (get_animation()->track_get_key_count(get_track()) > p_index + 1) {
646
len = MIN(len, get_animation()->track_get_key_time(get_track(), p_index + 1) - get_animation()->track_get_key_time(get_track(), p_index));
647
}
648
649
int pixel_len = len * p_pixels_sec;
650
651
int pixel_begin = p_x;
652
int pixel_end = p_x + pixel_len;
653
654
if (pixel_end < p_clip_left) {
655
return;
656
}
657
658
if (pixel_begin > p_clip_right) {
659
return;
660
}
661
662
int from_x = MAX(pixel_begin, p_clip_left);
663
int to_x = MIN(pixel_end, p_clip_right);
664
665
if (to_x <= from_x) {
666
return;
667
}
668
669
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
670
int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
671
int fh = font->get_height(font_size) * 1.5;
672
673
Rect2 rect(from_x, int(get_size().height - fh) / 2, to_x - from_x, fh);
674
675
Color color = get_theme_color(SceneStringName(font_color), SNAME("Label"));
676
Color bg = color;
677
bg.r = 1 - color.r;
678
bg.g = 1 - color.g;
679
bg.b = 1 - color.b;
680
draw_rect(rect, bg);
681
682
Vector<Vector2> points;
683
Vector<Color> colors = { color };
684
{
685
Ref<Animation> ap_anim = ap->get_animation(anim);
686
687
for (int i = 0; i < ap_anim->get_track_count(); i++) {
688
float h = (rect.size.height - 2) / ap_anim->get_track_count();
689
690
int y = 2 + h * i + h / 2;
691
692
for (int j = 0; j < ap_anim->track_get_key_count(i); j++) {
693
float ofs = ap_anim->track_get_key_time(i, j);
694
int x = p_x + ofs * p_pixels_sec + 2;
695
696
if (x < from_x || x >= (to_x - 4)) {
697
continue;
698
}
699
700
points.push_back(Point2(x, y));
701
points.push_back(Point2(x + 1, y));
702
}
703
}
704
}
705
706
if (points.size() > 2) {
707
RS::get_singleton()->canvas_item_add_multiline(get_canvas_item(), points, colors);
708
}
709
710
int limit = to_x - from_x - 4;
711
if (limit > 0) {
712
draw_string(font, Point2(from_x + 2, int(get_size().height - font->get_height(font_size)) / 2 + font->get_ascent(font_size)), anim, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color);
713
}
714
715
if (p_selected) {
716
Color accent = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
717
draw_rect(rect, accent, false);
718
}
719
} else {
720
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
721
int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
722
int fh = font->get_height(font_size) * 0.8;
723
Rect2 rect(Vector2(p_x, int(get_size().height - fh) / 2), Size2(fh, fh));
724
725
Color color = get_theme_color(SceneStringName(font_color), SNAME("Label"));
726
draw_rect_clipped(rect, color);
727
728
if (p_selected) {
729
Color accent = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
730
draw_rect_clipped(rect, accent, false);
731
}
732
}
733
}
734
735
void AnimationTrackEditSubAnim::set_node(Object *p_object) {
736
id = p_object->get_instance_id();
737
}
738
739
//// VOLUME DB ////
740
741
int AnimationTrackEditVolumeDB::get_key_height() const {
742
Ref<Texture2D> volume_texture = get_editor_theme_icon(SNAME("ColorTrackVu"));
743
return volume_texture->get_height() * 1.2;
744
}
745
746
void AnimationTrackEditVolumeDB::draw_bg(int p_clip_left, int p_clip_right) {
747
Ref<Texture2D> volume_texture = get_editor_theme_icon(SNAME("ColorTrackVu"));
748
int tex_h = volume_texture->get_height();
749
750
int y_from = (get_size().height - tex_h) / 2;
751
int y_size = tex_h;
752
753
Color color(1, 1, 1, 0.3);
754
draw_texture_rect(volume_texture, Rect2(p_clip_left, y_from, p_clip_right - p_clip_left, y_from + y_size), false, color);
755
}
756
757
void AnimationTrackEditVolumeDB::draw_fg(int p_clip_left, int p_clip_right) {
758
Ref<Texture2D> volume_texture = get_editor_theme_icon(SNAME("ColorTrackVu"));
759
int tex_h = volume_texture->get_height();
760
int y_from = (get_size().height - tex_h) / 2;
761
int db0 = y_from + (24 / 80.0) * tex_h;
762
763
draw_line(Vector2(p_clip_left, db0), Vector2(p_clip_right, db0), Color(1, 1, 1, 0.3));
764
}
765
766
void AnimationTrackEditVolumeDB::draw_key_link(int p_index_from, int p_index_to, float p_pixels_sec, int p_x, int p_next_x, int p_clip_left, int p_clip_right) {
767
if (p_x > p_clip_right || p_next_x < p_clip_left) {
768
return;
769
}
770
771
float db = get_animation()->track_get_key_value(get_track(), p_index_from);
772
float db_n = get_animation()->track_get_key_value(get_track(), p_index_to);
773
774
db = CLAMP(db, -60, 24);
775
db_n = CLAMP(db_n, -60, 24);
776
777
float h = 1.0 - ((db + 60) / 84.0);
778
float h_n = 1.0 - ((db_n + 60) / 84.0);
779
780
int from_x = p_x;
781
int to_x = p_next_x;
782
783
if (from_x < p_clip_left) {
784
h = Math::lerp(h, h_n, float(p_clip_left - from_x) / float(to_x - from_x));
785
from_x = p_clip_left;
786
}
787
788
if (to_x > p_clip_right) {
789
h_n = Math::lerp(h, h_n, float(p_clip_right - from_x) / float(to_x - from_x));
790
to_x = p_clip_right;
791
}
792
793
Ref<Texture2D> volume_texture = get_editor_theme_icon(SNAME("ColorTrackVu"));
794
int tex_h = volume_texture->get_height();
795
796
int y_from = (get_size().height - tex_h) / 2;
797
798
Color color = get_theme_color(SceneStringName(font_color), SNAME("Label"));
799
color.a *= 0.7;
800
801
draw_line(Point2(from_x, y_from + h * tex_h), Point2(to_x, y_from + h_n * tex_h), color, 2);
802
}
803
804
////////////////////////
805
806
/// AUDIO ///
807
808
void AnimationTrackEditTypeAudio::_preview_changed(ObjectID p_which) {
809
for (int i = 0; i < get_animation()->track_get_key_count(get_track()); i++) {
810
Ref<AudioStream> stream = get_animation()->audio_track_get_key_stream(get_track(), i);
811
if (stream.is_valid() && stream->get_instance_id() == p_which) {
812
queue_redraw();
813
return;
814
}
815
}
816
}
817
818
int AnimationTrackEditTypeAudio::get_key_height() const {
819
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
820
int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
821
return int(font->get_height(font_size) * 1.5);
822
}
823
824
Rect2 AnimationTrackEditTypeAudio::get_key_rect(int p_index, float p_pixels_sec) {
825
Ref<AudioStream> stream = get_animation()->audio_track_get_key_stream(get_track(), p_index);
826
827
if (stream.is_null()) {
828
return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);
829
}
830
831
float start_ofs = get_animation()->audio_track_get_key_start_offset(get_track(), p_index);
832
float end_ofs = get_animation()->audio_track_get_key_end_offset(get_track(), p_index);
833
834
float len = stream->get_length();
835
836
if (len == 0) {
837
Ref<AudioStreamPreview> preview = AudioStreamPreviewGenerator::get_singleton()->generate_preview(stream);
838
len = preview->get_length();
839
}
840
841
len -= end_ofs;
842
len -= start_ofs;
843
if (len <= 0.0001) {
844
len = 0.0001;
845
}
846
847
if (get_animation()->track_get_key_count(get_track()) > p_index + 1) {
848
len = MIN(len, get_animation()->track_get_key_time(get_track(), p_index + 1) - get_animation()->track_get_key_time(get_track(), p_index));
849
}
850
851
return Rect2(0, 0, len * p_pixels_sec, get_size().height);
852
}
853
854
bool AnimationTrackEditTypeAudio::is_key_selectable_by_distance() const {
855
return false;
856
}
857
858
void AnimationTrackEditTypeAudio::draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) {
859
Ref<AudioStream> stream = get_animation()->audio_track_get_key_stream(get_track(), p_index);
860
if (stream.is_null()) {
861
AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right); // Draw diamond.
862
return;
863
}
864
865
float len = stream->get_length();
866
if (len == 0) {
867
AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right); // Draw diamond.
868
return;
869
}
870
871
float start_ofs = get_animation()->audio_track_get_key_start_offset(get_track(), p_index);
872
float end_ofs = get_animation()->audio_track_get_key_end_offset(get_track(), p_index);
873
874
int px_offset = 0;
875
if (len_resizing && p_index == len_resizing_index) {
876
float ofs_local = len_resizing_rel / get_timeline()->get_zoom_scale();
877
if (len_resizing_start) {
878
start_ofs += ofs_local;
879
px_offset = ofs_local * p_pixels_sec;
880
} else {
881
end_ofs -= ofs_local;
882
}
883
}
884
885
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
886
int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
887
float fh = int(font->get_height(font_size) * 1.5);
888
889
Ref<AudioStreamPreview> preview = AudioStreamPreviewGenerator::get_singleton()->generate_preview(stream);
890
891
int pixel_total_len = len * p_pixels_sec;
892
893
len -= end_ofs;
894
len -= start_ofs;
895
896
if (len <= 0.0001) {
897
len = 0.0001;
898
}
899
900
int pixel_len = len * p_pixels_sec;
901
902
int pixel_begin = px_offset + p_x;
903
int pixel_end = px_offset + p_x + pixel_len;
904
905
if (pixel_end < p_clip_left) {
906
return;
907
}
908
909
if (pixel_begin > p_clip_right) {
910
return;
911
}
912
913
int from_x = MAX(pixel_begin, p_clip_left);
914
int to_x = MIN(pixel_end, p_clip_right);
915
916
if (get_animation()->track_get_key_count(get_track()) > p_index + 1) {
917
float limit = MIN(len, get_animation()->track_get_key_time(get_track(), p_index + 1) - get_animation()->track_get_key_time(get_track(), p_index));
918
int limit_x = pixel_begin + limit * p_pixels_sec;
919
to_x = MIN(limit_x, to_x);
920
}
921
922
if (to_x <= from_x) {
923
to_x = from_x + 1;
924
}
925
926
int h = get_size().height;
927
Rect2 rect = Rect2(from_x, (h - fh) / 2, to_x - from_x, fh);
928
draw_rect(rect, Color(0.25, 0.25, 0.25));
929
930
Vector<Vector2> points;
931
points.resize((to_x - from_x) * 2);
932
float preview_len = preview->get_length();
933
934
for (int i = from_x; i < to_x; i++) {
935
float ofs = (i - pixel_begin) * preview_len / pixel_total_len;
936
float ofs_n = ((i + 1) - pixel_begin) * preview_len / pixel_total_len;
937
ofs += start_ofs;
938
ofs_n += start_ofs;
939
940
float max = preview->get_max(ofs, ofs_n) * 0.5 + 0.5;
941
float min = preview->get_min(ofs, ofs_n) * 0.5 + 0.5;
942
943
int idx = i - from_x;
944
points.write[idx * 2 + 0] = Vector2(i, rect.position.y + min * rect.size.y);
945
points.write[idx * 2 + 1] = Vector2(i, rect.position.y + max * rect.size.y);
946
}
947
948
Vector<Color> colors = { Color(0.75, 0.75, 0.75) };
949
950
RS::get_singleton()->canvas_item_add_multiline(get_canvas_item(), points, colors);
951
952
Color cut_color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
953
cut_color.a = 0.7;
954
if (start_ofs > 0 && pixel_begin > p_clip_left) {
955
draw_rect(Rect2(pixel_begin, rect.position.y, 1, rect.size.y), cut_color);
956
}
957
if (end_ofs > 0 && pixel_end < p_clip_right) {
958
draw_rect(Rect2(pixel_end, rect.position.y, 1, rect.size.y), cut_color);
959
}
960
961
if (p_selected) {
962
Color accent = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
963
draw_rect(rect, accent, false);
964
}
965
}
966
967
AnimationTrackEditTypeAudio::AnimationTrackEditTypeAudio() {
968
AudioStreamPreviewGenerator::get_singleton()->connect("preview_updated", callable_mp(this, &AnimationTrackEditTypeAudio::_preview_changed));
969
}
970
971
bool AnimationTrackEditTypeAudio::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
972
if (p_point.x > get_timeline()->get_name_limit() && p_point.x < get_size().width - get_timeline()->get_buttons_width()) {
973
Dictionary drag_data = p_data;
974
if (drag_data.has("type") && String(drag_data["type"]) == "resource") {
975
Ref<AudioStream> res = drag_data["resource"];
976
if (res.is_valid()) {
977
return true;
978
}
979
}
980
981
if (drag_data.has("type") && String(drag_data["type"]) == "files") {
982
Vector<String> files = drag_data["files"];
983
984
if (files.size() == 1) {
985
Ref<AudioStream> res = ResourceLoader::load(files[0]);
986
if (res.is_valid()) {
987
return true;
988
}
989
}
990
}
991
}
992
993
return AnimationTrackEdit::can_drop_data(p_point, p_data);
994
}
995
996
void AnimationTrackEditTypeAudio::drop_data(const Point2 &p_point, const Variant &p_data) {
997
if (p_point.x > get_timeline()->get_name_limit() && p_point.x < get_size().width - get_timeline()->get_buttons_width()) {
998
Ref<AudioStream> stream;
999
Dictionary drag_data = p_data;
1000
if (drag_data.has("type") && String(drag_data["type"]) == "resource") {
1001
stream = drag_data["resource"];
1002
} else if (drag_data.has("type") && String(drag_data["type"]) == "files") {
1003
Vector<String> files = drag_data["files"];
1004
1005
if (files.size() == 1) {
1006
stream = ResourceLoader::load(files[0]);
1007
}
1008
}
1009
1010
if (stream.is_valid()) {
1011
int x = p_point.x - get_timeline()->get_name_limit();
1012
float ofs = x / get_timeline()->get_zoom_scale();
1013
ofs += get_timeline()->get_value();
1014
1015
ofs = get_editor()->snap_time(ofs);
1016
1017
while (get_animation()->track_find_key(get_track(), ofs, Animation::FIND_MODE_APPROX) != -1) { //make sure insertion point is valid
1018
ofs += 0.0001;
1019
}
1020
1021
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
1022
undo_redo->create_action(TTR("Add Audio Track Clip"));
1023
undo_redo->add_do_method(get_animation().ptr(), "audio_track_insert_key", get_track(), ofs, stream);
1024
undo_redo->add_undo_method(get_animation().ptr(), "track_remove_key_at_time", get_track(), ofs);
1025
undo_redo->commit_action();
1026
1027
queue_redraw();
1028
return;
1029
}
1030
}
1031
1032
AnimationTrackEdit::drop_data(p_point, p_data);
1033
}
1034
1035
void AnimationTrackEditTypeAudio::gui_input(const Ref<InputEvent> &p_event) {
1036
ERR_FAIL_COND(p_event.is_null());
1037
1038
Ref<InputEventMouseMotion> mm = p_event;
1039
if (!len_resizing && mm.is_valid()) {
1040
bool use_hsize_cursor = false;
1041
for (int i = 0; i < get_animation()->track_get_key_count(get_track()); i++) {
1042
Ref<AudioStream> stream = get_animation()->audio_track_get_key_stream(get_track(), i);
1043
1044
if (stream.is_null()) {
1045
continue;
1046
}
1047
1048
float len = stream->get_length();
1049
if (len == 0) {
1050
continue;
1051
}
1052
1053
float start_ofs = get_animation()->audio_track_get_key_start_offset(get_track(), i);
1054
float end_ofs = get_animation()->audio_track_get_key_end_offset(get_track(), i);
1055
len -= end_ofs;
1056
len -= start_ofs;
1057
1058
if (get_animation()->track_get_key_count(get_track()) > i + 1) {
1059
len = MIN(len, get_animation()->track_get_key_time(get_track(), i + 1) - get_animation()->track_get_key_time(get_track(), i));
1060
}
1061
1062
float ofs = get_animation()->track_get_key_time(get_track(), i);
1063
1064
ofs -= get_timeline()->get_value();
1065
ofs *= get_timeline()->get_zoom_scale();
1066
ofs += get_timeline()->get_name_limit();
1067
1068
int end = ofs + len * get_timeline()->get_zoom_scale();
1069
1070
if (end >= get_timeline()->get_name_limit() && end <= get_size().width - get_timeline()->get_buttons_width() && Math::abs(mm->get_position().x - end) < 5 * EDSCALE) {
1071
len_resizing_start = false;
1072
use_hsize_cursor = true;
1073
len_resizing_index = i;
1074
}
1075
1076
if (ofs >= get_timeline()->get_name_limit() && ofs <= get_size().width - get_timeline()->get_buttons_width() && Math::abs(mm->get_position().x - ofs) < 5 * EDSCALE) {
1077
len_resizing_start = true;
1078
use_hsize_cursor = true;
1079
len_resizing_index = i;
1080
}
1081
}
1082
over_drag_position = use_hsize_cursor;
1083
}
1084
1085
if (len_resizing && mm.is_valid()) {
1086
// Rezising index is some.
1087
len_resizing_rel += mm->get_relative().x;
1088
float ofs_local = len_resizing_rel / get_timeline()->get_zoom_scale();
1089
float prev_ofs_start = get_animation()->audio_track_get_key_start_offset(get_track(), len_resizing_index);
1090
float prev_ofs_end = get_animation()->audio_track_get_key_end_offset(get_track(), len_resizing_index);
1091
Ref<AudioStream> stream = get_animation()->audio_track_get_key_stream(get_track(), len_resizing_index);
1092
float len = stream->get_length();
1093
if (len == 0) {
1094
Ref<AudioStreamPreview> preview = AudioStreamPreviewGenerator::get_singleton()->generate_preview(stream);
1095
float preview_len = preview->get_length();
1096
len = preview_len;
1097
}
1098
1099
if (len_resizing_start) {
1100
len_resizing_rel = CLAMP(ofs_local, -prev_ofs_start, len - prev_ofs_end - prev_ofs_start) * get_timeline()->get_zoom_scale();
1101
} else {
1102
len_resizing_rel = CLAMP(ofs_local, -(len - prev_ofs_end - prev_ofs_start), prev_ofs_end) * get_timeline()->get_zoom_scale();
1103
}
1104
1105
queue_redraw();
1106
accept_event();
1107
return;
1108
}
1109
1110
Ref<InputEventMouseButton> mb = p_event;
1111
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT && over_drag_position) {
1112
len_resizing = true;
1113
// In case if resizing index is not set yet reset the flag.
1114
if (len_resizing_index < 0) {
1115
len_resizing = false;
1116
return;
1117
}
1118
len_resizing_from_px = mb->get_position().x;
1119
len_resizing_rel = 0;
1120
queue_redraw();
1121
accept_event();
1122
return;
1123
}
1124
1125
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
1126
if (len_resizing && mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
1127
if (len_resizing_rel == 0 || len_resizing_index < 0) {
1128
len_resizing = false;
1129
return;
1130
}
1131
1132
if (len_resizing_start) {
1133
float ofs_local = len_resizing_rel / get_timeline()->get_zoom_scale();
1134
float prev_ofs = get_animation()->audio_track_get_key_start_offset(get_track(), len_resizing_index);
1135
float prev_time = get_animation()->track_get_key_time(get_track(), len_resizing_index);
1136
float new_ofs = prev_ofs + ofs_local;
1137
float new_time = prev_time + ofs_local;
1138
if (prev_time != new_time) {
1139
undo_redo->create_action(TTR("Change Audio Track Clip Start Offset"));
1140
1141
undo_redo->add_do_method(get_animation().ptr(), "track_set_key_time", get_track(), len_resizing_index, new_time);
1142
undo_redo->add_undo_method(get_animation().ptr(), "track_set_key_time", get_track(), len_resizing_index, prev_time);
1143
1144
undo_redo->add_do_method(get_animation().ptr(), "audio_track_set_key_start_offset", get_track(), len_resizing_index, new_ofs);
1145
undo_redo->add_undo_method(get_animation().ptr(), "audio_track_set_key_start_offset", get_track(), len_resizing_index, prev_ofs);
1146
1147
undo_redo->commit_action();
1148
}
1149
} else {
1150
float ofs_local = -len_resizing_rel / get_timeline()->get_zoom_scale();
1151
float prev_ofs = get_animation()->audio_track_get_key_end_offset(get_track(), len_resizing_index);
1152
float new_ofs = prev_ofs + ofs_local;
1153
if (prev_ofs != new_ofs) {
1154
undo_redo->create_action(TTR("Change Audio Track Clip End Offset"));
1155
undo_redo->add_do_method(get_animation().ptr(), "audio_track_set_key_end_offset", get_track(), len_resizing_index, new_ofs);
1156
undo_redo->add_undo_method(get_animation().ptr(), "audio_track_set_key_end_offset", get_track(), len_resizing_index, prev_ofs);
1157
undo_redo->commit_action();
1158
}
1159
}
1160
1161
len_resizing_index = -1;
1162
len_resizing = false;
1163
queue_redraw();
1164
accept_event();
1165
return;
1166
}
1167
1168
AnimationTrackEdit::gui_input(p_event);
1169
}
1170
1171
Control::CursorShape AnimationTrackEditTypeAudio::get_cursor_shape(const Point2 &p_pos) const {
1172
if (over_drag_position || len_resizing) {
1173
return Control::CURSOR_HSIZE;
1174
} else {
1175
return get_default_cursor_shape();
1176
}
1177
}
1178
1179
////////////////////
1180
/// SUB ANIMATION ///
1181
1182
int AnimationTrackEditTypeAnimation::get_key_height() const {
1183
if (!ObjectDB::get_instance(id)) {
1184
return AnimationTrackEdit::get_key_height();
1185
}
1186
1187
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
1188
int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
1189
return int(font->get_height(font_size) * 1.5);
1190
}
1191
1192
Rect2 AnimationTrackEditTypeAnimation::get_key_rect(int p_index, float p_pixels_sec) {
1193
Object *object = ObjectDB::get_instance(id);
1194
1195
if (!object) {
1196
return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);
1197
}
1198
1199
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(object);
1200
1201
if (!ap) {
1202
return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);
1203
}
1204
1205
String anim = get_animation()->animation_track_get_key_animation(get_track(), p_index);
1206
1207
if (anim != "[stop]" && ap->has_animation(anim)) {
1208
float len = ap->get_animation(anim)->get_length();
1209
1210
if (get_animation()->track_get_key_count(get_track()) > p_index + 1) {
1211
len = MIN(len, get_animation()->track_get_key_time(get_track(), p_index + 1) - get_animation()->track_get_key_time(get_track(), p_index));
1212
}
1213
1214
return Rect2(0, 0, len * p_pixels_sec, get_size().height);
1215
} else {
1216
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
1217
int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
1218
int fh = font->get_height(font_size) * 0.8;
1219
return Rect2(0, 0, fh, get_size().height);
1220
}
1221
}
1222
1223
bool AnimationTrackEditTypeAnimation::is_key_selectable_by_distance() const {
1224
return false;
1225
}
1226
1227
void AnimationTrackEditTypeAnimation::draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) {
1228
Object *object = ObjectDB::get_instance(id);
1229
1230
if (!object) {
1231
AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);
1232
return;
1233
}
1234
1235
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(object);
1236
1237
if (!ap) {
1238
AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);
1239
return;
1240
}
1241
1242
String anim = get_animation()->animation_track_get_key_animation(get_track(), p_index);
1243
1244
if (anim != "[stop]" && ap->has_animation(anim)) {
1245
float len = ap->get_animation(anim)->get_length();
1246
1247
if (get_animation()->track_get_key_count(get_track()) > p_index + 1) {
1248
len = MIN(len, get_animation()->track_get_key_time(get_track(), p_index + 1) - get_animation()->track_get_key_time(get_track(), p_index));
1249
}
1250
1251
int pixel_len = len * p_pixels_sec;
1252
1253
int pixel_begin = p_x;
1254
int pixel_end = p_x + pixel_len;
1255
1256
if (pixel_end < p_clip_left) {
1257
return;
1258
}
1259
1260
if (pixel_begin > p_clip_right) {
1261
return;
1262
}
1263
1264
int from_x = MAX(pixel_begin, p_clip_left);
1265
int to_x = MIN(pixel_end, p_clip_right);
1266
1267
if (to_x <= from_x) {
1268
return;
1269
}
1270
1271
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
1272
int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
1273
int fh = font->get_height(font_size) * 1.5;
1274
1275
Rect2 rect(from_x, int(get_size().height - fh) / 2, to_x - from_x, fh);
1276
1277
Color color = get_theme_color(SceneStringName(font_color), SNAME("Label"));
1278
Color bg = color;
1279
bg.r = 1 - color.r;
1280
bg.g = 1 - color.g;
1281
bg.b = 1 - color.b;
1282
draw_rect(rect, bg);
1283
1284
Vector<Vector2> points;
1285
Vector<Color> colors = { color };
1286
{
1287
Ref<Animation> ap_anim = ap->get_animation(anim);
1288
1289
for (int i = 0; i < ap_anim->get_track_count(); i++) {
1290
float h = (rect.size.height - 2) / ap_anim->get_track_count();
1291
1292
int y = 2 + h * i + h / 2;
1293
1294
for (int j = 0; j < ap_anim->track_get_key_count(i); j++) {
1295
float ofs = ap_anim->track_get_key_time(i, j);
1296
int x = p_x + ofs * p_pixels_sec + 2;
1297
1298
if (x < from_x || x >= (to_x - 4)) {
1299
continue;
1300
}
1301
1302
points.push_back(Point2(x, y));
1303
points.push_back(Point2(x + 1, y));
1304
}
1305
}
1306
}
1307
1308
if (points.size() > 2) {
1309
RS::get_singleton()->canvas_item_add_multiline(get_canvas_item(), points, colors);
1310
}
1311
1312
int limit = to_x - from_x - 4;
1313
if (limit > 0) {
1314
draw_string(font, Point2(from_x + 2, int(get_size().height - font->get_height(font_size)) / 2 + font->get_ascent(font_size)), anim, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color);
1315
}
1316
1317
if (p_selected) {
1318
Color accent = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
1319
draw_rect(rect, accent, false);
1320
}
1321
} else {
1322
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
1323
int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
1324
int fh = font->get_height(font_size) * 0.8;
1325
Rect2 rect(Vector2(p_x, int(get_size().height - fh) / 2), Size2(fh, fh));
1326
1327
Color color = get_theme_color(SceneStringName(font_color), SNAME("Label"));
1328
draw_rect_clipped(rect, color);
1329
1330
if (p_selected) {
1331
Color accent = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
1332
draw_rect_clipped(rect, accent, false);
1333
}
1334
}
1335
}
1336
1337
void AnimationTrackEditTypeAnimation::set_node(Object *p_object) {
1338
id = p_object->get_instance_id();
1339
}
1340
1341
/////////
1342
AnimationTrackEdit *AnimationTrackEditDefaultPlugin::create_value_track_edit(Object *p_object, Variant::Type p_type, const String &p_property, PropertyHint p_hint, const String &p_hint_string, int p_usage) {
1343
if (p_property == "playing" && (p_object->is_class("AudioStreamPlayer") || p_object->is_class("AudioStreamPlayer2D") || p_object->is_class("AudioStreamPlayer3D"))) {
1344
AnimationTrackEditAudio *audio = memnew(AnimationTrackEditAudio);
1345
audio->set_node(p_object);
1346
return audio;
1347
}
1348
1349
if (p_property == "frame" && (p_object->is_class("Sprite2D") || p_object->is_class("Sprite3D") || p_object->is_class("AnimatedSprite2D") || p_object->is_class("AnimatedSprite3D"))) {
1350
AnimationTrackEditSpriteFrame *sprite = memnew(AnimationTrackEditSpriteFrame);
1351
sprite->set_node(p_object);
1352
return sprite;
1353
}
1354
1355
if (p_property == "frame_coords" && (p_object->is_class("Sprite2D") || p_object->is_class("Sprite3D"))) {
1356
AnimationTrackEditSpriteFrame *sprite = memnew(AnimationTrackEditSpriteFrame);
1357
sprite->set_as_coords();
1358
sprite->set_node(p_object);
1359
return sprite;
1360
}
1361
1362
if (p_property == "current_animation" && (p_object->is_class("AnimationPlayer"))) {
1363
AnimationTrackEditSubAnim *player = memnew(AnimationTrackEditSubAnim);
1364
player->set_node(p_object);
1365
return player;
1366
}
1367
1368
if (p_property == "volume_db") {
1369
AnimationTrackEditVolumeDB *vu = memnew(AnimationTrackEditVolumeDB);
1370
return vu;
1371
}
1372
1373
if (p_type == Variant::BOOL) {
1374
return memnew(AnimationTrackEditBool);
1375
}
1376
if (p_type == Variant::COLOR) {
1377
return memnew(AnimationTrackEditColor);
1378
}
1379
1380
return nullptr;
1381
}
1382
1383
AnimationTrackEdit *AnimationTrackEditDefaultPlugin::create_audio_track_edit() {
1384
return memnew(AnimationTrackEditTypeAudio);
1385
}
1386
1387
AnimationTrackEdit *AnimationTrackEditDefaultPlugin::create_animation_track_edit(Object *p_object) {
1388
AnimationTrackEditTypeAnimation *an = memnew(AnimationTrackEditTypeAnimation);
1389
an->set_node(p_object);
1390
return an;
1391
}
1392
1393