Fix #141909: Creating a pose assets captures unkeyed custom properties
This was an oversight caused by 358a0479e8
Before this, only keyed custom properties were capture into the pose asset.
This behavior is now restored.
Pull Request: https://projects.blender.org/blender/blender/pulls/141937
This commit is contained in:
committed by
Christoph Lendenfeld
parent
23d02ec57b
commit
0191848671
@@ -122,6 +122,19 @@ static blender::animrig::Action &extract_pose(Main &bmain,
|
||||
BLI_assert(pose_object->pose);
|
||||
Slot &slot = action.slot_add_for_id(pose_object->id);
|
||||
const bArmature *armature = static_cast<bArmature *>(pose_object->data);
|
||||
|
||||
Set<RNAPath> existing_paths;
|
||||
if (pose_object->adt && pose_object->adt->action &&
|
||||
pose_object->adt->slot_handle != Slot::unassigned)
|
||||
{
|
||||
Action &pose_object_action = pose_object->adt->action->wrap();
|
||||
const slot_handle_t pose_object_slot = pose_object->adt->slot_handle;
|
||||
foreach_fcurve_in_action_slot(pose_object_action, pose_object_slot, [&](FCurve &fcurve) {
|
||||
RNAPath existing_path = {fcurve.rna_path, std::nullopt, fcurve.array_index};
|
||||
existing_paths.add(existing_path);
|
||||
});
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (bPoseChannel *, pose_bone, &pose_object->pose->chanbase) {
|
||||
if (!(pose_bone->bone->flag & BONE_SELECTED) ||
|
||||
!blender::animrig::bone_is_visible(armature, pose_bone->bone))
|
||||
@@ -147,6 +160,12 @@ static blender::animrig::Action &extract_pose(Main &bmain,
|
||||
continue;
|
||||
}
|
||||
for (const int i : values.index_range()) {
|
||||
if (RNA_property_is_idprop(resolved_property) &&
|
||||
!existing_paths.contains({rna_path_id_to_prop.value(), std::nullopt, i}))
|
||||
{
|
||||
/* Skipping custom properties without animation. */
|
||||
continue;
|
||||
}
|
||||
strip_data.keyframe_insert(
|
||||
&bmain, slot, {rna_path_id_to_prop.value(), i}, {1, values[i]}, key_settings);
|
||||
}
|
||||
|
||||
@@ -85,14 +85,18 @@ class CreateAssetTest(unittest.TestCase):
|
||||
self._armature_object.pose.bones[_BONE_NAME_1].bone.select = True
|
||||
self._armature_object.pose.bones[_BONE_NAME_2].bone.select = False
|
||||
|
||||
self.assertEqual(len(bpy.data.actions), 0)
|
||||
self._armature_object.pose.bones[_BONE_NAME_1].keyframe_insert('["bool_test"]')
|
||||
self._armature_object.pose.bones[_BONE_NAME_1].keyframe_insert('["float_test"]')
|
||||
|
||||
# There is an action for the custom properties.
|
||||
self.assertEqual(len(bpy.data.actions), 1)
|
||||
bpy.ops.poselib.create_pose_asset(
|
||||
pose_name="local_asset",
|
||||
asset_library_reference='LOCAL',
|
||||
catalog_path="unit_test")
|
||||
|
||||
self.assertEqual(len(bpy.data.actions), 1, "Local poses should be stored as actions")
|
||||
pose_action = bpy.data.actions[0]
|
||||
self.assertEqual(len(bpy.data.actions), 2, "Local poses should be stored as actions")
|
||||
pose_action = bpy.data.actions[1]
|
||||
self.assertTrue(pose_action.asset_data is not None, "The created action should be marked as an asset")
|
||||
|
||||
expected_pose_values = {
|
||||
@@ -122,13 +126,17 @@ class CreateAssetTest(unittest.TestCase):
|
||||
self._armature_object.pose.bones[_BONE_NAME_1].bone.select = True
|
||||
self._armature_object.pose.bones[_BONE_NAME_2].bone.select = False
|
||||
|
||||
self.assertEqual(len(bpy.data.actions), 0)
|
||||
self._armature_object.pose.bones[_BONE_NAME_1].keyframe_insert('["bool_test"]')
|
||||
self._armature_object.pose.bones[_BONE_NAME_1].keyframe_insert('["float_test"]')
|
||||
|
||||
# There is an action for the custom properties.
|
||||
self.assertEqual(len(bpy.data.actions), 1)
|
||||
bpy.ops.poselib.create_pose_asset(
|
||||
pose_name="local_asset",
|
||||
asset_library_reference=_LIB_NAME,
|
||||
catalog_path="unit_test")
|
||||
|
||||
self.assertEqual(len(bpy.data.actions), 0, "The asset should not have been created in this file")
|
||||
self.assertEqual(len(bpy.data.actions), 1, "The asset should not have been created in this file")
|
||||
actions_folder = os.path.join(self._library.path, "Saved", "Actions")
|
||||
asset_files = os.listdir(actions_folder)
|
||||
self.assertEqual(len(asset_files),
|
||||
@@ -160,6 +168,41 @@ class CreateAssetTest(unittest.TestCase):
|
||||
self.assertAlmostEqual(fcurve.keyframe_points[0].co.y,
|
||||
expected_pose_values[fcurve.data_path][fcurve.array_index], 4)
|
||||
|
||||
def test_custom_properties_without_keys(self):
|
||||
# Custom properties without keys should not be added to the pose asset.
|
||||
self._armature_object.pose.bones[_BONE_NAME_1].location = (1, 1, 2)
|
||||
self._armature_object.pose.bones[_BONE_NAME_2].location = (-1, 0, 0)
|
||||
|
||||
self._armature_object.pose.bones[_BONE_NAME_1].bone.select = True
|
||||
self._armature_object.pose.bones[_BONE_NAME_2].bone.select = False
|
||||
|
||||
self.assertEqual(len(bpy.data.actions), 0)
|
||||
bpy.ops.poselib.create_pose_asset(
|
||||
pose_name="local_asset",
|
||||
asset_library_reference='LOCAL',
|
||||
catalog_path="unit_test")
|
||||
|
||||
pose_action = bpy.data.actions[0]
|
||||
self.assertTrue(pose_action.asset_data is not None, "The created action should be marked as an asset")
|
||||
|
||||
expected_pose_values = {
|
||||
f'pose.bones["{_BONE_NAME_1}"].location': (1, 1, 2),
|
||||
f'pose.bones["{_BONE_NAME_1}"].rotation_quaternion': (1, 0, 0, 0),
|
||||
f'pose.bones["{_BONE_NAME_1}"].scale': (1, 1, 1),
|
||||
|
||||
# The custom properties are not keyed, thus they should not be in the pose asset.
|
||||
}
|
||||
expected_pose_values.update(_BBONE_VALUES)
|
||||
self.assertEqual(len(pose_action.fcurves), 24)
|
||||
for fcurve in pose_action.fcurves:
|
||||
self.assertTrue(
|
||||
fcurve.data_path in expected_pose_values,
|
||||
"Only the selected bone should be in the pose asset")
|
||||
self.assertEqual(len(fcurve.keyframe_points), 1, "Only one key should have been created")
|
||||
self.assertEqual(fcurve.keyframe_points[0].co.x, 1, "Poses should be on the first frame")
|
||||
self.assertAlmostEqual(fcurve.keyframe_points[0].co.y,
|
||||
expected_pose_values[fcurve.data_path][fcurve.array_index], 4)
|
||||
|
||||
|
||||
def main():
|
||||
global args
|
||||
|
||||
Reference in New Issue
Block a user