[Bf-blender-cvs] [e18091650b9] master: Animation: action mirror RNA API using pose contents

Campbell Barton noreply at git.blender.org
Fri Mar 26 03:40:05 CET 2021


Commit: e18091650b92ee88339d1c8032572dabce21362a
Author: Campbell Barton
Date:   Fri Mar 26 13:12:56 2021 +1100
Branches: master
https://developer.blender.org/rBe18091650b92ee88339d1c8032572dabce21362a

Animation: action mirror RNA API using pose contents

This adds a new RNA method `Action.flip_with_pose(ob)` to flip the
action channels that control a pose.
The rest-pose is used to properly flip the bones transformation.

This is useful as a way to flip actions used in pose libraries,
so the same action need not be included for each side.

Reviewed By: sybren

Ref D10781

===================================================================

M	source/blender/blenkernel/BKE_action.h
M	source/blender/blenkernel/CMakeLists.txt
A	source/blender/blenkernel/intern/action_mirror.c
M	source/blender/makesrna/intern/rna_action_api.c

===================================================================

diff --git a/source/blender/blenkernel/BKE_action.h b/source/blender/blenkernel/BKE_action.h
index 717cfa607ad..62315643647 100644
--- a/source/blender/blenkernel/BKE_action.h
+++ b/source/blender/blenkernel/BKE_action.h
@@ -227,6 +227,9 @@ void BKE_pose_blend_read_data(struct BlendDataReader *reader, struct bPose *pose
 void BKE_pose_blend_read_lib(struct BlendLibReader *reader, struct Object *ob, struct bPose *pose);
 void BKE_pose_blend_read_expand(struct BlendExpander *expander, struct bPose *pose);
 
+/* action_mirror.c */
+void BKE_action_flip_with_pose(struct bAction *act, struct Object *ob_arm);
+
 #ifdef __cplusplus
 };
 #endif
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index 12a801545a9..8fbf49b31d4 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -69,6 +69,7 @@ set(SRC
   intern/CCGSubSurf_util.c
   intern/DerivedMesh.cc
   intern/action.c
+  intern/action_mirror.c
   intern/addon.c
   intern/anim_data.c
   intern/anim_path.c
diff --git a/source/blender/blenkernel/intern/action_mirror.c b/source/blender/blenkernel/intern/action_mirror.c
new file mode 100644
index 00000000000..c975d2bfb9c
--- /dev/null
+++ b/source/blender/blenkernel/intern/action_mirror.c
@@ -0,0 +1,457 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup bke
+ *
+ * Mirror/Symmetry functions applying to actions.
+ */
+
+#include <math.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_anim_types.h"
+#include "DNA_armature_types.h"
+#include "DNA_object_types.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
+#include "BLI_string_utils.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_action.h"
+#include "BKE_armature.h"
+#include "BKE_fcurve.h"
+
+#include "DEG_depsgraph.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Flip the Action (Armature/Pose Objects)
+ *
+ * This flips the action using the rest pose (not the evaluated pose).
+ *
+ * Details:
+ *
+ * - Key-frames are modified in-place, creating new key-frames is not yet supported.
+ *   That could be useful if a user for example only has 2x rotation channels set.
+ *   In practice users typically keyframe all rotation channels or none.
+ *
+ * - F-curve modifiers are disabled for evaluation,
+ *   so the values written back to the keyframes don't include modifier offsets.
+ *
+ * - Sub-frame key-frames aren't supported,
+ *   this could be added if needed without much trouble.
+ *
+ * - F-curves must have a #FCurve.bezt array (sampled curves aren't supported).
+ * \{ */
+
+/**
+ * This structure is created for each pose channels F-curve,
+ * an action be evaluated and stored in `fcurve_eval`,
+ * with the mirrored values written into `bezt_array`.
+ *
+ * Store F-curve evaluated values, constructed with a sorted array of rounded keyed-frames,
+ * passed to #action_flip_pchan_cache_init.
+ */
+struct FCurve_KeyCache {
+  /**
+   * When NULL, ignore this channel.
+   */
+  FCurve *fcurve;
+  /**
+   * Cached evaluated F-curve values (without modifiers).
+   */
+  float *fcurve_eval;
+  /**
+   * Cached #FCurve.bezt values, NULL when no key-frame exists on this frame.
+   *
+   * \note The case where two keyframes round to the same frame isn't supported.
+   * In this case only the first will be used.
+   */
+  BezTriple **bezt_array;
+};
+
+/**
+ * Assign `fkc` path, using a `path` lookup for a single value.
+ */
+static void action_flip_pchan_cache_fcurve_assign_value(struct FCurve_KeyCache *fkc,
+                                                        int index,
+                                                        const char *path,
+                                                        struct FCurvePathCache *fcache)
+{
+  FCurve *fcu = BKE_fcurve_pathcache_find(fcache, path, index);
+  if (fcu && fcu->bezt) {
+    fkc->fcurve = fcu;
+  }
+}
+
+/**
+ * Assign #FCurve_KeyCache.fcurve path, using a `path` lookup for an array.
+ */
+static void action_flip_pchan_cache_fcurve_assign_array(struct FCurve_KeyCache *fkc,
+                                                        int fkc_len,
+                                                        const char *path,
+                                                        struct FCurvePathCache *fcache)
+{
+  FCurve **fcurves = alloca(sizeof(*fcurves) * fkc_len);
+  if (BKE_fcurve_pathcache_find_array(fcache, path, fcurves, fkc_len)) {
+    for (int i = 0; i < fkc_len; i++) {
+      if (fcurves[i] && fcurves[i]->bezt) {
+        fkc[i].fcurve = fcurves[i];
+      }
+    }
+  }
+}
+
+/**
+ * Fill in pose channel cache for each frame in `keyed_frames`.
+ *
+ * \param keyed_frames: An array of keyed_frames to evaluate,
+ * note that each frame is rounded to the nearest int.
+ * \param keyed_frames_len: The length of the `keyed_frames` array.
+ */
+static void action_flip_pchan_cache_init(struct FCurve_KeyCache *fkc,
+                                         const float *keyed_frames,
+                                         int keyed_frames_len)
+{
+  BLI_assert(fkc->fcurve != NULL);
+
+  /* Cache the F-curve values for `keyed_frames`. */
+  const int fcurve_flag = fkc->fcurve->flag;
+  fkc->fcurve->flag |= FCURVE_MOD_OFF;
+  fkc->fcurve_eval = MEM_mallocN(sizeof(float) * keyed_frames_len, __func__);
+  for (int frame_index = 0; frame_index < keyed_frames_len; frame_index++) {
+    const float evaltime = keyed_frames[frame_index];
+    fkc->fcurve_eval[frame_index] = evaluate_fcurve_only_curve(fkc->fcurve, evaltime);
+  }
+  fkc->fcurve->flag = fcurve_flag;
+
+  /* Cache the #BezTriple for `keyed_frames`, or leave as NULL. */
+  fkc->bezt_array = MEM_mallocN(sizeof(*fkc->bezt_array) * keyed_frames_len, __func__);
+  BezTriple *bezt = fkc->fcurve->bezt;
+  BezTriple *bezt_end = fkc->fcurve->bezt + fkc->fcurve->totvert;
+
+  int frame_index = 0;
+  while (frame_index < keyed_frames_len) {
+    const float evaltime = keyed_frames[frame_index];
+    const float bezt_time = roundf(bezt->vec[1][0]);
+    if (bezt_time > evaltime) {
+      fkc->bezt_array[frame_index++] = NULL;
+    }
+    else {
+      if (bezt_time == evaltime) {
+        fkc->bezt_array[frame_index++] = bezt;
+      }
+      bezt++;
+      if (bezt == bezt_end) {
+        break;
+      }
+    }
+  }
+  /* Clear remaining unset keyed_frames (if-any). */
+  while (frame_index < keyed_frames_len) {
+    fkc->bezt_array[frame_index++] = NULL;
+  }
+}
+
+/**
+ */
+static void action_flip_pchan(Object *ob_arm,
+                              const bPoseChannel *pchan,
+                              struct FCurvePathCache *fcache)
+{
+  /* Begin F-Curve pose channel value extraction. */
+  /* Use a fixed buffer size as it's known this can only be at most:
+   * `pose.bones["{MAXBONENAME}"].rotation_quaternion`. */
+  char path_xform[256];
+  char pchan_name_esc[sizeof(((bActionChannel *)NULL)->name) * 2];
+  BLI_str_escape(pchan_name_esc, pchan->name, sizeof(pchan_name_esc));
+  const int path_xform_prefix_len = SNPRINTF(path_xform, "pose.bones[\"%s\"]", pchan_name_esc);
+  char *path_xform_suffix = path_xform + path_xform_prefix_len;
+  const int path_xform_suffix_len = sizeof(path_xform) - path_xform_prefix_len;
+
+  /* Lookup and assign all available #FCurve channels,
+   * unavailable channels are left NULL. */
+
+  /**
+   * Structure to store transformation F-curves corresponding to a pose bones transformation.
+   * Match struct member names from #bPoseChannel so macros avoid repetition.
+   *
+   * \note There is no need to read values unless they influence the 4x4 transform matrix,
+   * and no need to write values back unless they would be changed by a modified matrix.
+   * So `rotmode` needs to be read, but doesn't need to be written back to.
+   *
+   * Most bendy-bone settings don't need to be included either, flipping their RNA paths is enough.
+   * Although the X/Y settings could make sense to transform, in practice it would only
+   * work well if the rotation happened to swap X/Y alignment, leave this for now.
+   */
+  struct {
+    struct FCurve_KeyCache loc[3], eul[3], quat[4], rotAxis[3], rotAngle, size[3], rotmode;
+  } fkc_pchan = {{{NULL}}};
+
+#define FCURVE_ASSIGN_VALUE(id, path_test_suffix, index) \
+  BLI_strncpy(path_xform_suffix, path_test_suffix, path_xform_suffix_len); \
+  action_flip_pchan_cache_fcurve_assign_value(&fkc_pchan.id, index, path_xform, fcache)
+
+#define FCURVE_ASSIGN_ARRAY(id, path_test_suffix) \
+  BLI_strncpy(path_xform_suffix, path_test_suffix, path_xform_suffix_len); \
+  action_flip_pchan_cache_fcurve_assign_array( \
+      fkc_pchan.id, ARRAY_SIZE(fkc_pchan.id), path_xform, fcache)
+
+  FCURVE_ASSIGN_ARRAY(loc, ".location");
+  FCURVE_ASSIGN_ARRAY(eul, ".rotation_euler");
+  FCURVE_ASSIGN_ARRAY(quat, ".rotation_quaternion");
+  FCURVE_ASSIGN_ARRAY(rotAxis, ".rotation_axis_angle");
+  FCURVE_ASSIGN_VALUE(rotAngle, ".rotation_axis_angle", 3);
+  FCURVE_ASSIGN_ARRAY(size, ".scale");
+  FCURVE_ASSIGN_VALUE(rotmode, ".rotation_mode", 0);
+
+#undef FCURVE_ASSIGN_VALUE
+#undef FCURVE_ASSIGN_ARRAY
+
+  /* Array of F-curves, for convenient access. */
+#define FCURVE_CHANNEL_LEN (sizeof(fkc_pchan) / sizeof(struct FCurve_KeyCache))
+  FCurve *fcurve_array[FCURVE_CHANNEL_LEN];
+  int fcurve_array_len = 0;
+
+  for (int chan = 0; chan < FCURVE_CHANNEL_LEN; chan++) {
+    struct FCurve_KeyCache *fkc = (struct FCurve_KeyCache *)(&fkc_pchan) + chan;
+    if (fkc->fcurve != NULL) {
+      fcurve

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list