From a09d0cfd8ccecb57248c6ed96ba25900eab82e0f Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Tue, 7 Oct 2025 19:59:36 +0200 Subject: [PATCH] Anim: Move pose bone selection state to bPoseChannel Move the selection flag for pose bones, from the (edit)bone to the pose bone. Previously having multiple instances of armatures in pose mode at the same time caused issues because selecting a pose bone on one armature would automatically select it on all instances of it. This is now fixed since the selection state is stored on the pose bone (Object level) Doing so breaks API compatibility with 4.5 since the RNA property on the Bone no longer affects the pose bone. Instead, there is a new property on the pose bone for that. Due to this change, some runtime flags for the transform system also had to be moved to the pose bone. This is due to the fact that these flags are used by the transform system to pass information between functions. If we keep the flag at the bone level, this wouldn't work with armature instances. See `bPoseChannelRuntimeFlag` Fixes #117892 Pull Request: https://projects.blender.org/blender/blender/pulls/146102 --- source/blender/animrig/ANIM_armature.hh | 10 +- source/blender/animrig/intern/armature.cc | 33 ++++ source/blender/animrig/intern/pose.cc | 5 +- source/blender/animrig/intern/pose_test.cc | 20 +- source/blender/blenkernel/BKE_action.hh | 2 +- source/blender/blenkernel/BKE_armature.hh | 3 +- source/blender/blenkernel/intern/action.cc | 23 +-- .../blenkernel/intern/armature_selection.cc | 17 ++ source/blender/blenkernel/intern/object.cc | 8 +- .../blenkernel/intern/object_deform.cc | 2 +- .../blender/blenkernel/intern/pose_backup.cc | 5 +- .../draw/engines/overlay/overlay_armature.cc | 33 ++-- .../engines/overlay/overlay_motion_path.hh | 2 +- source/blender/editors/animation/anim_deps.cc | 2 +- .../blender/editors/animation/anim_filter.cc | 2 +- .../animation/keyframes_keylist_test.cc | 7 +- .../blender/editors/animation/keyframing.cc | 2 +- .../editors/armature/armature_select.cc | 4 +- .../editors/armature/armature_skinning.cc | 2 +- source/blender/editors/armature/pose_edit.cc | 9 +- .../blender/editors/armature/pose_select.cc | 183 +++++++++--------- .../editors/armature/pose_transform.cc | 9 +- source/blender/editors/include/ED_armature.hh | 2 +- source/blender/editors/object/object_edit.cc | 39 ++++ source/blender/editors/object/object_utils.cc | 2 +- .../blender/editors/space_info/info_stats.cc | 2 +- .../editors/space_outliner/outliner_select.cc | 12 +- .../editors/space_outliner/outliner_sync.cc | 11 +- .../editors/space_outliner/outliner_tools.cc | 6 +- .../editors/space_view3d/view3d_select.cc | 43 ++-- .../editors/space_view3d/view3d_snap.cc | 28 ++- .../editors/transform/transform_convert.hh | 1 - .../transform/transform_convert_armature.cc | 79 ++++---- .../editors/transform/transform_gizmo_3d.cc | 2 +- .../transform/transform_orientations.cc | 61 +++--- .../transform_snap_object_armature.cc | 5 +- source/blender/makesdna/DNA_action_types.h | 26 ++- source/blender/makesdna/DNA_armature_types.h | 4 +- .../blender/makesrna/intern/rna_armature.cc | 10 +- source/blender/makesrna/intern/rna_pose.cc | 32 ++- source/blender/modifiers/intern/MOD_mask.cc | 2 +- tests/python/bl_animation_keyframing.py | 4 +- tests/python/bl_pose_assets.py | 12 +- 43 files changed, 455 insertions(+), 311 deletions(-) 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(