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
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/* -------------------------------------------------------------------- */
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];
147 INIT_MINMAX(min, max);
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
189/* -------------------------------------------------------------------- */
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 */
473
474 /* flags */
476
477 /* properties */
479 RNA_def_boolean(ot->srna, "axis_flip", false, "Flip Axis", "Negate the alignment axis");
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
557/* -------------------------------------------------------------------- */
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
628/* -------------------------------------------------------------------- */
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
888/* -------------------------------------------------------------------- */
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 */
1017}
1018
1021/* -------------------------------------------------------------------- */
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 */
1165}
1166
1169/* -------------------------------------------------------------------- */
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 */
1210
1211 /* flags */
1213}
1214
1217/* -------------------------------------------------------------------- */
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
1285static int armature_delete_selected_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
1286{
1287 if (RNA_boolean_get(op->ptr, "confirm")) {
1288 return WM_operator_confirm_ex(C,
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 */
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 */
1488}
1489
1492/* -------------------------------------------------------------------- */
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 */
1544
1545 /* flags */
1547
1548 /* props */
1550 ot->srna, "unselected", false, "Unselected", "Hide unselected rather than selected");
1551}
1552
1555/* -------------------------------------------------------------------- */
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 */
1602
1603 /* flags */
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.
#define RNA_ENUM_ITEM_HEADING(name, description)
Definition RNA_types.hh:522
@ 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
Return the length of the vector.
Definition btVector3.h:257
#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
union EditBone::@4 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:42
float viewinv[4][4]
struct EditBone * act_edbone
ListBase * edbo
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
PropertyRNA * prop
Definition WM_types.hh:1092
StructRNA * srna
Definition WM_types.hh:1080
struct ReportList * reports
struct PointerRNA * ptr
#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