Blender  V3.3
action_mirror.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
9 #include <math.h>
10 #include <string.h>
11 
12 #include "MEM_guardedalloc.h"
13 
14 #include "DNA_anim_types.h"
15 #include "DNA_armature_types.h"
16 #include "DNA_object_types.h"
17 
18 #include "BLI_blenlib.h"
19 #include "BLI_math.h"
20 #include "BLI_string_utils.h"
21 #include "BLI_utildefines.h"
22 
23 #include "BKE_action.h"
24 #include "BKE_armature.h"
25 #include "BKE_fcurve.h"
26 
27 #include "DEG_depsgraph.h"
28 
29 /* -------------------------------------------------------------------- */
65  float *fcurve_eval;
73 };
74 
79  int index,
80  const char *path,
81  struct FCurvePathCache *fcache)
82 {
83  FCurve *fcu = BKE_fcurve_pathcache_find(fcache, path, index);
84  if (fcu && fcu->bezt) {
85  fkc->fcurve = fcu;
86  }
87 }
88 
93  int fkc_len,
94  const char *path,
95  struct FCurvePathCache *fcache)
96 {
97  FCurve **fcurves = alloca(sizeof(*fcurves) * fkc_len);
98  if (BKE_fcurve_pathcache_find_array(fcache, path, fcurves, fkc_len)) {
99  for (int i = 0; i < fkc_len; i++) {
100  if (fcurves[i] && fcurves[i]->bezt) {
101  fkc[i].fcurve = fcurves[i];
102  }
103  }
104  }
105 }
106 
115  const float *keyed_frames,
116  int keyed_frames_len)
117 {
118  BLI_assert(fkc->fcurve != NULL);
119 
120  /* Cache the F-Curve values for `keyed_frames`. */
121  const int fcurve_flag = fkc->fcurve->flag;
122  fkc->fcurve->flag |= FCURVE_MOD_OFF;
123  fkc->fcurve_eval = MEM_mallocN(sizeof(float) * keyed_frames_len, __func__);
124  for (int frame_index = 0; frame_index < keyed_frames_len; frame_index++) {
125  const float evaltime = keyed_frames[frame_index];
126  fkc->fcurve_eval[frame_index] = evaluate_fcurve_only_curve(fkc->fcurve, evaltime);
127  }
128  fkc->fcurve->flag = fcurve_flag;
129 
130  /* Cache the #BezTriple for `keyed_frames`, or leave as NULL. */
131  fkc->bezt_array = MEM_mallocN(sizeof(*fkc->bezt_array) * keyed_frames_len, __func__);
132  BezTriple *bezt = fkc->fcurve->bezt;
133  BezTriple *bezt_end = fkc->fcurve->bezt + fkc->fcurve->totvert;
134 
135  int frame_index = 0;
136  while (frame_index < keyed_frames_len) {
137  const float evaltime = keyed_frames[frame_index];
138  const float bezt_time = roundf(bezt->vec[1][0]);
139  if (bezt_time > evaltime) {
140  fkc->bezt_array[frame_index++] = NULL;
141  }
142  else {
143  if (bezt_time == evaltime) {
144  fkc->bezt_array[frame_index++] = bezt;
145  }
146  bezt++;
147  if (bezt == bezt_end) {
148  break;
149  }
150  }
151  }
152  /* Clear remaining unset keyed_frames (if-any). */
153  while (frame_index < keyed_frames_len) {
154  fkc->bezt_array[frame_index++] = NULL;
155  }
156 }
157 
160 static void action_flip_pchan(Object *ob_arm,
161  const bPoseChannel *pchan,
162  struct FCurvePathCache *fcache)
163 {
164  /* Begin F-Curve pose channel value extraction. */
165  /* Use a fixed buffer size as it's known this can only be at most:
166  * `pose.bones["{MAXBONENAME}"].rotation_quaternion`. */
167  char path_xform[256];
168  char pchan_name_esc[sizeof(((bActionChannel *)NULL)->name) * 2];
169  BLI_str_escape(pchan_name_esc, pchan->name, sizeof(pchan_name_esc));
170  const int path_xform_prefix_len = SNPRINTF(path_xform, "pose.bones[\"%s\"]", pchan_name_esc);
171  char *path_xform_suffix = path_xform + path_xform_prefix_len;
172  const int path_xform_suffix_len = sizeof(path_xform) - path_xform_prefix_len;
173 
174  /* Lookup and assign all available #FCurve channels,
175  * unavailable channels are left NULL. */
176 
189  struct {
190  struct FCurve_KeyCache loc[3], eul[3], quat[4], rotAxis[3], rotAngle, size[3], rotmode;
191  } fkc_pchan = {{{NULL}}};
192 
193 #define FCURVE_ASSIGN_VALUE(id, path_test_suffix, index) \
194  BLI_strncpy(path_xform_suffix, path_test_suffix, path_xform_suffix_len); \
195  action_flip_pchan_cache_fcurve_assign_value(&fkc_pchan.id, index, path_xform, fcache)
196 
197 #define FCURVE_ASSIGN_ARRAY(id, path_test_suffix) \
198  BLI_strncpy(path_xform_suffix, path_test_suffix, path_xform_suffix_len); \
199  action_flip_pchan_cache_fcurve_assign_array( \
200  fkc_pchan.id, ARRAY_SIZE(fkc_pchan.id), path_xform, fcache)
201 
202  FCURVE_ASSIGN_ARRAY(loc, ".location");
203  FCURVE_ASSIGN_ARRAY(eul, ".rotation_euler");
204  FCURVE_ASSIGN_ARRAY(quat, ".rotation_quaternion");
205  FCURVE_ASSIGN_ARRAY(rotAxis, ".rotation_axis_angle");
206  FCURVE_ASSIGN_VALUE(rotAngle, ".rotation_axis_angle", 3);
207  FCURVE_ASSIGN_ARRAY(size, ".scale");
208  FCURVE_ASSIGN_VALUE(rotmode, ".rotation_mode", 0);
209 
210 #undef FCURVE_ASSIGN_VALUE
211 #undef FCURVE_ASSIGN_ARRAY
212 
213  /* Array of F-Curves, for convenient access. */
214 #define FCURVE_CHANNEL_LEN (sizeof(fkc_pchan) / sizeof(struct FCurve_KeyCache))
215  FCurve *fcurve_array[FCURVE_CHANNEL_LEN];
216  int fcurve_array_len = 0;
217 
218  for (int chan = 0; chan < FCURVE_CHANNEL_LEN; chan++) {
219  struct FCurve_KeyCache *fkc = (struct FCurve_KeyCache *)(&fkc_pchan) + chan;
220  if (fkc->fcurve != NULL) {
221  fcurve_array[fcurve_array_len++] = fkc->fcurve;
222  }
223  }
224 
225  /* If this pose has no transform channels, there is nothing to do. */
226  if (fcurve_array_len == 0) {
227  return;
228  }
229 
230  /* Calculate an array of frames used by any of the key-frames in `fcurve_array`. */
231  int keyed_frames_len;
232  const float *keyed_frames = BKE_fcurves_calc_keyed_frames(
233  fcurve_array, fcurve_array_len, &keyed_frames_len);
234 
235  /* Initialize the pose channel curve cache from the F-Curve. */
236  for (int chan = 0; chan < FCURVE_CHANNEL_LEN; chan++) {
237  struct FCurve_KeyCache *fkc = (struct FCurve_KeyCache *)(&fkc_pchan) + chan;
238  if (fkc->fcurve == NULL) {
239  continue;
240  }
241  action_flip_pchan_cache_init(fkc, keyed_frames, keyed_frames_len);
242  }
243 
244  /* X-axis flipping matrix. */
245  float flip_mtx[4][4];
246  unit_m4(flip_mtx);
247  flip_mtx[0][0] = -1;
248 
249  bPoseChannel *pchan_flip = NULL;
250  char pchan_name_flip[MAXBONENAME];
251  BLI_string_flip_side_name(pchan_name_flip, pchan->name, false, sizeof(pchan_name_flip));
252  if (!STREQ(pchan_name_flip, pchan->name)) {
253  pchan_flip = BKE_pose_channel_find_name(ob_arm->pose, pchan_name_flip);
254  }
255 
256  float arm_mat_inv[4][4];
257  invert_m4_m4(arm_mat_inv, pchan_flip ? pchan_flip->bone->arm_mat : pchan->bone->arm_mat);
258 
259  /* Now flip the transformation & write it back to the F-Curves in `fkc_pchan`. */
260 
261  for (int frame_index = 0; frame_index < keyed_frames_len; frame_index++) {
262 
263  /* Temporary pose channel to write values into,
264  * using the `fkc_pchan` values, falling back to the values in the pose channel. */
265  bPoseChannel pchan_temp = *pchan;
266 
267  /* Load the values into the channel. */
268 #define READ_VALUE_FLT(id) \
269  if (fkc_pchan.id.fcurve_eval != NULL) { \
270  pchan_temp.id = fkc_pchan.id.fcurve_eval[frame_index]; \
271  } \
272  ((void)0)
273 
274 #define READ_VALUE_INT(id) \
275  if (fkc_pchan.id.fcurve_eval != NULL) { \
276  pchan_temp.id = floorf(fkc_pchan.id.fcurve_eval[frame_index] + 0.5f); \
277  } \
278  ((void)0)
279 
280 #define READ_ARRAY_FLT(id) \
281  for (int i = 0; i < ARRAY_SIZE(pchan_temp.id); i++) { \
282  READ_VALUE_FLT(id[i]); \
283  } \
284  ((void)0)
285 
286  READ_ARRAY_FLT(loc);
287  READ_ARRAY_FLT(eul);
288  READ_ARRAY_FLT(quat);
289  READ_ARRAY_FLT(rotAxis);
290  READ_VALUE_FLT(rotAngle);
292  READ_VALUE_INT(rotmode);
293 
294 #undef READ_ARRAY_FLT
295 #undef READ_VALUE_FLT
296 #undef READ_VALUE_INT
297 
298  float chan_mat[4][4];
299  BKE_pchan_to_mat4(&pchan_temp, chan_mat);
300 
301  /* Move to the pose-space. */
302  mul_m4_m4m4(chan_mat, pchan->bone->arm_mat, chan_mat);
303 
304  /* Flip the matrix. */
305  mul_m4_m4m4(chan_mat, chan_mat, flip_mtx);
306  mul_m4_m4m4(chan_mat, flip_mtx, chan_mat);
307 
308  /* Move back to bone-space space, using the flipped bone if it exists. */
309  mul_m4_m4m4(chan_mat, arm_mat_inv, chan_mat);
310 
311  /* The rest pose having an X-axis that is not mapping to a left/right direction (so aligned
312  * with the Y or Z axis) creates issues when flipping the pose. Instead of a negative scale on
313  * the X-axis, it turns into a 180 degree rotation over the Y-axis.
314  * This has only been observed with bones that can't be flipped,
315  * hence the check for `pchan_flip`. */
316  const float unit_x[3] = {1.0f, 0.0f, 0.0f};
317  const bool is_x_axis_orthogonal = (pchan_flip == NULL) &&
318  (fabsf(dot_v3v3(pchan->bone->arm_mat[0], unit_x)) <= 1e-6f);
319  if (is_x_axis_orthogonal) {
320  /* Matrix needs to flip both the X and Z axes to come out right. */
321  float extra_mat[4][4] = {
322  {-1.0f, 0.0f, 0.0f, 0.0f},
323  {0.0f, 1.0f, 0.0f, 0.0f},
324  {0.0f, 0.0f, -1.0f, 0.0f},
325  {0.0f, 0.0f, 0.0f, 1.0f},
326  };
327  mul_m4_m4m4(chan_mat, extra_mat, chan_mat);
328  }
329 
330  BKE_pchan_apply_mat4(&pchan_temp, chan_mat, false);
331 
332  /* Write the values back to the F-Curves. */
333 #define WRITE_VALUE_FLT(id) \
334  if (fkc_pchan.id.fcurve_eval != NULL) { \
335  BezTriple *bezt = fkc_pchan.id.bezt_array[frame_index]; \
336  if (bezt != NULL) { \
337  const float delta = pchan_temp.id - bezt->vec[1][1]; \
338  bezt->vec[0][1] += delta; \
339  bezt->vec[1][1] += delta; \
340  bezt->vec[2][1] += delta; \
341  } \
342  } \
343  ((void)0)
344 
345 #define WRITE_ARRAY_FLT(id) \
346  for (int i = 0; i < ARRAY_SIZE(pchan_temp.id); i++) { \
347  WRITE_VALUE_FLT(id[i]); \
348  } \
349  ((void)0)
350 
351  /* Write the values back the F-Curves. */
352  WRITE_ARRAY_FLT(loc);
353  WRITE_ARRAY_FLT(eul);
354  WRITE_ARRAY_FLT(quat);
355  WRITE_ARRAY_FLT(rotAxis);
356  WRITE_VALUE_FLT(rotAngle);
358  /* No need to write back 'rotmode' as it can't be transformed. */
359 
360 #undef WRITE_ARRAY_FLT
361 #undef WRITE_VALUE_FLT
362  }
363 
364  /* Recalculate handles. */
365  for (int i = 0; i < fcurve_array_len; i++) {
366  BKE_fcurve_handles_recalc_ex(fcurve_array[i], 0);
367  }
368 
369  MEM_freeN((void *)keyed_frames);
370 
371  for (int chan = 0; chan < FCURVE_CHANNEL_LEN; chan++) {
372  struct FCurve_KeyCache *fkc = (struct FCurve_KeyCache *)(&fkc_pchan) + chan;
373  if (fkc->fcurve_eval) {
374  MEM_freeN(fkc->fcurve_eval);
375  }
376  if (fkc->bezt_array) {
377  MEM_freeN(fkc->bezt_array);
378  }
379  }
380 }
381 
385 static void action_flip_pchan_rna_paths(struct bAction *act)
386 {
387  const char *path_pose_prefix = "pose.bones[\"";
388  const int path_pose_prefix_len = strlen(path_pose_prefix);
389 
390  /* Tag curves that have renamed f-curves. */
391  LISTBASE_FOREACH (bActionGroup *, agrp, &act->groups) {
392  agrp->flag &= ~AGRP_TEMP;
393  }
394 
395  LISTBASE_FOREACH (FCurve *, fcu, &act->curves) {
396  if (!STRPREFIX(fcu->rna_path, path_pose_prefix)) {
397  continue;
398  }
399 
400  const char *name_esc = fcu->rna_path + path_pose_prefix_len;
401  const char *name_esc_end = BLI_str_escape_find_quote(name_esc);
402 
403  /* While unlikely, an RNA path could be malformed. */
404  if (UNLIKELY(name_esc_end == NULL)) {
405  continue;
406  }
407 
408  char name[MAXBONENAME];
409  const size_t name_esc_len = (size_t)(name_esc_end - name_esc);
410  const size_t name_len = BLI_str_unescape(name, name_esc, name_esc_len);
411 
412  /* While unlikely, data paths could be constructed that have longer names than
413  * are currently supported. */
414  if (UNLIKELY(name_len >= sizeof(name))) {
415  continue;
416  }
417 
418  /* When the flipped name differs, perform the rename. */
419  char name_flip[MAXBONENAME];
420  BLI_string_flip_side_name(name_flip, name, false, sizeof(name_flip));
421  if (!STREQ(name_flip, name)) {
422  char name_flip_esc[MAXBONENAME * 2];
423  BLI_str_escape(name_flip_esc, name_flip, sizeof(name_flip_esc));
424  char *path_flip = BLI_sprintfN("pose.bones[\"%s%s", name_flip_esc, name_esc_end);
425  MEM_freeN(fcu->rna_path);
426  fcu->rna_path = path_flip;
427 
428  if (fcu->grp != NULL) {
429  fcu->grp->flag |= AGRP_TEMP;
430  }
431  }
432  }
433 
434  /* Rename tagged groups. */
435  LISTBASE_FOREACH (bActionGroup *, agrp, &act->groups) {
436  if ((agrp->flag & AGRP_TEMP) == 0) {
437  continue;
438  }
439  agrp->flag &= ~AGRP_TEMP;
440  char name_flip[MAXBONENAME];
441  BLI_string_flip_side_name(name_flip, agrp->name, false, sizeof(name_flip));
442  if (!STREQ(name_flip, agrp->name)) {
443  STRNCPY(agrp->name, name_flip);
444  }
445  }
446 }
447 
448 void BKE_action_flip_with_pose(struct bAction *act, struct Object *ob_arm)
449 {
450  struct FCurvePathCache *fcache = BKE_fcurve_pathcache_create(&act->curves);
451  int i;
452  LISTBASE_FOREACH_INDEX (bPoseChannel *, pchan, &ob_arm->pose->chanbase, i) {
453  action_flip_pchan(ob_arm, pchan, fcache);
454  }
456 
458 
460 }
461 
Blender kernel action and pose functionality.
struct bPoseChannel * BKE_pose_channel_find_name(const struct bPose *pose, const char *name)
void BKE_pchan_to_mat4(const struct bPoseChannel *pchan, float r_chanmat[4][4])
void BKE_pchan_apply_mat4(struct bPoseChannel *pchan, const float mat[4][4], bool use_compat)
Definition: armature.c:1958
struct FCurvePathCache * BKE_fcurve_pathcache_create(ListBase *list)
Definition: fcurve_cache.c:64
float evaluate_fcurve_only_curve(struct FCurve *fcu, float evaltime)
Definition: fcurve.c:2142
float * BKE_fcurves_calc_keyed_frames(struct FCurve **fcurve_array, int fcurve_array_len, int *r_frames_len)
Definition: fcurve.c:836
void BKE_fcurve_pathcache_destroy(struct FCurvePathCache *fcache)
Definition: fcurve_cache.c:113
void BKE_fcurve_handles_recalc_ex(struct FCurve *fcu, eBezTriple_Flag handle_sel_flag)
Definition: fcurve.c:1220
int BKE_fcurve_pathcache_find_array(struct FCurvePathCache *fcache, const char *rna_path, struct FCurve **fcurve_result, int fcurve_result_len)
Definition: fcurve_cache.c:144
struct FCurve * BKE_fcurve_pathcache_find(struct FCurvePathCache *fcache, const char rna_path[], int array_index)
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
Definition: BLI_listbase.h:344
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
Definition: math_matrix.c:259
void unit_m4(float m[4][4])
Definition: rct.c:1090
bool invert_m4_m4(float R[4][4], const float A[4][4])
Definition: math_matrix.c:1287
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
size_t size_t char * BLI_sprintfN(const char *__restrict format,...) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_PRINTF_FORMAT(1
size_t BLI_str_unescape(char *__restrict dst, const char *__restrict src, size_t src_maxncpy) ATTR_NONNULL()
Definition: string.c:327
#define STRNCPY(dst, src)
Definition: BLI_string.h:483
#define SNPRINTF(dst, format,...)
Definition: BLI_string.h:485
const char * BLI_str_escape_find_quote(const char *str) ATTR_NONNULL()
Definition: string.c:341
size_t size_t char size_t BLI_str_escape(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL()
Definition: string.c:250
size_t BLI_string_flip_side_name(char *r_name, const char *from_name, bool strip_number, size_t name_len)
Definition: string_utils.c:112
#define STRPREFIX(a, b)
#define UNLIKELY(x)
#define STREQ(a, b)
void DEG_id_tag_update(struct ID *id, int flag)
@ ID_RECALC_COPY_ON_WRITE
Definition: DNA_ID.h:834
@ AGRP_TEMP
@ FCURVE_MOD_OFF
#define MAXBONENAME
Object is a sort of wrapper for general info.
Read Guarded memory(de)allocation.
#define READ_VALUE_FLT(id)
#define WRITE_ARRAY_FLT(id)
static void action_flip_pchan_cache_fcurve_assign_value(struct FCurve_KeyCache *fkc, int index, const char *path, struct FCurvePathCache *fcache)
Definition: action_mirror.c:78
void BKE_action_flip_with_pose(struct bAction *act, struct Object *ob_arm)
static void action_flip_pchan_cache_init(struct FCurve_KeyCache *fkc, const float *keyed_frames, int keyed_frames_len)
#define READ_VALUE_INT(id)
#define FCURVE_ASSIGN_ARRAY(id, path_test_suffix)
#define FCURVE_CHANNEL_LEN
static void action_flip_pchan_rna_paths(struct bAction *act)
static void action_flip_pchan_cache_fcurve_assign_array(struct FCurve_KeyCache *fkc, int fkc_len, const char *path, struct FCurvePathCache *fcache)
Definition: action_mirror.c:92
#define WRITE_VALUE_FLT(id)
#define READ_ARRAY_FLT(id)
static void action_flip_pchan(Object *ob_arm, const bPoseChannel *pchan, struct FCurvePathCache *fcache)
#define FCURVE_ASSIGN_VALUE(id, path_test_suffix, index)
float evaltime
Definition: bpy_driver.c:161
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:33
#define fabsf(x)
Definition: metal/compat.h:219
float vec[3][3]
float arm_mat[4][4]
float * fcurve_eval
Definition: action_mirror.c:65
BezTriple ** bezt_array
Definition: action_mirror.c:72
BezTriple * bezt
short flag
unsigned int totvert
struct bPose * pose
ListBase curves
ListBase groups
struct Bone * bone
ListBase chanbase