Blender V4.5
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) {
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_editbone(arm, ebone) &&
336 EBONE_EDITABLE(ebone));
337 bool is_edit_parent = (blender::animrig::bone_is_visible_editbone(arm, ebone->parent) &&
338 EBONE_EDITABLE(ebone->parent));
339
340 if (is_edit || is_edit_parent) {
341 EditBone *ebone_other = ebone->parent;
342 float dir_a[3];
343 float dir_b[3];
344 float vec[3];
345 bool is_vec_zero;
346
347 sub_v3_v3v3(dir_a, ebone->tail, ebone->head);
348 normalize_v3(dir_a);
349
350 /* find the first bone in the chain with a different direction */
351 do {
352 sub_v3_v3v3(dir_b, ebone_other->head, ebone_other->tail);
353 normalize_v3(dir_b);
354
355 if (type == CALC_ROLL_TAN_POS_Z) {
356 cross_v3_v3v3(vec, dir_a, dir_b);
357 }
358 else {
359 add_v3_v3v3(vec, dir_a, dir_b);
360 }
361 } while ((is_vec_zero = (normalize_v3(vec) < 0.00001f)) &&
362 (ebone_other = ebone_other->parent));
363
364 if (!is_vec_zero) {
365 if (axis_flip) {
366 negate_v3(vec);
367 }
368
369 if (is_edit) {
370 ebone->roll = ED_armature_ebone_roll_to_vector(ebone, vec, axis_only);
371 changed = true;
372 }
373
374 /* parentless bones use cross product with child */
375 if (is_edit_parent) {
376 if (ebone->parent->parent == nullptr) {
378 ebone->parent, vec, axis_only);
379 changed = true;
380 }
381 }
382 }
383 }
384 }
385 }
386 }
387 else {
388 float vec[3] = {0.0f, 0.0f, 0.0f};
389 if (type == CALC_ROLL_VIEW) { /* View */
391 if (rv3d == nullptr) {
392 BKE_report(op->reports, RPT_ERROR, "No region view3d available");
393 return OPERATOR_CANCELLED;
394 }
395
396 copy_v3_v3(vec, rv3d->viewinv[2]);
397 mul_m3_v3(imat, vec);
398 }
399 else if (type == CALC_ROLL_ACTIVE) {
400 float mat[3][3];
401 bArmature *arm_active = static_cast<bArmature *>(ob_active->data);
402 ebone = (EditBone *)arm_active->act_edbone;
403 if (ebone == nullptr) {
404 BKE_report(op->reports, RPT_ERROR, "No active bone set");
405 return OPERATOR_CANCELLED;
406 }
407
408 ED_armature_ebone_to_mat3(ebone, mat);
409 copy_v3_v3(vec, mat[2]);
410 }
411 else if (type < 6) { /* NOTE: always true, check to quiet GCC12.2 `-Warray-bounds`. */
412 /* Axis */
413 if (type < 3) {
414 vec[type] = 1.0f;
415 }
416 else {
417 vec[type - 2] = -1.0f;
418 }
419 mul_m3_v3(imat, vec);
420 normalize_v3(vec);
421 }
422 else {
423 /* The previous block should handle all remaining cases. */
425 }
426
427 if (axis_flip) {
428 negate_v3(vec);
429 }
430
431 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
433 /* roll func is a callback which assumes that all is well */
434 ebone->roll = ED_armature_ebone_roll_to_vector(ebone, vec, axis_only);
435 changed = true;
436 }
437 }
438 }
439
440 if (arm->flag & ARM_MIRROR_EDIT) {
441 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
442 if ((blender::animrig::bone_is_visible_editbone(arm, ebone) && EBONE_EDITABLE(ebone)) == 0)
443 {
444 EditBone *ebone_mirr = ED_armature_ebone_get_mirrored(arm->edbo, ebone);
445 if (ebone_mirr && (blender::animrig::bone_is_visible_editbone(arm, ebone_mirr) &&
446 EBONE_EDITABLE(ebone_mirr)))
447 {
448 ebone->roll = -ebone_mirr->roll;
449 }
450 }
451 }
452 }
453
454 if (changed) {
455 /* NOTE: notifier might evolve. */
458 }
459 }
460
461 return OPERATOR_FINISHED;
462}
463
465{
466 /* identifiers */
467 ot->name = "Recalculate Roll";
468 ot->idname = "ARMATURE_OT_calculate_roll";
469 ot->description = "Automatically fix alignment of select bones' axes";
470
471 /* API callbacks. */
472 ot->invoke = WM_menu_invoke;
475
476 /* flags */
478
479 /* properties */
480 ot->prop = RNA_def_enum(ot->srna, "type", prop_calc_roll_types, CALC_ROLL_TAN_POS_X, "Type", "");
481 RNA_def_boolean(ot->srna, "axis_flip", false, "Flip Axis", "Negate the alignment axis");
482 RNA_def_boolean(ot->srna,
483 "axis_only",
484 false,
485 "Shortest Rotation",
486 "Ignore the axis direction, use the shortest rotation to align");
487}
488
490{
491 const Scene *scene = CTX_data_scene(C);
492 ViewLayer *view_layer = CTX_data_view_layer(C);
493 const float roll = RNA_float_get(op->ptr, "roll");
494
496 scene, view_layer, CTX_wm_view3d(C));
497 for (Object *ob : objects) {
498 bArmature *arm = static_cast<bArmature *>(ob->data);
499 bool changed = false;
500
501 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
503 /* Roll func is a callback which assumes that all is well. */
504 ebone->roll = roll;
505 changed = true;
506 }
507 }
508
509 if (arm->flag & ARM_MIRROR_EDIT) {
510 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
511 if ((blender::animrig::bone_is_visible_editbone(arm, ebone) && EBONE_EDITABLE(ebone)) == 0)
512 {
513 EditBone *ebone_mirr = ED_armature_ebone_get_mirrored(arm->edbo, ebone);
514 if (ebone_mirr && (blender::animrig::bone_is_visible_editbone(arm, ebone_mirr) &&
515 EBONE_EDITABLE(ebone_mirr)))
516 {
517 ebone->roll = -ebone_mirr->roll;
518 changed = true;
519 }
520 }
521 }
522 }
523
524 if (changed) {
525 /* NOTE: notifier might evolve. */
528 }
529 }
530
531 return OPERATOR_FINISHED;
532}
533
535{
536 /* identifiers */
537 ot->name = "Clear Roll";
538 ot->idname = "ARMATURE_OT_roll_clear";
539 ot->description = "Clear roll for selected bones";
540
541 /* API callbacks. */
544
545 /* flags */
547
549 "roll",
550 0,
551 nullptr,
552 DEG2RADF(-360.0f),
553 DEG2RADF(360.0f),
554 "Roll",
555 "",
556 DEG2RADF(-360.0f),
557 DEG2RADF(360.0f));
558}
559
561
562/* -------------------------------------------------------------------- */
565
566/* temporary data-structure for merge/fill bones */
569
570 EditBone *head_owner; /* EditBone which uses this point as a 'head' point */
571 EditBone *tail_owner; /* EditBone which uses this point as a 'tail' point */
572
573 float vec[3]; /* the actual location of the point in local/EditMode space */
574};
575
576/* find chain-tips (i.e. bones without children) */
577static void chains_find_tips(ListBase *edbo, ListBase *list)
578{
579 EditBone *ebo;
580 LinkData *ld;
581
582 /* NOTE: this is potentially very slow ... there's got to be a better way. */
583 LISTBASE_FOREACH (EditBone *, curBone, edbo) {
584 short stop = 0;
585
586 /* is this bone contained within any existing chain? (skip if so) */
587 LISTBASE_FOREACH (LinkData *, ld, list) {
588 for (ebo = static_cast<EditBone *>(ld->data); ebo; ebo = ebo->parent) {
589 if (ebo == curBone) {
590 stop = 1;
591 break;
592 }
593 }
594
595 if (stop) {
596 break;
597 }
598 }
599 /* skip current bone if it is part of an existing chain */
600 if (stop) {
601 continue;
602 }
603
604 /* is any existing chain part of the chain formed by this bone? */
605 stop = 0;
606 for (ebo = curBone->parent; ebo; ebo = ebo->parent) {
607 LISTBASE_FOREACH (LinkData *, ld, list) {
608 if (ld->data == ebo) {
609 ld->data = curBone;
610 stop = 1;
611 break;
612 }
613 }
614
615 if (stop) {
616 break;
617 }
618 }
619 /* current bone has already been added to a chain? */
620 if (stop) {
621 continue;
622 }
623
624 /* add current bone to a new chain */
625 ld = MEM_callocN<LinkData>("BoneChain");
626 ld->data = curBone;
627 BLI_addtail(list, ld);
628 }
629}
630
632
633/* -------------------------------------------------------------------- */
636
637static void fill_add_joint(EditBone *ebo, short eb_tail, ListBase *points)
638{
639 EditBonePoint *ebp;
640 float vec[3];
641 short found = 0;
642
643 if (eb_tail) {
644 copy_v3_v3(vec, ebo->tail);
645 }
646 else {
647 copy_v3_v3(vec, ebo->head);
648 }
649
650 LISTBASE_FOREACH (EditBonePoint *, ebp, points) {
651 if (equals_v3v3(ebp->vec, vec)) {
652 if (eb_tail) {
653 if ((ebp->head_owner) && (ebp->head_owner->parent == ebo)) {
654 /* so this bone's tail owner is this bone */
655 ebp->tail_owner = ebo;
656 found = 1;
657 break;
658 }
659 }
660 else {
661 if ((ebp->tail_owner) && (ebo->parent == ebp->tail_owner)) {
662 /* so this bone's head owner is this bone */
663 ebp->head_owner = ebo;
664 found = 1;
665 break;
666 }
667 }
668 }
669 }
670
671 /* allocate a new point if no existing point was related */
672 if (found == 0) {
673 ebp = MEM_callocN<EditBonePoint>("EditBonePoint");
674
675 if (eb_tail) {
676 copy_v3_v3(ebp->vec, ebo->tail);
677 ebp->tail_owner = ebo;
678 }
679 else {
680 copy_v3_v3(ebp->vec, ebo->head);
681 ebp->head_owner = ebo;
682 }
683
684 BLI_addtail(points, ebp);
685 }
686}
687
688/* bone adding between selected joints */
690{
691 Scene *scene = CTX_data_scene(C);
692 View3D *v3d = CTX_wm_view3d(C);
693 ListBase points = {nullptr, nullptr};
694 EditBone *newbone = nullptr;
695 int count;
696 bool mixed_object_error = false;
697
698 /* loop over all bones, and only consider if visible */
699 bArmature *arm = nullptr;
700 CTX_DATA_BEGIN_WITH_ID (C, EditBone *, ebone, visible_bones, bArmature *, arm_iter) {
701 bool check = false;
702 if (!(ebone->flag & BONE_CONNECTED) && (ebone->flag & BONE_ROOTSEL)) {
703 fill_add_joint(ebone, 0, &points);
704 check = true;
705 }
706 if (ebone->flag & BONE_TIPSEL) {
707 fill_add_joint(ebone, 1, &points);
708 check = true;
709 }
710
711 if (check) {
712 if (arm && (arm != arm_iter)) {
713 mixed_object_error = true;
714 }
715 arm = arm_iter;
716 }
717 }
719
720 /* the number of joints determines how we fill:
721 * 1) between joint and cursor (joint=head, cursor=tail)
722 * 2) between the two joints (order is dependent on active-bone/hierarchy)
723 * 3+) error (a smarter method involving finding chains needs to be worked out
724 */
725 count = BLI_listbase_count(&points);
726
727 if (count == 0) {
728 BKE_report(op->reports, RPT_ERROR, "No joints selected");
729 return OPERATOR_CANCELLED;
730 }
731
732 if (mixed_object_error) {
733 BKE_report(op->reports, RPT_ERROR, "Bones for different objects selected");
734 BLI_freelistN(&points);
735 return OPERATOR_CANCELLED;
736 }
737
738 Object *obedit = nullptr;
739 {
740 ViewLayer *view_layer = CTX_data_view_layer(C);
741 FOREACH_OBJECT_IN_EDIT_MODE_BEGIN (scene, view_layer, v3d, ob_iter) {
742 if (ob_iter->data == arm) {
743 obedit = ob_iter;
744 }
745 }
747 }
748 BLI_assert(obedit != nullptr);
749
750 if (count == 1) {
751 EditBonePoint *ebp;
752 float curs[3];
753
754 /* Get Points - selected joint */
755 ebp = static_cast<EditBonePoint *>(points.first);
756
757 /* Get points - cursor (tail) */
758 invert_m4_m4(obedit->runtime->world_to_object.ptr(), obedit->object_to_world().ptr());
759 mul_v3_m4v3(curs, obedit->world_to_object().ptr(), scene->cursor.location);
760
761 /* Create a bone */
762 newbone = add_points_bone(obedit, ebp->vec, curs);
763 }
764 else if (count == 2) {
765 EditBonePoint *ebp_a, *ebp_b;
766 float head[3], tail[3];
767 short headtail = 0;
768
769 /* check that the points don't belong to the same bone */
770 ebp_a = static_cast<EditBonePoint *>(points.first);
771 ebp_b = ebp_a->next;
772
773 if (((ebp_a->head_owner == ebp_b->tail_owner) && (ebp_a->head_owner != nullptr)) ||
774 ((ebp_a->tail_owner == ebp_b->head_owner) && (ebp_a->tail_owner != nullptr)))
775 {
776 BKE_report(op->reports, RPT_ERROR, "Same bone selected...");
777 BLI_freelistN(&points);
778 return OPERATOR_CANCELLED;
779 }
780
781 /* find which one should be the 'head' */
782 if ((ebp_a->head_owner && ebp_b->head_owner) || (ebp_a->tail_owner && ebp_b->tail_owner)) {
783 /* use active, nice predictable */
784 if (arm->act_edbone && ELEM(arm->act_edbone, ebp_a->head_owner, ebp_a->tail_owner)) {
785 headtail = 1;
786 }
787 else if (arm->act_edbone && ELEM(arm->act_edbone, ebp_b->head_owner, ebp_b->tail_owner)) {
788 headtail = 2;
789 }
790 else {
791 /* rule: whichever one is closer to 3d-cursor */
792 float curs[3];
793 float dist_sq_a, dist_sq_b;
794
795 /* get cursor location */
796 invert_m4_m4(obedit->runtime->world_to_object.ptr(), obedit->object_to_world().ptr());
797 mul_v3_m4v3(curs, obedit->world_to_object().ptr(), scene->cursor.location);
798
799 /* get distances */
800 dist_sq_a = len_squared_v3v3(ebp_a->vec, curs);
801 dist_sq_b = len_squared_v3v3(ebp_b->vec, curs);
802
803 /* compare distances - closer one therefore acts as direction for bone to go */
804 headtail = (dist_sq_a < dist_sq_b) ? 2 : 1;
805 }
806 }
807 else if (ebp_a->head_owner) {
808 headtail = 1;
809 }
810 else if (ebp_b->head_owner) {
811 headtail = 2;
812 }
813
814 /* assign head/tail combinations */
815 if (headtail == 2) {
816 copy_v3_v3(head, ebp_a->vec);
817 copy_v3_v3(tail, ebp_b->vec);
818 }
819 else if (headtail == 1) {
820 copy_v3_v3(head, ebp_b->vec);
821 copy_v3_v3(tail, ebp_a->vec);
822 }
823
824 /* add new bone and parent it to the appropriate end */
825 if (headtail) {
826 newbone = add_points_bone(obedit, head, tail);
827
828 /* do parenting (will need to set connected flag too) */
829 if (headtail == 2) {
830 /* ebp tail or head - tail gets priority */
831 if (ebp_a->tail_owner) {
832 newbone->parent = ebp_a->tail_owner;
833 }
834 else {
835 newbone->parent = ebp_a->head_owner;
836 }
837 }
838 else {
839 /* ebp_b tail or head - tail gets priority */
840 if (ebp_b->tail_owner) {
841 newbone->parent = ebp_b->tail_owner;
842 }
843 else {
844 newbone->parent = ebp_b->head_owner;
845 }
846 }
847
848 /* don't set for bone connecting two head points of bones */
849 if (ebp_a->tail_owner || ebp_b->tail_owner) {
850 newbone->flag |= BONE_CONNECTED;
851 }
852 }
853 }
854 else {
855 BKE_reportf(op->reports, RPT_ERROR, "Too many points selected: %d", count);
856 BLI_freelistN(&points);
857 return OPERATOR_CANCELLED;
858 }
859
860 if (newbone) {
862 arm->act_edbone = newbone;
863 newbone->flag |= BONE_TIPSEL;
864 }
865
866 /* updates */
869
870 /* free points */
871 BLI_freelistN(&points);
872
873 return OPERATOR_FINISHED;
874}
875
877{
878 /* identifiers */
879 ot->name = "Fill Between Joints";
880 ot->idname = "ARMATURE_OT_fill";
881 ot->description = "Add bone between selected joint(s) and/or 3D cursor";
882
883 /* callbacks */
886
887 /* flags */
889}
890
892
893/* -------------------------------------------------------------------- */
900
901/* helper to clear BONE_TRANSFORM flags */
903{
904 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
905 ebone->flag &= ~BONE_TRANSFORM;
906 }
907}
908
910{
911 const Scene *scene = CTX_data_scene(C);
912 ViewLayer *view_layer = CTX_data_view_layer(C);
914 scene, view_layer, CTX_wm_view3d(C));
915
916 for (Object *ob : objects) {
917 bArmature *arm = static_cast<bArmature *>(ob->data);
918
919 ListBase chains = {nullptr, nullptr};
920
921 /* get chains of bones (ends on chains) */
922 chains_find_tips(arm->edbo, &chains);
923 if (BLI_listbase_is_empty(&chains)) {
924 continue;
925 }
926
927 /* ensure that mirror bones will also be operated on */
929
930 /* Clear BONE_TRANSFORM flags
931 * - Used to prevent duplicate/canceling operations from occurring #34123.
932 * - #BONE_DONE cannot be used here as that's already used for mirroring.
933 */
935
936 /* loop over chains, only considering selected and visible bones */
937 LISTBASE_FOREACH (LinkData *, chain, &chains) {
938 EditBone *ebo, *child = nullptr, *parent = nullptr;
939
940 /* loop over bones in chain */
941 for (ebo = static_cast<EditBone *>(chain->data); ebo; ebo = parent) {
942 /* parent is this bone's original parent
943 * - we store this, as the next bone that is checked is this one
944 * but the value of ebo->parent may change here...
945 */
946 parent = ebo->parent;
947
948 /* skip bone if already handled, see #34123. */
949 if ((ebo->flag & BONE_TRANSFORM) == 0) {
950 /* only if selected and editable */
952 /* swap head and tail coordinates */
953 swap_v3_v3(ebo->head, ebo->tail);
954
955 /* do parent swapping:
956 * - use 'child' as new parent
957 * - connected flag is only set if points are coincidental
958 */
959 ebo->parent = child;
960 if ((child) && equals_v3v3(ebo->head, child->tail)) {
961 ebo->flag |= BONE_CONNECTED;
962 }
963 else {
964 ebo->flag &= ~BONE_CONNECTED;
965 }
966
967 /* get next bones
968 * - child will become the new parent of next bone
969 */
970 child = ebo;
971 }
972 else {
973 /* not swapping this bone, however, if its 'parent' got swapped, unparent us from it
974 * as it will be facing in opposite direction
975 */
976 if ((parent) && (blender::animrig::bone_is_visible_editbone(arm, parent) &&
977 EBONE_EDITABLE(parent)))
978 {
979 ebo->parent = nullptr;
980 ebo->flag &= ~BONE_CONNECTED;
981 }
982
983 /* get next bones
984 * - child will become new parent of next bone (not swapping occurred,
985 * so set to nullptr to prevent infinite-loop)
986 */
987 child = nullptr;
988 }
989
990 /* tag as done (to prevent double-swaps) */
991 ebo->flag |= BONE_TRANSFORM;
992 }
993 }
994 }
995
996 /* free chains */
997 BLI_freelistN(&chains);
998
999 /* clear temp flags */
1002
1003 /* NOTE: notifier might evolve. */
1006 }
1007
1008 return OPERATOR_FINISHED;
1009}
1010
1012{
1013 /* identifiers */
1014 ot->name = "Switch Direction";
1015 ot->idname = "ARMATURE_OT_switch_direction";
1016 ot->description = "Change the direction that a chain of bones points in (head and tail swap)";
1017
1018 /* API callbacks. */
1021
1022 /* flags */
1023 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1024}
1025
1027
1028/* -------------------------------------------------------------------- */
1031
1032/* Helper to fix a ebone position if its parent has moved due to alignment. */
1033static void fix_connected_bone(EditBone *ebone)
1034{
1035 float diff[3];
1036
1037 if (!(ebone->parent) || !(ebone->flag & BONE_CONNECTED) ||
1038 equals_v3v3(ebone->parent->tail, ebone->head))
1039 {
1040 return;
1041 }
1042
1043 /* if the parent has moved we translate child's head and tail accordingly */
1044 sub_v3_v3v3(diff, ebone->parent->tail, ebone->head);
1045 add_v3_v3(ebone->head, diff);
1046 add_v3_v3(ebone->tail, diff);
1047}
1048
1049/* helper to recursively find chains of connected bones starting at ebone and fix their position */
1051{
1052 LISTBASE_FOREACH (EditBone *, selbone, edbo) {
1053 if ((selbone->parent) && (selbone->parent == ebone) && (selbone->flag & BONE_CONNECTED)) {
1054 fix_connected_bone(selbone);
1055 fix_editbone_connected_children(edbo, selbone);
1056 }
1057 }
1058}
1059
1060static void bone_align_to_bone(ListBase *edbo, EditBone *selbone, EditBone *actbone)
1061{
1062 float selboneaxis[3], actboneaxis[3], length;
1063
1064 sub_v3_v3v3(actboneaxis, actbone->tail, actbone->head);
1065 normalize_v3(actboneaxis);
1066
1067 sub_v3_v3v3(selboneaxis, selbone->tail, selbone->head);
1068 length = len_v3(selboneaxis);
1069
1070 mul_v3_fl(actboneaxis, length);
1071 add_v3_v3v3(selbone->tail, selbone->head, actboneaxis);
1072 selbone->roll = actbone->roll;
1073
1074 /* If the bone being aligned has connected descendants they must be moved
1075 * according to their parent new position, otherwise they would be left
1076 * in an inconsistent state: connected but away from the parent. */
1077 fix_editbone_connected_children(edbo, selbone);
1078}
1079
1081{
1083 bArmature *arm = static_cast<bArmature *>(ob->data);
1084 EditBone *actbone = CTX_data_active_bone(C);
1085 EditBone *actmirb = nullptr;
1086 int num_selected_bones;
1087
1088 /* there must be an active bone */
1089 if (actbone == nullptr) {
1090 BKE_report(op->reports, RPT_ERROR, "Operation requires an active bone");
1091 return OPERATOR_CANCELLED;
1092 }
1093
1094 if (arm->flag & ARM_MIRROR_EDIT) {
1095 /* For X-Axis Mirror Editing option, we may need a mirror copy of actbone
1096 * - if there's a mirrored copy of selbone, try to find a mirrored copy of actbone
1097 * (i.e. selbone="child.L" and actbone="parent.L", find "child.R" and "parent.R").
1098 * This is useful for arm-chains, for example parenting lower arm to upper arm
1099 * - if there's no mirrored copy of actbone (i.e. actbone = "parent.C" or "parent")
1100 * then just use actbone. Useful when doing upper arm to spine.
1101 */
1102 actmirb = ED_armature_ebone_get_mirrored(arm->edbo, actbone);
1103 if (actmirb == nullptr) {
1104 actmirb = actbone;
1105 }
1106 }
1107
1108 /* if there is only 1 selected bone, we assume that it is the active bone,
1109 * since a user will need to have clicked on a bone (thus selecting it) to make it active
1110 */
1111 num_selected_bones = CTX_DATA_COUNT(C, selected_editable_bones);
1112 if (num_selected_bones <= 1) {
1113 /* When only the active bone is selected, and it has a parent,
1114 * align it to the parent, as that is the only possible outcome.
1115 */
1116 if (actbone->parent) {
1117 bone_align_to_bone(arm->edbo, actbone, actbone->parent);
1118
1119 if ((arm->flag & ARM_MIRROR_EDIT) && (actmirb->parent)) {
1120 bone_align_to_bone(arm->edbo, actmirb, actmirb->parent);
1121 }
1122
1123 BKE_reportf(op->reports, RPT_INFO, "Aligned bone '%s' to parent", actbone->name);
1124 }
1125 }
1126 else {
1127 /* Align 'selected' bones to the active one
1128 * - the context iterator contains both selected bones and their mirrored copies,
1129 * so we assume that unselected bones are mirrored copies of some selected bone
1130 * - since the active one (and/or its mirror) will also be selected, we also need
1131 * to check that we are not trying to operate on them, since such an operation
1132 * would cause errors
1133 */
1134
1135 /* align selected bones to the active one */
1136 CTX_DATA_BEGIN (C, EditBone *, ebone, selected_editable_bones) {
1137 if (ELEM(ebone, actbone, actmirb) == 0) {
1138 if (ebone->flag & BONE_SELECTED) {
1139 bone_align_to_bone(arm->edbo, ebone, actbone);
1140 }
1141 else if ((arm->flag & ARM_MIRROR_EDIT)) {
1142 /* Need to check for the mirror mode, because when editing multiple armatures with
1143 * differing mirror settings `actmirb` can be a nullptr. See #146242. */
1144 bone_align_to_bone(arm->edbo, ebone, actmirb);
1145 }
1146 }
1147 }
1149
1151 op->reports, RPT_INFO, "%d bones aligned to bone '%s'", num_selected_bones, actbone->name);
1152 }
1153
1154 /* NOTE: notifier might evolve. */
1157
1158 return OPERATOR_FINISHED;
1159}
1160
1162{
1163 /* identifiers */
1164 ot->name = "Align Bones";
1165 ot->idname = "ARMATURE_OT_align";
1166 ot->description = "Align selected bones to the active bone (or to their parent)";
1167
1168 /* API callbacks. */
1171
1172 /* flags */
1173 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1174}
1175
1177
1178/* -------------------------------------------------------------------- */
1181
1183{
1184 const Scene *scene = CTX_data_scene(C);
1185 ViewLayer *view_layer = CTX_data_view_layer(C);
1186
1188 scene, view_layer, CTX_wm_view3d(C));
1189 for (Object *ob : objects) {
1190 bArmature *arm = static_cast<bArmature *>(ob->data);
1191
1192 LISTBASE_FOREACH (EditBone *, bone, arm->edbo) {
1193 if (bone->parent && (bone->flag & BONE_SELECTED) != (bone->parent->flag & BONE_SELECTED)) {
1194 bone->parent = nullptr;
1195 bone->flag &= ~BONE_CONNECTED;
1196 }
1197 }
1198 LISTBASE_FOREACH (EditBone *, bone, arm->edbo) {
1199 ED_armature_ebone_select_set(bone, (bone->flag & BONE_SELECTED) != 0);
1200 }
1201
1204 }
1205
1206 return OPERATOR_FINISHED;
1207}
1208
1210{
1211 /* identifiers */
1212 ot->name = "Split";
1213 ot->idname = "ARMATURE_OT_split";
1214 ot->description = "Split off selected bones from connected unselected bones";
1215
1216 /* API callbacks. */
1217 ot->exec = armature_split_exec;
1219
1220 /* flags */
1221 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1222}
1223
1225
1226/* -------------------------------------------------------------------- */
1229
1230static bool armature_delete_ebone_cb(const char *bone_name, void *arm_p)
1231{
1232 bArmature *arm = static_cast<bArmature *>(arm_p);
1233 EditBone *ebone;
1234
1235 ebone = ED_armature_ebone_find_name(arm->edbo, bone_name);
1236 return (ebone && (ebone->flag & BONE_SELECTED) &&
1238}
1239
1240/* previously delete_armature */
1241/* only editmode! */
1243{
1244 EditBone *curBone, *ebone_next;
1245 bool changed_multi = false;
1246
1247 /* cancel if nothing selected */
1248 if (CTX_DATA_COUNT(C, selected_bones) == 0) {
1249 return OPERATOR_CANCELLED;
1250 }
1251
1252 const Scene *scene = CTX_data_scene(C);
1253 ViewLayer *view_layer = CTX_data_view_layer(C);
1255 scene, view_layer, CTX_wm_view3d(C));
1256 for (Object *obedit : objects) {
1257 bArmature *arm = static_cast<bArmature *>(obedit->data);
1258 bool changed = false;
1259
1261
1263
1264 for (curBone = static_cast<EditBone *>(arm->edbo->first); curBone; curBone = ebone_next) {
1265 ebone_next = curBone->next;
1267 if (curBone->flag & BONE_SELECTED) {
1268 if (curBone == arm->act_edbone) {
1269 arm->act_edbone = nullptr;
1270 }
1271 ED_armature_ebone_remove(arm, curBone);
1272 changed = true;
1273 }
1274 }
1275 }
1276
1277 if (changed) {
1278 changed_multi = true;
1279
1281 BKE_pose_tag_recalc(CTX_data_main(C), obedit->pose);
1285 }
1286 }
1287
1288 if (!changed_multi) {
1289 return OPERATOR_CANCELLED;
1290 }
1291
1292 return OPERATOR_FINISHED;
1293}
1294
1296 wmOperator *op,
1297 const wmEvent * /*event*/)
1298{
1299 if (RNA_boolean_get(op->ptr, "confirm")) {
1301 op,
1302 IFACE_("Delete selected bones?"),
1303 nullptr,
1304 IFACE_("Delete"),
1306 false);
1307 }
1308 return armature_delete_selected_exec(C, op);
1309}
1310
1312{
1313 /* identifiers */
1314 ot->name = "Delete Selected Bone(s)";
1315 ot->idname = "ARMATURE_OT_delete";
1316 ot->description = "Remove selected bones from the armature";
1317
1318 /* API callbacks. */
1322
1323 /* flags */
1324 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1326}
1327
1328static bool armature_dissolve_ebone_cb(const char *bone_name, void *arm_p)
1329{
1330 bArmature *arm = static_cast<bArmature *>(arm_p);
1331 EditBone *ebone;
1332
1333 ebone = ED_armature_ebone_find_name(arm->edbo, bone_name);
1334 return (ebone && (ebone->flag & BONE_DONE));
1335}
1336
1338{
1339 const Scene *scene = CTX_data_scene(C);
1340 ViewLayer *view_layer = CTX_data_view_layer(C);
1341 EditBone *ebone, *ebone_next;
1342 bool changed_multi = false;
1343
1345 scene, view_layer, CTX_wm_view3d(C));
1346 for (Object *obedit : objects) {
1347 bArmature *arm = static_cast<bArmature *>(obedit->data);
1348 bool changed = false;
1349
1350 /* store for mirror */
1351 GHash *ebone_flag_orig = nullptr;
1352 int ebone_num = 0;
1353
1354 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1355 ebone->temp.p = nullptr;
1356 ebone->flag &= ~BONE_DONE;
1357 ebone_num++;
1358 }
1359
1360 if (arm->flag & ARM_MIRROR_EDIT) {
1361 GHashIterator gh_iter;
1362
1363 ebone_flag_orig = BLI_ghash_ptr_new_ex(__func__, ebone_num);
1364 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1365 union {
1366 int flag;
1367 void *p;
1368 } val = {0};
1369 val.flag = ebone->flag;
1370 BLI_ghash_insert(ebone_flag_orig, ebone, val.p);
1371 }
1372
1374
1375 GHASH_ITER (gh_iter, ebone_flag_orig) {
1376 union Value {
1377 int flag;
1378 void *p;
1379 } *val_p = (Value *)BLI_ghashIterator_getValue_p(&gh_iter);
1380 ebone = static_cast<EditBone *>(BLI_ghashIterator_getKey(&gh_iter));
1381 val_p->flag = ebone->flag & ~val_p->flag;
1382 }
1383 }
1384
1385 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1386 if (ebone->parent && ebone->flag & BONE_CONNECTED) {
1387 if (ebone->parent->temp.ebone == ebone->parent) {
1388 /* ignore */
1389 }
1390 else if (ebone->parent->temp.ebone) {
1391 /* set ignored */
1392 ebone->parent->temp.ebone = ebone->parent;
1393 }
1394 else {
1395 /* set child */
1396 ebone->parent->temp.ebone = ebone;
1397 }
1398 }
1399 }
1400
1401 /* cleanup multiple used bones */
1402 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1403 if (ebone->temp.ebone == ebone) {
1404 ebone->temp.ebone = nullptr;
1405 }
1406 }
1407
1408 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1409 /* break connections for unseen bones */
1412 {
1413 ebone->temp.ebone = nullptr;
1414 }
1415
1418 {
1419 if (ebone->parent && (ebone->flag & BONE_CONNECTED)) {
1420 ebone->parent->temp.ebone = nullptr;
1421 }
1422 }
1423 }
1424
1425 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1426
1427 if (ebone->parent && (ebone->parent->temp.ebone == ebone)) {
1428 ebone->flag |= BONE_DONE;
1429 }
1430 }
1431
1433
1434 for (ebone = static_cast<EditBone *>(arm->edbo->first); ebone; ebone = ebone_next) {
1435 ebone_next = ebone->next;
1436
1437 if (ebone->flag & BONE_DONE) {
1438 copy_v3_v3(ebone->parent->tail, ebone->tail);
1439 ebone->parent->rad_tail = ebone->rad_tail;
1441
1442 ED_armature_ebone_remove_ex(arm, ebone, false);
1443 changed = true;
1444 }
1445 }
1446
1447 if (changed) {
1448 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1449 if (ebone->parent && ebone->parent->temp.ebone && (ebone->flag & BONE_CONNECTED)) {
1450 ebone->rad_head = ebone->parent->rad_tail;
1451 }
1452 }
1453
1454 if (arm->flag & ARM_MIRROR_EDIT) {
1455 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1456 union Value {
1457 int flag;
1458 void *p;
1459 } *val_p = (Value *)BLI_ghash_lookup_p(ebone_flag_orig, ebone);
1460 if (val_p && val_p->flag) {
1461 ebone->flag &= ~val_p->flag;
1462 }
1463 }
1464 }
1465 }
1466
1467 if (arm->flag & ARM_MIRROR_EDIT) {
1468 BLI_ghash_free(ebone_flag_orig, nullptr, nullptr);
1469 }
1470
1471 if (changed) {
1472 changed_multi = true;
1477 }
1478 }
1479
1480 if (!changed_multi) {
1481 return OPERATOR_CANCELLED;
1482 }
1483
1484 return OPERATOR_FINISHED;
1485}
1486
1488{
1489 /* identifiers */
1490 ot->name = "Dissolve Selected Bone(s)";
1491 ot->idname = "ARMATURE_OT_dissolve";
1492 ot->description = "Dissolve selected bones from the armature";
1493
1494 /* API callbacks. */
1497
1498 /* flags */
1499 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1500}
1501
1503
1504/* -------------------------------------------------------------------- */
1507
1509{
1510 const Scene *scene = CTX_data_scene(C);
1511 ViewLayer *view_layer = CTX_data_view_layer(C);
1512 const int invert = RNA_boolean_get(op->ptr, "unselected") ? BONE_SELECTED : 0;
1513
1514 /* cancel if nothing selected */
1515 if (CTX_DATA_COUNT(C, selected_bones) == 0) {
1516 return OPERATOR_CANCELLED;
1517 }
1518
1520 scene, view_layer, CTX_wm_view3d(C));
1521 for (Object *obedit : objects) {
1522 bArmature *arm = static_cast<bArmature *>(obedit->data);
1523 bool changed = false;
1524
1525 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1527 if ((ebone->flag & BONE_SELECTED) != invert) {
1528 ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
1529 ebone->flag |= BONE_HIDDEN_A;
1530 changed = true;
1531 }
1532 }
1533 }
1534
1535 if (!changed) {
1536 continue;
1537 }
1539
1542 }
1543 return OPERATOR_FINISHED;
1544}
1545
1547{
1548 /* identifiers */
1549 ot->name = "Hide Selected";
1550 ot->idname = "ARMATURE_OT_hide";
1551 ot->description = "Tag selected bones to not be visible in Edit Mode";
1552
1553 /* API callbacks. */
1554 ot->exec = armature_hide_exec;
1556
1557 /* flags */
1558 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1559
1560 /* props */
1562 ot->srna, "unselected", false, "Unselected", "Hide unselected rather than selected");
1563}
1564
1566
1567/* -------------------------------------------------------------------- */
1570
1572{
1573 const Scene *scene = CTX_data_scene(C);
1574 ViewLayer *view_layer = CTX_data_view_layer(C);
1575 const bool select = RNA_boolean_get(op->ptr, "select");
1577 scene, view_layer, CTX_wm_view3d(C));
1578 for (Object *obedit : objects) {
1579 bArmature *arm = static_cast<bArmature *>(obedit->data);
1580 bool changed = false;
1581
1582 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1583 if (ANIM_bonecoll_is_visible_editbone(arm, ebone)) {
1584 if (ebone->flag & BONE_HIDDEN_A) {
1585 if (!(ebone->flag & BONE_UNSELECTABLE)) {
1587 }
1588 ebone->flag &= ~BONE_HIDDEN_A;
1589 changed = true;
1590 }
1591 }
1592 }
1593
1594 if (changed) {
1596
1599 }
1600 }
1601 return OPERATOR_FINISHED;
1602}
1603
1605{
1606 /* identifiers */
1607 ot->name = "Reveal Hidden";
1608 ot->idname = "ARMATURE_OT_reveal";
1609 ot->description = "Reveal all bones hidden in Edit Mode";
1610
1611 /* API callbacks. */
1612 ot->exec = armature_reveal_exec;
1614
1615 /* flags */
1616 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1617
1618 RNA_def_boolean(ot->srna, "select", true, "Select", "");
1619}
1620
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)
Definition armature.cc:776
void mat3_to_vec_roll(const float mat[3][3], float r_vec[3], float *r_roll)
Definition armature.cc:2566
void vec_roll_to_mat3_normalized(const float nor[3], float roll, float r_mat[3][3])
Definition armature.cc:2592
#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
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:126
#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:1009
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1026
@ 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:639
#define C
Definition RandGen.cpp:29
@ ALERT_ICON_NONE
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
bool stop
Definition WM_types.hh:1016
#define ND_POSE
Definition WM_types.hh:455
#define ND_BONE_SELECT
Definition WM_types.hh:457
#define NC_OBJECT
Definition WM_types.hh:376
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)
#define fabsf(x)
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_editbone(const bArmature *armature, const EditBone *ebone)
Object * context_active_object(const bContext *C)
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]
EditBone * parent
EditBone * next
union EditBone::@061331032346151154065124154371375335047127225354 temp
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:4225
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:139