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
This commit is contained in:
Christoph Lendenfeld
2025-10-07 19:59:36 +02:00
committed by Sybren A. Stüvel
parent 37a676212c
commit a09d0cfd8c
43 changed files with 455 additions and 311 deletions

View File

@@ -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<void(bPoseChannel &child_bone)> 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<bool(bPoseChannel &child_bone)> callback);
} // namespace blender::animrig

View File

@@ -40,4 +40,37 @@ void pose_bone_descendent_iterator(bPose &pose,
}
};
static bool pose_depth_iterator_recursive(bPose &pose,
bPoseChannel &pose_bone,
FunctionRef<bool(bPoseChannel &child_bone)> 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<bool(bPoseChannel &child_bone)> callback)
{
/* Needed for fast name lookups. */
BKE_pose_channels_hash_ensure(&pose);
return pose_depth_iterator_recursive(pose, pose_bone, callback);
}
} // namespace blender::animrig

View File

@@ -67,9 +67,8 @@ void pose_apply(Object *ob,
return;
}
const bArmature *armature = static_cast<bArmature *>(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<const Object *> 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;
}
}

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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<std::string>;
*/
BoneNameSet BKE_armature_find_selected_bone_names(const bArmature *armature);
BoneNameSet BKE_pose_channel_find_selected_names(const Object *object);
}; // namespace blender::bke

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -109,7 +109,7 @@ static blender::Set<bPoseChannel *> 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 *> object
pose_backup->is_bone_selection_relevant = !selected_bones.is_empty();
for (Object *ob : objects) {
const bArmature *armature = static_cast<const bArmature *>(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<bAction *>(action), selected_bone_names, *pose_backup);
}

View File

@@ -128,7 +128,18 @@ class UnifiedBonePtr {
eBone_Flag flag() const
{
return static_cast<eBone_Flag>(is_editbone_ ? eBone_->flag : pchan_->bone->flag);
if (is_editbone_) {
return static_cast<eBone_Flag>(eBone_->flag);
}
/* Making sure the select flag is set correctly since it moved to the pose channel. */
eBone_Flag flag = static_cast<eBone_Flag>(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);

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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;
}
}

View File

@@ -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();

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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 &params)
{
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<bArmature *>(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;
child.flag &= ~POSE_SELECTED;
}
else {
bone->flag |= BONE_SELECTED;
}
LISTBASE_FOREACH (Bone *, curBone, &bone->childbase) {
selectconnected_posebonechildren(ob, curBone, extend);
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<bArmature *>(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<bPoseChannel *> &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<bPoseChannel *, eBone_Flag> old_selection_flags;
blender::Map<bPoseChannel *, ePchan_Flag> 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);
}

View File

@@ -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;
}

View File

@@ -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 &params) ATTR_NONNULL(1, 2, 3, 4);
/**
* Called for mode-less pose selection.

View File

@@ -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<bArmature *>(ob->data);
ok = true;
ED_armature_to_edit(arm);

View File

@@ -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<bArmature *>(ob->data);
BKE_pose_channel_transform_location(arm, pchan, r_center);
return true;

View File

@@ -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++;
}

View File

@@ -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<bPoseChannel *>(te->directdata);
if (ob == ob_pose && ob->pose) {
if (pchan->bone->flag & BONE_SELECTED) {
if (pchan->flag & POSE_SELECTED) {
return OL_DRAWSEL_NORMAL;
}
}

View File

@@ -227,21 +227,21 @@ static void outliner_select_sync_to_pose_bone(TreeElement *te,
bArmature *arm = static_cast<bArmature *>(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 {

View File

@@ -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;

View File

@@ -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<Base *> do_pose_tag_select_op_prepare(const ViewContext *
Object *ob = base->object;
bArmature *arm = static_cast<bArmature *>(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<Base *> bases, const
Object *ob_iter = base_iter->object;
bArmature *arm = static_cast<bArmature *>(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 *>(
bPoseChannel *next = static_cast<bPoseChannel *>(
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;
}

View File

@@ -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<bArmature *>(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);

View File

@@ -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);

View File

@@ -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<Bone *>(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<bPoseChannel *>(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<Bone *>(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;
}
bone_children_clear_transflag(mode, around, &bone->childbase);
child.runtime.flag &= ~POSE_RUNTIME_TRANSFORM;
}
});
}
void transform_convert_pose_transflags_update(Object *ob, const int mode, const short around)
{
bArmature *arm = static_cast<bArmature *>(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);
}

View File

@@ -840,7 +840,7 @@ static int gizmo_3d_foreach_selected(const bContext *C,
bArmature *arm = static_cast<bArmature *>(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;
}

View File

@@ -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;
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++;
/* No transform on children if one parent bone is selected. */
do_next = false;
}
}
}
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);

View File

@@ -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;
}

View File

@@ -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),

View File

@@ -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 */

View File

@@ -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();
}
}

View File

@@ -682,6 +682,29 @@ bool rna_Pose_custom_shape_object_poll(PointerRNA * /*ptr*/, PointerRNA value)
return (reinterpret_cast<Object *>(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");

View File

@@ -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();

View File

@@ -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

View File

@@ -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(