Blender  V3.3
armature_relations.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2001-2002 NaN Holding BV. All rights reserved. */
3 
9 #include "MEM_guardedalloc.h"
10 
11 #include "DNA_anim_types.h"
12 #include "DNA_armature_types.h"
13 #include "DNA_constraint_types.h"
14 #include "DNA_object_types.h"
15 #include "DNA_scene_types.h"
16 
17 #include "BLI_blenlib.h"
18 #include "BLI_ghash.h"
19 #include "BLI_math.h"
20 
21 #include "BLT_translation.h"
22 
23 #include "BKE_action.h"
24 #include "BKE_anim_data.h"
25 #include "BKE_animsys.h"
26 #include "BKE_armature.h"
27 #include "BKE_constraint.h"
28 #include "BKE_context.h"
29 #include "BKE_fcurve_driver.h"
30 #include "BKE_layer.h"
31 #include "BKE_main.h"
32 #include "BKE_report.h"
33 
34 #include "DEG_depsgraph.h"
35 #include "DEG_depsgraph_build.h"
36 
37 #include "RNA_access.h"
38 #include "RNA_define.h"
39 
40 #include "WM_api.h"
41 #include "WM_types.h"
42 
43 #include "ED_armature.h"
44 #include "ED_object.h"
45 #include "ED_outliner.h"
46 #include "ED_screen.h"
47 
48 #include "UI_interface.h"
49 #include "UI_resources.h"
50 
51 #include "armature_intern.h"
52 
53 /* -------------------------------------------------------------------- */
60  Object *ob,
61  Object *tarArm,
62  Object *srcArm,
63  bPoseChannel *pchan,
64  EditBone *curbone,
65  ListBase *lb)
66 {
67  bConstraint *con;
68  bool changed = false;
69 
70  for (con = lb->first; con; con = con->next) {
71  ListBase targets = {NULL, NULL};
73 
74  /* constraint targets */
75  if (BKE_constraint_targets_get(con, &targets)) {
76  for (ct = targets.first; ct; ct = ct->next) {
77  if (ct->tar == srcArm) {
78  if (ct->subtarget[0] == '\0') {
79  ct->tar = tarArm;
80  changed = true;
81  }
82  else if (STREQ(ct->subtarget, pchan->name)) {
83  ct->tar = tarArm;
84  BLI_strncpy(ct->subtarget, curbone->name, sizeof(ct->subtarget));
85  changed = true;
86  }
87  }
88  }
89 
90  BKE_constraint_targets_flush(con, &targets, 0);
91  }
92 
93  /* action constraint? (pose constraints only) */
94  if (con->type == CONSTRAINT_TYPE_ACTION) {
95  bActionConstraint *data = con->data;
96 
97  if (data->act) {
99  &tarArm->id, data->act, "pose.bones[", pchan->name, curbone->name, 0, 0, false);
100 
102  }
103  }
104  }
105 
106  if (changed) {
108  }
109 }
110 
111 /* userdata for joined_armature_fix_animdata_cb() */
112 typedef struct tJoinArmature_AdtFixData {
114 
117 
120 
121 /* Callback to pass to BKE_animdata_main_cb() for fixing driver ID's to point to the new ID. */
122 /* FIXME: For now, we only care about drivers here.
123  * When editing rigs, it's very rare to have animation on the rigs being edited already,
124  * so it should be safe to skip these.
125  */
126 static void joined_armature_fix_animdata_cb(ID *id, FCurve *fcu, void *user_data)
127 {
129  ID *src_id = &afd->srcArm->id;
130  ID *dst_id = &afd->tarArm->id;
131 
132  GHashIterator gh_iter;
133  bool changed = false;
134 
135  /* Fix paths - If this is the target object, it will have some "dirty" paths */
136  if ((id == src_id) && strstr(fcu->rna_path, "pose.bones[")) {
137  GHASH_ITER (gh_iter, afd->names_map) {
138  const char *old_name = BLI_ghashIterator_getKey(&gh_iter);
139  const char *new_name = BLI_ghashIterator_getValue(&gh_iter);
140 
141  /* only remap if changed; this still means there will be some
142  * waste if there aren't many drivers/keys */
143  if (!STREQ(old_name, new_name) && strstr(fcu->rna_path, old_name)) {
145  id, fcu->rna_path, "pose.bones", old_name, new_name, 0, 0, false);
146 
147  changed = true;
148 
149  /* we don't want to apply a second remapping on this driver now,
150  * so stop trying names, but keep fixing drivers
151  */
152  break;
153  }
154  }
155  }
156 
157  /* Driver targets */
158  if (fcu->driver) {
159  ChannelDriver *driver = fcu->driver;
160  DriverVar *dvar;
161 
162  /* Ensure that invalid drivers gets re-evaluated in case they become valid once the join
163  * operation is finished. */
164  fcu->flag &= ~FCURVE_DISABLED;
165  driver->flag &= ~DRIVER_FLAG_INVALID;
166 
167  /* Fix driver references to invalid ID's */
168  for (dvar = driver->variables.first; dvar; dvar = dvar->next) {
169  /* only change the used targets, since the others will need fixing manually anyway */
171  /* change the ID's used... */
172  if (dtar->id == src_id) {
173  dtar->id = dst_id;
174 
175  changed = true;
176 
177  /* also check on the subtarget...
178  * XXX: We duplicate the logic from drivers_path_rename_fix() here, with our own
179  * little twists so that we know that it isn't going to clobber the wrong data
180  */
181  if ((dtar->rna_path && strstr(dtar->rna_path, "pose.bones[")) || (dtar->pchan_name[0])) {
182  GHASH_ITER (gh_iter, afd->names_map) {
183  const char *old_name = BLI_ghashIterator_getKey(&gh_iter);
184  const char *new_name = BLI_ghashIterator_getValue(&gh_iter);
185 
186  /* only remap if changed */
187  if (!STREQ(old_name, new_name)) {
188  if ((dtar->rna_path) && strstr(dtar->rna_path, old_name)) {
189  /* Fix up path */
190  dtar->rna_path = BKE_animsys_fix_rna_path_rename(
191  id, dtar->rna_path, "pose.bones", old_name, new_name, 0, 0, false);
192  break; /* no need to try any more names for bone path */
193  }
194  if (STREQ(dtar->pchan_name, old_name)) {
195  /* Change target bone name */
196  BLI_strncpy(dtar->pchan_name, new_name, sizeof(dtar->pchan_name));
197  break; /* no need to try any more names for bone subtarget */
198  }
199  }
200  }
201  }
202  }
203  }
205  }
206  }
207 
208  if (changed) {
210  }
211 }
212 
213 /* Helper function for armature joining - link fixing */
215  Main *bmain, Object *tarArm, Object *srcArm, bPoseChannel *pchan, EditBone *curbone)
216 {
217  Object *ob;
218  bPose *pose;
219  bPoseChannel *pchant;
220 
221  /* let's go through all objects in database */
222  for (ob = bmain->objects.first; ob; ob = ob->id.next) {
223  /* do some object-type specific things */
224  if (ob->type == OB_ARMATURE) {
225  pose = ob->pose;
226  for (pchant = pose->chanbase.first; pchant; pchant = pchant->next) {
228  bmain, ob, tarArm, srcArm, pchan, curbone, &pchant->constraints);
229  }
230  }
231 
232  /* fix object-level constraints */
233  if (ob != srcArm) {
235  bmain, ob, tarArm, srcArm, pchan, curbone, &ob->constraints);
236  }
237 
238  /* See if an object is parented to this armature */
239  if (ob->parent && (ob->parent == srcArm)) {
240  /* Is object parented to a bone of this src armature? */
241  if (ob->partype == PARBONE) {
242  /* bone name in object */
243  if (STREQ(ob->parsubstr, pchan->name)) {
244  BLI_strncpy(ob->parsubstr, curbone->name, sizeof(ob->parsubstr));
245  }
246  }
247 
248  /* make tar armature be new parent */
249  ob->parent = tarArm;
250 
252  }
253  }
254 }
255 
257 {
258  Main *bmain = CTX_data_main(C);
260  Object *ob_active = CTX_data_active_object(C);
261  bArmature *arm = (ob_active) ? ob_active->data : NULL;
262  bPose *pose, *opose;
263  bPoseChannel *pchan, *pchann;
264  EditBone *curbone;
265  float mat[4][4], oimat[4][4];
266  bool ok = false;
267 
268  /* Ensure we're not in edit-mode and that the active object is an armature. */
269  if (!ob_active || ob_active->type != OB_ARMATURE) {
270  return OPERATOR_CANCELLED;
271  }
272  if (!arm || arm->edbo) {
273  return OPERATOR_CANCELLED;
274  }
275 
276  CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) {
277  if (ob_iter == ob_active) {
278  ok = true;
279  break;
280  }
281  }
282  CTX_DATA_END;
283 
284  /* that way the active object is always selected */
285  if (ok == false) {
286  BKE_report(op->reports, RPT_WARNING, "Active object is not a selected armature");
287  return OPERATOR_CANCELLED;
288  }
289 
290  /* Inverse transform for all selected armatures in this object,
291  * See #object_join_exec for detailed comment on why the safe version is used. */
292  invert_m4_m4_safe_ortho(oimat, ob_active->obmat);
293 
294  /* Get edit-bones of active armature to add edit-bones to */
295  ED_armature_to_edit(arm);
296 
297  /* Get pose of active object and move it out of pose-mode */
298  pose = ob_active->pose;
299  ob_active->mode &= ~OB_MODE_POSE;
300 
301  CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) {
302  if ((ob_iter->type == OB_ARMATURE) && (ob_iter != ob_active)) {
304  bArmature *curarm = ob_iter->data;
305 
306  /* we assume that each armature datablock is only used in a single place */
307  BLI_assert(ob_active->data != ob_iter->data);
308 
309  /* init callback data for fixing up AnimData links later */
310  afd.bmain = bmain;
311  afd.srcArm = ob_iter;
312  afd.tarArm = ob_active;
313  afd.names_map = BLI_ghash_str_new("join_armature_adt_fix");
314 
315  /* Make a list of edit-bones in current armature */
316  ED_armature_to_edit(ob_iter->data);
317 
318  /* Get Pose of current armature */
319  opose = ob_iter->pose;
320  ob_iter->mode &= ~OB_MODE_POSE;
321  // BASACT->flag &= ~OB_MODE_POSE;
322 
323  /* Find the difference matrix */
324  mul_m4_m4m4(mat, oimat, ob_iter->obmat);
325 
326  /* Copy bones and posechannels from the object to the edit armature */
327  for (pchan = opose->chanbase.first; pchan; pchan = pchann) {
328  pchann = pchan->next;
329  curbone = ED_armature_ebone_find_name(curarm->edbo, pchan->name);
330 
331  /* Get new name */
332  ED_armature_ebone_unique_name(arm->edbo, curbone->name, NULL);
333  BLI_ghash_insert(afd.names_map, BLI_strdup(pchan->name), curbone->name);
334 
335  /* Transform the bone */
336  {
337  float premat[4][4];
338  float postmat[4][4];
339  float difmat[4][4];
340  float imat[4][4];
341  float temp[3][3];
342 
343  /* Get the premat */
344  ED_armature_ebone_to_mat3(curbone, temp);
345 
346  unit_m4(premat); /* mul_m4_m3m4 only sets 3x3 part */
347  mul_m4_m3m4(premat, temp, mat);
348 
349  mul_m4_v3(mat, curbone->head);
350  mul_m4_v3(mat, curbone->tail);
351 
352  /* Get the postmat */
353  ED_armature_ebone_to_mat3(curbone, temp);
354  copy_m4_m3(postmat, temp);
355 
356  /* Find the roll */
357  invert_m4_m4(imat, premat);
358  mul_m4_m4m4(difmat, imat, postmat);
359 
360  curbone->roll -= atan2f(difmat[2][0], difmat[2][2]);
361  }
362 
363  /* Fix Constraints and Other Links to this Bone and Armature */
364  joined_armature_fix_links(bmain, ob_active, ob_iter, pchan, curbone);
365 
366  /* Rename pchan */
367  BLI_strncpy(pchan->name, curbone->name, sizeof(pchan->name));
368 
369  /* Jump Ship! */
370  BLI_remlink(curarm->edbo, curbone);
371  BLI_addtail(arm->edbo, curbone);
372 
373  /* Pose channel is moved from one storage to another, its UUID is still unique. */
374  BLI_remlink(&opose->chanbase, pchan);
375  BLI_addtail(&pose->chanbase, pchan);
378  }
379 
380  /* Armature ID itself is not freed below, however it has been modified (and is now completely
381  * empty). This needs to be told to the depsgraph, it will also ensure that the global
382  * memfile undo system properly detects the change.
383  *
384  * FIXME: Modifying an existing obdata because we are joining an object using it into another
385  * object is a very questionable behavior, which also does not match with other object types
386  * joining. */
387  DEG_id_tag_update_ex(bmain, &curarm->id, ID_RECALC_GEOMETRY);
388 
389  /* Fix all the drivers (and animation data) */
392 
393  /* Only copy over animdata now, after all the remapping has been done,
394  * so that we don't have to worry about ambiguities re which armature
395  * a bone came from!
396  */
397  if (ob_iter->adt) {
398  if (ob_active->adt == NULL) {
399  /* no animdata, so just use a copy of the whole thing */
400  ob_active->adt = BKE_animdata_copy(bmain, ob_iter->adt, 0);
401  }
402  else {
403  /* merge in data - we'll fix the drivers manually */
405  bmain, &ob_active->id, &ob_iter->id, ADT_MERGECOPY_KEEP_DST, false);
406  }
407  }
408 
409  if (curarm->adt) {
410  if (arm->adt == NULL) {
411  /* no animdata, so just use a copy of the whole thing */
412  arm->adt = BKE_animdata_copy(bmain, curarm->adt, 0);
413  }
414  else {
415  /* merge in data - we'll fix the drivers manually */
416  BKE_animdata_merge_copy(bmain, &arm->id, &curarm->id, ADT_MERGECOPY_KEEP_DST, false);
417  }
418  }
419 
420  /* Free the old object data */
421  ED_object_base_free_and_unlink(bmain, scene, ob_iter);
422  }
423  }
424  CTX_DATA_END;
425 
426  DEG_relations_tag_update(bmain); /* because we removed object(s) */
427 
428  ED_armature_from_edit(bmain, arm);
430 
434 
435  return OPERATOR_FINISHED;
436 }
437 
440 /* -------------------------------------------------------------------- */
444 /* Helper function for armature separating - link fixing */
445 static void separated_armature_fix_links(Main *bmain, Object *origArm, Object *newArm)
446 {
447  Object *ob;
448  bPoseChannel *pchan;
449  bConstraint *con;
450  ListBase *opchans, *npchans;
451 
452  /* Get reference to list of bones in original and new armatures. */
453  opchans = &origArm->pose->chanbase;
454  npchans = &newArm->pose->chanbase;
455 
456  /* let's go through all objects in database */
457  for (ob = bmain->objects.first; ob; ob = ob->id.next) {
458  /* do some object-type specific things */
459  if (ob->type == OB_ARMATURE) {
460  for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
461  for (con = pchan->constraints.first; con; con = con->next) {
462  ListBase targets = {NULL, NULL};
463  bConstraintTarget *ct;
464 
465  /* constraint targets */
466  if (BKE_constraint_targets_get(con, &targets)) {
467  for (ct = targets.first; ct; ct = ct->next) {
468  /* Any targets which point to original armature
469  * are redirected to the new one only if:
470  * - The target isn't origArm/newArm itself.
471  * - The target is one that can be found in newArm/origArm.
472  */
473  if (ct->subtarget[0] != 0) {
474  if (ct->tar == origArm) {
475  if (BLI_findstring(npchans, ct->subtarget, offsetof(bPoseChannel, name))) {
476  ct->tar = newArm;
477  }
478  }
479  else if (ct->tar == newArm) {
480  if (BLI_findstring(opchans, ct->subtarget, offsetof(bPoseChannel, name))) {
481  ct->tar = origArm;
482  }
483  }
484  }
485  }
486 
487  BKE_constraint_targets_flush(con, &targets, 0);
488  }
489  }
490  }
491  }
492 
493  /* fix object-level constraints */
494  if (ob != origArm) {
495  for (con = ob->constraints.first; con; con = con->next) {
496  ListBase targets = {NULL, NULL};
497  bConstraintTarget *ct;
498 
499  /* constraint targets */
500  if (BKE_constraint_targets_get(con, &targets)) {
501  for (ct = targets.first; ct; ct = ct->next) {
502  /* any targets which point to original armature are redirected to the new one only if:
503  * - the target isn't origArm/newArm itself
504  * - the target is one that can be found in newArm/origArm
505  */
506  if (ct->subtarget[0] != '\0') {
507  if (ct->tar == origArm) {
508  if (BLI_findstring(npchans, ct->subtarget, offsetof(bPoseChannel, name))) {
509  ct->tar = newArm;
510  }
511  }
512  else if (ct->tar == newArm) {
513  if (BLI_findstring(opchans, ct->subtarget, offsetof(bPoseChannel, name))) {
514  ct->tar = origArm;
515  }
516  }
517  }
518  }
519 
520  BKE_constraint_targets_flush(con, &targets, 0);
521  }
522  }
523  }
524 
525  /* See if an object is parented to this armature */
526  if (ob->parent && (ob->parent == origArm)) {
527  /* Is object parented to a bone of this src armature? */
528  if ((ob->partype == PARBONE) && (ob->parsubstr[0] != '\0')) {
529  if (BLI_findstring(npchans, ob->parsubstr, offsetof(bPoseChannel, name))) {
530  ob->parent = newArm;
531  }
532  }
533  }
534  }
535 }
536 
544 static void separate_armature_bones(Main *bmain, Object *ob, const bool is_select)
545 {
546  bArmature *arm = (bArmature *)ob->data;
547  bPoseChannel *pchan, *pchann;
548  EditBone *curbone;
549 
550  /* make local set of edit-bones to manipulate here */
551  ED_armature_to_edit(arm);
552 
553  /* go through pose-channels, checking if a bone should be removed */
554  for (pchan = ob->pose->chanbase.first; pchan; pchan = pchann) {
555  pchann = pchan->next;
556  curbone = ED_armature_ebone_find_name(arm->edbo, pchan->name);
557 
558  /* check if bone needs to be removed */
559  if (is_select == (EBONE_VISIBLE(arm, curbone) && (curbone->flag & BONE_SELECTED))) {
560 
561  /* Clear the bone->parent var of any bone that had this as its parent. */
562  LISTBASE_FOREACH (EditBone *, ebo, arm->edbo) {
563  if (ebo->parent == curbone) {
564  ebo->parent = NULL;
565  /* this is needed to prevent random crashes with in ED_armature_from_edit */
566  ebo->temp.p = NULL;
567  ebo->flag &= ~BONE_CONNECTED;
568  }
569  }
570 
571  /* clear the pchan->parent var of any pchan that had this as its parent */
572  LISTBASE_FOREACH (bPoseChannel *, pchn, &ob->pose->chanbase) {
573  if (pchn->parent == pchan) {
574  pchn->parent = NULL;
575  }
576  if (pchn->bbone_next == pchan) {
577  pchn->bbone_next = NULL;
578  }
579  if (pchn->bbone_prev == pchan) {
580  pchn->bbone_prev = NULL;
581  }
582  }
583 
584  /* Free any of the extra-data this pchan might have. */
585  BKE_pose_channel_free(pchan);
587 
588  /* get rid of unneeded bone */
589  bone_free(arm, curbone);
590  BLI_freelinkN(&ob->pose->chanbase, pchan);
591  }
592  }
593 
594  /* Exit edit-mode (recalculates pose-channels too). */
596  ED_armature_from_edit(bmain, ob->data);
598 }
599 
600 /* separate selected bones into their armature */
602 {
603  Main *bmain = CTX_data_main(C);
605  ViewLayer *view_layer = CTX_data_view_layer(C);
606  bool ok = false;
607 
608  /* set wait cursor in case this takes a while */
609  WM_cursor_wait(true);
610 
611  uint bases_len = 0;
613  view_layer, CTX_wm_view3d(C), &bases_len);
614 
615  for (uint base_index = 0; base_index < bases_len; base_index++) {
616  Base *base_old = bases[base_index];
617  Object *ob_old = base_old->object;
618 
619  {
620  bArmature *arm_old = ob_old->data;
621  bool has_selected_bone = false;
622  bool has_selected_any = false;
623  LISTBASE_FOREACH (EditBone *, ebone, arm_old->edbo) {
624  if (EBONE_VISIBLE(arm_old, ebone)) {
625  if (ebone->flag & BONE_SELECTED) {
626  has_selected_bone = true;
627  break;
628  }
629  if (ebone->flag & (BONE_TIPSEL | BONE_ROOTSEL)) {
630  has_selected_any = true;
631  }
632  }
633  }
634  if (has_selected_bone == false) {
635  if (has_selected_any) {
636  /* Without this, we may leave head/tail selected
637  * which isn't expected after separating. */
639  }
640  continue;
641  }
642  }
643 
644  /* We are going to do this as follows (unlike every other instance of separate):
645  * 1. Exit edit-mode & pose-mode for active armature/base. Take note of what this is.
646  * 2. Duplicate base - BASACT is the new one now
647  * 3. For each of the two armatures,
648  * enter edit-mode -> remove appropriate bones -> exit edit-mode + recalculate.
649  * 4. Fix constraint links
650  * 5. Make original armature active and enter edit-mode
651  */
652 
653  /* 1) store starting settings and exit edit-mode */
654  ob_old->mode &= ~OB_MODE_POSE;
655 
656  ED_armature_from_edit(bmain, ob_old->data);
657  ED_armature_edit_free(ob_old->data);
658 
659  /* 2) duplicate base */
660 
661  /* Only duplicate linked armature but take into account
662  * user preferences for duplicating actions. */
663  short dupflag = USER_DUP_ARM | (U.dupflag & USER_DUP_ACT);
664  Base *base_new = ED_object_add_duplicate(bmain, scene, view_layer, base_old, dupflag);
665  Object *ob_new = base_new->object;
666 
668 
669  /* 3) remove bones that shouldn't still be around on both armatures */
670  separate_armature_bones(bmain, ob_old, true);
671  separate_armature_bones(bmain, ob_new, false);
672 
673  /* 4) fix links before depsgraph flushes, err... or after? */
674  separated_armature_fix_links(bmain, ob_old, ob_new);
675 
676  DEG_id_tag_update(&ob_old->id, ID_RECALC_GEOMETRY); /* this is the original one */
677  DEG_id_tag_update(&ob_new->id, ID_RECALC_GEOMETRY); /* this is the separated one */
678 
679  /* 5) restore original conditions */
680  ED_armature_to_edit(ob_old->data);
682 
683  /* parents tips remain selected when connected children are removed. */
685 
686  ok = true;
687 
688  /* NOTE: notifier might evolve. */
690  }
691  MEM_freeN(bases);
692 
693  /* Recalculate/redraw + cleanup */
694  WM_cursor_wait(false);
695 
696  if (ok) {
697  BKE_report(op->reports, RPT_INFO, "Separated bones");
699  }
700 
701  return OPERATOR_FINISHED;
702 }
703 
705 {
706  /* identifiers */
707  ot->name = "Separate Bones";
708  ot->idname = "ARMATURE_OT_separate";
709  ot->description = "Isolate selected bones into a separate armature";
710 
711  /* callbacks */
715 
716  /* flags */
718 }
719 
722 /* -------------------------------------------------------------------- */
726 /* armature parenting options */
727 #define ARM_PAR_CONNECT 1
728 #define ARM_PAR_OFFSET 2
729 
730 /* armature un-parenting options */
731 #define ARM_PAR_CLEAR 1
732 #define ARM_PAR_CLEAR_DISCONNECT 2
733 
734 /* check for null, before calling! */
736 {
737  bone->flag |= BONE_CONNECTED;
738  copy_v3_v3(bone->head, bone->parent->tail);
739  bone->rad_head = bone->parent->rad_tail;
740 }
741 
743  EditBone *selbone,
744  EditBone *actbone,
745  short mode)
746 {
747  EditBone *ebone;
748  float offset[3];
749 
750  if ((selbone->parent) && (selbone->flag & BONE_CONNECTED)) {
751  selbone->parent->flag &= ~BONE_TIPSEL;
752  }
753 
754  /* make actbone the parent of selbone */
755  selbone->parent = actbone;
756 
757  /* in actbone tree we cannot have a loop */
758  for (ebone = actbone->parent; ebone; ebone = ebone->parent) {
759  if (ebone->parent == selbone) {
760  ebone->parent = NULL;
761  ebone->flag &= ~BONE_CONNECTED;
762  }
763  }
764 
765  if (mode == ARM_PAR_CONNECT) {
766  /* Connected: Child bones will be moved to the parent tip */
767  selbone->flag |= BONE_CONNECTED;
768  sub_v3_v3v3(offset, actbone->tail, selbone->head);
769 
770  copy_v3_v3(selbone->head, actbone->tail);
771  selbone->rad_head = actbone->rad_tail;
772 
773  add_v3_v3(selbone->tail, offset);
774 
775  /* offset for all its children */
776  for (ebone = edbo->first; ebone; ebone = ebone->next) {
777  EditBone *par;
778 
779  for (par = ebone->parent; par; par = par->parent) {
780  if (par == selbone) {
781  add_v3_v3(ebone->head, offset);
782  add_v3_v3(ebone->tail, offset);
783  break;
784  }
785  }
786  }
787  }
788  else {
789  /* Offset: Child bones will retain their distance from the parent tip */
790  selbone->flag &= ~BONE_CONNECTED;
791  }
792 }
793 
795  {ARM_PAR_CONNECT, "CONNECTED", 0, "Connected", ""},
796  {ARM_PAR_OFFSET, "OFFSET", 0, "Keep Offset", ""},
797  {0, NULL, 0, NULL, NULL},
798 };
799 
801 {
803  bArmature *arm = (bArmature *)ob->data;
804  EditBone *actbone = CTX_data_active_bone(C);
805  EditBone *actmirb = NULL;
806  short val = RNA_enum_get(op->ptr, "type");
807 
808  /* there must be an active bone */
809  if (actbone == NULL) {
810  BKE_report(op->reports, RPT_ERROR, "Operation requires an active bone");
811  return OPERATOR_CANCELLED;
812  }
813  if (arm->flag & ARM_MIRROR_EDIT) {
814  /* For X-Axis Mirror Editing option, we may need a mirror copy of actbone:
815  * - If there's a mirrored copy of selbone, try to find a mirrored copy of actbone
816  * (i.e. selbone="child.L" and actbone="parent.L", find "child.R" and "parent.R").
817  * This is useful for arm-chains, for example parenting lower arm to upper arm.
818  * - If there's no mirrored copy of actbone (i.e. actbone = "parent.C" or "parent")
819  * then just use actbone. Useful when doing upper arm to spine.
820  */
821  actmirb = ED_armature_ebone_get_mirrored(arm->edbo, actbone);
822  if (actmirb == NULL) {
823  actmirb = actbone;
824  }
825  }
826 
827  /* If there is only 1 selected bone, we assume that it is the active bone,
828  * since a user will need to have clicked on a bone (thus selecting it) to make it active. */
829  bool is_active_only_selected = false;
830  if (actbone->flag & BONE_SELECTED) {
831  is_active_only_selected = true;
832  LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
833  if (EBONE_EDITABLE(ebone) && (ebone->flag & BONE_SELECTED)) {
834  if (ebone != actbone) {
835  is_active_only_selected = false;
836  break;
837  }
838  }
839  }
840  }
841 
842  if (is_active_only_selected) {
843  /* When only the active bone is selected, and it has a parent,
844  * connect it to the parent, as that is the only possible outcome.
845  */
846  if (actbone->parent) {
848 
849  if ((arm->flag & ARM_MIRROR_EDIT) && (actmirb->parent)) {
851  }
852  }
853  }
854  else {
855  /* Parent 'selected' bones to the active one:
856  * - The context iterator contains both selected bones and their mirrored copies,
857  * so we assume that unselected bones are mirrored copies of some selected bone.
858  * - Since the active one (and/or its mirror) will also be selected, we also need
859  * to check that we are not trying to operate on them, since such an operation
860  * would cause errors.
861  */
862 
863  /* Parent selected bones to the active one. */
864  LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
865  if (EBONE_EDITABLE(ebone) && (ebone->flag & BONE_SELECTED)) {
866  if (ebone != actbone) {
867  bone_connect_to_new_parent(arm->edbo, ebone, actbone, val);
868  }
869 
870  if (arm->flag & ARM_MIRROR_EDIT) {
871  EditBone *ebone_mirror = ED_armature_ebone_get_mirrored(arm->edbo, ebone);
872  if (ebone_mirror && (ebone_mirror->flag & BONE_SELECTED) == 0) {
873  if (ebone_mirror != actmirb) {
874  bone_connect_to_new_parent(arm->edbo, ebone_mirror, actmirb, val);
875  }
876  }
877  }
878  }
879  }
880  }
881 
882  /* NOTE: notifier might evolve. */
885 
886  return OPERATOR_FINISHED;
887 }
888 
890  wmOperator *UNUSED(op),
891  const wmEvent *UNUSED(event))
892 {
893  /* False when all selected bones are parented to the active bone. */
894  bool enable_offset = false;
895  /* False when all selected bones are connected to the active bone. */
896  bool enable_connect = false;
897  {
899  bArmature *arm = ob->data;
900  EditBone *actbone = arm->act_edbone;
901  LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
902  if (!EBONE_EDITABLE(ebone) || !(ebone->flag & BONE_SELECTED)) {
903  continue;
904  }
905  if (ebone == actbone) {
906  continue;
907  }
908 
909  if (ebone->parent != actbone) {
910  enable_offset = true;
911  enable_connect = true;
912  break;
913  }
914  if (!(ebone->flag & BONE_CONNECTED)) {
915  enable_connect = true;
916  }
917  }
918  }
919 
921  C, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Make Parent"), ICON_NONE);
922  uiLayout *layout = UI_popup_menu_layout(pup);
923 
924  uiLayout *row_offset = uiLayoutRow(layout, false);
925  uiLayoutSetEnabled(row_offset, enable_offset);
926  uiItemEnumO(row_offset, "ARMATURE_OT_parent_set", NULL, 0, "type", ARM_PAR_OFFSET);
927 
928  uiLayout *row_connect = uiLayoutRow(layout, false);
929  uiLayoutSetEnabled(row_connect, enable_connect);
930  uiItemEnumO(row_connect, "ARMATURE_OT_parent_set", NULL, 0, "type", ARM_PAR_CONNECT);
931 
932  UI_popup_menu_end(C, pup);
933 
934  return OPERATOR_INTERFACE;
935 }
936 
938 {
939  /* identifiers */
940  ot->name = "Make Parent";
941  ot->idname = "ARMATURE_OT_parent_set";
942  ot->description = "Set the active bone as the parent of the selected bones";
943 
944  /* api callbacks */
948 
949  /* flags */
951 
952  RNA_def_enum(
953  ot->srna, "type", prop_editarm_make_parent_types, 0, "Parent Type", "Type of parenting");
954 }
955 
957  {ARM_PAR_CLEAR, "CLEAR", 0, "Clear Parent", ""},
958  {ARM_PAR_CLEAR_DISCONNECT, "DISCONNECT", 0, "Disconnect Bone", ""},
959  {0, NULL, 0, NULL, NULL},
960 };
961 
962 static void editbone_clear_parent(EditBone *ebone, int mode)
963 {
964  if (ebone->parent) {
965  /* for nice selection */
966  ebone->parent->flag &= ~BONE_TIPSEL;
967  }
968 
969  if (mode == 1) {
970  ebone->parent = NULL;
971  }
972  ebone->flag &= ~BONE_CONNECTED;
973 }
974 
976 {
977  ViewLayer *view_layer = CTX_data_view_layer(C);
978  const int val = RNA_enum_get(op->ptr, "type");
979 
980  CTX_DATA_BEGIN (C, EditBone *, ebone, selected_editable_bones) {
981  editbone_clear_parent(ebone, val);
982  }
983  CTX_DATA_END;
984 
985  uint objects_len = 0;
987  view_layer, CTX_wm_view3d(C), &objects_len);
988  for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
989  Object *ob = objects[ob_index];
990  bArmature *arm = ob->data;
991  bool changed = false;
992 
993  LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
994  if (EBONE_EDITABLE(ebone)) {
995  changed = true;
996  break;
997  }
998  }
999 
1000  if (!changed) {
1001  continue;
1002  }
1003 
1005 
1006  /* NOTE: notifier might evolve. */
1008  }
1009  MEM_freeN(objects);
1010 
1011  return OPERATOR_FINISHED;
1012 }
1013 
1015  wmOperator *UNUSED(op),
1016  const wmEvent *UNUSED(event))
1017 {
1018  /* False when no selected bones are connected to the active bone. */
1019  bool enable_disconnect = false;
1020  /* False when no selected bones are parented to the active bone. */
1021  bool enable_clear = false;
1022  {
1023  Object *ob = CTX_data_edit_object(C);
1024  bArmature *arm = ob->data;
1025  LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
1026  if (!EBONE_EDITABLE(ebone) || !(ebone->flag & BONE_SELECTED)) {
1027  continue;
1028  }
1029  if (ebone->parent == NULL) {
1030  continue;
1031  }
1032  enable_clear = true;
1033 
1034  if (ebone->flag & BONE_CONNECTED) {
1035  enable_disconnect = true;
1036  break;
1037  }
1038  }
1039  }
1040 
1042  C, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Clear Parent"), ICON_NONE);
1043  uiLayout *layout = UI_popup_menu_layout(pup);
1044 
1045  uiLayout *row_clear = uiLayoutRow(layout, false);
1046  uiLayoutSetEnabled(row_clear, enable_clear);
1047  uiItemEnumO(row_clear, "ARMATURE_OT_parent_clear", NULL, 0, "type", ARM_PAR_CLEAR);
1048 
1049  uiLayout *row_disconnect = uiLayoutRow(layout, false);
1050  uiLayoutSetEnabled(row_disconnect, enable_disconnect);
1051  uiItemEnumO(
1052  row_disconnect, "ARMATURE_OT_parent_clear", NULL, 0, "type", ARM_PAR_CLEAR_DISCONNECT);
1053 
1054  UI_popup_menu_end(C, pup);
1055 
1056  return OPERATOR_INTERFACE;
1057 }
1058 
1060 {
1061  /* identifiers */
1062  ot->name = "Clear Parent";
1063  ot->idname = "ARMATURE_OT_parent_clear";
1064  ot->description =
1065  "Remove the parent-child relationship between selected bones and their parents";
1066 
1067  /* api callbacks */
1071 
1072  /* flags */
1074 
1075  ot->prop = RNA_def_enum(ot->srna,
1076  "type",
1078  0,
1079  "Clear Type",
1080  "What way to clear parenting");
1081 }
1082 
Blender kernel action and pose functionality.
void BKE_pose_channels_hash_free(struct bPose *pose)
Definition: action.c:937
void BKE_pose_channel_free(struct bPoseChannel *pchan)
Definition: action.c:1078
@ ADT_MERGECOPY_KEEP_DST
struct AnimData * BKE_animdata_copy(struct Main *bmain, struct AnimData *adt, int flag)
Definition: anim_data.c:275
void BKE_animdata_merge_copy(struct Main *bmain, struct ID *dst_id, struct ID *src_id, eAnimData_MergeCopy_Modes action_mode, bool fix_drivers)
Definition: anim_data.c:387
void BKE_fcurves_main_cb(struct Main *bmain, ID_FCurve_Edit_Callback func, void *user_data)
Definition: anim_data.c:1166
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.c:860
void BKE_action_fix_paths_rename(struct ID *owner_id, struct bAction *act, const char *prefix, const char *oldName, const char *newName, int oldSubscript, int newSubscript, bool verify_paths)
Definition: anim_data.c:915
void BKE_constraint_targets_flush(struct bConstraint *con, struct ListBase *targets, bool no_copy)
Definition: constraint.c:6186
int BKE_constraint_targets_get(struct bConstraint *con, struct ListBase *r_targets)
Definition: constraint.c:6157
struct Scene * CTX_data_scene(const bContext *C)
Definition: context.c:1090
struct Object * CTX_data_edit_object(const bContext *C)
Definition: context.c:1370
#define CTX_DATA_BEGIN(C, Type, instance, member)
Definition: BKE_context.h:269
struct ViewLayer * CTX_data_view_layer(const bContext *C)
Definition: context.c:1100
struct Object * CTX_data_active_object(const bContext *C)
Definition: context.c:1353
struct View3D * CTX_wm_view3d(const bContext *C)
Definition: context.c:784
struct EditBone * CTX_data_active_bone(const bContext *C)
Definition: context.c:1395
struct Main * CTX_data_main(const bContext *C)
Definition: context.c:1074
#define CTX_DATA_END
Definition: BKE_context.h:278
#define DRIVER_TARGETS_USED_LOOPER_BEGIN(dvar)
#define DRIVER_TARGETS_LOOPER_END
#define BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, v3d, r_len)
Definition: BKE_layer.h:542
#define BKE_view_layer_array_from_bases_in_edit_mode_unique_data(view_layer, v3d, r_len)
Definition: BKE_layer.h:546
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition: report.c:83
#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:298
BLI_INLINE void * BLI_ghashIterator_getValue(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.h:302
GHash * BLI_ghash_str_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
#define GHASH_ITER(gh_iter_, ghash_)
Definition: BLI_ghash.h:321
void BLI_ghash_insert(GHash *gh, void *key, void *val)
Definition: BLI_ghash.c:710
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition: BLI_ghash.c:863
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
void BLI_freelinkN(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:239
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:80
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:100
void * BLI_findstring(const struct ListBase *listbase, const char *id, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
Definition: math_matrix.c:259
void invert_m4_m4_safe_ortho(float Ainv[4][4], const float A[4][4])
Definition: math_matrix.c:3188
void unit_m4(float m[4][4])
Definition: rct.c:1090
void copy_m4_m3(float m1[4][4], const float m2[3][3])
Definition: math_matrix.c:102
bool invert_m4_m4(float R[4][4], const float A[4][4])
Definition: math_matrix.c:1287
void mul_m4_v3(const float M[4][4], float r[3])
Definition: math_matrix.c:729
void mul_m4_m3m4(float R[4][4], const float A[3][3], const float B[4][4])
Definition: math_matrix.c:500
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() ATTR_MALLOC
Definition: string.c:42
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL()
Definition: string.c:64
unsigned int uint
Definition: BLI_sys_types.h:67
#define UNUSED(x)
#define STREQ(a, b)
#define CTX_IFACE_(context, msgid)
#define BLT_I18NCONTEXT_OPERATOR_DEFAULT
void DEG_id_tag_update_ex(struct Main *bmain, struct ID *id, int flag)
void DEG_id_tag_update(struct ID *id, int flag)
void DEG_relations_tag_update(struct Main *bmain)
@ ID_RECALC_COPY_ON_WRITE
Definition: DNA_ID.h:834
@ ID_RECALC_SELECT
Definition: DNA_ID.h:818
@ ID_RECALC_GEOMETRY
Definition: DNA_ID.h:791
@ 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.
@ OB_ARMATURE
@ PARBONE
@ USER_DUP_ARM
@ USER_DUP_ACT
@ OPERATOR_CANCELLED
@ OPERATOR_INTERFACE
@ OPERATOR_FINISHED
#define EBONE_VISIBLE(arm, ebone)
Definition: ED_armature.h:47
#define EBONE_EDITABLE(ebone)
Definition: ED_armature.h:55
struct Base * ED_object_add_duplicate(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, struct Base *base, eDupli_ID_Flags dupflag)
Definition: object_add.cc:3632
void ED_object_base_free_and_unlink(struct Main *bmain, struct Scene *scene, struct Object *ob)
Definition: object_add.cc:2190
void ED_outliner_select_sync_from_object_tag(struct bContext *C)
bool ED_operator_editarmature(struct bContext *C)
Definition: screen_ops.c:466
Read Guarded memory(de)allocation.
#define C
Definition: RandGen.cpp:25
void uiLayoutSetEnabled(uiLayout *layout, bool enabled)
struct uiLayout * UI_popup_menu_layout(uiPopupMenu *pup)
void uiItemEnumO(uiLayout *layout, const char *opname, const char *name, int icon, const char *propname, int value)
uiLayout * uiLayoutRow(uiLayout *layout, bool align)
void UI_popup_menu_end(struct bContext *C, struct uiPopupMenu *pup)
uiPopupMenu * UI_popup_menu_begin(struct bContext *C, const char *title, int icon) ATTR_NONNULL()
@ OPTYPE_UNDO
Definition: WM_types.h:148
@ OPTYPE_REGISTER
Definition: WM_types.h:146
#define ND_OB_ACTIVE
Definition: WM_types.h:388
#define NC_SCENE
Definition: WM_types.h:328
#define ND_LAYER_CONTENT
Definition: WM_types.h:402
#define ND_POSE
Definition: WM_types.h:407
#define ND_BONE_SELECT
Definition: WM_types.h:409
#define NC_OBJECT
Definition: WM_types.h:329
void bone_free(struct bArmature *arm, struct 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 void joined_armature_fix_links(Main *bmain, Object *tarArm, Object *srcArm, bPoseChannel *pchan, EditBone *curbone)
static const EnumPropertyItem prop_editarm_clear_parent_types[]
void ARMATURE_OT_parent_clear(wmOperatorType *ot)
static int armature_parent_clear_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event))
#define ARM_PAR_CLEAR_DISCONNECT
static void joined_armature_fix_animdata_cb(ID *id, FCurve *fcu, void *user_data)
static int armature_parent_set_exec(bContext *C, wmOperator *op)
static void joined_armature_fix_links_constraints(Main *bmain, Object *ob, Object *tarArm, Object *srcArm, bPoseChannel *pchan, EditBone *curbone, ListBase *lb)
static int separate_armature_exec(bContext *C, wmOperator *op)
static int armature_parent_clear_exec(bContext *C, wmOperator *op)
int ED_armature_join_objects_exec(bContext *C, wmOperator *op)
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)
static int armature_parent_set_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event))
#define ARM_PAR_OFFSET
void ARMATURE_OT_separate(wmOperatorType *ot)
static void editbone_clear_parent(EditBone *ebone, int mode)
#define ARM_PAR_CONNECT
#define ARM_PAR_CLEAR
static const EnumPropertyItem prop_editarm_make_parent_types[]
struct tJoinArmature_AdtFixData tJoinArmature_AdtFixData
static void bone_connect_to_existing_parent(EditBone *bone)
bool ED_armature_edit_deselect_all(Object *obedit)
void ED_armature_edit_refresh_layer_used(bArmature *arm)
void ED_armature_edit_sync_selection(ListBase *edbo)
void ED_armature_ebone_to_mat3(EditBone *ebone, float r_mat[3][3])
void ED_armature_edit_free(struct bArmature *arm)
void ED_armature_from_edit(Main *bmain, bArmature *arm)
EditBone * ED_armature_ebone_find_name(const ListBase *edbo, const char *name)
void ED_armature_to_edit(bArmature *arm)
EditBone * ED_armature_ebone_get_mirrored(const ListBase *edbo, EditBone *ebo)
unsigned int U
Definition: btGjkEpa3.h:78
Scene scene
void * user_data
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
#define atan2f(x, y)
Definition: metal/compat.h:227
int RNA_enum_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:5004
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, int default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3783
struct Object * object
ListBase variables
struct DriverVar * next
char name[64]
Definition: BKE_armature.h:43
struct EditBone * next
Definition: BKE_armature.h:33
float tail[3]
Definition: BKE_armature.h:54
struct EditBone * parent
Definition: BKE_armature.h:41
float rad_tail
Definition: BKE_armature.h:68
float rad_head
Definition: BKE_armature.h:68
float head[3]
Definition: BKE_armature.h:53
char * rna_path
ChannelDriver * driver
short flag
Definition: DNA_ID.h:368
void * next
Definition: DNA_ID.h:369
void * first
Definition: DNA_listBase.h:31
Definition: BKE_main.h:121
ListBase objects
Definition: BKE_main.h:170
short partype
ListBase constraints
struct bPose * pose
float obmat[4][4]
struct AnimData * adt
struct Object * parent
void * data
char parsubstr[64]
struct AnimData * adt
struct EditBone * act_edbone
ListBase * edbo
struct bConstraintTarget * next
struct bConstraint * next
ListBase constraints
struct bPoseChannel * next
ListBase chanbase
int(* invoke)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:919
const char * name
Definition: WM_types.h:888
const char * idname
Definition: WM_types.h:890
bool(* poll)(struct bContext *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:943
struct StructRNA * srna
Definition: WM_types.h:969
const char * description
Definition: WM_types.h:893
int(* exec)(struct bContext *, struct wmOperator *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:903
PropertyRNA * prop
Definition: WM_types.h:981
struct ReportList * reports
struct PointerRNA * ptr
void WM_cursor_wait(bool val)
Definition: wm_cursors.c:209
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition: wm_files.c:3479
int WM_operator_confirm(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))