/* SPDX-FileCopyrightText: 2022 Blender Authors * * SPDX-License-Identifier: GPL-2.0-or-later */ /** \file * \ingroup sequencer */ #include #include "DNA_anim_types.h" #include "DNA_scene_types.h" #include "DNA_sequence_types.h" #include "BKE_fcurve.hh" #include "BLI_listbase.h" #include "DEG_depsgraph.hh" #include "SEQ_animation.hh" namespace blender::seq { bool animation_keyframes_exist(const Scene *scene) { return scene->adt != nullptr && scene->adt->action != nullptr && scene->adt->action->wrap().has_keyframes(scene->adt->slot_handle); } bool animation_drivers_exist(Scene *scene) { return scene->adt != nullptr && !scene->adt->drivers.is_empty(); } bool fcurve_matches(const Strip &strip, const FCurve &fcurve) { return animrig::fcurve_matches_collection_path( fcurve, "sequence_editor.strips_all[", strip.name + 2); } void offset_animdata(const Scene *scene, Strip *strip, float ofs) { if (!animation_keyframes_exist(scene) || ofs == 0.0f) { return; } Vector fcurves = animrig::fcurves_in_action_slot_filtered( scene->adt->action, scene->adt->slot_handle, [&](const FCurve &fcurve) { return fcurve_matches(*strip, fcurve); }); for (FCurve *fcu : fcurves) { uint i; if (fcu->bezt) { for (i = 0; i < fcu->totvert; i++) { BezTriple *bezt = &fcu->bezt[i]; bezt->vec[0][0] += ofs; bezt->vec[1][0] += ofs; bezt->vec[2][0] += ofs; } } if (fcu->fpt) { for (i = 0; i < fcu->totvert; i++) { FPoint *fpt = &fcu->fpt[i]; fpt->vec[0] += ofs; } } } DEG_id_tag_update(&scene->adt->action->id, ID_RECALC_ANIMATION); } void free_animdata(Scene *scene, Strip *strip) { if (!animation_keyframes_exist(scene)) { return; } Vector fcurves = animrig::fcurves_in_action_slot_filtered( scene->adt->action, scene->adt->slot_handle, [&](const FCurve &fcurve) { return fcurve_matches(*strip, fcurve); }); animrig::Action &action = scene->adt->action->wrap(); for (FCurve *fcu : fcurves) { action_fcurve_remove(action, *fcu); } } void animation_backup_original(Scene *scene, AnimationBackup *backup) { if (animation_keyframes_exist(scene)) { animrig::Action &action = scene->adt->action->wrap(); assert_baklava_phase_1_invariants(action); if (animrig::Channelbag *channelbag = animrig::channelbag_for_action_slot( action, scene->adt->slot_handle)) { animrig::channelbag_fcurves_move(backup->channelbag, *channelbag); } } if (animation_drivers_exist(scene)) { BLI_movelisttolist(&backup->drivers, &scene->adt->drivers); } } void animation_restore_original(Scene *scene, AnimationBackup *backup) { if (!backup->channelbag.fcurves().is_empty()) { BLI_assert(scene->adt != nullptr && scene->adt->action != nullptr); animrig::Action &action = scene->adt->action->wrap(); assert_baklava_phase_1_invariants(action); animrig::Channelbag *channelbag = animrig::channelbag_for_action_slot(action, scene->adt->slot_handle); /* The channel bag should exist if we got here, because otherwise the * backup channel bag would have been empty. */ BLI_assert(channelbag != nullptr); animrig::channelbag_fcurves_move(*channelbag, backup->channelbag); } if (!backup->drivers.is_empty()) { BLI_assert(scene->adt != nullptr); BLI_movelisttolist(&scene->adt->drivers, &backup->drivers); } } /** * Duplicate the animation in `src` that matches items in `strip` into `dst`. */ static void strip_animation_duplicate(Strip *strip, animrig::Action &dst, const animrig::slot_handle_t dst_slot_handle, AnimationBackup *src) { if (strip->type == STRIP_TYPE_META) { for (Strip &meta_child : strip->seqbase) { strip_animation_duplicate(&meta_child, dst, dst_slot_handle, src); } } Vector fcurves = animrig::fcurves_in_span_filtered( src->channelbag.fcurves(), [&](const FCurve &fcurve) { return fcurve_matches(*strip, fcurve); }); for (const FCurve *fcu : fcurves) { FCurve *fcu_copy = BKE_fcurve_copy(fcu); /* Handling groups properly requires more work, so we ignore them for now. * * Note that when legacy actions are deprecated, then we can handle channel * groups way more easily because we know they're stored in the * already-duplicated channelbag in `src`, and we therefore don't have to * worry that they might have already been freed. */ fcu_copy->grp = nullptr; animrig::action_fcurve_attach(dst, dst_slot_handle, *fcu_copy, std::nullopt); } } /** * Duplicate the drivers in `src` that matches items in `strip` into `dst`. */ static void strip_drivers_duplicate(Strip *strip, AnimData *dst, AnimationBackup *src) { if (strip->type == STRIP_TYPE_META) { for (Strip &meta_child : strip->seqbase) { strip_drivers_duplicate(&meta_child, dst, src); } } Vector fcurves = animrig::fcurves_in_listbase_filtered( src->drivers, [&](const FCurve &fcurve) { return fcurve_matches(*strip, fcurve); }); for (const FCurve *fcu : fcurves) { FCurve *fcu_cpy = BKE_fcurve_copy(fcu); BLI_addtail(&dst->drivers, fcu_cpy); } } void animation_duplicate_backup_to_scene(Scene *scene, Strip *strip, AnimationBackup *backup) { BLI_assert(scene != nullptr); if (!backup->channelbag.fcurves().is_empty()) { BLI_assert(scene->adt != nullptr); BLI_assert(scene->adt->action != nullptr); strip_animation_duplicate(strip, scene->adt->action->wrap(), scene->adt->slot_handle, backup); } if (!backup->drivers.is_empty()) { BLI_assert(scene->adt != nullptr); strip_drivers_duplicate(strip, scene->adt, backup); } } } // namespace blender::seq