diff --git a/source/blender/animrig/ANIM_armature.hh b/source/blender/animrig/ANIM_armature.hh index 161a4543884..206bd351be5 100644 --- a/source/blender/animrig/ANIM_armature.hh +++ b/source/blender/animrig/ANIM_armature.hh @@ -48,7 +48,7 @@ inline bool bone_is_selected(const bArmature *armature, const Bone *bone) inline bool bone_is_selected(const bArmature *armature, const bPoseChannel *pchan) { - return (pchan->bone->flag & BONE_SELECTED) && bone_is_visible(armature, pchan); + return (pchan->flag & POSE_SELECTED) && bone_is_visible(armature, pchan); } inline bool bone_is_selected(const bArmature *armature, const EditBone *ebone) @@ -74,4 +74,12 @@ void pose_bone_descendent_iterator(bPose &pose, bPoseChannel &pose_bone, FunctionRef callback); +/** + * Iterates all descendents of the given pose bone depth first. The traversal for a branch is + * stopped if the callback returns false. Returns true if the iteration completed or false if it + * was stopped before visiting all bones. + */ +bool pose_bone_descendent_depth_iterator(bPose &pose, + bPoseChannel &pose_bone, + FunctionRef callback); } // namespace blender::animrig diff --git a/source/blender/animrig/intern/armature.cc b/source/blender/animrig/intern/armature.cc index 014a30519c3..276cb9d2e3a 100644 --- a/source/blender/animrig/intern/armature.cc +++ b/source/blender/animrig/intern/armature.cc @@ -40,4 +40,37 @@ void pose_bone_descendent_iterator(bPose &pose, } }; +static bool pose_depth_iterator_recursive(bPose &pose, + bPoseChannel &pose_bone, + FunctionRef callback) +{ + if (!callback(pose_bone)) { + return false; + } + if (!pose_bone.bone) { + BLI_assert_unreachable(); + return false; + } + bool success = true; + LISTBASE_FOREACH (Bone *, child_bone, &pose_bone.bone->childbase) { + bPoseChannel *child_pose_bone = BKE_pose_channel_find_name(&pose, child_bone->name); + if (!child_pose_bone) { + BLI_assert_unreachable(); + success = false; + continue; + } + success &= pose_depth_iterator_recursive(pose, *child_pose_bone, callback); + } + return success; +} + +bool pose_bone_descendent_depth_iterator(bPose &pose, + bPoseChannel &pose_bone, + FunctionRef callback) +{ + /* Needed for fast name lookups. */ + BKE_pose_channels_hash_ensure(&pose); + return pose_depth_iterator_recursive(pose, pose_bone, callback); +} + } // namespace blender::animrig diff --git a/source/blender/animrig/intern/pose.cc b/source/blender/animrig/intern/pose.cc index a04bbe1e230..937fdfda018 100644 --- a/source/blender/animrig/intern/pose.cc +++ b/source/blender/animrig/intern/pose.cc @@ -67,9 +67,8 @@ void pose_apply(Object *ob, return; } - const bArmature *armature = static_cast(ob->data); const blender::bke::BoneNameSet selected_bone_names = - blender::bke::BKE_armature_find_selected_bone_names(armature); + blender::bke::BKE_pose_channel_find_selected_names(ob); /* Mute all FCurves that are not associated with selected bones. This separates the concept of * bone selection from the FCurve evaluation code. */ @@ -128,7 +127,7 @@ bool any_bone_selected(const blender::Span objects) continue; } LISTBASE_FOREACH (bPoseChannel *, pose_bone, &obj->pose->chanbase) { - if (pose_bone->bone->flag & BONE_SELECTED) { + if (pose_bone->flag & POSE_SELECTED) { return true; } } diff --git a/source/blender/animrig/intern/pose_test.cc b/source/blender/animrig/intern/pose_test.cc index e411d68b2ac..835ceeff20e 100644 --- a/source/blender/animrig/intern/pose_test.cc +++ b/source/blender/animrig/intern/pose_test.cc @@ -229,8 +229,8 @@ TEST_F(PoseTest, apply_action_blend_single_slot) bone_a->loc[0] = 0.0; bone_b->loc[1] = 0.0; - bone_a->bone->flag |= BONE_SELECTED; - bone_b->bone->flag &= ~BONE_SELECTED; + bone_a->flag |= POSE_SELECTED; + bone_b->flag &= ~POSE_SELECTED; /* This should only affect the selected bone. */ blender::animrig::pose_apply_action_blend( @@ -265,7 +265,7 @@ TEST_F(PoseTest, apply_action_multiple_objects) arm_a_bone_a, arm_a_bone_b, arm_b_bone_a, arm_b_bone_b}; for (bPoseChannel *pose_bone : all_bones) { - pose_bone->bone->flag &= ~BONE_SELECTED; + pose_bone->flag &= ~POSE_SELECTED; pose_bone->loc[0] = 0.0; pose_bone->loc[1] = 0.0; } @@ -285,7 +285,7 @@ TEST_F(PoseTest, apply_action_multiple_objects) pose_bone->loc[1] = 0.0; } - arm_a_bone_a->bone->flag |= BONE_SELECTED; + arm_a_bone_a->flag |= POSE_SELECTED; blender::animrig::pose_apply_action( {obj_armature_a, obj_armature_b}, *pose_action, &eval_context, 1.0); @@ -301,8 +301,8 @@ TEST_F(PoseTest, apply_action_multiple_objects) pose_bone->loc[1] = 0.0; } - arm_a_bone_a->bone->flag |= BONE_SELECTED; - arm_b_bone_a->bone->flag |= BONE_SELECTED; + arm_a_bone_a->flag |= POSE_SELECTED; + arm_b_bone_a->flag |= POSE_SELECTED; blender::animrig::pose_apply_action( {obj_armature_a, obj_armature_b}, *pose_action, &eval_context, 1.0); @@ -332,9 +332,9 @@ TEST_F(PoseTest, apply_action_multiple_objects) pose_bone->loc[1] = 0.0; } - arm_a_bone_a->bone->flag |= BONE_SELECTED; - arm_a_bone_b->bone->flag |= BONE_SELECTED; - arm_b_bone_a->bone->flag |= BONE_SELECTED; + arm_a_bone_a->flag |= POSE_SELECTED; + arm_a_bone_b->flag |= POSE_SELECTED; + arm_b_bone_a->flag |= POSE_SELECTED; blender::animrig::pose_apply_action( {obj_armature_a, obj_armature_b}, *pose_action, &eval_context, 1.0); @@ -364,7 +364,7 @@ TEST_F(PoseTest, apply_action_multiple_objects_single_slot) arm_a_bone_a, arm_a_bone_b, arm_b_bone_a, arm_b_bone_b}; for (bPoseChannel *pose_bone : all_bones) { - pose_bone->bone->flag &= ~BONE_SELECTED; + pose_bone->flag &= ~POSE_SELECTED; pose_bone->loc[0] = 0.0; pose_bone->loc[1] = 0.0; } diff --git a/source/blender/blenkernel/BKE_action.hh b/source/blender/blenkernel/BKE_action.hh index 7cf9188b0f1..47d78df4c13 100644 --- a/source/blender/blenkernel/BKE_action.hh +++ b/source/blender/blenkernel/BKE_action.hh @@ -371,7 +371,7 @@ void BKE_pose_rest(bPose *pose, bool selected_bones_only); */ void BKE_pose_tag_recalc(Main *bmain, bPose *pose) ATTR_NONNULL(1, 2); -void BKE_pose_blend_write(BlendWriter *writer, bPose *pose, bArmature *arm) ATTR_NONNULL(1, 2, 3); +void BKE_pose_blend_write(BlendWriter *writer, bPose *pose) ATTR_NONNULL(1, 2); void BKE_pose_blend_read_data(BlendDataReader *reader, ID *id_owner, bPose *pose) ATTR_NONNULL(1, 2); void BKE_pose_blend_read_after_liblink(BlendLibReader *reader, Object *ob, bPose *pose) diff --git a/source/blender/blenkernel/BKE_armature.hh b/source/blender/blenkernel/BKE_armature.hh index 54754bbe4aa..e1f7c3dff59 100644 --- a/source/blender/blenkernel/BKE_armature.hh +++ b/source/blender/blenkernel/BKE_armature.hh @@ -574,7 +574,7 @@ void BKE_pchan_bbone_deform_segment_index(const bPoseChannel *pchan, _pchan = _pchan->next) \ { \ if (blender::animrig::bone_is_visible(((bArmature *)(_ob)->data), _pchan) && \ - ((_pchan)->bone->flag & BONE_SELECTED)) \ + ((_pchan)->flag & POSE_SELECTED)) \ { #define FOREACH_PCHAN_SELECTED_IN_OBJECT_END \ } \ @@ -692,4 +692,5 @@ using BoneNameSet = blender::Set; */ BoneNameSet BKE_armature_find_selected_bone_names(const bArmature *armature); +BoneNameSet BKE_pose_channel_find_selected_names(const Object *object); }; // namespace blender::bke diff --git a/source/blender/blenkernel/intern/action.cc b/source/blender/blenkernel/intern/action.cc index 08f33456219..826e091cde5 100644 --- a/source/blender/blenkernel/intern/action.cc +++ b/source/blender/blenkernel/intern/action.cc @@ -1879,8 +1879,7 @@ void BKE_pose_rest(bPose *pose, bool selected_bones_only) memset(pose->cyclic_offset, 0, sizeof(pose->cyclic_offset)); LISTBASE_FOREACH (bPoseChannel *, pchan, &pose->chanbase) { - if (selected_bones_only && pchan->bone != nullptr && (pchan->bone->flag & BONE_SELECTED) == 0) - { + if (selected_bones_only && pchan->bone != nullptr && (pchan->flag & POSE_SELECTED) == 0) { continue; } zero_v3(pchan->loc); @@ -2089,10 +2088,10 @@ void BKE_pose_check_uids_unique_and_report(const bPose *pose) BLI_gset_free(used_uids, nullptr); } -void BKE_pose_blend_write(BlendWriter *writer, bPose *pose, bArmature *arm) +void BKE_pose_blend_write(BlendWriter *writer, bPose *pose) { #ifndef __GNUC__ - BLI_assert(pose != nullptr && arm != nullptr); + BLI_assert(pose != nullptr); #endif /* Write channels */ @@ -2110,17 +2109,6 @@ void BKE_pose_blend_write(BlendWriter *writer, bPose *pose, bArmature *arm) animviz_motionpath_blend_write(writer, chan->mpath); - /* Prevent crashes with auto-save, - * when a bone duplicated in edit-mode has not yet been assigned to its pose-channel. - * Also needed with memundo, in some cases we can store a step before pose has been - * properly rebuilt from previous undo step. */ - Bone *bone = (pose->flag & POSE_RECALC) ? BKE_armature_find_bone_name(arm, chan->name) : - chan->bone; - if (bone != nullptr) { - /* gets restored on read, for library armatures */ - chan->selectflag = bone->flag & BONE_SELECTED; - } - BLO_write_struct(writer, bPoseChannel, chan); } @@ -2220,11 +2208,6 @@ void BKE_pose_blend_read_after_liblink(BlendLibReader *reader, Object *ob, bPose if (UNLIKELY(pchan->bone == nullptr)) { rebuild = true; } - else if (!ID_IS_LINKED(ob) && ID_IS_LINKED(arm)) { - /* local pose selection copied to armature, bit hackish */ - pchan->bone->flag &= ~BONE_SELECTED; - pchan->bone->flag |= pchan->selectflag; - } /* At some point in history, bones could have an armature object as custom shape, which caused * all kinds of wonderful issues. This is now avoided in RNA, but through the magic of linking diff --git a/source/blender/blenkernel/intern/armature_selection.cc b/source/blender/blenkernel/intern/armature_selection.cc index f613e455833..c3202b2169e 100644 --- a/source/blender/blenkernel/intern/armature_selection.cc +++ b/source/blender/blenkernel/intern/armature_selection.cc @@ -13,6 +13,7 @@ #include "BLI_listbase.h" #include "DNA_armature_types.h" +#include "DNA_object_types.h" namespace blender::bke { @@ -35,6 +36,7 @@ void find_selected_bones__visit_bone(const bArmature *armature, find_selected_bones__visit_bone(armature, callback, result, child_bone); } } + } // namespace SelectedBonesResult BKE_armature_find_selected_bones(const bArmature *armature, @@ -58,4 +60,19 @@ BoneNameSet BKE_armature_find_selected_bone_names(const bArmature *armature) return selected_bone_names; } +BoneNameSet BKE_pose_channel_find_selected_names(const Object *object) +{ + if (!object->pose) { + return {}; + } + + BoneNameSet selected_bone_names; + LISTBASE_FOREACH (bPoseChannel *, pose_bone, &object->pose->chanbase) { + if (pose_bone->flag & POSE_SELECTED) { + selected_bone_names.add(pose_bone->name); + } + } + return selected_bone_names; +} + } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index f3a9a42a57b..bdace0c7263 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -637,13 +637,9 @@ static void object_blend_write(BlendWriter *writer, ID *id, const void *id_addre BLO_write_pointer_array(writer, ob->totcol, ob->mat); BLO_write_char_array(writer, ob->totcol, ob->matbits); - bArmature *arm = nullptr; - if (ob->type == OB_ARMATURE) { - arm = (bArmature *)ob->data; - } - if (ob->pose) { - BKE_pose_blend_write(writer, ob->pose, arm); + BLI_assert(ob->type == OB_ARMATURE); + BKE_pose_blend_write(writer, ob->pose); } BKE_constraint_blend_write(writer, &ob->constraints); animviz_motionpath_blend_write(writer, ob->mpath); diff --git a/source/blender/blenkernel/intern/object_deform.cc b/source/blender/blenkernel/intern/object_deform.cc index 8a08ffd5fa4..49704167cb6 100644 --- a/source/blender/blenkernel/intern/object_deform.cc +++ b/source/blender/blenkernel/intern/object_deform.cc @@ -648,7 +648,7 @@ bool *BKE_object_defgroup_selected_get(Object *ob, int defbase_tot, int *r_dg_fl defgroup = defgroup->next, i++) { bPoseChannel *pchan = BKE_pose_channel_find_name(pose, defgroup->name); - if (pchan && (pchan->bone->flag & BONE_SELECTED)) { + if (pchan && (pchan->flag & POSE_SELECTED)) { dg_selection[i] = true; (*r_dg_flags_sel_tot) += 1; } diff --git a/source/blender/blenkernel/intern/pose_backup.cc b/source/blender/blenkernel/intern/pose_backup.cc index cd51935adcd..56a8abd7a43 100644 --- a/source/blender/blenkernel/intern/pose_backup.cc +++ b/source/blender/blenkernel/intern/pose_backup.cc @@ -109,7 +109,7 @@ static blender::Set armature_find_selected_pose_bones( for (Object *obj : objects) { /* Iterate over the selected bones to fill the set of bone names. */ LISTBASE_FOREACH (bPoseChannel *, pose_bone, &obj->pose->chanbase) { - if (pose_bone->bone->flag & BONE_SELECTED) { + if (pose_bone->flag & POSE_SELECTED) { selected_bones.add(pose_bone); } else { @@ -147,8 +147,7 @@ PoseBackup *BKE_pose_backup_create_selected_bones(blender::Span object pose_backup->is_bone_selection_relevant = !selected_bones.is_empty(); for (Object *ob : objects) { - const bArmature *armature = static_cast(ob->data); - const BoneNameSet selected_bone_names = BKE_armature_find_selected_bone_names(armature); + const BoneNameSet selected_bone_names = BKE_pose_channel_find_selected_names(ob); pose_backup_create(ob, const_cast(action), selected_bone_names, *pose_backup); } diff --git a/source/blender/draw/engines/overlay/overlay_armature.cc b/source/blender/draw/engines/overlay/overlay_armature.cc index 3bdef58588f..ab7cd82e78c 100644 --- a/source/blender/draw/engines/overlay/overlay_armature.cc +++ b/source/blender/draw/engines/overlay/overlay_armature.cc @@ -128,7 +128,18 @@ class UnifiedBonePtr { eBone_Flag flag() const { - return static_cast(is_editbone_ ? eBone_->flag : pchan_->bone->flag); + if (is_editbone_) { + return static_cast(eBone_->flag); + } + /* Making sure the select flag is set correctly since it moved to the pose channel. */ + eBone_Flag flag = static_cast(pchan_->bone->flag); + if (pchan_->flag & POSE_SELECTED) { + flag |= BONE_SELECTED; + } + else { + flag &= ~BONE_SELECTED; + } + return flag; } /** Return the pose bone's constraint flags, or 0 if not a pose bone. */ @@ -1683,8 +1694,8 @@ static bool should_draw_relation_to_parent(const UnifiedBonePtr bone, const eBon /* Only draw if bone or its parent is selected - reduces viewport * complexity with complex rigs */ const bPoseChannel *pchan = bone.as_posebone(); - return (boneflag & BONE_SELECTED) || - (pchan->parent->bone && (pchan->parent->bone->flag & BONE_SELECTED)); + return (pchan->flag & POSE_SELECTED) || + (pchan->parent && (pchan->parent->flag & POSE_SELECTED)); } return false; @@ -1807,7 +1818,7 @@ static void draw_bone_relations(const Armatures::DrawContext *ctx, /* Draw a line to IK root bone if bone is selected. */ if (ctx->draw_mode == ARM_DRAW_MODE_POSE) { if (pchan->constflag & (PCHAN_HAS_IK | PCHAN_HAS_SPLINEIK)) { - if (boneflag & BONE_SELECTED) { + if (pchan->flag & POSE_SELECTED) { pchan_draw_ik_lines(ctx, pchan, !ctx->do_relations); } } @@ -1816,9 +1827,7 @@ static void draw_bone_relations(const Armatures::DrawContext *ctx, } } -static void draw_bone_name(const Armatures::DrawContext *ctx, - const UnifiedBonePtr bone, - const eBone_Flag boneflag) +static void draw_bone_name(const Armatures::DrawContext *ctx, const UnifiedBonePtr bone) { uchar color[4]; float vec[3]; @@ -1830,7 +1839,7 @@ static void draw_bone_name(const Armatures::DrawContext *ctx, /* TODO: make this look at `boneflag` only. */ bool highlight = (is_pose && ctx->draw_mode == ARM_DRAW_MODE_POSE && - (boneflag & BONE_SELECTED)) || + (pchan->flag & POSE_SELECTED)) || (!is_pose && (eBone->flag & BONE_SELECTED)); /* Color Management: Exception here as texts are drawn in sRGB space directly. */ @@ -1934,7 +1943,7 @@ void Armatures::draw_armature_edit(Armatures::DrawContext *ctx) if (!is_select) { if (show_text && (arm.flag & ARM_DRAWNAMES)) { - draw_bone_name(ctx, bone, boneflag); + draw_bone_name(ctx, bone); } if (arm.flag & ARM_DRAWAXES) { @@ -2030,7 +2039,7 @@ void Armatures::draw_armature_pose(Armatures::DrawContext *ctx) Bone *bone = pchan->bone; const bool draw_dofs = !is_pose_select && ctx->show_relations && (ctx->draw_mode == ARM_DRAW_MODE_POSE) && - (bone->flag & BONE_SELECTED) && + (pchan->flag & POSE_SELECTED) && ((ob->base_flag & BASE_FROM_DUPLI) == 0) && (pchan->ikflag & (BONE_IK_XLIMIT | BONE_IK_ZLIMIT)); const int select_id = is_pose_select ? index : uint(-1); @@ -2042,7 +2051,7 @@ void Armatures::draw_armature_pose(Armatures::DrawContext *ctx) set_ctx_bcolor(ctx, bone_ptr); } - eBone_Flag boneflag = eBone_Flag(bone->flag); + eBone_Flag boneflag = bone_ptr.flag(); if (pchan->parent && !blender::animrig::bone_is_visible(&arm, pchan->parent)) { /* Avoid drawing connection line to hidden parent. */ boneflag &= ~BONE_CONNECTED; @@ -2075,7 +2084,7 @@ void Armatures::draw_armature_pose(Armatures::DrawContext *ctx) draw_bone_degrees_of_freedom(ctx, pchan); } if (show_text && (arm.flag & ARM_DRAWNAMES)) { - draw_bone_name(ctx, bone_ptr, boneflag); + draw_bone_name(ctx, bone_ptr); } if (arm.flag & ARM_DRAWAXES) { draw_axes(ctx, bone_ptr, arm); diff --git a/source/blender/draw/engines/overlay/overlay_motion_path.hh b/source/blender/draw/engines/overlay/overlay_motion_path.hh index 401423c90d8..826fd4b398f 100644 --- a/source/blender/draw/engines/overlay/overlay_motion_path.hh +++ b/source/blender/draw/engines/overlay/overlay_motion_path.hh @@ -114,7 +114,7 @@ class MotionPath : Overlay { const bool show_frame_number = (avs.path_viewflag & MOTIONPATH_VIEW_FNUMS); const bool show_lines = (mpath->flag & MOTIONPATH_FLAG_LINES); const bool custom_color = (mpath->flag & MOTIONPATH_FLAG_CUSTOM); - const bool selected = (pchan) ? (pchan->bone->flag & BONE_SELECTED) : + const bool selected = (pchan) ? (pchan->flag & POSE_SELECTED) : (ob->base_flag & BASE_SELECTED); const float3 color_pre = custom_color ? float3(mpath->color) : float3(-1.0f); diff --git a/source/blender/editors/animation/anim_deps.cc b/source/blender/editors/animation/anim_deps.cc index ebdd21c0fbe..26fc6e4b10a 100644 --- a/source/blender/editors/animation/anim_deps.cc +++ b/source/blender/editors/animation/anim_deps.cc @@ -146,7 +146,7 @@ static void animchan_sync_group(bAnimContext *ac, bAnimListElem *ale, bActionGro if (pchan) { /* if one matches, sync the selection status */ - if ((pchan->bone) && (pchan->bone->flag & BONE_SELECTED)) { + if ((pchan->bone) && (pchan->flag & POSE_SELECTED)) { agrp->flag |= AGRP_SELECTED; } else { diff --git a/source/blender/editors/animation/anim_filter.cc b/source/blender/editors/animation/anim_filter.cc index 0e6f8a2ed14..1e11ebe02eb 100644 --- a/source/blender/editors/animation/anim_filter.cc +++ b/source/blender/editors/animation/anim_filter.cc @@ -1026,7 +1026,7 @@ static bool skip_fcurve_selected_data(bAnimContext *ac, /* can only add this F-Curve if it is selected */ if (ac->ads->filterflag & ADS_FILTER_ONLYSEL) { - if ((pchan->bone->flag & BONE_SELECTED) == 0) { + if ((pchan->flag & POSE_SELECTED) == 0) { return true; } } diff --git a/source/blender/editors/animation/keyframes_keylist_test.cc b/source/blender/editors/animation/keyframes_keylist_test.cc index fc9e5438dde..5b264946550 100644 --- a/source/blender/editors/animation/keyframes_keylist_test.cc +++ b/source/blender/editors/animation/keyframes_keylist_test.cc @@ -343,8 +343,11 @@ TEST_F(KeylistSummaryTest, slot_summary_bone_selection) ASSERT_EQ(SingleKeyingResult::SUCCESS, insert_vert_fcurve(&bone2_loc_x, {3.0, 3.0}, {}, {})); /* Select only Bone.001. */ - bone1->flag |= BONE_SELECTED; - bone2->flag &= ~BONE_SELECTED; + bPoseChannel *pose_bone1 = BKE_pose_channel_find_name(armature->pose, bone1->name); + ASSERT_NE(pose_bone1, nullptr); + pose_bone1->flag |= POSE_SELECTED; + bPoseChannel *pose_bone2 = BKE_pose_channel_find_name(armature->pose, bone2->name); + pose_bone2->flag &= ~POSE_SELECTED; /* Generate slot summary keylist. */ AnimKeylist *keylist = ED_keylist_create(); diff --git a/source/blender/editors/animation/keyframing.cc b/source/blender/editors/animation/keyframing.cc index 28a02831492..54c7fa13351 100644 --- a/source/blender/editors/animation/keyframing.cc +++ b/source/blender/editors/animation/keyframing.cc @@ -714,7 +714,7 @@ static bool can_delete_fcurve(FCurve *fcu, Object *ob) pchan = BKE_pose_channel_find_name(ob->pose, bone_name); /* Delete if bone is selected. */ if ((pchan) && (pchan->bone)) { - if (pchan->bone->flag & BONE_SELECTED) { + if (pchan->flag & POSE_SELECTED) { can_delete = true; } } diff --git a/source/blender/editors/armature/armature_select.cc b/source/blender/editors/armature/armature_select.cc index 4829813c9ff..d95ab8aaf22 100644 --- a/source/blender/editors/armature/armature_select.cc +++ b/source/blender/editors/armature/armature_select.cc @@ -183,10 +183,10 @@ static void *ed_armature_pick_bone_from_selectbuffer_impl(const bool is_editmode continue; } if (findunsel) { - sel = (pchan->bone->flag & BONE_SELECTED); + sel = (pchan->flag & POSE_SELECTED); } else { - sel = !(pchan->bone->flag & BONE_SELECTED); + sel = !(pchan->flag & POSE_SELECTED); } data = pchan; diff --git a/source/blender/editors/armature/armature_skinning.cc b/source/blender/editors/armature/armature_skinning.cc index 0c8f8b78042..efa6e9db125 100644 --- a/source/blender/editors/armature/armature_skinning.cc +++ b/source/blender/editors/armature/armature_skinning.cc @@ -175,7 +175,7 @@ static int dgroup_skinnable_cb(Object *ob, Bone *bone, void *datap) } if (!data->is_weight_paint || - (ANIM_bone_in_visible_collection(arm, bone) && (bone->flag & BONE_SELECTED))) + (ANIM_bone_in_visible_collection(arm, bone) && (pose_bone->flag & POSE_SELECTED))) { if (!(defgroup = BKE_object_defgroup_find_name(ob, bone->name))) { defgroup = BKE_object_defgroup_add_name(ob, bone->name); diff --git a/source/blender/editors/armature/pose_edit.cc b/source/blender/editors/armature/pose_edit.cc index 8ec05effcd7..c0a2ee555c7 100644 --- a/source/blender/editors/armature/pose_edit.cc +++ b/source/blender/editors/armature/pose_edit.cc @@ -83,6 +83,7 @@ bool ED_object_posemode_enter_ex(Main *bmain, Object *ob) case OB_ARMATURE: ob->restore_mode = ob->mode; ob->mode |= OB_MODE_POSE; + /* Inform all evaluated versions that we changed the mode. */ DEG_id_tag_update_ex(bmain, &ob->id, ID_RECALC_SYNC_TO_EVAL); ok = true; @@ -386,7 +387,7 @@ static void pose_clear_paths(Object *ob, bool only_selected) /* free the motionpath blocks for all bones - This is easier for users to quickly clear all */ LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { if (pchan->mpath) { - if ((only_selected == false) || ((pchan->bone) && (pchan->bone->flag & BONE_SELECTED))) { + if ((only_selected == false) || (pchan->flag & POSE_SELECTED)) { animviz_free_motionpath(pchan->mpath); pchan->mpath = nullptr; } @@ -680,11 +681,11 @@ static wmOperatorStatus pose_hide_exec(bContext *C, wmOperator *op) if (!ANIM_bone_in_visible_collection(arm, pchan->bone)) { continue; } - if (((pchan->bone->flag & BONE_SELECTED) != 0) != hide_select) { + if (((pchan->flag & POSE_SELECTED) != 0) != hide_select) { continue; } pchan->drawflag |= PCHAN_DRAW_HIDDEN; - pchan->bone->flag &= ~BONE_SELECTED; + pchan->flag &= ~POSE_SELECTED; changed = true; } @@ -737,7 +738,7 @@ static wmOperatorStatus pose_reveal_exec(bContext *C, wmOperator *op) continue; } if (!(pchan->bone->flag & BONE_UNSELECTABLE)) { - SET_FLAG_FROM_TEST(pchan->bone->flag, select, BONE_SELECTED); + SET_FLAG_FROM_TEST(pchan->flag, select, POSE_SELECTED); } pchan->drawflag &= ~PCHAN_DRAW_HIDDEN; changed = true; diff --git a/source/blender/editors/armature/pose_select.cc b/source/blender/editors/armature/pose_select.cc index 713c0957611..ee7ba5e14cd 100644 --- a/source/blender/editors/armature/pose_select.cc +++ b/source/blender/editors/armature/pose_select.cc @@ -64,18 +64,18 @@ static void pose_do_bone_select(bPoseChannel *pchan, const int select_mode) switch (select_mode) { case SEL_SELECT: if (!(pchan->bone->flag & BONE_UNSELECTABLE)) { - pchan->bone->flag |= BONE_SELECTED; + pchan->flag |= POSE_SELECTED; } break; case SEL_DESELECT: - pchan->bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + pchan->flag &= ~POSE_SELECTED; break; case SEL_INVERT: - if (pchan->bone->flag & BONE_SELECTED) { - pchan->bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + if (pchan->flag & POSE_SELECTED) { + pchan->flag &= ~POSE_SELECTED; } else if (!(pchan->bone->flag & BONE_UNSELECTABLE)) { - pchan->bone->flag |= BONE_SELECTED; + pchan->flag |= POSE_SELECTED; } break; } @@ -112,13 +112,13 @@ void ED_pose_bone_select(Object *ob, bPoseChannel *pchan, bool select, bool chan if (blender::animrig::bone_is_selectable(arm, pchan)) { /* change selection state - activate too if selected */ if (select) { - pchan->bone->flag |= BONE_SELECTED; + pchan->flag |= POSE_SELECTED; if (change_active) { arm->act_bone = pchan->bone; } } else { - pchan->bone->flag &= ~BONE_SELECTED; + pchan->flag &= ~POSE_SELECTED; if (change_active) { arm->act_bone = nullptr; } @@ -133,20 +133,20 @@ bool ED_armature_pose_select_pick_bone(const Scene *scene, ViewLayer *view_layer, View3D *v3d, Object *ob, - Bone *bone, + bPoseChannel *pchan, const SelectPick_Params ¶ms) { bool found = false; bool changed = false; if (ob->pose) { - if (bone && ((bone->flag & BONE_UNSELECTABLE) == 0)) { + if (pchan && pchan->bone && ((pchan->bone->flag & BONE_UNSELECTABLE) == 0)) { found = true; } } if (params.sel_op == SEL_OP_SET) { - if ((found && params.select_passthrough) && (bone->flag & BONE_SELECTED)) { + if ((found && params.select_passthrough) && (pchan->flag & POSE_SELECTED)) { found = false; } else if (found || params.deselect_all) { @@ -185,39 +185,39 @@ bool ED_armature_pose_select_pick_bone(const Scene *scene, * from another active object - always select the bone. */ if (params.sel_op == SEL_OP_SET) { /* Re-select the bone again later in this function. */ - bone->flag &= ~BONE_SELECTED; + pchan->flag &= ~POSE_SELECTED; } } switch (params.sel_op) { case SEL_OP_ADD: { - bone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - arm->act_bone = bone; + pchan->flag |= POSE_SELECTED; + arm->act_bone = pchan->bone; break; } case SEL_OP_SUB: { - bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + pchan->flag &= ~POSE_SELECTED; break; } case SEL_OP_XOR: { - if (bone->flag & BONE_SELECTED) { + if (pchan->flag & POSE_SELECTED) { /* If not active, we make it active. */ - if (bone != arm->act_bone) { - arm->act_bone = bone; + if (pchan->bone != arm->act_bone) { + arm->act_bone = pchan->bone; } else { - bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + pchan->flag &= ~POSE_SELECTED; } } else { - bone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - arm->act_bone = bone; + pchan->flag |= POSE_SELECTED; + arm->act_bone = pchan->bone; } break; } case SEL_OP_SET: { - bone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - arm->act_bone = bone; + pchan->flag |= POSE_SELECTED; + arm->act_bone = pchan->bone; break; } case SEL_OP_AND: { @@ -229,8 +229,8 @@ bool ED_armature_pose_select_pick_bone(const Scene *scene, if (ob_act) { /* In weight-paint we select the associated vertex group too. */ if (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) { - if (bone == arm->act_bone) { - blender::ed::object::vgroup_select_by_name(ob_act, bone->name); + if (pchan->bone && pchan->bone == arm->act_bone) { + blender::ed::object::vgroup_select_by_name(ob_act, pchan->bone->name); DEG_id_tag_update(&ob_act->id, ID_RECALC_GEOMETRY); } } @@ -264,7 +264,7 @@ bool ED_armature_pose_select_pick_with_buffer(const Scene *scene, bool do_nearest) { Object *ob = base->object; - Bone *nearBone; + bPoseChannel *nearBone; if (!ob || !ob->pose) { return false; @@ -272,7 +272,7 @@ bool ED_armature_pose_select_pick_with_buffer(const Scene *scene, /* Callers happen to already get the active base */ Base *base_dummy = nullptr; - nearBone = ED_armature_pick_bone_from_selectbuffer( + nearBone = ED_armature_pick_pchan_from_selectbuffer( {base}, hit_results, hits, true, do_nearest, &base_dummy); return ED_armature_pose_select_pick_bone(scene, view_layer, v3d, ob, nearBone, params); @@ -321,7 +321,7 @@ bool ED_pose_deselect_all(Object *ob, int select_mode, const bool ignore_visibil select_mode = SEL_SELECT; LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { if (ignore_visibility || blender::animrig::bone_is_visible(arm, pchan)) { - if (pchan->bone->flag & BONE_SELECTED) { + if (pchan->flag & POSE_SELECTED) { select_mode = SEL_DESELECT; break; } @@ -334,9 +334,9 @@ bool ED_pose_deselect_all(Object *ob, int select_mode, const bool ignore_visibil LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { /* ignore the pchan if it isn't visible or if its selection cannot be changed */ if (ignore_visibility || blender::animrig::bone_is_visible(arm, pchan)) { - int flag_prev = pchan->bone->flag; + int flag_prev = pchan->flag; pose_do_bone_select(pchan, select_mode); - changed = (changed || flag_prev != pchan->bone->flag); + changed = (changed || flag_prev != pchan->flag); } } return changed; @@ -347,7 +347,7 @@ static bool ed_pose_is_any_selected(Object *ob, bool ignore_visibility) bArmature *arm = static_cast(ob->data); LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { if (ignore_visibility || blender::animrig::bone_is_visible(arm, pchan)) { - if (pchan->bone->flag & BONE_SELECTED) { + if (pchan->flag & POSE_SELECTED) { return true; } } @@ -397,23 +397,29 @@ bool ED_pose_deselect_all_multi(bContext *C, int select_mode, const bool ignore_ /* ***************** Selections ********************** */ -static void selectconnected_posebonechildren(Object *ob, Bone *bone, int extend) +static void selectconnected_posebonechildren(Object &ob, + bPoseChannel &pose_bone, + const bool extend) { - /* stop when unconnected child is encountered, or when unselectable bone is encountered */ - if (!(bone->flag & BONE_CONNECTED) || (bone->flag & BONE_UNSELECTABLE)) { - return; - } + blender::animrig::pose_bone_descendent_depth_iterator( + *ob.pose, pose_bone, [extend](bPoseChannel &child) { + if (!child.bone) { + BLI_assert_unreachable(); + return false; + } + /* Stop when unconnected child is encountered, or when unselectable bone is encountered. */ + if (!(child.bone->flag & BONE_CONNECTED) || (child.bone->flag & BONE_UNSELECTABLE)) { + return false; + } - if (extend) { - bone->flag &= ~BONE_SELECTED; - } - else { - bone->flag |= BONE_SELECTED; - } - - LISTBASE_FOREACH (Bone *, curBone, &bone->childbase) { - selectconnected_posebonechildren(ob, curBone, extend); - } + if (extend) { + child.flag &= ~POSE_SELECTED; + } + else { + child.flag |= POSE_SELECTED; + } + return true; + }); } /* within active object context */ @@ -422,30 +428,30 @@ static wmOperatorStatus pose_select_connected_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - Bone *bone, *curBone, *next = nullptr; + bPoseChannel *pchan, *curBone, *next = nullptr; const bool extend = RNA_boolean_get(op->ptr, "extend"); view3d_operator_needs_gpu(C); Base *base = nullptr; - bone = ED_armature_pick_bone(C, event->mval, !extend, &base); + pchan = ED_armature_pick_pchan(C, event->mval, !extend, &base); - if (!bone) { + if (!pchan) { return OPERATOR_CANCELLED; } /* Select parents */ - for (curBone = bone; curBone; curBone = next) { + for (curBone = pchan; curBone; curBone = next) { /* ignore bone if cannot be selected */ if ((curBone->flag & BONE_UNSELECTABLE) == 0) { if (extend) { - curBone->flag &= ~BONE_SELECTED; + curBone->flag &= ~POSE_SELECTED; } else { - curBone->flag |= BONE_SELECTED; + curBone->flag |= POSE_SELECTED; } - if (curBone->flag & BONE_CONNECTED) { + if (curBone->bone->flag & BONE_CONNECTED) { next = curBone->parent; } else { @@ -458,9 +464,7 @@ static wmOperatorStatus pose_select_connected_invoke(bContext *C, } /* Select children */ - LISTBASE_FOREACH (Bone *, curBone, &bone->childbase) { - selectconnected_posebonechildren(base->object, curBone, extend); - } + selectconnected_posebonechildren(*base->object, *pchan, extend); ED_outliner_select_sync_from_pose_bone_tag(C); @@ -502,21 +506,21 @@ void POSE_OT_select_linked_pick(wmOperatorType *ot) static wmOperatorStatus pose_select_linked_exec(bContext *C, wmOperator * /*op*/) { - Bone *curBone, *next = nullptr; + bPoseChannel *curBone, *next = nullptr; CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, visible_pose_bones, Object *, ob) { - if ((pchan->bone->flag & BONE_SELECTED) == 0) { + if ((pchan->flag & POSE_SELECTED) == 0) { continue; } bArmature *arm = static_cast(ob->data); /* Select parents */ - for (curBone = pchan->bone; curBone; curBone = next) { + for (curBone = pchan; curBone; curBone = next) { if (blender::animrig::bone_is_selectable(arm, curBone)) { - curBone->flag |= BONE_SELECTED; + curBone->flag |= POSE_SELECTED; - if (curBone->flag & BONE_CONNECTED) { + if (curBone->bone->flag & BONE_CONNECTED) { next = curBone->parent; } else { @@ -529,9 +533,7 @@ static wmOperatorStatus pose_select_linked_exec(bContext *C, wmOperator * /*op*/ } /* Select children */ - LISTBASE_FOREACH (Bone *, curBone, &pchan->bone->childbase) { - selectconnected_posebonechildren(ob, curBone, false); - } + selectconnected_posebonechildren(*ob, *pchan, false); ED_pose_bone_select_tag_update(ob); } CTX_DATA_END; @@ -627,7 +629,7 @@ static wmOperatorStatus pose_select_parent_exec(bContext *C, wmOperator * /*op*/ if ((parent) && !(parent->drawflag & PCHAN_DRAW_HIDDEN) && !(parent->bone->flag & BONE_UNSELECTABLE)) { - parent->bone->flag |= BONE_SELECTED; + parent->flag |= POSE_SELECTED; arm->act_bone = parent->bone; } else { @@ -666,7 +668,7 @@ static wmOperatorStatus pose_select_constraint_target_exec(bContext *C, wmOperat bool found = false; CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) { - if (pchan->bone->flag & BONE_SELECTED) { + if (pchan->flag & POSE_SELECTED) { LISTBASE_FOREACH (bConstraint *, con, &pchan->constraints) { ListBase targets = {nullptr, nullptr}; if (BKE_constraint_targets_get(con, &targets)) { @@ -679,7 +681,7 @@ static wmOperatorStatus pose_select_constraint_target_exec(bContext *C, wmOperat { bPoseChannel *pchanc = BKE_pose_channel_find_name(ob->pose, ct->subtarget); if ((pchanc) && !(pchanc->bone->flag & BONE_UNSELECTABLE)) { - pchanc->bone->flag |= BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL; + pchanc->flag |= POSE_SELECTED; ED_pose_bone_select_tag_update(ob); found = true; } @@ -742,9 +744,9 @@ static wmOperatorStatus pose_select_hierarchy_exec(bContext *C, wmOperator *op) if (blender::animrig::bone_is_selectable(arm, bone_parent)) { if (!add_to_sel) { - pchan_act->bone->flag &= ~BONE_SELECTED; + pchan_act->flag &= ~POSE_SELECTED; } - bone_parent->flag |= BONE_SELECTED; + pchan_act->parent->flag |= POSE_SELECTED; arm->act_bone = bone_parent; changed = true; @@ -752,7 +754,7 @@ static wmOperatorStatus pose_select_hierarchy_exec(bContext *C, wmOperator *op) } } else { /* direction == BONE_SELECT_CHILD */ - Bone *bone_child = nullptr; + bPoseChannel *bone_child = nullptr; int pass; /* first pass, only connected bones (the logical direct child) */ @@ -762,7 +764,7 @@ static wmOperatorStatus pose_select_hierarchy_exec(bContext *C, wmOperator *op) if (blender::animrig::bone_is_selectable(arm, pchan_iter)) { if (pchan_iter->parent == pchan_act) { if ((pass == 1) || (pchan_iter->bone->flag & BONE_CONNECTED)) { - bone_child = pchan_iter->bone; + bone_child = pchan_iter; break; } } @@ -771,12 +773,12 @@ static wmOperatorStatus pose_select_hierarchy_exec(bContext *C, wmOperator *op) } if (bone_child) { - arm->act_bone = bone_child; + arm->act_bone = bone_child->bone; if (!add_to_sel) { - pchan_act->bone->flag &= ~BONE_SELECTED; + pchan_act->flag &= ~POSE_SELECTED; } - bone_child->flag |= BONE_SELECTED; + bone_child->flag |= POSE_SELECTED; changed = true; } @@ -851,7 +853,7 @@ static bool pose_select_same_color(bContext *C, const bool extend) */ if (!extend) { CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, selected_pose_bones, Object *, ob) { - pchan->bone->flag &= ~BONE_SELECTED; + pchan->flag &= ~POSE_SELECTED; updated_objects.add(ob); changed_any_selection = true; } @@ -866,7 +868,7 @@ static bool pose_select_same_color(bContext *C, const bool extend) /* Select all visible bones that have the same color. */ CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, visible_pose_bones, Object *, ob) { Bone *bone = pchan->bone; - if (bone->flag & (BONE_UNSELECTABLE | BONE_SELECTED)) { + if ((bone->flag & BONE_UNSELECTABLE) && (pchan->flag & POSE_SELECTED)) { /* Skip bones that are unselectable or already selected. */ continue; } @@ -876,7 +878,7 @@ static bool pose_select_same_color(bContext *C, const bool extend) continue; } - bone->flag |= BONE_SELECTED; + pchan->flag |= POSE_SELECTED; changed_any_selection = true; updated_objects.add(ob); } @@ -906,7 +908,7 @@ static bool pose_select_same_collection(bContext *C, const bool extend) if (!extend) { /* Deselect all the bones. */ CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, selected_pose_bones, Object *, ob) { - pchan->bone->flag &= ~BONE_SELECTED; + pchan->flag &= ~POSE_SELECTED; updated_objects.add(ob); changed_any_selection = true; } @@ -923,7 +925,7 @@ static bool pose_select_same_collection(bContext *C, const bool extend) /* Select all bones that match any of the collection names. */ CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, visible_pose_bones, Object *, ob) { Bone *bone = pchan->bone; - if (bone->flag & (BONE_UNSELECTABLE | BONE_SELECTED)) { + if ((pchan->flag & POSE_SELECTED) && bone->flag & BONE_UNSELECTABLE) { continue; } @@ -932,7 +934,7 @@ static bool pose_select_same_collection(bContext *C, const bool extend) continue; } - bone->flag |= BONE_SELECTED; + pchan->flag |= POSE_SELECTED; changed_any_selection = true; updated_objects.add(ob); } @@ -979,7 +981,7 @@ static void deselect_pose_bones(const blender::Set &pose_bones) /* There may be a nullptr in the set if selecting siblings of root bones. */ continue; } - pose_bone->bone->flag &= ~BONE_SELECTED; + pose_bone->flag &= ~POSE_SELECTED; } } @@ -1119,7 +1121,7 @@ static bool pose_select_same_keyingset(bContext *C, ReportList *reports, bool ex if (extend == false) { CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) { if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0) { - pchan->bone->flag &= ~BONE_SELECTED; + pchan->flag &= ~POSE_SELECTED; } } CTX_DATA_END; @@ -1154,7 +1156,7 @@ static bool pose_select_same_keyingset(bContext *C, ReportList *reports, bool ex if (pchan) { /* select if bone is visible and can be affected */ if (blender::animrig::bone_is_selectable(arm, pchan)) { - pchan->bone->flag |= BONE_SELECTED; + pchan->flag |= POSE_SELECTED; changed = true; } } @@ -1288,16 +1290,15 @@ void POSE_OT_select_grouped(wmOperatorType *ot) /* -------------------------------------- */ /* Add the given selection flags to the bone flags. */ -static void bone_selection_flags_add(bPoseChannel *pchan, const eBone_Flag new_selection_flags) +static void bone_selection_flags_add(bPoseChannel *pchan, const ePchan_Flag new_selection_flags) { - pchan->bone->flag |= (new_selection_flags & BONE_SELECTED); + pchan->flag |= new_selection_flags; } /* Set the bone flags to the given selection flags. */ -static void bone_selection_flags_set(bPoseChannel *pchan, const eBone_Flag new_selection_flags) +static void bone_selection_flags_set(bPoseChannel *pchan, const ePchan_Flag new_selection_flags) { - pchan->bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - pchan->bone->flag |= (new_selection_flags & BONE_SELECTED); + pchan->flag = new_selection_flags; } /** @@ -1322,12 +1323,12 @@ static wmOperatorStatus pose_select_mirror_exec(bContext *C, wmOperator *op) bPoseChannel *pchan_mirror_act = nullptr; /* Remember the pre-mirroring selection flags of the bones. */ - blender::Map old_selection_flags; + blender::Map old_selection_flags; LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { /* Treat invisible bones as deselected. */ - const int flags = blender::animrig::bone_is_visible(arm, pchan) ? pchan->bone->flag : 0; + const int flags = blender::animrig::bone_is_visible(arm, pchan) ? pchan->flag : 0; - old_selection_flags.add_new(pchan, eBone_Flag(flags)); + old_selection_flags.add_new(pchan, ePchan_Flag(flags)); } LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { @@ -1352,7 +1353,7 @@ static wmOperatorStatus pose_select_mirror_exec(bContext *C, wmOperator *op) continue; } - const eBone_Flag flags_mirror = old_selection_flags.lookup(pchan_mirror); + const ePchan_Flag flags_mirror = old_selection_flags.lookup(pchan_mirror); set_bone_selection_flags(pchan, flags_mirror); } diff --git a/source/blender/editors/armature/pose_transform.cc b/source/blender/editors/armature/pose_transform.cc index 6d6bfe184b5..e46f31e6b1b 100644 --- a/source/blender/editors/armature/pose_transform.cc +++ b/source/blender/editors/armature/pose_transform.cc @@ -380,7 +380,7 @@ static void applyarmature_reset_constraints(bPose *pose, const bool use_selected { LISTBASE_FOREACH (bPoseChannel *, pchan, &pose->chanbase) { BLI_assert(pchan->bone != nullptr); - if (use_selected && (pchan->bone->flag & BONE_SELECTED) == 0) { + if (use_selected && (pchan->flag & POSE_SELECTED) == 0) { continue; } applyarmature_reset_bone_constraints(pchan); @@ -608,8 +608,9 @@ static void set_pose_keys(Object *ob) if (ob->pose) { LISTBASE_FOREACH (bPoseChannel *, chan, &ob->pose->chanbase) { - Bone *bone = chan->bone; - if ((bone) && (bone->flag & BONE_SELECTED) && ANIM_bone_in_visible_collection(arm, bone)) { + if ((chan->flag & POSE_SELECTED) && chan->bone && + ANIM_bone_in_visible_collection(arm, chan->bone)) + { chan->flag |= POSE_KEY; } else { @@ -652,7 +653,7 @@ static bPoseChannel *pose_bone_do_paste(Object *ob, if (pchan == nullptr) { return nullptr; } - if (selOnly && (pchan->bone->flag & BONE_SELECTED) == 0) { + if (selOnly && (pchan->flag & POSE_SELECTED) == 0) { return nullptr; } diff --git a/source/blender/editors/include/ED_armature.hh b/source/blender/editors/include/ED_armature.hh index 8010cf3ab32..2e1e2e0a9f5 100644 --- a/source/blender/editors/include/ED_armature.hh +++ b/source/blender/editors/include/ED_armature.hh @@ -284,7 +284,7 @@ bool ED_armature_pose_select_pick_bone(const Scene *scene, ViewLayer *view_layer, View3D *v3d, Object *ob, - Bone *bone, + bPoseChannel *pchan, const SelectPick_Params ¶ms) ATTR_NONNULL(1, 2, 3, 4); /** * Called for mode-less pose selection. diff --git a/source/blender/editors/object/object_edit.cc b/source/blender/editors/object/object_edit.cc index 25d3d041f1d..6d6fb388738 100644 --- a/source/blender/editors/object/object_edit.cc +++ b/source/blender/editors/object/object_edit.cc @@ -555,6 +555,35 @@ void OBJECT_OT_hide_collection(wmOperatorType *ot) /** \name Toggle Edit-Mode Operator * \{ */ +/* When switching mode, certain data needs to be copied from the `bPoseChannel` to the `Bone`. This + * is not done in `BKE_pose_rebuild` because that is called in other cases other than mode + * switching. */ +static void flush_bone_selection_to_pose(Object &ob) +{ + BLI_assert(ob.pose); + LISTBASE_FOREACH (bPoseChannel *, pose_bone, &ob.pose->chanbase) { + if (pose_bone->bone->flag & BONE_SELECTED) { + pose_bone->flag |= POSE_SELECTED; + } + else { + pose_bone->flag &= ~POSE_SELECTED; + } + } +} +static void flush_pose_selection_to_bone(Object &ob) +{ + BLI_assert(ob.pose); + constexpr int selection_flags = (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL); + LISTBASE_FOREACH (bPoseChannel *, pose_bone, &ob.pose->chanbase) { + if (pose_bone->flag & POSE_SELECTED) { + pose_bone->bone->flag |= selection_flags; + } + else { + pose_bone->bone->flag &= ~selection_flags; + } + } +} + static bool mesh_needs_keyindex(Main *bmain, const Mesh *mesh) { if (mesh->key) { @@ -643,6 +672,9 @@ static bool editmode_load_free_ex(Main *bmain, } } } + + /* After regenerating the bones, sync the selection onto the pose bones. */ + flush_bone_selection_to_pose(*obedit); /* TODO(sergey): Pose channels might have been changed, so need * to inform dependency graph about this. But is it really the * best place to do this? @@ -857,6 +889,13 @@ bool editmode_enter_ex(Main *bmain, Scene *scene, Object *ob, int flag) WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_MESH, nullptr); } else if (ob->type == OB_ARMATURE) { + /* Syncing the selection to the `Bone` before converting to edit bones. This is not possible if + * the Armature was just created, because then there is no pose data yet. Which is fine, the + * just-created edit bones already have the expected selection state. */ + if (ob->pose) { + flush_pose_selection_to_bone(*ob); + } + bArmature *arm = static_cast(ob->data); ok = true; ED_armature_to_edit(arm); diff --git a/source/blender/editors/object/object_utils.cc b/source/blender/editors/object/object_utils.cc index 31ccf00f6f2..b5530267ae6 100644 --- a/source/blender/editors/object/object_utils.cc +++ b/source/blender/editors/object/object_utils.cc @@ -125,7 +125,7 @@ bool calc_active_center_for_editmode(Object *obedit, const bool select_only, flo bool calc_active_center_for_posemode(Object *ob, const bool select_only, float r_center[3]) { bPoseChannel *pchan = BKE_pose_channel_active_if_bonecoll_visible(ob); - if (pchan && (!select_only || (pchan->bone->flag & BONE_SELECTED))) { + if (pchan && (!select_only || (pchan->flag & POSE_SELECTED))) { const bArmature *arm = static_cast(ob->data); BKE_pose_channel_transform_location(arm, pchan, r_center); return true; diff --git a/source/blender/editors/space_info/info_stats.cc b/source/blender/editors/space_info/info_stats.cc index 67f60a08404..14b831c4266 100644 --- a/source/blender/editors/space_info/info_stats.cc +++ b/source/blender/editors/space_info/info_stats.cc @@ -363,7 +363,7 @@ static void stats_object_pose(const Object *ob, SceneStats *stats) LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { stats->totbone++; - if (pchan->bone && (pchan->bone->flag & BONE_SELECTED)) { + if ((pchan->flag & POSE_SELECTED)) { if (BKE_pose_is_bonecoll_visible(arm, pchan)) { stats->totbonesel++; } diff --git a/source/blender/editors/space_outliner/outliner_select.cc b/source/blender/editors/space_outliner/outliner_select.cc index e19a0e5ebf2..e91b201bd12 100644 --- a/source/blender/editors/space_outliner/outliner_select.cc +++ b/source/blender/editors/space_outliner/outliner_select.cc @@ -564,7 +564,7 @@ static void tree_element_posechannel_activate(bContext *C, } LISTBASE_FOREACH (bPoseChannel *, pchannel, &ob_iter->pose->chanbase) { - pchannel->bone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL); + pchannel->flag &= ~POSE_SELECTED; } if (ob != ob_iter) { @@ -573,19 +573,19 @@ static void tree_element_posechannel_activate(bContext *C, } } - if ((set == OL_SETSEL_EXTEND) && (pchan->bone->flag & BONE_SELECTED)) { - pchan->bone->flag &= ~BONE_SELECTED; + if ((set == OL_SETSEL_EXTEND) && (pchan->flag & POSE_SELECTED)) { + pchan->flag &= ~POSE_SELECTED; } else { if (blender::animrig::bone_is_visible(arm, pchan)) { - pchan->bone->flag |= BONE_SELECTED; + pchan->flag |= POSE_SELECTED; } arm->act_bone = pchan->bone; } if (recursive) { /* Recursive select/deselect */ - do_outliner_bone_select_recursive(arm, pchan->bone, (pchan->bone->flag & BONE_SELECTED) != 0); + do_outliner_bone_select_recursive(arm, pchan->bone, (pchan->flag & POSE_SELECTED) != 0); } WM_event_add_notifier(C, NC_OBJECT | ND_BONE_ACTIVE, ob); @@ -992,7 +992,7 @@ static eOLDrawState tree_element_posechannel_state_get(const Object *ob_pose, const Object *ob = (const Object *)tselem->id; const bPoseChannel *pchan = static_cast(te->directdata); if (ob == ob_pose && ob->pose) { - if (pchan->bone->flag & BONE_SELECTED) { + if (pchan->flag & POSE_SELECTED) { return OL_DRAWSEL_NORMAL; } } diff --git a/source/blender/editors/space_outliner/outliner_sync.cc b/source/blender/editors/space_outliner/outliner_sync.cc index 681a76cf091..1f3122046b2 100644 --- a/source/blender/editors/space_outliner/outliner_sync.cc +++ b/source/blender/editors/space_outliner/outliner_sync.cc @@ -227,21 +227,21 @@ static void outliner_select_sync_to_pose_bone(TreeElement *te, bArmature *arm = static_cast(ob->data); bPoseChannel *pchan = (bPoseChannel *)te->directdata; - short bone_flag = pchan->bone->flag; + short bone_flag = pchan->flag; if (blender::animrig::bone_is_selectable(arm, pchan)) { if (tselem->flag & TSE_SELECTED) { - pchan->bone->flag |= BONE_SELECTED; + pchan->flag |= POSE_SELECTED; selected_pbones.add(pchan); } else if (!selected_pbones.contains(pchan)) { - pchan->bone->flag &= ~BONE_SELECTED; + pchan->flag &= ~POSE_SELECTED; } } /* Tag if selection changed */ - if (bone_flag != pchan->bone->flag) { + if (bone_flag != pchan->flag) { DEG_id_tag_update(&arm->id, ID_RECALC_SELECT); WM_main_add_notifier(NC_OBJECT | ND_BONE_SELECT, ob); } @@ -409,7 +409,6 @@ static void outliner_select_sync_from_pose_bone(bPoseChannel *pchan_active, TreeStoreElem *tselem) { bPoseChannel *pchan = (bPoseChannel *)te->directdata; - Bone *bone = pchan->bone; if (pchan == pchan_active) { tselem->flag |= TSE_ACTIVE; @@ -418,7 +417,7 @@ static void outliner_select_sync_from_pose_bone(bPoseChannel *pchan_active, tselem->flag &= ~TSE_ACTIVE; } - if (bone->flag & BONE_SELECTED) { + if (pchan->flag & POSE_SELECTED) { tselem->flag |= TSE_SELECTED; } else { diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc index b4667ec0295..36fcbd1cc79 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -2093,14 +2093,14 @@ static void pchan_fn(int event, TreeElement *te, TreeStoreElem * /*tselem*/, voi bPoseChannel *pchan = (bPoseChannel *)te->directdata; if (event == OL_DOP_SELECT) { - pchan->bone->flag |= BONE_SELECTED; + pchan->flag |= POSE_SELECTED; } else if (event == OL_DOP_DESELECT) { - pchan->bone->flag &= ~BONE_SELECTED; + pchan->flag &= ~POSE_SELECTED; } else if (event == OL_DOP_HIDE) { pchan->drawflag |= PCHAN_DRAW_HIDDEN; - pchan->bone->flag &= ~BONE_SELECTED; + pchan->flag &= ~POSE_SELECTED; } else if (event == OL_DOP_UNHIDE) { pchan->drawflag &= ~PCHAN_DRAW_HIDDEN; diff --git a/source/blender/editors/space_view3d/view3d_select.cc b/source/blender/editors/space_view3d/view3d_select.cc index def785b4648..a88750ee771 100644 --- a/source/blender/editors/space_view3d/view3d_select.cc +++ b/source/blender/editors/space_view3d/view3d_select.cc @@ -547,7 +547,7 @@ static void do_lasso_select_pose__do_tag(void *user_data, if (BLI_rctf_isect_segment(data->rect_fl, screen_co_a, screen_co_b) && BLI_lasso_is_edge_inside(data->mcoords, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX)) { - pchan->bone->flag |= BONE_DONE; + pchan->runtime.flag |= POSE_RUNTIME_IN_SELECTION_AREA; data->is_changed = true; } } @@ -621,8 +621,7 @@ static blender::Vector do_pose_tag_select_op_prepare(const ViewContext * Object *ob = base->object; bArmature *arm = static_cast(ob->data); LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { - Bone *bone = pchan->bone; - bone->flag &= ~BONE_DONE; + pchan->runtime.flag &= ~POSE_RUNTIME_IN_SELECTION_AREA; } arm->id.tag |= ID_TAG_DOIT; ob->id.tag &= ~ID_TAG_DOIT; @@ -671,23 +670,15 @@ static bool do_pose_tag_select_op_exec(blender::MutableSpan bases, const Object *ob_iter = base_iter->object; bArmature *arm = static_cast(ob_iter->data); - /* Don't handle twice. */ - if (arm->id.tag & ID_TAG_DOIT) { - arm->id.tag &= ~ID_TAG_DOIT; - } - else { - continue; - } - bool changed = false; LISTBASE_FOREACH (bPoseChannel *, pchan, &ob_iter->pose->chanbase) { Bone *bone = pchan->bone; if ((bone->flag & BONE_UNSELECTABLE) == 0) { - const bool is_select = bone->flag & BONE_SELECTED; - const bool is_inside = bone->flag & BONE_DONE; + const bool is_select = pchan->flag & POSE_SELECTED; + const bool is_inside = pchan->runtime.flag & POSE_RUNTIME_IN_SELECTION_AREA; const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); if (sel_op_result != -1) { - SET_FLAG_FROM_TEST(bone->flag, sel_op_result, BONE_SELECTED); + SET_FLAG_FROM_TEST(pchan->flag, sel_op_result, POSE_SELECTED); if (sel_op_result == 0) { if (arm->act_bone == bone) { arm->act_bone = nullptr; @@ -1874,7 +1865,7 @@ static wmOperatorStatus bone_select_menu_exec(bContext *C, wmOperator *op) } else { bPoseChannel *pchan = (bPoseChannel *)object_mouse_select_menu_data[name_index].item_ptr; - ED_armature_pose_select_pick_bone(scene, view_layer, v3d, basact->object, pchan->bone, params); + ED_armature_pose_select_pick_bone(scene, view_layer, v3d, basact->object, pchan, params); } /* Weak but ensures we activate the menu again before using the enum. */ @@ -4442,8 +4433,8 @@ static bool do_pose_box_select(bContext *C, buf_iter < buf_end; buf_iter++) { - Bone *bone; - Base *base = ED_armature_base_and_bone_from_select_buffer(bases, buf_iter->id, &bone); + bPoseChannel *pose_bone; + Base *base = ED_armature_base_and_pchan_from_select_buffer(bases, buf_iter->id, &pose_bone); if (base == nullptr) { continue; @@ -4452,9 +4443,9 @@ static bool do_pose_box_select(bContext *C, /* Loop over contiguous bone hits for 'base'. */ for (; buf_iter != buf_end; buf_iter++) { /* should never fail */ - if (bone != nullptr) { + if (pose_bone != nullptr) { base->object->id.tag |= ID_TAG_DOIT; - bone->flag |= BONE_DONE; + pose_bone->runtime.flag |= POSE_RUNTIME_IN_SELECTION_AREA; } /* Select the next bone if we're not switching bases. */ @@ -4465,12 +4456,12 @@ static bool do_pose_box_select(bContext *C, } if (base->object->pose != nullptr) { const uint hit_bone = (col_next->id & ~BONESEL_ANY) >> 16; - bPoseChannel *pchan = static_cast( + bPoseChannel *next = static_cast( BLI_findlink(&base->object->pose->chanbase, hit_bone)); - bone = pchan ? pchan->bone : nullptr; + pose_bone = next; } else { - bone = nullptr; + pose_bone = nullptr; } } } @@ -5133,10 +5124,10 @@ static bool pchan_circle_doSelectJoint(void *user_data, if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) { if (data->select) { - pchan->bone->flag |= BONE_SELECTED; + pchan->flag |= POSE_SELECTED; } else { - pchan->bone->flag &= ~BONE_SELECTED; + pchan->flag &= ~POSE_SELECTED; } return true; } @@ -5183,10 +5174,10 @@ static void do_circle_select_pose__doSelectBone(void *user_data, edge_inside_circle(data->mval_fl, data->radius, screen_co_a, screen_co_b)) { if (data->select) { - pchan->bone->flag |= BONE_SELECTED; + pchan->flag |= POSE_SELECTED; } else { - pchan->bone->flag &= ~BONE_SELECTED; + pchan->flag &= ~POSE_SELECTED; } data->is_changed = true; } diff --git a/source/blender/editors/space_view3d/view3d_snap.cc b/source/blender/editors/space_view3d/view3d_snap.cc index 38c73845cd5..30f621fed5f 100644 --- a/source/blender/editors/space_view3d/view3d_snap.cc +++ b/source/blender/editors/space_view3d/view3d_snap.cc @@ -138,7 +138,7 @@ static wmOperatorStatus snap_sel_to_grid_exec(bContext *C, wmOperator *op) invert_m4_m4(ob_eval->runtime->world_to_object.ptr(), ob_eval->object_to_world().ptr()); LISTBASE_FOREACH (bPoseChannel *, pchan_eval, &ob_eval->pose->chanbase) { - if (pchan_eval->bone->flag & BONE_SELECTED) { + if (pchan_eval->flag & POSE_SELECTED) { if (ANIM_bonecoll_is_visible_pchan(arm_eval, pchan_eval)) { if ((pchan_eval->bone->flag & BONE_CONNECTED) == 0) { float nLoc[3]; @@ -285,6 +285,18 @@ void VIEW3D_OT_snap_selected_to_grid(wmOperatorType *ot) /** \name Snap Selection to Location (Utility) * \{ */ +/* Return true if the bone or any of its parents has the given runtime flag set. */ +static bool pose_bone_runtime_flag_test_recursive(const bPoseChannel *pose_bone, int flag) +{ + if (pose_bone->runtime.flag & flag) { + return true; + } + if (pose_bone->parent) { + return pose_bone_runtime_flag_test_recursive(pose_bone->parent, flag); + } + return false; +} + /** * Snaps the selection as a whole (use_offset=true) or each selected object to the given location. * @@ -404,24 +416,24 @@ static bool snap_selected_to_location_rotation(bContext *C, mul_v3_m4v3(target_loc_local, ob->world_to_object().ptr(), target_loc_global); LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { - if ((pchan->bone->flag & BONE_SELECTED) && blender::animrig::bone_is_visible(arm, pchan) && + if ((pchan->flag & POSE_SELECTED) && blender::animrig::bone_is_visible(arm, pchan) && /* if the bone has a parent and is connected to the parent, * don't do anything - will break chain unless we do auto-ik. */ (pchan->bone->flag & BONE_CONNECTED) == 0) { - pchan->bone->flag |= BONE_TRANSFORM; + pchan->runtime.flag |= POSE_RUNTIME_TRANSFORM; } else { - pchan->bone->flag &= ~BONE_TRANSFORM; + pchan->runtime.flag &= ~POSE_RUNTIME_TRANSFORM; } } LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { - if ((pchan->bone->flag & BONE_TRANSFORM) && + if ((pchan->runtime.flag & POSE_RUNTIME_TRANSFORM) && /* check that our parents not transformed (if we have one) */ ((pchan->bone->parent && - BKE_armature_bone_flag_test_recursive(pchan->bone->parent, BONE_TRANSFORM)) == 0)) + pose_bone_runtime_flag_test_recursive(pchan->parent, POSE_RUNTIME_TRANSFORM)) == 0)) { /* Get position in pchan (pose) space. */ blender::float3 target_loc_pose; @@ -497,7 +509,7 @@ static bool snap_selected_to_location_rotation(bContext *C, } LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { - pchan->bone->flag &= ~BONE_TRANSFORM; + pchan->runtime.flag &= ~POSE_RUNTIME_TRANSFORM; } ob->pose->flag |= (POSE_LOCKED | POSE_DO_UNLOCK); @@ -945,7 +957,7 @@ static bool snap_curs_to_sel_ex(bContext *C, const int pivot_point, float r_curs bArmature *arm = static_cast(obact_eval->data); LISTBASE_FOREACH (bPoseChannel *, pchan, &obact_eval->pose->chanbase) { if (ANIM_bonecoll_is_visible_pchan(arm, pchan)) { - if (pchan->bone->flag & BONE_SELECTED) { + if (pchan->flag & POSE_SELECTED) { copy_v3_v3(vec, pchan->pose_head); mul_m4_v3(obact_eval->object_to_world().ptr(), vec); add_v3_v3(centroid, vec); diff --git a/source/blender/editors/transform/transform_convert.hh b/source/blender/editors/transform/transform_convert.hh index 210ba8347ee..47a70d53474 100644 --- a/source/blender/editors/transform/transform_convert.hh +++ b/source/blender/editors/transform/transform_convert.hh @@ -238,7 +238,6 @@ extern TransConvertTypeInfo TransConvertType_Pose; /** * Sets transform flags in the bones. - * Returns total number of bones with #BONE_TRANSFORM. */ void transform_convert_pose_transflags_update(Object *ob, int mode, short around); diff --git a/source/blender/editors/transform/transform_convert_armature.cc b/source/blender/editors/transform/transform_convert_armature.cc index 15f6d36b45c..0d5dd2cdf88 100644 --- a/source/blender/editors/transform/transform_convert_armature.cc +++ b/source/blender/editors/transform/transform_convert_armature.cc @@ -266,7 +266,7 @@ static short pose_grab_with_ik(Main *bmain, Object *ob) * (but they must be selected, and only one ik-solver per chain should get added). */ LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { if (BKE_pose_is_bonecoll_visible(arm, pchan)) { - if (pchan->bone->flag & (BONE_SELECTED | BONE_TRANSFORM_MIRROR)) { + if ((pchan->flag & POSE_SELECTED) && (pchan->bone->flag & BONE_TRANSFORM_MIRROR)) { /* Rule: no IK for solitary (unconnected) bones. */ for (bonec = static_cast(pchan->bone->childbase.first); bonec; bonec = bonec->next) { @@ -283,7 +283,7 @@ static short pose_grab_with_ik(Main *bmain, Object *ob) /* Only adds if there's no IK yet (and no parent bone was selected). */ bPoseChannel *parent; for (parent = pchan->parent; parent; parent = parent->parent) { - if (parent->bone->flag & (BONE_SELECTED | BONE_TRANSFORM_MIRROR)) { + if ((parent->flag & POSE_SELECTED) && (parent->bone->flag & BONE_TRANSFORM_MIRROR)) { break; } } @@ -406,11 +406,11 @@ static void add_pose_transdata( } td->flag = TD_SELECTED; - if (bone->flag & BONE_HINGE_CHILD_TRANSFORM) { + if (pchan->runtime.flag & POSE_RUNTIME_HINGE_CHILD_TRANSFORM) { td->flag |= TD_NOCENTER; } - if (bone->flag & BONE_TRANSFORM_CHILD) { + if (pchan->runtime.flag & POSE_RUNTIME_TRANSFORM_CHILD) { td->flag |= TD_NOCENTER; td->flag |= TD_NO_LOC; } @@ -591,7 +591,7 @@ static void createTransPose(bContext * /*C*/, TransInfo *t) /* Now count, and check if we have autoIK or have to switch from translate to rotate. */ LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { Bone *bone = pchan->bone; - if (!(bone->flag & BONE_TRANSFORM)) { + if (!(pchan->runtime.flag & POSE_RUNTIME_TRANSFORM)) { continue; } @@ -604,7 +604,7 @@ static void createTransPose(bContext * /*C*/, TransInfo *t) if (has_targetless_ik(pchan) == nullptr) { if (pchan->parent && (bone->flag & BONE_CONNECTED)) { - if (bone->flag & BONE_HINGE_CHILD_TRANSFORM) { + if (pchan->runtime.flag & POSE_RUNTIME_HINGE_CHILD_TRANSFORM) { has_translate_rotate[0] = true; } } @@ -641,7 +641,7 @@ static void createTransPose(bContext * /*C*/, TransInfo *t) /* Clear the MIRROR flag from previous runs. */ pchan->bone->flag &= ~BONE_TRANSFORM_MIRROR; - if ((pchan->bone->flag & BONE_TRANSFORM) && + if ((pchan->runtime.flag & POSE_RUNTIME_TRANSFORM) && BKE_pose_channel_get_mirrored(ob->pose, pchan->name)) { total_mirrored++; @@ -687,7 +687,7 @@ static void createTransPose(bContext * /*C*/, TransInfo *t) if (mirror) { LISTBASE_FOREACH (bPoseChannel *, pchan, &pose->chanbase) { - if (pchan->bone->flag & BONE_TRANSFORM) { + if (pchan->runtime.flag & POSE_RUNTIME_TRANSFORM) { bPoseChannel *pchan_mirror = BKE_pose_channel_get_mirrored(ob->pose, pchan->name); if (pchan_mirror) { pchan_mirror->bone->flag |= BONE_TRANSFORM_MIRROR; @@ -713,13 +713,14 @@ static void createTransPose(bContext * /*C*/, TransInfo *t) tdx->center_no_override[1] = 0; tdx->center_no_override[2] = 0; LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { - if (pchan->bone->flag & BONE_TRANSFORM) { + if (pchan->runtime.flag & POSE_RUNTIME_TRANSFORM) { add_pose_transdata(t, pchan, ob, td++, tdx++); } } if (td != (tc->data + tc->data_len)) { BKE_report(t->reports, RPT_DEBUG, "Bone selection count error"); + BLI_assert_unreachable(); } } @@ -1161,7 +1162,7 @@ static void pose_transform_mirror_update(TransInfo *t, TransDataContainer *tc, O TransData *td = tc->data; for (int i = tc->data_len; i--; td++) { bPoseChannel *pchan_orig = static_cast(td->extra); - BLI_assert(pchan_orig->bone->flag & BONE_TRANSFORM); + BLI_assert(pchan_orig->runtime.flag & POSE_RUNTIME_TRANSFORM); /* No layer check, correct mirror is more important. */ bPoseChannel *pchan = BKE_pose_channel_get_mirrored(pose, pchan_orig->name); if (pchan == nullptr) { @@ -1342,7 +1343,7 @@ static void autokeyframe_pose(bContext *C, bPose *pose = ob->pose; LISTBASE_FOREACH (bPoseChannel *, pchan, &pose->chanbase) { - if ((pchan->bone->flag & BONE_TRANSFORM) == 0 && + if ((pchan->runtime.flag & POSE_RUNTIME_TRANSFORM) == 0 && !((pose->flag & POSE_MIRROR_EDIT) && (pchan->bone->flag & BONE_TRANSFORM_MIRROR))) { continue; @@ -1456,57 +1457,56 @@ static void recalcData_pose(TransInfo *t) /** \name Special After Transform Pose * \{ */ -static void bone_children_clear_transflag(int mode, short around, ListBase *lb) +static void pose_channel_children_clear_transflag(bPose &pose, + bPoseChannel &pose_bone, + const int mode, + const short around) { - Bone *bone = static_cast(lb->first); - - for (; bone; bone = bone->next) { - if ((bone->flag & BONE_HINGE) && (bone->flag & BONE_CONNECTED)) { - bone->flag |= BONE_HINGE_CHILD_TRANSFORM; + blender::animrig::pose_bone_descendent_iterator(pose, pose_bone, [&](bPoseChannel &child) { + if (&pose_bone == &child) { + return; } - else if ((bone->flag & BONE_TRANSFORM) && ELEM(mode, TFM_ROTATION, TFM_TRACKBALL) && - (around == V3D_AROUND_LOCAL_ORIGINS)) + Bone *bone = child.bone; + if ((bone->flag & BONE_HINGE) && (bone->flag & BONE_CONNECTED)) { + child.runtime.flag |= POSE_RUNTIME_HINGE_CHILD_TRANSFORM; + } + else if ((child.runtime.flag & POSE_RUNTIME_TRANSFORM) && + ELEM(mode, TFM_ROTATION, TFM_TRACKBALL) && (around == V3D_AROUND_LOCAL_ORIGINS)) { - bone->flag |= BONE_TRANSFORM_CHILD; + child.runtime.flag |= POSE_RUNTIME_TRANSFORM_CHILD; } else { - bone->flag &= ~BONE_TRANSFORM; + child.runtime.flag &= ~POSE_RUNTIME_TRANSFORM; } - - bone_children_clear_transflag(mode, around, &bone->childbase); - } + }); } void transform_convert_pose_transflags_update(Object *ob, const int mode, const short around) { bArmature *arm = static_cast(ob->data); - Bone *bone; LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { - bone = pchan->bone; if (blender::animrig::bone_is_visible(arm, pchan)) { - if (bone->flag & BONE_SELECTED) { - bone->flag |= BONE_TRANSFORM; + if (pchan->flag & POSE_SELECTED) { + pchan->runtime.flag |= POSE_RUNTIME_TRANSFORM; } else { - bone->flag &= ~BONE_TRANSFORM; + pchan->runtime.flag &= ~POSE_RUNTIME_TRANSFORM; } - bone->flag &= ~BONE_HINGE_CHILD_TRANSFORM; - bone->flag &= ~BONE_TRANSFORM_CHILD; + pchan->runtime.flag &= ~POSE_RUNTIME_HINGE_CHILD_TRANSFORM; + pchan->runtime.flag &= ~POSE_RUNTIME_TRANSFORM_CHILD; } else { - bone->flag &= ~BONE_TRANSFORM; + pchan->runtime.flag &= ~POSE_RUNTIME_TRANSFORM; } } /* Make sure no bone can be transformed when a parent is transformed. */ - /* Since pchans are depsgraph sorted, the parents are in beginning of list. */ if (!ELEM(mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) { LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { - bone = pchan->bone; - if (bone->flag & BONE_TRANSFORM) { - bone_children_clear_transflag(mode, around, &bone->childbase); + if (pchan->runtime.flag & POSE_RUNTIME_TRANSFORM) { + pose_channel_children_clear_transflag(*ob->pose, *pchan, mode, around); } } } @@ -1542,7 +1542,6 @@ static short apply_targetless_ik(Object *ob) } } for (; segcount; segcount--) { - Bone *bone; float mat[4][4]; /* `pose_mat(b) = pose_mat(b-1) * offs_bone * channel * constraint * IK`. */ @@ -1551,8 +1550,8 @@ static short apply_targetless_ik(Object *ob) /* `mat = pose_mat(b) * inv(pose_mat(b-1) * offs_bone)`. */ parchan = chanlist[segcount - 1]; - bone = parchan->bone; - bone->flag |= BONE_TRANSFORM; /* Ensures it gets an auto key inserted. */ + /* Ensures it gets an auto key inserted. */ + parchan->runtime.flag |= POSE_RUNTIME_TRANSFORM; BKE_armature_mat_pose_to_bone(parchan, parchan->pose_mat, mat); /* Apply and decompose, doesn't work for constraints or non-uniform scale well. */ @@ -1671,7 +1670,7 @@ static void special_aftertrans_update__pose(bContext *C, TransInfo *t) BKE_pose_where_is(t->depsgraph, t->scene, pose_ob); } - /* Set BONE_TRANSFORM flags for auto-key, gizmo draw might have changed them. */ + /* Set POSE_RUNTIME_TRANSFORM flags for auto-key, gizmo draw might have changed them. */ if (!canceled && (t->mode != TFM_DUMMY)) { transform_convert_pose_transflags_update(ob, t->mode, t->around); } diff --git a/source/blender/editors/transform/transform_gizmo_3d.cc b/source/blender/editors/transform/transform_gizmo_3d.cc index 7138ce52d7f..328f8a99efc 100644 --- a/source/blender/editors/transform/transform_gizmo_3d.cc +++ b/source/blender/editors/transform/transform_gizmo_3d.cc @@ -840,7 +840,7 @@ static int gizmo_3d_foreach_selected(const bContext *C, bArmature *arm = static_cast(ob_iter->data); /* Use channels to get stats. */ LISTBASE_FOREACH (bPoseChannel *, pchan, &ob_iter->pose->chanbase) { - if (!(pchan->bone->flag & BONE_TRANSFORM)) { + if (!(pchan->runtime.flag & POSE_RUNTIME_TRANSFORM)) { continue; } diff --git a/source/blender/editors/transform/transform_orientations.cc b/source/blender/editors/transform/transform_orientations.cc index be5371218c9..89e99da25b4 100644 --- a/source/blender/editors/transform/transform_orientations.cc +++ b/source/blender/editors/transform/transform_orientations.cc @@ -571,33 +571,47 @@ void applyTransformOrientation(const TransformOrientation *ts, float r_mat[3][3] copy_m3_m3(r_mat, ts->mat); } -/* Updates all `BONE_TRANSFORM` flags. - * Returns total number of bones with `BONE_TRANSFORM`. - * NOTE: `transform_convert_pose_transflags_update` has a similar logic. */ -static int armature_bone_transflags_update_recursive(bArmature *arm, - ListBase *lb, - const bool do_it) +static int bone_children_clear_transflag(bPose &pose, bPoseChannel &pose_bone) +{ + int cleared = 0; + animrig::pose_bone_descendent_iterator(pose, pose_bone, [&](bPoseChannel &child) { + if (&child == &pose_bone) { + return; + } + if (child.runtime.flag & POSE_RUNTIME_TRANSFORM) { + child.runtime.flag &= ~POSE_RUNTIME_TRANSFORM; + cleared++; + } + }); + return cleared; +} + +/* Updates all `POSE_RUNTIME_TRANSFORM` flags. + * Returns total number of bones with `POSE_RUNTIME_TRANSFORM`. + * NOTE: `transform_convert_pose_transflags_update` has a similar logic. */ +static int armature_bone_transflags_update(Object &ob, + bArmature *arm, + ListBase /* bPoseChannel */ *lb) { - bool do_next; int total = 0; - LISTBASE_FOREACH (Bone *, bone, lb) { - bone->flag &= ~BONE_TRANSFORM; - do_next = do_it; - if (do_it) { - if (ANIM_bone_in_visible_collection(arm, bone)) { - if (bone->flag & BONE_SELECTED) { - bone->flag |= BONE_TRANSFORM; - total++; - - /* No transform on children if one parent bone is selected. */ - do_next = false; - } - } + LISTBASE_FOREACH (bPoseChannel *, pchan, lb) { + pchan->runtime.flag &= ~POSE_RUNTIME_TRANSFORM; + if (!ANIM_bone_in_visible_collection(arm, pchan->bone)) { + continue; + } + if (pchan->flag & POSE_SELECTED) { + pchan->runtime.flag |= POSE_RUNTIME_TRANSFORM; + total++; } - total += armature_bone_transflags_update_recursive(arm, &bone->childbase, do_next); } + /* No transform on children if any parent bone is selected. */ + LISTBASE_FOREACH (bPoseChannel *, pchan, lb) { + if (pchan->runtime.flag & POSE_RUNTIME_TRANSFORM) { + total -= bone_children_clear_transflag(*ob.pose, *pchan); + } + } return total; } @@ -1437,12 +1451,11 @@ int getTransformOrientation_ex(const Scene *scene, ok = true; } else { - int transformed_len; - transformed_len = armature_bone_transflags_update_recursive(arm, &arm->bonebase, true); + const int transformed_len = armature_bone_transflags_update(*ob, arm, &ob->pose->chanbase); if (transformed_len) { /* Use channels to get stats. */ LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { - if (pchan->bone && pchan->bone->flag & BONE_TRANSFORM) { + if (pchan->runtime.flag & POSE_RUNTIME_TRANSFORM) { float pose_mat[3][3]; BKE_pose_channel_transform_orientation(arm, pchan, pose_mat); diff --git a/source/blender/editors/transform/transform_snap_object_armature.cc b/source/blender/editors/transform/transform_snap_object_armature.cc index 0053e1a3820..c9643514b94 100644 --- a/source/blender/editors/transform/transform_snap_object_armature.cc +++ b/source/blender/editors/transform/transform_snap_object_armature.cc @@ -64,13 +64,12 @@ eSnapMode snapArmature(SnapObjectContext *sctx, } else if (ob_eval->pose && ob_eval->pose->chanbase.first) { LISTBASE_FOREACH (bPoseChannel *, pchan, &ob_eval->pose->chanbase) { - Bone *bone = pchan->bone; - if (!bone || !blender::animrig::bone_is_visible(arm, pchan)) { + if (!blender::animrig::bone_is_visible(arm, pchan)) { /* Skip hidden bones. */ continue; } - const bool is_selected = (bone->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL)) != 0; + const bool is_selected = (pchan->flag & POSE_SELECTED) != 0; if (is_selected && skip_selected) { continue; } diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index 2a4c98ef996..f208a53bf44 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -242,6 +242,21 @@ typedef struct bPoseChannel_BBoneSegmentBoundary { float depth_scale; } bPoseChannel_BBoneSegmentBoundary; +/** + * Runtime flags on pose bones. Those are only used internally and are not exposed to the user. + */ +typedef enum bPoseChannelRuntimeFlag { + /** Used during transform. Not every selected bone is transformed. For example in a chain of + bones, only the first selected may be transformed. */ + POSE_RUNTIME_TRANSFORM = (1 << 0), + /** Set to prevent hinge child bones from influencing the transform center. */ + POSE_RUNTIME_HINGE_CHILD_TRANSFORM = (1 << 1), + /** Indicates that a parent is also being transformed. */ + POSE_RUNTIME_TRANSFORM_CHILD = (1 << 2), + /* Set on bones during selection to tell following code that this bone should be operated on. */ + POSE_RUNTIME_IN_SELECTION_AREA = (1 << 3), +} bPoseChannelRuntimeFlag; + typedef struct bPoseChannel_Runtime { SessionUID session_uid; @@ -253,7 +268,9 @@ typedef struct bPoseChannel_Runtime { /* Inverse of the total length of the segment polyline. */ float bbone_arc_length_reciprocal; - char _pad1[4]; + /* bPoseChannelRuntimeFlag */ + uint8_t flag; + char _pad1[3]; /* Rest and posed matrices for segments. */ struct Mat4 *bbone_rest_mats; @@ -313,8 +330,9 @@ typedef struct bPoseChannel { short agrp_index; /** For quick detecting which constraints affect this channel. */ char constflag; - /** Copy of bone flag, so you can work with library armatures, not for runtime use. */ - char selectflag; + /** This used to store the selectionflag for serialization but is not longer required since that + * is now natively stored on the `flag` property. */ + char selectflag DNA_DEPRECATED; char drawflag; char bboneflag DNA_DEPRECATED; char _pad0[4]; @@ -462,6 +480,8 @@ typedef enum ePchan_Flag { `custom_tx` are set. This can be useful for rigs where the deformation is coming from blendshapes in addition to the armature. */ POSE_TRANSFORM_AROUND_CUSTOM_TX = (1 << 5), + POSE_SELECTED = (1 << 6), + /* IK/Pose solving */ POSE_CHAIN = (1 << 9), POSE_DONE = (1 << 10), diff --git a/source/blender/makesdna/DNA_armature_types.h b/source/blender/makesdna/DNA_armature_types.h index bc5665fe292..708a59152db 100644 --- a/source/blender/makesdna/DNA_armature_types.h +++ b/source/blender/makesdna/DNA_armature_types.h @@ -466,10 +466,8 @@ typedef enum eBone_Flag { #ifdef DNA_DEPRECATED_ALLOW /** set to prevent destruction of its unkeyframed pose (after transform) */ BONE_UNKEYED = (1 << 13), -#endif /** set to prevent hinge child bones from influencing the transform center */ BONE_HINGE_CHILD_TRANSFORM = (1 << 14), -#ifdef DNA_DEPRECATED_ALLOW /** No parent scale */ BONE_NO_SCALE = (1 << 15), #endif @@ -479,8 +477,10 @@ typedef enum eBone_Flag { BONE_NO_CYCLICOFFSET = (1 << 18), /** bone transforms are locked in EditMode */ BONE_EDITMODE_LOCKED = (1 << 19), +#ifdef DNA_DEPRECATED_ALLOW /** Indicates that a parent is also being transformed */ BONE_TRANSFORM_CHILD = (1 << 20), +#endif /** bone cannot be selected */ BONE_UNSELECTABLE = (1 << 21), /** bone location is in armature space */ diff --git a/source/blender/makesrna/intern/rna_armature.cc b/source/blender/makesrna/intern/rna_armature.cc index 7c992ccc034..1420e9de974 100644 --- a/source/blender/makesrna/intern/rna_armature.cc +++ b/source/blender/makesrna/intern/rna_armature.cc @@ -760,14 +760,8 @@ static void rna_Bone_select_update(Main * /*bmain*/, Scene * /*scene*/, PointerR DEG_id_tag_update(id, ID_RECALC_SYNC_TO_EVAL); } else if (GS(id->name) == ID_OB) { - Object *ob = (Object *)id; - bArmature *arm = (bArmature *)ob->data; - - if (arm->flag & ARM_HAS_VIZ_DEPS) { - DEG_id_tag_update(id, ID_RECALC_GEOMETRY); - } - - DEG_id_tag_update(&arm->id, ID_RECALC_SYNC_TO_EVAL); + /* This should be handled by the pose bone. */ + BLI_assert_unreachable(); } } diff --git a/source/blender/makesrna/intern/rna_pose.cc b/source/blender/makesrna/intern/rna_pose.cc index d1bdbf56d48..039f18b0c0f 100644 --- a/source/blender/makesrna/intern/rna_pose.cc +++ b/source/blender/makesrna/intern/rna_pose.cc @@ -682,6 +682,29 @@ bool rna_Pose_custom_shape_object_poll(PointerRNA * /*ptr*/, PointerRNA value) return (reinterpret_cast(value.owner_id))->type != OB_ARMATURE; } +static void rna_Pose_select_update(Main * /*bmain*/, Scene * /*scene*/, PointerRNA *ptr) +{ + ID *id = ptr->owner_id; + + if (!id) { + /* There shouldn't be a pose bone without an object. */ + BLI_assert_unreachable(); + return; + } + BLI_assert(GS(id->name) == ID_OB); + Object *ob = (Object *)id; + bArmature *arm = (bArmature *)ob->data; + + if (arm->flag & ARM_HAS_VIZ_DEPS) { + DEG_id_tag_update(id, ID_RECALC_GEOMETRY); + } + + DEG_id_tag_update(&arm->id, ID_RECALC_SYNC_TO_EVAL); + + WM_main_add_notifier(NC_GEOM | ND_DATA, id); + WM_main_add_notifier(NC_ANIMATION | ND_ANIMCHAN, id); +} + #else void rna_def_actionbone_group_common(StructRNA *srna, int update_flag, const char *update_cb) @@ -1198,13 +1221,20 @@ static void rna_def_pose_channel(BlenderRNA *brna) RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update"); prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_boolean_sdna(prop, nullptr, "drawflag", PCHAN_DRAW_HIDDEN); RNA_def_property_ui_text(prop, "Hide", "Bone is not visible except for Edit Mode"); RNA_def_property_ui_icon(prop, ICON_RESTRICT_VIEW_OFF, -1); RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_PoseBone_visibility_update"); + prop = RNA_def_property(srna, "select", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", POSE_SELECTED); + RNA_def_property_ui_text(prop, "Select", "Bone is selected in Pose Mode"); + RNA_def_property_ui_icon(prop, ICON_RESTRICT_VIEW_OFF, -1); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, 0, "rna_Pose_select_update"); + prop = RNA_def_property(srna, "custom_shape_transform", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, nullptr, "custom_tx"); RNA_def_property_struct_type(prop, "PoseBone"); diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc index 49a5bdf951f..6fbcf5dc49f 100644 --- a/source/blender/modifiers/intern/MOD_mask.cc +++ b/source/blender/modifiers/intern/MOD_mask.cc @@ -96,7 +96,7 @@ static void compute_vertex_mask__armature_mode(const MDeformVert *dvert, LISTBASE_FOREACH (bDeformGroup *, def, &mesh->vertex_group_names) { bPoseChannel *pchan = BKE_pose_channel_find_name(armature_ob->pose, def->name); - bool bone_for_group_exists = pchan && pchan->bone && (pchan->bone->flag & BONE_SELECTED); + bool bone_for_group_exists = pchan && pchan->bone && (pchan->flag & POSE_SELECTED); selected_bone_uses_group.append(bone_for_group_exists); } const int64_t total_size = selected_bone_uses_group.size(); diff --git a/tests/python/bl_animation_keyframing.py b/tests/python/bl_animation_keyframing.py index adee2065b15..5af8cc6bf0a 100644 --- a/tests/python/bl_animation_keyframing.py +++ b/tests/python/bl_animation_keyframing.py @@ -89,9 +89,7 @@ def _create_armature(): edit_bone.head = (1, 0, 0) bpy.ops.object.mode_set(mode='POSE') armature_obj.pose.bones[_BONE_NAME].rotation_mode = "XYZ" - armature_obj.pose.bones[_BONE_NAME].bone.select = True - armature_obj.pose.bones[_BONE_NAME].bone.select_head = True - armature_obj.pose.bones[_BONE_NAME].bone.select_tail = True + armature_obj.pose.bones[_BONE_NAME].select = True bpy.ops.object.mode_set(mode='OBJECT') return armature_obj diff --git a/tests/python/bl_pose_assets.py b/tests/python/bl_pose_assets.py index 437835f2c25..e9e69b57590 100644 --- a/tests/python/bl_pose_assets.py +++ b/tests/python/bl_pose_assets.py @@ -88,8 +88,8 @@ class CreateAssetTest(unittest.TestCase): 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._armature_object.pose.bones[_BONE_NAME_1].select = True + self._armature_object.pose.bones[_BONE_NAME_2].select = False self._armature_object.pose.bones[_BONE_NAME_1].keyframe_insert('["bool_test"]') self._armature_object.pose.bones[_BONE_NAME_1].keyframe_insert('["float_test"]') @@ -130,8 +130,8 @@ class CreateAssetTest(unittest.TestCase): 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._armature_object.pose.bones[_BONE_NAME_1].select = True + self._armature_object.pose.bones[_BONE_NAME_2].select = False self._armature_object.pose.bones[_BONE_NAME_1].keyframe_insert('["bool_test"]') self._armature_object.pose.bones[_BONE_NAME_1].keyframe_insert('["float_test"]') @@ -181,8 +181,8 @@ class CreateAssetTest(unittest.TestCase): 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._armature_object.pose.bones[_BONE_NAME_1].select = True + self._armature_object.pose.bones[_BONE_NAME_2].select = False self.assertEqual(len(bpy.data.actions), 0) bpy.ops.poselib.create_pose_asset(