Blender V5.0
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
8
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
26#include "ANIM_action.hh"
27#include "ANIM_pose.hh"
28
29using namespace blender::bke;
30
31/* simple struct for storing backup info for one pose channel */
34
35 bPoseChannel *pchan; /* Pose channel this backup is for. */
36
37 bPoseChannel olddata; /* Backup of pose channel. */
38 IDProperty *oldprops; /* Backup copy (needs freeing) of pose channel's ID properties. */
39 /* Backup copy (needs freeing) of pose channel's system IDProperties. */
41 const Object *owner; /* The object to which this pose channel belongs. */
42};
43
44struct PoseBackup {
46 ListBase /*PoseChannelBackup*/ backups;
47};
48
57static void pose_backup_create(const Object *ob,
58 bAction *action,
59 const BoneNameSet &selected_bone_names,
60 PoseBackup &pose_backup)
61{
62 BoneNameSet backed_up_bone_names;
63 const bool is_bone_selection_relevant = pose_backup.is_bone_selection_relevant;
64 /* Make a backup of the given pose channel. */
65 auto store_animated_pchans = [&](const FCurve * /*unused*/, const char *bone_name) {
66 if (backed_up_bone_names.contains(bone_name)) {
67 /* Only backup each bone once. */
68 return;
69 }
70
71 bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, bone_name);
72 if (pchan == nullptr) {
73 /* FCurve targets non-existent bone. */
74 return;
75 }
76
77 if (is_bone_selection_relevant && !selected_bone_names.contains(bone_name)) {
78 return;
79 }
80
81 PoseChannelBackup *chan_bak = MEM_callocN<PoseChannelBackup>("PoseChannelBackup");
82 chan_bak->pchan = pchan;
83 chan_bak->olddata = blender::dna::shallow_copy(*chan_bak->pchan);
84 chan_bak->owner = ob;
85
86 if (pchan->prop) {
87 chan_bak->oldprops = IDP_CopyProperty(pchan->prop);
88 }
89 if (pchan->system_properties) {
91 }
92
93 BLI_addtail(&pose_backup.backups, chan_bak);
94 backed_up_bone_names.add_new(bone_name);
95 };
96
98 action->wrap());
99 /* Call `store_animated_pchans()` for each FCurve that targets a bone. */
100 BKE_action_find_fcurves_with_bones(action, slot.handle, store_animated_pchans);
101}
102
105{
106 blender::Set<bPoseChannel *> selected_bones;
107 bool all_bones_selected = true;
108
109 for (Object *obj : objects) {
110 /* Iterate over the selected bones to fill the set of bone names. */
111 LISTBASE_FOREACH (bPoseChannel *, pose_bone, &obj->pose->chanbase) {
112 if (pose_bone->flag & POSE_SELECTED) {
113 selected_bones.add(pose_bone);
114 }
115 else {
116 all_bones_selected = false;
117 }
118 }
119 }
120
121 /* If all bones are selected, act as if none are. */
122 if (all_bones_selected) {
123 return {};
124 }
125
126 return selected_bones;
127}
128
130 const bAction *action)
131{
132 PoseBackup *pose_backup = MEM_callocN<PoseBackup>(__func__);
133 pose_backup->backups = {nullptr, nullptr};
134 pose_backup->is_bone_selection_relevant = false;
135 for (Object *ob : objects) {
136 pose_backup_create(ob, const_cast<bAction *>(action), BoneNameSet(), *pose_backup);
137 }
138 return pose_backup;
139}
140
142 const bAction *action)
143{
144 PoseBackup *pose_backup = MEM_callocN<PoseBackup>(__func__);
145 pose_backup->backups = {nullptr, nullptr};
147 pose_backup->is_bone_selection_relevant = !selected_bones.is_empty();
148
149 for (Object *ob : objects) {
150 const BoneNameSet selected_bone_names = BKE_pose_channel_find_selected_names(ob);
151 pose_backup_create(ob, const_cast<bAction *>(action), selected_bone_names, *pose_backup);
152 }
153
154 return pose_backup;
155}
156
158{
159 return pose_backup->is_bone_selection_relevant;
160}
161
163{
164 LISTBASE_FOREACH (PoseChannelBackup *, chan_bak, &pbd->backups) {
165 *chan_bak->pchan = blender::dna::shallow_copy(chan_bak->olddata);
166
167 if (chan_bak->oldprops) {
168 IDP_SyncGroupValues(chan_bak->pchan->prop, chan_bak->oldprops);
169 }
170 if (chan_bak->old_system_properties) {
171 IDP_SyncGroupValues(chan_bak->pchan->system_properties, chan_bak->old_system_properties);
172 }
173
174 /* TODO: constraints settings aren't restored yet,
175 * even though these could change (though not that likely) */
176 }
177}
178
180{
181 if (!pbd) {
182 /* Can happen if initialization was aborted. */
183 return;
184 }
186 if (chan_bak->oldprops) {
187 IDP_FreeProperty(chan_bak->oldprops);
188 }
189 if (chan_bak->old_system_properties) {
190 IDP_FreeProperty(chan_bak->old_system_properties);
191 }
192 BLI_freelinkN(&pbd->backups, chan_bak);
193 }
194 MEM_freeN(pbd);
195}
196
198{
200 PoseBackup *pose_backup = BKE_pose_backup_create_all_bones({ob}, action);
201 ob->runtime->pose_backup = pose_backup;
202}
203
205{
206 if (ob->runtime->pose_backup == nullptr) {
207 return false;
208 }
209 BKE_pose_backup_restore(ob->runtime->pose_backup);
210 return true;
211}
212
214{
215 if (ob->runtime->pose_backup == nullptr) {
216 return;
217 }
218
219 BKE_pose_backup_free(ob->runtime->pose_backup);
220 ob->runtime->pose_backup = nullptr;
221}
Functions and classes to work with Actions.
Functions to work with animation poses.
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:1251
IDProperty * IDP_CopyProperty(const IDProperty *prop) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition idprop.cc:863
void IDP_SyncGroupValues(IDProperty *dest, const IDProperty *src) ATTR_NONNULL()
Definition idprop.cc:585
#define LISTBASE_FOREACH(type, var, list)
void BLI_freelinkN(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:270
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
@ POSE_SELECTED
Object is a sort of wrapper for general info.
Read Guarded memory(de)allocation.
bool contains(const Key &key) const
Definition BLI_set.hh:310
bool add(const Key &key)
Definition BLI_set.hh:248
bool is_empty() const
Definition BLI_set.hh:595
void add_new(const Key &key)
Definition BLI_set.hh:233
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
Slot & get_best_pose_slot_for_id(const ID &id, Action &pose_data)
Definition pose.cc:160
void BKE_action_find_fcurves_with_bones(bAction *action, blender::animrig::slot_handle_t slot_handle, FoundFCurveCallback callback)
blender::Set< std::string > BoneNameSet
BoneNameSet BKE_pose_channel_find_selected_names(const Object *object)
void BKE_pose_backup_clear(Object *ob)
bool BKE_pose_backup_restore_on_object(Object *ob)
PoseBackup * BKE_pose_backup_create_all_bones(blender::Span< Object * > objects, const bAction *action)
bool BKE_pose_backup_is_selection_relevant(const PoseBackup *pose_backup)
void BKE_pose_backup_restore(const PoseBackup *pbd)
static void pose_backup_create(const Object *ob, bAction *action, const BoneNameSet &selected_bone_names, PoseBackup &pose_backup)
void BKE_pose_backup_free(PoseBackup *pbd)
PoseBackup * BKE_pose_backup_create_selected_bones(blender::Span< Object * > objects, const bAction *action)
static blender::Set< bPoseChannel * > armature_find_selected_pose_bones(blender::Span< Object * > objects)
void BKE_pose_backup_create_on_object(Object *ob, const bAction *action)
struct bPose * pose
ObjectRuntimeHandle * runtime
bool is_bone_selection_relevant
ListBase backups
const Object * owner
bPoseChannel olddata
IDProperty * old_system_properties
IDProperty * oldprops
PoseChannelBackup * prev
PoseChannelBackup * next
bPoseChannel * pchan
IDProperty * prop
IDProperty * system_properties