Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/armature/pose_backup.cc
- This file was added.
| /* | |||||
| * 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 <cstring> | |||||
| #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<PoseChannelBackup *>( | |||||
| 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<PoseBackup *>(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<const bArmature *>(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); | |||||
| } | |||||