Blender V5.0
armature_edit.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
9
10#include "DNA_armature_types.h"
11#include "DNA_object_types.h"
12#include "DNA_scene_types.h"
13
14#include "MEM_guardedalloc.h"
15
16#include "BLT_translation.hh"
17
18#include "BLI_ghash.h"
19#include "BLI_listbase.h"
20#include "BLI_math_matrix.h"
21#include "BLI_math_rotation.h"
22#include "BLI_math_vector.h"
23
24#include "BKE_action.hh"
25#include "BKE_armature.hh"
26#include "BKE_context.hh"
27#include "BKE_global.hh"
28#include "BKE_layer.hh"
29#include "BKE_main.hh"
30#include "BKE_object.hh"
31#include "BKE_object_types.hh"
32#include "BKE_report.hh"
33
34#include "RNA_access.hh"
35#include "RNA_define.hh"
36#include "RNA_prototypes.hh"
37
38#include "UI_interface_icons.hh"
39
40#include "WM_api.hh"
41#include "WM_types.hh"
42
43#include "ED_armature.hh"
44#include "ED_object.hh"
45#include "ED_outliner.hh"
46#include "ED_screen.hh"
47#include "ED_view3d.hh"
48
49#include "ANIM_armature.hh"
50
51#include "DEG_depsgraph.hh"
52
53#include "armature_intern.hh"
54
55using blender::Vector;
56
57/* -------------------------------------------------------------------- */
60
62{
63 bArmature *armature = static_cast<bArmature *>(
64 CTX_data_pointer_get_type(C, "armature", &RNA_Armature).data);
65
66 if (armature == nullptr) {
68 if (object && object->type == OB_ARMATURE) {
69 armature = static_cast<bArmature *>(object->data);
70 }
71 }
72
73 return armature;
74}
75
76/* NOTE: these functions are exported to the Object module to be called from the tools there */
77
78void ED_armature_edit_transform(bArmature *arm, const float mat[4][4], const bool do_props)
79{
80 float scale = mat4_to_scale(mat); /* store the scale of the matrix here to use on envelopes */
81 float mat3[3][3];
82
83 copy_m3_m4(mat3, mat);
84 normalize_m3(mat3);
85 /* Do the rotations */
86 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
87 float tmat[3][3];
88
89 /* find the current bone's roll matrix */
90 ED_armature_ebone_to_mat3(ebone, tmat);
91
92 /* transform the roll matrix */
93 mul_m3_m3m3(tmat, mat3, tmat);
94
95 /* transform the bone */
96 mul_m4_v3(mat, ebone->head);
97 mul_m4_v3(mat, ebone->tail);
98
99 /* apply the transformed roll back */
100 mat3_to_vec_roll(tmat, nullptr, &ebone->roll);
101
102 if (do_props) {
103 ebone->rad_head *= scale;
104 ebone->rad_tail *= scale;
105 ebone->dist *= scale;
106
107 /* we could be smarter and scale by the matrix along the x & z axis */
108 ebone->xwidth *= scale;
109 ebone->zwidth *= scale;
110 }
111 }
112}
113
114void ED_armature_transform(bArmature *arm, const float mat[4][4], const bool do_props)
115{
116 if (arm->edbo) {
117 ED_armature_edit_transform(arm, mat, do_props);
118 }
119 else {
120 BKE_armature_transform(arm, mat, do_props);
121 }
122}
123
125 Main *bmain, Object *ob, const float cursor[3], int centermode, int around)
126{
127 const bool is_editmode = BKE_object_is_in_editmode(ob);
128 bArmature *arm = static_cast<bArmature *>(ob->data);
129 float cent[3];
130
131 /* Put the armature into edit-mode. */
132 if (is_editmode == false) {
134 }
135
136 /* Find the center-point. */
137 if (centermode == 2) {
138 copy_v3_v3(cent, cursor);
139 invert_m4_m4(ob->runtime->world_to_object.ptr(), ob->object_to_world().ptr());
140 mul_m4_v3(ob->world_to_object().ptr(), cent);
141 }
142 else {
143 if (around == V3D_AROUND_CENTER_BOUNDS) {
144 float min[3], max[3];
146 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
147 minmax_v3v3_v3(min, max, ebone->head);
148 minmax_v3v3_v3(min, max, ebone->tail);
149 }
150 mid_v3_v3v3(cent, min, max);
151 }
152 else { /* #V3D_AROUND_CENTER_MEDIAN. */
153 int total = 0;
154 zero_v3(cent);
155 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
156 total += 2;
157 add_v3_v3(cent, ebone->head);
158 add_v3_v3(cent, ebone->tail);
159 }
160 if (total) {
161 mul_v3_fl(cent, 1.0f / float(total));
162 }
163 }
164 }
165
166 /* Do the adjustments */
167 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
168 sub_v3_v3(ebone->head, cent);
169 sub_v3_v3(ebone->tail, cent);
170 }
171
172 /* Turn the list into an armature */
173 if (is_editmode == false) {
174 ED_armature_from_edit(bmain, arm);
176 }
177
178 /* Adjust object location for new center-point. */
179 if (centermode && (is_editmode == false)) {
180 mul_mat3_m4_v3(ob->object_to_world().ptr(), cent); /* omit translation part */
181 add_v3_v3(ob->loc, cent);
182 }
183}
184
186
187/* -------------------------------------------------------------------- */
190
192 const float align_axis[3],
193 const bool axis_only)
194{
195 float mat[3][3], nor[3];
196 float vec[3], align_axis_proj[3], roll = 0.0f;
197
198 BLI_ASSERT_UNIT_V3(align_axis);
199
200 sub_v3_v3v3(nor, bone->tail, bone->head);
201
202 /* If tail == head or the bone is aligned with the axis... */
203 if (normalize_v3(nor) <= FLT_EPSILON ||
204 (fabsf(dot_v3v3(align_axis, nor)) >= (1.0f - FLT_EPSILON)))
205 {
206 return roll;
207 }
208
210
211 /* project the new_up_axis along the normal */
212 project_v3_v3v3_normalized(vec, align_axis, nor);
213 sub_v3_v3v3(align_axis_proj, align_axis, vec);
214
215 if (axis_only) {
216 if (angle_v3v3(align_axis_proj, mat[2]) > float(M_PI_2)) {
217 negate_v3(align_axis_proj);
218 }
219 }
220
221 roll = angle_v3v3(align_axis_proj, mat[2]);
222
223 cross_v3_v3v3(vec, mat[2], align_axis_proj);
224
225 if (dot_v3v3(vec, nor) < 0.0f) {
226 return -roll;
227 }
228 return roll;
229}
230
231/* NOTE: ranges arithmetic is used below. */
254
256 RNA_ENUM_ITEM_HEADING(N_("Positive"), nullptr),
257 {CALC_ROLL_TAN_POS_X, "POS_X", 0, "Local +X Tangent", ""},
258 {CALC_ROLL_TAN_POS_Z, "POS_Z", 0, "Local +Z Tangent", ""},
259
260 {CALC_ROLL_POS_X, "GLOBAL_POS_X", 0, "Global +X Axis", ""},
261 {CALC_ROLL_POS_Y, "GLOBAL_POS_Y", 0, "Global +Y Axis", ""},
262 {CALC_ROLL_POS_Z, "GLOBAL_POS_Z", 0, "Global +Z Axis", ""},
263
264 RNA_ENUM_ITEM_HEADING(N_("Negative"), nullptr),
265 {CALC_ROLL_TAN_NEG_X, "NEG_X", 0, "Local -X Tangent", ""},
266 {CALC_ROLL_TAN_NEG_Z, "NEG_Z", 0, "Local -Z Tangent", ""},
267
268 {CALC_ROLL_NEG_X, "GLOBAL_NEG_X", 0, "Global -X Axis", ""},
269 {CALC_ROLL_NEG_Y, "GLOBAL_NEG_Y", 0, "Global -Y Axis", ""},
270 {CALC_ROLL_NEG_Z, "GLOBAL_NEG_Z", 0, "Global -Z Axis", ""},
271
272 RNA_ENUM_ITEM_HEADING(N_("Other"), nullptr),
273 {CALC_ROLL_ACTIVE, "ACTIVE", 0, "Active Bone", ""},
274 {CALC_ROLL_VIEW, "VIEW", 0, "View Axis", ""},
275 {CALC_ROLL_CURSOR, "CURSOR", 0, "Cursor", ""},
276 {0, nullptr, 0, nullptr, nullptr},
277};
278
280{
281 const Scene *scene = CTX_data_scene(C);
282 ViewLayer *view_layer = CTX_data_view_layer(C);
283 Object *ob_active = CTX_data_edit_object(C);
284
285 eCalcRollTypes type = eCalcRollTypes(RNA_enum_get(op->ptr, "type"));
286 const bool axis_only = RNA_boolean_get(op->ptr, "axis_only");
287 /* axis_flip when matching the active bone never makes sense */
288 bool axis_flip = ((type >= CALC_ROLL_ACTIVE) ? RNA_boolean_get(op->ptr, "axis_flip") :
289 (type >= CALC_ROLL_TAN_NEG_X) ? true :
290 false);
291
293 scene, view_layer, CTX_wm_view3d(C));
294 for (Object *ob : objects) {
295 bArmature *arm = static_cast<bArmature *>(ob->data);
296 bool changed = false;
297
298 float imat[3][3];
299 EditBone *ebone;
300
301 if ((type >= CALC_ROLL_NEG_X) && (type <= CALC_ROLL_TAN_NEG_Z)) {
302 type = eCalcRollTypes(int(type) - (CALC_ROLL_ACTIVE - CALC_ROLL_NEG_X));
303 axis_flip = true;
304 }
305
306 copy_m3_m4(imat, ob->object_to_world().ptr());
307 invert_m3(imat);
308
309 if (type == CALC_ROLL_CURSOR) { /* Cursor */
310 float cursor_local[3];
311 const View3DCursor *cursor = &scene->cursor;
312
313 invert_m4_m4(ob->runtime->world_to_object.ptr(), ob->object_to_world().ptr());
314 copy_v3_v3(cursor_local, cursor->location);
315 mul_m4_v3(ob->world_to_object().ptr(), cursor_local);
316
317 /* cursor */
318 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
319 if (blender::animrig::bone_is_visible(arm, ebone) && EBONE_EDITABLE(ebone)) {
320 float cursor_rel[3];
321 sub_v3_v3v3(cursor_rel, cursor_local, ebone->head);
322 if (axis_flip) {
323 negate_v3(cursor_rel);
324 }
325 if (normalize_v3(cursor_rel) != 0.0f) {
326 ebone->roll = ED_armature_ebone_roll_to_vector(ebone, cursor_rel, axis_only);
327 changed = true;
328 }
329 }
330 }
331 }
333 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
334 if (ebone->parent) {
335 bool is_edit = (blender::animrig::bone_is_visible(arm, ebone) && EBONE_EDITABLE(ebone));
336 bool is_edit_parent = (blender::animrig::bone_is_visible(arm, ebone->parent) &&
337 EBONE_EDITABLE(ebone->parent));
338
339 if (is_edit || is_edit_parent) {
340 EditBone *ebone_other = ebone->parent;
341 float dir_a[3];
342 float dir_b[3];
343 float vec[3];
344 bool is_vec_zero;
345
346 sub_v3_v3v3(dir_a, ebone->tail, ebone->head);
347 normalize_v3(dir_a);
348
349 /* find the first bone in the chain with a different direction */
350 do {
351 sub_v3_v3v3(dir_b, ebone_other->head, ebone_other->tail);
352 normalize_v3(dir_b);
353
354 if (type == CALC_ROLL_TAN_POS_Z) {
355 cross_v3_v3v3(vec, dir_a, dir_b);
356 }
357 else {
358 add_v3_v3v3(vec, dir_a, dir_b);
359 }
360 } while ((is_vec_zero = (normalize_v3(vec) < 0.00001f)) &&
361 (ebone_other = ebone_other->parent));
362
363 if (!is_vec_zero) {
364 if (axis_flip) {
365 negate_v3(vec);
366 }
367
368 if (is_edit) {
369 ebone->roll = ED_armature_ebone_roll_to_vector(ebone, vec, axis_only);
370 changed = true;
371 }
372
373 /* parentless bones use cross product with child */
374 if (is_edit_parent) {
375 if (ebone->parent->parent == nullptr) {
377 ebone->parent, vec, axis_only);
378 changed = true;
379 }
380 }
381 }
382 }
383 }
384 }
385 }
386 else {
387 float vec[3] = {0.0f, 0.0f, 0.0f};
388 if (type == CALC_ROLL_VIEW) { /* View */
390 if (rv3d == nullptr) {
391 BKE_report(op->reports, RPT_ERROR, "No region view3d available");
392 return OPERATOR_CANCELLED;
393 }
394
395 copy_v3_v3(vec, rv3d->viewinv[2]);
396 mul_m3_v3(imat, vec);
397 }
398 else if (type == CALC_ROLL_ACTIVE) {
399 float mat[3][3];
400 bArmature *arm_active = static_cast<bArmature *>(ob_active->data);
401 ebone = (EditBone *)arm_active->act_edbone;
402 if (ebone == nullptr) {
403 BKE_report(op->reports, RPT_ERROR, "No active bone set");
404 return OPERATOR_CANCELLED;
405 }
406
407 ED_armature_ebone_to_mat3(ebone, mat);
408 copy_v3_v3(vec, mat[2]);
409 }
410 else if (type < 6) { /* NOTE: always true, check to quiet GCC12.2 `-Warray-bounds`. */
411 /* Axis */
412 if (type < 3) {
413 vec[type] = 1.0f;
414 }
415 else {
416 vec[type - 2] = -1.0f;
417 }
418 mul_m3_v3(imat, vec);
419 normalize_v3(vec);
420 }
421 else {
422 /* The previous block should handle all remaining cases. */
424 }
425
426 if (axis_flip) {
427 negate_v3(vec);
428 }
429
430 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
431 if (blender::animrig::bone_is_visible(arm, ebone) && EBONE_EDITABLE(ebone)) {
432 /* roll func is a callback which assumes that all is well */
433 ebone->roll = ED_armature_ebone_roll_to_vector(ebone, vec, axis_only);
434 changed = true;
435 }
436 }
437 }
438
439 if (arm->flag & ARM_MIRROR_EDIT) {
440 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
441 if ((blender::animrig::bone_is_visible(arm, ebone) && EBONE_EDITABLE(ebone)) == 0) {
442 EditBone *ebone_mirr = ED_armature_ebone_get_mirrored(arm->edbo, ebone);
443 if (ebone_mirr &&
444 (blender::animrig::bone_is_visible(arm, ebone_mirr) && EBONE_EDITABLE(ebone_mirr)))
445 {
446 ebone->roll = -ebone_mirr->roll;
447 }
448 }
449 }
450 }
451
452 if (changed) {
453 /* NOTE: notifier might evolve. */
456 }
457 }
458
459 return OPERATOR_FINISHED;
460}
461
463{
464 /* identifiers */
465 ot->name = "Recalculate Roll";
466 ot->idname = "ARMATURE_OT_calculate_roll";
467 ot->description = "Automatically fix alignment of select bones' axes";
468
469 /* API callbacks. */
470 ot->invoke = WM_menu_invoke;
473
474 /* flags */
476
477 /* properties */
478 ot->prop = RNA_def_enum(ot->srna, "type", prop_calc_roll_types, CALC_ROLL_TAN_POS_X, "Type", "");
479 RNA_def_boolean(ot->srna, "axis_flip", false, "Flip Axis", "Negate the alignment axis");
480 RNA_def_boolean(ot->srna,
481 "axis_only",
482 false,
483 "Shortest Rotation",
484 "Ignore the axis direction, use the shortest rotation to align");
485}
486
488{
489 const Scene *scene = CTX_data_scene(C);
490 ViewLayer *view_layer = CTX_data_view_layer(C);
491 const float roll = RNA_float_get(op->ptr, "roll");
492
494 scene, view_layer, CTX_wm_view3d(C));
495 for (Object *ob : objects) {
496 bArmature *arm = static_cast<bArmature *>(ob->data);
497 bool changed = false;
498
499 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
500 if (blender::animrig::bone_is_visible(arm, ebone) && EBONE_EDITABLE(ebone)) {
501 /* Roll func is a callback which assumes that all is well. */
502 ebone->roll = roll;
503 changed = true;
504 }
505 }
506
507 if (arm->flag & ARM_MIRROR_EDIT) {
508 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
509 if ((blender::animrig::bone_is_visible(arm, ebone) && EBONE_EDITABLE(ebone)) == 0) {
510 EditBone *ebone_mirr = ED_armature_ebone_get_mirrored(arm->edbo, ebone);
511 if (ebone_mirr &&
512 (blender::animrig::bone_is_visible(arm, ebone_mirr) && EBONE_EDITABLE(ebone_mirr)))
513 {
514 ebone->roll = -ebone_mirr->roll;
515 changed = true;
516 }
517 }
518 }
519 }
520
521 if (changed) {
522 /* NOTE: notifier might evolve. */
525 }
526 }
527
528 return OPERATOR_FINISHED;
529}
530
532{
533 /* identifiers */
534 ot->name = "Clear Roll";
535 ot->idname = "ARMATURE_OT_roll_clear";
536 ot->description = "Clear roll for selected bones";
537
538 /* API callbacks. */
541
542 /* flags */
544
546 "roll",
547 0,
548 nullptr,
549 DEG2RADF(-360.0f),
550 DEG2RADF(360.0f),
551 "Roll",
552 "",
553 DEG2RADF(-360.0f),
554 DEG2RADF(360.0f));
555}
556
558
559/* -------------------------------------------------------------------- */
562
563/* temporary data-structure for merge/fill bones */
566
567 EditBone *head_owner; /* EditBone which uses this point as a 'head' point */
568 EditBone *tail_owner; /* EditBone which uses this point as a 'tail' point */
569
570 float vec[3]; /* the actual location of the point in local/EditMode space */
571};
572
573/* find chain-tips (i.e. bones without children) */
574static void chains_find_tips(ListBase *edbo, ListBase *list)
575{
576 EditBone *ebo;
577 LinkData *ld;
578
579 /* NOTE: this is potentially very slow ... there's got to be a better way. */
580 LISTBASE_FOREACH (EditBone *, curBone, edbo) {
581 short stop = 0;
582
583 /* is this bone contained within any existing chain? (skip if so) */
584 LISTBASE_FOREACH (LinkData *, ld, list) {
585 for (ebo = static_cast<EditBone *>(ld->data); ebo; ebo = ebo->parent) {
586 if (ebo == curBone) {
587 stop = 1;
588 break;
589 }
590 }
591
592 if (stop) {
593 break;
594 }
595 }
596 /* skip current bone if it is part of an existing chain */
597 if (stop) {
598 continue;
599 }
600
601 /* is any existing chain part of the chain formed by this bone? */
602 stop = 0;
603 for (ebo = curBone->parent; ebo; ebo = ebo->parent) {
604 LISTBASE_FOREACH (LinkData *, ld, list) {
605 if (ld->data == ebo) {
606 ld->data = curBone;
607 stop = 1;
608 break;
609 }
610 }
611
612 if (stop) {
613 break;
614 }
615 }
616 /* current bone has already been added to a chain? */
617 if (stop) {
618 continue;
619 }
620
621 /* add current bone to a new chain */
622 ld = MEM_callocN<LinkData>("BoneChain");
623 ld->data = curBone;
624 BLI_addtail(list, ld);
625 }
626}
627
629
630/* -------------------------------------------------------------------- */
633
634static void fill_add_joint(EditBone *ebo, short eb_tail, ListBase *points)
635{
636 EditBonePoint *ebp;
637 float vec[3];
638 short found = 0;
639
640 if (eb_tail) {
641 copy_v3_v3(vec, ebo->tail);
642 }
643 else {
644 copy_v3_v3(vec, ebo->head);
645 }
646
647 LISTBASE_FOREACH (EditBonePoint *, ebp, points) {
648 if (equals_v3v3(ebp->vec, vec)) {
649 if (eb_tail) {
650 if ((ebp->head_owner) && (ebp->head_owner->parent == ebo)) {
651 /* so this bone's tail owner is this bone */
652 ebp->tail_owner = ebo;
653 found = 1;
654 break;
655 }
656 }
657 else {
658 if ((ebp->tail_owner) && (ebo->parent == ebp->tail_owner)) {
659 /* so this bone's head owner is this bone */
660 ebp->head_owner = ebo;
661 found = 1;
662 break;
663 }
664 }
665 }
666 }
667
668 /* allocate a new point if no existing point was related */
669 if (found == 0) {
670 ebp = MEM_callocN<EditBonePoint>("EditBonePoint");
671
672 if (eb_tail) {
673 copy_v3_v3(ebp->vec, ebo->tail);
674 ebp->tail_owner = ebo;
675 }
676 else {
677 copy_v3_v3(ebp->vec, ebo->head);
678 ebp->head_owner = ebo;
679 }
680
681 BLI_addtail(points, ebp);
682 }
683}
684
685/* bone adding between selected joints */
687{
688 Scene *scene = CTX_data_scene(C);
689 View3D *v3d = CTX_wm_view3d(C);
690 ListBase points = {nullptr, nullptr};
691 EditBone *newbone = nullptr;
692 int count;
693 bool mixed_object_error = false;
694
695 /* loop over all bones, and only consider if visible */
696 bArmature *arm = nullptr;
697 CTX_DATA_BEGIN_WITH_ID (C, EditBone *, ebone, visible_bones, bArmature *, arm_iter) {
698 bool check = false;
699 if (!(ebone->flag & BONE_CONNECTED) && (ebone->flag & BONE_ROOTSEL)) {
700 fill_add_joint(ebone, 0, &points);
701 check = true;
702 }
703 if (ebone->flag & BONE_TIPSEL) {
704 fill_add_joint(ebone, 1, &points);
705 check = true;
706 }
707
708 if (check) {
709 if (arm && (arm != arm_iter)) {
710 mixed_object_error = true;
711 }
712 arm = arm_iter;
713 }
714 }
716
717 /* the number of joints determines how we fill:
718 * 1) between joint and cursor (joint=head, cursor=tail)
719 * 2) between the two joints (order is dependent on active-bone/hierarchy)
720 * 3+) error (a smarter method involving finding chains needs to be worked out
721 */
722 count = BLI_listbase_count(&points);
723
724 if (count == 0) {
725 BKE_report(op->reports, RPT_ERROR, "No joints selected");
726 return OPERATOR_CANCELLED;
727 }
728
729 if (mixed_object_error) {
730 BKE_report(op->reports, RPT_ERROR, "Bones for different objects selected");
731 BLI_freelistN(&points);
732 return OPERATOR_CANCELLED;
733 }
734
735 Object *obedit = nullptr;
736 {
737 ViewLayer *view_layer = CTX_data_view_layer(C);
738 FOREACH_OBJECT_IN_EDIT_MODE_BEGIN (scene, view_layer, v3d, ob_iter) {
739 if (ob_iter->data == arm) {
740 obedit = ob_iter;
741 }
742 }
744 }
745 BLI_assert(obedit != nullptr);
746
747 if (count == 1) {
748 EditBonePoint *ebp;
749 float curs[3];
750
751 /* Get Points - selected joint */
752 ebp = static_cast<EditBonePoint *>(points.first);
753
754 /* Get points - cursor (tail) */
755 invert_m4_m4(obedit->runtime->world_to_object.ptr(), obedit->object_to_world().ptr());
756 mul_v3_m4v3(curs, obedit->world_to_object().ptr(), scene->cursor.location);
757
758 /* Create a bone */
759 newbone = add_points_bone(obedit, ebp->vec, curs);
760 }
761 else if (count == 2) {
762 EditBonePoint *ebp_a, *ebp_b;
763 float head[3], tail[3];
764 short headtail = 0;
765
766 /* check that the points don't belong to the same bone */
767 ebp_a = static_cast<EditBonePoint *>(points.first);
768 ebp_b = ebp_a->next;
769
770 if (((ebp_a->head_owner == ebp_b->tail_owner) && (ebp_a->head_owner != nullptr)) ||
771 ((ebp_a->tail_owner == ebp_b->head_owner) && (ebp_a->tail_owner != nullptr)))
772 {
773 BKE_report(op->reports, RPT_ERROR, "Same bone selected...");
774 BLI_freelistN(&points);
775 return OPERATOR_CANCELLED;
776 }
777
778 /* find which one should be the 'head' */
779 if ((ebp_a->head_owner && ebp_b->head_owner) || (ebp_a->tail_owner && ebp_b->tail_owner)) {
780 /* use active, nice predictable */
781 if (arm->act_edbone && ELEM(arm->act_edbone, ebp_a->head_owner, ebp_a->tail_owner)) {
782 headtail = 1;
783 }
784 else if (arm->act_edbone && ELEM(arm->act_edbone, ebp_b->head_owner, ebp_b->tail_owner)) {
785 headtail = 2;
786 }
787 else {
788 /* rule: whichever one is closer to 3d-cursor */
789 float curs[3];
790 float dist_sq_a, dist_sq_b;
791
792 /* get cursor location */
793 invert_m4_m4(obedit->runtime->world_to_object.ptr(), obedit->object_to_world().ptr());
794 mul_v3_m4v3(curs, obedit->world_to_object().ptr(), scene->cursor.location);
795
796 /* get distances */
797 dist_sq_a = len_squared_v3v3(ebp_a->vec, curs);
798 dist_sq_b = len_squared_v3v3(ebp_b->vec, curs);
799
800 /* compare distances - closer one therefore acts as direction for bone to go */
801 headtail = (dist_sq_a < dist_sq_b) ? 2 : 1;
802 }
803 }
804 else if (ebp_a->head_owner) {
805 headtail = 1;
806 }
807 else if (ebp_b->head_owner) {
808 headtail = 2;
809 }
810
811 /* assign head/tail combinations */
812 if (headtail == 2) {
813 copy_v3_v3(head, ebp_a->vec);
814 copy_v3_v3(tail, ebp_b->vec);
815 }
816 else if (headtail == 1) {
817 copy_v3_v3(head, ebp_b->vec);
818 copy_v3_v3(tail, ebp_a->vec);
819 }
820
821 /* add new bone and parent it to the appropriate end */
822 if (headtail) {
823 newbone = add_points_bone(obedit, head, tail);
824
825 /* do parenting (will need to set connected flag too) */
826 if (headtail == 2) {
827 /* ebp tail or head - tail gets priority */
828 if (ebp_a->tail_owner) {
829 newbone->parent = ebp_a->tail_owner;
830 }
831 else {
832 newbone->parent = ebp_a->head_owner;
833 }
834 }
835 else {
836 /* ebp_b tail or head - tail gets priority */
837 if (ebp_b->tail_owner) {
838 newbone->parent = ebp_b->tail_owner;
839 }
840 else {
841 newbone->parent = ebp_b->head_owner;
842 }
843 }
844
845 /* don't set for bone connecting two head points of bones */
846 if (ebp_a->tail_owner || ebp_b->tail_owner) {
847 newbone->flag |= BONE_CONNECTED;
848 }
849 }
850 }
851 else {
852 BKE_reportf(op->reports, RPT_ERROR, "Too many points selected: %d", count);
853 BLI_freelistN(&points);
854 return OPERATOR_CANCELLED;
855 }
856
857 if (newbone) {
859 arm->act_edbone = newbone;
860 newbone->flag |= BONE_TIPSEL;
861 }
862
863 /* updates */
866
867 /* free points */
868 BLI_freelistN(&points);
869
870 return OPERATOR_FINISHED;
871}
872
874{
875 /* identifiers */
876 ot->name = "Fill Between Joints";
877 ot->idname = "ARMATURE_OT_fill";
878 ot->description = "Add bone between selected joint(s) and/or 3D cursor";
879
880 /* callbacks */
883
884 /* flags */
886}
887
889
890/* -------------------------------------------------------------------- */
897
898/* helper to clear BONE_TRANSFORM flags */
900{
901 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
902 ebone->flag &= ~BONE_TRANSFORM;
903 }
904}
905
907{
908 const Scene *scene = CTX_data_scene(C);
909 ViewLayer *view_layer = CTX_data_view_layer(C);
911 scene, view_layer, CTX_wm_view3d(C));
912
913 for (Object *ob : objects) {
914 bArmature *arm = static_cast<bArmature *>(ob->data);
915
916 ListBase chains = {nullptr, nullptr};
917
918 /* get chains of bones (ends on chains) */
919 chains_find_tips(arm->edbo, &chains);
920 if (BLI_listbase_is_empty(&chains)) {
921 continue;
922 }
923
924 /* ensure that mirror bones will also be operated on */
926
927 /* Clear BONE_TRANSFORM flags
928 * - Used to prevent duplicate/canceling operations from occurring #34123.
929 * - #BONE_DONE cannot be used here as that's already used for mirroring.
930 */
932
933 /* loop over chains, only considering selected and visible bones */
934 LISTBASE_FOREACH (LinkData *, chain, &chains) {
935 EditBone *ebo, *child = nullptr, *parent = nullptr;
936
937 /* loop over bones in chain */
938 for (ebo = static_cast<EditBone *>(chain->data); ebo; ebo = parent) {
939 /* parent is this bone's original parent
940 * - we store this, as the next bone that is checked is this one
941 * but the value of ebo->parent may change here...
942 */
943 parent = ebo->parent;
944
945 /* skip bone if already handled, see #34123. */
946 if ((ebo->flag & BONE_TRANSFORM) == 0) {
947 /* only if selected and editable */
949 /* swap head and tail coordinates */
950 swap_v3_v3(ebo->head, ebo->tail);
951
952 /* do parent swapping:
953 * - use 'child' as new parent
954 * - connected flag is only set if points are coincidental
955 */
956 ebo->parent = child;
957 if ((child) && equals_v3v3(ebo->head, child->tail)) {
958 ebo->flag |= BONE_CONNECTED;
959 }
960 else {
961 ebo->flag &= ~BONE_CONNECTED;
962 }
963
964 /* get next bones
965 * - child will become the new parent of next bone
966 */
967 child = ebo;
968 }
969 else {
970 /* not swapping this bone, however, if its 'parent' got swapped, unparent us from it
971 * as it will be facing in opposite direction
972 */
973 if ((parent) &&
974 (blender::animrig::bone_is_visible(arm, parent) && EBONE_EDITABLE(parent)))
975 {
976 ebo->parent = nullptr;
977 ebo->flag &= ~BONE_CONNECTED;
978 }
979
980 /* get next bones
981 * - child will become new parent of next bone (not swapping occurred,
982 * so set to nullptr to prevent infinite-loop)
983 */
984 child = nullptr;
985 }
986
987 /* tag as done (to prevent double-swaps) */
988 ebo->flag |= BONE_TRANSFORM;
989 }
990 }
991 }
992
993 /* free chains */
994 BLI_freelistN(&chains);
995
996 /* clear temp flags */
999
1000 /* NOTE: notifier might evolve. */
1003 }
1004
1005 return OPERATOR_FINISHED;
1006}
1007
1009{
1010 /* identifiers */
1011 ot->name = "Switch Direction";
1012 ot->idname = "ARMATURE_OT_switch_direction";
1013 ot->description = "Change the direction that a chain of bones points in (head and tail swap)";
1014
1015 /* API callbacks. */
1018
1019 /* flags */
1020 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1021}
1022
1024
1025/* -------------------------------------------------------------------- */
1028
1029/* Helper to fix a ebone position if its parent has moved due to alignment. */
1030static void fix_connected_bone(EditBone *ebone)
1031{
1032 float diff[3];
1033
1034 if (!(ebone->parent) || !(ebone->flag & BONE_CONNECTED) ||
1035 equals_v3v3(ebone->parent->tail, ebone->head))
1036 {
1037 return;
1038 }
1039
1040 /* if the parent has moved we translate child's head and tail accordingly */
1041 sub_v3_v3v3(diff, ebone->parent->tail, ebone->head);
1042 add_v3_v3(ebone->head, diff);
1043 add_v3_v3(ebone->tail, diff);
1044}
1045
1046/* helper to recursively find chains of connected bones starting at ebone and fix their position */
1048{
1049 LISTBASE_FOREACH (EditBone *, selbone, edbo) {
1050 if ((selbone->parent) && (selbone->parent == ebone) && (selbone->flag & BONE_CONNECTED)) {
1051 fix_connected_bone(selbone);
1052 fix_editbone_connected_children(edbo, selbone);
1053 }
1054 }
1055}
1056
1057static void bone_align_to_bone(ListBase *edbo, EditBone *selbone, EditBone *actbone)
1058{
1059 float selboneaxis[3], actboneaxis[3], length;
1060
1061 sub_v3_v3v3(actboneaxis, actbone->tail, actbone->head);
1062 normalize_v3(actboneaxis);
1063
1064 sub_v3_v3v3(selboneaxis, selbone->tail, selbone->head);
1065 length = len_v3(selboneaxis);
1066
1067 mul_v3_fl(actboneaxis, length);
1068 add_v3_v3v3(selbone->tail, selbone->head, actboneaxis);
1069 selbone->roll = actbone->roll;
1070
1071 /* If the bone being aligned has connected descendants they must be moved
1072 * according to their parent new position, otherwise they would be left
1073 * in an inconsistent state: connected but away from the parent. */
1074 fix_editbone_connected_children(edbo, selbone);
1075}
1076
1078{
1080 bArmature *arm = static_cast<bArmature *>(ob->data);
1081 EditBone *actbone = CTX_data_active_bone(C);
1082 EditBone *actmirb = nullptr;
1083 int num_selected_bones;
1084
1085 /* there must be an active bone */
1086 if (actbone == nullptr) {
1087 BKE_report(op->reports, RPT_ERROR, "Operation requires an active bone");
1088 return OPERATOR_CANCELLED;
1089 }
1090
1091 if (arm->flag & ARM_MIRROR_EDIT) {
1092 /* For X-Axis Mirror Editing option, we may need a mirror copy of actbone
1093 * - if there's a mirrored copy of selbone, try to find a mirrored copy of actbone
1094 * (i.e. selbone="child.L" and actbone="parent.L", find "child.R" and "parent.R").
1095 * This is useful for arm-chains, for example parenting lower arm to upper arm
1096 * - if there's no mirrored copy of actbone (i.e. actbone = "parent.C" or "parent")
1097 * then just use actbone. Useful when doing upper arm to spine.
1098 */
1099 actmirb = ED_armature_ebone_get_mirrored(arm->edbo, actbone);
1100 if (actmirb == nullptr) {
1101 actmirb = actbone;
1102 }
1103 }
1104
1105 /* if there is only 1 selected bone, we assume that it is the active bone,
1106 * since a user will need to have clicked on a bone (thus selecting it) to make it active
1107 */
1108 num_selected_bones = CTX_DATA_COUNT(C, selected_editable_bones);
1109 if (num_selected_bones <= 1) {
1110 /* When only the active bone is selected, and it has a parent,
1111 * align it to the parent, as that is the only possible outcome.
1112 */
1113 if (actbone->parent) {
1114 bone_align_to_bone(arm->edbo, actbone, actbone->parent);
1115
1116 if ((arm->flag & ARM_MIRROR_EDIT) && (actmirb->parent)) {
1117 bone_align_to_bone(arm->edbo, actmirb, actmirb->parent);
1118 }
1119
1120 BKE_reportf(op->reports, RPT_INFO, "Aligned bone '%s' to parent", actbone->name);
1121 }
1122 }
1123 else {
1124 /* Align 'selected' bones to the active one
1125 * - the context iterator contains both selected bones and their mirrored copies,
1126 * so we assume that unselected bones are mirrored copies of some selected bone
1127 * - since the active one (and/or its mirror) will also be selected, we also need
1128 * to check that we are not trying to operate on them, since such an operation
1129 * would cause errors
1130 */
1131
1132 /* align selected bones to the active one */
1133 CTX_DATA_BEGIN (C, EditBone *, ebone, selected_editable_bones) {
1134 if (ELEM(ebone, actbone, actmirb) == 0) {
1135 if (ebone->flag & BONE_SELECTED) {
1136 bone_align_to_bone(arm->edbo, ebone, actbone);
1137 }
1138 else if ((arm->flag & ARM_MIRROR_EDIT)) {
1139 /* Need to check for the mirror mode, because when editing multiple armatures with
1140 * differing mirror settings `actmirb` can be a nullptr. See #146242. */
1141 bone_align_to_bone(arm->edbo, ebone, actmirb);
1142 }
1143 }
1144 }
1146
1148 op->reports, RPT_INFO, "%d bones aligned to bone '%s'", num_selected_bones, actbone->name);
1149 }
1150
1151 /* NOTE: notifier might evolve. */
1154
1155 return OPERATOR_FINISHED;
1156}
1157
1159{
1160 /* identifiers */
1161 ot->name = "Align Bones";
1162 ot->idname = "ARMATURE_OT_align";
1163 ot->description = "Align selected bones to the active bone (or to their parent)";
1164
1165 /* API callbacks. */
1168
1169 /* flags */
1170 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1171}
1172
1174
1175/* -------------------------------------------------------------------- */
1178
1180{
1181 const Scene *scene = CTX_data_scene(C);
1182 ViewLayer *view_layer = CTX_data_view_layer(C);
1183
1185 scene, view_layer, CTX_wm_view3d(C));
1186 for (Object *ob : objects) {
1187 bArmature *arm = static_cast<bArmature *>(ob->data);
1188
1189 LISTBASE_FOREACH (EditBone *, bone, arm->edbo) {
1190 if (bone->parent && (bone->flag & BONE_SELECTED) != (bone->parent->flag & BONE_SELECTED)) {
1191 bone->parent = nullptr;
1192 bone->flag &= ~BONE_CONNECTED;
1193 }
1194 }
1195 LISTBASE_FOREACH (EditBone *, bone, arm->edbo) {
1196 ED_armature_ebone_select_set(bone, (bone->flag & BONE_SELECTED) != 0);
1197 }
1198
1201 }
1202
1203 return OPERATOR_FINISHED;
1204}
1205
1207{
1208 /* identifiers */
1209 ot->name = "Split";
1210 ot->idname = "ARMATURE_OT_split";
1211 ot->description = "Split off selected bones from connected unselected bones";
1212
1213 /* API callbacks. */
1214 ot->exec = armature_split_exec;
1216
1217 /* flags */
1218 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1219}
1220
1222
1223/* -------------------------------------------------------------------- */
1226
1227static bool armature_delete_ebone_cb(const char *bone_name, void *arm_p)
1228{
1229 bArmature *arm = static_cast<bArmature *>(arm_p);
1230 EditBone *ebone;
1231
1232 ebone = ED_armature_ebone_find_name(arm->edbo, bone_name);
1233 return (ebone && blender::animrig::bone_is_selected(arm, ebone));
1234}
1235
1236/* previously delete_armature */
1237/* only editmode! */
1239{
1240 EditBone *curBone, *ebone_next;
1241 bool changed_multi = false;
1242
1243 /* cancel if nothing selected */
1244 if (CTX_DATA_COUNT(C, selected_bones) == 0) {
1245 return OPERATOR_CANCELLED;
1246 }
1247
1248 const Scene *scene = CTX_data_scene(C);
1249 ViewLayer *view_layer = CTX_data_view_layer(C);
1251 scene, view_layer, CTX_wm_view3d(C));
1252 for (Object *obedit : objects) {
1253 bArmature *arm = static_cast<bArmature *>(obedit->data);
1254 bool changed = false;
1255
1257
1259
1260 for (curBone = static_cast<EditBone *>(arm->edbo->first); curBone; curBone = ebone_next) {
1261 ebone_next = curBone->next;
1262 if (blender::animrig::bone_is_selected(arm, curBone)) {
1263 if (curBone == arm->act_edbone) {
1264 arm->act_edbone = nullptr;
1265 }
1266 ED_armature_ebone_remove(arm, curBone);
1267 changed = true;
1268 }
1269 }
1270
1271 if (changed) {
1272 changed_multi = true;
1273
1275 BKE_pose_tag_recalc(CTX_data_main(C), obedit->pose);
1279 }
1280 }
1281
1282 if (!changed_multi) {
1283 return OPERATOR_CANCELLED;
1284 }
1285
1286 return OPERATOR_FINISHED;
1287}
1288
1290 wmOperator *op,
1291 const wmEvent * /*event*/)
1292{
1293 if (RNA_boolean_get(op->ptr, "confirm")) {
1295 op,
1296 IFACE_("Delete selected bones?"),
1297 nullptr,
1298 IFACE_("Delete"),
1300 false);
1301 }
1302 return armature_delete_selected_exec(C, op);
1303}
1304
1306{
1307 /* identifiers */
1308 ot->name = "Delete Selected Bone(s)";
1309 ot->idname = "ARMATURE_OT_delete";
1310 ot->description = "Remove selected bones from the armature";
1311
1312 /* API callbacks. */
1316
1317 /* flags */
1318 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1320}
1321
1322static bool armature_dissolve_ebone_cb(const char *bone_name, void *arm_p)
1323{
1324 bArmature *arm = static_cast<bArmature *>(arm_p);
1325 EditBone *ebone;
1326
1327 ebone = ED_armature_ebone_find_name(arm->edbo, bone_name);
1328 return (ebone && (ebone->flag & BONE_DONE));
1329}
1330
1332{
1333 const Scene *scene = CTX_data_scene(C);
1334 ViewLayer *view_layer = CTX_data_view_layer(C);
1335 EditBone *ebone, *ebone_next;
1336 bool changed_multi = false;
1337
1339 scene, view_layer, CTX_wm_view3d(C));
1340 for (Object *obedit : objects) {
1341 bArmature *arm = static_cast<bArmature *>(obedit->data);
1342 bool changed = false;
1343
1344 /* store for mirror */
1345 GHash *ebone_flag_orig = nullptr;
1346 int ebone_num = 0;
1347
1348 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1349 ebone->temp.p = nullptr;
1350 ebone->flag &= ~BONE_DONE;
1351 ebone_num++;
1352 }
1353
1354 if (arm->flag & ARM_MIRROR_EDIT) {
1355 GHashIterator gh_iter;
1356
1357 ebone_flag_orig = BLI_ghash_ptr_new_ex(__func__, ebone_num);
1358 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1359 union {
1360 int flag;
1361 void *p;
1362 } val = {0};
1363 val.flag = ebone->flag;
1364 BLI_ghash_insert(ebone_flag_orig, ebone, val.p);
1365 }
1366
1368
1369 GHASH_ITER (gh_iter, ebone_flag_orig) {
1370 union Value {
1371 int flag;
1372 void *p;
1373 } *val_p = (Value *)BLI_ghashIterator_getValue_p(&gh_iter);
1374 ebone = static_cast<EditBone *>(BLI_ghashIterator_getKey(&gh_iter));
1375 val_p->flag = ebone->flag & ~val_p->flag;
1376 }
1377 }
1378
1379 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1380 if (ebone->parent && ebone->flag & BONE_CONNECTED) {
1381 if (ebone->parent->temp.ebone == ebone->parent) {
1382 /* ignore */
1383 }
1384 else if (ebone->parent->temp.ebone) {
1385 /* set ignored */
1386 ebone->parent->temp.ebone = ebone->parent;
1387 }
1388 else {
1389 /* set child */
1390 ebone->parent->temp.ebone = ebone;
1391 }
1392 }
1393 }
1394
1395 /* cleanup multiple used bones */
1396 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1397 if (ebone->temp.ebone == ebone) {
1398 ebone->temp.ebone = nullptr;
1399 }
1400 }
1401
1402 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1403 /* break connections for unseen bones */
1404 if ((blender::animrig::bone_is_visible(arm, ebone) &&
1406 {
1407 ebone->temp.ebone = nullptr;
1408 }
1409
1410 if ((blender::animrig::bone_is_visible(arm, ebone) &&
1412 {
1413 if (ebone->parent && (ebone->flag & BONE_CONNECTED)) {
1414 ebone->parent->temp.ebone = nullptr;
1415 }
1416 }
1417 }
1418
1419 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1420
1421 if (ebone->parent && (ebone->parent->temp.ebone == ebone)) {
1422 ebone->flag |= BONE_DONE;
1423 }
1424 }
1425
1427
1428 for (ebone = static_cast<EditBone *>(arm->edbo->first); ebone; ebone = ebone_next) {
1429 ebone_next = ebone->next;
1430
1431 if (ebone->flag & BONE_DONE) {
1432 copy_v3_v3(ebone->parent->tail, ebone->tail);
1433 ebone->parent->rad_tail = ebone->rad_tail;
1435
1436 ED_armature_ebone_remove_ex(arm, ebone, false);
1437 changed = true;
1438 }
1439 }
1440
1441 if (changed) {
1442 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1443 if (ebone->parent && ebone->parent->temp.ebone && (ebone->flag & BONE_CONNECTED)) {
1444 ebone->rad_head = ebone->parent->rad_tail;
1445 }
1446 }
1447
1448 if (arm->flag & ARM_MIRROR_EDIT) {
1449 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1450 union Value {
1451 int flag;
1452 void *p;
1453 } *val_p = (Value *)BLI_ghash_lookup_p(ebone_flag_orig, ebone);
1454 if (val_p && val_p->flag) {
1455 ebone->flag &= ~val_p->flag;
1456 }
1457 }
1458 }
1459 }
1460
1461 if (arm->flag & ARM_MIRROR_EDIT) {
1462 BLI_ghash_free(ebone_flag_orig, nullptr, nullptr);
1463 }
1464
1465 if (changed) {
1466 changed_multi = true;
1471 }
1472 }
1473
1474 if (!changed_multi) {
1475 return OPERATOR_CANCELLED;
1476 }
1477
1478 return OPERATOR_FINISHED;
1479}
1480
1482{
1483 /* identifiers */
1484 ot->name = "Dissolve Selected Bone(s)";
1485 ot->idname = "ARMATURE_OT_dissolve";
1486 ot->description = "Dissolve selected bones from the armature";
1487
1488 /* API callbacks. */
1491
1492 /* flags */
1493 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1494}
1495
1497
1498/* -------------------------------------------------------------------- */
1501
1503{
1504 const Scene *scene = CTX_data_scene(C);
1505 ViewLayer *view_layer = CTX_data_view_layer(C);
1506 const int invert = RNA_boolean_get(op->ptr, "unselected") ? BONE_SELECTED : 0;
1507
1508 /* cancel if nothing selected */
1509 if (CTX_DATA_COUNT(C, selected_bones) == 0) {
1510 return OPERATOR_CANCELLED;
1511 }
1512
1514 scene, view_layer, CTX_wm_view3d(C));
1515 for (Object *obedit : objects) {
1516 bArmature *arm = static_cast<bArmature *>(obedit->data);
1517 bool changed = false;
1518
1519 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1520 if (blender::animrig::bone_is_visible(arm, ebone)) {
1521 if ((ebone->flag & BONE_SELECTED) != invert) {
1522 ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
1523 ebone->flag |= BONE_HIDDEN_A;
1524 changed = true;
1525 }
1526 }
1527 }
1528
1529 if (!changed) {
1530 continue;
1531 }
1533
1536 }
1537 return OPERATOR_FINISHED;
1538}
1539
1541{
1542 /* identifiers */
1543 ot->name = "Hide Selected";
1544 ot->idname = "ARMATURE_OT_hide";
1545 ot->description = "Tag selected bones to not be visible in Edit Mode";
1546
1547 /* API callbacks. */
1548 ot->exec = armature_hide_exec;
1550
1551 /* flags */
1552 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1553
1554 /* props */
1556 ot->srna, "unselected", false, "Unselected", "Hide unselected rather than selected");
1557}
1558
1560
1561/* -------------------------------------------------------------------- */
1564
1566{
1567 const Scene *scene = CTX_data_scene(C);
1568 ViewLayer *view_layer = CTX_data_view_layer(C);
1569 const bool select = RNA_boolean_get(op->ptr, "select");
1571 scene, view_layer, CTX_wm_view3d(C));
1572 for (Object *obedit : objects) {
1573 bArmature *arm = static_cast<bArmature *>(obedit->data);
1574 bool changed = false;
1575
1576 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1577 if (ANIM_bonecoll_is_visible_editbone(arm, ebone)) {
1578 if (ebone->flag & BONE_HIDDEN_A) {
1579 if (!(ebone->flag & BONE_UNSELECTABLE)) {
1581 }
1582 ebone->flag &= ~BONE_HIDDEN_A;
1583 changed = true;
1584 }
1585 }
1586 }
1587
1588 if (changed) {
1590
1593 }
1594 }
1595 return OPERATOR_FINISHED;
1596}
1597
1599{
1600 /* identifiers */
1601 ot->name = "Reveal Hidden";
1602 ot->idname = "ARMATURE_OT_reveal";
1603 ot->description = "Reveal all bones hidden in Edit Mode";
1604
1605 /* API callbacks. */
1606 ot->exec = armature_reveal_exec;
1608
1609 /* flags */
1610 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1611
1612 RNA_def_boolean(ot->srna, "select", true, "Select", "");
1613}
1614
Functions to deal with Armatures.
bool ANIM_bonecoll_is_visible_editbone(const bArmature *armature, const EditBone *ebone)
Blender kernel action and pose functionality.
void BKE_pose_channels_remove(Object *ob, bool(*filter_fn)(const char *bone_name, void *user_data), void *user_data) ATTR_NONNULL(1
void BKE_pose_tag_recalc(Main *bmain, bPose *pose) ATTR_NONNULL(1
void BKE_armature_transform(bArmature *arm, const float mat[4][4], bool do_props)
void mat3_to_vec_roll(const float mat[3][3], float r_vec[3], float *r_roll)
void vec_roll_to_mat3_normalized(const float nor[3], float roll, float r_mat[3][3])
#define CTX_DATA_BEGIN_WITH_ID(C, Type, instance, member, Type_id, instance_id)
#define CTX_DATA_BEGIN(C, Type, instance, member)
PointerRNA CTX_data_pointer_get_type(const bContext *C, const char *member, StructRNA *type)
#define CTX_DATA_COUNT(C, member)
Scene * CTX_data_scene(const bContext *C)
Object * CTX_data_edit_object(const bContext *C)
Main * CTX_data_main(const bContext *C)
EditBone * CTX_data_active_bone(const bContext *C)
RegionView3D * CTX_wm_region_view3d(const bContext *C)
#define CTX_DATA_END
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
#define FOREACH_OBJECT_IN_EDIT_MODE_END
Definition BKE_layer.hh:390
blender::Vector< Object * > BKE_view_layer_array_from_objects_in_edit_mode_unique_data(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
#define FOREACH_OBJECT_IN_EDIT_MODE_BEGIN(_scene, _view_layer, _v3d, _instance)
Definition BKE_layer.hh:386
General operations, lookup, etc. for blender objects.
bool BKE_object_is_in_editmode(const Object *ob)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_INFO
Definition BKE_report.hh:35
@ RPT_ERROR
Definition BKE_report.hh:39
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
BLI_INLINE void * BLI_ghashIterator_getKey(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.h:295
BLI_INLINE void ** BLI_ghashIterator_getValue_p(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.h:303
#define GHASH_ITER(gh_iter_, ghash_)
Definition BLI_ghash.h:318
void ** BLI_ghash_lookup_p(GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.cc:745
GHash * BLI_ghash_ptr_new_ex(const char *info, unsigned int nentries_reserve) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
void BLI_ghash_insert(GHash *gh, void *key, void *val)
Definition BLI_ghash.cc:707
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition BLI_ghash.cc:860
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
void void BLI_freelistN(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:497
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
#define BLI_ASSERT_UNIT_V3(v)
#define DEG2RADF(_deg)
#define M_PI_2
float mat4_to_scale(const float mat[4][4])
void mul_m3_v3(const float M[3][3], float r[3])
void copy_m3_m4(float m1[3][3], const float m2[4][4])
void normalize_m3(float R[3][3]) ATTR_NONNULL()
void mul_m4_v3(const float M[4][4], float r[3])
void mul_v3_m4v3(float r[3], const float mat[4][4], const float vec[3])
bool invert_m4_m4(float inverse[4][4], const float mat[4][4])
void mul_m3_m3m3(float R[3][3], const float A[3][3], const float B[3][3])
bool invert_m3(float mat[3][3])
void mul_mat3_m4_v3(const float mat[4][4], float r[3])
float angle_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
void minmax_v3v3_v3(float min[3], float max[3], const float vec[3])
MINLINE void sub_v3_v3(float r[3], const float a[3])
MINLINE bool equals_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
void project_v3_v3v3_normalized(float out[3], const float p[3], const float v_proj[3])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void cross_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void negate_v3(float r[3])
void mid_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void swap_v3_v3(float a[3], float b[3])
MINLINE void zero_v3(float r[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3(float n[3])
MINLINE float len_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
#define INIT_MINMAX(min, max)
#define SET_FLAG_FROM_TEST(value, test, flag)
#define ELEM(...)
#define IFACE_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_SELECT
Definition DNA_ID.h:1101
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1118
@ BONE_ROOTSEL
@ BONE_SELECTED
@ BONE_TRANSFORM
@ BONE_UNSELECTABLE
@ BONE_DONE
@ BONE_HIDDEN_A
@ BONE_TIPSEL
@ BONE_CONNECTED
@ ARM_MIRROR_EDIT
Object is a sort of wrapper for general info.
@ OB_ARMATURE
@ V3D_AROUND_CENTER_BOUNDS
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
#define EBONE_EDITABLE(ebone)
void ED_outliner_select_sync_from_edit_bone_tag(bContext *C)
bool ED_operator_editarmature(bContext *C)
Read Guarded memory(de)allocation.
#define RNA_ENUM_ITEM_HEADING(name, description)
Definition RNA_types.hh:673
#define C
Definition RandGen.cpp:29
@ ALERT_ICON_NONE
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_POSE
Definition WM_types.hh:458
#define ND_BONE_SELECT
Definition WM_types.hh:460
#define NC_OBJECT
Definition WM_types.hh:379
EditBone * add_points_bone(Object *obedit, float head[3], float tail[3])
void ARMATURE_OT_hide(wmOperatorType *ot)
void ARMATURE_OT_delete(wmOperatorType *ot)
static void armature_clear_swap_done_flags(bArmature *arm)
static bool armature_delete_ebone_cb(const char *bone_name, void *arm_p)
static void fix_editbone_connected_children(ListBase *edbo, EditBone *ebone)
static wmOperatorStatus armature_fill_bones_exec(bContext *C, wmOperator *op)
static wmOperatorStatus armature_reveal_exec(bContext *C, wmOperator *op)
void ARMATURE_OT_roll_clear(wmOperatorType *ot)
bArmature * ED_armature_context(const bContext *C)
void ARMATURE_OT_fill(wmOperatorType *ot)
static void fix_connected_bone(EditBone *ebone)
static void chains_find_tips(ListBase *edbo, ListBase *list)
static void bone_align_to_bone(ListBase *edbo, EditBone *selbone, EditBone *actbone)
float ED_armature_ebone_roll_to_vector(const EditBone *bone, const float align_axis[3], const bool axis_only)
static wmOperatorStatus armature_delete_selected_invoke(bContext *C, wmOperator *op, const wmEvent *)
static wmOperatorStatus armature_delete_selected_exec(bContext *C, wmOperator *)
void ARMATURE_OT_align(wmOperatorType *ot)
static void fill_add_joint(EditBone *ebo, short eb_tail, ListBase *points)
static const EnumPropertyItem prop_calc_roll_types[]
void ARMATURE_OT_calculate_roll(wmOperatorType *ot)
static wmOperatorStatus armature_calc_roll_exec(bContext *C, wmOperator *op)
void ED_armature_origin_set(Main *bmain, Object *ob, const float cursor[3], int centermode, int around)
void ED_armature_transform(bArmature *arm, const float mat[4][4], const bool do_props)
static wmOperatorStatus armature_dissolve_selected_exec(bContext *C, wmOperator *)
static wmOperatorStatus armature_roll_clear_exec(bContext *C, wmOperator *op)
void ARMATURE_OT_switch_direction(wmOperatorType *ot)
void ED_armature_edit_transform(bArmature *arm, const float mat[4][4], const bool do_props)
void ARMATURE_OT_dissolve(wmOperatorType *ot)
void ARMATURE_OT_reveal(wmOperatorType *ot)
static bool armature_dissolve_ebone_cb(const char *bone_name, void *arm_p)
static wmOperatorStatus armature_split_exec(bContext *C, wmOperator *)
eCalcRollTypes
@ CALC_ROLL_POS_Z
@ CALC_ROLL_VIEW
@ CALC_ROLL_NEG_Y
@ CALC_ROLL_TAN_NEG_Z
@ CALC_ROLL_CURSOR
@ CALC_ROLL_POS_Y
@ CALC_ROLL_NEG_Z
@ CALC_ROLL_TAN_NEG_X
@ CALC_ROLL_TAN_POS_X
@ CALC_ROLL_POS_X
@ CALC_ROLL_TAN_POS_Z
@ CALC_ROLL_ACTIVE
@ CALC_ROLL_NEG_X
static wmOperatorStatus armature_hide_exec(bContext *C, wmOperator *op)
static wmOperatorStatus armature_switch_direction_exec(bContext *C, wmOperator *)
void ARMATURE_OT_split(wmOperatorType *ot)
static wmOperatorStatus armature_align_bones_exec(bContext *C, wmOperator *op)
void armature_tag_select_mirrored(bArmature *arm)
void armature_select_mirrored(bArmature *arm)
void armature_tag_unselect(bArmature *arm)
void armature_select_mirrored_ex(bArmature *arm, int flag)
bool ED_armature_edit_deselect_all(Object *obedit)
EditBone * ED_armature_ebone_find_name(const ListBase *edbo, const char *name)
void ED_armature_ebone_remove_ex(bArmature *arm, EditBone *exBone, bool clear_connected)
void ED_armature_edit_sync_selection(ListBase *edbo)
int ED_armature_ebone_selectflag_get(const EditBone *ebone)
void ED_armature_ebone_select_set(EditBone *ebone, bool select)
void ED_armature_edit_free(bArmature *arm)
void ED_armature_ebone_to_mat3(EditBone *ebone, float r_mat[3][3])
void ED_armature_from_edit(Main *bmain, bArmature *arm)
void ED_armature_ebone_remove(bArmature *arm, EditBone *exBone)
void ED_armature_to_edit(bArmature *arm)
EditBone * ED_armature_ebone_get_mirrored(const ListBase *edbo, EditBone *ebo)
IMETHOD Vector diff(const Vector &a, const Vector &b, double dt)
Definition frames.inl:1166
uint nor
#define select(A, B, C)
float length(VecOp< float, D >) RET
int count
CCL_NAMESPACE_BEGIN ccl_device float invert(const float color, const float factor)
Definition invert.h:11
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
bool bone_is_visible(const bArmature *armature, const Bone *bone)
bool bone_is_selected(const bArmature *armature, const Bone *bone)
Object * context_active_object(const bContext *C)
#define fabsf
float RNA_float_get(PointerRNA *ptr, const char *name)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_float_rotation(StructOrFunctionRNA *cont_, const char *identifier, const int len, const float *default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
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)
#define min(a, b)
Definition sort.cc:36
EditBone * tail_owner
EditBonePoint * next
EditBone * head_owner
EditBonePoint * prev
char name[64]
float tail[3]
union EditBone::@275371335250266324235150226366250166246037204077 temp
EditBone * parent
EditBone * next
float rad_tail
EditBone * ebone
float rad_head
float head[3]
void * data
void * first
ObjectRuntimeHandle * runtime
float loc[3]
void * data
Definition RNA_types.hh:53
float viewinv[4][4]
View3DCursor cursor
struct EditBone * act_edbone
ListBase * edbo
struct ReportList * reports
struct PointerRNA * ptr
max
Definition text_draw.cc:251
#define N_(msgid)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4237
void WM_operator_properties_confirm_or_exec(wmOperatorType *ot)
wmOperatorStatus WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)
wmOperatorStatus WM_operator_confirm_ex(bContext *C, wmOperator *op, const char *title, const char *message, const char *confirm_text, int icon, bool cancel_default)
uint8_t flag
Definition wm_window.cc:145