1
1

Merge remote-tracking branch 'origin/blender-v3.0-release'

This commit is contained in:
2021-11-12 14:36:55 +01:00
3 changed files with 173 additions and 0 deletions

View File

@@ -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);

View File

@@ -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)
{

View File

@@ -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