Blender V5.0
armature_add.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_anim_types.h"
11#include "DNA_armature_types.h"
13#include "DNA_object_types.h"
14#include "DNA_scene_types.h"
15
16#include "MEM_guardedalloc.h"
17
18#include "BLI_listbase.h"
19#include "BLI_map.hh"
20#include "BLI_math_matrix.h"
21#include "BLI_math_rotation.h"
22#include "BLI_math_vector.h"
23#include "BLI_string.h"
24#include "BLI_string_utf8.h"
25#include "BLI_string_utils.hh"
26
27#include "BLT_translation.hh"
28
29#include "BKE_action.hh"
30#include "BKE_armature.hh"
31#include "BKE_constraint.h"
32#include "BKE_context.hh"
33#include "BKE_fcurve.hh"
34#include "BKE_idprop.hh"
35#include "BKE_layer.hh"
36#include "BKE_lib_id.hh"
37#include "BKE_object_types.hh"
38#include "BKE_report.hh"
39
40#include "ANIM_action.hh"
41
42#include "RNA_access.hh"
43#include "RNA_define.hh"
44
45#include "WM_api.hh"
46#include "WM_types.hh"
47
48#include "ED_armature.hh"
49#include "ED_outliner.hh"
50#include "ED_screen.hh"
51#include "ED_view3d.hh"
52
53#include "ANIM_armature.hh"
55
56#include "DEG_depsgraph.hh"
57
58#include "armature_intern.hh"
59
60using blender::Vector;
61
62/* *************** Adding stuff in editmode *************** */
63
65{
66 EditBone *bone = MEM_callocN<EditBone>("eBone");
67
68 STRNCPY_UTF8(bone->name, name);
69 ED_armature_ebone_unique_name(arm->edbo, bone->name, nullptr);
70
71 BLI_addtail(arm->edbo, bone);
72
73 bone->flag |= BONE_TIPSEL;
75 bone->weight = 1.0f;
76 bone->dist = 0.25f;
77 bone->xwidth = 0.1f;
78 bone->zwidth = 0.1f;
79 bone->rad_head = 0.10f;
80 bone->rad_tail = 0.05f;
81 bone->segments = 1;
82
83 /* Bendy-Bone parameters */
84 bone->roll1 = 0.0f;
85 bone->roll2 = 0.0f;
86 bone->curve_in_x = 0.0f;
87 bone->curve_in_z = 0.0f;
88 bone->curve_out_x = 0.0f;
89 bone->curve_out_z = 0.0f;
90 bone->ease1 = 1.0f;
91 bone->ease2 = 1.0f;
92
93 /* Prevent custom bone colors from having alpha zero.
94 * Part of the fix for issue #115434. */
95 bone->color.custom.solid[3] = 255;
96 bone->color.custom.select[3] = 255;
97 bone->color.custom.active[3] = 255;
98
99 copy_v3_fl(bone->scale_in, 1.0f);
100 copy_v3_fl(bone->scale_out, 1.0f);
101
102 return bone;
103}
104
106 const float length,
107 const bool view_aligned)
108{
109 bArmature *arm = static_cast<bArmature *>(obedit_arm->data);
110 EditBone *bone;
111
113
114 /* Create a bone */
115 bone = ED_armature_ebone_add(arm, DATA_("Bone"));
116
117 arm->act_edbone = bone;
118
119 zero_v3(bone->head);
120 zero_v3(bone->tail);
121
122 bone->tail[view_aligned ? 1 : 2] = length;
123
124 if (arm->runtime.active_collection) {
126 }
127
128 return bone;
129}
130
140{
141 bArmature *arm;
142 EditBone *ebone, *newbone, *flipbone;
143 float mat[3][3], imat[3][3];
144 int a, to_root = 0;
145 Object *obedit;
146 Scene *scene;
147
148 scene = CTX_data_scene(C);
149 obedit = CTX_data_edit_object(C);
150 arm = static_cast<bArmature *>(obedit->data);
151
152 /* find the active or selected bone */
153 for (ebone = static_cast<EditBone *>(arm->edbo->first); ebone; ebone = ebone->next) {
154 if (!blender::animrig::bone_is_visible(arm, ebone)) {
155 continue;
156 }
157 if (ebone->flag & BONE_TIPSEL || arm->act_edbone == ebone) {
158 break;
159 }
160 }
161
162 if (ebone == nullptr) {
163 for (ebone = static_cast<EditBone *>(arm->edbo->first); ebone; ebone = ebone->next) {
164 if (!blender::animrig::bone_is_visible(arm, ebone)) {
165 continue;
166 }
167 if (ebone->flag & BONE_ROOTSEL || arm->act_edbone == ebone) {
168 break;
169 }
170 }
171 if (ebone == nullptr) {
172 return OPERATOR_CANCELLED;
173 }
174
175 to_root = 1;
176 }
177
179
180 /* we re-use code for mirror editing... */
181 flipbone = nullptr;
182 if (arm->flag & ARM_MIRROR_EDIT) {
183 flipbone = ED_armature_ebone_get_mirrored(arm->edbo, ebone);
184 }
185
186 for (a = 0; a < 2; a++) {
187 if (a == 1) {
188 if (flipbone == nullptr) {
189 break;
190 }
191 std::swap(flipbone, ebone);
192 }
193
194 newbone = ED_armature_ebone_add(arm, ebone->name);
195 arm->act_edbone = newbone;
196
197 if (to_root) {
198 copy_v3_v3(newbone->head, ebone->head);
199 newbone->rad_head = ebone->rad_tail;
200 newbone->parent = ebone->parent;
201 }
202 else {
203 copy_v3_v3(newbone->head, ebone->tail);
204 newbone->rad_head = ebone->rad_tail;
205 newbone->parent = ebone;
206 newbone->flag |= BONE_CONNECTED;
207 }
208
209 const View3DCursor *curs = &scene->cursor;
210 copy_v3_v3(newbone->tail, curs->location);
211 sub_v3_v3v3(newbone->tail, newbone->tail, obedit->object_to_world().location());
212
213 if (a == 1) {
214 newbone->tail[0] = -newbone->tail[0];
215 }
216
217 copy_m3_m4(mat, obedit->object_to_world().ptr());
218 invert_m3_m3(imat, mat);
219 mul_m3_v3(imat, newbone->tail);
220
221 newbone->length = len_v3v3(newbone->head, newbone->tail);
222 newbone->rad_tail = newbone->length * 0.05f;
223 newbone->dist = newbone->length * 0.25f;
224 }
225
227
231
232 return OPERATOR_FINISHED;
233}
234
236 wmOperator *op,
237 const wmEvent *event)
238{
239 /* TODO: most of this code is copied from set3dcursor_invoke,
240 * it would be better to reuse code in set3dcursor_invoke */
241
242 /* temporarily change 3d cursor position */
243 Scene *scene;
244 ARegion *region;
245 View3D *v3d;
246 float tvec[3], oldcurs[3], mval_f[2];
247
248 scene = CTX_data_scene(C);
249 region = CTX_wm_region(C);
250 v3d = CTX_wm_view3d(C);
251
252 View3DCursor *cursor = &scene->cursor;
253
254 copy_v3_v3(oldcurs, cursor->location);
255
256 copy_v2fl_v2i(mval_f, event->mval);
257 ED_view3d_win_to_3d(v3d, region, cursor->location, mval_f, tvec);
258 copy_v3_v3(cursor->location, tvec);
259
260 /* extrude to the where new cursor is and store the operation result */
262
263 /* restore previous 3d cursor position */
264 copy_v3_v3(cursor->location, oldcurs);
265
266 /* Support dragging to move after extrude, see: #114282. */
267 if (retval & OPERATOR_FINISHED) {
268 retval |= OPERATOR_PASS_THROUGH;
269 }
271}
272
274{
275 /* identifiers */
276 ot->name = "Extrude to Cursor";
277 ot->idname = "ARMATURE_OT_click_extrude";
278 ot->description = "Create a new bone going from the last selected joint to the mouse position";
279
280 /* API callbacks. */
284
285 /* flags */
287
288 /* props */
289}
290
291EditBone *add_points_bone(Object *obedit, float head[3], float tail[3])
292{
293 EditBone *ebo;
294
295 ebo = ED_armature_ebone_add(static_cast<bArmature *>(obedit->data), DATA_("Bone"));
296
297 copy_v3_v3(ebo->head, head);
298 copy_v3_v3(ebo->tail, tail);
299
300 return ebo;
301}
302
303static void pre_edit_bone_duplicate(ListBase *editbones)
304{
305 /* clear temp */
307}
308
314 const bPose *pose,
316 bPoseChannel *pchan_src)
317{
318 bPoseChannel *pchan_dst = nullptr;
319 const char *name_src = pchan_src->name;
320 const blender::StringRefNull name_dst = name_map.lookup_default(name_src, "");
321 if (!name_dst.is_empty()) {
322 pchan_dst = BKE_pose_channel_find_name(pose, name_dst.c_str());
323 }
324
325 if (pchan_dst == nullptr) {
326 pchan_dst = pchan_src;
327 }
328
329 return pchan_dst;
330}
331
332static void post_edit_bone_duplicate(ListBase *editbones, Object *ob)
333{
334 if (ob->pose == nullptr) {
335 return;
336 }
337
340
342
343 LISTBASE_FOREACH (EditBone *, ebone_src, editbones) {
344 EditBone *ebone_dst = ebone_src->temp.ebone;
345 if (!ebone_dst) {
346 ebone_dst = ED_armature_ebone_get_mirrored(editbones, ebone_src);
347 }
348
349 if (ebone_dst) {
350 name_map.add_as(ebone_src->name, ebone_dst->name);
351 }
352 }
353
354 LISTBASE_FOREACH (EditBone *, ebone_src, editbones) {
355 EditBone *ebone_dst = ebone_src->temp.ebone;
356 if (!ebone_dst) {
357 continue;
358 }
359
360 bPoseChannel *pchan_src = BKE_pose_channel_find_name(ob->pose, ebone_src->name);
361 if (!pchan_src) {
362 continue;
363 }
364
365 bPoseChannel *pchan_dst = BKE_pose_channel_find_name(ob->pose, ebone_dst->name);
366 if (!pchan_dst) {
367 continue;
368 }
369
370 if (pchan_src->custom_tx) {
371 pchan_dst->custom_tx = pchan_duplicate_map(ob->pose, name_map, pchan_src->custom_tx);
372 }
373 if (pchan_src->bbone_prev) {
374 pchan_dst->bbone_prev = pchan_duplicate_map(ob->pose, name_map, pchan_src->bbone_prev);
375 }
376 if (pchan_src->bbone_next) {
377 pchan_dst->bbone_next = pchan_duplicate_map(ob->pose, name_map, pchan_src->bbone_next);
378 }
379 }
380}
381
383 Object *ob,
384 const bool lookup_mirror_subtarget)
385{
386 /* If an edit bone has been duplicated, lets update its constraints if the
387 * subtarget they point to has also been duplicated.
388 */
389 bPoseChannel *pchan = BKE_pose_channel_ensure(ob->pose, dup_bone->name);
390
391 if (!pchan) {
392 return;
393 }
394
395 EditBone *oldtarget, *newtarget;
396 ListBase *conlist = &pchan->constraints;
397 char name_flipped[MAX_ID_NAME - 2];
398 LISTBASE_FOREACH (bConstraint *, curcon, conlist) {
399 /* does this constraint have a subtarget in
400 * this armature?
401 */
402 ListBase targets = {nullptr, nullptr};
403
404 if (!BKE_constraint_targets_get(curcon, &targets)) {
405 continue;
406 }
407 LISTBASE_FOREACH (bConstraintTarget *, ct, &targets) {
408 if (!ct->tar || !ct->subtarget[0]) {
409 continue;
410 }
411 Object *target_ob = ct->tar;
412 if (target_ob->type != OB_ARMATURE || !target_ob->data) {
413 /* Can only mirror armature. */
414 continue;
415 }
416 bArmature *target_armature = static_cast<bArmature *>(target_ob->data);
417 /* Was the subtarget bone duplicated too? If
418 * so, update the constraint to point at the
419 * duplicate of the old subtarget.
420 */
421
422 /* TODO: support updating sub-targets for multi-object edit mode.
423 * This requires all objects bones to be duplicated before this runs. */
424 oldtarget = (ob == target_ob) ?
425 ED_armature_ebone_find_name(target_armature->edbo, ct->subtarget) :
426 nullptr;
427 if (oldtarget && oldtarget->temp.ebone) {
428 newtarget = oldtarget->temp.ebone;
429 STRNCPY_UTF8(ct->subtarget, newtarget->name);
430 }
431 else if (lookup_mirror_subtarget) {
432 BLI_string_flip_side_name(name_flipped, ct->subtarget, false, sizeof(name_flipped));
433 if (bPoseChannel *flipped_bone = BKE_pose_channel_find_name(ct->tar->pose, name_flipped)) {
434 STRNCPY_UTF8(ct->subtarget, flipped_bone->name);
435 }
436 }
437 }
438 BKE_constraint_targets_flush(curcon, &targets, false);
439 }
440}
441
443 EditBone *dup_bone, EditBone *orig_bone, Object *ob, bPoseChannel *pchan, bConstraint *curcon)
444{
445 bActionConstraint *act_con = static_cast<bActionConstraint *>(curcon->data);
446
447 float mat[4][4];
448
449 bConstraintOb cob{};
450 cob.depsgraph = nullptr;
451 cob.scene = nullptr;
452 cob.ob = ob;
453 cob.pchan = pchan;
455
456 unit_m4(mat);
457 bPoseChannel *target_pchan = BKE_pose_channel_find_name(ob->pose, act_con->subtarget);
459 ob, target_pchan, &cob, mat, curcon->tarspace, CONSTRAINT_SPACE_LOCAL, false);
460
461 float max_axis_val = 0;
462 int max_axis = 0;
463 /* Which axis represents X now. IE, which axis defines the mirror plane. */
464 for (int i = 0; i < 3; i++) {
465 float cur_val = fabsf(mat[0][i]);
466 if (cur_val > max_axis_val) {
467 max_axis = i;
468 max_axis_val = cur_val;
469 }
470 }
471
472 /* data->type is mapped as follows for backwards compatibility:
473 * 00,01,02 - rotation (it used to be like this)
474 * 10,11,12 - scaling
475 * 20,21,22 - location
476 */
477 /* Mirror the target range */
478 if (act_con->type < 10 && act_con->type != max_axis) {
479 /* Y or Z rotation */
480 act_con->min = -act_con->min;
481 act_con->max = -act_con->max;
482 }
483 else if (act_con->type == max_axis + 10) {
484 /* X scaling */
485 }
486 else if (act_con->type == max_axis + 20) {
487 /* X location */
488 float imat[4][4];
489
490 invert_m4_m4(imat, mat);
491
492 float min_vec[3], max_vec[3];
493
494 zero_v3(min_vec);
495 zero_v3(max_vec);
496
497 min_vec[0] = act_con->min;
498 max_vec[0] = act_con->max;
499
500 /* convert values into local object space */
501 mul_m4_v3(mat, min_vec);
502 mul_m4_v3(mat, max_vec);
503
504 min_vec[0] *= -1;
505 max_vec[0] *= -1;
506
507 /* convert back to the settings space */
508 mul_m4_v3(imat, min_vec);
509 mul_m4_v3(imat, max_vec);
510
511 act_con->min = min_vec[0];
512 act_con->max = max_vec[0];
513 }
514
515 /* See if there is any channels that uses this bone */
516 bAction *act = (bAction *)act_con->act;
517 if (act) {
518 blender::animrig::Action &action = act->wrap();
520 action, act_con->action_slot_handle);
521
522 /* Create a copy and mirror the animation */
523 auto bone_name_filter = [&](const FCurve &fcurve) -> bool {
525 fcurve, "pose.bones[", orig_bone->name);
526 };
528 act, act_con->action_slot_handle, bone_name_filter);
529 for (const FCurve *old_curve : fcurves) {
530 FCurve *new_curve = BKE_fcurve_copy(old_curve);
531 char *old_path = new_curve->rna_path;
532
533 new_curve->rna_path = BLI_string_replaceN(old_path, orig_bone->name, dup_bone->name);
534 MEM_freeN(old_path);
535
536 /* FIXME: deal with the case where this F-Curve already exists. */
537
538 /* Flip the animation */
539 int i;
540 BezTriple *bezt;
541 for (i = 0, bezt = new_curve->bezt; i < new_curve->totvert; i++, bezt++) {
542 const size_t slength = strlen(new_curve->rna_path);
543 bool flip = false;
544 if (BLI_strn_endswith(new_curve->rna_path, "location", slength) &&
545 new_curve->array_index == 0)
546 {
547 flip = true;
548 }
549 else if (BLI_strn_endswith(new_curve->rna_path, "rotation_quaternion", slength) &&
550 ELEM(new_curve->array_index, 2, 3))
551 {
552 flip = true;
553 }
554 else if (BLI_strn_endswith(new_curve->rna_path, "rotation_euler", slength) &&
555 ELEM(new_curve->array_index, 1, 2))
556 {
557 flip = true;
558 }
559 else if (BLI_strn_endswith(new_curve->rna_path, "rotation_axis_angle", slength) &&
560 ELEM(new_curve->array_index, 2, 3))
561 {
562 flip = true;
563 }
564
565 if (flip) {
566 bezt->vec[0][1] *= -1;
567 bezt->vec[1][1] *= -1;
568 bezt->vec[2][1] *= -1;
569 }
570 }
571
572 if (action.is_action_legacy()) {
573 /* Make sure that a action group name for the new bone exists */
574 bActionGroup *agrp = BKE_action_group_find_name(act, dup_bone->name);
575 if (agrp == nullptr) {
576 agrp = action_groups_add_new(act, dup_bone->name);
577 }
578 BLI_assert(agrp != nullptr);
579 action_groups_add_channel(act, agrp, new_curve);
580 continue;
581 }
582
583 BLI_assert_msg(cbag, "If there are F-Curves for this slot, there should be a channelbag");
584 bActionGroup &agrp = cbag->channel_group_ensure(dup_bone->name);
585 cbag->fcurve_append(*new_curve);
586 cbag->fcurve_assign_to_channel_group(*new_curve, agrp);
587 }
588 }
589
590 /* Make depsgraph aware of our changes. */
592}
593
595{
596 /* IK constraint */
597 bKinematicConstraint *ik = static_cast<bKinematicConstraint *>(curcon->data);
598 ik->poleangle = -M_PI - ik->poleangle;
599 /* Wrap the angle to the +/-180.0f range (default soft limit of the input boxes). */
601}
602
604 bPoseChannel *pchan,
605 bConstraint *curcon)
606{
607 /* This code assumes that bRotLimitConstraint and bLocLimitConstraint have the same fields in
608 * the same memory locations. */
609 bRotLimitConstraint *limit = static_cast<bRotLimitConstraint *>(curcon->data);
610 float local_mat[4][4], imat[4][4];
611
612 float min_vec[3], max_vec[3];
613
614 min_vec[0] = limit->xmin;
615 min_vec[1] = limit->ymin;
616 min_vec[2] = limit->zmin;
617
618 max_vec[0] = limit->xmax;
619 max_vec[1] = limit->ymax;
620 max_vec[2] = limit->zmax;
621
622 unit_m4(local_mat);
623
624 bConstraintOb cob{};
625 cob.depsgraph = nullptr;
626 cob.scene = nullptr;
627 cob.ob = ob;
628 cob.pchan = pchan;
630
632 ob, pchan, &cob, local_mat, curcon->ownspace, CONSTRAINT_SPACE_LOCAL, false);
633
634 if (curcon->type == CONSTRAINT_TYPE_ROTLIMIT) {
635 /* Zero out any location translation */
636 local_mat[3][0] = local_mat[3][1] = local_mat[3][2] = 0;
637 }
638
639 invert_m4_m4(imat, local_mat);
640 /* convert values into local object space */
641 mul_m4_v3(local_mat, min_vec);
642 mul_m4_v3(local_mat, max_vec);
643
644 if (curcon->type == CONSTRAINT_TYPE_ROTLIMIT) {
645 float min_copy[3];
646
647 copy_v3_v3(min_copy, min_vec);
648
649 min_vec[1] = max_vec[1] * -1;
650 min_vec[2] = max_vec[2] * -1;
651
652 max_vec[1] = min_copy[1] * -1;
653 max_vec[2] = min_copy[2] * -1;
654 }
655 else {
656 float min_x_copy = min_vec[0];
657
658 min_vec[0] = max_vec[0] * -1;
659 max_vec[0] = min_x_copy * -1;
660
661 /* Also flip the enabled axis check-boxes accordingly. */
662 const bool use_max_x = (limit->flag & LIMIT_XMAX);
663 const bool use_min_x = (limit->flag & LIMIT_XMIN);
664 limit->flag |= use_max_x ? LIMIT_XMIN : 0;
665 limit->flag &= (use_max_x && !use_min_x) ? ~LIMIT_XMAX : limit->flag;
666 limit->flag |= use_min_x ? LIMIT_XMAX : 0;
667 limit->flag &= (use_min_x && !use_max_x) ? ~LIMIT_XMIN : limit->flag;
668 }
669
670 /* convert back to the settings space */
671 mul_m4_v3(imat, min_vec);
672 mul_m4_v3(imat, max_vec);
673
674 limit->xmin = min_vec[0];
675 limit->ymin = min_vec[1];
676 limit->zmin = min_vec[2];
677
678 limit->xmax = max_vec[0];
679 limit->ymax = max_vec[1];
680 limit->zmax = max_vec[2];
681}
682
684 bPoseChannel *pchan,
685 bConstraint *curcon)
686{
687 bTransformConstraint *trans = static_cast<bTransformConstraint *>(curcon->data);
688
689 float target_mat[4][4], own_mat[4][4], imat[4][4];
690
691 bConstraintOb cob{};
692 cob.depsgraph = nullptr;
693 cob.scene = nullptr;
694 cob.ob = ob;
695 cob.pchan = pchan;
697
698 unit_m4(own_mat);
700 ob, pchan, &cob, own_mat, curcon->ownspace, CONSTRAINT_SPACE_LOCAL, false);
701
702 /* ###Source map mirroring### */
703 float old_min, old_max;
704
705 /* Source location */
706 invert_m4_m4(imat, own_mat);
707
708 /* convert values into local object space */
709 mul_m4_v3(own_mat, trans->from_min);
710 mul_m4_v3(own_mat, trans->from_max);
711
712 old_min = trans->from_min[0];
713 old_max = trans->from_max[0];
714
715 trans->from_min[0] = -old_max;
716 trans->from_max[0] = -old_min;
717
718 /* convert back to the settings space */
719 mul_m4_v3(imat, trans->from_min);
720 mul_m4_v3(imat, trans->from_max);
721
722 /* Source rotation */
723
724 /* Zero out any location translation */
725 own_mat[3][0] = own_mat[3][1] = own_mat[3][2] = 0;
726
727 invert_m4_m4(imat, own_mat);
728
729 /* convert values into local object space */
730 mul_m4_v3(own_mat, trans->from_min_rot);
731 mul_m4_v3(own_mat, trans->from_max_rot);
732
733 old_min = trans->from_min_rot[1];
734 old_max = trans->from_max_rot[1];
735
736 trans->from_min_rot[1] = old_max * -1;
737 trans->from_max_rot[1] = old_min * -1;
738
739 old_min = trans->from_min_rot[2];
740 old_max = trans->from_max_rot[2];
741
742 trans->from_min_rot[2] = old_max * -1;
743 trans->from_max_rot[2] = old_min * -1;
744
745 /* convert back to the settings space */
746 mul_m4_v3(imat, trans->from_min_rot);
747 mul_m4_v3(imat, trans->from_max_rot);
748
749 /* Source scale does not require any mirroring */
750
751 /* ###Destination map mirroring### */
752 float temp_vec[3];
753 float imat_rot[4][4];
754
755 bPoseChannel *target_pchan = BKE_pose_channel_find_name(ob->pose, trans->subtarget);
756 unit_m4(target_mat);
758 ob, target_pchan, &cob, target_mat, curcon->tarspace, CONSTRAINT_SPACE_LOCAL, false);
759
760 invert_m4_m4(imat, target_mat);
761 /* convert values into local object space */
762 mul_m4_v3(target_mat, trans->to_min);
763 mul_m4_v3(target_mat, trans->to_max);
764 mul_m4_v3(target_mat, trans->to_min_scale);
765 mul_m4_v3(target_mat, trans->to_max_scale);
766
767 /* Zero out any location translation */
768 target_mat[3][0] = target_mat[3][1] = target_mat[3][2] = 0;
769 invert_m4_m4(imat_rot, target_mat);
770
771 mul_m4_v3(target_mat, trans->to_min_rot);
772 mul_m4_v3(target_mat, trans->to_max_rot);
773
774 /* TODO(sebpa): This does not support euler order, but doing so will make this way more complex.
775 * For now we have decided to not support all corner cases and advanced setups. */
776
777 /* Helper variables to denote the axis in trans->map */
778 const char X = 0;
779 const char Y = 1;
780 const char Z = 2;
781
782 switch (trans->to) {
783 case TRANS_SCALE:
784 copy_v3_v3(temp_vec, trans->to_max_scale);
785
786 for (int i = 0; i < 3; i++) {
787 if ((trans->from == TRANS_LOCATION && trans->map[i] == X) ||
788 (trans->from == TRANS_ROTATION && trans->map[i] != X))
789 {
790 /* X Loc to X/Y/Z Scale: Min/Max Flipped */
791 /* Y Rot to X/Y/Z Scale: Min/Max Flipped */
792 /* Z Rot to X/Y/Z Scale: Min/Max Flipped */
793 trans->to_max_scale[i] = trans->to_min_scale[i];
794 trans->to_min_scale[i] = temp_vec[i];
795 }
796 }
797 break;
798 case TRANS_LOCATION:
799 /* Invert the X location */
800 trans->to_min[0] *= -1;
801 trans->to_max[0] *= -1;
802
803 copy_v3_v3(temp_vec, trans->to_max);
804
805 for (int i = 0; i < 3; i++) {
806 if ((trans->from == TRANS_LOCATION && trans->map[i] == X) ||
807 (trans->from == TRANS_ROTATION && trans->map[i] != X))
808 {
809 /* X Loc to X/Y/Z Loc: Min/Max Flipped (and Inverted)
810 * Y Rot to X/Y/Z Loc: Min/Max Flipped
811 * Z Rot to X/Y/Z Loc: Min/Max Flipped */
812 trans->to_max[i] = trans->to_min[i];
813 trans->to_min[i] = temp_vec[i];
814 }
815 }
816 break;
817 case TRANS_ROTATION:
818 /* Invert the Z rotation */
819 trans->to_min_rot[2] *= -1;
820 trans->to_max_rot[2] *= -1;
821
822 if ((trans->from == TRANS_LOCATION && trans->map[1] != X) ||
823 (trans->from == TRANS_ROTATION && trans->map[1] != Y) || trans->from == TRANS_SCALE)
824 {
825 /* Invert the Y rotation */
826 trans->to_min_rot[1] *= -1;
827 trans->to_max_rot[1] *= -1;
828 }
829
830 copy_v3_v3(temp_vec, trans->to_max_rot);
831
832 for (int i = 0; i < 3; i++) {
833 if ((trans->from == TRANS_LOCATION && trans->map[i] == X && i != 1) ||
834 (trans->from == TRANS_ROTATION && trans->map[i] == Y && i != 1) ||
835 (trans->from == TRANS_ROTATION && trans->map[i] == Z))
836 {
837 /* X Loc to X/Z Rot: Flipped
838 * Y Rot to X/Z Rot: Flipped
839 * Z Rot to X/Y/Z rot: Flipped */
840 trans->to_max_rot[i] = trans->to_min_rot[i];
841 trans->to_min_rot[i] = temp_vec[i];
842 }
843 }
844
845 if (trans->from == TRANS_ROTATION && trans->map[1] == Y) {
846 /* Y Rot to Y Rot: Flip and invert */
847 trans->to_max_rot[1] = -trans->to_min_rot[1];
848 trans->to_min_rot[1] = -temp_vec[1];
849 }
850
851 break;
852 }
853 /* convert back to the settings space */
854 mul_m4_v3(imat, trans->to_min);
855 mul_m4_v3(imat, trans->to_max);
856 mul_m4_v3(imat_rot, trans->to_min_rot);
857 mul_m4_v3(imat_rot, trans->to_max_rot);
858 mul_m4_v3(imat, trans->to_min_scale);
859 mul_m4_v3(imat, trans->to_max_scale);
860}
861
862static void track_axis_x_swap(int &value)
863{
864 /* Swap track axis X <> -X. */
865 if (value == TRACK_X) {
866 value = TRACK_nX;
867 }
868 else if (value == TRACK_nX) {
869 value = TRACK_X;
870 }
871}
872
873static void track_axis_x_swap(char &value)
874{
875 /* Swap track axis X <> -X. */
876 if (value == TRACK_X) {
877 value = TRACK_nX;
878 }
879 else if (value == TRACK_nX) {
880 value = TRACK_X;
881 }
882}
883
885{
886 bTrackToConstraint *data = static_cast<bTrackToConstraint *>(curcon->data);
887 track_axis_x_swap(data->reserved1);
888}
889
891{
892 bLockTrackConstraint *data = static_cast<bLockTrackConstraint *>(curcon->data);
893 track_axis_x_swap(data->trackflag);
894}
895
897{
898 bDampTrackConstraint *data = static_cast<bDampTrackConstraint *>(curcon->data);
899 track_axis_x_swap(data->trackflag);
900}
901
907
909 EditBone *orig_bone,
910 Object *ob)
911{
912 /* If an edit bone has been duplicated, lets update its constraints if the
913 * subtarget they point to has also been duplicated.
914 */
915 bPoseChannel *pchan;
916 ListBase *conlist;
917
918 if ((pchan = BKE_pose_channel_ensure(ob->pose, dup_bone->name)) == nullptr ||
919 (conlist = &pchan->constraints) == nullptr)
920 {
921 return;
922 }
923
924 LISTBASE_FOREACH (bConstraint *, curcon, conlist) {
925 switch (curcon->type) {
927 update_duplicate_action_constraint_settings(dup_bone, orig_bone, ob, pchan, curcon);
928 break;
931 break;
935 break;
938 break;
941 break;
944 break;
947 break;
950 break;
951 }
952 }
953}
954
956{
957 if (ob->pose == nullptr) {
958 return;
959 }
960 bPoseChannel *pchan;
961 pchan = BKE_pose_channel_ensure(ob->pose, dup_bone->name);
962
963 if (pchan->custom != nullptr) {
964 Main *bmain = CTX_data_main(C);
965 char name_flip[MAX_ID_NAME - 2];
966
967 /* Invert the X location */
968 pchan->custom_translation[0] *= -1;
969 /* Invert the Y rotation */
970 pchan->custom_rotation_euler[1] *= -1;
971 /* Invert the Z rotation */
972 pchan->custom_rotation_euler[2] *= -1;
973
974 /* Skip the first two chars in the object name as those are used to store object type */
975 BLI_string_flip_side_name(name_flip, pchan->custom->id.name + 2, false, sizeof(name_flip));
976 Object *shape_ob = reinterpret_cast<Object *>(BKE_libblock_find_name(bmain, ID_OB, name_flip));
977
978 /* If name_flip doesn't exist, BKE_libblock_find_name() returns pchan->custom (best match) */
979 shape_ob = shape_ob == pchan->custom ? nullptr : shape_ob;
980
981 if (shape_ob != nullptr) {
982 /* A flipped shape object exists, use it! */
983 pchan->custom = shape_ob;
984 }
985 else {
986 /* Flip shape */
987 pchan->custom_scale_xyz[0] *= -1;
988 }
989 }
990}
991
992/* Properties should be added on a case by case basis whenever needed to avoid mirroring things
993 * that shouldn't be mirrored. */
994static void mirror_pose_bone(Object &ob, EditBone &ebone)
995{
996 bPoseChannel *pose_bone = BKE_pose_channel_find_name(ob.pose, ebone.name);
997 BLI_assert(pose_bone);
998 float limit_min = pose_bone->limitmin[2];
999 pose_bone->limitmin[2] = -pose_bone->limitmax[2];
1000 pose_bone->limitmax[2] = -limit_min;
1001}
1002
1004 EditBone &source_bone,
1005 EditBone &target_bone)
1006{
1007 BLI_assert_msg(armature.edbo != nullptr, "Expecting the armature to be in edit mode");
1008 char name_flip[64];
1009 /* Avoiding modification of the ListBase in the iteration. */
1010 blender::Vector<BoneCollection *> unassign_collections;
1011 blender::Vector<BoneCollection *> assign_collections;
1012
1013 /* Find all collections from source_bone that can be flipped. */
1014 LISTBASE_FOREACH (BoneCollectionReference *, collection_reference, &source_bone.bone_collections)
1015 {
1016 BoneCollection *collection = collection_reference->bcoll;
1017 BLI_string_flip_side_name(name_flip, collection->name, false, sizeof(name_flip));
1018 if (STREQ(name_flip, collection->name)) {
1019 /* Name flipping failed. */
1020 continue;
1021 }
1022 BoneCollection *flipped_collection = ANIM_armature_bonecoll_get_by_name(&armature, name_flip);
1023 if (!flipped_collection) {
1024 const int bcoll_index = blender::animrig::armature_bonecoll_find_index(&armature,
1025 collection);
1026 const int parent_index = blender::animrig::armature_bonecoll_find_parent_index(&armature,
1027 bcoll_index);
1028 flipped_collection = ANIM_armature_bonecoll_new(&armature, name_flip, parent_index);
1029 }
1030 BLI_assert(flipped_collection != nullptr);
1031 unassign_collections.append(collection);
1032 assign_collections.append(flipped_collection);
1033 }
1034
1035 /* The target_bone might not be in unassign_collections anymore, or might already be in
1036 * assign_collections. The assign functions will just do nothing in those cases. */
1037 for (BoneCollection *collection : unassign_collections) {
1038 ANIM_armature_bonecoll_unassign_editbone(collection, &target_bone);
1039 }
1040
1041 for (BoneCollection *collection : assign_collections) {
1042 ANIM_armature_bonecoll_assign_editbone(collection, &target_bone);
1043 }
1044}
1045
1046static void copy_pchan(EditBone *src_bone, EditBone *dst_bone, Object *src_ob, Object *dst_ob)
1047{
1048 /* copy the ID property */
1049 if (src_bone->prop) {
1050 dst_bone->prop = IDP_CopyProperty(src_bone->prop);
1051 }
1052 if (src_bone->system_properties) {
1054 }
1055
1056 /* Lets duplicate the list of constraints that the
1057 * current bone has.
1058 */
1059 if (src_ob->pose) {
1060 bPoseChannel *chanold, *channew;
1061
1062 chanold = BKE_pose_channel_ensure(src_ob->pose, src_bone->name);
1063 if (chanold) {
1064 /* WARNING: this creates a new pose-channel, but there will not be an attached bone
1065 * yet as the new bones created here are still 'EditBones' not 'Bones'.
1066 */
1067 channew = BKE_pose_channel_ensure(dst_ob->pose, dst_bone->name);
1068
1069 if (channew) {
1070 BKE_pose_channel_copy_data(channew, chanold);
1071 }
1072 }
1073 }
1074}
1075
1076void ED_armature_ebone_copy(EditBone *dest, const EditBone *source)
1077{
1078 memcpy(dest, source, sizeof(*dest));
1080}
1081
1083 EditBone *cur_bone, const char *name, ListBase *editbones, Object *src_ob, Object *dst_ob)
1084{
1085 EditBone *e_bone = MEM_mallocN<EditBone>("addup_editbone");
1086
1087 /* Copy data from old bone to new bone */
1088 ED_armature_ebone_copy(e_bone, cur_bone);
1089
1090 cur_bone->temp.ebone = e_bone;
1091 e_bone->temp.ebone = cur_bone;
1092
1093 if (name != nullptr) {
1094 STRNCPY_UTF8(e_bone->name, name);
1095 }
1096
1097 ED_armature_ebone_unique_name(editbones, e_bone->name, nullptr);
1098 BLI_addtail(editbones, e_bone);
1099
1100 copy_pchan(cur_bone, e_bone, src_ob, dst_ob);
1101
1102 return e_bone;
1103}
1104
1105EditBone *duplicateEditBone(EditBone *cur_bone, const char *name, ListBase *editbones, Object *ob)
1106{
1107 return duplicateEditBoneObjects(cur_bone, name, editbones, ob, ob);
1108}
1109
1111{
1112 const Scene *scene = CTX_data_scene(C);
1113 ViewLayer *view_layer = CTX_data_view_layer(C);
1114 const bool do_flip_names = RNA_boolean_get(op->ptr, "do_flip_names");
1115
1116 /* cancel if nothing selected */
1117 if (CTX_DATA_COUNT(C, selected_bones) == 0) {
1118 return OPERATOR_CANCELLED;
1119 }
1120
1122 scene, view_layer, CTX_wm_view3d(C));
1123 for (Object *ob : objects) {
1124 EditBone *ebone_iter;
1125 /* The beginning of the duplicated bones in the edbo list */
1126 EditBone *ebone_first_dupe = nullptr;
1127
1128 bArmature *arm = static_cast<bArmature *>(ob->data);
1129
1130 ED_armature_edit_sync_selection(arm->edbo); /* XXX why is this needed? */
1131
1133
1134 /* Select mirrored bones */
1135 if (arm->flag & ARM_MIRROR_EDIT) {
1136 LISTBASE_FOREACH (EditBone *, ebone_iter, arm->edbo) {
1137 if (blender::animrig::bone_is_selected(arm, ebone_iter)) {
1138 EditBone *ebone;
1139
1140 ebone = ED_armature_ebone_get_mirrored(arm->edbo, ebone_iter);
1141 if (ebone) {
1142 ebone->flag |= BONE_SELECTED;
1143 }
1144 }
1145 }
1146 }
1147
1148 /* Find the selected bones and duplicate them as needed */
1149 for (ebone_iter = static_cast<EditBone *>(arm->edbo->first);
1150 ebone_iter && ebone_iter != ebone_first_dupe;
1151 ebone_iter = ebone_iter->next)
1152 {
1153 if (blender::animrig::bone_is_selected(arm, ebone_iter)) {
1154 EditBone *ebone;
1155 char new_bone_name_buff[MAXBONENAME];
1156 const char *new_bone_name = ebone_iter->name;
1157
1158 if (do_flip_names) {
1160 new_bone_name_buff, ebone_iter->name, false, sizeof(new_bone_name_buff));
1161
1162 /* Only use flipped name if not yet in use. Otherwise we'd get again inconsistent
1163 * namings (different numbers), better keep default behavior in this case. */
1164 if (ED_armature_ebone_find_name(arm->edbo, new_bone_name_buff) == nullptr) {
1165 new_bone_name = new_bone_name_buff;
1166 }
1167 }
1168
1169 ebone = duplicateEditBone(ebone_iter, new_bone_name, arm->edbo, ob);
1170
1171 if (!ebone_first_dupe) {
1172 ebone_first_dupe = ebone;
1173 }
1174 }
1175 }
1176
1177 /* Run though the list and fix the pointers */
1178 for (ebone_iter = static_cast<EditBone *>(arm->edbo->first);
1179 ebone_iter && ebone_iter != ebone_first_dupe;
1180 ebone_iter = ebone_iter->next)
1181 {
1182 if (blender::animrig::bone_is_selected(arm, ebone_iter)) {
1183 EditBone *ebone = ebone_iter->temp.ebone;
1184
1185 if (!ebone_iter->parent) {
1186 /* If this bone has no parent,
1187 * Set the duplicate->parent to nullptr
1188 */
1189 ebone->parent = nullptr;
1190 }
1191 else if (ebone_iter->parent->temp.ebone) {
1192 /* If this bone has a parent that was duplicated,
1193 * Set the duplicate->parent to the cur_bone->parent->temp
1194 */
1195 ebone->parent = ebone_iter->parent->temp.ebone;
1196 }
1197 else {
1198 /* If this bone has a parent that IS not selected,
1199 * Set the duplicate->parent to the cur_bone->parent
1200 */
1201 ebone->parent = ebone_iter->parent;
1202 ebone->flag &= ~BONE_CONNECTED;
1203 }
1204
1205 /* Update custom handle links. */
1206 if (ebone_iter->bbone_prev && ebone_iter->bbone_prev->temp.ebone) {
1207 ebone->bbone_prev = ebone_iter->bbone_prev->temp.ebone;
1208 }
1209 if (ebone_iter->bbone_next && ebone_iter->bbone_next->temp.ebone) {
1210 ebone->bbone_next = ebone_iter->bbone_next->temp.ebone;
1211 }
1212
1213 /* Lets try to fix any constraint sub-targets that might have been duplicated. */
1214 update_duplicate_subtarget(ebone, ob, false);
1215 }
1216 }
1217
1218 /* correct the active bone */
1219 if (arm->act_edbone && arm->act_edbone->temp.ebone) {
1220 arm->act_edbone = arm->act_edbone->temp.ebone;
1221 }
1222
1223 /* Deselect the old bones and select the new ones */
1224 for (ebone_iter = static_cast<EditBone *>(arm->edbo->first);
1225 ebone_iter && ebone_iter != ebone_first_dupe;
1226 ebone_iter = ebone_iter->next)
1227 {
1228 if (blender::animrig::bone_is_visible(arm, ebone_iter)) {
1229 ebone_iter->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
1230 }
1231 }
1232
1234
1237 }
1238
1240
1241 return OPERATOR_FINISHED;
1242}
1243
1245{
1246 /* identifiers */
1247 ot->name = "Duplicate Selected Bone(s)";
1248 ot->idname = "ARMATURE_OT_duplicate";
1249 ot->description = "Make copies of the selected bones within the same armature";
1250
1251 /* API callbacks. */
1254
1255 /* flags */
1256 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1257
1259 ot->srna,
1260 "do_flip_names",
1261 false,
1262 "Flip Names",
1263 "Try to flip names of the bones, if possible, instead of adding a number extension");
1264}
1265
1266/* Get the duplicated or existing mirrored copy of the bone. */
1268{
1269 if (bone == nullptr) {
1270 return nullptr;
1271 }
1272 if (bone->temp.ebone != nullptr) {
1273 return bone->temp.ebone;
1274 }
1275
1276 EditBone *mirror = ED_armature_ebone_get_mirrored(arm->edbo, bone);
1277 return (mirror != nullptr) ? mirror : bone;
1278}
1279
1285{
1286 const Scene *scene = CTX_data_scene(C);
1287 ViewLayer *view_layer = CTX_data_view_layer(C);
1288 const int direction = RNA_enum_get(op->ptr, "direction");
1289 const bool copy_bone_colors = RNA_boolean_get(op->ptr, "copy_bone_colors");
1290 const int axis = 0;
1291
1292 /* cancel if nothing selected */
1293 if (CTX_DATA_COUNT(C, selected_bones) == 0) {
1294 return OPERATOR_CANCELLED;
1295 }
1296
1298 scene, view_layer, CTX_wm_view3d(C));
1299 for (Object *obedit : objects) {
1300 EditBone *ebone_iter;
1301 /* The beginning of the duplicated mirrored bones in the edbo list */
1302 EditBone *ebone_first_dupe = nullptr;
1303
1304 bArmature *arm = static_cast<bArmature *>(obedit->data);
1305
1306 ED_armature_edit_sync_selection(arm->edbo); /* XXX why is this needed? */
1307
1309
1310 /* Deselect ebones depending on input axis and direction.
1311 * A symmetrizable selection contains selected ebones of the input direction
1312 * and unique selected bones with an unique flippable name.
1313 *
1314 * Storing temp pointers to mirrored unselected ebones. */
1315 LISTBASE_FOREACH (EditBone *, ebone_iter, arm->edbo) {
1316 if (!(blender::animrig::bone_is_visible(arm, ebone_iter) &&
1317 (ebone_iter->flag & BONE_SELECTED)))
1318 {
1319 /* Skipping invisible selected bones. */
1320 continue;
1321 }
1322
1323 char name_flip[MAXBONENAME];
1324 if (ebone_iter == nullptr) {
1325 continue;
1326 }
1327
1328 BLI_string_flip_side_name(name_flip, ebone_iter->name, false, sizeof(name_flip));
1329
1330 if (STREQ(name_flip, ebone_iter->name)) {
1331 /* Skipping ebones without flippable as they don't have the potential to be mirrored. */
1332 ebone_iter->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
1333 continue;
1334 }
1335
1336 EditBone *ebone = ED_armature_ebone_find_name(arm->edbo, name_flip);
1337
1338 if (!ebone) {
1339 /* The ebone_iter is unique and mirror-able. */
1340 continue;
1341 }
1342
1343 if (ebone->flag & BONE_SELECTED) {
1344 /* The mirrored ebone and the ebone_iter are selected.
1345 * Deselect based on the input direction and axis. */
1346 float axis_delta;
1347
1348 axis_delta = ebone->head[axis] - ebone_iter->head[axis];
1349 if (axis_delta == 0.0f) {
1350 /* The ebone heads are overlapping. */
1351 axis_delta = ebone->tail[axis] - ebone_iter->tail[axis];
1352
1353 if (axis_delta == 0.0f) {
1354 /* Both mirrored bones point to each other and overlap exactly.
1355 * In this case there's no well defined solution, so de-select both and skip. */
1356 ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
1357 ebone_iter->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
1358 continue;
1359 }
1360 }
1361
1362 /* Deselect depending on direction. */
1363 if (((axis_delta < 0.0f) ? -1 : 1) == direction) {
1364 /* Don't store temp ptr if the iter_bone gets deselected.
1365 * In this case, the ebone.temp should point to the ebone_iter. */
1366 ebone_iter->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
1367 continue;
1368 }
1369
1370 ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
1371 }
1372
1373 /* Set temp pointer to mirrored ebones */
1374 ebone_iter->temp.ebone = ebone;
1375 }
1376
1377 /* Find the selected bones and duplicate them as needed, with mirrored name. */
1378 for (ebone_iter = static_cast<EditBone *>(arm->edbo->first);
1379 ebone_iter && ebone_iter != ebone_first_dupe;
1380 ebone_iter = ebone_iter->next)
1381 {
1382 if (blender::animrig::bone_is_selected(arm, ebone_iter)) {
1383 if (ebone_iter->temp.ebone != nullptr) {
1384 /* This will be set if the mirror bone already exists (no need to make a new one)
1385 * but we do need to make sure that the 'pchan' settings (constraints etc)
1386 * is synchronized. */
1387 bPoseChannel *pchan;
1388 /* Make sure we clean up the old data before overwriting it */
1389 pchan = BKE_pose_channel_ensure(obedit->pose, ebone_iter->temp.ebone->name);
1390 BKE_pose_channel_free(pchan);
1391 /* Sync pchan data */
1392 copy_pchan(ebone_iter, ebone_iter->temp.ebone, obedit, obedit);
1393 /* Sync scale mode */
1394 ebone_iter->temp.ebone->inherit_scale_mode = ebone_iter->inherit_scale_mode;
1395 continue;
1396 }
1397
1398 char name_flip[MAXBONENAME];
1399
1400 BLI_string_flip_side_name(name_flip, ebone_iter->name, false, sizeof(name_flip));
1401
1402 /* mirrored bones must have a side-suffix */
1403 if (!STREQ(name_flip, ebone_iter->name)) {
1404 EditBone *ebone;
1405
1406 ebone = duplicateEditBone(ebone_iter, name_flip, arm->edbo, obedit);
1407
1408 if (!ebone_first_dupe) {
1409 ebone_first_dupe = ebone;
1410 }
1411 }
1412 }
1413 }
1414
1415 /* Run through the list and fix the pointers. */
1416 for (ebone_iter = static_cast<EditBone *>(arm->edbo->first);
1417 ebone_iter && ebone_iter != ebone_first_dupe;
1418 ebone_iter = ebone_iter->next)
1419 {
1420 if (ebone_iter->temp.ebone) {
1421 /* copy all flags except for ... */
1422 const int flag_copy = (~0) & ~(BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL);
1423
1424 EditBone *ebone = ebone_iter->temp.ebone;
1425
1426 /* Copy flags in case bone is pre-existing data. */
1427 ebone->flag = (ebone->flag & ~flag_copy) | (ebone_iter->flag & flag_copy);
1428
1429 /* Copy Viewport Display. */
1430 ebone->drawtype = ebone_iter->drawtype;
1431 if (copy_bone_colors) {
1432 ebone->color.palette_index = ebone_iter->color.palette_index;
1433 copy_v4_v4_uchar(ebone->color.custom.active, ebone_iter->color.custom.active);
1434 copy_v4_v4_uchar(ebone->color.custom.select, ebone_iter->color.custom.select);
1435 copy_v4_v4_uchar(ebone->color.custom.solid, ebone_iter->color.custom.solid);
1436 ebone->color.custom.flag = ebone_iter->color.custom.flag;
1437 }
1438
1439 if (ebone_iter->parent == nullptr) {
1440 /* If this bone has no parent,
1441 * Set the duplicate->parent to nullptr
1442 */
1443 ebone->parent = nullptr;
1444 ebone->flag &= ~BONE_CONNECTED;
1445 }
1446 else {
1447 /* the parent may have been duplicated, if not lookup the mirror parent */
1448 EditBone *ebone_parent = get_symmetrized_bone(arm, ebone_iter->parent);
1449
1450 if (ebone_parent == ebone_iter->parent) {
1451 /* If the mirror lookup failed, (but the current bone has a parent)
1452 * then we can assume the parent has no L/R but is a center bone.
1453 * So just use the same parent for both.
1454 */
1455
1456 if (ebone->head[axis] != 0.0f) {
1457 /* The mirrored bone doesn't start on the mirror axis, so assume that this one
1458 * should not be connected to the old parent */
1459 ebone->flag &= ~BONE_CONNECTED;
1460 }
1461 }
1462
1463 ebone->parent = ebone_parent;
1464 }
1465
1466 /* Update custom handle links. */
1467 ebone->bbone_prev = get_symmetrized_bone(arm, ebone_iter->bbone_prev);
1468 ebone->bbone_next = get_symmetrized_bone(arm, ebone_iter->bbone_next);
1469
1470 /* Sync bbone handle types */
1471 ebone->bbone_prev_type = ebone_iter->bbone_prev_type;
1472 ebone->bbone_next_type = ebone_iter->bbone_next_type;
1473
1474 ebone->bbone_mapping_mode = ebone_iter->bbone_mapping_mode;
1475 ebone->bbone_flag = ebone_iter->bbone_flag;
1476 ebone->bbone_prev_flag = ebone_iter->bbone_prev_flag;
1477 ebone->bbone_next_flag = ebone_iter->bbone_next_flag;
1478
1479 /* Lets try to fix any constraint sub-targets that might have been duplicated. */
1480 update_duplicate_subtarget(ebone, obedit, true);
1481 /* Try to update constraint options so that they are mirrored as well
1482 * (need to supply bone_iter as well in case we are working with existing bones) */
1483 update_duplicate_constraint_settings(ebone, ebone_iter, obedit);
1484 /* Mirror bone shapes if possible */
1485 update_duplicate_custom_bone_shapes(C, ebone, obedit);
1486 /* Mirror any settings on the pose bone. */
1487 mirror_pose_bone(*obedit, *ebone);
1488 mirror_bone_collection_assignments(*arm, *ebone_iter, *ebone);
1489 }
1490 }
1491
1493
1494 /* Selected bones now have their 'temp' pointer set,
1495 * so we don't need this anymore */
1496
1497 /* Deselect the old bones and select the new ones */
1498 for (ebone_iter = static_cast<EditBone *>(arm->edbo->first);
1499 ebone_iter && ebone_iter != ebone_first_dupe;
1500 ebone_iter = ebone_iter->next)
1501 {
1502 if (blender::animrig::bone_is_visible(arm, ebone_iter)) {
1503 ebone_iter->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
1504 }
1505 }
1506
1507 /* New bones will be selected, but some of the bones may already exist */
1508 for (ebone_iter = static_cast<EditBone *>(arm->edbo->first);
1509 ebone_iter && ebone_iter != ebone_first_dupe;
1510 ebone_iter = ebone_iter->next)
1511 {
1512 EditBone *ebone = ebone_iter->temp.ebone;
1513 if (ebone && EBONE_SELECTABLE(arm, ebone)) {
1514 ED_armature_ebone_select_set(ebone, true);
1515 }
1516 }
1517
1518 /* correct the active bone */
1519 if (arm->act_edbone && arm->act_edbone->temp.ebone) {
1520 arm->act_edbone = arm->act_edbone->temp.ebone;
1521 }
1522
1523 post_edit_bone_duplicate(arm->edbo, obedit);
1524
1526 DEG_id_tag_update(&obedit->id, ID_RECALC_SELECT);
1527 }
1528
1529 return OPERATOR_FINISHED;
1530}
1531
1533{
1534 /* NOTE: following conventions from #MESH_OT_symmetrize */
1535
1536 /* subset of 'rna_enum_symmetrize_direction_items' */
1537 static const EnumPropertyItem arm_symmetrize_direction_items[] = {
1538 {-1, "NEGATIVE_X", 0, "-X to +X", ""},
1539 {+1, "POSITIVE_X", 0, "+X to -X", ""},
1540 {0, nullptr, 0, nullptr, nullptr},
1541 };
1542
1543 /* identifiers */
1544 ot->name = "Symmetrize";
1545 ot->idname = "ARMATURE_OT_symmetrize";
1546 ot->description = "Enforce symmetry, make copies of the selection or use existing";
1547
1548 /* API callbacks. */
1551
1552 /* flags */
1553 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1554
1555 ot->prop = RNA_def_enum(ot->srna,
1556 "direction",
1557 arm_symmetrize_direction_items,
1558 -1,
1559 "Direction",
1560 "Which sides to copy from and to (when both are selected)");
1561 ot->prop = RNA_def_boolean(
1562 ot->srna, "copy_bone_colors", false, "Bone Colors", "Copy colors to existing bones");
1563}
1564
1565/* ------------------------------------------ */
1566
1567/* previously extrude_armature */
1568/* context; editmode armature */
1569/* if forked && mirror-edit: makes two bones with flipped names */
1571{
1572 const Scene *scene = CTX_data_scene(C);
1573 ViewLayer *view_layer = CTX_data_view_layer(C);
1574 const bool forked = RNA_boolean_get(op->ptr, "forked");
1575 bool changed_multi = false;
1577 scene, view_layer, CTX_wm_view3d(C));
1578
1579 enum ExtrudePoint {
1580 SKIP_EXTRUDE,
1581 TIP_EXTRUDE,
1582 ROOT_EXTRUDE,
1583 };
1584
1585 for (Object *ob : objects) {
1586 bArmature *arm = static_cast<bArmature *>(ob->data);
1587 bool forked_iter = forked;
1588
1589 EditBone *newbone = nullptr, *ebone, *flipbone, *first = nullptr;
1590 int a, totbone = 0;
1591 ExtrudePoint do_extrude;
1592
1593 /* since we allow root extrude too, we have to make sure selection is OK */
1594 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1595 if (blender::animrig::bone_is_visible(arm, ebone)) {
1596 if (ebone->flag & BONE_ROOTSEL) {
1597 if (ebone->parent && (ebone->flag & BONE_CONNECTED)) {
1598 if (ebone->parent->flag & BONE_TIPSEL) {
1599 ebone->flag &= ~BONE_ROOTSEL;
1600 }
1601 }
1602 }
1603 }
1604 }
1605
1606 /* Duplicate the necessary bones */
1607 for (ebone = static_cast<EditBone *>(arm->edbo->first); ((ebone) && (ebone != first));
1608 ebone = ebone->next)
1609 {
1610 if (!blender::animrig::bone_is_visible(arm, ebone)) {
1611 continue;
1612 }
1613 /* We extrude per definition the tip. */
1614 do_extrude = SKIP_EXTRUDE;
1615 if (ebone->flag & (BONE_TIPSEL | BONE_SELECTED)) {
1616 do_extrude = TIP_EXTRUDE;
1617 }
1618 else if (ebone->flag & BONE_ROOTSEL) {
1619 /* but, a bone with parent deselected we do the root... */
1620 if (ebone->parent && (ebone->parent->flag & BONE_TIPSEL)) {
1621 /* pass */
1622 }
1623 else {
1624 do_extrude = ROOT_EXTRUDE;
1625 }
1626 }
1627
1628 if (do_extrude) {
1629 /* we re-use code for mirror editing... */
1630 flipbone = nullptr;
1631 if (arm->flag & ARM_MIRROR_EDIT) {
1632 flipbone = ED_armature_ebone_get_mirrored(arm->edbo, ebone);
1633 if (flipbone) {
1634 forked_iter = false; /* we extrude 2 different bones */
1635 if (flipbone->flag & (BONE_TIPSEL | BONE_ROOTSEL | BONE_SELECTED)) {
1636 /* don't want this bone to be selected... */
1637 flipbone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
1638 }
1639 }
1640 if ((flipbone == nullptr) && (forked_iter)) {
1641 flipbone = ebone;
1642 }
1643 }
1644
1645 for (a = 0; a < 2; a++) {
1646 if (a == 1) {
1647 if (flipbone == nullptr) {
1648 break;
1649 }
1650 std::swap(flipbone, ebone);
1651 }
1652
1653 totbone++;
1654 newbone = MEM_callocN<EditBone>("extrudebone");
1655
1656 if (do_extrude == TIP_EXTRUDE) {
1657 copy_v3_v3(newbone->head, ebone->tail);
1658 copy_v3_v3(newbone->tail, newbone->head);
1659 newbone->parent = ebone;
1660
1661 /* copies it, in case mirrored bone */
1662 newbone->flag = ebone->flag & (BONE_TIPSEL | BONE_RELATIVE_PARENTING);
1663
1664 if (newbone->parent) {
1665 newbone->flag |= BONE_CONNECTED;
1666 }
1667 }
1668 else if (do_extrude == ROOT_EXTRUDE) {
1669 copy_v3_v3(newbone->head, ebone->head);
1670 copy_v3_v3(newbone->tail, ebone->head);
1671 newbone->parent = ebone->parent;
1672
1673 newbone->flag = BONE_TIPSEL;
1674
1675 if (newbone->parent && (ebone->flag & BONE_CONNECTED)) {
1676 newbone->flag |= BONE_CONNECTED;
1677 }
1678 }
1679
1680 newbone->color = ebone->color;
1681 newbone->drawtype = ebone->drawtype;
1682
1683 newbone->weight = ebone->weight;
1684 newbone->dist = ebone->dist;
1685 newbone->xwidth = ebone->xwidth;
1686 newbone->zwidth = ebone->zwidth;
1687 newbone->rad_head = ebone->rad_tail; /* don't copy entire bone. */
1688 newbone->rad_tail = ebone->rad_tail;
1689 newbone->segments = 1;
1690 newbone->layer = ebone->layer;
1691
1692 /* Bendy-Bone parameters */
1693 newbone->roll1 = ebone->roll1;
1694 newbone->roll2 = ebone->roll2;
1695 newbone->curve_in_x = ebone->curve_in_x;
1696 newbone->curve_in_z = ebone->curve_in_z;
1697 newbone->curve_out_x = ebone->curve_out_x;
1698 newbone->curve_out_z = ebone->curve_out_z;
1699 newbone->ease1 = ebone->ease1;
1700 newbone->ease2 = ebone->ease2;
1701
1702 copy_v3_v3(newbone->scale_in, ebone->scale_in);
1703 copy_v3_v3(newbone->scale_out, ebone->scale_out);
1704
1705 STRNCPY_UTF8(newbone->name, ebone->name);
1706
1707 if (flipbone && forked_iter) { /* only set if mirror edit */
1708 if (strlen(newbone->name) < (MAXBONENAME - 2)) {
1709 BLI_strncat(newbone->name, (a == 0) ? "_L" : "_R", sizeof(newbone->name));
1710 }
1711 }
1712 ED_armature_ebone_unique_name(arm->edbo, newbone->name, nullptr);
1713
1714 /* Copy bone collection membership. */
1715 BLI_duplicatelist(&newbone->bone_collections, &ebone->bone_collections);
1716
1717 /* Add the new bone to the list */
1718 BLI_addtail(arm->edbo, newbone);
1719 if (!first) {
1720 first = newbone;
1721 }
1722
1723 /* restore ebone if we were flipping */
1724 if (a == 1 && flipbone) {
1725 std::swap(flipbone, ebone);
1726 }
1727 }
1728 }
1729
1730 /* Deselect the old bone */
1731 ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
1732 }
1733 /* if only one bone, make this one active */
1734 if (totbone == 1 && first) {
1735 arm->act_edbone = first;
1736 }
1737 else {
1738 arm->act_edbone = newbone;
1739 }
1740
1741 if (totbone == 0) {
1742 continue;
1743 }
1744
1745 changed_multi = true;
1746
1747 /* Transform the endpoints */
1749
1752 }
1753
1754 if (!changed_multi) {
1755 return OPERATOR_CANCELLED;
1756 }
1757
1759
1760 return OPERATOR_FINISHED;
1761}
1762
1764{
1765 /* identifiers */
1766 ot->name = "Extrude";
1767 ot->idname = "ARMATURE_OT_extrude";
1768 ot->description = "Create new bones from the selected joints";
1769
1770 /* API callbacks. */
1771 ot->exec = armature_extrude_exec;
1773
1774 /* flags */
1775 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1776
1777 /* props */
1778 RNA_def_boolean(ot->srna, "forked", false, "Forked", "");
1779}
1780
1781/* ********************** Bone Add *************************************/
1782
1783/* Op makes a new bone and returns it with its tip selected. */
1784
1786{
1788 Object *obedit = CTX_data_edit_object(C);
1789 EditBone *bone;
1790 float obmat[3][3], curs[3], viewmat[3][3], totmat[3][3], imat[3][3];
1791 char name[MAXBONENAME];
1792
1793 RNA_string_get(op->ptr, "name", name);
1794
1795 copy_v3_v3(curs, CTX_data_scene(C)->cursor.location);
1796
1797 /* Get inverse point for head and orientation for tail */
1798 invert_m4_m4(obedit->runtime->world_to_object.ptr(), obedit->object_to_world().ptr());
1799 mul_m4_v3(obedit->world_to_object().ptr(), curs);
1800
1801 if (rv3d && (U.flag & USER_ADD_VIEWALIGNED)) {
1802 copy_m3_m4(obmat, rv3d->viewmat);
1803 }
1804 else {
1805 unit_m3(obmat);
1806 }
1807
1808 copy_m3_m4(viewmat, obedit->object_to_world().ptr());
1809 mul_m3_m3m3(totmat, obmat, viewmat);
1810 invert_m3_m3(imat, totmat);
1811
1813
1814 /* Create a bone. */
1815 bone = ED_armature_ebone_add(static_cast<bArmature *>(obedit->data), name);
1816 ANIM_armature_bonecoll_assign_active(static_cast<bArmature *>(obedit->data), bone);
1817
1818 bArmature *arm = static_cast<bArmature *>(obedit->data);
1819 if (!ANIM_bonecoll_is_visible_editbone(arm, bone)) {
1820 const BoneCollectionReference *bcoll_ref = static_cast<const BoneCollectionReference *>(
1821 bone->bone_collections.first);
1822 BLI_assert_msg(bcoll_ref,
1823 "Bone that is not visible due to its bone collections MUST be assigned to at "
1824 "least one of them.");
1825 BKE_reportf(op->reports,
1827 "Bone was added to a hidden collection '%s'",
1828 bcoll_ref->bcoll->name);
1829 }
1830
1831 copy_v3_v3(bone->head, curs);
1832
1833 if (rv3d && (U.flag & USER_ADD_VIEWALIGNED)) {
1834 add_v3_v3v3(bone->tail, bone->head, imat[1]); /* bone with unit length 1 */
1835 }
1836 else {
1837 add_v3_v3v3(bone->tail, bone->head, imat[2]); /* bone with unit length 1, pointing up Z */
1838 }
1839
1840 /* NOTE: notifier might evolve. */
1844
1845 return OPERATOR_FINISHED;
1846}
1847
1849{
1850 /* identifiers */
1851 ot->name = "Add Bone";
1852 ot->idname = "ARMATURE_OT_bone_primitive_add";
1853 ot->description = "Add a new bone located at the 3D cursor";
1854
1855 /* API callbacks. */
1858
1859 /* flags */
1860 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1861
1862 RNA_def_string(ot->srna, "name", nullptr, MAXBONENAME, "Name", "Name of the newly created bone");
1863}
1864
1865/* ********************** Subdivide *******************************/
1866
1867/* Subdivide Operators:
1868 * This group of operators all use the same 'exec' callback, but they are called
1869 * through several different operators - a combined menu (which just calls the exec in the
1870 * appropriate ways), and two separate ones.
1871 */
1872
1874{
1875 Object *obedit = CTX_data_edit_object(C);
1876 EditBone *newbone;
1877 int cuts, i;
1878
1879 /* there may not be a number_cuts property defined (for 'simple' subdivide) */
1880 cuts = RNA_int_get(op->ptr, "number_cuts");
1881
1882 /* loop over all editable bones */
1883 CTX_DATA_BEGIN_WITH_ID (C, EditBone *, ebone, selected_editable_bones, bArmature *, arm) {
1884 /* Keep track of the last bone in the editbone list. The newly created ones
1885 * will be appended after this one. */
1886 EditBone *last_bone_before_cutting = static_cast<EditBone *>(arm->edbo->last);
1887 BLI_assert_msg(last_bone_before_cutting,
1888 "If there is no bone before subdividing, which bone is being subdivided here?");
1889
1890 for (i = cuts + 1; i > 1; i--) {
1891 /* compute cut ratio first */
1892 float cutratio = 1.0f / float(i);
1893 float cutratioI = 1.0f - cutratio;
1894
1895 float val1[3];
1896 float val2[3];
1897 float val3[3];
1898
1899 newbone = MEM_mallocN<EditBone>("ebone subdiv");
1900 *newbone = *ebone;
1901 BLI_addtail(arm->edbo, newbone);
1902
1903 /* calculate location of newbone->head */
1904 copy_v3_v3(val1, ebone->head);
1905 copy_v3_v3(val2, ebone->tail);
1906 copy_v3_v3(val3, newbone->head);
1907
1908 val3[0] = val1[0] * cutratio + val2[0] * cutratioI;
1909 val3[1] = val1[1] * cutratio + val2[1] * cutratioI;
1910 val3[2] = val1[2] * cutratio + val2[2] * cutratioI;
1911
1912 copy_v3_v3(newbone->head, val3);
1913 copy_v3_v3(newbone->tail, ebone->tail);
1914 copy_v3_v3(ebone->tail, newbone->head);
1915
1916 newbone->rad_head = ((ebone->rad_head * cutratio) + (ebone->rad_tail * cutratioI));
1917 ebone->rad_tail = newbone->rad_head;
1918
1919 newbone->flag |= BONE_CONNECTED;
1920 newbone->prop = nullptr;
1921 newbone->system_properties = nullptr;
1922
1923 /* correct parent bones */
1924 LISTBASE_FOREACH (EditBone *, tbone, arm->edbo) {
1925 if (tbone->parent == ebone) {
1926 tbone->parent = newbone;
1927 }
1928 }
1929 newbone->parent = ebone;
1930
1931 /* Copy bone collection membership. */
1932 BLI_duplicatelist(&newbone->bone_collections, &ebone->bone_collections);
1933 }
1934
1935 /* Ensure the bones are uniquely named, in the right order to ensure "Bone" is subdivided into
1936 * "Bone", "Bone.001", "Bone.002", etc. This has to be in the opposite order as the cuts in the
1937 * code above.
1938 *
1939 * The code above cuts into fractions (for cuts=3 it cuts into 1/4, then 1/3, then 1/2), which
1940 * means that it MUST be run in that order. Since the loop below also must run in the order it
1941 * is now in, and that's the opposite order of the loop above, they cannot be combined.
1942 *
1943 * If the code above were refactored, it could just calculate the final bone length and create
1944 * (N-1) bones of that length, which can then be done in any order. Then the code below can be
1945 * integrated into the code above.
1946 */
1947 ListBase new_bones;
1948 new_bones.first = last_bone_before_cutting->next;
1949 new_bones.last = static_cast<EditBone *>(arm->edbo->last);
1950 LISTBASE_FOREACH_BACKWARD (EditBone *, newbone, &new_bones) {
1951 ED_armature_ebone_unique_name(arm->edbo, newbone->name, newbone);
1952 }
1953 }
1955
1956 /* NOTE: notifier might evolve. */
1960
1961 return OPERATOR_FINISHED;
1962}
1963
1965{
1966 PropertyRNA *prop;
1967
1968 /* identifiers */
1969 ot->name = "Subdivide";
1970 ot->idname = "ARMATURE_OT_subdivide";
1971 ot->description = "Break selected bones into chains of smaller bones";
1972
1973 /* API callbacks. */
1976
1977 /* flags */
1978 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1979
1980 /* Properties */
1981 prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 1000, "Number of Cuts", "", 1, 10);
1982 /* Avoid re-using last var because it can cause
1983 * _very_ high poly meshes and annoy users (or worse crash) */
1985}
Functions and classes to work with Actions.
Functions to deal with Armatures.
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)
bool ANIM_armature_bonecoll_unassign_editbone(BoneCollection *bcoll, EditBone *ebone)
void ANIM_armature_bonecoll_assign_active(const bArmature *armature, EditBone *ebone)
BoneCollection * ANIM_armature_bonecoll_new(bArmature *armature, const char *name, int parent_index=-1)
BoneCollection * ANIM_armature_bonecoll_get_by_name(bArmature *armature, const char *name) ATTR_WARN_UNUSED_RESULT
bool ANIM_armature_bonecoll_assign_editbone(BoneCollection *bcoll, EditBone *ebone)
Blender kernel action and pose functionality.
void BKE_pose_channel_copy_data(bPoseChannel *pchan, const bPoseChannel *pchan_from)
void BKE_pose_channels_hash_free(bPose *pose) ATTR_NONNULL(1)
void action_groups_add_channel(bAction *act, bActionGroup *agrp, FCurve *fcurve)
void BKE_pose_channels_hash_ensure(bPose *pose) ATTR_NONNULL(1)
bPoseChannel * BKE_pose_channel_find_name(const bPose *pose, const char *name)
void BKE_pose_channel_free(bPoseChannel *pchan) ATTR_NONNULL(1)
bPoseChannel * BKE_pose_channel_ensure(bPose *pose, const char *name) ATTR_NONNULL(2)
bActionGroup * action_groups_add_new(bAction *act, const char name[])
bActionGroup * BKE_action_group_find_name(bAction *act, const char name[])
void BKE_constraint_targets_flush(struct bConstraint *con, struct ListBase *targets, bool no_copy)
void BKE_constraint_mat_convertspace(struct Object *ob, struct bPoseChannel *pchan, struct bConstraintOb *cob, float mat[4][4], short from, short to, bool keep_scale)
int BKE_constraint_targets_get(struct bConstraint *con, struct ListBase *r_targets)
void BKE_constraint_custom_object_space_init(struct bConstraintOb *cob, struct bConstraint *con)
#define CTX_DATA_BEGIN_WITH_ID(C, Type, instance, member, Type_id, instance_id)
#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)
RegionView3D * CTX_wm_region_view3d(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
#define CTX_DATA_END
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
FCurve * BKE_fcurve_copy(const FCurve *fcu)
IDProperty * IDP_CopyProperty(const IDProperty *prop) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition idprop.cc:863
blender::Vector< Object * > BKE_view_layer_array_from_objects_in_edit_mode_unique_data(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
ID * BKE_libblock_find_name(Main *bmain, short type, const char *name, const std::optional< Library * > lib=std::nullopt) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition lib_id.cc:1710
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_WARNING
Definition BKE_report.hh:38
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define LISTBASE_FOREACH(type, var, list)
#define LISTBASE_FOREACH_BACKWARD(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void void void void void void BLI_duplicatelist(ListBase *dst, const ListBase *src) ATTR_NONNULL(1
#define M_PI
void mul_m3_v3(const float M[3][3], float r[3])
void unit_m3(float m[3][3])
void copy_m3_m4(float m1[3][3], const float m2[4][4])
bool invert_m3_m3(float inverse[3][3], const float mat[3][3])
void mul_m4_v3(const float M[4][4], float r[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])
void unit_m4(float m[4][4])
float angle_wrap_rad(float angle)
MINLINE void copy_v2fl_v2i(float r[2], const int a[2])
MINLINE float len_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 copy_v4_v4_uchar(unsigned char r[4], const unsigned char a[4])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v3_fl(float r[3], float f)
MINLINE void zero_v3(float r[3])
int bool bool bool BLI_strn_endswith(const char *__restrict str, const char *__restrict end, size_t str_len) ATTR_NONNULL(1
char char size_t char * BLI_strncat(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
#define STRNCPY_UTF8(dst, src)
size_t BLI_string_flip_side_name(char *name_dst, const char *name_src, bool strip_number, size_t name_dst_maxncpy) ATTR_NONNULL(1
char * BLI_string_replaceN(const char *__restrict str, const char *__restrict substr_old, const char *__restrict substr_new) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
#define ELEM(...)
#define STREQ(a, b)
#define DATA_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_SELECT
Definition DNA_ID.h:1101
@ ID_RECALC_ANIMATION_NO_FLUSH
Definition DNA_ID.h:1176
#define MAX_ID_NAME
Definition DNA_ID.h:373
@ ID_OB
#define MAXBONENAME
@ BONE_ROOTSEL
@ BONE_SELECTED
@ BONE_TIPSEL
@ BONE_CONNECTED
@ BONE_RELATIVE_PARENTING
@ ARM_MIRROR_EDIT
@ ARM_DRAW_TYPE_ARMATURE_DEFINED
@ CONSTRAINT_TYPE_TRACKTO
@ CONSTRAINT_TYPE_TRANSFORM
@ CONSTRAINT_TYPE_SHRINKWRAP
@ CONSTRAINT_TYPE_ROTLIMIT
@ CONSTRAINT_TYPE_KINEMATIC
@ CONSTRAINT_TYPE_LOCLIMIT
@ CONSTRAINT_TYPE_LOCKTRACK
@ CONSTRAINT_TYPE_ACTION
@ CONSTRAINT_TYPE_DAMPTRACK
@ CONSTRAINT_SPACE_LOCAL
@ TRANS_ROTATION
@ TRANS_LOCATION
Object is a sort of wrapper for general info.
@ OB_ARMATURE
@ USER_ADD_VIEWALIGNED
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_PASS_THROUGH
#define EBONE_SELECTABLE(arm, ebone)
void ED_outliner_select_sync_from_edit_bone_tag(bContext *C)
bool ED_operator_editarmature(bContext *C)
void ED_view3d_win_to_3d(const View3D *v3d, const ARegion *region, const float depth_pt[3], const float mval[2], float r_out[3])
#define X
#define Z
#define Y
Read Guarded memory(de)allocation.
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
#define C
Definition RandGen.cpp:29
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:218
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_BONE_SELECT
Definition WM_types.hh:460
#define NC_OBJECT
Definition WM_types.hh:379
EditBone * ED_armature_ebone_add(bArmature *arm, const char *name)
void ARMATURE_OT_subdivide(wmOperatorType *ot)
EditBone * ED_armature_ebone_add_primitive(Object *obedit_arm, const float length, const bool view_aligned)
static void update_duplicate_constraint_settings(EditBone *dup_bone, EditBone *orig_bone, Object *ob)
EditBone * duplicateEditBone(EditBone *cur_bone, const char *name, ListBase *editbones, Object *ob)
static void track_axis_x_swap(int &value)
static void update_duplicate_action_constraint_settings(EditBone *dup_bone, EditBone *orig_bone, Object *ob, bPoseChannel *pchan, bConstraint *curcon)
static wmOperatorStatus armature_subdivide_exec(bContext *C, wmOperator *op)
static wmOperatorStatus armature_click_extrude_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus armature_duplicate_selected_exec(bContext *C, wmOperator *op)
static void update_duplicate_transform_constraint_settings(Object *ob, bPoseChannel *pchan, bConstraint *curcon)
static void update_duplicate_kinematics_constraint_settings(bConstraint *curcon)
static EditBone * get_symmetrized_bone(bArmature *arm, EditBone *bone)
void ED_armature_ebone_copy(EditBone *dest, const EditBone *source)
void ARMATURE_OT_click_extrude(wmOperatorType *ot)
static wmOperatorStatus armature_click_extrude_exec(bContext *C, wmOperator *)
static wmOperatorStatus armature_bone_primitive_add_exec(bContext *C, wmOperator *op)
static void mirror_bone_collection_assignments(bArmature &armature, EditBone &source_bone, EditBone &target_bone)
static void update_duplicate_subtarget(EditBone *dup_bone, Object *ob, const bool lookup_mirror_subtarget)
static wmOperatorStatus armature_extrude_exec(bContext *C, wmOperator *op)
static void update_duplicate_constraint_shrinkwrap_settings(bConstraint *curcon)
static void update_duplicate_constraint_damp_track_settings(bConstraint *curcon)
void ARMATURE_OT_duplicate(wmOperatorType *ot)
void ARMATURE_OT_symmetrize(wmOperatorType *ot)
void ARMATURE_OT_extrude(wmOperatorType *ot)
static void pre_edit_bone_duplicate(ListBase *editbones)
static void mirror_pose_bone(Object &ob, EditBone &ebone)
static bPoseChannel * pchan_duplicate_map(const bPose *pose, const blender::Map< blender::StringRefNull, blender::StringRefNull > &name_map, bPoseChannel *pchan_src)
void ARMATURE_OT_bone_primitive_add(wmOperatorType *ot)
static void post_edit_bone_duplicate(ListBase *editbones, Object *ob)
static void update_duplicate_constraint_lock_track_settings(bConstraint *curcon)
static void update_duplicate_custom_bone_shapes(bContext *C, EditBone *dup_bone, Object *ob)
static wmOperatorStatus armature_symmetrize_exec(bContext *C, wmOperator *op)
static void update_duplicate_constraint_track_to_settings(bConstraint *curcon)
static void update_duplicate_loc_rot_constraint_settings(Object *ob, bPoseChannel *pchan, bConstraint *curcon)
static void copy_pchan(EditBone *src_bone, EditBone *dst_bone, Object *src_ob, Object *dst_ob)
EditBone * add_points_bone(Object *obedit, float head[3], float tail[3])
EditBone * duplicateEditBoneObjects(EditBone *cur_bone, const char *name, ListBase *editbones, Object *src_ob, Object *dst_ob)
void ED_armature_ebone_unique_name(ListBase *ebones, char *name, EditBone *bone)
bool ED_armature_edit_deselect_all(Object *obedit)
void ED_armature_edit_transform_mirror_update(Object *obedit)
EditBone * ED_armature_ebone_find_name(const ListBase *edbo, const char *name)
void ED_armature_ebone_listbase_temp_clear(ListBase *lb)
void ED_armature_edit_sync_selection(ListBase *edbo)
void ED_armature_ebone_select_set(EditBone *ebone, bool select)
EditBone * ED_armature_ebone_get_mirrored(const ListBase *edbo, EditBone *ebo)
#define U
BMesh const char void * data
Value lookup_default(const Key &key, const Value &default_value) const
Definition BLI_map.hh:570
bool add_as(ForwardKey &&key, ForwardValue &&...value)
Definition BLI_map.hh:312
constexpr bool is_empty() const
constexpr const char * c_str() const
void append(const T &value)
bool fcurve_assign_to_channel_group(FCurve &fcurve, bActionGroup &to_group)
bActionGroup & channel_group_ensure(StringRefNull name)
nullptr float
float length(VecOp< float, D >) RET
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
bool fcurve_matches_collection_path(const FCurve &fcurve, StringRefNull collection_rna_path, StringRefNull data_name)
Vector< FCurve * > fcurves_in_action_slot_filtered(bAction *act, slot_handle_t slot_handle, FunctionRef< bool(const FCurve &fcurve)> predicate)
const animrig::Channelbag * channelbag_for_action_slot(const Action &action, slot_handle_t slot_handle)
bool bone_is_visible(const bArmature *armature, const Bone *bone)
bool bone_is_selected(const bArmature *armature, const Bone *bone)
int armature_bonecoll_find_index(const bArmature *armature, const ::BoneCollection *bcoll)
int armature_bonecoll_find_parent_index(const bArmature *armature, int bcoll_index)
const char * name
#define fabsf
int RNA_int_get(PointerRNA *ptr, const char *name)
std::string RNA_string_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_string(StructOrFunctionRNA *cont_, const char *identifier, const char *default_value, const int maxlen, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
float vec[3][3]
struct BoneCollection * bcoll
ThemeWireColor custom
float curve_out_z
float scale_in[3]
char name[64]
short bbone_prev_flag
float ease2
float weight
float roll1
::BoneColor color
short segments
float tail[3]
IDProperty * prop
union EditBone::@275371335250266324235150226366250166246037204077 temp
char bbone_prev_type
EditBone * parent
float roll2
ListBase bone_collections
float curve_in_x
float zwidth
short bbone_next_flag
float curve_in_z
float length
float xwidth
EditBone * next
EditBone * bbone_prev
char bbone_next_type
IDProperty * system_properties
float rad_tail
EditBone * bbone_next
EditBone * ebone
float ease1
eBone_BBoneMappingMode bbone_mapping_mode
float rad_head
float scale_out[3]
char inherit_scale_mode
float curve_out_x
float head[3]
char * rna_path
BezTriple * bezt
int array_index
char name[258]
Definition DNA_ID.h:432
void * last
void * first
struct bPose * pose
ObjectRuntimeHandle * runtime
float viewmat[4][4]
View3DCursor cursor
unsigned char select[4]
unsigned char solid[4]
unsigned char active[4]
struct BoneCollection * active_collection
struct EditBone * act_edbone
ListBase * edbo
struct bArmature_Runtime runtime
struct bPoseChannel * pchan
struct Scene * scene
struct Object * ob
struct Depsgraph * depsgraph
float custom_scale_xyz[3]
float custom_rotation_euler[3]
struct bPoseChannel * custom_tx
struct bPoseChannel * bbone_next
struct Object * custom
float custom_translation[3]
struct bPoseChannel * bbone_prev
int mval[2]
Definition WM_types.hh:763
struct ReportList * reports
struct PointerRNA * ptr
i
Definition text_draw.cc:230
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4237
wmOperatorStatus WM_operator_flag_only_pass_through_on_press(wmOperatorStatus retval, const wmEvent *event)