/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** \file * \ingroup edarmature */ #include "ED_armature.h" #include #include "BLI_listbase.h" #include "MEM_guardedalloc.h" #include "DNA_action_types.h" #include "DNA_armature_types.h" #include "DNA_object_types.h" #include "BKE_action.h" #include "BKE_action.hh" #include "BKE_armature.hh" #include "BKE_idprop.h" using namespace blender::bke; /* simple struct for storing backup info for one pose channel */ struct PoseChannelBackup { struct PoseChannelBackup *next, *prev; struct bPoseChannel *pchan; /* Pose channel this backup is for. */ struct bPoseChannel olddata; /* Backup of pose channel. */ struct IDProperty *oldprops; /* Backup copy (needs freeing) of pose channel's ID properties. */ }; struct PoseBackup { bool is_bone_selection_relevant; ListBase /* PoseChannelBackup* */ backups; }; static PoseBackup *pose_backup_create(const Object *ob, const bAction *action, const BoneNameSet &selected_bone_names) { ListBase backups = {nullptr, nullptr}; const bool is_bone_selection_relevant = !selected_bone_names.is_empty(); BoneNameSet backed_up_bone_names; /* Make a backup of the given pose channel. */ auto store_animated_pchans = [&](FCurve * /* unused */, const char *bone_name) { if (backed_up_bone_names.contains(bone_name)) { /* Only backup each bone once. */ return; } bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, bone_name); if (pchan == nullptr) { /* FCurve targets non-existent bone. */ return; } if (is_bone_selection_relevant && !selected_bone_names.contains(bone_name)) { return; } PoseChannelBackup *chan_bak = static_cast( MEM_callocN(sizeof(*chan_bak), "PoseChannelBackup")); chan_bak->pchan = pchan; memcpy(&chan_bak->olddata, chan_bak->pchan, sizeof(chan_bak->olddata)); if (pchan->prop) { chan_bak->oldprops = IDP_CopyProperty(pchan->prop); } BLI_addtail(&backups, chan_bak); backed_up_bone_names.add_new(bone_name); }; /* Call `store_animated_pchans()` for each FCurve that targets a bone. */ BKE_action_find_fcurves_with_bones(action, store_animated_pchans); /* PoseBackup is constructed late, so that the above loop can use stack variables. */ PoseBackup *pose_backup = static_cast(MEM_callocN(sizeof(*pose_backup), __func__)); pose_backup->is_bone_selection_relevant = is_bone_selection_relevant; pose_backup->backups = backups; return pose_backup; } PoseBackup *ED_pose_backup_create_all_bones(const Object *ob, const bAction *action) { return pose_backup_create(ob, action, BoneNameSet()); } PoseBackup *ED_pose_backup_create_selected_bones(const Object *ob, const bAction *action) { const bArmature *armature = static_cast(ob->data); const BoneNameSet selected_bone_names = BKE_armature_find_selected_bone_names(armature); return pose_backup_create(ob, action, selected_bone_names); } bool ED_pose_backup_is_selection_relevant(const struct PoseBackup *pose_backup) { return pose_backup->is_bone_selection_relevant; } void ED_pose_backup_restore(const PoseBackup *pbd) { LISTBASE_FOREACH (PoseChannelBackup *, chan_bak, &pbd->backups) { memcpy(chan_bak->pchan, &chan_bak->olddata, sizeof(chan_bak->olddata)); if (chan_bak->oldprops) { IDP_SyncGroupValues(chan_bak->pchan->prop, chan_bak->oldprops); } /* TODO: constraints settings aren't restored yet, * even though these could change (though not that likely) */ } } void ED_pose_backup_free(PoseBackup *pbd) { LISTBASE_FOREACH_MUTABLE (PoseChannelBackup *, chan_bak, &pbd->backups) { if (chan_bak->oldprops) { IDP_FreeProperty(chan_bak->oldprops); } BLI_freelinkN(&pbd->backups, chan_bak); } MEM_freeN(pbd); }