Files
test2/source/blender/animrig/intern/action_iterators_test.cc
Jacques Lucke d9b91c73e3 Core: use template for BKE_id_new
This is the same change as e09ccc9b35 but for `BKE_id_new`.

Pull Request: https://projects.blender.org/blender/blender/pulls/138667
2025-05-09 16:13:25 +02:00

193 lines
6.8 KiB
C++

/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "ANIM_action.hh"
#include "ANIM_action_iterators.hh"
#include "BKE_idtype.hh"
#include "BKE_lib_id.hh"
#include "BKE_main.hh"
#include "BKE_object.hh"
#include "DNA_anim_types.h"
#include "DNA_object_types.h"
#include "RNA_access.hh"
#include "RNA_prototypes.hh"
#include "CLG_log.h"
#include "testing/testing.h"
namespace blender::animrig::tests {
class ActionIteratorsTest : public testing::Test {
public:
Main *bmain;
Action *action;
static void SetUpTestSuite()
{
/* BKE_id_free() hits a code path that uses CLOG, which crashes if not initialized properly. */
CLG_init();
/* To make id_can_have_animdata() and friends work, the `id_types` array needs to be set up. */
BKE_idtype_init();
}
static void TearDownTestSuite()
{
CLG_exit();
}
void SetUp() override
{
bmain = BKE_main_new();
action = BKE_id_new<Action>(bmain, "ACLayeredAction");
}
void TearDown() override
{
BKE_main_free(bmain);
}
};
TEST_F(ActionIteratorsTest, iterate_all_fcurves_of_slot)
{
Slot &cube_slot = action->slot_add();
Slot &monkey_slot = action->slot_add();
EXPECT_TRUE(action->is_action_layered());
/* Try iterating an empty action. */
blender::Vector<FCurve *> no_fcurves;
foreach_fcurve_in_action_slot(
*action, cube_slot.handle, [&](FCurve &fcurve) { no_fcurves.append(&fcurve); });
ASSERT_TRUE(no_fcurves.is_empty());
Layer &layer = action->layer_add("Layer One");
Strip &strip = layer.strip_add(*action, Strip::Type::Keyframe);
StripKeyframeData &strip_data = strip.data<StripKeyframeData>(*action);
const KeyframeSettings settings = get_keyframe_settings(false);
/* Insert 3 FCurves for each slot. */
for (int i = 0; i < 3; i++) {
SingleKeyingResult result_cube = strip_data.keyframe_insert(
bmain, cube_slot, {"location", i}, {1.0f, 0.0f}, settings);
ASSERT_EQ(SingleKeyingResult::SUCCESS, result_cube)
<< "Expected keyframe insertion to be successful";
SingleKeyingResult result_monkey = strip_data.keyframe_insert(
bmain, monkey_slot, {"rotation", i}, {1.0f, 0.0f}, settings);
ASSERT_EQ(SingleKeyingResult::SUCCESS, result_monkey)
<< "Expected keyframe insertion to be successful";
}
/* Get all FCurves. */
blender::Vector<FCurve *> cube_fcurves;
foreach_fcurve_in_action_slot(
*action, cube_slot.handle, [&](FCurve &fcurve) { cube_fcurves.append(&fcurve); });
ASSERT_EQ(cube_fcurves.size(), 3);
for (FCurve *fcurve : cube_fcurves) {
ASSERT_STREQ(fcurve->rna_path, "location");
}
/* Get only FCurves with index 0 which should be 1. */
blender::Vector<FCurve *> monkey_fcurves;
foreach_fcurve_in_action_slot(*action, monkey_slot.handle, [&](FCurve &fcurve) {
if (fcurve.array_index == 0) {
monkey_fcurves.append(&fcurve);
}
});
ASSERT_EQ(monkey_fcurves.size(), 1);
ASSERT_STREQ(monkey_fcurves[0]->rna_path, "rotation");
/* Slots handles are just numbers. Passing in a slot handle that doesn't exist should return
* nothing. */
blender::Vector<FCurve *> invalid_slot_fcurves;
foreach_fcurve_in_action_slot(*action,
monkey_slot.handle + cube_slot.handle,
[&](FCurve &fcurve) { invalid_slot_fcurves.append(&fcurve); });
ASSERT_TRUE(invalid_slot_fcurves.is_empty());
}
TEST_F(ActionIteratorsTest, foreach_action_slot_use_with_references)
{
/* Create a cube and assign the Action + a slot. */
Object *cube = BKE_id_new<Object>(bmain, "OBCube");
Slot *slot_cube = assign_action_ensure_slot_for_keying(*action, cube->id);
ASSERT_NE(slot_cube, nullptr);
/* Create another Action with slot to assign. */
Action &other_action = BKE_id_new<bAction>(bmain, "ACAnotherAction")->wrap();
Slot &another_slot = other_action.slot_add();
std::optional<ActionSlotAssignmentResult> slot_assignment_result;
bool all_assigns_ok = true;
const auto assign_other_action = [&](ID & /* animated_id */,
bAction *&action_ptr_ref,
slot_handle_t &slot_handle_ref,
char *last_slot_identifier) -> bool {
/* Assign the other Action. */
all_assigns_ok &= generic_assign_action(
cube->id, &other_action, action_ptr_ref, slot_handle_ref, last_slot_identifier);
/* Assign the slot of the other Action. */
slot_assignment_result = generic_assign_action_slot(
&another_slot, cube->id, action_ptr_ref, slot_handle_ref, last_slot_identifier);
return true;
};
foreach_action_slot_use_with_references(cube->id, assign_other_action);
ASSERT_TRUE(all_assigns_ok);
/* Check the result, the slot assignment should have been changed. */
ASSERT_TRUE(slot_assignment_result.has_value());
EXPECT_EQ(ActionSlotAssignmentResult::OK, slot_assignment_result.value());
std::optional<std::pair<Action *, Slot *>> action_and_slot = get_action_slot_pair(cube->id);
ASSERT_TRUE(action_and_slot.has_value());
EXPECT_EQ(&other_action, action_and_slot->first)
<< "Expected Action " << other_action.id.name << " but found "
<< action_and_slot->first->id.name;
EXPECT_EQ(&another_slot, action_and_slot->second)
<< "Expected Slot " << another_slot.identifier << " but found "
<< action_and_slot->second->identifier;
}
TEST_F(ActionIteratorsTest, foreach_action_slot_use_with_rna)
{
/* Create a cube and assign the Action + a slot. */
Object *cube = BKE_id_new<Object>(bmain, "OBCube");
Slot *slot_cube = assign_action_ensure_slot_for_keying(*action, cube->id);
ASSERT_NE(slot_cube, nullptr);
Slot &another_slot = action->slot_add();
const auto assign_other_slot = [&](ID & /* animated_id */,
bAction *action,
PointerRNA &action_slot_owner_ptr,
PropertyRNA &action_slot_prop,
char * /*last_slot_identifier*/) -> bool {
PointerRNA rna_slot = RNA_pointer_create_discrete(&action->id, &RNA_ActionSlot, &another_slot);
RNA_property_pointer_set(&action_slot_owner_ptr, &action_slot_prop, rna_slot, nullptr);
return true;
};
foreach_action_slot_use_with_rna(cube->id, assign_other_slot);
/* Check the result, the slot assignment should have been changed. */
std::optional<std::pair<Action *, Slot *>> action_and_slot = get_action_slot_pair(cube->id);
ASSERT_TRUE(action_and_slot.has_value());
EXPECT_EQ(action, action_and_slot->first)
<< "Expected Action " << action->id.name << " but found " << action_and_slot->first->id.name;
EXPECT_EQ(&another_slot, action_and_slot->second)
<< "Expected Slot " << another_slot.identifier << " but found "
<< action_and_slot->second->identifier;
}
} // namespace blender::animrig::tests