Blender V5.0
armature_relations.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 "MEM_guardedalloc.h"
11
12#include "DNA_anim_types.h"
13#include "DNA_armature_types.h"
15#include "DNA_object_types.h"
16#include "DNA_scene_types.h"
17
18#include "BLI_ghash.h"
19#include "BLI_listbase.h"
20#include "BLI_map.hh"
21#include "BLI_math_matrix.h"
22#include "BLI_math_vector.h"
23#include "BLI_string.h"
24#include "BLI_string_utf8.h"
25
26#include "BLT_translation.hh"
27
28#include "BKE_action.hh"
29#include "BKE_anim_data.hh"
30#include "BKE_animsys.h"
31#include "BKE_armature.hh"
32#include "BKE_constraint.h"
33#include "BKE_context.hh"
34#include "BKE_fcurve_driver.h"
35#include "BKE_idprop.hh"
36#include "BKE_layer.hh"
37#include "BKE_main.hh"
38#include "BKE_report.hh"
39
40#include "DEG_depsgraph.hh"
42
43#include "RNA_access.hh"
44#include "RNA_define.hh"
45
46#include "WM_api.hh"
47#include "WM_types.hh"
48
49#include "ED_armature.hh"
50#include "ED_object.hh"
51#include "ED_outliner.hh"
52#include "ED_screen.hh"
53
54#include "UI_interface.hh"
56#include "UI_resources.hh"
57
58#include "ANIM_armature.hh"
60
61#include "armature_intern.hh"
62
63using blender::Vector;
64
65/* -------------------------------------------------------------------- */
70
72 Object *ob,
73 Object *tarArm,
74 Object *srcArm,
75 bPoseChannel *pchan,
76 EditBone *curbone,
77 ListBase *lb)
78{
79 bool changed = false;
80
81 LISTBASE_FOREACH (bConstraint *, con, lb) {
82 ListBase targets = {nullptr, nullptr};
83
84 /* constraint targets */
85 if (BKE_constraint_targets_get(con, &targets)) {
86 LISTBASE_FOREACH (bConstraintTarget *, ct, &targets) {
87 if (ct->tar == srcArm) {
88 if (ct->subtarget[0] == '\0') {
89 ct->tar = tarArm;
90 changed = true;
91 }
92 else if (STREQ(ct->subtarget, pchan->name)) {
93 ct->tar = tarArm;
94 STRNCPY_UTF8(ct->subtarget, curbone->name);
95 changed = true;
96 }
97 }
98 }
99
100 BKE_constraint_targets_flush(con, &targets, false);
101 }
102
103 /* action constraint? (pose constraints only) */
104 if (con->type == CONSTRAINT_TYPE_ACTION) {
105 bActionConstraint *data = static_cast<bActionConstraint *>(con->data);
106
107 if (data->act) {
109 data->act,
110 data->action_slot_handle,
111 "pose.bones[",
112 pchan->name,
113 curbone->name,
114 0,
115 0,
116 false);
117
119 }
120 }
121 }
122
123 if (changed) {
125 }
126}
127
128/* Callback to pass to BKE_animdata_main_cb() for fixing driver ID's to point to the new ID. */
129/* FIXME: For now, we only care about drivers here.
130 * When editing rigs, it's very rare to have animation on the rigs being edited already,
131 * so it should be safe to skip these.
132 */
134 Main *bmain, ID *id, FCurve *fcu, Object *srcArm, Object *tarArm, GHash *names_map)
135{
136 ID *src_id = &srcArm->id;
137 ID *dst_id = &tarArm->id;
138
139 GHashIterator gh_iter;
140 bool changed = false;
141
142 /* Fix paths - If this is the target object, it will have some "dirty" paths */
143 if ((id == src_id) && strstr(fcu->rna_path, "pose.bones[")) {
144 GHASH_ITER (gh_iter, names_map) {
145 const char *old_name = static_cast<const char *>(BLI_ghashIterator_getKey(&gh_iter));
146 const char *new_name = static_cast<const char *>(BLI_ghashIterator_getValue(&gh_iter));
147
148 /* only remap if changed; this still means there will be some
149 * waste if there aren't many drivers/keys */
150 if (!STREQ(old_name, new_name) && strstr(fcu->rna_path, old_name)) {
152 id, fcu->rna_path, "pose.bones", old_name, new_name, 0, 0, false);
153
154 changed = true;
155
156 /* we don't want to apply a second remapping on this driver now,
157 * so stop trying names, but keep fixing drivers
158 */
159 break;
160 }
161 }
162 }
163
164 /* Driver targets */
165 if (fcu->driver) {
166 ChannelDriver *driver = fcu->driver;
167
168 /* Ensure that invalid drivers gets re-evaluated in case they become valid once the join
169 * operation is finished. */
170 fcu->flag &= ~FCURVE_DISABLED;
171 driver->flag &= ~DRIVER_FLAG_INVALID;
172
173 /* Fix driver references to invalid ID's */
174 LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) {
175 /* only change the used targets, since the others will need fixing manually anyway */
177 /* change the ID's used... */
178 if (dtar->id == src_id) {
179 dtar->id = dst_id;
180
181 changed = true;
182
183 /* also check on the subtarget...
184 * XXX: We duplicate the logic from drivers_path_rename_fix() here, with our own
185 * little twists so that we know that it isn't going to clobber the wrong data
186 */
187 if ((dtar->rna_path && strstr(dtar->rna_path, "pose.bones[")) || (dtar->pchan_name[0])) {
188 GHASH_ITER (gh_iter, names_map) {
189 const char *old_name = static_cast<const char *>(BLI_ghashIterator_getKey(&gh_iter));
190 const char *new_name = static_cast<const char *>(
192
193 /* only remap if changed */
194 if (!STREQ(old_name, new_name)) {
195 if ((dtar->rna_path) && strstr(dtar->rna_path, old_name)) {
196 /* Fix up path */
197 dtar->rna_path = BKE_animsys_fix_rna_path_rename(
198 id, dtar->rna_path, "pose.bones", old_name, new_name, 0, 0, false);
199 break; /* no need to try any more names for bone path */
200 }
201 if (STREQ(dtar->pchan_name, old_name)) {
202 /* Change target bone name */
203 STRNCPY_UTF8(dtar->pchan_name, new_name);
204 break; /* no need to try any more names for bone subtarget */
205 }
206 }
207 }
208 }
209 }
210 }
212 }
213 }
214
215 if (changed) {
217 }
218}
219
220/* Helper function for armature joining - link fixing */
222 Main *bmain, Object *tarArm, Object *srcArm, bPoseChannel *pchan, EditBone *curbone)
223{
224 Object *ob;
225 bPose *pose;
226
227 /* let's go through all objects in database */
228 for (ob = static_cast<Object *>(bmain->objects.first); ob;
229 ob = static_cast<Object *>(ob->id.next))
230 {
231 /* do some object-type specific things */
232 if (ob->type == OB_ARMATURE) {
233 pose = ob->pose;
234 LISTBASE_FOREACH (bPoseChannel *, pchant, &pose->chanbase) {
236 bmain, ob, tarArm, srcArm, pchan, curbone, &pchant->constraints);
237 }
238 }
239
240 /* fix object-level constraints */
241 if (ob != srcArm) {
243 bmain, ob, tarArm, srcArm, pchan, curbone, &ob->constraints);
244 }
245
246 /* See if an object is parented to this armature */
247 if (ob->parent && (ob->parent == srcArm)) {
248 /* Is object parented to a bone of this src armature? */
249 if (ob->partype == PARBONE) {
250 /* bone name in object */
251 if (STREQ(ob->parsubstr, pchan->name)) {
252 STRNCPY_UTF8(ob->parsubstr, curbone->name);
253 }
254 }
255
256 /* make tar armature be new parent */
257 ob->parent = tarArm;
258
260 }
261 }
262}
263
265 const bArmature *src_arm,
266 const int src_index,
267 bArmature *dest_arm,
268 blender::Map<std::string, BoneCollection *> &bone_collection_by_name)
269{
270 using namespace blender::animrig;
271 const BoneCollection *bcoll = src_arm->collection_array[src_index];
272
273 /* Check if already remapped. */
274 BoneCollection *mapped = bone_collection_by_name.lookup_default(bcoll->name, nullptr);
275
276 if (mapped) {
277 return mapped;
278 }
279
280 /* Remap the parent collection if necessary. */
281 const int src_parent_index = armature_bonecoll_find_parent_index(src_arm, src_index);
282 int parent_index = -1;
283
284 if (src_parent_index >= 0) {
286 src_arm, src_parent_index, dest_arm, bone_collection_by_name);
287
288 if (mapped_parent) {
289 parent_index = armature_bonecoll_find_index(dest_arm, mapped_parent);
290 }
291 }
292
293 /* Create the new collection instance. */
294 BoneCollection *new_bcoll = ANIM_armature_bonecoll_new(dest_arm, bcoll->name, parent_index);
295
296 /* Copy collection visibility. */
297 new_bcoll->flags = bcoll->flags;
298
299 /* Copy custom properties. */
300 if (bcoll->prop) {
301 new_bcoll->prop = IDP_CopyProperty_ex(bcoll->prop, 0);
302 }
303 if (bcoll->system_properties) {
305 }
306
307 bone_collection_by_name.add(bcoll->name, new_bcoll);
308 return new_bcoll;
309}
310
312{
313 Main *bmain = CTX_data_main(C);
314 Scene *scene = CTX_data_scene(C);
315 Object *ob_active = CTX_data_active_object(C);
316 bArmature *arm = static_cast<bArmature *>((ob_active) ? ob_active->data : nullptr);
317 bPose *pose, *opose;
318 bPoseChannel *pchan, *pchann;
319 EditBone *curbone;
320 float mat[4][4], oimat[4][4];
321 bool ok = false;
322
323 /* Ensure we're not in edit-mode and that the active object is an armature. */
324 if (!ob_active || ob_active->type != OB_ARMATURE) {
325 return OPERATOR_CANCELLED;
326 }
327 if (!arm || arm->edbo) {
328 return OPERATOR_CANCELLED;
329 }
330
331 CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) {
332 if (ob_iter == ob_active) {
333 ok = true;
334 break;
335 }
336 }
338
339 /* that way the active object is always selected */
340 if (ok == false) {
341 BKE_report(op->reports, RPT_WARNING, "Active object is not a selected armature");
342 return OPERATOR_CANCELLED;
343 }
344
345 /* Check that there are no shared Armatures, as the code below assumes that
346 * each to-be-joined Armature is unique. */
347 {
348 blender::Set<const bArmature *> seen_armatures;
349 CTX_DATA_BEGIN (C, const Object *, ob_iter, selected_editable_objects) {
350 if (ob_iter->type != OB_ARMATURE) {
351 continue;
352 }
353
354 const bArmature *armature = static_cast<bArmature *>(ob_iter->data);
355 if (seen_armatures.add(armature)) {
356 /* Armature pointer was added to the set, which means it wasn't seen before. */
357 continue;
358 }
359
361 RPT_ERROR,
362 "Cannot join objects that share armature data: %s",
363 armature->id.name + 2);
364 return OPERATOR_CANCELLED;
365 }
367 }
368
369 /* Inverse transform for all selected armatures in this object,
370 * See #object_join_exec for detailed comment on why the safe version is used. */
371 invert_m4_m4_safe_ortho(oimat, ob_active->object_to_world().ptr());
372
373 /* Index bone collections by name. This is also used later to keep track
374 * of collections added from other armatures. */
375 blender::Map<std::string, BoneCollection *> bone_collection_by_name;
376 for (BoneCollection *bcoll : arm->collections_span()) {
377 bone_collection_by_name.add(bcoll->name, bcoll);
378 }
379
380 /* Used to track how bone collections should be remapped after merging
381 * other armatures. */
383
384 /* Get edit-bones of active armature to add edit-bones to */
386
387 /* Get pose of active object and move it out of pose-mode */
388 pose = ob_active->pose;
389 ob_active->mode &= ~OB_MODE_POSE;
390
391 CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) {
392 if ((ob_iter->type == OB_ARMATURE) && (ob_iter != ob_active)) {
393 bArmature *curarm = static_cast<bArmature *>(ob_iter->data);
394
395 /* we assume that each armature datablock is only used in a single place */
396 BLI_assert(ob_active->data != ob_iter->data);
397
398 /* init callback data for fixing up AnimData links later */
399 GHash *names_map = BLI_ghash_str_new("join_armature_adt_fix");
400
401 /* Make a list of edit-bones in current armature */
402 ED_armature_to_edit(curarm);
403
404 /* Copy new bone collections, and store their remapping info. */
405 for (int i = 0; i < curarm->collection_array_num; i++) {
407 curarm, i, arm, bone_collection_by_name);
408
409 bone_collection_remap.add(curarm->collection_array[i], mapped);
410 }
411
412 /* Get Pose of current armature */
413 opose = ob_iter->pose;
414 ob_iter->mode &= ~OB_MODE_POSE;
415 // BASACT->flag &= ~OB_MODE_POSE;
416
417 /* Find the difference matrix */
418 mul_m4_m4m4(mat, oimat, ob_iter->object_to_world().ptr());
419
420 /* Copy bones and pose-channels from the object to the edit armature. */
421 for (pchan = static_cast<bPoseChannel *>(opose->chanbase.first); pchan; pchan = pchann) {
422 pchann = pchan->next;
423 curbone = ED_armature_ebone_find_name(curarm->edbo, pchan->name);
424
425 /* Get new name */
426 ED_armature_ebone_unique_name(arm->edbo, curbone->name, nullptr);
427 BLI_ghash_insert(names_map, BLI_strdup(pchan->name), curbone->name);
428
429 /* Transform the bone */
430 {
431 float premat[4][4];
432 float postmat[4][4];
433 float difmat[4][4];
434 float imat[4][4];
435 float temp[3][3];
436
437 /* Get the premat */
438 ED_armature_ebone_to_mat3(curbone, temp);
439
440 unit_m4(premat); /* mul_m4_m3m4 only sets 3x3 part */
441 mul_m4_m3m4(premat, temp, mat);
442
443 mul_m4_v3(mat, curbone->head);
444 mul_m4_v3(mat, curbone->tail);
445
446 /* Get the postmat */
447 ED_armature_ebone_to_mat3(curbone, temp);
448 copy_m4_m3(postmat, temp);
449
450 /* Find the roll */
451 invert_m4_m4(imat, premat);
452 mul_m4_m4m4(difmat, imat, postmat);
453
454 curbone->roll -= atan2f(difmat[2][0], difmat[2][2]);
455 }
456
457 /* Fix Constraints and Other Links to this Bone and Armature */
458 joined_armature_fix_links(bmain, ob_active, ob_iter, pchan, curbone);
459
460 /* Rename pchan */
461 STRNCPY_UTF8(pchan->name, curbone->name);
462
463 /* Jump Ship! */
464 BLI_remlink(curarm->edbo, curbone);
465 BLI_addtail(arm->edbo, curbone);
466
467 /* Pose channel is moved from one storage to another, its UUID is still unique. */
468 BLI_remlink(&opose->chanbase, pchan);
469 BLI_addtail(&pose->chanbase, pchan);
472
473 /* Remap collections. */
475 bcoll_ref->bcoll = bone_collection_remap.lookup(bcoll_ref->bcoll);
476 }
477 }
478
479 /* Armature ID itself is not freed below, however it has been modified (and is now completely
480 * empty). This needs to be told to the depsgraph, it will also ensure that the global
481 * memfile undo system properly detects the change.
482 *
483 * FIXME: Modifying an existing obdata because we are joining an object using it into another
484 * object is a very questionable behavior, which also does not match with other object types
485 * joining. */
487
488 /* Fix all the drivers (and animation data) */
489 BKE_fcurves_main_cb(bmain, [&](ID *id, FCurve *fcu) {
490 joined_armature_fix_animdata_cb(bmain, id, fcu, ob_iter, ob_active, names_map);
491 });
492 BLI_ghash_free(names_map, MEM_freeN, nullptr);
493
494 /* Only copy over animdata now, after all the remapping has been done,
495 * so that we don't have to worry about ambiguities re which armature
496 * a bone came from!
497 */
498 if (ob_iter->adt) {
499 if (ob_active->adt == nullptr) {
500 /* no animdata, so just use a copy of the whole thing */
501 ob_active->adt = BKE_animdata_copy(bmain, ob_iter->adt, 0);
502 }
503 else {
504 /* merge in data - we'll fix the drivers manually */
506 bmain, &ob_active->id, &ob_iter->id, ADT_MERGECOPY_KEEP_DST, false);
507 }
508 }
509
510 if (curarm->adt) {
511 if (arm->adt == nullptr) {
512 /* no animdata, so just use a copy of the whole thing */
513 arm->adt = BKE_animdata_copy(bmain, curarm->adt, 0);
514 }
515 else {
516 /* merge in data - we'll fix the drivers manually */
517 BKE_animdata_merge_copy(bmain, &arm->id, &curarm->id, ADT_MERGECOPY_KEEP_DST, false);
518 }
519 }
520
521 /* Free the old object data */
522 blender::ed::object::base_free_and_unlink(bmain, scene, ob_iter);
523 }
524 }
526
527 DEG_relations_tag_update(bmain); /* because we removed object(s) */
528
529 ED_armature_from_edit(bmain, arm);
531
532 /* Make sure to recompute bone collection visibility. */
534
538
539 return OPERATOR_FINISHED;
540}
541
543
544/* -------------------------------------------------------------------- */
547
548/* Helper function for armature separating - link fixing */
549static void separated_armature_fix_links(Main *bmain, Object *origArm, Object *newArm)
550{
551 Object *ob;
552 ListBase *opchans, *npchans;
553
554 /* Get reference to list of bones in original and new armatures. */
555 opchans = &origArm->pose->chanbase;
556 npchans = &newArm->pose->chanbase;
557
558 /* let's go through all objects in database */
559 for (ob = static_cast<Object *>(bmain->objects.first); ob;
560 ob = static_cast<Object *>(ob->id.next))
561 {
562 /* do some object-type specific things */
563 if (ob->type == OB_ARMATURE) {
564 LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
565 LISTBASE_FOREACH (bConstraint *, con, &pchan->constraints) {
566 ListBase targets = {nullptr, nullptr};
567
568 /* constraint targets */
569 if (BKE_constraint_targets_get(con, &targets)) {
570 LISTBASE_FOREACH (bConstraintTarget *, ct, &targets) {
571 /* Any targets which point to original armature
572 * are redirected to the new one only if:
573 * - The target isn't origArm/newArm itself.
574 * - The target is one that can be found in newArm/origArm.
575 */
576 if (ct->subtarget[0] != 0) {
577 if (ct->tar == origArm) {
578 if (BLI_findstring(npchans, ct->subtarget, offsetof(bPoseChannel, name))) {
579 ct->tar = newArm;
580 }
581 }
582 else if (ct->tar == newArm) {
583 if (BLI_findstring(opchans, ct->subtarget, offsetof(bPoseChannel, name))) {
584 ct->tar = origArm;
585 }
586 }
587 }
588 }
589
590 BKE_constraint_targets_flush(con, &targets, false);
591 }
592 }
593 }
594 }
595
596 /* fix object-level constraints */
597 if (ob != origArm) {
599 ListBase targets = {nullptr, nullptr};
600
601 /* constraint targets */
602 if (BKE_constraint_targets_get(con, &targets)) {
603 LISTBASE_FOREACH (bConstraintTarget *, ct, &targets) {
604 /* any targets which point to original armature are redirected to the new one only if:
605 * - the target isn't origArm/newArm itself
606 * - the target is one that can be found in newArm/origArm
607 */
608 if (ct->subtarget[0] != '\0') {
609 if (ct->tar == origArm) {
610 if (BLI_findstring(npchans, ct->subtarget, offsetof(bPoseChannel, name))) {
611 ct->tar = newArm;
612 }
613 }
614 else if (ct->tar == newArm) {
615 if (BLI_findstring(opchans, ct->subtarget, offsetof(bPoseChannel, name))) {
616 ct->tar = origArm;
617 }
618 }
619 }
620 }
621
622 BKE_constraint_targets_flush(con, &targets, false);
623 }
624 }
625 }
626
627 /* See if an object is parented to this armature */
628 if (ob->parent && (ob->parent == origArm)) {
629 /* Is object parented to a bone of this src armature? */
630 if ((ob->partype == PARBONE) && (ob->parsubstr[0] != '\0')) {
631 if (BLI_findstring(npchans, ob->parsubstr, offsetof(bPoseChannel, name))) {
632 ob->parent = newArm;
633 }
634 }
635 }
636 }
637}
638
646static void separate_armature_bones(Main *bmain, Object *ob, const bool is_select)
647{
648 bArmature *arm = static_cast<bArmature *>(ob->data);
649 bPoseChannel *pchan, *pchann;
650 EditBone *curbone;
651
652 /* make local set of edit-bones to manipulate here */
654
655 /* go through pose-channels, checking if a bone should be removed */
656 for (pchan = static_cast<bPoseChannel *>(ob->pose->chanbase.first); pchan; pchan = pchann) {
657 pchann = pchan->next;
658 curbone = ED_armature_ebone_find_name(arm->edbo, pchan->name);
659
660 /* check if bone needs to be removed */
661 if (is_select == blender::animrig::bone_is_selected(arm, curbone)) {
662
663 /* Clear the bone->parent var of any bone that had this as its parent. */
664 LISTBASE_FOREACH (EditBone *, ebo, arm->edbo) {
665 if (ebo->parent == curbone) {
666 ebo->parent = nullptr;
667 /* this is needed to prevent random crashes with in ED_armature_from_edit */
668 ebo->temp.p = nullptr;
669 ebo->flag &= ~BONE_CONNECTED;
670 }
671 }
672
673 /* clear the pchan->parent var of any pchan that had this as its parent */
674 LISTBASE_FOREACH (bPoseChannel *, pchn, &ob->pose->chanbase) {
675 if (pchn->parent == pchan) {
676 pchn->parent = nullptr;
677 }
678 if (pchn->bbone_next == pchan) {
679 pchn->bbone_next = nullptr;
680 }
681 if (pchn->bbone_prev == pchan) {
682 pchn->bbone_prev = nullptr;
683 }
684 }
685
686 /* Free any of the extra-data this pchan might have. */
689
690 /* get rid of unneeded bone */
691 bone_free(arm, curbone);
692 BLI_freelinkN(&ob->pose->chanbase, pchan);
693 }
694 }
695
696 /* Exit edit-mode (recalculates pose-channels too). */
698 ED_armature_from_edit(bmain, static_cast<bArmature *>(ob->data));
699 ED_armature_edit_free(static_cast<bArmature *>(ob->data));
700}
701
702/* separate selected bones into their armature */
704{
705 Main *bmain = CTX_data_main(C);
706 Scene *scene = CTX_data_scene(C);
707 ViewLayer *view_layer = CTX_data_view_layer(C);
708 bool ok = false;
709
710 /* set wait cursor in case this takes a while */
711 WM_cursor_wait(true);
712
714 scene, view_layer, CTX_wm_view3d(C));
715
716 for (Base *base_old : bases) {
717 Object *ob_old = base_old->object;
718
719 {
720 bArmature *arm_old = static_cast<bArmature *>(ob_old->data);
721 bool has_selected_bone = false;
722 bool has_selected_any = false;
723 LISTBASE_FOREACH (EditBone *, ebone, arm_old->edbo) {
724 if (blender::animrig::bone_is_visible(arm_old, ebone)) {
725 if (ebone->flag & BONE_SELECTED) {
726 has_selected_bone = true;
727 break;
728 }
729 if (ebone->flag & (BONE_TIPSEL | BONE_ROOTSEL)) {
730 has_selected_any = true;
731 }
732 }
733 }
734 if (has_selected_bone == false) {
735 if (has_selected_any) {
736 /* Without this, we may leave head/tail selected
737 * which isn't expected after separating. */
739 }
740 continue;
741 }
742 }
743
744 /* We are going to do this as follows (unlike every other instance of separate):
745 * 1. Exit edit-mode & pose-mode for active armature/base. Take note of what this is.
746 * 2. Duplicate base - BASACT is the new one now
747 * 3. For each of the two armatures,
748 * enter edit-mode -> remove appropriate bones -> exit edit-mode + recalculate.
749 * 4. Fix constraint links
750 * 5. Make original armature active and enter edit-mode
751 */
752
753 /* 1) store starting settings and exit edit-mode */
754 ob_old->mode &= ~OB_MODE_POSE;
755
756 ED_armature_from_edit(bmain, static_cast<bArmature *>(ob_old->data));
757 ED_armature_edit_free(static_cast<bArmature *>(ob_old->data));
758
759 /* 2) duplicate base */
760
761 /* Only duplicate linked armature but take into account
762 * user preferences for duplicating actions. */
763 short dupflag = USER_DUP_ARM | (U.dupflag & USER_DUP_ACT);
765 bmain, scene, view_layer, base_old, eDupli_ID_Flags(dupflag));
766 Object *ob_new = base_new->object;
767
769
770 /* 3) remove bones that shouldn't still be around on both armatures */
771 separate_armature_bones(bmain, ob_old, true);
772 separate_armature_bones(bmain, ob_new, false);
773
774 /* 4) fix links before depsgraph flushes, err... or after? */
775 separated_armature_fix_links(bmain, ob_old, ob_new);
776
777 DEG_id_tag_update(&ob_old->id, ID_RECALC_GEOMETRY); /* this is the original one */
778 DEG_id_tag_update(&ob_new->id, ID_RECALC_GEOMETRY); /* this is the separated one */
779
780 /* 5) restore original conditions */
781 ED_armature_to_edit(static_cast<bArmature *>(ob_old->data));
782
783 /* parents tips remain selected when connected children are removed. */
785
786 ok = true;
787
788 /* NOTE: notifier might evolve. */
790 }
791
792 /* Recalculate/redraw + cleanup */
793 WM_cursor_wait(false);
794
795 if (ok) {
796 BKE_report(op->reports, RPT_INFO, "Separated bones");
798 }
799
800 return OPERATOR_FINISHED;
801}
802
804{
805 /* identifiers */
806 ot->name = "Separate Bones";
807 ot->idname = "ARMATURE_OT_separate";
808 ot->description = "Isolate selected bones into a separate armature";
809
810 /* callbacks */
813
814 /* flags */
816}
817
819
820/* -------------------------------------------------------------------- */
823
824/* armature parenting options */
825#define ARM_PAR_CONNECT 1
826#define ARM_PAR_OFFSET 2
827
828/* armature un-parenting options */
829#define ARM_PAR_CLEAR 1
830#define ARM_PAR_CLEAR_DISCONNECT 2
831
832/* check for null, before calling! */
834{
835 bone->flag |= BONE_CONNECTED;
836 copy_v3_v3(bone->head, bone->parent->tail);
837 bone->rad_head = bone->parent->rad_tail;
838}
839
841 EditBone *selbone,
842 EditBone *actbone,
843 short mode)
844{
845 EditBone *ebone;
846 float offset[3];
847
848 if ((selbone->parent) && (selbone->flag & BONE_CONNECTED)) {
849 selbone->parent->flag &= ~BONE_TIPSEL;
850 }
851
852 /* make actbone the parent of selbone */
853 selbone->parent = actbone;
854
855 /* in actbone tree we cannot have a loop */
856 for (ebone = actbone->parent; ebone; ebone = ebone->parent) {
857 if (ebone->parent == selbone) {
858 ebone->parent = nullptr;
859 ebone->flag &= ~BONE_CONNECTED;
860 }
861 }
862
863 if (mode == ARM_PAR_CONNECT) {
864 /* Connected: Child bones will be moved to the parent tip */
865 selbone->flag |= BONE_CONNECTED;
866 sub_v3_v3v3(offset, actbone->tail, selbone->head);
867
868 copy_v3_v3(selbone->head, actbone->tail);
869 selbone->rad_head = actbone->rad_tail;
870
871 add_v3_v3(selbone->tail, offset);
872
873 /* offset for all its children */
874 LISTBASE_FOREACH (EditBone *, ebone, edbo) {
875 EditBone *par;
876
877 for (par = ebone->parent; par; par = par->parent) {
878 if (par == selbone) {
879 add_v3_v3(ebone->head, offset);
880 add_v3_v3(ebone->tail, offset);
881 break;
882 }
883 }
884 }
885 }
886 else {
887 /* Offset: Child bones will retain their distance from the parent tip */
888 selbone->flag &= ~BONE_CONNECTED;
889 }
890}
891
893 {ARM_PAR_CONNECT, "CONNECTED", 0, "Connected", ""},
894 {ARM_PAR_OFFSET, "OFFSET", 0, "Keep Offset", ""},
895 {0, nullptr, 0, nullptr, nullptr},
896};
897
899{
901 bArmature *arm = static_cast<bArmature *>(ob->data);
902 EditBone *actbone = CTX_data_active_bone(C);
903 EditBone *actmirb = nullptr;
904 short val = RNA_enum_get(op->ptr, "type");
905
906 /* there must be an active bone */
907 if (actbone == nullptr) {
908 BKE_report(op->reports, RPT_ERROR, "Operation requires an active bone");
909 return OPERATOR_CANCELLED;
910 }
911 if (arm->flag & ARM_MIRROR_EDIT) {
912 /* For X-Axis Mirror Editing option, we may need a mirror copy of actbone:
913 * - If there's a mirrored copy of selbone, try to find a mirrored copy of actbone
914 * (i.e. selbone="child.L" and actbone="parent.L", find "child.R" and "parent.R").
915 * This is useful for arm-chains, for example parenting lower arm to upper arm.
916 * - If there's no mirrored copy of actbone (i.e. actbone = "parent.C" or "parent")
917 * then just use actbone. Useful when doing upper arm to spine.
918 */
919 actmirb = ED_armature_ebone_get_mirrored(arm->edbo, actbone);
920 if (actmirb == nullptr) {
921 actmirb = actbone;
922 }
923 }
924
925 /* If there is only 1 selected bone, we assume that it is the active bone,
926 * since a user will need to have clicked on a bone (thus selecting it) to make it active. */
927 bool is_active_only_selected = false;
928 if (actbone->flag & BONE_SELECTED) {
929 is_active_only_selected = true;
930 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
931 if (EBONE_EDITABLE(ebone)) {
932 if (ebone != actbone) {
933 is_active_only_selected = false;
934 break;
935 }
936 }
937 }
938 }
939
940 if (is_active_only_selected) {
941 /* When only the active bone is selected, and it has a parent,
942 * connect it to the parent, as that is the only possible outcome.
943 */
944 if (actbone->parent) {
946
947 if ((arm->flag & ARM_MIRROR_EDIT) && (actmirb->parent)) {
949 }
950 }
951 }
952 else {
953 /* Parent 'selected' bones to the active one:
954 * - The context iterator contains both selected bones and their mirrored copies,
955 * so we assume that unselected bones are mirrored copies of some selected bone.
956 * - Since the active one (and/or its mirror) will also be selected, we also need
957 * to check that we are not trying to operate on them, since such an operation
958 * would cause errors.
959 */
960
961 /* Parent selected bones to the active one. */
962 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
963 if (EBONE_EDITABLE(ebone)) {
964 if (ebone != actbone) {
965 bone_connect_to_new_parent(arm->edbo, ebone, actbone, val);
966 }
967
968 if (arm->flag & ARM_MIRROR_EDIT) {
969 EditBone *ebone_mirror = ED_armature_ebone_get_mirrored(arm->edbo, ebone);
970 if (ebone_mirror && (ebone_mirror->flag & BONE_SELECTED) == 0) {
971 if (ebone_mirror != actmirb) {
972 bone_connect_to_new_parent(arm->edbo, ebone_mirror, actmirb, val);
973 }
974 }
975 }
976 }
977 }
978 }
979
980 /* NOTE: notifier might evolve. */
983
984 return OPERATOR_FINISHED;
985}
986
988 wmOperator * /*op*/,
989 const wmEvent * /*event*/)
990{
991 /* False when all selected bones are parented to the active bone. */
992 bool enable_offset = false;
993 /* False when all selected bones are connected to the active bone. */
994 bool enable_connect = false;
995 {
997 bArmature *arm = static_cast<bArmature *>(ob->data);
998 EditBone *actbone = arm->act_edbone;
999 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1000 if (!EBONE_EDITABLE(ebone) || !(ebone->flag & BONE_SELECTED)) {
1001 continue;
1002 }
1003 if (ebone == actbone) {
1004 continue;
1005 }
1006
1007 if (ebone->parent != actbone) {
1008 enable_offset = true;
1009 enable_connect = true;
1010 break;
1011 }
1012 if (!(ebone->flag & BONE_CONNECTED)) {
1013 enable_connect = true;
1014 }
1015 }
1016 }
1017
1019 C, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Make Parent"), ICON_NONE);
1020 uiLayout *layout = UI_popup_menu_layout(pup);
1021
1022 uiLayout *row_offset = &layout->row(false);
1023 row_offset->enabled_set(enable_offset);
1024 PointerRNA op_ptr = row_offset->op("ARMATURE_OT_parent_set", IFACE_("Keep Offset"), ICON_NONE);
1025 RNA_enum_set(&op_ptr, "type", ARM_PAR_OFFSET);
1026
1027 uiLayout *row_connect = &layout->row(false);
1028 row_connect->enabled_set(enable_connect);
1029 op_ptr = row_connect->op("ARMATURE_OT_parent_set", IFACE_("Connected"), ICON_NONE);
1030 RNA_enum_set(&op_ptr, "type", ARM_PAR_CONNECT);
1031
1032 UI_popup_menu_end(C, pup);
1033
1034 return OPERATOR_INTERFACE;
1035}
1036
1038{
1039 /* identifiers */
1040 ot->name = "Make Parent";
1041 ot->idname = "ARMATURE_OT_parent_set";
1042 ot->description = "Set the active bone as the parent of the selected bones";
1043
1044 /* API callbacks. */
1048
1049 /* flags */
1050 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1051
1053 ot->srna, "type", prop_editarm_make_parent_types, 0, "Parent Type", "Type of parenting");
1054}
1055
1057 {ARM_PAR_CLEAR, "CLEAR", 0, "Clear Parent", ""},
1058 {ARM_PAR_CLEAR_DISCONNECT, "DISCONNECT", 0, "Disconnect Bone", ""},
1059 {0, nullptr, 0, nullptr, nullptr},
1060};
1061
1062static void editbone_clear_parent(EditBone *ebone, int mode)
1063{
1064 if (ebone->parent) {
1065 /* for nice selection */
1066 ebone->parent->flag &= ~BONE_TIPSEL;
1067 }
1068
1069 if (mode == 1) {
1070 ebone->parent = nullptr;
1071 }
1072 ebone->flag &= ~BONE_CONNECTED;
1073}
1074
1076{
1077 const Scene *scene = CTX_data_scene(C);
1078 ViewLayer *view_layer = CTX_data_view_layer(C);
1079 const int val = RNA_enum_get(op->ptr, "type");
1080
1081 CTX_DATA_BEGIN (C, EditBone *, ebone, selected_editable_bones) {
1082 editbone_clear_parent(ebone, val);
1083 }
1085
1087 scene, view_layer, CTX_wm_view3d(C));
1088 for (Object *ob : objects) {
1089 bArmature *arm = static_cast<bArmature *>(ob->data);
1090 bool changed = false;
1091
1092 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1093 if (EBONE_EDITABLE(ebone)) {
1094 changed = true;
1095 break;
1096 }
1097 }
1098
1099 if (!changed) {
1100 continue;
1101 }
1102
1104
1105 /* NOTE: notifier might evolve. */
1107 }
1108 return OPERATOR_FINISHED;
1109}
1110
1112 wmOperator * /*op*/,
1113 const wmEvent * /*event*/)
1114{
1115 /* False when no selected bones are connected to the active bone. */
1116 bool enable_disconnect = false;
1117 /* False when no selected bones are parented to the active bone. */
1118 bool enable_clear = false;
1119 {
1121 bArmature *arm = static_cast<bArmature *>(ob->data);
1122 LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1123 if (!EBONE_EDITABLE(ebone) || !(ebone->flag & BONE_SELECTED)) {
1124 continue;
1125 }
1126 if (ebone->parent == nullptr) {
1127 continue;
1128 }
1129 enable_clear = true;
1130
1131 if (ebone->flag & BONE_CONNECTED) {
1132 enable_disconnect = true;
1133 break;
1134 }
1135 }
1136 }
1137
1139 C, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Clear Parent"), ICON_NONE);
1140 uiLayout *layout = UI_popup_menu_layout(pup);
1141
1142 uiLayout *row_clear = &layout->row(false);
1143 row_clear->enabled_set(enable_clear);
1144 PointerRNA op_ptr = row_clear->op("ARMATURE_OT_parent_clear", IFACE_("Clear Parent"), ICON_NONE);
1145 RNA_enum_set(&op_ptr, "type", ARM_PAR_CLEAR);
1146
1147 uiLayout *row_disconnect = &layout->row(false);
1148 row_disconnect->enabled_set(enable_disconnect);
1149 op_ptr = row_disconnect->op("ARMATURE_OT_parent_clear", IFACE_("Disconnect Bone"), ICON_NONE);
1150 RNA_enum_set(&op_ptr, "type", ARM_PAR_CLEAR_DISCONNECT);
1151
1152 UI_popup_menu_end(C, pup);
1153
1154 return OPERATOR_INTERFACE;
1155}
1156
1158{
1159 /* identifiers */
1160 ot->name = "Clear Parent";
1161 ot->idname = "ARMATURE_OT_parent_clear";
1162 ot->description =
1163 "Remove the parent-child relationship between selected bones and their parents";
1164
1165 /* API callbacks. */
1169
1170 /* flags */
1171 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1172
1173 ot->prop = RNA_def_enum(ot->srna,
1174 "type",
1176 0,
1177 "Clear Type",
1178 "What way to clear parenting");
1179}
1180
Functions to deal with Armatures.
C++ functions to deal with Armature collections (i.e. the successor of bone layers).
void ANIM_armature_runtime_refresh(bArmature *armature)
BoneCollection * ANIM_armature_bonecoll_new(bArmature *armature, const char *name, int parent_index=-1)
Blender kernel action and pose functionality.
void BKE_pose_channels_hash_free(bPose *pose) ATTR_NONNULL(1)
void BKE_pose_channel_free(bPoseChannel *pchan) ATTR_NONNULL(1)
void BKE_animdata_merge_copy(Main *bmain, ID *dst_id, ID *src_id, eAnimData_MergeCopy_Modes action_mode, bool fix_drivers)
Definition anim_data.cc:447
AnimData * BKE_animdata_copy(Main *bmain, AnimData *adt, int flag)
Definition anim_data.cc:364
@ ADT_MERGECOPY_KEEP_DST
void BKE_fcurves_main_cb(struct Main *bmain, blender::FunctionRef< void(ID *, FCurve *)> func)
void BKE_action_fix_paths_rename(struct ID *owner_id, struct bAction *act, int32_t slot_handle, const char *prefix, const char *oldName, const char *newName, int oldSubscript, int newSubscript, bool verify_paths)
char * BKE_animsys_fix_rna_path_rename(struct ID *owner_id, char *old_path, const char *prefix, const char *oldName, const char *newName, int oldSubscript, int newSubscript, bool verify_paths)
Definition anim_data.cc:890
void BKE_constraint_targets_flush(struct bConstraint *con, struct ListBase *targets, bool no_copy)
int BKE_constraint_targets_get(struct bConstraint *con, struct ListBase *r_targets)
#define CTX_DATA_BEGIN(C, Type, instance, member)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Object * CTX_data_edit_object(const bContext *C)
Main * CTX_data_main(const bContext *C)
EditBone * CTX_data_active_bone(const bContext *C)
#define CTX_DATA_END
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
#define DRIVER_TARGETS_USED_LOOPER_BEGIN(dvar)
#define DRIVER_TARGETS_LOOPER_END
IDProperty * IDP_CopyProperty_ex(const IDProperty *prop, int flag) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition idprop.cc:845
blender::Vector< Base * > BKE_view_layer_array_from_bases_in_edit_mode_unique_data(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
blender::Vector< Object * > BKE_view_layer_array_from_objects_in_edit_mode_unique_data(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_INFO
Definition BKE_report.hh:35
@ RPT_ERROR
Definition BKE_report.hh:39
@ RPT_WARNING
Definition BKE_report.hh:38
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
#define BLI_assert(a)
Definition BLI_assert.h:46
BLI_INLINE void * BLI_ghashIterator_getKey(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.h:295
BLI_INLINE void * BLI_ghashIterator_getValue(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.h:299
#define GHASH_ITER(gh_iter_, ghash_)
Definition BLI_ghash.h:318
GHash * BLI_ghash_str_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
void BLI_ghash_insert(GHash *gh, void *key, void *val)
Definition BLI_ghash.cc:707
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition BLI_ghash.cc:860
#define LISTBASE_FOREACH(type, var, list)
void * BLI_findstring(const ListBase *listbase, const char *id, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:608
void BLI_freelinkN(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:270
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
void copy_m4_m3(float m1[4][4], const float m2[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 invert_m4_m4_safe_ortho(float inverse[4][4], const float mat[4][4])
void mul_m4_m3m4(float R[4][4], const float A[3][3], const float B[4][4])
void unit_m4(float m[4][4])
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_v3(float r[3], const float a[3])
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.cc:41
#define STRNCPY_UTF8(dst, src)
#define STREQ(a, b)
#define CTX_IFACE_(context, msgid)
#define BLT_I18NCONTEXT_OPERATOR_DEFAULT
#define IFACE_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_relations_tag_update(Main *bmain)
@ ID_RECALC_SELECT
Definition DNA_ID.h:1101
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1118
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ DRIVER_FLAG_INVALID
@ FCURVE_DISABLED
@ BONE_ROOTSEL
@ BONE_SELECTED
@ BONE_TIPSEL
@ BONE_CONNECTED
@ ARM_MIRROR_EDIT
@ CONSTRAINT_TYPE_ACTION
@ OB_MODE_POSE
Object is a sort of wrapper for general info.
@ PARBONE
@ OB_ARMATURE
eDupli_ID_Flags
@ USER_DUP_ARM
@ USER_DUP_ACT
@ OPERATOR_CANCELLED
@ OPERATOR_INTERFACE
@ OPERATOR_FINISHED
#define EBONE_EDITABLE(ebone)
void ED_outliner_select_sync_from_object_tag(bContext *C)
bool ED_operator_editarmature(bContext *C)
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
void UI_popup_menu_end(bContext *C, uiPopupMenu *pup)
uiPopupMenu * UI_popup_menu_begin(bContext *C, const char *title, int icon) ATTR_NONNULL()
uiLayout * UI_popup_menu_layout(uiPopupMenu *pup)
#define ND_OB_ACTIVE
Definition WM_types.hh:440
#define NC_SCENE
Definition WM_types.hh:378
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_LAYER_CONTENT
Definition WM_types.hh:453
#define ND_POSE
Definition WM_types.hh:458
#define ND_BONE_SELECT
Definition WM_types.hh:460
#define NC_OBJECT
Definition WM_types.hh:379
void bone_free(bArmature *arm, EditBone *bone)
void ED_armature_ebone_unique_name(ListBase *ebones, char *name, EditBone *bone)
static void separate_armature_bones(Main *bmain, Object *ob, const bool is_select)
static wmOperatorStatus armature_parent_clear_invoke(bContext *C, wmOperator *, const wmEvent *)
static void joined_armature_fix_links(Main *bmain, Object *tarArm, Object *srcArm, bPoseChannel *pchan, EditBone *curbone)
static const EnumPropertyItem prop_editarm_clear_parent_types[]
static wmOperatorStatus armature_parent_set_invoke(bContext *C, wmOperator *, const wmEvent *)
static wmOperatorStatus armature_parent_set_exec(bContext *C, wmOperator *op)
void ARMATURE_OT_parent_clear(wmOperatorType *ot)
static void joined_armature_fix_animdata_cb(Main *bmain, ID *id, FCurve *fcu, Object *srcArm, Object *tarArm, GHash *names_map)
static BoneCollection * join_armature_remap_collection(const bArmature *src_arm, const int src_index, bArmature *dest_arm, blender::Map< std::string, BoneCollection * > &bone_collection_by_name)
#define ARM_PAR_CLEAR_DISCONNECT
static void joined_armature_fix_links_constraints(Main *bmain, Object *ob, Object *tarArm, Object *srcArm, bPoseChannel *pchan, EditBone *curbone, ListBase *lb)
void ARMATURE_OT_parent_set(wmOperatorType *ot)
static void separated_armature_fix_links(Main *bmain, Object *origArm, Object *newArm)
static void bone_connect_to_new_parent(ListBase *edbo, EditBone *selbone, EditBone *actbone, short mode)
wmOperatorStatus ED_armature_join_objects_exec(bContext *C, wmOperator *op)
#define ARM_PAR_OFFSET
static wmOperatorStatus separate_armature_exec(bContext *C, wmOperator *op)
void ARMATURE_OT_separate(wmOperatorType *ot)
static void editbone_clear_parent(EditBone *ebone, int mode)
#define ARM_PAR_CONNECT
static wmOperatorStatus armature_parent_clear_exec(bContext *C, wmOperator *op)
#define ARM_PAR_CLEAR
static const EnumPropertyItem prop_editarm_make_parent_types[]
static void bone_connect_to_existing_parent(EditBone *bone)
bool ED_armature_edit_deselect_all(Object *obedit)
EditBone * ED_armature_ebone_find_name(const ListBase *edbo, const char *name)
void ED_armature_edit_sync_selection(ListBase *edbo)
void ED_armature_edit_free(bArmature *arm)
void ED_armature_ebone_to_mat3(EditBone *ebone, float r_mat[3][3])
void ED_armature_from_edit(Main *bmain, bArmature *arm)
void ED_armature_to_edit(bArmature *arm)
EditBone * ED_armature_ebone_get_mirrored(const ListBase *edbo, EditBone *ebo)
#define U
BMesh const char void * data
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
const Value & lookup(const Key &key) const
Definition BLI_map.hh:545
Value lookup_default(const Key &key, const Value &default_value) const
Definition BLI_map.hh:570
bool add(const Key &key)
Definition BLI_set.hh:248
#define offsetof(t, d)
DEG_id_tag_update_ex(cb_data->bmain, cb_data->owner_id, ID_RECALC_TAG_FOR_UNDO|ID_RECALC_SYNC_TO_EVAL)
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
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)
Base * add_duplicate(Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base, eDupli_ID_Flags dupflag)
void base_free_and_unlink(Main *bmain, Scene *scene, Object *ob)
const char * name
#define atan2f
void RNA_enum_set(PointerRNA *ptr, const char *name, int value)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
struct Object * object
struct IDProperty * system_properties
struct IDProperty * prop
char name[64]
float tail[3]
EditBone * parent
ListBase bone_collections
float rad_tail
float rad_head
float head[3]
char * rna_path
ChannelDriver * driver
Definition DNA_ID.h:414
char name[258]
Definition DNA_ID.h:432
void * next
Definition DNA_ID.h:417
void * first
ListBase objects
Definition BKE_main.hh:280
ListBase constraints
struct bPose * pose
struct AnimData * adt
struct Object * parent
char parsubstr[64]
struct AnimData * adt
struct BoneCollection ** collection_array
struct EditBone * act_edbone
ListBase * edbo
struct bPoseChannel * next
ListBase chanbase
void enabled_set(bool enabled)
uiLayout & row(bool align)
PointerRNA op(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, blender::wm::OpCallContext context, eUI_Item_Flag flag)
struct ReportList * reports
struct PointerRNA * ptr
i
Definition text_draw.cc:230
void WM_cursor_wait(bool val)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4237