Blender V5.0
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
8
9#include "MEM_guardedalloc.h"
10
11#include "BLI_listbase.h"
12#include "BLI_math_rotation.h"
13#include "BLI_math_vector.h"
14#include "BLI_string.h"
15
16#include "DNA_anim_types.h"
17#include "DNA_armature_types.h"
18#include "DNA_object_types.h"
19
20#include "BKE_action.hh"
21#include "BKE_anim_data.hh"
22#include "BKE_idprop.hh"
23#include "BKE_layer.hh"
24#include "BKE_object.hh"
25
26#include "BKE_context.hh"
27
28#include "DEG_depsgraph.hh"
29
30#include "RNA_access.hh"
31#include "RNA_path.hh"
32#include "RNA_prototypes.hh"
33
34#include "WM_api.hh"
35#include "WM_types.hh"
36
37#include "ED_armature.hh"
38#include "ED_keyframing.hh"
39
40#include "ANIM_action.hh"
42#include "ANIM_keyframing.hh"
43#include "ANIM_keyingsets.hh"
44
45#include "armature_intern.hh"
46
47/* *********************************************** */
48/* Contents of this File:
49 *
50 * This file contains methods shared between Pose Slide and Pose Lib;
51 * primarily the functions in question concern Animato <-> Pose
52 * convenience functions, such as applying/getting pose values
53 * and/or inserting keyframes for these.
54 */
55/* *********************************************** */
56/* FCurves <-> PoseChannels Links */
57
63 ACT_TRANS_LOC = (1 << 0),
64 ACT_TRANS_ROT = (1 << 1),
65 ACT_TRANS_SCALE = (1 << 2),
66
67 /* BBone shape - for all the parameters, provided one is set. */
68 ACT_TRANS_BBONE = (1 << 3),
69 ACT_TRANS_PROP = (1 << 4),
70
73};
74
76 bPoseChannel &pchan,
77 ListBase &r_curves)
78{
79 if (!ob.adt || !ob.adt->action) {
80 return eAction_TransformFlags(0);
81 }
82 blender::animrig::Action &action = ob.adt->action->wrap();
83
84 short flags = 0;
85
86 /* Build PointerRNA from provided data to obtain the paths to use. */
87 PointerRNA ptr = RNA_pointer_create_discrete(reinterpret_cast<ID *>(&ob), &RNA_PoseBone, &pchan);
88
89 /* Get the basic path to the properties of interest. */
90 const std::optional<std::string> basePath = RNA_path_from_ID_to_struct(&ptr);
91 if (!basePath) {
92 return eAction_TransformFlags(0);
93 }
94
95 /* Search F-Curves for the given properties
96 * - we cannot use the groups, since they may not be grouped in that way...
97 */
99 action, ob.adt->slot_handle, [&](FCurve &fcurve) {
100 const char *bPtr = nullptr, *pPtr = nullptr;
101
102 if (fcurve.rna_path == nullptr) {
103 return;
104 }
105
106 /* Step 1: check for matching base path */
107 bPtr = strstr(fcurve.rna_path, basePath->c_str());
108
109 if (!bPtr) {
110 return;
111 }
112
113 /* We must add `len(basePath)` bytes to the match so that we are at the end of the
114 * base path so that we don't get false positives with these strings in the names
115 */
116 bPtr += strlen(basePath->c_str());
117
118 /* Step 2: check for some property with transforms
119 * - once a match has been found, the curve cannot possibly be any other one
120 */
121 pPtr = strstr(bPtr, "location");
122 if (pPtr) {
123 flags |= ACT_TRANS_LOC;
124
125 BLI_addtail(&r_curves, BLI_genericNodeN(&fcurve));
126 return;
127 }
128
129 pPtr = strstr(bPtr, "scale");
130 if (pPtr) {
131 flags |= ACT_TRANS_SCALE;
132
133 BLI_addtail(&r_curves, BLI_genericNodeN(&fcurve));
134 return;
135 }
136
137 pPtr = strstr(bPtr, "rotation");
138 if (pPtr) {
139 flags |= ACT_TRANS_ROT;
140
141 BLI_addtail(&r_curves, BLI_genericNodeN(&fcurve));
142 return;
143 }
144
145 pPtr = strstr(bPtr, "bbone_");
146 if (pPtr) {
147 flags |= ACT_TRANS_BBONE;
148
149 BLI_addtail(&r_curves, BLI_genericNodeN(&fcurve));
150 return;
151 }
152
153 /* Custom properties only. */
154 pPtr = strstr(bPtr, "[\"");
155 if (pPtr) {
156 flags |= ACT_TRANS_PROP;
157
158 BLI_addtail(&r_curves, BLI_genericNodeN(&fcurve));
159 return;
160 }
161 });
162
163 /* return flags found */
164 return eAction_TransformFlags(flags);
165}
166
167/* helper for poseAnim_mapping_get() -> get the relevant F-Curves per PoseChannel */
168static void fcurves_to_pchan_links_get(ListBase &pfLinks, Object &ob, bPoseChannel &pchan)
169{
170 ListBase curves = {nullptr, nullptr};
172 ob, pchan, curves);
173
175
176 if (!transFlags) {
177 return;
178 }
179
180 tPChanFCurveLink *pfl = MEM_callocN<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_discrete(reinterpret_cast<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_SCALE;
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.scale);
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 (transFlags & ACT_TRANS_PROP) {
228 if (pchan.prop) {
229 pfl->oldprops = IDP_CopyProperty(pchan.prop);
230 }
231 if (pchan.system_properties) {
233 }
234 }
235}
236
238{
240 if (!ELEM(nullptr, ob, ob->data, ob->adt, ob->adt->action)) {
241 return ob;
242 }
243 return nullptr;
244}
245
247{
248 BLI_assert(pfLinks != nullptr);
249 /* For each Pose-Channel which gets affected, get the F-Curves for that channel
250 * and set the relevant transform flags...
251 */
252 Object *prev_ob, *ob_pose_armature;
253
254 prev_ob = nullptr;
255 ob_pose_armature = nullptr;
256 CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, selected_pose_bones, Object *, ob) {
257 BLI_assert(pchan != nullptr);
258 if (ob != prev_ob) {
259 prev_ob = ob;
260 ob_pose_armature = poseAnim_object_get(ob);
261 }
262
263 if (ob_pose_armature == nullptr) {
264 continue;
265 }
266 if (!ob_pose_armature->adt || !ob_pose_armature->adt->action) {
267 /* No action means no FCurves. */
268 continue;
269 }
270
271 fcurves_to_pchan_links_get(*pfLinks, *ob_pose_armature, *pchan);
272 }
274
275 /* If no PoseChannels were found, try a second pass, doing visible ones instead.
276 * i.e. if nothing selected, do whole pose.
277 */
278 if (BLI_listbase_is_empty(pfLinks)) {
279 prev_ob = nullptr;
280 ob_pose_armature = nullptr;
281 CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, visible_pose_bones, Object *, ob) {
282 BLI_assert(pchan != nullptr);
283 if (ob != prev_ob) {
284 prev_ob = ob;
285 ob_pose_armature = poseAnim_object_get(ob);
286 }
287
288 if (ob_pose_armature == nullptr) {
289 continue;
290 }
291 if (!ob_pose_armature->adt || !ob_pose_armature->adt->action) {
292 /* No action means no FCurves. */
293 continue;
294 }
295
296 fcurves_to_pchan_links_get(*pfLinks, *ob_pose_armature, *pchan);
297 }
299 }
300}
301
303{
304 tPChanFCurveLink *pfl, *pfln = nullptr;
305
306 /* free the temp pchan links and their data */
307 for (pfl = static_cast<tPChanFCurveLink *>(pfLinks->first); pfl; pfl = pfln) {
308 pfln = pfl->next;
309
310 /* free custom properties */
311 if (pfl->oldprops) {
313 }
314
315 /* free list of F-Curve reference links */
316 BLI_freelistN(&pfl->fcurves);
317
318 /* free pchan RNA Path */
319 MEM_freeN(pfl->pchan_path);
320
321 /* free link itself */
322 BLI_freelinkN(pfLinks, pfl);
323 }
324}
325
326/* ------------------------- */
327
329{
332
333 AnimData *adt = BKE_animdata_from_id(&ob->id);
334 if (adt && adt->action) {
336 }
337}
338
340{
341 /* iterate over each pose-channel affected, restoring all channels to their original values */
342 LISTBASE_FOREACH (tPChanFCurveLink *, pfl, pfLinks) {
343 bPoseChannel *pchan = pfl->pchan;
344
345 /* just copy all the values over regardless of whether they changed or not */
346 copy_v3_v3(pchan->loc, pfl->oldloc);
347 copy_v3_v3(pchan->eul, pfl->oldrot);
348 copy_v3_v3(pchan->scale, pfl->oldscale);
349 copy_qt_qt(pchan->quat, pfl->oldquat);
350 copy_v3_v3(pchan->rotAxis, pfl->oldaxis);
351 pchan->rotAngle = pfl->oldangle;
352
353 /* store current bbone values */
354 pchan->roll1 = pfl->roll1;
355 pchan->roll2 = pfl->roll2;
356 pchan->curve_in_x = pfl->curve_in_x;
357 pchan->curve_in_z = pfl->curve_in_z;
358 pchan->curve_out_x = pfl->curve_out_x;
359 pchan->curve_out_z = pfl->curve_out_z;
360 pchan->ease1 = pfl->ease1;
361 pchan->ease2 = pfl->ease2;
362
363 copy_v3_v3(pchan->scale_in, pfl->scale_in);
364 copy_v3_v3(pchan->scale_out, pfl->scale_out);
365
366 /* just overwrite values of properties from the stored copies (there should be some) */
367 if (pfl->oldprops) {
368 IDP_SyncGroupValues(pfl->pchan->prop, pfl->oldprops);
369 }
370 if (pfl->old_system_properties) {
371 IDP_SyncGroupValues(pfl->pchan->system_properties, pfl->old_system_properties);
372 }
373 }
374}
375
376void poseAnim_mapping_autoKeyframe(bContext *C, Scene *scene, ListBase *pfLinks, float cframe)
377{
378 ViewLayer *view_layer = CTX_data_view_layer(C);
379 View3D *v3d = CTX_wm_view3d(C);
380 bool skip = true;
381
382 FOREACH_OBJECT_IN_MODE_BEGIN (scene, view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) {
383 ob->id.tag &= ~ID_TAG_DOIT;
384 ob = poseAnim_object_get(ob);
385
386 /* Ensure validity of the settings from the context. */
387 if (ob == nullptr) {
388 continue;
389 }
390
392 ob->id.tag |= ID_TAG_DOIT;
393 skip = false;
394 }
395 }
397
398 if (skip) {
399 return;
400 }
401
402 /* Insert keyframes as necessary if auto-key-framing. */
406
407 /* iterate over each pose-channel affected, tagging bones to be keyed */
408 /* XXX: here we already have the information about what transforms exist, though
409 * it might be easier to just overwrite all using normal mechanisms
410 */
411 LISTBASE_FOREACH (tPChanFCurveLink *, pfl, pfLinks) {
412 bPoseChannel *pchan = pfl->pchan;
413
414 if ((pfl->ob->id.tag & ID_TAG_DOIT) == 0) {
415 continue;
416 }
417
418 /* Add data-source override for the PoseChannel, to be used later. */
419 blender::animrig::relative_keyingset_add_source(sources, &pfl->ob->id, &RNA_PoseBone, pchan);
420 }
421
422 /* insert keyframes for all relevant bones in one go */
424 C, &sources, ks, blender::animrig::ModifyKeyMode::INSERT, cframe);
425
426 /* do the bone paths
427 * - only do this if keyframes should have been added
428 * - do not calculate unless there are paths already to update...
429 */
430 FOREACH_OBJECT_IN_MODE_BEGIN (scene, view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) {
431 if (ob->id.tag & ID_TAG_DOIT) {
432 if (ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) {
433 // ED_pose_clear_paths(C, ob); /* XXX for now, don't need to clear. */
434 /* TODO(sergey): Should ensure we can use more narrow update range here. */
436 }
437 }
438 }
440}
441
442/* ------------------------- */
443
444LinkData *poseAnim_mapping_getNextFCurve(ListBase *fcuLinks, LinkData *prev, const char *path)
445{
446 LinkData *first = static_cast<LinkData *>((prev) ? prev->next :
447 (fcuLinks) ? fcuLinks->first :
448 nullptr);
449 LinkData *ld;
450
451 /* check each link to see if the linked F-Curve has a matching path */
452 for (ld = first; ld; ld = ld->next) {
453 const FCurve *fcu = static_cast<const FCurve *>(ld->data);
454
455 /* check if paths match */
456 if (STREQ(path, fcu->rna_path)) {
457 return ld;
458 }
459 }
460
461 /* none found */
462 return nullptr;
463}
464
465/* *********************************************** */
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.
static constexpr const char * ANIM_KS_WHOLE_CHARACTER_ID
Blender kernel action and pose functionality.
AnimData * BKE_animdata_from_id(const ID *id)
Definition anim_data.cc:83
#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: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 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:46
LinkData * BLI_genericNodeN(void *data)
Definition listbase.cc:922
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
void BLI_freelinkN(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:270
void void BLI_freelistN(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:497
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
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.cc:41
#define ELEM(...)
#define STREQ(a, b)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_TAG_DOIT
Definition DNA_ID.h:1036
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ ID_RECALC_ANIMATION_NO_FLUSH
Definition DNA_ID.h:1176
@ MOTIONPATH_BAKE_HAS_PATHS
@ POSE_SCALE
@ POSE_BBONE_SHAPE
@ POSE_ROT
@ POSE_LOC
@ OB_MODE_POSE
Object is a sort of wrapper for general info.
@ OB_ARMATURE
@ POSE_PATH_CALC_RANGE_FULL
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
#define ND_POSE
Definition WM_types.hh:458
#define NC_OBJECT
Definition WM_types.hh:379
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
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 relative_keyingset_add_source(blender::Vector< PointerRNA > &sources, ID *id, StructRNA *srna, void *data)
KeyingSet * get_keyingset_for_autokeying(const Scene *scene, const char *transformKSName)
int apply_keyingset(bContext *C, blender::Vector< PointerRNA > *sources, KeyingSet *keyingset, ModifyKeyMode mode, float cfra)
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:62
@ ACT_TRANS_ALL
Definition pose_utils.cc:72
@ ACT_TRANS_PROP
Definition pose_utils.cc:69
@ ACT_TRANS_SCALE
Definition pose_utils.cc:65
@ ACT_TRANS_BBONE
Definition pose_utils.cc:68
@ ACT_TRANS_ROT
Definition pose_utils.cc:64
@ ACT_TRANS_LOC
Definition pose_utils.cc:63
@ ACT_TRANS_ONLY
Definition pose_utils.cc:71
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:75
LinkData * poseAnim_mapping_getNextFCurve(ListBase *fcuLinks, LinkData *prev, const char *path)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
std::optional< std::string > RNA_path_from_ID_to_struct(const PointerRNA *ptr)
Definition rna_path.cc:1014
bAction * action
int32_t slot_handle
char * rna_path
Definition DNA_ID.h:414
void * data
struct LinkData * next
void * first
struct AnimData * adt
IDProperty * prop
IDProperty * system_properties
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4238