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