Blender V4.3
pose_backup.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "BKE_pose_backup.h"
10
11#include <cstring>
12
13#include "BLI_listbase.h"
14
15#include "MEM_guardedalloc.h"
16
17#include "DNA_action_types.h"
18#include "DNA_armature_types.h"
19#include "DNA_object_types.h"
20
21#include "BKE_action.hh"
22#include "BKE_armature.hh"
23#include "BKE_idprop.hh"
24#include "BKE_object_types.hh"
25
26using namespace blender::bke;
27
28/* simple struct for storing backup info for one pose channel */
31
32 bPoseChannel *pchan; /* Pose channel this backup is for. */
33
34 bPoseChannel olddata; /* Backup of pose channel. */
35 IDProperty *oldprops; /* Backup copy (needs freeing) of pose channel's ID properties. */
36};
37
38struct PoseBackup {
40 ListBase /*PoseChannelBackup*/ backups;
41};
42
52 const bAction *action,
53 const BoneNameSet &selected_bone_names)
54{
55 ListBase backups = {nullptr, nullptr};
56 const bool is_bone_selection_relevant = !selected_bone_names.is_empty();
57
58 BoneNameSet backed_up_bone_names;
59 /* Make a backup of the given pose channel. */
60 auto store_animated_pchans = [&](FCurve * /*unused*/, const char *bone_name) {
61 if (backed_up_bone_names.contains(bone_name)) {
62 /* Only backup each bone once. */
63 return;
64 }
65
66 bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, bone_name);
67 if (pchan == nullptr) {
68 /* FCurve targets non-existent bone. */
69 return;
70 }
71
72 if (is_bone_selection_relevant && !selected_bone_names.contains(bone_name)) {
73 return;
74 }
75
76 PoseChannelBackup *chan_bak = static_cast<PoseChannelBackup *>(
77 MEM_callocN(sizeof(*chan_bak), "PoseChannelBackup"));
78 chan_bak->pchan = pchan;
79 chan_bak->olddata = blender::dna::shallow_copy(*chan_bak->pchan);
80
81 if (pchan->prop) {
82 chan_bak->oldprops = IDP_CopyProperty(pchan->prop);
83 }
84
85 BLI_addtail(&backups, chan_bak);
86 backed_up_bone_names.add_new(bone_name);
87 };
88
89 /* Call `store_animated_pchans()` for each FCurve that targets a bone. */
90 BKE_action_find_fcurves_with_bones(action, store_animated_pchans);
91
92 /* PoseBackup is constructed late, so that the above loop can use stack variables. */
93 PoseBackup *pose_backup = static_cast<PoseBackup *>(MEM_callocN(sizeof(*pose_backup), __func__));
94 pose_backup->is_bone_selection_relevant = is_bone_selection_relevant;
95 pose_backup->backups = backups;
96 return pose_backup;
97}
98
100{
101 return pose_backup_create(ob, action, BoneNameSet());
102}
103
105{
106 const bArmature *armature = static_cast<const bArmature *>(ob->data);
107 const BoneNameSet selected_bone_names = BKE_armature_find_selected_bone_names(armature);
108 return pose_backup_create(ob, action, selected_bone_names);
109}
110
112{
113 return pose_backup->is_bone_selection_relevant;
114}
115
117{
118 LISTBASE_FOREACH (PoseChannelBackup *, chan_bak, &pbd->backups) {
119 *chan_bak->pchan = blender::dna::shallow_copy(chan_bak->olddata);
120
121 if (chan_bak->oldprops) {
122 IDP_SyncGroupValues(chan_bak->pchan->prop, chan_bak->oldprops);
123 }
124
125 /* TODO: constraints settings aren't restored yet,
126 * even though these could change (though not that likely) */
127 }
128}
129
131{
133 if (chan_bak->oldprops) {
134 IDP_FreeProperty(chan_bak->oldprops);
135 }
136 BLI_freelinkN(&pbd->backups, chan_bak);
137 }
138 MEM_freeN(pbd);
139}
140
142{
144 PoseBackup *pose_backup = BKE_pose_backup_create_all_bones(ob, action);
145 ob->runtime->pose_backup = pose_backup;
146}
147
149{
150 if (ob->runtime->pose_backup == nullptr) {
151 return false;
152 }
153 BKE_pose_backup_restore(ob->runtime->pose_backup);
154 return true;
155}
156
158{
159 if (ob->runtime->pose_backup == nullptr) {
160 return;
161 }
162
163 BKE_pose_backup_free(ob->runtime->pose_backup);
164 ob->runtime->pose_backup = nullptr;
165}
Blender kernel action and pose functionality.
bPoseChannel * BKE_pose_channel_find_name(const bPose *pose, const char *name)
void IDP_FreeProperty(IDProperty *prop)
Definition idprop.cc:1227
IDProperty * IDP_CopyProperty(const IDProperty *prop) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition idprop.cc:861
void IDP_SyncGroupValues(IDProperty *dest, const IDProperty *src) ATTR_NONNULL()
Definition idprop.cc:578
#define LISTBASE_FOREACH(type, var, list)
void BLI_freelinkN(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:269
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
Object is a sort of wrapper for general info.
Read Guarded memory(de)allocation.
bool contains(const Key &key) const
Definition BLI_set.hh:291
bool is_empty() const
Definition BLI_set.hh:572
void add_new(const Key &key)
Definition BLI_set.hh:233
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
void BKE_action_find_fcurves_with_bones(const bAction *action, FoundFCurveCallback callback)
BoneNameSet BKE_armature_find_selected_bone_names(const bArmature *armature)
blender::Set< std::string > BoneNameSet
void BKE_pose_backup_clear(Object *ob)
PoseBackup * BKE_pose_backup_create_selected_bones(const Object *ob, const bAction *action)
bool BKE_pose_backup_restore_on_object(Object *ob)
bool BKE_pose_backup_is_selection_relevant(const PoseBackup *pose_backup)
void BKE_pose_backup_restore(const PoseBackup *pbd)
static PoseBackup * pose_backup_create(const Object *ob, const bAction *action, const BoneNameSet &selected_bone_names)
void BKE_pose_backup_free(PoseBackup *pbd)
PoseBackup * BKE_pose_backup_create_all_bones(const Object *ob, const bAction *action)
void BKE_pose_backup_create_on_object(Object *ob, const bAction *action)
struct bPose * pose
ObjectRuntimeHandle * runtime
bool is_bone_selection_relevant
ListBase backups
bPoseChannel olddata
IDProperty * oldprops
PoseChannelBackup * prev
PoseChannelBackup * next
bPoseChannel * pchan
IDProperty * prop