Blender V4.3
pose_select.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cstring>
10
11#include "DNA_anim_types.h"
12#include "DNA_armature_types.h"
15#include "DNA_object_types.h"
16#include "DNA_scene_types.h"
17
18#include "MEM_guardedalloc.h"
19
20#include "BLI_blenlib.h"
21
22#include "BKE_action.hh"
23#include "BKE_armature.hh"
24#include "BKE_constraint.h"
25#include "BKE_context.hh"
26#include "BKE_layer.hh"
27#include "BKE_modifier.hh"
28#include "BKE_object.hh"
29#include "BKE_report.hh"
30
31#include "DEG_depsgraph.hh"
32
33#include "RNA_access.hh"
34#include "RNA_define.hh"
35
36#include "WM_api.hh"
37#include "WM_types.hh"
38
39#include "ED_armature.hh"
40#include "ED_keyframing.hh"
41#include "ED_mesh.hh"
42#include "ED_object.hh"
43#include "ED_object_vgroup.hh"
44#include "ED_outliner.hh"
45#include "ED_screen.hh"
46#include "ED_select_utils.hh"
47#include "ED_view3d.hh"
48
50#include "ANIM_bonecolor.hh"
51#include "ANIM_keyingsets.hh"
52
53#include "armature_intern.hh"
54
55using blender::Span;
56using blender::Vector;
57
58/* utility macros for storing a temp int in the bone (selection flag) */
59#define PBONE_PREV_FLAG_GET(pchan) ((void)0, POINTER_AS_INT((pchan)->temp))
60#define PBONE_PREV_FLAG_SET(pchan, val) ((pchan)->temp = POINTER_FROM_INT(val))
61
62/* ***************** Pose Select Utilities ********************* */
63
64/* NOTE: SEL_TOGGLE is assumed to have already been handled! */
65static void pose_do_bone_select(bPoseChannel *pchan, const int select_mode)
66{
67 /* select pchan only if selectable, but deselect works always */
68 switch (select_mode) {
69 case SEL_SELECT:
70 if (!(pchan->bone->flag & BONE_UNSELECTABLE)) {
71 pchan->bone->flag |= BONE_SELECTED;
72 }
73 break;
74 case SEL_DESELECT:
76 break;
77 case SEL_INVERT:
78 if (pchan->bone->flag & BONE_SELECTED) {
80 }
81 else if (!(pchan->bone->flag & BONE_UNSELECTABLE)) {
82 pchan->bone->flag |= BONE_SELECTED;
83 }
84 break;
85 }
86}
87
89{
91 bArmature *arm = static_cast<bArmature *>(ob->data);
94
95 if (arm->flag & ARM_HAS_VIZ_DEPS) {
96 /* mask modifier ('armature' mode), etc. */
98 }
99
101}
102
103void ED_pose_bone_select(Object *ob, bPoseChannel *pchan, bool select, bool change_active)
104{
105 bArmature *arm;
106
107 /* sanity checks */
108 /* XXX: actually, we can probably still get away with no object - at most we have no updates */
109 if (ELEM(nullptr, ob, ob->pose, pchan, pchan->bone)) {
110 return;
111 }
112
113 arm = static_cast<bArmature *>(ob->data);
114
115 /* can only change selection state if bone can be modified */
116 if (PBONE_SELECTABLE(arm, pchan->bone)) {
117 /* change selection state - activate too if selected */
118 if (select) {
119 pchan->bone->flag |= BONE_SELECTED;
120 if (change_active) {
121 arm->act_bone = pchan->bone;
122 }
123 }
124 else {
125 pchan->bone->flag &= ~BONE_SELECTED;
126 if (change_active) {
127 arm->act_bone = nullptr;
128 }
129 }
130
131 /* TODO: select and activate corresponding vgroup? */
133 }
134}
135
137 ViewLayer *view_layer,
138 View3D *v3d,
139 Object *ob,
140 Bone *bone,
142{
143 bool found = false;
144 bool changed = false;
145
146 if (ob->pose) {
147 if (bone && ((bone->flag & BONE_UNSELECTABLE) == 0)) {
148 found = true;
149 }
150 }
151
152 if (params->sel_op == SEL_OP_SET) {
153 if ((found && params->select_passthrough) && (bone->flag & BONE_SELECTED)) {
154 found = false;
155 }
156 else if (found || params->deselect_all) {
157 /* Deselect everything. */
158 /* Don't use 'BKE_object_pose_base_array_get_unique'
159 * because we may be selecting from object mode. */
160 FOREACH_VISIBLE_BASE_BEGIN (scene, view_layer, v3d, base_iter) {
161 Object *ob_iter = base_iter->object;
162 if ((ob_iter->type == OB_ARMATURE) && (ob_iter->mode & OB_MODE_POSE)) {
163 if (ED_pose_deselect_all(ob_iter, SEL_DESELECT, true)) {
165 }
166 }
167 }
169 changed = true;
170 }
171 }
172
173 if (found) {
174 BKE_view_layer_synced_ensure(scene, view_layer);
175 Object *ob_act = BKE_view_layer_active_object_get(view_layer);
176 BLI_assert(BKE_view_layer_edit_object_get(view_layer) == nullptr);
177
178 /* If the bone cannot be affected, don't do anything. */
179 bArmature *arm = static_cast<bArmature *>(ob->data);
180
181 /* Since we do unified select, we don't shift+select a bone if the
182 * armature object was not active yet.
183 * NOTE(@ideasman42): special exception for armature mode so we can do multi-select
184 * we could check for multi-select explicitly but think its fine to
185 * always give predictable behavior in weight paint mode. */
186 if ((ob_act == nullptr) || ((ob_act != ob) && (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) == 0))
187 {
188 /* When we are entering into posemode via toggle-select,
189 * from another active object - always select the bone. */
190 if (params->sel_op == SEL_OP_SET) {
191 /* Re-select the bone again later in this function. */
192 bone->flag &= ~BONE_SELECTED;
193 }
194 }
195
196 switch (params->sel_op) {
197 case SEL_OP_ADD: {
199 arm->act_bone = bone;
200 break;
201 }
202 case SEL_OP_SUB: {
204 break;
205 }
206 case SEL_OP_XOR: {
207 if (bone->flag & BONE_SELECTED) {
208 /* If not active, we make it active. */
209 if (bone != arm->act_bone) {
210 arm->act_bone = bone;
211 }
212 else {
214 }
215 }
216 else {
218 arm->act_bone = bone;
219 }
220 break;
221 }
222 case SEL_OP_SET: {
224 arm->act_bone = bone;
225 break;
226 }
227 case SEL_OP_AND: {
228 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
229 break;
230 }
231 }
232
233 if (ob_act) {
234 /* In weight-paint we select the associated vertex group too. */
235 if (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) {
236 if (bone == arm->act_bone) {
239 }
240 }
241 /* If there are some dependencies for visualizing armature state
242 * (e.g. Mask Modifier in 'Armature' mode), force update.
243 */
244 else if (arm->flag & ARM_HAS_VIZ_DEPS) {
245 /* NOTE: ob not ob_act here is intentional - it's the source of the
246 * bones being selected [#37247].
247 */
249 }
250
251 /* Tag armature for copy-on-evaluation update (since act_bone is in armature not object). */
253 }
254
255 changed = true;
256 }
257
258 return changed || found;
259}
260
262 ViewLayer *view_layer,
263 View3D *v3d,
264 Base *base,
265 const GPUSelectResult *hit_results,
266 const int hits,
268 bool do_nearest)
269{
270 Object *ob = base->object;
271 Bone *nearBone;
272
273 if (!ob || !ob->pose) {
274 return false;
275 }
276
277 /* Callers happen to already get the active base */
278 Base *base_dummy = nullptr;
280 {base}, hit_results, hits, true, do_nearest, &base_dummy);
281
282 return ED_armature_pose_select_pick_bone(scene, view_layer, v3d, ob, nearBone, params);
283}
284
286 ViewLayer *view_layer,
287 Base *base_select)
288{
289 BLI_assert(base_select && (base_select->object->type == OB_ARMATURE));
290 BKE_view_layer_synced_ensure(scene, view_layer);
291 Object *ob_active = BKE_view_layer_active_object_get(view_layer);
292 BLI_assert(ob_active && (ob_active->mode & OB_MODE_ALL_WEIGHT_PAINT));
293
294 VirtualModifierData virtual_modifier_data;
295 ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob_active, &virtual_modifier_data);
296 for (; md; md = md->next) {
297 if (md->type == eModifierType_Armature) {
299 Object *ob_arm = amd->object;
300 if (ob_arm != nullptr) {
301 Base *base_arm = BKE_view_layer_base_find(view_layer, ob_arm);
302 if ((base_arm != nullptr) && (base_arm != base_select) && (base_arm->flag & BASE_SELECTED))
303 {
305 }
306 }
307 }
308 }
309 if ((base_select->flag & BASE_SELECTED) == 0) {
311 }
312}
313
314bool ED_pose_deselect_all(Object *ob, int select_mode, const bool ignore_visibility)
315{
316 bArmature *arm = static_cast<bArmature *>(ob->data);
317
318 /* we call this from outliner too */
319 if (ob->pose == nullptr) {
320 return false;
321 }
322
323 /* Determine if we're selecting or deselecting */
324 if (select_mode == SEL_TOGGLE) {
325 select_mode = SEL_SELECT;
326 LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
327 if (ignore_visibility || PBONE_VISIBLE(arm, pchan->bone)) {
328 if (pchan->bone->flag & BONE_SELECTED) {
329 select_mode = SEL_DESELECT;
330 break;
331 }
332 }
333 }
334 }
335
336 /* Set the flags accordingly */
337 bool changed = false;
338 LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
339 /* ignore the pchan if it isn't visible or if its selection cannot be changed */
340 if (ignore_visibility || PBONE_VISIBLE(arm, pchan->bone)) {
341 int flag_prev = pchan->bone->flag;
342 pose_do_bone_select(pchan, select_mode);
343 changed = (changed || flag_prev != pchan->bone->flag);
344 }
345 }
346 return changed;
347}
348
349static bool ed_pose_is_any_selected(Object *ob, bool ignore_visibility)
350{
351 bArmature *arm = static_cast<bArmature *>(ob->data);
352 LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
353 if (ignore_visibility || PBONE_VISIBLE(arm, pchan->bone)) {
354 if (pchan->bone->flag & BONE_SELECTED) {
355 return true;
356 }
357 }
358 }
359 return false;
360}
361
362static bool ed_pose_is_any_selected_multi(const Span<Base *> bases, bool ignore_visibility)
363{
364 for (Base *base : bases) {
365 Object *ob_iter = base->object;
366 if (ed_pose_is_any_selected(ob_iter, ignore_visibility)) {
367 return true;
368 }
369 }
370 return false;
371}
372
374 int select_mode,
375 const bool ignore_visibility)
376{
377 if (select_mode == SEL_TOGGLE) {
378 select_mode = ed_pose_is_any_selected_multi(bases, ignore_visibility) ? SEL_DESELECT :
380 }
381
382 bool changed_multi = false;
383 for (Base *base : bases) {
384 Object *ob_iter = base->object;
385 if (ED_pose_deselect_all(ob_iter, select_mode, ignore_visibility)) {
387 changed_multi = true;
388 }
389 }
390 return changed_multi;
391}
392
393bool ED_pose_deselect_all_multi(bContext *C, int select_mode, const bool ignore_visibility)
394{
397
399 return ED_pose_deselect_all_multi_ex(bases, select_mode, ignore_visibility);
400}
401
402/* ***************** Selections ********************** */
403
404static void selectconnected_posebonechildren(Object *ob, Bone *bone, int extend)
405{
406 /* stop when unconnected child is encountered, or when unselectable bone is encountered */
407 if (!(bone->flag & BONE_CONNECTED) || (bone->flag & BONE_UNSELECTABLE)) {
408 return;
409 }
410
411 if (extend) {
412 bone->flag &= ~BONE_SELECTED;
413 }
414 else {
415 bone->flag |= BONE_SELECTED;
416 }
417
418 LISTBASE_FOREACH (Bone *, curBone, &bone->childbase) {
419 selectconnected_posebonechildren(ob, curBone, extend);
420 }
421}
422
423/* within active object context */
424/* previously known as "selectconnected_posearmature" */
426{
427 Bone *bone, *curBone, *next = nullptr;
428 const bool extend = RNA_boolean_get(op->ptr, "extend");
429
431
432 Base *base = nullptr;
433 bone = ED_armature_pick_bone(C, event->mval, !extend, &base);
434
435 if (!bone) {
436 return OPERATOR_CANCELLED;
437 }
438
439 /* Select parents */
440 for (curBone = bone; curBone; curBone = next) {
441 /* ignore bone if cannot be selected */
442 if ((curBone->flag & BONE_UNSELECTABLE) == 0) {
443 if (extend) {
444 curBone->flag &= ~BONE_SELECTED;
445 }
446 else {
447 curBone->flag |= BONE_SELECTED;
448 }
449
450 if (curBone->flag & BONE_CONNECTED) {
451 next = curBone->parent;
452 }
453 else {
454 next = nullptr;
455 }
456 }
457 else {
458 next = nullptr;
459 }
460 }
461
462 /* Select children */
463 LISTBASE_FOREACH (Bone *, curBone, &bone->childbase) {
464 selectconnected_posebonechildren(base->object, curBone, extend);
465 }
466
468
470
471 return OPERATOR_FINISHED;
472}
473
478
480{
481 PropertyRNA *prop;
482
483 /* identifiers */
484 ot->name = "Select Connected";
485 ot->idname = "POSE_OT_select_linked_pick";
486 ot->description = "Select bones linked by parent/child connections under the mouse cursor";
487
488 /* callbacks */
489 /* leave 'exec' unset */
492
493 /* flags */
495
496 /* props */
497 prop = RNA_def_boolean(ot->srna,
498 "extend",
499 false,
500 "Extend",
501 "Extend selection instead of deselecting everything first");
503}
504
506{
507 Bone *curBone, *next = nullptr;
508
509 CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, visible_pose_bones, Object *, ob) {
510 if ((pchan->bone->flag & BONE_SELECTED) == 0) {
511 continue;
512 }
513
514 bArmature *arm = static_cast<bArmature *>(ob->data);
515
516 /* Select parents */
517 for (curBone = pchan->bone; curBone; curBone = next) {
518 if (PBONE_SELECTABLE(arm, curBone)) {
519 curBone->flag |= BONE_SELECTED;
520
521 if (curBone->flag & BONE_CONNECTED) {
522 next = curBone->parent;
523 }
524 else {
525 next = nullptr;
526 }
527 }
528 else {
529 next = nullptr;
530 }
531 }
532
533 /* Select children */
534 LISTBASE_FOREACH (Bone *, curBone, &pchan->bone->childbase) {
535 selectconnected_posebonechildren(ob, curBone, false);
536 }
538 }
540
542
543 return OPERATOR_FINISHED;
544}
545
547{
548 /* identifiers */
549 ot->name = "Select Connected";
550 ot->idname = "POSE_OT_select_linked";
551 ot->description = "Select all bones linked by parent/child connections to the current selection";
552
553 /* callbacks */
555 ot->poll = ED_operator_posemode;
556
557 /* flags */
559}
560
561/* -------------------------------------- */
562
564{
565 int action = RNA_enum_get(op->ptr, "action");
566
567 Scene *scene = CTX_data_scene(C);
568 int multipaint = scene->toolsettings->multipaint;
569
570 if (action == SEL_TOGGLE) {
571 action = CTX_DATA_COUNT(C, selected_pose_bones) ? SEL_DESELECT : SEL_SELECT;
572 }
573
574 Object *ob_prev = nullptr;
575
576 /* Set the flags. */
577 CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, visible_pose_bones, Object *, ob) {
578 bArmature *arm = static_cast<bArmature *>(ob->data);
579 pose_do_bone_select(pchan, action);
580
581 if (ob_prev != ob) {
582 /* Weight-paint or mask modifiers need depsgraph updates. */
583 if (multipaint || (arm->flag & ARM_HAS_VIZ_DEPS)) {
585 }
586 /* need to tag armature for cow updates, or else selection doesn't update */
588 ob_prev = ob;
589 }
590 }
592
594
596
597 return OPERATOR_FINISHED;
598}
599
601{
602 /* identifiers */
603 ot->name = "(De)select All";
604 ot->idname = "POSE_OT_select_all";
605 ot->description = "Toggle selection status of all bones";
606
607 /* api callbacks */
609 ot->poll = ED_operator_posemode;
610
611 /* flags */
613
615}
616
617/* -------------------------------------- */
618
620{
622 bArmature *arm = (bArmature *)ob->data;
623 bPoseChannel *pchan, *parent;
624
625 /* Determine if there is an active bone */
627 if (pchan) {
628 parent = pchan->parent;
629 if ((parent) && !(parent->bone->flag & (BONE_HIDDEN_P | BONE_UNSELECTABLE))) {
630 parent->bone->flag |= BONE_SELECTED;
631 arm->act_bone = parent->bone;
632 }
633 else {
634 return OPERATOR_CANCELLED;
635 }
636 }
637 else {
638 return OPERATOR_CANCELLED;
639 }
640
642
644 return OPERATOR_FINISHED;
645}
646
648{
649 /* identifiers */
650 ot->name = "Select Parent Bone";
651 ot->idname = "POSE_OT_select_parent";
652 ot->description = "Select bones that are parents of the currently selected bones";
653
654 /* api callbacks */
656 ot->poll = ED_operator_posemode;
657
658 /* flags */
660}
661
662/* -------------------------------------- */
663
665{
666 int found = 0;
667
668 CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) {
669 if (pchan->bone->flag & BONE_SELECTED) {
670 LISTBASE_FOREACH (bConstraint *, con, &pchan->constraints) {
671 ListBase targets = {nullptr, nullptr};
672 if (BKE_constraint_targets_get(con, &targets)) {
673 LISTBASE_FOREACH (bConstraintTarget *, ct, &targets) {
674 Object *ob = ct->tar;
675
676 /* Any armature that is also in pose mode should be selected. */
677 if ((ct->subtarget[0] != '\0') && (ob != nullptr) && (ob->type == OB_ARMATURE) &&
678 (ob->mode == OB_MODE_POSE))
679 {
680 bPoseChannel *pchanc = BKE_pose_channel_find_name(ob->pose, ct->subtarget);
681 if ((pchanc) && !(pchanc->bone->flag & BONE_UNSELECTABLE)) {
684 found = 1;
685 }
686 }
687 }
688
689 BKE_constraint_targets_flush(con, &targets, true);
690 }
691 }
692 }
693 }
695
696 if (!found) {
697 return OPERATOR_CANCELLED;
698 }
699
701
702 return OPERATOR_FINISHED;
703}
704
706{
707 /* identifiers */
708 ot->name = "Select Constraint Target";
709 ot->idname = "POSE_OT_select_constraint_target";
710 ot->description = "Select bones used as targets for the currently selected bones";
711
712 /* api callbacks */
714 ot->poll = ED_operator_posemode;
715
716 /* flags */
718}
719
720/* -------------------------------------- */
721
722/* No need to convert to multi-objects. Just like we keep the non-active bones
723 * selected we then keep the non-active objects untouched (selected/unselected). */
725{
727 bArmature *arm = static_cast<bArmature *>(ob->data);
728 bPoseChannel *pchan_act;
729 int direction = RNA_enum_get(op->ptr, "direction");
730 const bool add_to_sel = RNA_boolean_get(op->ptr, "extend");
731 bool changed = false;
732
734 if (pchan_act == nullptr) {
735 return OPERATOR_CANCELLED;
736 }
737
738 if (direction == BONE_SELECT_PARENT) {
739 if (pchan_act->parent) {
740 Bone *bone_parent;
741 bone_parent = pchan_act->parent->bone;
742
743 if (PBONE_SELECTABLE(arm, bone_parent)) {
744 if (!add_to_sel) {
745 pchan_act->bone->flag &= ~BONE_SELECTED;
746 }
747 bone_parent->flag |= BONE_SELECTED;
748 arm->act_bone = bone_parent;
749
750 changed = true;
751 }
752 }
753 }
754 else { /* direction == BONE_SELECT_CHILD */
755 Bone *bone_child = nullptr;
756 int pass;
757
758 /* first pass, only connected bones (the logical direct child) */
759 for (pass = 0; pass < 2 && (bone_child == nullptr); pass++) {
760 LISTBASE_FOREACH (bPoseChannel *, pchan_iter, &ob->pose->chanbase) {
761 /* possible we have multiple children, some invisible */
762 if (PBONE_SELECTABLE(arm, pchan_iter->bone)) {
763 if (pchan_iter->parent == pchan_act) {
764 if ((pass == 1) || (pchan_iter->bone->flag & BONE_CONNECTED)) {
765 bone_child = pchan_iter->bone;
766 break;
767 }
768 }
769 }
770 }
771 }
772
773 if (bone_child) {
774 arm->act_bone = bone_child;
775
776 if (!add_to_sel) {
777 pchan_act->bone->flag &= ~BONE_SELECTED;
778 }
779 bone_child->flag |= BONE_SELECTED;
780
781 changed = true;
782 }
783 }
784
785 if (changed == false) {
786 return OPERATOR_CANCELLED;
787 }
788
790
792
793 return OPERATOR_FINISHED;
794}
795
797{
798 static const EnumPropertyItem direction_items[] = {
799 {BONE_SELECT_PARENT, "PARENT", 0, "Select Parent", ""},
800 {BONE_SELECT_CHILD, "CHILD", 0, "Select Child", ""},
801 {0, nullptr, 0, nullptr, nullptr},
802 };
803
804 /* identifiers */
805 ot->name = "Select Hierarchy";
806 ot->idname = "POSE_OT_select_hierarchy";
807 ot->description = "Select immediate parent/children of selected bones";
808
809 /* api callbacks */
811 ot->poll = ED_operator_posemode;
812
813 /* flags */
815
816 /* props */
817 ot->prop = RNA_def_enum(
818 ot->srna, "direction", direction_items, BONE_SELECT_PARENT, "Direction", "");
819 RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
820}
821
822/* -------------------------------------- */
823
824/* modes for select same */
830
831static bool pose_select_same_color(bContext *C, const bool extend)
832{
833 /* Get a set of all the colors of the selected bones. */
835 blender::Set<Object *> updated_objects;
836 bool changed_any_selection = false;
837
838 /* Old approach that we may want to reinstate behind some option at some point. This will match
839 * against the colors of all selected bones, instead of just the active one. It also explains why
840 * there is a set of colors to begin with.
841 *
842 * CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones) {
843 * auto color = blender::animrig::ANIM_bonecolor_posebone_get(pchan);
844 * used_colors.add(color);
845 * }
846 * CTX_DATA_END;
847 */
848 if (!extend) {
849 CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, selected_pose_bones, Object *, ob) {
850 pchan->bone->flag &= ~BONE_SELECTED;
851 updated_objects.add(ob);
852 changed_any_selection = true;
853 }
855 }
856
857 /* Use the color of the active pose bone. */
858 bPoseChannel *active_pose_bone = CTX_data_active_pose_bone(C);
860 used_colors.add(color);
861
862 /* Select all visible bones that have the same color. */
863 CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, visible_pose_bones, Object *, ob) {
864 Bone *bone = pchan->bone;
865 if (bone->flag & (BONE_UNSELECTABLE | BONE_SELECTED)) {
866 /* Skip bones that are unselectable or already selected. */
867 continue;
868 }
869
871 if (!used_colors.contains(color)) {
872 continue;
873 }
874
875 bone->flag |= BONE_SELECTED;
876 changed_any_selection = true;
877 updated_objects.add(ob);
878 }
880
881 if (!changed_any_selection) {
882 return false;
883 }
884
885 for (Object *ob : updated_objects) {
887 }
888 return true;
889}
890
891static bool pose_select_same_collection(bContext *C, const bool extend)
892{
893 bool changed_any_selection = false;
894 blender::Set<Object *> updated_objects;
895
896 /* Refuse to do anything if there is no active pose bone. */
898 if (!active_pchan) {
899 return false;
900 }
901
902 if (!extend) {
903 /* Deselect all the bones. */
904 CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, selected_pose_bones, Object *, ob) {
905 pchan->bone->flag &= ~BONE_SELECTED;
906 updated_objects.add(ob);
907 changed_any_selection = true;
908 }
910 }
911
912 /* Build a set of bone collection names, to allow cross-Armature selection. */
913 blender::Set<std::string> collection_names;
914 LISTBASE_FOREACH (BoneCollectionReference *, bcoll_ref, &active_pchan->bone->runtime.collections)
915 {
916 collection_names.add(bcoll_ref->bcoll->name);
917 }
918
919 /* Select all bones that match any of the collection names. */
920 CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, visible_pose_bones, Object *, ob) {
921 Bone *bone = pchan->bone;
922 if (bone->flag & (BONE_UNSELECTABLE | BONE_SELECTED)) {
923 continue;
924 }
925
927 if (!collection_names.contains(bcoll_ref->bcoll->name)) {
928 continue;
929 }
930
931 bone->flag |= BONE_SELECTED;
932 changed_any_selection = true;
933 updated_objects.add(ob);
934 }
935 }
937
938 for (Object *ob : updated_objects) {
940 }
941
942 return changed_any_selection;
943}
944
945static bool pose_select_same_keyingset(bContext *C, ReportList *reports, bool extend)
946{
947 Scene *scene = CTX_data_scene(C);
948 ViewLayer *view_layer = CTX_data_view_layer(C);
949 bool changed_multi = false;
951
952 /* sanity checks: validate Keying Set and object */
953 if (ks == nullptr) {
954 BKE_report(reports, RPT_ERROR, "No active Keying Set to use");
955 return false;
956 }
958 if (ks->paths.first == nullptr) {
959 if ((ks->flag & KEYINGSET_ABSOLUTE) == 0) {
960 BKE_report(reports,
961 RPT_ERROR,
962 "Use another Keying Set, as the active one depends on the currently "
963 "selected items or cannot find any targets due to unsuitable context");
964 }
965 else {
966 BKE_report(reports, RPT_ERROR, "Keying Set does not contain any paths");
967 }
968 }
969 return false;
970 }
971
972 /* if not extending selection, deselect all selected first */
973 if (extend == false) {
974 CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) {
975 if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0) {
976 pchan->bone->flag &= ~BONE_SELECTED;
977 }
978 }
980 }
981
983
984 for (const int ob_index : objects.index_range()) {
985 Object *ob = BKE_object_pose_armature_get(objects[ob_index]);
986 bArmature *arm = static_cast<bArmature *>((ob) ? ob->data : nullptr);
987 bPose *pose = (ob) ? ob->pose : nullptr;
988 bool changed = false;
989
990 /* Sanity checks. */
991 if (ELEM(nullptr, ob, pose, arm)) {
992 continue;
993 }
994
995 /* iterate over elements in the Keying Set, setting selection depending on whether
996 * that bone is visible or not...
997 */
998 LISTBASE_FOREACH (KS_Path *, ksp, &ks->paths) {
999 /* only items related to this object will be relevant */
1000 if ((ksp->id == &ob->id) && (ksp->rna_path != nullptr)) {
1001 bPoseChannel *pchan = nullptr;
1002 char boneName[sizeof(pchan->name)];
1003 if (!BLI_str_quoted_substr(ksp->rna_path, "bones[", boneName, sizeof(boneName))) {
1004 continue;
1005 }
1006 pchan = BKE_pose_channel_find_name(pose, boneName);
1007
1008 if (pchan) {
1009 /* select if bone is visible and can be affected */
1010 if (PBONE_SELECTABLE(arm, pchan->bone)) {
1011 pchan->bone->flag |= BONE_SELECTED;
1012 changed = true;
1013 }
1014 }
1015 }
1016 }
1017
1018 if (changed || !extend) {
1020 changed_multi = true;
1021 }
1022 }
1023
1024 return changed_multi;
1025}
1026
1028{
1031 const bool extend = RNA_boolean_get(op->ptr, "extend");
1032 bool changed = false;
1033
1034 /* sanity check */
1035 if (ob->pose == nullptr) {
1036 return OPERATOR_CANCELLED;
1037 }
1038
1039 /* selection types */
1040 switch (type) {
1042 changed = pose_select_same_collection(C, extend);
1043 break;
1044
1046 changed = pose_select_same_color(C, extend);
1047 break;
1048
1049 case POSE_SEL_SAME_KEYINGSET: /* Keying Set */
1050 changed = pose_select_same_keyingset(C, op->reports, extend);
1051 break;
1052
1053 default:
1054 printf("pose_select_grouped() - Unknown selection type %d\n", type);
1055 break;
1056 }
1057
1058 /* report done status */
1059 if (changed) {
1061
1062 return OPERATOR_FINISHED;
1063 }
1064 return OPERATOR_CANCELLED;
1065}
1066
1068{
1069 static const EnumPropertyItem prop_select_grouped_types[] = {
1071 "COLLECTION",
1072 0,
1073 "Collection",
1074 "Same collections as the active bone"},
1075 {POSE_SEL_SAME_COLOR, "COLOR", 0, "Color", "Same color as the active bone"},
1077 "KEYINGSET",
1078 0,
1079 "Keying Set",
1080 "All bones affected by active Keying Set"},
1081 {0, nullptr, 0, nullptr, nullptr},
1082 };
1083
1084 /* identifiers */
1085 ot->name = "Select Grouped";
1086 ot->description = "Select all visible bones grouped by similar properties";
1087 ot->idname = "POSE_OT_select_grouped";
1088
1089 /* api callbacks */
1090 ot->invoke = WM_menu_invoke;
1092 ot->poll = ED_operator_posemode; /* TODO: expand to support edit mode as well. */
1093
1094 /* flags */
1095 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1096
1097 /* properties */
1098 RNA_def_boolean(ot->srna,
1099 "extend",
1100 false,
1101 "Extend",
1102 "Extend selection instead of deselecting everything first");
1103 ot->prop = RNA_def_enum(ot->srna, "type", prop_select_grouped_types, 0, "Type", "");
1104}
1105
1106/* -------------------------------------- */
1107
1112{
1113 const Scene *scene = CTX_data_scene(C);
1114 ViewLayer *view_layer = CTX_data_view_layer(C);
1115 Object *ob_active = CTX_data_active_object(C);
1116
1117 const bool is_weight_paint = (ob_active->mode & OB_MODE_WEIGHT_PAINT) != 0;
1118 const bool active_only = RNA_boolean_get(op->ptr, "only_active");
1119 const bool extend = RNA_boolean_get(op->ptr, "extend");
1120
1122 for (Object *ob : objects) {
1123 bArmature *arm = static_cast<bArmature *>(ob->data);
1124 bPoseChannel *pchan_mirror_act = nullptr;
1125
1126 LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
1127 const int flag = (pchan->bone->flag & BONE_SELECTED);
1128 PBONE_PREV_FLAG_SET(pchan, flag);
1129 }
1130
1131 LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
1132 if (PBONE_SELECTABLE(arm, pchan->bone)) {
1133 bPoseChannel *pchan_mirror;
1134 int flag_new = extend ? PBONE_PREV_FLAG_GET(pchan) : 0;
1135
1136 if ((pchan_mirror = BKE_pose_channel_get_mirrored(ob->pose, pchan->name)) &&
1137 PBONE_VISIBLE(arm, pchan_mirror->bone))
1138 {
1139 const int flag_mirror = PBONE_PREV_FLAG_GET(pchan_mirror);
1140 flag_new |= flag_mirror;
1141
1142 if (pchan->bone == arm->act_bone) {
1143 pchan_mirror_act = pchan_mirror;
1144 }
1145
1146 /* Skip all but the active or its mirror. */
1147 if (active_only && !ELEM(arm->act_bone, pchan->bone, pchan_mirror->bone)) {
1148 continue;
1149 }
1150 }
1151
1152 pchan->bone->flag = (pchan->bone->flag & ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)) |
1153 flag_new;
1154 }
1155 }
1156
1157 if (pchan_mirror_act) {
1158 arm->act_bone = pchan_mirror_act->bone;
1159
1160 /* In weight-paint we select the associated vertex group too. */
1161 if (is_weight_paint) {
1162 blender::ed::object::vgroup_select_by_name(ob_active, pchan_mirror_act->name);
1164 }
1165 }
1166
1168
1169 /* Need to tag armature for cow updates, or else selection doesn't update. */
1171 }
1172
1174
1175 return OPERATOR_FINISHED;
1176}
1177
1179{
1180 /* identifiers */
1181 ot->name = "Select Mirror";
1182 ot->idname = "POSE_OT_select_mirror";
1183 ot->description = "Mirror the bone selection";
1184
1185 /* api callbacks */
1187 ot->poll = ED_operator_posemode;
1188
1189 /* flags */
1190 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1191
1192 /* properties */
1194 ot->srna, "only_active", false, "Active Only", "Only operate on the active bone");
1195 RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
1196}
C++ functions to deal with Armature collections (i.e. the successor of bone layers).
C++ part of the BoneColor DNA struct.
Functionality to interact with keying sets.
Blender kernel action and pose functionality.
bPoseChannel * BKE_pose_channel_find_name(const bPose *pose, const char *name)
bPoseChannel * BKE_pose_channel_get_mirrored(const bPose *pose, const char *name) ATTR_WARN_UNUSED_RESULT
bPoseChannel * BKE_pose_channel_active_if_bonecoll_visible(Object *ob) ATTR_WARN_UNUSED_RESULT
#define PBONE_VISIBLE(arm, bone)
#define PBONE_SELECTABLE(arm, bone)
void BKE_constraint_targets_flush(struct bConstraint *con, struct ListBase *targets, bool no_copy)
int BKE_constraint_targets_get(struct bConstraint *con, struct ListBase *r_targets)
#define CTX_DATA_BEGIN_WITH_ID(C, Type, instance, member, Type_id, instance_id)
bPoseChannel * CTX_data_active_pose_bone(const bContext *C)
#define CTX_DATA_BEGIN(C, Type, instance, member)
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
#define CTX_DATA_COUNT(C, member)
Scene * CTX_data_scene(const bContext *C)
#define CTX_DATA_END
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
#define FOREACH_VISIBLE_BASE_END
Definition BKE_layer.hh:417
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
Object * BKE_view_layer_active_object_get(const ViewLayer *view_layer)
#define FOREACH_VISIBLE_BASE_BEGIN(_scene, _view_layer, _v3d, _instance)
Definition BKE_layer.hh:404
Base * BKE_view_layer_base_find(ViewLayer *view_layer, Object *ob)
Object * BKE_view_layer_edit_object_get(const ViewLayer *view_layer)
ModifierData * BKE_modifiers_get_virtual_modifierlist(const Object *ob, VirtualModifierData *data)
General operations, lookup, etc. for blender objects.
blender::Vector< Base * > BKE_object_pose_base_array_get_unique(const Scene *scene, ViewLayer *view_layer, View3D *v3d)
Object * BKE_object_pose_armature_get(Object *ob)
blender::Vector< Object * > BKE_object_pose_array_get_unique(const Scene *scene, ViewLayer *view_layer, View3D *v3d)
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
#define LISTBASE_FOREACH(type, var, list)
bool bool BLI_str_quoted_substr(const char *__restrict str, const char *__restrict prefix, char *result, size_t result_maxncpy)
Definition string.c:517
#define ELEM(...)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_SELECT
Definition DNA_ID.h:1068
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1085
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
@ KEYINGSET_ABSOLUTE
@ BONE_ROOTSEL
@ BONE_SELECTED
@ BONE_UNSELECTABLE
@ BONE_HIDDEN_P
@ BONE_TIPSEL
@ BONE_CONNECTED
@ ARM_HAS_VIZ_DEPS
@ eModifierType_Armature
#define OB_MODE_ALL_WEIGHT_PAINT
@ OB_MODE_WEIGHT_PAINT
@ OB_MODE_POSE
Object is a sort of wrapper for general info.
@ OB_ARMATURE
#define BASE_SELECTED(v3d, base)
#define BONE_SELECT_CHILD
#define BONE_SELECT_PARENT
void ED_outliner_select_sync_from_pose_bone_tag(bContext *C)
bool ED_operator_view3d_active(bContext *C)
bool ED_operator_posemode(bContext *C)
@ SEL_SELECT
@ SEL_INVERT
@ SEL_DESELECT
@ SEL_TOGGLE
@ SEL_OP_ADD
@ SEL_OP_SUB
@ SEL_OP_SET
@ SEL_OP_AND
@ SEL_OP_XOR
ViewContext ED_view3d_viewcontext_init(bContext *C, Depsgraph *depsgraph)
void view3d_operator_needs_opengl(const bContext *C)
Read Guarded memory(de)allocation.
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Brightness Control the brightness and contrast of the input color Vector Map input vector components with curves Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert Invert a color
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
#define C
Definition RandGen.cpp:29
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DATA
Definition WM_types.hh:475
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define ND_BONE_SELECT
Definition WM_types.hh:427
#define NC_OBJECT
Definition WM_types.hh:346
Bone * ED_armature_pick_bone_from_selectbuffer(blender::Span< Base * > bases, const GPUSelectResult *hit_results, int hits, bool findunsel, bool do_nearest, Base **r_base)
Bone * ED_armature_pick_bone(bContext *C, const int xy[2], bool findunsel, Base **r_base)
BPy_StructRNA * depsgraph
bool contains(const Key &key) const
Definition BLI_set.hh:291
bool add(const Key &key)
Definition BLI_set.hh:248
IndexRange index_range() const
#define printf
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
KeyingSet * ANIM_scene_get_active_keyingset(const Scene *scene)
blender::animrig::ModifyKeyReturn ANIM_validate_keyingset(bContext *C, blender::Vector< PointerRNA > *sources, KeyingSet *keyingset)
ccl_device_inline float4 select(const int4 mask, const float4 a, const float4 b)
static ulong * next
const BoneColor & ANIM_bonecolor_posebone_get(const bPoseChannel *pose_bone)
Definition bonecolor.cc:88
void base_select(Base *base, eObjectSelect_Mode mode)
void vgroup_select_by_name(Object *ob, const char *name)
static bool pose_select_same_collection(bContext *C, const bool extend)
void POSE_OT_select_parent(wmOperatorType *ot)
void ED_armature_pose_select_in_wpaint_mode(const Scene *scene, ViewLayer *view_layer, Base *base_select)
#define PBONE_PREV_FLAG_SET(pchan, val)
static int pose_select_hierarchy_exec(bContext *C, wmOperator *op)
bool ED_armature_pose_select_pick_with_buffer(const Scene *scene, ViewLayer *view_layer, View3D *v3d, Base *base, const GPUSelectResult *hit_results, const int hits, const SelectPick_Params *params, bool do_nearest)
bool ED_armature_pose_select_pick_bone(const Scene *scene, ViewLayer *view_layer, View3D *v3d, Object *ob, Bone *bone, const SelectPick_Params *params)
static bool ed_pose_is_any_selected_multi(const Span< Base * > bases, bool ignore_visibility)
void POSE_OT_select_hierarchy(wmOperatorType *ot)
void POSE_OT_select_grouped(wmOperatorType *ot)
void POSE_OT_select_linked_pick(wmOperatorType *ot)
static void pose_do_bone_select(bPoseChannel *pchan, const int select_mode)
static int pose_select_linked_exec(bContext *C, wmOperator *)
void ED_pose_bone_select_tag_update(Object *ob)
bool ED_pose_deselect_all(Object *ob, int select_mode, const bool ignore_visibility)
static int pose_select_connected_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void POSE_OT_select_mirror(wmOperatorType *ot)
bool ED_pose_deselect_all_multi_ex(const Span< Base * > bases, int select_mode, const bool ignore_visibility)
static bool ed_pose_is_any_selected(Object *ob, bool ignore_visibility)
bool ED_pose_deselect_all_multi(bContext *C, int select_mode, const bool ignore_visibility)
static int pose_select_mirror_exec(bContext *C, wmOperator *op)
void POSE_OT_select_all(wmOperatorType *ot)
void POSE_OT_select_constraint_target(wmOperatorType *ot)
static bool pose_select_linked_pick_poll(bContext *C)
ePose_SelectSame_Mode
@ POSE_SEL_SAME_COLOR
@ POSE_SEL_SAME_COLLECTION
@ POSE_SEL_SAME_KEYINGSET
static int pose_de_select_all_exec(bContext *C, wmOperator *op)
static int pose_select_parent_exec(bContext *C, wmOperator *)
static bool pose_select_same_keyingset(bContext *C, ReportList *reports, bool extend)
void ED_pose_bone_select(Object *ob, bPoseChannel *pchan, bool select, bool change_active)
static int pose_select_constraint_target_exec(bContext *C, wmOperator *)
void POSE_OT_select_linked(wmOperatorType *ot)
#define PBONE_PREV_FLAG_GET(pchan)
static bool pose_select_same_color(bContext *C, const bool extend)
static void selectconnected_posebonechildren(Object *ob, Bone *bone, int extend)
static int pose_select_grouped_exec(bContext *C, wmOperator *op)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
short flag
struct Object * object
Bone_Runtime runtime
struct Bone * parent
char name[64]
ListBase childbase
ListBase paths
void * first
struct ModifierData * next
struct bPose * pose
struct ToolSettings * toolsettings
Scene * scene
Definition ED_view3d.hh:69
ViewLayer * view_layer
Definition ED_view3d.hh:70
View3D * v3d
Definition ED_view3d.hh:74
struct Bone * bone
struct bPoseChannel * parent
ListBase chanbase
int mval[2]
Definition WM_types.hh:728
struct ReportList * reports
struct PointerRNA * ptr
void WM_main_add_notifier(uint type, void *reference)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4125
void WM_operator_properties_select_all(wmOperatorType *ot)
int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)
uint8_t flag
Definition wm_window.cc:138