diff options
-rw-r--r-- | source/blender/editors/armature/pose_select.c | 114 | ||||
-rw-r--r-- | source/blender/editors/include/ED_armature.h | 3 | ||||
-rw-r--r-- | source/blender/editors/space_view3d/view3d_select.c | 511 |
3 files changed, 213 insertions, 415 deletions
diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c index 621b20c0ca7..eb178d422ae 100644 --- a/source/blender/editors/armature/pose_select.c +++ b/source/blender/editors/armature/pose_select.c @@ -95,6 +95,23 @@ static void pose_do_bone_select(bPoseChannel *pchan, const int select_mode) } } +void ED_pose_bone_select_tag_update(Object *ob) +{ + BLI_assert(ob->type == OB_ARMATURE); + bArmature *arm = ob->data; + WM_main_add_notifier(NC_OBJECT | ND_BONE_SELECT, ob); + WM_main_add_notifier(NC_GEOM | ND_DATA, ob); + + if (arm->flag & ARM_HAS_VIZ_DEPS) { + /* mask modifier ('armature' mode), etc. */ + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + } + + /* copy on write tag is needed (for the armature), or else no refresh happens */ + DEG_id_tag_update(&arm->id, DEG_TAG_COPY_ON_WRITE); +} + + /* Utility method for changing the selection status of a bone */ void ED_pose_bone_select(Object *ob, bPoseChannel *pchan, bool select) { @@ -120,19 +137,7 @@ void ED_pose_bone_select(Object *ob, bPoseChannel *pchan, bool select) } // TODO: select and activate corresponding vgroup? - - /* tag necessary depsgraph updates - * (see rna_Bone_select_update() in rna_armature.c for details) - */ - if (arm->flag & ARM_HAS_VIZ_DEPS) { - DEG_id_tag_update(&ob->id, OB_RECALC_DATA); - } - - /* send necessary notifiers */ - WM_main_add_notifier(NC_GEOM | ND_DATA, ob); - - /* tag armature for copy-on-write update (since act_bone is in armature not object) */ - DEG_id_tag_update(&arm->id, DEG_TAG_COPY_ON_WRITE); + ED_pose_bone_select_tag_update(ob); } } @@ -235,14 +240,14 @@ bool ED_armature_pose_select_pick_with_buffer( /* 'select_mode' is usual SEL_SELECT/SEL_DESELECT/SEL_TOGGLE/SEL_INVERT. * When true, 'ignore_visibility' makes this func also affect invisible bones (hidden or on hidden layers). */ -void ED_pose_deselect_all(Object *ob, int select_mode, const bool ignore_visibility) +bool ED_pose_deselect_all(Object *ob, int select_mode, const bool ignore_visibility) { bArmature *arm = ob->data; bPoseChannel *pchan; /* we call this from outliner too */ if (ob->pose == NULL) { - return; + return false; } /* Determine if we're selecting or deselecting */ @@ -259,12 +264,16 @@ void ED_pose_deselect_all(Object *ob, int select_mode, const bool ignore_visibil } /* Set the flags accordingly */ + bool changed = false; for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { /* ignore the pchan if it isn't visible or if its selection cannot be changed */ if (ignore_visibility || PBONE_VISIBLE(arm, pchan->bone)) { + int flag_prev = pchan->bone->flag; pose_do_bone_select(pchan, select_mode); + changed = (changed || flag_prev != pchan->bone->flag); } } + return changed; } static bool ed_pose_is_any_selected(Object *ob, bool ignore_visibility) @@ -300,22 +309,9 @@ void ED_pose_deselect_all_multi(Object **objects, uint objects_len, int select_m for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *ob_iter = objects[ob_index]; - bArmature *arm = ob_iter->data; - - ED_pose_deselect_all(ob_iter, select_mode, ignore_visibility); - - /* if there are some dependencies for visualizing armature state - * (e.g. Mask Modifier in 'Armature' mode), force update - */ - if (arm->flag & ARM_HAS_VIZ_DEPS) { - /* NOTE: ob not ob_act here is intentional - it's the source of the - * bones being selected [T37247] - */ - DEG_id_tag_update(&ob_iter->id, OB_RECALC_DATA); + if (ED_pose_deselect_all(ob_iter, select_mode, ignore_visibility)) { + ED_pose_bone_select_tag_update(ob_iter); } - - /* need to tag armature for cow updates, or else selection doesn't update */ - DEG_id_tag_update(&arm->id, DEG_TAG_COPY_ON_WRITE); } } @@ -353,8 +349,6 @@ static int pose_select_connected_invoke(bContext *C, wmOperator *op, const wmEve if (!bone) return OPERATOR_CANCELLED; - bArmature *arm = base->object->data; - /* Select parents */ for (curBone = bone; curBone; curBone = next) { /* ignore bone if cannot be selected */ @@ -377,16 +371,7 @@ static int pose_select_connected_invoke(bContext *C, wmOperator *op, const wmEve for (curBone = bone->childbase.first; curBone; curBone = next) selectconnected_posebonechildren(base->object, curBone, extend); - /* updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, base->object); - - if (arm->flag & ARM_HAS_VIZ_DEPS) { - /* mask modifier ('armature' mode), etc. */ - DEG_id_tag_update(&base->object->id, OB_RECALC_DATA); - } - - /* need to tag armature for cow updates, or else selection doesn't update */ - DEG_id_tag_update(&arm->id, DEG_TAG_COPY_ON_WRITE); + ED_pose_bone_select_tag_update(base->object); return OPERATOR_FINISHED; } @@ -496,18 +481,7 @@ static int pose_select_parent_exec(bContext *C, wmOperator *UNUSED(op)) else { continue; } - - /* updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - - if (arm->flag & ARM_HAS_VIZ_DEPS) { - /* mask modifier ('armature' mode), etc. */ - DEG_id_tag_update(&ob->id, OB_RECALC_DATA); - } - - /* tag armature for copy-on-write update (since act_bone is in armature not object) */ - DEG_id_tag_update(&arm->id, DEG_TAG_COPY_ON_WRITE); - + ED_pose_bone_select_tag_update(ob); } FOREACH_PCHAN_SELECTED_IN_OBJECT_END; } @@ -541,7 +515,6 @@ static int pose_select_constraint_target_exec(bContext *C, wmOperator *UNUSED(op CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, visible_pose_bones, Object *, ob) { - bArmature *arm = ob->data; if (pchan->bone->flag & BONE_SELECTED) { for (con = pchan->constraints.first; con; con = con->next) { const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); @@ -559,14 +532,7 @@ static int pose_select_constraint_target_exec(bContext *C, wmOperator *UNUSED(op found = 1; if (ob != ob_prev) { - /* updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - if (arm->flag & ARM_HAS_VIZ_DEPS) { - /* mask modifier ('armature' mode), etc. */ - DEG_id_tag_update(&ob->id, OB_RECALC_DATA); - } - /* tag armature for copy on write, since selection status is armature data */ - DEG_id_tag_update(&arm->id, DEG_TAG_COPY_ON_WRITE); + ED_pose_bone_select_tag_update(ob); ob_prev = ob; } } @@ -670,16 +636,7 @@ static int pose_select_hierarchy_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - /* updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - - if (arm->flag & ARM_HAS_VIZ_DEPS) { - /* mask modifier ('armature' mode), etc. */ - DEG_id_tag_update(&ob->id, OB_RECALC_DATA); - } - - /* tag armature for copy-on-write update (since act_bone is in armature not object) */ - DEG_id_tag_update(&arm->id, DEG_TAG_COPY_ON_WRITE); + ED_pose_bone_select_tag_update(ob); return OPERATOR_FINISHED; } @@ -890,7 +847,6 @@ static bool pose_select_same_keyingset(bContext *C, ReportList *reports, Object static int pose_select_grouped_exec(bContext *C, wmOperator *op) { Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - bArmature *arm = (bArmature *)ob->data; const ePose_SelectSame_Mode type = RNA_enum_get(op->ptr, "type"); const bool extend = RNA_boolean_get(op->ptr, "extend"); bool changed = false; @@ -919,16 +875,10 @@ static int pose_select_grouped_exec(bContext *C, wmOperator *op) } /* notifiers for updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - - if (arm->flag & ARM_HAS_VIZ_DEPS) { - /* mask modifier ('armature' mode), etc. */ - DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + if (changed) { + ED_pose_bone_select_tag_update(ob); } - /* need to tag armature for cow updates, or else selection doesn't update */ - DEG_id_tag_update(&arm->id, DEG_TAG_COPY_ON_WRITE); - /* report done status */ if (changed) return OPERATOR_FINISHED; diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h index cb422c2fb95..5c193392052 100644 --- a/source/blender/editors/include/ED_armature.h +++ b/source/blender/editors/include/ED_armature.h @@ -221,8 +221,9 @@ bool ED_object_posemode_exit_ex(struct Main *bmain, struct Object *ob); bool ED_object_posemode_exit(struct bContext *C, struct Object *ob); bool ED_object_posemode_enter_ex(struct Main *bmain, struct Object *ob); bool ED_object_posemode_enter(struct bContext *C, struct Object *ob); -void ED_pose_deselect_all(struct Object *ob, int select_mode, const bool ignore_visibility); +bool ED_pose_deselect_all(struct Object *ob, int select_mode, const bool ignore_visibility); void ED_pose_deselect_all_multi(struct Object **objects, uint objects_len, int select_mode, const bool ignore_visibility); +void ED_pose_bone_select_tag_update(struct Object *ob); void ED_pose_bone_select(struct Object *ob, struct bPoseChannel *pchan, bool select); void ED_pose_recalculate_paths(struct bContext *C, struct Scene *scene, struct Object *ob); struct Object *ED_pose_object_from_context(struct bContext *C); diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 3b6796485ed..d32176ab20a 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -342,7 +342,7 @@ static bool edge_inside_rect(const rctf *rect, const float v1[2], const float v2 return 1; } -static void do_lasso_select_pose__doSelectBone( +static void do_lasso_select_pose__do_tag( void *userData, struct bPoseChannel *pchan, const float screen_co_a[2], const float screen_co_b[2]) { LassoSelectUserData *data = userData; @@ -352,26 +352,21 @@ static void do_lasso_select_pose__doSelectBone( bool is_point_done = false; int points_proj_tot = 0; - const int x0 = screen_co_a[0]; - const int y0 = screen_co_a[1]; - const int x1 = screen_co_b[0]; - const int y1 = screen_co_b[1]; - /* project head location to screenspace */ - if (x0 != IS_CLIPPED) { + if (screen_co_a[0] != IS_CLIPPED) { points_proj_tot++; - if (BLI_rcti_isect_pt(data->rect, x0, y0) && - BLI_lasso_is_point_inside(data->mcords, data->moves, x0, y0, INT_MAX)) + if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_a)) && + BLI_lasso_is_point_inside(data->mcords, data->moves, UNPACK2(screen_co_a), INT_MAX)) { is_point_done = true; } } /* project tail location to screenspace */ - if (x1 != IS_CLIPPED) { + if (screen_co_b[0] != IS_CLIPPED) { points_proj_tot++; - if (BLI_rcti_isect_pt(data->rect, x1, y1) && - BLI_lasso_is_point_inside(data->mcords, data->moves, x1, y1, INT_MAX)) + if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_b)) && + BLI_lasso_is_point_inside(data->mcords, data->moves, UNPACK2(screen_co_b), INT_MAX)) { is_point_done = true; } @@ -379,22 +374,15 @@ static void do_lasso_select_pose__doSelectBone( /* if one of points selected, we skip the bone itself */ if ((is_point_done == true) || - ((is_point_done == false) && (points_proj_tot == 2))) + ((is_point_done == false) && (points_proj_tot == 2) && + BLI_lasso_is_edge_inside(data->mcords, data->moves, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX))) { - const bool is_select = pchan->bone->flag & BONE_SELECTED; - const bool is_inside = BLI_lasso_is_edge_inside(data->mcords, data->moves, x0, y0, x1, y1, INT_MAX); - const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); - if (sel_op_result != -1) { - SET_FLAG_FROM_TEST(pchan->bone->flag, sel_op_result, BONE_SELECTED); - data->is_changed = true; - } + pchan->bone->flag |= BONE_DONE; } data->is_changed |= is_point_done; } } -static void do_lasso_select_pose( - ViewContext *vc, Object *ob, const int mcords[][2], short moves, - const eSelectOp sel_op) +static void do_lasso_tag_pose(ViewContext *vc, Object *ob, const int mcords[][2], short moves) { ViewContext vc_tmp; LassoSelectUserData data; @@ -409,21 +397,11 @@ static void do_lasso_select_pose( BLI_lasso_boundbox(&rect, mcords, moves); - view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, sel_op); - - ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d); + view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, 0); - pose_foreachScreenBone(&vc_tmp, do_lasso_select_pose__doSelectBone, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + ED_view3d_init_mats_rv3d(vc_tmp.obact, vc->rv3d); - if (data.is_changed) { - bArmature *arm = ob->data; - if (arm->flag & ARM_HAS_VIZ_DEPS) { - /* mask modifier ('armature' mode), etc. */ - DEG_id_tag_update(&ob->id, OB_RECALC_DATA); - } - /* bone selection status is on armature not object */ - DEG_id_tag_update(&arm->id, DEG_TAG_COPY_ON_WRITE); - } + pose_foreachScreenBone(&vc_tmp, do_lasso_select_pose__do_tag, &data, V3D_PROJ_TEST_CLIP_DEFAULT); } static void object_deselect_all_visible(ViewLayer *view_layer) @@ -441,30 +419,132 @@ static void do_lasso_select_objects( ViewContext *vc, const int mcords[][2], const short moves, const eSelectOp sel_op) { - bool is_pose_mode = vc->obact ? (vc->obact->mode & OB_MODE_POSE) : false; Base *base; if (SEL_OP_USE_PRE_DESELECT(sel_op)) { object_deselect_all_visible(vc->view_layer); } + bool changed = false; for (base = vc->view_layer->object_bases.first; base; base = base->next) { if (BASE_SELECTABLE(base)) { /* use this to avoid un-needed lasso lookups */ - if (((vc->scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) ? - (is_pose_mode == false) : true) && - ED_view3d_project_base(vc->ar, base) == V3D_PROJ_RET_OK) - { - const bool is_select = base->flag & BASE_SELECTED; - const bool is_inside = BLI_lasso_is_point_inside(mcords, moves, base->sx, base->sy, IS_CLIPPED); + const bool is_select = base->flag & BASE_SELECTED; + const bool is_inside = ( + (ED_view3d_project_base(vc->ar, base) == V3D_PROJ_RET_OK) && + BLI_lasso_is_point_inside(mcords, moves, base->sx, base->sy, IS_CLIPPED)); + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT); + changed = true; + } + } + } + + if (changed) { + DEG_id_tag_update(&vc->scene->id, DEG_TAG_SELECT_UPDATE); + WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, vc->scene); + } +} + + +/** + * Use for lasso & border select. + */ +static Base **do_pose_tag_select_op_prepare(ViewContext *vc, uint *r_bases_len) +{ + Base **bases = NULL; + BLI_array_declare(bases); + FOREACH_BASE_IN_MODE_BEGIN (vc->view_layer, OB_MODE_POSE, base_iter) { + Object *ob_iter = base_iter->object; + bArmature *arm = ob_iter->data; + for (bPoseChannel *pchan = ob_iter->pose->chanbase.first; pchan; pchan = pchan->next) { + Bone *bone = pchan->bone; + bone->flag &= ~BONE_DONE; + } + arm->id.tag |= LIB_TAG_DOIT; + ob_iter->id.tag &= ~LIB_TAG_DOIT; + BLI_array_append(bases, base_iter); + } + FOREACH_BASE_IN_MODE_END; + *r_bases_len = BLI_array_len(bases); + return bases; +} + +static bool do_pose_tag_select_op_exec(Base **bases, const uint bases_len, const eSelectOp sel_op) +{ + bool changed_multi = false; + + if (SEL_OP_USE_PRE_DESELECT(sel_op)) { + for (int i = 0; i < bases_len; i++) { + Base *base_iter = bases[i]; + Object *ob_iter = base_iter->object; + if (ED_pose_deselect_all(ob_iter, SEL_DESELECT, false)) { + ED_pose_bone_select_tag_update(ob_iter); + changed_multi = true; + } + } + } + + for (int i = 0; i < bases_len; i++) { + Base *base_iter = bases[i]; + Object *ob_iter = base_iter->object; + bArmature *arm = ob_iter->data; + + /* Don't handle twice. */ + if (arm->id.tag & LIB_TAG_DOIT) { + arm->id.tag &= ~LIB_TAG_DOIT; + } + else { + continue; + } + + bool changed = true; + for (bPoseChannel *pchan = ob_iter->pose->chanbase.first; pchan; pchan = pchan->next) { + 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 int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); if (sel_op_result != -1) { - ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT); + SET_FLAG_FROM_TEST(bone->flag, sel_op_result, BONE_SELECTED); + if (sel_op_result == 0) { + if (arm->act_bone == bone) { + arm->act_bone = NULL; + } + } + changed = true; } } - if (is_pose_mode && (base->object->mode & OB_MODE_POSE)) { - do_lasso_select_pose(vc, base->object, mcords, moves, sel_op); - } } + if (changed) { + ED_pose_bone_select_tag_update(ob_iter); + changed_multi = true; + } + } + return changed_multi; +} + +static void do_lasso_select_pose( + ViewContext *vc, const int mcords[][2], const short moves, + const eSelectOp sel_op) +{ + uint bases_len; + Base **bases = do_pose_tag_select_op_prepare(vc, &bases_len); + + for (int i = 0; i < bases_len; i++) { + Base *base_iter = bases[i]; + Object *ob_iter = base_iter->object; + do_lasso_tag_pose(vc, ob_iter, mcords, moves); + } + + const bool changed_multi = do_pose_tag_select_op_exec(bases, bases_len, sel_op); + if (changed_multi) { + DEG_id_tag_update(&vc->scene->id, DEG_TAG_SELECT_UPDATE); + WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, vc->scene); + } + + if (bases != NULL) { + MEM_freeN(bases); } } @@ -910,10 +990,11 @@ static void view3d_lasso_select( else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT)) { PE_lasso_select(C, mcords, moves, sel_op); } + else if (ob && (ob->mode & OB_MODE_POSE)) { + do_lasso_select_pose(vc, mcords, moves, sel_op); + } else { do_lasso_select_objects(vc, mcords, moves, sel_op); - DEG_id_tag_update(&vc->scene->id, DEG_TAG_SELECT_UPDATE); - WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc->scene); } } else { /* Edit Mode */ @@ -2255,182 +2336,63 @@ static int opengl_bone_select_buffer_cmp(const void *sel_a_p, const void *sel_b_ static int do_object_box_select(bContext *C, ViewContext *vc, rcti *rect, const eSelectOp sel_op) { - unsigned int *vbuffer = NULL; /* selection buffer */ - int bone_only; int totobj = MAXPICKBUF; /* XXX solve later */ - int hits; - - if (vc->obact && (vc->obact->mode & OB_MODE_POSE)) - bone_only = 1; - else - bone_only = 0; - - if (SEL_OP_USE_PRE_DESELECT(sel_op)) { - if (bone_only) { - FOREACH_OBJECT_IN_MODE_BEGIN (vc->view_layer, OB_MODE_POSE, ob_iter) { - bArmature *arm = ob_iter->data; - for (bPoseChannel *pchan = ob_iter->pose->chanbase.first; pchan; pchan = pchan->next) { - if (PBONE_VISIBLE(arm, pchan->bone)) { - if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0) { - pchan->bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - } - } - } - FOREACH_OBJECT_IN_MODE_END; - } - else { - object_deselect_all_visible(vc->view_layer); - } - } /* selection buffer now has bones potentially too, so we add MAXPICKBUF */ - vbuffer = MEM_mallocN(4 * (totobj + MAXPICKELEMS) * sizeof(unsigned int), "selection buffer"); + uint *vbuffer = MEM_mallocN(4 * (totobj + MAXPICKELEMS) * sizeof(uint[4]), "selection buffer"); const eV3DSelectObjectFilter select_filter = ( (vc->scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) ? VIEW3D_SELECT_FILTER_OBJECT_MODE_LOCK : VIEW3D_SELECT_FILTER_NOP); - hits = view3d_opengl_select( + const int hits = view3d_opengl_select( vc, vbuffer, 4 * (totobj + MAXPICKELEMS), rect, VIEW3D_SELECT_ALL, select_filter); - /* - * LOGIC NOTES (theeth): - * The buffer and ListBase have the same relative order, which makes the selection - * very simple. Loop through both data sets at the same time, if the color - * is the same as the object, we have a hit and can move to the next color - * and object pair, if not, just move to the next object, - * keeping the same color until we have a hit. - */ - if (hits <= 0) { - if (SEL_OP_USE_OUTSIDE(sel_op)) { - for (Base *base = vc->view_layer->object_bases.first; base && hits; base = base->next) { - if (BASE_SELECTABLE(base)) { - const bool is_select = base->flag & BASE_SELECTED; - const bool is_inside = false; /* we know there are no hits. */ - const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); - if (sel_op_result != -1) { - ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT); - } - } - } - } + for (Base *base = vc->view_layer->object_bases.first; base; base = base->next) { + base->object->id.tag &= ~LIB_TAG_DOIT; } - else { - /* no need to loop if there's no hit */ - /* The draw order doesn't always match the order we populate the engine, see: T51695. */ - qsort(vbuffer, hits, sizeof(uint[4]), opengl_bone_select_buffer_cmp); + Base **bases = NULL; + BLI_array_declare(bases); - Base **bases = NULL; - BLI_array_declare(bases); + /* The draw order doesn't always match the order we populate the engine, see: T51695. */ + if (hits > 0) { + qsort(vbuffer, hits, sizeof(uint[4]), opengl_bone_select_buffer_cmp); - for (Base *base = vc->view_layer->object_bases.first; base && hits; base = base->next) { + for (Base *base = vc->view_layer->object_bases.first; base; base = base->next) { if (BASE_SELECTABLE(base)) { if ((base->object->select_color & 0x0000FFFF) != 0) { BLI_array_append(bases, base); - base->object->id.tag &= ~LIB_TAG_DOIT; } } } + } - for (const uint *col = vbuffer + 3, *col_end = col + (hits * 4); col < col_end; col += 4) { - Bone *bone; - Base *base = ED_armature_base_and_bone_from_select_buffer(bases, BLI_array_len(bases), *col, &bone); - - if (base == NULL) { - continue; - } - /* Loop over contiguous bone hits for 'base'. */ - bool changed = false; - for (; col != col_end; col += 4) { - /* should never fail */ - if (bone != NULL) { - const bool is_select = (bone->flag & BONE_SELECTED) != 0; - const bool is_inside = true; - const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); - - if (sel_op_result != -1) { - if (sel_op_result) { - if ((bone->flag & BONE_UNSELECTABLE) == 0) { - bone->flag |= BONE_SELECTED; - } - } - else { - bArmature *arm = base->object->data; - if ((bone->flag & BONE_UNSELECTABLE) == 0) { - bone->flag &= ~BONE_SELECTED; - if (arm->act_bone == bone) - arm->act_bone = NULL; - } - } - } - changed = true; - } - else if (!bone_only) { - if ((base->object->id.tag & LIB_TAG_DOIT) == 0) { - base->object->id.tag |= LIB_TAG_DOIT; - const bool is_select = base->flag & BASE_SELECTED; - const bool is_inside = true; - const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); - if (sel_op_result != -1) { - ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT); - } - } - } - - /* Select the next bone if we're not switching bases. */ - if (col + 4 != col_end) { - if ((base->object->select_color & 0x0000FFFF) != (col[4] & 0x0000FFFF)) { - break; - } - - if ((base->object->pose != NULL) && bone_only) { - const uint hit_bone = (col[4] & ~BONESEL_ANY) >> 16; - bPoseChannel *pchan = BLI_findlink(&base->object->pose->chanbase, hit_bone); - bone = pchan ? pchan->bone : NULL; - } - else { - bone = NULL; - } - } - } - - if (changed) { - if (base->object && (base->object->type == OB_ARMATURE)) { - bArmature *arm = base->object->data; - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, base->object); - - if (vc->obact && arm && (arm->flag & ARM_HAS_VIZ_DEPS)) { - /* mask modifier ('armature' mode), etc. */ - DEG_id_tag_update(&vc->obact->id, OB_RECALC_DATA); - } + for (const uint *col = vbuffer + 3, *col_end = col + (hits * 4); col < col_end; col += 4) { + Bone *bone; + Base *base = ED_armature_base_and_bone_from_select_buffer(bases, BLI_array_len(bases), *col, &bone); + base->object->id.tag |= LIB_TAG_DOIT; + } - /* copy on write tag is needed (for the armature), or else no refresh happens */ - DEG_id_tag_update(&arm->id, DEG_TAG_COPY_ON_WRITE); - } - } - } + if (SEL_OP_USE_PRE_DESELECT(sel_op)) { + object_deselect_all_visible(vc->view_layer); + } - if (SEL_OP_USE_OUTSIDE(sel_op)) { - for (int i = 0; i < BLI_array_len(bases); i++) { - Base *base = bases[i]; - if ((base->object->id.tag & LIB_TAG_DOIT) == 0) { - const bool is_select = base->flag & BASE_SELECTED; - const bool is_inside = false; /* we know there are no hits. */ - const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); - if (sel_op_result != -1) { - ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT); - } - } + for (Base *base = vc->view_layer->object_bases.first; base && hits; base = base->next) { + if (BASE_SELECTABLE(base)) { + const bool is_select = base->flag & BASE_SELECTED; + const bool is_inside = base->object->id.tag & LIB_TAG_DOIT; + const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); + if (sel_op_result != -1) { + ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT); } } - - MEM_freeN(bases); - DEG_id_tag_update(&vc->scene->id, DEG_TAG_SELECT_UPDATE); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc->scene); } + + if (bases != NULL) { + MEM_freeN(bases); + } MEM_freeN(vbuffer); return hits > 0 ? OPERATOR_FINISHED : OPERATOR_CANCELLED; @@ -2438,41 +2400,17 @@ static int do_object_box_select(bContext *C, ViewContext *vc, rcti *rect, const static int do_pose_box_select(bContext *C, ViewContext *vc, rcti *rect, const eSelectOp sel_op) { - unsigned int *vbuffer = NULL; /* selection buffer */ - int bone_only; - int totobj = MAXPICKBUF; /* XXX solve later */ - int hits; - - if (vc->obact && (vc->obact->mode & OB_MODE_POSE)) - bone_only = 1; - else - bone_only = 0; + uint bases_len; + Base **bases = do_pose_tag_select_op_prepare(vc, &bases_len); - if (SEL_OP_USE_PRE_DESELECT(sel_op)) { - if (bone_only) { - FOREACH_OBJECT_IN_MODE_BEGIN (vc->view_layer, OB_MODE_POSE, ob_iter) { - bArmature *arm = ob_iter->data; - for (bPoseChannel *pchan = ob_iter->pose->chanbase.first; pchan; pchan = pchan->next) { - if (PBONE_VISIBLE(arm, pchan->bone)) { - if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0) { - pchan->bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - } - } - } - FOREACH_OBJECT_IN_MODE_END; - } - else { - object_deselect_all_visible(vc->view_layer); - } - } + int totobj = MAXPICKBUF; /* XXX solve later */ /* selection buffer now has bones potentially too, so we add MAXPICKBUF */ - vbuffer = MEM_mallocN(4 * (totobj + MAXPICKELEMS) * sizeof(unsigned int), "selection buffer"); + uint *vbuffer = MEM_mallocN((totobj + MAXPICKELEMS) * sizeof(uint[4]), "selection buffer"); const eV3DSelectObjectFilter select_filter = ( (vc->scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) ? VIEW3D_SELECT_FILTER_OBJECT_MODE_LOCK : VIEW3D_SELECT_FILTER_NOP); - hits = view3d_opengl_select( + const int hits = view3d_opengl_select( vc, vbuffer, 4 * (totobj + MAXPICKELEMS), rect, VIEW3D_SELECT_ALL, select_filter); /* @@ -2484,81 +2422,26 @@ static int do_pose_box_select(bContext *C, ViewContext *vc, rcti *rect, const eS * keeping the same color until we have a hit. */ - if (hits <= 0) { - if (SEL_OP_USE_OUTSIDE(sel_op)) { - for (Base *base = vc->view_layer->object_bases.first; base && hits; base = base->next) { - if (BASE_SELECTABLE(base)) { - const bool is_select = base->flag & BASE_SELECTED; - const bool is_inside = false; /* we know there are no hits. */ - const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); - if (sel_op_result != -1) { - ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT); - } - } - } - } - } - else { + if (hits > 0) { /* no need to loop if there's no hit */ /* The draw order doesn't always match the order we populate the engine, see: T51695. */ qsort(vbuffer, hits, sizeof(uint[4]), opengl_bone_select_buffer_cmp); - Base **bases = NULL; - BLI_array_declare(bases); - - for (Base *base = vc->view_layer->object_bases.first; base && hits; base = base->next) { - if (BASE_SELECTABLE(base)) { - if ((base->object->select_color & 0x0000FFFF) != 0) { - BLI_array_append(bases, base); - base->object->id.tag &= ~LIB_TAG_DOIT; - } - } - } - for (const uint *col = vbuffer + 3, *col_end = col + (hits * 4); col < col_end; col += 4) { Bone *bone; - Base *base = ED_armature_base_and_bone_from_select_buffer(bases, BLI_array_len(bases), *col, &bone); + Base *base = ED_armature_base_and_bone_from_select_buffer(bases, bases_len, *col, &bone); if (base == NULL) { continue; } + /* Loop over contiguous bone hits for 'base'. */ - bool changed = false; for (; col != col_end; col += 4) { /* should never fail */ if (bone != NULL) { - const bool is_select = (bone->flag & BONE_SELECTED) != 0; - const bool is_inside = true; - const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); - - if (sel_op_result != -1) { - if (sel_op_result) { - if ((bone->flag & BONE_UNSELECTABLE) == 0) { - bone->flag |= BONE_SELECTED; - } - } - else { - bArmature *arm = base->object->data; - if ((bone->flag & BONE_UNSELECTABLE) == 0) { - bone->flag &= ~BONE_SELECTED; - if (arm->act_bone == bone) - arm->act_bone = NULL; - } - } - } - changed = true; - } - else if (!bone_only) { - if ((base->object->id.tag & LIB_TAG_DOIT) == 0) { - base->object->id.tag |= LIB_TAG_DOIT; - const bool is_select = base->flag & BASE_SELECTED; - const bool is_inside = true; - const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); - if (sel_op_result != -1) { - ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT); - } - } + base->object->id.tag |= LIB_TAG_DOIT; + bone->flag |= BONE_DONE; } /* Select the next bone if we're not switching bases. */ @@ -2566,8 +2449,7 @@ static int do_pose_box_select(bContext *C, ViewContext *vc, rcti *rect, const eS if ((base->object->select_color & 0x0000FFFF) != (col[4] & 0x0000FFFF)) { break; } - - if ((base->object->pose != NULL) && bone_only) { + if (base->object->pose != NULL) { const uint hit_bone = (col[4] & ~BONESEL_ANY) >> 16; bPoseChannel *pchan = BLI_findlink(&base->object->pose->chanbase, hit_bone); bone = pchan ? pchan->bone : NULL; @@ -2577,43 +2459,18 @@ static int do_pose_box_select(bContext *C, ViewContext *vc, rcti *rect, const eS } } } - - if (changed) { - if (base->object && (base->object->type == OB_ARMATURE)) { - bArmature *arm = base->object->data; - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, base->object); - - if (vc->obact && arm && (arm->flag & ARM_HAS_VIZ_DEPS)) { - /* mask modifier ('armature' mode), etc. */ - DEG_id_tag_update(&vc->obact->id, OB_RECALC_DATA); - } - - /* copy on write tag is needed (for the armature), or else no refresh happens */ - DEG_id_tag_update(&arm->id, DEG_TAG_COPY_ON_WRITE); - } - } - } - - if (SEL_OP_USE_OUTSIDE(sel_op)) { - for (int i = 0; i < BLI_array_len(bases); i++) { - Base *base = bases[i]; - if ((base->object->id.tag & LIB_TAG_DOIT) == 0) { - const bool is_select = base->flag & BASE_SELECTED; - const bool is_inside = false; /* we know there are no hits. */ - const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); - if (sel_op_result != -1) { - ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT); - } - } - } } + } - MEM_freeN(bases); - + const bool changed_multi = do_pose_tag_select_op_exec(bases, bases_len, sel_op); + if (changed_multi) { DEG_id_tag_update(&vc->scene->id, DEG_TAG_SELECT_UPDATE); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc->scene); } + + if (bases != NULL) { + MEM_freeN(bases); + } MEM_freeN(vbuffer); return hits > 0 ? OPERATOR_FINISHED : OPERATOR_CANCELLED; @@ -3181,17 +3038,7 @@ static void pose_circle_select(ViewContext *vc, const bool select, const int mva pose_foreachScreenBone(vc, do_circle_select_pose__doSelectBone, &data, V3D_PROJ_TEST_CLIP_DEFAULT); if (data.is_changed) { - bArmature *arm = vc->obact->data; - - WM_main_add_notifier(NC_OBJECT | ND_BONE_SELECT, vc->obact); - - if (arm->flag & ARM_HAS_VIZ_DEPS) { - /* mask modifier ('armature' mode), etc. */ - DEG_id_tag_update(&vc->obact->id, OB_RECALC_DATA); - } - - /* copy on write tag is needed (for the armature), or else no refresh happens */ - DEG_id_tag_update(&arm->id, DEG_TAG_COPY_ON_WRITE); + ED_pose_bone_select_tag_update(vc->obact); } } |