Blender  V3.3
pose_group.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2008 Blender Foundation. All rights reserved. */
3 
9 #include <string.h>
10 
11 #include "MEM_guardedalloc.h"
12 
13 #include "BLI_blenlib.h"
14 
15 #include "DNA_armature_types.h"
16 #include "DNA_object_types.h"
17 
18 #include "BKE_action.h"
19 #include "BKE_armature.h"
20 #include "BKE_context.h"
21 
22 #include "DEG_depsgraph.h"
23 
24 #include "RNA_access.h"
25 #include "RNA_define.h"
26 
27 #include "WM_api.h"
28 #include "WM_types.h"
29 
30 #include "ED_armature.h"
31 #include "ED_outliner.h"
32 #include "ED_screen.h"
33 
34 #include "UI_interface.h"
35 #include "UI_resources.h"
36 
37 #include "armature_intern.h"
38 
39 /* ********************************************** */
40 /* Bone Groups */
41 
42 static bool pose_group_poll(bContext *C)
43 {
45  CTX_wm_operator_poll_msg_set(C, "Bone groups can only be edited in pose mode");
46  return false;
47  }
48 
50  if (ID_IS_OVERRIDE_LIBRARY(obpose)) {
51  CTX_wm_operator_poll_msg_set(C, "Cannot edit bone groups for library overrides");
52  return false;
53  }
54 
55  return true;
56 }
57 
59 {
61 
62  /* only continue if there's an object and pose */
63  if (ELEM(NULL, ob, ob->pose)) {
64  return OPERATOR_CANCELLED;
65  }
66 
67  /* for now, just call the API function for this */
69 
70  /* notifiers for updates */
72 
73  return OPERATOR_FINISHED;
74 }
75 
77 {
78  /* identifiers */
79  ot->name = "Add Bone Group";
80  ot->idname = "POSE_OT_group_add";
81  ot->description = "Add a new bone group";
82 
83  /* api callbacks */
86 
87  /* flags */
89 }
90 
92 {
94 
95  /* only continue if there's an object and pose */
96  if (ELEM(NULL, ob, ob->pose)) {
97  return OPERATOR_CANCELLED;
98  }
99 
100  /* for now, just call the API function for this */
102 
103  /* notifiers for updates */
106 
107  return OPERATOR_FINISHED;
108 }
109 
111 {
112  /* identifiers */
113  ot->name = "Remove Bone Group";
114  ot->idname = "POSE_OT_group_remove";
115  ot->description = "Remove the active bone group";
116 
117  /* api callbacks */
120 
121  /* flags */
123 }
124 
125 /* ------------ */
126 
127 /* invoke callback which presents a list of bone-groups for the user to choose from */
128 static int pose_groups_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
129 {
131  bPose *pose;
132  PropertyRNA *prop = RNA_struct_find_property(op->ptr, "type");
133 
134  uiPopupMenu *pup;
135  uiLayout *layout;
136  bActionGroup *grp;
137  int i;
138 
139  /* only continue if there's an object, and a pose there too */
140  if (ELEM(NULL, ob, ob->pose)) {
141  return OPERATOR_CANCELLED;
142  }
143  pose = ob->pose;
144 
145  /* If group index is set, try to use it! */
146  if (RNA_property_is_set(op->ptr, prop)) {
147  const int num_groups = BLI_listbase_count(&pose->agroups);
148  const int group = RNA_property_int_get(op->ptr, prop);
149 
150  /* just use the active group index, and call the exec callback for the calling operator */
151  if (group > 0 && group <= num_groups) {
152  return op->type->exec(C, op);
153  }
154  }
155 
156  /* if there's no active group (or active is invalid), create a new menu to find it */
157  if (pose->active_group <= 0) {
158  /* create a new menu, and start populating it with group names */
159  pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
160  layout = UI_popup_menu_layout(pup);
161 
162  /* special entry - allow creating a new group, then using that
163  * (not to be used for removing though)
164  */
165  if (strstr(op->idname, "assign")) {
166  uiItemIntO(layout, "New Group", ICON_NONE, op->idname, "type", 0);
167  uiItemS(layout);
168  }
169 
170  /* add entries for each group */
171  for (grp = pose->agroups.first, i = 1; grp; grp = grp->next, i++) {
172  uiItemIntO(layout, grp->name, ICON_NONE, op->idname, "type", i);
173  }
174 
175  /* finish building the menu, and process it (should result in calling self again) */
176  UI_popup_menu_end(C, pup);
177 
178  return OPERATOR_INTERFACE;
179  }
180 
181  /* just use the active group index, and call the exec callback for the calling operator */
182  RNA_int_set(op->ptr, "type", pose->active_group);
183  return op->type->exec(C, op);
184 }
185 
186 /* Assign selected pchans to the bone group that the user selects */
188 {
190  bPose *pose;
191  bool done = false;
192 
193  /* only continue if there's an object, and a pose there too */
194  if (ELEM(NULL, ob, ob->pose)) {
195  return OPERATOR_CANCELLED;
196  }
197 
198  pose = ob->pose;
199 
200  /* set the active group number to the one from operator props
201  * - if 0 after this, make a new group...
202  */
203  pose->active_group = RNA_int_get(op->ptr, "type");
204  if (pose->active_group == 0) {
206  }
207 
208  /* add selected bones to group then */
210  pchan->agrp_index = pose->active_group;
211  done = true;
212  }
214 
215  /* notifiers for updates */
218 
219  /* report done status */
220  if (done) {
221  return OPERATOR_FINISHED;
222  }
223  return OPERATOR_CANCELLED;
224 }
225 
227 {
228  /* identifiers */
229  ot->name = "Add Selected to Bone Group";
230  ot->idname = "POSE_OT_group_assign";
231  ot->description = "Add selected bones to the chosen bone group";
232 
233  /* api callbacks */
237 
238  /* flags */
240 
241  /* properties */
242  RNA_def_int(ot->srna, "type", 0, 0, INT_MAX, "Bone Group Index", "", 0, 10);
243 }
244 
246 {
248  bool done = false;
249 
250  /* only continue if there's an object, and a pose there too */
251  if (ELEM(NULL, ob, ob->pose)) {
252  return OPERATOR_CANCELLED;
253  }
254 
255  /* find selected bones to remove from all bone groups */
257  if (pchan->agrp_index) {
258  pchan->agrp_index = 0;
259  done = true;
260  }
261  }
263 
264  /* notifiers for updates */
267 
268  /* report done status */
269  if (done) {
270  return OPERATOR_FINISHED;
271  }
272  return OPERATOR_CANCELLED;
273 }
274 
276 {
277  /* identifiers */
278  ot->name = "Remove Selected from Bone Groups";
279  ot->idname = "POSE_OT_group_unassign";
280  ot->description = "Remove selected bones from all bone groups";
281 
282  /* api callbacks */
285 
286  /* flags */
288 }
289 
291 {
293  bPose *pose = (ob) ? ob->pose : NULL;
294  bPoseChannel *pchan;
295  bActionGroup *grp;
296  int dir = RNA_enum_get(op->ptr, "direction");
297 
298  if (ELEM(NULL, ob, pose)) {
299  return OPERATOR_CANCELLED;
300  }
301  if (pose->active_group <= 0) {
302  return OPERATOR_CANCELLED;
303  }
304 
305  /* get group to move */
306  grp = BLI_findlink(&pose->agroups, pose->active_group - 1);
307  if (grp == NULL) {
308  return OPERATOR_CANCELLED;
309  }
310 
311  /* move bone group */
312  if (BLI_listbase_link_move(&pose->agroups, grp, dir)) {
313  int grpIndexA = pose->active_group;
314  int grpIndexB = grpIndexA + dir;
315 
316  pose->active_group += dir;
317  /* fix changed bone group indices in bones (swap grpIndexA with grpIndexB) */
318  for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
319  if (pchan->agrp_index == grpIndexB) {
320  pchan->agrp_index = grpIndexA;
321  }
322  else if (pchan->agrp_index == grpIndexA) {
323  pchan->agrp_index = grpIndexB;
324  }
325  }
326 
327  /* notifiers for updates */
329  }
330 
331  return OPERATOR_FINISHED;
332 }
333 
335 {
336  static const EnumPropertyItem group_slot_move[] = {
337  {-1, "UP", 0, "Up", ""},
338  {1, "DOWN", 0, "Down", ""},
339  {0, NULL, 0, NULL, NULL},
340  };
341 
342  /* identifiers */
343  ot->name = "Move Bone Group";
344  ot->idname = "POSE_OT_group_move";
345  ot->description = "Change position of active Bone Group in list of Bone Groups";
346 
347  /* api callbacks */
350 
351  /* flags */
353 
355  "direction",
356  group_slot_move,
357  0,
358  "Direction",
359  "Direction to move the active Bone Group towards");
360 }
361 
362 /* bone group sort element */
363 typedef struct tSortActionGroup {
365  int index;
367 
368 /* compare bone groups by name */
369 static int compare_agroup(const void *sgrp_a_ptr, const void *sgrp_b_ptr)
370 {
371  const tSortActionGroup *sgrp_a = sgrp_a_ptr;
372  const tSortActionGroup *sgrp_b = sgrp_b_ptr;
373 
374  return strcmp(sgrp_a->agrp->name, sgrp_b->agrp->name);
375 }
376 
378 {
380  bPose *pose = (ob) ? ob->pose : NULL;
381  bPoseChannel *pchan;
382  tSortActionGroup *agrp_array;
383  bActionGroup *agrp;
384 
385  if (ELEM(NULL, ob, pose)) {
386  return OPERATOR_CANCELLED;
387  }
388  if (pose->active_group <= 0) {
389  return OPERATOR_CANCELLED;
390  }
391 
392  /* create temporary array with bone groups and indices */
393  int agrp_count = BLI_listbase_count(&pose->agroups);
394  agrp_array = MEM_mallocN(sizeof(tSortActionGroup) * agrp_count, "sort bone groups");
395  int i;
396  for (agrp = pose->agroups.first, i = 0; agrp; agrp = agrp->next, i++) {
397  BLI_assert(i < agrp_count);
398  agrp_array[i].agrp = agrp;
399  agrp_array[i].index = i + 1;
400  }
401 
402  /* sort bone groups by name */
403  qsort(agrp_array, agrp_count, sizeof(tSortActionGroup), compare_agroup);
404 
405  /* create sorted bone group list from sorted array */
406  BLI_listbase_clear(&pose->agroups);
407  for (i = 0; i < agrp_count; i++) {
408  BLI_addtail(&pose->agroups, agrp_array[i].agrp);
409  }
410 
411  /* Fix changed bone group indices in bones. */
412  for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
413  for (i = 0; i < agrp_count; i++) {
414  if (pchan->agrp_index == agrp_array[i].index) {
415  pchan->agrp_index = i + 1;
416  break;
417  }
418  }
419  }
420 
421  /* free temp resources */
422  MEM_freeN(agrp_array);
423 
424  /* notifiers for updates */
427 
428  return OPERATOR_FINISHED;
429 }
430 
432 {
433  /* identifiers */
434  ot->name = "Sort Bone Groups";
435  ot->idname = "POSE_OT_group_sort";
436  ot->description = "Sort Bone Groups by their names in ascending order";
437 
438  /* api callbacks */
441 
442  /* flags */
444 }
445 
446 static void pose_group_select(Object *ob, bool select)
447 {
448  bPose *pose = ob->pose;
449 
451  if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0) {
452  if (select) {
453  if (pchan->agrp_index == pose->active_group) {
454  pchan->bone->flag |= BONE_SELECTED;
455  }
456  }
457  else {
458  if (pchan->agrp_index == pose->active_group) {
459  pchan->bone->flag &= ~BONE_SELECTED;
460  }
461  }
462  }
463  }
465 }
466 
468 {
470 
471  /* only continue if there's an object, and a pose there too */
472  if (ELEM(NULL, ob, ob->pose)) {
473  return OPERATOR_CANCELLED;
474  }
475 
476  pose_group_select(ob, 1);
477 
478  /* notifiers for updates */
479  bArmature *arm = ob->data;
483 
484  return OPERATOR_FINISHED;
485 }
486 
488 {
489  /* identifiers */
490  ot->name = "Select Bones of Bone Group";
491  ot->idname = "POSE_OT_group_select";
492  ot->description = "Select bones in active Bone Group";
493 
494  /* api callbacks */
497 
498  /* flags */
500 }
501 
503 {
505 
506  /* only continue if there's an object, and a pose there too */
507  if (ELEM(NULL, ob, ob->pose)) {
508  return OPERATOR_CANCELLED;
509  }
510 
511  pose_group_select(ob, 0);
512 
513  /* notifiers for updates */
514  bArmature *arm = ob->data;
518 
519  return OPERATOR_FINISHED;
520 }
521 
523 {
524  /* identifiers */
525  ot->name = "Deselect Bone Group";
526  ot->idname = "POSE_OT_group_deselect";
527  ot->description = "Deselect bones of active Bone Group";
528 
529  /* api callbacks */
532 
533  /* flags */
535 }
536 
537 /* ********************************************** */
Blender kernel action and pose functionality.
struct bActionGroup * BKE_pose_add_group(struct bPose *pose, const char *name)
Definition: action.c:1266
void BKE_pose_remove_group_index(struct bPose *pose, int index)
Definition: action.c:1322
#define FOREACH_PCHAN_SELECTED_IN_OBJECT_END
Definition: BKE_armature.h:564
#define FOREACH_PCHAN_VISIBLE_IN_OBJECT_BEGIN(_ob, _pchan)
Definition: BKE_armature.h:569
#define FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN(_ob, _pchan)
Definition: BKE_armature.h:560
#define FOREACH_PCHAN_VISIBLE_IN_OBJECT_END
Definition: BKE_armature.h:572
void CTX_wm_operator_poll_msg_set(struct bContext *C, const char *msg)
Definition: context.c:1042
#define BLI_assert(a)
Definition: BLI_assert.h:46
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
Definition: BLI_listbase.h:273
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:80
void void void bool BLI_listbase_link_move(ListBase *listbase, void *vlink, int step) ATTR_NONNULL()
Definition: listbase.c:405
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
#define UNUSED(x)
#define ELEM(...)
void DEG_id_tag_update(struct ID *id, int flag)
@ ID_RECALC_COPY_ON_WRITE
Definition: DNA_ID.h:834
@ ID_RECALC_SELECT
Definition: DNA_ID.h:818
#define ID_IS_OVERRIDE_LIBRARY(_id)
Definition: DNA_ID.h:588
@ BONE_SELECTED
@ BONE_UNSELECTABLE
Object is a sort of wrapper for general info.
@ OPERATOR_CANCELLED
@ OPERATOR_INTERFACE
@ OPERATOR_FINISHED
void ED_outliner_select_sync_from_pose_bone_tag(struct bContext *C)
bool ED_operator_posemode_context(struct bContext *C)
Definition: screen_ops.c:517
Read Guarded memory(de)allocation.
#define C
Definition: RandGen.cpp:25
void uiItemIntO(uiLayout *layout, const char *name, int icon, const char *opname, const char *propname, int value)
struct uiLayout * UI_popup_menu_layout(uiPopupMenu *pup)
void uiItemS(uiLayout *layout)
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_POSE
Definition: WM_types.h:407
#define NC_OBJECT
Definition: WM_types.h:329
__forceinline const avxb select(const avxb &m, const avxb &t, const avxb &f)
Definition: avxb.h:154
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:33
Object * ED_pose_object_from_context(bContext *C)
Definition: pose_edit.c:61
void POSE_OT_group_unassign(wmOperatorType *ot)
Definition: pose_group.c:275
void POSE_OT_group_select(wmOperatorType *ot)
Definition: pose_group.c:487
static int pose_group_deselect_exec(bContext *C, wmOperator *UNUSED(op))
Definition: pose_group.c:502
static int pose_groups_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
Definition: pose_group.c:128
static int pose_group_assign_exec(bContext *C, wmOperator *op)
Definition: pose_group.c:187
struct tSortActionGroup tSortActionGroup
void POSE_OT_group_deselect(wmOperatorType *ot)
Definition: pose_group.c:522
static int pose_group_unassign_exec(bContext *C, wmOperator *UNUSED(op))
Definition: pose_group.c:245
void POSE_OT_group_assign(wmOperatorType *ot)
Definition: pose_group.c:226
static bool pose_group_poll(bContext *C)
Definition: pose_group.c:42
static int group_sort_exec(bContext *C, wmOperator *UNUSED(op))
Definition: pose_group.c:377
static int pose_group_remove_exec(bContext *C, wmOperator *UNUSED(op))
Definition: pose_group.c:91
static int compare_agroup(const void *sgrp_a_ptr, const void *sgrp_b_ptr)
Definition: pose_group.c:369
static void pose_group_select(Object *ob, bool select)
Definition: pose_group.c:446
void POSE_OT_group_remove(wmOperatorType *ot)
Definition: pose_group.c:110
void POSE_OT_group_move(wmOperatorType *ot)
Definition: pose_group.c:334
static int pose_group_select_exec(bContext *C, wmOperator *UNUSED(op))
Definition: pose_group.c:467
void POSE_OT_group_add(wmOperatorType *ot)
Definition: pose_group.c:76
static int pose_group_add_exec(bContext *C, wmOperator *UNUSED(op))
Definition: pose_group.c:58
static int group_move_exec(bContext *C, wmOperator *op)
Definition: pose_group.c:290
void POSE_OT_group_sort(wmOperatorType *ot)
Definition: pose_group.c:431
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
Definition: rna_access.c:5271
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
Definition: rna_access.c:4921
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
Definition: rna_access.c:717
int RNA_property_int_get(PointerRNA *ptr, PropertyRNA *prop)
Definition: rna_access.c:2429
int RNA_int_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4910
int RNA_enum_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:5004
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, int default_value, int hardmin, int hardmax, const char *ui_name, const char *ui_description, int softmin, int softmax)
Definition: rna_define.c:3597
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
void * first
Definition: DNA_listBase.h:31
struct bPose * pose
void * data
struct bActionGroup * next
ListBase chanbase
ListBase agroups
int active_group
bActionGroup * agrp
Definition: pose_group.c:364
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
struct wmOperatorType * type
struct PointerRNA * ptr
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition: wm_files.c:3479