Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSybren A. Stüvel <sybren@blender.org>2021-11-11 19:51:38 +0300
committerSybren A. Stüvel <sybren@blender.org>2021-11-12 16:36:22 +0300
commitb4cfe80547244e69017bb9c8432a4b18c21b5a6b (patch)
treed755d8fbe10634f2763fd66426df75824b76b382
parent5f7d5c0809104bf702dff4be1b706c6841095fda (diff)
Assets: Store Action sub-type in asset metadata
Blender 3.0 will only support single-frame Actions in the pose library. The goal of this patch is to lay the groundwork for making it possible for the Asset Browser to reject/hide "animation snippet" Action assets. Determining whether an Action has one or more frames (i.e. whether it has a single pose or animation) requires inspecting the Action itself, and thus loading the data-block itself. This would make it impossible to quickly determine from the asset browser. To solve this, the Action is inspected before saving, and a `"is_single_frame"` boolean (well, 0/1 integer) IDProperty is added. Reviewed by: Severin Differential Revision: https://developer.blender.org/D13202
-rw-r--r--source/blender/blenkernel/BKE_action.h10
-rw-r--r--source/blender/blenkernel/intern/action.c68
-rw-r--r--source/blender/blenkernel/intern/action_test.cc95
3 files changed, 173 insertions, 0 deletions
diff --git a/source/blender/blenkernel/BKE_action.h b/source/blender/blenkernel/BKE_action.h
index 9f69c5e3976..db55daddba6 100644
--- a/source/blender/blenkernel/BKE_action.h
+++ b/source/blender/blenkernel/BKE_action.h
@@ -125,6 +125,16 @@ struct bActionGroup *BKE_action_group_find_name(struct bAction *act, const char
/* Clear all 'temp' flags on all groups */
void action_groups_clear_tempflags(struct bAction *act);
+/**
+ * Return whether the action has one unique point in time keyed.
+ *
+ * This is mostly for the pose library, which will have different behaviour depending on whether an
+ * Action corresponds to a "pose" (one keyframe) or "animation snippet" (multiple keyframes).
+ *
+ * \return `false` when there is no keyframe at all or keys on different points in time, `true`
+ * when exactly one point in time is keyed. */
+bool BKE_action_has_single_frame(const struct bAction *act);
+
/* Pose API ----------------- */
void BKE_pose_channel_free(struct bPoseChannel *pchan);
diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c
index 7403b7f2109..842902b51ed 100644
--- a/source/blender/blenkernel/intern/action.c
+++ b/source/blender/blenkernel/intern/action.c
@@ -51,6 +51,7 @@
#include "BKE_anim_visualization.h"
#include "BKE_animsys.h"
#include "BKE_armature.h"
+#include "BKE_asset.h"
#include "BKE_constraint.h"
#include "BKE_deform.h"
#include "BKE_fcurve.h"
@@ -286,6 +287,30 @@ static void action_blend_read_expand(BlendExpander *expander, ID *id)
}
}
+static IDProperty *action_asset_type_property(const bAction *action)
+{
+ const bool is_single_frame = !BKE_action_has_single_frame(action);
+
+ IDPropertyTemplate idprop = {0};
+ idprop.i = is_single_frame;
+
+ IDProperty *property = IDP_New(IDP_INT, &idprop, "is_single_frame");
+ return property;
+}
+
+static void action_asset_pre_save(void *asset_ptr, struct AssetMetaData *asset_data)
+{
+ bAction *action = (bAction *)asset_ptr;
+ BLI_assert(GS(action->id.name) == ID_AC);
+
+ IDProperty *action_type = action_asset_type_property(action);
+ BKE_asset_metadata_idprop_ensure(asset_data, action_type);
+}
+
+AssetTypeInfo AssetType_AC = {
+ /* pre_save_fn */ action_asset_pre_save,
+};
+
IDTypeInfo IDType_ID_AC = {
.id_code = ID_AC,
.id_filter = FILTER_ID_AC,
@@ -312,6 +337,8 @@ IDTypeInfo IDType_ID_AC = {
.blend_read_undo_preserve = NULL,
.lib_override_apply_post = NULL,
+
+ .asset_type_info = &AssetType_AC,
};
/* ***************** Library data level operations on action ************** */
@@ -1418,6 +1445,47 @@ bool action_has_motion(const bAction *act)
return false;
}
+bool BKE_action_has_single_frame(const struct bAction *act)
+{
+ if (act == NULL || BLI_listbase_is_empty(&act->curves)) {
+ return false;
+ }
+
+ bool found_key = false;
+ float found_key_frame = 0.0f;
+
+ LISTBASE_FOREACH (FCurve *, fcu, &act->curves) {
+ switch (fcu->totvert) {
+ case 0:
+ /* No keys, so impossible to come to a conclusion on this curve alone. */
+ continue;
+ case 1:
+ /* Single key, which is the complex case, so handle below. */
+ break;
+ default:
+ /* Multiple keys, so there is animation. */
+ return false;
+ }
+
+ const float this_key_frame = fcu->bezt != NULL ? fcu->bezt[0].vec[1][0] : fcu->fpt[0].vec[0];
+ if (!found_key) {
+ found_key = true;
+ found_key_frame = this_key_frame;
+ continue;
+ }
+
+ /* The graph editor rounds to 1/1000th of a frame, so it's not necessary to be really precise
+ * with these comparisons. */
+ if (!compare_ff(found_key_frame, this_key_frame, 0.001f)) {
+ /* This key differs from the already-found key, so this Action represents animation. */
+ return false;
+ }
+ }
+
+ /* There is only a single frame if we found at least one key. */
+ return found_key;
+}
+
/* Calculate the extents of given action */
void calc_action_range(const bAction *act, float *start, float *end, short incl_modifiers)
{
diff --git a/source/blender/blenkernel/intern/action_test.cc b/source/blender/blenkernel/intern/action_test.cc
index c02eca966ad..c0d9a4c6055 100644
--- a/source/blender/blenkernel/intern/action_test.cc
+++ b/source/blender/blenkernel/intern/action_test.cc
@@ -24,6 +24,8 @@
#include "BLI_listbase.h"
+#include "MEM_guardedalloc.h"
+
#include "testing/testing.h"
namespace blender::bke::tests {
@@ -141,4 +143,97 @@ TEST(action_groups, ReconstructGroupsWithReordering)
EXPECT_EQ(groupDcurve2.next, nullptr);
}
+namespace {
+
+/* Allocate fcu->bezt, and also return a unique_ptr to it for easily freeing the memory. */
+std::unique_ptr<BezTriple[]> allocate_keyframes(FCurve *fcu, const size_t num_keyframes)
+{
+ auto bezt_uptr = std::make_unique<BezTriple[]>(num_keyframes);
+ fcu->bezt = bezt_uptr.get();
+ return bezt_uptr;
+}
+
+/* Append keyframe, assumes that fcu->bezt is allocated and has enough space. */
+void add_keyframe(FCurve *fcu, float x, float y)
+{
+ /* The insert_keyframe functions are in the editors, so we cannot link to those here. */
+ BezTriple the_keyframe;
+ memset(&the_keyframe, 0, sizeof(the_keyframe));
+
+ /* Copied from insert_vert_fcurve() in keyframing.c. */
+ the_keyframe.vec[0][0] = x - 1.0f;
+ the_keyframe.vec[0][1] = y;
+ the_keyframe.vec[1][0] = x;
+ the_keyframe.vec[1][1] = y;
+ the_keyframe.vec[2][0] = x + 1.0f;
+ the_keyframe.vec[2][1] = y;
+
+ memcpy(&fcu->bezt[fcu->totvert], &the_keyframe, sizeof(the_keyframe));
+ fcu->totvert++;
+}
+
+} // namespace
+
+TEST(action_assets, BKE_action_has_single_frame)
+{
+ /* NULL action. */
+ EXPECT_FALSE(BKE_action_has_single_frame(nullptr)) << "NULL Action cannot have a single frame.";
+
+ /* No FCurves. */
+ {
+ const bAction empty = {nullptr};
+ EXPECT_FALSE(BKE_action_has_single_frame(&empty))
+ << "Action without FCurves cannot have a single frame.";
+ }
+
+ /* One curve with one key. */
+ {
+ FCurve fcu = {nullptr};
+ std::unique_ptr<BezTriple[]> bezt = allocate_keyframes(&fcu, 1);
+ add_keyframe(&fcu, 1.0f, 2.0f);
+
+ bAction action = {nullptr};
+ BLI_addtail(&action.curves, &fcu);
+
+ EXPECT_TRUE(BKE_action_has_single_frame(&action))
+ << "Action with one FCurve and one key should have single frame.";
+ }
+
+ /* Two curves with one key each. */
+ {
+ FCurve fcu1 = {nullptr};
+ FCurve fcu2 = {nullptr};
+ std::unique_ptr<BezTriple[]> bezt1 = allocate_keyframes(&fcu1, 1);
+ std::unique_ptr<BezTriple[]> bezt2 = allocate_keyframes(&fcu2, 1);
+ add_keyframe(&fcu1, 1.0f, 327.0f);
+ add_keyframe(&fcu2, 1.0f, 47.0f); /* Same X-coordinate as the other one. */
+
+ bAction action = {nullptr};
+ BLI_addtail(&action.curves, &fcu1);
+ BLI_addtail(&action.curves, &fcu2);
+
+ EXPECT_TRUE(BKE_action_has_single_frame(&action))
+ << "Two FCurves with keys on the same frame should have single frame.";
+
+ /* Modify the 2nd curve so it's keyed on a different frame. */
+ fcu2.bezt[0].vec[1][0] = 2.0f;
+ EXPECT_FALSE(BKE_action_has_single_frame(&action))
+ << "Two FCurves with keys on different frames should have animation.";
+ }
+
+ /* One curve with two keys. */
+ {
+ FCurve fcu = {nullptr};
+ std::unique_ptr<BezTriple[]> bezt = allocate_keyframes(&fcu, 2);
+ add_keyframe(&fcu, 1.0f, 2.0f);
+ add_keyframe(&fcu, 2.0f, 2.5f);
+
+ bAction action = {nullptr};
+ BLI_addtail(&action.curves, &fcu);
+
+ EXPECT_FALSE(BKE_action_has_single_frame(&action))
+ << "Action with one FCurve and two keys must have animation.";
+ }
+}
+
} // namespace blender::bke::tests