Merge remote-tracking branch 'origin/blender-v3.0-release'
This commit is contained in:
@@ -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);
|
||||
|
@@ -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)
|
||||
{
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user