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