Path: blob/master/editor/animation/animation_track_editor_plugins.cpp
21117 views
/**************************************************************************/1/* animation_track_editor_plugins.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "animation_track_editor_plugins.h"3132#include "editor/audio/audio_stream_preview.h"33#include "editor/editor_string_names.h"34#include "editor/editor_undo_redo_manager.h"35#include "editor/inspector/editor_resource_preview.h"36#include "editor/themes/editor_scale.h"37#include "scene/2d/animated_sprite_2d.h"38#include "scene/2d/sprite_2d.h"39#include "scene/3d/sprite_3d.h"40#include "scene/animation/animation_player.h"41#include "servers/audio/audio_stream.h"4243/// BOOL ///44int AnimationTrackEditBool::get_key_height() const {45Ref<Texture2D> checked = get_theme_icon(SNAME("checked"), SNAME("CheckBox"));46return checked->get_height();47}4849Rect2 AnimationTrackEditBool::get_key_rect(int p_index, float p_pixels_sec) {50Ref<Texture2D> checked = get_theme_icon(SNAME("checked"), SNAME("CheckBox"));51return Rect2(-checked->get_width() / 2, 0, checked->get_width(), get_size().height);52}5354bool AnimationTrackEditBool::is_key_selectable_by_distance() const {55return false;56}5758void AnimationTrackEditBool::draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) {59bool checked = get_animation()->track_get_key_value(get_track(), p_index);60Ref<Texture2D> icon = get_theme_icon(checked ? "checked" : "unchecked", "CheckBox");6162Vector2 ofs(p_x - icon->get_width() / 2, int(get_size().height - icon->get_height()) / 2);6364if (ofs.x + icon->get_width() / 2 < p_clip_left) {65return;66}6768if (ofs.x + icon->get_width() / 2 > p_clip_right) {69return;70}7172draw_texture(icon, ofs);7374if (p_selected) {75Color color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));76draw_rect_clipped(Rect2(ofs, icon->get_size()), color, false);77}78}7980/// COLOR ///8182int AnimationTrackEditColor::get_key_height() const {83Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));84int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));85return font->get_height(font_size) * 0.8;86}8788Rect2 AnimationTrackEditColor::get_key_rect(int p_index, float p_pixels_sec) {89Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));90int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));91int fh = font->get_height(font_size) * 0.8;92return Rect2(-fh / 2, 0, fh, get_size().height);93}9495bool AnimationTrackEditColor::is_key_selectable_by_distance() const {96return false;97}9899void 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) {100Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));101int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));102int fh = (font->get_height(font_size) * 0.8);103104fh /= 3;105106int x_from = p_x + fh / 2 - 1;107int x_to = p_next_x - fh / 2 + 1;108x_from = MAX(x_from, p_clip_left);109x_to = MIN(x_to, p_clip_right);110111int y_from = (get_size().height - fh) / 2;112113if (x_from > p_clip_right || x_to < p_clip_left) {114return;115}116117Vector<Color> color_samples;118color_samples.append(get_animation()->track_get_key_value(get_track(), p_index_from));119120if (get_animation()->track_get_type(get_track()) == Animation::TYPE_VALUE) {121if (get_animation()->track_get_interpolation_type(get_track()) != Animation::INTERPOLATION_NEAREST &&122(get_animation()->value_track_get_update_mode(get_track()) == Animation::UPDATE_CONTINUOUS ||123get_animation()->value_track_get_update_mode(get_track()) == Animation::UPDATE_CAPTURE) &&124!Math::is_zero_approx(get_animation()->track_get_key_transition(get_track(), p_index_from))) {125float start_time = get_animation()->track_get_key_time(get_track(), p_index_from);126float end_time = get_animation()->track_get_key_time(get_track(), p_index_to);127128Color color_next = get_animation()->value_track_interpolate(get_track(), end_time);129130if (!color_samples[0].is_equal_approx(color_next)) {131color_samples.resize(1 + (x_to - x_from) / 64); // Make a color sample every 64 px.132for (int i = 1; i < color_samples.size(); i++) {133float j = i;134color_samples.write[i] = get_animation()->value_track_interpolate(135get_track(),136Math::lerp(start_time, end_time, j / color_samples.size()));137}138}139color_samples.append(color_next);140} else {141color_samples.append(color_samples[0]);142}143} else {144color_samples.append(get_animation()->track_get_key_value(get_track(), p_index_to));145}146147for (int i = 0; i < color_samples.size() - 1; i++) {148Vector<Vector2> points = {149Vector2(Math::lerp(x_from, x_to, float(i) / (color_samples.size() - 1)), y_from),150Vector2(Math::lerp(x_from, x_to, float(i + 1) / (color_samples.size() - 1)), y_from),151Vector2(Math::lerp(x_from, x_to, float(i + 1) / (color_samples.size() - 1)), y_from + fh),152Vector2(Math::lerp(x_from, x_to, float(i) / (color_samples.size() - 1)), y_from + fh)153};154155Vector<Color> colors = {156color_samples[i],157color_samples[i + 1],158color_samples[i + 1],159color_samples[i]160};161162draw_primitive(points, colors, Vector<Vector2>());163}164}165166void AnimationTrackEditColor::draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) {167Color color = get_animation()->track_get_key_value(get_track(), p_index);168169Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));170int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));171int fh = font->get_height(font_size) * 0.8;172173Rect2 rect(Vector2(p_x - fh / 2, int(get_size().height - fh) / 2), Size2(fh, fh));174175draw_rect_clipped(Rect2(rect.position, rect.size / 2), Color(0.4, 0.4, 0.4));176draw_rect_clipped(Rect2(rect.position + rect.size / 2, rect.size / 2), Color(0.4, 0.4, 0.4));177draw_rect_clipped(Rect2(rect.position + Vector2(rect.size.x / 2, 0), rect.size / 2), Color(0.6, 0.6, 0.6));178draw_rect_clipped(Rect2(rect.position + Vector2(0, rect.size.y / 2), rect.size / 2), Color(0.6, 0.6, 0.6));179draw_rect_clipped(rect, color);180181if (p_selected) {182Color accent = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));183draw_rect_clipped(rect, accent, false);184}185}186187/// AUDIO ///188189void AnimationTrackEditAudio::_preview_changed(ObjectID p_which) {190Object *object = ObjectDB::get_instance(id);191192if (!object) {193return;194}195196Ref<AudioStream> stream = object->call("get_stream");197198if (stream.is_valid() && stream->get_instance_id() == p_which) {199queue_redraw();200}201}202203int AnimationTrackEditAudio::get_key_height() const {204if (!ObjectDB::get_instance(id)) {205return AnimationTrackEdit::get_key_height();206}207208Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));209int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));210return int(font->get_height(font_size) * 1.5);211}212213Rect2 AnimationTrackEditAudio::get_key_rect(int p_index, float p_pixels_sec) {214Object *object = ObjectDB::get_instance(id);215216if (!object) {217return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);218}219220Ref<AudioStream> stream = object->call("get_stream");221222if (stream.is_null()) {223return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);224}225226bool play = get_animation()->track_get_key_value(get_track(), p_index);227if (play) {228float len = stream->get_length();229230if (len == 0) {231Ref<AudioStreamPreview> preview = AudioStreamPreviewGenerator::get_singleton()->generate_preview(stream);232len = preview->get_length();233}234235if (get_animation()->track_get_key_count(get_track()) > p_index + 1) {236len = MIN(len, get_animation()->track_get_key_time(get_track(), p_index + 1) - get_animation()->track_get_key_time(get_track(), p_index));237}238239return Rect2(0, 0, len * p_pixels_sec, get_size().height);240} else {241Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));242int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));243int fh = font->get_height(font_size) * 0.8;244return Rect2(0, 0, fh, get_size().height);245}246}247248bool AnimationTrackEditAudio::is_key_selectable_by_distance() const {249return false;250}251252void AnimationTrackEditAudio::draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) {253Object *object = ObjectDB::get_instance(id);254255if (!object) {256AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);257return;258}259260Ref<AudioStream> stream = object->call("get_stream");261262if (stream.is_null()) {263AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);264return;265}266267bool play = get_animation()->track_get_key_value(get_track(), p_index);268if (play) {269float len = stream->get_length();270271Ref<AudioStreamPreview> preview = AudioStreamPreviewGenerator::get_singleton()->generate_preview(stream);272273float preview_len = preview->get_length();274275if (len == 0) {276len = preview_len;277}278279int pixel_len = len * p_pixels_sec;280281int pixel_begin = p_x;282int pixel_end = p_x + pixel_len;283284if (pixel_end < p_clip_left) {285return;286}287288if (pixel_begin > p_clip_right) {289return;290}291292int from_x = MAX(pixel_begin, p_clip_left);293int to_x = MIN(pixel_end, p_clip_right);294295if (get_animation()->track_get_key_count(get_track()) > p_index + 1) {296float limit = MIN(len, get_animation()->track_get_key_time(get_track(), p_index + 1) - get_animation()->track_get_key_time(get_track(), p_index));297int limit_x = pixel_begin + limit * p_pixels_sec;298to_x = MIN(limit_x, to_x);299}300301if (to_x <= from_x) {302return;303}304305Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));306int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));307float fh = int(font->get_height(font_size) * 1.5);308Rect2 rect = Rect2(from_x, (get_size().height - fh) / 2, to_x - from_x, fh);309draw_rect(rect, Color(0.25, 0.25, 0.25));310311Vector<Vector2> points;312points.resize((to_x - from_x) * 2);313preview_len = preview->get_length();314315for (int i = from_x; i < to_x; i++) {316float ofs = (i - pixel_begin) * preview_len / pixel_len;317float ofs_n = ((i + 1) - pixel_begin) * preview_len / pixel_len;318float max = preview->get_max(ofs, ofs_n) * 0.5 + 0.5;319float min = preview->get_min(ofs, ofs_n) * 0.5 + 0.5;320321int idx = i - from_x;322points.write[idx * 2 + 0] = Vector2(i, rect.position.y + min * rect.size.y);323points.write[idx * 2 + 1] = Vector2(i, rect.position.y + max * rect.size.y);324}325326Vector<Color> colors = { Color(0.75, 0.75, 0.75) };327328RS::get_singleton()->canvas_item_add_multiline(get_canvas_item(), points, colors);329330if (p_selected) {331Color accent = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));332draw_rect(rect, accent, false);333}334} else {335Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));336int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));337int fh = font->get_height(font_size) * 0.8;338Rect2 rect(Vector2(p_x, int(get_size().height - fh) / 2), Size2(fh, fh));339340Color color = get_theme_color(SceneStringName(font_color), SNAME("Label"));341draw_rect_clipped(rect, color);342343if (p_selected) {344Color accent = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));345draw_rect_clipped(rect, accent, false);346}347}348}349350void AnimationTrackEditAudio::set_node(Object *p_object) {351id = p_object->get_instance_id();352}353354AnimationTrackEditAudio::AnimationTrackEditAudio() {355AudioStreamPreviewGenerator::get_singleton()->connect("preview_updated", callable_mp(this, &AnimationTrackEditAudio::_preview_changed));356}357358/// SPRITE FRAME / FRAME_COORDS ///359360int AnimationTrackEditSpriteFrame::get_key_height() const {361if (!ObjectDB::get_instance(id)) {362return AnimationTrackEdit::get_key_height();363}364365Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));366int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));367return int(font->get_height(font_size) * 2);368}369370Rect2 AnimationTrackEditSpriteFrame::get_key_rect(int p_index, float p_pixels_sec) {371Object *object = ObjectDB::get_instance(id);372373if (!object) {374return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);375}376377Size2 size;378379if (Object::cast_to<Sprite2D>(object) || Object::cast_to<Sprite3D>(object)) {380Ref<Texture2D> texture = object->call("get_texture");381if (texture.is_null()) {382return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);383}384385size = texture->get_size();386387if (bool(object->call("is_region_enabled"))) {388size = Rect2(object->call("get_region_rect")).size;389}390391int hframes = object->call("get_hframes");392int vframes = object->call("get_vframes");393394if (hframes > 1) {395size.x /= hframes;396}397if (vframes > 1) {398size.y /= vframes;399}400} else if (Object::cast_to<AnimatedSprite2D>(object) || Object::cast_to<AnimatedSprite3D>(object)) {401Ref<SpriteFrames> sf = object->call("get_sprite_frames");402if (sf.is_null()) {403return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);404}405406List<StringName> animations;407sf->get_animation_list(&animations);408409int frame = get_animation()->track_get_key_value(get_track(), p_index);410String animation_name;411if (animations.size() == 1) {412animation_name = animations.front()->get();413} else {414// Go through other track to find if animation is set415String animation_path = String(get_animation()->track_get_path(get_track()));416animation_path = animation_path.replace(":frame", ":animation");417int animation_track = get_animation()->find_track(animation_path, get_animation()->track_get_type(get_track()));418419if (animation_track != -1) {420float track_time = get_animation()->track_get_key_time(get_track(), p_index);421int animation_index = get_animation()->track_find_key(animation_track, track_time);422if (animation_index != -1) {423animation_name = get_animation()->track_get_key_value(animation_track, animation_index);424}425}426}427428Ref<Texture2D> texture;429if (!animation_name.is_empty()) {430texture = sf->get_frame_texture(animation_name, frame);431}432if (texture.is_null()) {433return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);434}435436size = texture->get_size();437}438439size = size.floor();440441Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));442int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));443int height = int(font->get_height(font_size) * 2);444int width = height * size.width / size.height;445446return Rect2(0, 0, width, get_size().height);447}448449bool AnimationTrackEditSpriteFrame::is_key_selectable_by_distance() const {450return false;451}452453void AnimationTrackEditSpriteFrame::draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) {454Object *object = ObjectDB::get_instance(id);455456if (!object) {457AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);458return;459}460461Ref<Texture2D> texture;462Rect2 region;463464if (Object::cast_to<Sprite2D>(object) || Object::cast_to<Sprite3D>(object)) {465texture = object->call("get_texture");466if (texture.is_null()) {467AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);468return;469}470471int hframes = object->call("get_hframes");472int vframes = object->call("get_vframes");473474Vector2 coords;475if (is_coords) {476coords = get_animation()->track_get_key_value(get_track(), p_index);477} else {478int frame = get_animation()->track_get_key_value(get_track(), p_index);479coords.x = frame % hframes;480coords.y = frame / hframes;481}482483region.size = texture->get_size();484485if (bool(object->call("is_region_enabled"))) {486region = Rect2(object->call("get_region_rect"));487}488489if (hframes > 1) {490region.size.x /= hframes;491}492if (vframes > 1) {493region.size.y /= vframes;494}495496region.position.x += region.size.x * coords.x;497region.position.y += region.size.y * coords.y;498499} else if (Object::cast_to<AnimatedSprite2D>(object) || Object::cast_to<AnimatedSprite3D>(object)) {500Ref<SpriteFrames> sf = object->call("get_sprite_frames");501if (sf.is_null()) {502AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);503return;504}505506List<StringName> animations;507sf->get_animation_list(&animations);508509int frame = get_animation()->track_get_key_value(get_track(), p_index);510String animation_name;511if (animations.size() == 1) {512animation_name = animations.front()->get();513} else {514// Go through other track to find if animation is set515String animation_path = String(get_animation()->track_get_path(get_track()));516animation_path = animation_path.replace(":frame", ":animation");517int animation_track = get_animation()->find_track(animation_path, get_animation()->track_get_type(get_track()));518519if (animation_track != -1) {520float track_time = get_animation()->track_get_key_time(get_track(), p_index);521int animation_index = get_animation()->track_find_key(animation_track, track_time);522if (animation_index != -1) {523animation_name = get_animation()->track_get_key_value(animation_track, animation_index);524}525}526}527528if (!animation_name.is_empty()) {529texture = sf->get_frame_texture(animation_name, frame);530}531532if (texture.is_null()) {533AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);534return;535}536537region.size = texture->get_size();538}539540Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));541int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));542int height = int(font->get_height(font_size) * 2);543544int width = height * region.size.width / region.size.height;545546Rect2 rect(p_x, int(get_size().height - height) / 2, width, height);547548if (rect.position.x + rect.size.x < p_clip_left) {549return;550}551552if (rect.position.x > p_clip_right) {553return;554}555556Color accent = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));557Color bg = accent;558bg.a = 0.15;559560draw_rect_clipped(rect, bg);561562draw_texture_region_clipped(texture, rect, region);563564if (p_selected) {565draw_rect_clipped(rect, accent, false);566}567}568569void AnimationTrackEditSpriteFrame::set_node(Object *p_object) {570id = p_object->get_instance_id();571}572573void AnimationTrackEditSpriteFrame::set_as_coords() {574is_coords = true;575}576577/// SUB ANIMATION ///578579int AnimationTrackEditSubAnim::get_key_height() const {580if (!ObjectDB::get_instance(id)) {581return AnimationTrackEdit::get_key_height();582}583584Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));585int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));586return int(font->get_height(font_size) * 1.5);587}588589Rect2 AnimationTrackEditSubAnim::get_key_rect(int p_index, float p_pixels_sec) {590Object *object = ObjectDB::get_instance(id);591592if (!object) {593return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);594}595596AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(object);597598if (!ap) {599return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);600}601602String anim = get_animation()->track_get_key_value(get_track(), p_index);603604if (anim != "[stop]" && ap->has_animation(anim)) {605float len = ap->get_animation(anim)->get_length();606607if (get_animation()->track_get_key_count(get_track()) > p_index + 1) {608len = MIN(len, get_animation()->track_get_key_time(get_track(), p_index + 1) - get_animation()->track_get_key_time(get_track(), p_index));609}610611return Rect2(0, 0, len * p_pixels_sec, get_size().height);612} else {613Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));614int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));615int fh = font->get_height(font_size) * 0.8;616return Rect2(0, 0, fh, get_size().height);617}618}619620bool AnimationTrackEditSubAnim::is_key_selectable_by_distance() const {621return false;622}623624void AnimationTrackEditSubAnim::draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) {625Object *object = ObjectDB::get_instance(id);626627if (!object) {628AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);629return;630}631632AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(object);633634if (!ap) {635AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);636return;637}638639String anim = get_animation()->track_get_key_value(get_track(), p_index);640641if (anim != "[stop]" && ap->has_animation(anim)) {642float len = ap->get_animation(anim)->get_length();643644if (get_animation()->track_get_key_count(get_track()) > p_index + 1) {645len = MIN(len, get_animation()->track_get_key_time(get_track(), p_index + 1) - get_animation()->track_get_key_time(get_track(), p_index));646}647648int pixel_len = len * p_pixels_sec;649650int pixel_begin = p_x;651int pixel_end = p_x + pixel_len;652653if (pixel_end < p_clip_left) {654return;655}656657if (pixel_begin > p_clip_right) {658return;659}660661int from_x = MAX(pixel_begin, p_clip_left);662int to_x = MIN(pixel_end, p_clip_right);663664if (to_x <= from_x) {665return;666}667668Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));669int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));670int fh = font->get_height(font_size) * 1.5;671672Rect2 rect(from_x, int(get_size().height - fh) / 2, to_x - from_x, fh);673674Color color = get_theme_color(SceneStringName(font_color), SNAME("Label"));675Color bg = color;676bg.r = 1 - color.r;677bg.g = 1 - color.g;678bg.b = 1 - color.b;679draw_rect(rect, bg);680681Vector<Vector2> points;682Vector<Color> colors = { color };683{684Ref<Animation> ap_anim = ap->get_animation(anim);685686for (int i = 0; i < ap_anim->get_track_count(); i++) {687float h = (rect.size.height - 2) / ap_anim->get_track_count();688689int y = 2 + h * i + h / 2;690691for (int j = 0; j < ap_anim->track_get_key_count(i); j++) {692float ofs = ap_anim->track_get_key_time(i, j);693int x = p_x + ofs * p_pixels_sec + 2;694695if (x < from_x || x >= (to_x - 4)) {696continue;697}698699points.push_back(Point2(x, y));700points.push_back(Point2(x + 1, y));701}702}703}704705if (points.size() > 2) {706RS::get_singleton()->canvas_item_add_multiline(get_canvas_item(), points, colors);707}708709int limit = to_x - from_x - 4;710if (limit > 0) {711draw_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);712}713714if (p_selected) {715Color accent = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));716draw_rect(rect, accent, false);717}718} else {719Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));720int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));721int fh = font->get_height(font_size) * 0.8;722Rect2 rect(Vector2(p_x, int(get_size().height - fh) / 2), Size2(fh, fh));723724Color color = get_theme_color(SceneStringName(font_color), SNAME("Label"));725draw_rect_clipped(rect, color);726727if (p_selected) {728Color accent = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));729draw_rect_clipped(rect, accent, false);730}731}732}733734void AnimationTrackEditSubAnim::set_node(Object *p_object) {735id = p_object->get_instance_id();736}737738//// VOLUME DB ////739740int AnimationTrackEditVolumeDB::get_key_height() const {741Ref<Texture2D> volume_texture = get_editor_theme_icon(SNAME("ColorTrackVu"));742return volume_texture->get_height() * 1.2;743}744745void AnimationTrackEditVolumeDB::draw_bg(int p_clip_left, int p_clip_right) {746Ref<Texture2D> volume_texture = get_editor_theme_icon(SNAME("ColorTrackVu"));747int tex_h = volume_texture->get_height();748749int y_from = (get_size().height - tex_h) / 2;750int y_size = tex_h;751752Color color(1, 1, 1, 0.3);753draw_texture_rect(volume_texture, Rect2(p_clip_left, y_from, p_clip_right - p_clip_left, y_from + y_size), false, color);754}755756void AnimationTrackEditVolumeDB::draw_fg(int p_clip_left, int p_clip_right) {757Ref<Texture2D> volume_texture = get_editor_theme_icon(SNAME("ColorTrackVu"));758int tex_h = volume_texture->get_height();759int y_from = (get_size().height - tex_h) / 2;760int db0 = y_from + (24 / 80.0) * tex_h;761762draw_line(Vector2(p_clip_left, db0), Vector2(p_clip_right, db0), Color(1, 1, 1, 0.3));763}764765void 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) {766if (p_x > p_clip_right || p_next_x < p_clip_left) {767return;768}769770float db = get_animation()->track_get_key_value(get_track(), p_index_from);771float db_n = get_animation()->track_get_key_value(get_track(), p_index_to);772773db = CLAMP(db, -60, 24);774db_n = CLAMP(db_n, -60, 24);775776float h = 1.0 - ((db + 60) / 84.0);777float h_n = 1.0 - ((db_n + 60) / 84.0);778779int from_x = p_x;780int to_x = p_next_x;781782if (from_x < p_clip_left) {783h = Math::lerp(h, h_n, float(p_clip_left - from_x) / float(to_x - from_x));784from_x = p_clip_left;785}786787if (to_x > p_clip_right) {788h_n = Math::lerp(h, h_n, float(p_clip_right - from_x) / float(to_x - from_x));789to_x = p_clip_right;790}791792Ref<Texture2D> volume_texture = get_editor_theme_icon(SNAME("ColorTrackVu"));793int tex_h = volume_texture->get_height();794795int y_from = (get_size().height - tex_h) / 2;796797Color color = get_theme_color(SceneStringName(font_color), SNAME("Label"));798color.a *= 0.7;799800draw_line(Point2(from_x, y_from + h * tex_h), Point2(to_x, y_from + h_n * tex_h), color, 2);801}802803////////////////////////804805/// AUDIO ///806807void AnimationTrackEditTypeAudio::_preview_changed(ObjectID p_which) {808for (int i = 0; i < get_animation()->track_get_key_count(get_track()); i++) {809Ref<AudioStream> stream = get_animation()->audio_track_get_key_stream(get_track(), i);810if (stream.is_valid() && stream->get_instance_id() == p_which) {811queue_redraw();812return;813}814}815}816817int AnimationTrackEditTypeAudio::get_key_height() const {818Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));819int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));820return int(font->get_height(font_size) * 1.5);821}822823Rect2 AnimationTrackEditTypeAudio::get_key_rect(int p_index, float p_pixels_sec) {824Ref<AudioStream> stream = get_animation()->audio_track_get_key_stream(get_track(), p_index);825826if (stream.is_null()) {827return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);828}829830float start_ofs = get_animation()->audio_track_get_key_start_offset(get_track(), p_index);831float end_ofs = get_animation()->audio_track_get_key_end_offset(get_track(), p_index);832833float len = stream->get_length();834835if (len == 0) {836Ref<AudioStreamPreview> preview = AudioStreamPreviewGenerator::get_singleton()->generate_preview(stream);837len = preview->get_length();838}839840len -= end_ofs;841len -= start_ofs;842if (len <= 0.0001) {843len = 0.0001;844}845846if (get_animation()->track_get_key_count(get_track()) > p_index + 1) {847len = MIN(len, get_animation()->track_get_key_time(get_track(), p_index + 1) - get_animation()->track_get_key_time(get_track(), p_index));848}849850return Rect2(0, 0, len * p_pixels_sec, get_size().height);851}852853bool AnimationTrackEditTypeAudio::is_key_selectable_by_distance() const {854return false;855}856857void AnimationTrackEditTypeAudio::draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) {858Ref<AudioStream> stream = get_animation()->audio_track_get_key_stream(get_track(), p_index);859if (stream.is_null()) {860AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right); // Draw diamond.861return;862}863864float len = stream->get_length();865if (len == 0) {866AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right); // Draw diamond.867return;868}869870float start_ofs = get_animation()->audio_track_get_key_start_offset(get_track(), p_index);871float end_ofs = get_animation()->audio_track_get_key_end_offset(get_track(), p_index);872873int px_offset = 0;874if (len_resizing && p_index == len_resizing_index) {875float ofs_local = len_resizing_rel / get_timeline()->get_zoom_scale();876if (len_resizing_start) {877start_ofs += ofs_local;878px_offset = ofs_local * p_pixels_sec;879} else {880end_ofs -= ofs_local;881}882}883884Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));885int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));886float fh = int(font->get_height(font_size) * 1.5);887888Ref<AudioStreamPreview> preview = AudioStreamPreviewGenerator::get_singleton()->generate_preview(stream);889890int pixel_total_len = len * p_pixels_sec;891892len -= end_ofs;893len -= start_ofs;894895if (len <= 0.0001) {896len = 0.0001;897}898899int pixel_len = len * p_pixels_sec;900901int pixel_begin = px_offset + p_x;902int pixel_end = px_offset + p_x + pixel_len;903904if (pixel_end < p_clip_left) {905return;906}907908if (pixel_begin > p_clip_right) {909return;910}911912int from_x = MAX(pixel_begin, p_clip_left);913int to_x = MIN(pixel_end, p_clip_right);914915if (get_animation()->track_get_key_count(get_track()) > p_index + 1) {916float limit = MIN(len, get_animation()->track_get_key_time(get_track(), p_index + 1) - get_animation()->track_get_key_time(get_track(), p_index));917int limit_x = pixel_begin + limit * p_pixels_sec;918to_x = MIN(limit_x, to_x);919}920921if (to_x <= from_x) {922to_x = from_x + 1;923}924925int h = get_size().height;926Rect2 rect = Rect2(from_x, (h - fh) / 2, to_x - from_x, fh);927draw_rect(rect, Color(0.25, 0.25, 0.25));928929Vector<Vector2> points;930points.resize((to_x - from_x) * 2);931float preview_len = preview->get_length();932933for (int i = from_x; i < to_x; i++) {934float ofs = (i - pixel_begin) * preview_len / pixel_total_len;935float ofs_n = ((i + 1) - pixel_begin) * preview_len / pixel_total_len;936ofs += start_ofs;937ofs_n += start_ofs;938939float max = preview->get_max(ofs, ofs_n) * 0.5 + 0.5;940float min = preview->get_min(ofs, ofs_n) * 0.5 + 0.5;941942int idx = i - from_x;943points.write[idx * 2 + 0] = Vector2(i, rect.position.y + min * rect.size.y);944points.write[idx * 2 + 1] = Vector2(i, rect.position.y + max * rect.size.y);945}946947Vector<Color> colors = { Color(0.75, 0.75, 0.75) };948949RS::get_singleton()->canvas_item_add_multiline(get_canvas_item(), points, colors);950951Color cut_color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));952cut_color.a = 0.7;953if (start_ofs > 0 && pixel_begin > p_clip_left) {954draw_rect(Rect2(pixel_begin, rect.position.y, 1, rect.size.y), cut_color);955}956if (end_ofs > 0 && pixel_end < p_clip_right) {957draw_rect(Rect2(pixel_end, rect.position.y, 1, rect.size.y), cut_color);958}959960if (p_selected) {961Color accent = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));962draw_rect(rect, accent, false);963}964}965966AnimationTrackEditTypeAudio::AnimationTrackEditTypeAudio() {967AudioStreamPreviewGenerator::get_singleton()->connect("preview_updated", callable_mp(this, &AnimationTrackEditTypeAudio::_preview_changed));968}969970bool AnimationTrackEditTypeAudio::can_drop_data(const Point2 &p_point, const Variant &p_data) const {971if (p_point.x > get_timeline()->get_name_limit() && p_point.x < get_size().width - get_timeline()->get_buttons_width()) {972Dictionary drag_data = p_data;973if (drag_data.has("type") && String(drag_data["type"]) == "resource") {974Ref<AudioStream> res = drag_data["resource"];975if (res.is_valid()) {976return true;977}978}979980if (drag_data.has("type") && String(drag_data["type"]) == "files") {981Vector<String> files = drag_data["files"];982983if (files.size() == 1) {984Ref<AudioStream> res = ResourceLoader::load(files[0]);985if (res.is_valid()) {986return true;987}988}989}990}991992return AnimationTrackEdit::can_drop_data(p_point, p_data);993}994995void AnimationTrackEditTypeAudio::drop_data(const Point2 &p_point, const Variant &p_data) {996if (p_point.x > get_timeline()->get_name_limit() && p_point.x < get_size().width - get_timeline()->get_buttons_width()) {997Ref<AudioStream> stream;998Dictionary drag_data = p_data;999if (drag_data.has("type") && String(drag_data["type"]) == "resource") {1000stream = drag_data["resource"];1001} else if (drag_data.has("type") && String(drag_data["type"]) == "files") {1002Vector<String> files = drag_data["files"];10031004if (files.size() == 1) {1005stream = ResourceLoader::load(files[0]);1006}1007}10081009if (stream.is_valid()) {1010int x = p_point.x - get_timeline()->get_name_limit();1011float ofs = x / get_timeline()->get_zoom_scale();1012ofs += get_timeline()->get_value();10131014ofs = get_editor()->snap_time(ofs);10151016while (get_animation()->track_find_key(get_track(), ofs, Animation::FIND_MODE_APPROX) != -1) { //make sure insertion point is valid1017ofs += 0.0001;1018}10191020EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();1021undo_redo->create_action(TTR("Add Audio Track Clip"));1022undo_redo->add_do_method(get_animation().ptr(), "audio_track_insert_key", get_track(), ofs, stream);1023undo_redo->add_undo_method(get_animation().ptr(), "track_remove_key_at_time", get_track(), ofs);1024undo_redo->commit_action();10251026queue_redraw();1027return;1028}1029}10301031AnimationTrackEdit::drop_data(p_point, p_data);1032}10331034void AnimationTrackEditTypeAudio::gui_input(const Ref<InputEvent> &p_event) {1035ERR_FAIL_COND(p_event.is_null());10361037Ref<InputEventMouseMotion> mm = p_event;1038if (!len_resizing && mm.is_valid()) {1039bool use_hsize_cursor = false;1040for (int i = 0; i < get_animation()->track_get_key_count(get_track()); i++) {1041Ref<AudioStream> stream = get_animation()->audio_track_get_key_stream(get_track(), i);10421043if (stream.is_null()) {1044continue;1045}10461047float len = stream->get_length();1048if (len == 0) {1049continue;1050}10511052float start_ofs = get_animation()->audio_track_get_key_start_offset(get_track(), i);1053float end_ofs = get_animation()->audio_track_get_key_end_offset(get_track(), i);1054len -= end_ofs;1055len -= start_ofs;10561057if (get_animation()->track_get_key_count(get_track()) > i + 1) {1058len = MIN(len, get_animation()->track_get_key_time(get_track(), i + 1) - get_animation()->track_get_key_time(get_track(), i));1059}10601061float ofs = get_animation()->track_get_key_time(get_track(), i);10621063ofs -= get_timeline()->get_value();1064ofs *= get_timeline()->get_zoom_scale();1065ofs += get_timeline()->get_name_limit();10661067int end = ofs + len * get_timeline()->get_zoom_scale();10681069if (end >= get_timeline()->get_name_limit() && end <= get_size().width - get_timeline()->get_buttons_width() && Math::abs(mm->get_position().x - end) < 5 * EDSCALE) {1070len_resizing_start = false;1071use_hsize_cursor = true;1072len_resizing_index = i;1073}10741075if (ofs >= get_timeline()->get_name_limit() && ofs <= get_size().width - get_timeline()->get_buttons_width() && Math::abs(mm->get_position().x - ofs) < 5 * EDSCALE) {1076len_resizing_start = true;1077use_hsize_cursor = true;1078len_resizing_index = i;1079}1080}1081over_drag_position = use_hsize_cursor;1082}10831084if (len_resizing && mm.is_valid()) {1085// Rezising index is some.1086len_resizing_rel += mm->get_relative().x;1087float ofs_local = len_resizing_rel / get_timeline()->get_zoom_scale();1088float prev_ofs_start = get_animation()->audio_track_get_key_start_offset(get_track(), len_resizing_index);1089float prev_ofs_end = get_animation()->audio_track_get_key_end_offset(get_track(), len_resizing_index);1090Ref<AudioStream> stream = get_animation()->audio_track_get_key_stream(get_track(), len_resizing_index);1091float len = stream->get_length();1092if (len == 0) {1093Ref<AudioStreamPreview> preview = AudioStreamPreviewGenerator::get_singleton()->generate_preview(stream);1094float preview_len = preview->get_length();1095len = preview_len;1096}10971098if (len_resizing_start) {1099len_resizing_rel = CLAMP(ofs_local, -prev_ofs_start, len - prev_ofs_end - prev_ofs_start) * get_timeline()->get_zoom_scale();1100} else {1101len_resizing_rel = CLAMP(ofs_local, -(len - prev_ofs_end - prev_ofs_start), prev_ofs_end) * get_timeline()->get_zoom_scale();1102}11031104queue_redraw();1105accept_event();1106return;1107}11081109Ref<InputEventMouseButton> mb = p_event;1110if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT && over_drag_position) {1111len_resizing = true;1112// In case if resizing index is not set yet reset the flag.1113if (len_resizing_index < 0) {1114len_resizing = false;1115return;1116}1117len_resizing_from_px = mb->get_position().x;1118len_resizing_rel = 0;1119queue_redraw();1120accept_event();1121return;1122}11231124EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();1125if (len_resizing && mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {1126if (len_resizing_rel == 0 || len_resizing_index < 0) {1127len_resizing = false;1128return;1129}11301131if (len_resizing_start) {1132float ofs_local = len_resizing_rel / get_timeline()->get_zoom_scale();1133float prev_ofs = get_animation()->audio_track_get_key_start_offset(get_track(), len_resizing_index);1134float prev_time = get_animation()->track_get_key_time(get_track(), len_resizing_index);1135float new_ofs = prev_ofs + ofs_local;1136float new_time = prev_time + ofs_local;1137if (prev_time != new_time) {1138undo_redo->create_action(TTR("Change Audio Track Clip Start Offset"));11391140undo_redo->add_do_method(get_animation().ptr(), "track_set_key_time", get_track(), len_resizing_index, new_time);1141undo_redo->add_undo_method(get_animation().ptr(), "track_set_key_time", get_track(), len_resizing_index, prev_time);11421143undo_redo->add_do_method(get_animation().ptr(), "audio_track_set_key_start_offset", get_track(), len_resizing_index, new_ofs);1144undo_redo->add_undo_method(get_animation().ptr(), "audio_track_set_key_start_offset", get_track(), len_resizing_index, prev_ofs);11451146undo_redo->commit_action();1147}1148} else {1149float ofs_local = -len_resizing_rel / get_timeline()->get_zoom_scale();1150float prev_ofs = get_animation()->audio_track_get_key_end_offset(get_track(), len_resizing_index);1151float new_ofs = prev_ofs + ofs_local;1152if (prev_ofs != new_ofs) {1153undo_redo->create_action(TTR("Change Audio Track Clip End Offset"));1154undo_redo->add_do_method(get_animation().ptr(), "audio_track_set_key_end_offset", get_track(), len_resizing_index, new_ofs);1155undo_redo->add_undo_method(get_animation().ptr(), "audio_track_set_key_end_offset", get_track(), len_resizing_index, prev_ofs);1156undo_redo->commit_action();1157}1158}11591160len_resizing_index = -1;1161len_resizing = false;1162queue_redraw();1163accept_event();1164return;1165}11661167AnimationTrackEdit::gui_input(p_event);1168}11691170Control::CursorShape AnimationTrackEditTypeAudio::get_cursor_shape(const Point2 &p_pos) const {1171if (over_drag_position || len_resizing) {1172return Control::CURSOR_HSIZE;1173} else {1174return get_default_cursor_shape();1175}1176}11771178////////////////////1179/// SUB ANIMATION ///11801181int AnimationTrackEditTypeAnimation::get_key_height() const {1182if (!ObjectDB::get_instance(id)) {1183return AnimationTrackEdit::get_key_height();1184}11851186Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));1187int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));1188return int(font->get_height(font_size) * 1.5);1189}11901191Rect2 AnimationTrackEditTypeAnimation::get_key_rect(int p_index, float p_pixels_sec) {1192Object *object = ObjectDB::get_instance(id);11931194if (!object) {1195return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);1196}11971198AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(object);11991200if (!ap) {1201return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);1202}12031204String anim = get_animation()->animation_track_get_key_animation(get_track(), p_index);12051206if (anim != "[stop]" && ap->has_animation(anim)) {1207float len = ap->get_animation(anim)->get_length();12081209if (get_animation()->track_get_key_count(get_track()) > p_index + 1) {1210len = MIN(len, get_animation()->track_get_key_time(get_track(), p_index + 1) - get_animation()->track_get_key_time(get_track(), p_index));1211}12121213return Rect2(0, 0, len * p_pixels_sec, get_size().height);1214} else {1215Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));1216int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));1217int fh = font->get_height(font_size) * 0.8;1218return Rect2(0, 0, fh, get_size().height);1219}1220}12211222bool AnimationTrackEditTypeAnimation::is_key_selectable_by_distance() const {1223return false;1224}12251226void AnimationTrackEditTypeAnimation::draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) {1227Object *object = ObjectDB::get_instance(id);12281229if (!object) {1230AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);1231return;1232}12331234AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(object);12351236if (!ap) {1237AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);1238return;1239}12401241String anim = get_animation()->animation_track_get_key_animation(get_track(), p_index);12421243if (anim != "[stop]" && ap->has_animation(anim)) {1244float len = ap->get_animation(anim)->get_length();12451246if (get_animation()->track_get_key_count(get_track()) > p_index + 1) {1247len = MIN(len, get_animation()->track_get_key_time(get_track(), p_index + 1) - get_animation()->track_get_key_time(get_track(), p_index));1248}12491250int pixel_len = len * p_pixels_sec;12511252int pixel_begin = p_x;1253int pixel_end = p_x + pixel_len;12541255if (pixel_end < p_clip_left) {1256return;1257}12581259if (pixel_begin > p_clip_right) {1260return;1261}12621263int from_x = MAX(pixel_begin, p_clip_left);1264int to_x = MIN(pixel_end, p_clip_right);12651266if (to_x <= from_x) {1267return;1268}12691270Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));1271int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));1272int fh = font->get_height(font_size) * 1.5;12731274Rect2 rect(from_x, int(get_size().height - fh) / 2, to_x - from_x, fh);12751276Color color = get_theme_color(SceneStringName(font_color), SNAME("Label"));1277Color bg = color;1278bg.r = 1 - color.r;1279bg.g = 1 - color.g;1280bg.b = 1 - color.b;1281draw_rect(rect, bg);12821283Vector<Vector2> points;1284Vector<Color> colors = { color };1285{1286Ref<Animation> ap_anim = ap->get_animation(anim);12871288for (int i = 0; i < ap_anim->get_track_count(); i++) {1289float h = (rect.size.height - 2) / ap_anim->get_track_count();12901291int y = 2 + h * i + h / 2;12921293for (int j = 0; j < ap_anim->track_get_key_count(i); j++) {1294float ofs = ap_anim->track_get_key_time(i, j);1295int x = p_x + ofs * p_pixels_sec + 2;12961297if (x < from_x || x >= (to_x - 4)) {1298continue;1299}13001301points.push_back(Point2(x, y));1302points.push_back(Point2(x + 1, y));1303}1304}1305}13061307if (points.size() > 2) {1308RS::get_singleton()->canvas_item_add_multiline(get_canvas_item(), points, colors);1309}13101311int limit = to_x - from_x - 4;1312if (limit > 0) {1313draw_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);1314}13151316if (p_selected) {1317Color accent = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));1318draw_rect(rect, accent, false);1319}1320} else {1321Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));1322int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));1323int fh = font->get_height(font_size) * 0.8;1324Rect2 rect(Vector2(p_x, int(get_size().height - fh) / 2), Size2(fh, fh));13251326Color color = get_theme_color(SceneStringName(font_color), SNAME("Label"));1327draw_rect_clipped(rect, color);13281329if (p_selected) {1330Color accent = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));1331draw_rect_clipped(rect, accent, false);1332}1333}1334}13351336void AnimationTrackEditTypeAnimation::set_node(Object *p_object) {1337id = p_object->get_instance_id();1338}13391340/////////1341AnimationTrackEdit *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) {1342if (p_property == "playing" && (p_object->is_class("AudioStreamPlayer") || p_object->is_class("AudioStreamPlayer2D") || p_object->is_class("AudioStreamPlayer3D"))) {1343AnimationTrackEditAudio *audio = memnew(AnimationTrackEditAudio);1344audio->set_node(p_object);1345return audio;1346}13471348if (p_property == "frame" && (p_object->is_class("Sprite2D") || p_object->is_class("Sprite3D") || p_object->is_class("AnimatedSprite2D") || p_object->is_class("AnimatedSprite3D"))) {1349AnimationTrackEditSpriteFrame *sprite = memnew(AnimationTrackEditSpriteFrame);1350sprite->set_node(p_object);1351return sprite;1352}13531354if (p_property == "frame_coords" && (p_object->is_class("Sprite2D") || p_object->is_class("Sprite3D"))) {1355AnimationTrackEditSpriteFrame *sprite = memnew(AnimationTrackEditSpriteFrame);1356sprite->set_as_coords();1357sprite->set_node(p_object);1358return sprite;1359}13601361if (p_property == "current_animation" && (p_object->is_class("AnimationPlayer"))) {1362AnimationTrackEditSubAnim *player = memnew(AnimationTrackEditSubAnim);1363player->set_node(p_object);1364return player;1365}13661367if (p_property == "volume_db") {1368AnimationTrackEditVolumeDB *vu = memnew(AnimationTrackEditVolumeDB);1369return vu;1370}13711372if (p_type == Variant::BOOL) {1373return memnew(AnimationTrackEditBool);1374}1375if (p_type == Variant::COLOR) {1376return memnew(AnimationTrackEditColor);1377}13781379return nullptr;1380}13811382AnimationTrackEdit *AnimationTrackEditDefaultPlugin::create_audio_track_edit() {1383return memnew(AnimationTrackEditTypeAudio);1384}13851386AnimationTrackEdit *AnimationTrackEditDefaultPlugin::create_animation_track_edit(Object *p_object) {1387AnimationTrackEditTypeAnimation *an = memnew(AnimationTrackEditTypeAnimation);1388an->set_node(p_object);1389return an;1390}139113921393