Blender V4.3
pose_utils.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2009 Blender Authors, Joshua Leung.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "MEM_guardedalloc.h"
10
11#include "BLI_blenlib.h"
12#include "BLI_math_rotation.h"
13#include "BLI_math_vector.h"
14
15#include "DNA_anim_types.h"
16#include "DNA_armature_types.h"
17#include "DNA_object_types.h"
18
19#include "BKE_action.hh"
20#include "BKE_anim_data.hh"
21#include "BKE_idprop.hh"
22#include "BKE_layer.hh"
23#include "BKE_object.hh"
24
25#include "BKE_context.hh"
26
27#include "DEG_depsgraph.hh"
28
29#include "RNA_access.hh"
30#include "RNA_path.hh"
31#include "RNA_prototypes.hh"
32
33#include "WM_api.hh"
34#include "WM_types.hh"
35
36#include "ED_armature.hh"
37#include "ED_keyframing.hh"
38
39#include "ANIM_action.hh"
41#include "ANIM_keyframing.hh"
42#include "ANIM_keyingsets.hh"
43
44#include "armature_intern.hh"
45
46/* *********************************************** */
47/* Contents of this File:
48 *
49 * This file contains methods shared between Pose Slide and Pose Lib;
50 * primarily the functions in question concern Animato <-> Pose
51 * convenience functions, such as applying/getting pose values
52 * and/or inserting keyframes for these.
53 */
54/* *********************************************** */
55/* FCurves <-> PoseChannels Links */
56
62 ACT_TRANS_LOC = (1 << 0),
63 ACT_TRANS_ROT = (1 << 1),
64 ACT_TRANS_SCALE = (1 << 2),
65
66 /* BBone shape - for all the parameters, provided one is set. */
67 ACT_TRANS_BBONE = (1 << 3),
68 ACT_TRANS_PROP = (1 << 4),
69
73
75 bPoseChannel &pchan,
76 ListBase &r_curves)
77{
78 if (!ob.adt || !ob.adt->action) {
79 return eAction_TransformFlags(0);
80 }
81 blender::animrig::Action &action = ob.adt->action->wrap();
82
83 short flags = 0;
84
85 /* Build PointerRNA from provided data to obtain the paths to use. */
86 PointerRNA ptr = RNA_pointer_create((ID *)&ob, &RNA_PoseBone, &pchan);
87
88 /* Get the basic path to the properties of interest. */
89 const std::optional<std::string> basePath = RNA_path_from_ID_to_struct(&ptr);
90 if (!basePath) {
91 return eAction_TransformFlags(0);
92 }
93
94 /* Search F-Curves for the given properties
95 * - we cannot use the groups, since they may not be grouped in that way...
96 */
98 action, ob.adt->slot_handle, [&](FCurve &fcurve) {
99 const char *bPtr = nullptr, *pPtr = nullptr;
100
101 if (fcurve.rna_path == nullptr) {
102 return;
103 }
104
105 /* Step 1: check for matching base path */
106 bPtr = strstr(fcurve.rna_path, basePath->c_str());
107
108 if (!bPtr) {
109 return;
110 }
111
112 /* We must add `len(basePath)` bytes to the match so that we are at the end of the
113 * base path so that we don't get false positives with these strings in the names
114 */
115 bPtr += strlen(basePath->c_str());
116
117 /* Step 2: check for some property with transforms
118 * - once a match has been found, the curve cannot possibly be any other one
119 */
120 pPtr = strstr(bPtr, "location");
121 if (pPtr) {
122 flags |= ACT_TRANS_LOC;
123
124 BLI_addtail(&r_curves, BLI_genericNodeN(&fcurve));
125 return;
126 }
127
128 pPtr = strstr(bPtr, "scale");
129 if (pPtr) {
130 flags |= ACT_TRANS_SCALE;
131
132 BLI_addtail(&r_curves, BLI_genericNodeN(&fcurve));
133 return;
134 }
135
136 pPtr = strstr(bPtr, "rotation");
137 if (pPtr) {
138 flags |= ACT_TRANS_ROT;
139
140 BLI_addtail(&r_curves, BLI_genericNodeN(&fcurve));
141 return;
142 }
143
144 pPtr = strstr(bPtr, "bbone_");
145 if (pPtr) {
146 flags |= ACT_TRANS_BBONE;
147
148 BLI_addtail(&r_curves, BLI_genericNodeN(&fcurve));
149 return;
150 }
151
152 /* Custom properties only. */
153 pPtr = strstr(bPtr, "[\"");
154 if (pPtr) {
155 flags |= ACT_TRANS_PROP;
156
157 BLI_addtail(&r_curves, BLI_genericNodeN(&fcurve));
158 return;
159 }
160 });
161
162 /* return flags found */
163 return eAction_TransformFlags(flags);
164}
165
166/* helper for poseAnim_mapping_get() -> get the relevant F-Curves per PoseChannel */
167static void fcurves_to_pchan_links_get(ListBase &pfLinks, Object &ob, bPoseChannel &pchan)
168{
169 ListBase curves = {nullptr, nullptr};
171 ob, pchan, curves);
172
174
175 if (!transFlags) {
176 return;
177 }
178
179 tPChanFCurveLink *pfl = static_cast<tPChanFCurveLink *>(
180 MEM_callocN(sizeof(tPChanFCurveLink), "tPChanFCurveLink"));
181
182 pfl->ob = &ob;
183 pfl->fcurves = curves;
184 pfl->pchan = &pchan;
185
186 /* Get the RNA path to this pchan - this needs to be freed! */
187 PointerRNA ptr = RNA_pointer_create((ID *)&ob, &RNA_PoseBone, &pchan);
188 pfl->pchan_path = BLI_strdup(RNA_path_from_ID_to_struct(&ptr).value_or("").c_str());
189
190 BLI_addtail(&pfLinks, pfl);
191
192 /* Set pchan's transform flags. */
193 if (transFlags & ACT_TRANS_LOC) {
194 pchan.flag |= POSE_LOC;
195 }
196 if (transFlags & ACT_TRANS_ROT) {
197 pchan.flag |= POSE_ROT;
198 }
199 if (transFlags & ACT_TRANS_SCALE) {
200 pchan.flag |= POSE_SIZE;
201 }
202 if (transFlags & ACT_TRANS_BBONE) {
203 pchan.flag |= POSE_BBONE_SHAPE;
204 }
205
206 copy_v3_v3(pfl->oldloc, pchan.loc);
207 copy_v3_v3(pfl->oldrot, pchan.eul);
208 copy_v3_v3(pfl->oldscale, pchan.size);
209 copy_qt_qt(pfl->oldquat, pchan.quat);
210 copy_v3_v3(pfl->oldaxis, pchan.rotAxis);
211 pfl->oldangle = pchan.rotAngle;
212
213 /* Store current bbone values. */
214 pfl->roll1 = pchan.roll1;
215 pfl->roll2 = pchan.roll2;
216 pfl->curve_in_x = pchan.curve_in_x;
217 pfl->curve_in_z = pchan.curve_in_z;
218 pfl->curve_out_x = pchan.curve_out_x;
219 pfl->curve_out_z = pchan.curve_out_z;
220 pfl->ease1 = pchan.ease1;
221 pfl->ease2 = pchan.ease2;
222
223 copy_v3_v3(pfl->scale_in, pchan.scale_in);
224 copy_v3_v3(pfl->scale_out, pchan.scale_out);
225
226 /* Make copy of custom properties. */
227 if (pchan.prop && (transFlags & ACT_TRANS_PROP)) {
228 pfl->oldprops = IDP_CopyProperty(pchan.prop);
229 }
230}
231
233{
235 if (!ELEM(nullptr, ob, ob->data, ob->adt, ob->adt->action)) {
236 return ob;
237 }
238 return nullptr;
239}
240
242{
243 BLI_assert(pfLinks != nullptr);
244 /* For each Pose-Channel which gets affected, get the F-Curves for that channel
245 * and set the relevant transform flags...
246 */
247 Object *prev_ob, *ob_pose_armature;
248
249 prev_ob = nullptr;
250 ob_pose_armature = nullptr;
251 CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, selected_pose_bones, Object *, ob) {
252 BLI_assert(pchan != nullptr);
253 if (ob != prev_ob) {
254 prev_ob = ob;
255 ob_pose_armature = poseAnim_object_get(ob);
256 }
257
258 if (ob_pose_armature == nullptr) {
259 continue;
260 }
261 if (!ob_pose_armature->adt || !ob_pose_armature->adt->action) {
262 /* No action means no FCurves. */
263 continue;
264 }
265
266 fcurves_to_pchan_links_get(*pfLinks, *ob_pose_armature, *pchan);
267 }
269
270 /* If no PoseChannels were found, try a second pass, doing visible ones instead.
271 * i.e. if nothing selected, do whole pose.
272 */
273 if (BLI_listbase_is_empty(pfLinks)) {
274 prev_ob = nullptr;
275 ob_pose_armature = nullptr;
276 CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, visible_pose_bones, Object *, ob) {
277 BLI_assert(pchan != nullptr);
278 if (ob != prev_ob) {
279 prev_ob = ob;
280 ob_pose_armature = poseAnim_object_get(ob);
281 }
282
283 if (ob_pose_armature == nullptr) {
284 continue;
285 }
286 if (!ob_pose_armature->adt || !ob_pose_armature->adt->action) {
287 /* No action means no FCurves. */
288 continue;
289 }
290
291 fcurves_to_pchan_links_get(*pfLinks, *ob_pose_armature, *pchan);
292 }
294 }
295}
296
298{
299 tPChanFCurveLink *pfl, *pfln = nullptr;
300
301 /* free the temp pchan links and their data */
302 for (pfl = static_cast<tPChanFCurveLink *>(pfLinks->first); pfl; pfl = pfln) {
303 pfln = pfl->next;
304
305 /* free custom properties */
306 if (pfl->oldprops) {
308 }
309
310 /* free list of F-Curve reference links */
311 BLI_freelistN(&pfl->fcurves);
312
313 /* free pchan RNA Path */
314 MEM_freeN(pfl->pchan_path);
315
316 /* free link itself */
317 BLI_freelinkN(pfLinks, pfl);
318 }
319}
320
321/* ------------------------- */
322
324{
327
328 AnimData *adt = BKE_animdata_from_id(&ob->id);
329 if (adt && adt->action) {
331 }
332}
333
335{
336 /* iterate over each pose-channel affected, restoring all channels to their original values */
337 LISTBASE_FOREACH (tPChanFCurveLink *, pfl, pfLinks) {
338 bPoseChannel *pchan = pfl->pchan;
339
340 /* just copy all the values over regardless of whether they changed or not */
341 copy_v3_v3(pchan->loc, pfl->oldloc);
342 copy_v3_v3(pchan->eul, pfl->oldrot);
343 copy_v3_v3(pchan->size, pfl->oldscale);
344 copy_qt_qt(pchan->quat, pfl->oldquat);
345 copy_v3_v3(pchan->rotAxis, pfl->oldaxis);
346 pchan->rotAngle = pfl->oldangle;
347
348 /* store current bbone values */
349 pchan->roll1 = pfl->roll1;
350 pchan->roll2 = pfl->roll2;
351 pchan->curve_in_x = pfl->curve_in_x;
352 pchan->curve_in_z = pfl->curve_in_z;
353 pchan->curve_out_x = pfl->curve_out_x;
354 pchan->curve_out_z = pfl->curve_out_z;
355 pchan->ease1 = pfl->ease1;
356 pchan->ease2 = pfl->ease2;
357
358 copy_v3_v3(pchan->scale_in, pfl->scale_in);
359 copy_v3_v3(pchan->scale_out, pfl->scale_out);
360
361 /* just overwrite values of properties from the stored copies (there should be some) */
362 if (pfl->oldprops) {
363 IDP_SyncGroupValues(pfl->pchan->prop, pfl->oldprops);
364 }
365 }
366}
367
368void poseAnim_mapping_autoKeyframe(bContext *C, Scene *scene, ListBase *pfLinks, float cframe)
369{
370 ViewLayer *view_layer = CTX_data_view_layer(C);
371 View3D *v3d = CTX_wm_view3d(C);
372 bool skip = true;
373
374 FOREACH_OBJECT_IN_MODE_BEGIN (scene, view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) {
375 ob->id.tag &= ~ID_TAG_DOIT;
376 ob = poseAnim_object_get(ob);
377
378 /* Ensure validity of the settings from the context. */
379 if (ob == nullptr) {
380 continue;
381 }
382
384 ob->id.tag |= ID_TAG_DOIT;
385 skip = false;
386 }
387 }
389
390 if (skip) {
391 return;
392 }
393
394 /* Insert keyframes as necessary if auto-key-framing. */
397
398 /* iterate over each pose-channel affected, tagging bones to be keyed */
399 /* XXX: here we already have the information about what transforms exist, though
400 * it might be easier to just overwrite all using normal mechanisms
401 */
402 LISTBASE_FOREACH (tPChanFCurveLink *, pfl, pfLinks) {
403 bPoseChannel *pchan = pfl->pchan;
404
405 if ((pfl->ob->id.tag & ID_TAG_DOIT) == 0) {
406 continue;
407 }
408
409 /* Add data-source override for the PoseChannel, to be used later. */
410 ANIM_relative_keyingset_add_source(sources, &pfl->ob->id, &RNA_PoseBone, pchan);
411 }
412
413 /* insert keyframes for all relevant bones in one go */
415
416 /* do the bone paths
417 * - only do this if keyframes should have been added
418 * - do not calculate unless there are paths already to update...
419 */
420 FOREACH_OBJECT_IN_MODE_BEGIN (scene, view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) {
421 if (ob->id.tag & ID_TAG_DOIT) {
422 if (ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) {
423 // ED_pose_clear_paths(C, ob); /* XXX for now, don't need to clear. */
424 /* TODO(sergey): Should ensure we can use more narrow update range here. */
426 }
427 }
428 }
430}
431
432/* ------------------------- */
433
434LinkData *poseAnim_mapping_getNextFCurve(ListBase *fcuLinks, LinkData *prev, const char *path)
435{
436 LinkData *first = static_cast<LinkData *>((prev) ? prev->next :
437 (fcuLinks) ? fcuLinks->first :
438 nullptr);
439 LinkData *ld;
440
441 /* check each link to see if the linked F-Curve has a matching path */
442 for (ld = first; ld; ld = ld->next) {
443 const FCurve *fcu = (const FCurve *)ld->data;
444
445 /* check if paths match */
446 if (STREQ(path, fcu->rna_path)) {
447 return ld;
448 }
449 }
450
451 /* none found */
452 return nullptr;
453}
454
455/* *********************************************** */
Functions and classes to work with Actions.
Functionality to iterate an Action in various ways.
Functions to insert, delete or modify keyframes.
Functionality to interact with keying sets.
Blender kernel action and pose functionality.
AnimData * BKE_animdata_from_id(const ID *id)
Definition anim_data.cc:89
#define CTX_DATA_BEGIN_WITH_ID(C, Type, instance, member, Type_id, instance_id)
#define CTX_DATA_END
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
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 FOREACH_OBJECT_IN_MODE_END
Definition BKE_layer.hh:382
#define FOREACH_OBJECT_IN_MODE_BEGIN(_scene, _view_layer, _v3d, _object_type, _object_mode, _instance)
Definition BKE_layer.hh:377
General operations, lookup, etc. for blender objects.
Object * BKE_object_pose_armature_get(Object *ob)
#define BLI_assert(a)
Definition BLI_assert.h:50
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
#define LISTBASE_FOREACH(type, var, list)
void BLI_freelinkN(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:269
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:496
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
struct LinkData * BLI_genericNodeN(void *data)
Definition listbase.cc:909
void copy_qt_qt(float q[4], const float a[4])
MINLINE void copy_v3_v3(float r[3], const float a[3])
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.c:40
#define ELEM(...)
#define STREQ(a, b)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
@ ID_RECALC_ANIMATION_NO_FLUSH
Definition DNA_ID.h:1143
@ ID_TAG_DOIT
Definition DNA_ID.h:1003
@ MOTIONPATH_BAKE_HAS_PATHS
@ POSE_BBONE_SHAPE
@ POSE_ROT
@ POSE_LOC
@ POSE_SIZE
@ OB_MODE_POSE
Object is a sort of wrapper for general info.
@ OB_ARMATURE
@ POSE_PATH_CALC_RANGE_FULL
#define ANIM_KS_WHOLE_CHARACTER_ID
Read Guarded memory(de)allocation.
#define ND_POSE
Definition WM_types.hh:425
#define NC_OBJECT
Definition WM_types.hh:346
void ANIM_relative_keyingset_add_source(blender::Vector< PointerRNA > &sources, ID *id, StructRNA *srna, void *data)
KeyingSet * ANIM_get_keyingset_for_autokeying(const Scene *scene, const char *transformKSName)
int ANIM_apply_keyingset(bContext *C, blender::Vector< PointerRNA > *sources, KeyingSet *keyingset, const blender::animrig::ModifyKeyMode mode, const float cfra)
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
void foreach_fcurve_in_action_slot(Action &action, slot_handle_t handle, FunctionRef< void(FCurve &fcurve)> callback)
bool autokeyframe_cfra_can_key(const Scene *scene, ID *id)
void ED_pose_recalculate_paths(bContext *C, Scene *scene, Object *ob, ePosePathCalcRange range)
Definition pose_edit.cc:152
void poseAnim_mapping_autoKeyframe(bContext *C, Scene *scene, ListBase *pfLinks, float cframe)
eAction_TransformFlags
Definition pose_utils.cc:61
@ ACT_TRANS_ALL
Definition pose_utils.cc:71
@ ACT_TRANS_PROP
Definition pose_utils.cc:68
@ ACT_TRANS_SCALE
Definition pose_utils.cc:64
@ ACT_TRANS_BBONE
Definition pose_utils.cc:67
@ ACT_TRANS_ROT
Definition pose_utils.cc:63
@ ACT_TRANS_LOC
Definition pose_utils.cc:62
@ ACT_TRANS_ONLY
Definition pose_utils.cc:70
static void fcurves_to_pchan_links_get(ListBase &pfLinks, Object &ob, bPoseChannel &pchan)
void poseAnim_mapping_free(ListBase *pfLinks)
void poseAnim_mapping_reset(ListBase *pfLinks)
void poseAnim_mapping_get(bContext *C, ListBase *pfLinks)
Object * poseAnim_object_get(Object *ob_)
void poseAnim_mapping_refresh(bContext *C, Scene *, Object *ob)
static eAction_TransformFlags get_item_transform_flags_and_fcurves(Object &ob, bPoseChannel &pchan, ListBase &r_curves)
Definition pose_utils.cc:74
LinkData * poseAnim_mapping_getNextFCurve(ListBase *fcuLinks, LinkData *prev, const char *path)
PointerRNA RNA_pointer_create(ID *id, StructRNA *type, void *data)
std::optional< std::string > RNA_path_from_ID_to_struct(const PointerRNA *ptr)
Definition rna_path.cc:1007
bAction * action
int32_t slot_handle
char * rna_path
Definition DNA_ID.h:413
void * data
struct LinkData * next
void * first
struct AnimData * adt
IDProperty * prop
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4126