Blender  V2.93
pose_group.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2008 Blender Foundation
17  * All rights reserved.
18  * Implementation of Bone Groups operators and editing API's
19  */
20 
25 #include <string.h>
26 
27 #include "MEM_guardedalloc.h"
28 
29 #include "BLI_blenlib.h"
30 
31 #include "DNA_armature_types.h"
32 #include "DNA_object_types.h"
33 
34 #include "BKE_action.h"
35 #include "BKE_armature.h"
36 #include "BKE_context.h"
37 
38 #include "DEG_depsgraph.h"
39 
40 #include "RNA_access.h"
41 #include "RNA_define.h"
42 
43 #include "WM_api.h"
44 #include "WM_types.h"
45 
46 #include "ED_armature.h"
47 #include "ED_outliner.h"
48 #include "ED_screen.h"
49 
50 #include "UI_interface.h"
51 #include "UI_resources.h"
52 
53 #include "armature_intern.h"
54 
55 /* ********************************************** */
56 /* Bone Groups */
57 
58 static bool pose_group_poll(bContext *C)
59 {
61  return false;
62  }
63 
65  if ((obpose->proxy != NULL) || (obpose->proxy_group != NULL) || ID_IS_OVERRIDE_LIBRARY(obpose)) {
66  CTX_wm_operator_poll_msg_set(C, "Cannot edit bone groups for proxies or library overrides");
67  return false;
68  }
69 
70  return true;
71 }
72 
74 {
76 
77  /* only continue if there's an object and pose */
78  if (ELEM(NULL, ob, ob->pose)) {
79  return OPERATOR_CANCELLED;
80  }
81 
82  /* for now, just call the API function for this */
84 
85  /* notifiers for updates */
87 
88  return OPERATOR_FINISHED;
89 }
90 
92 {
93  /* identifiers */
94  ot->name = "Add Bone Group";
95  ot->idname = "POSE_OT_group_add";
96  ot->description = "Add a new bone group";
97 
98  /* api callbacks */
101 
102  /* flags */
104 }
105 
107 {
109 
110  /* only continue if there's an object and pose */
111  if (ELEM(NULL, ob, ob->pose)) {
112  return OPERATOR_CANCELLED;
113  }
114 
115  /* for now, just call the API function for this */
117 
118  /* notifiers for updates */
121 
122  return OPERATOR_FINISHED;
123 }
124 
126 {
127  /* identifiers */
128  ot->name = "Remove Bone Group";
129  ot->idname = "POSE_OT_group_remove";
130  ot->description = "Remove the active bone group";
131 
132  /* api callbacks */
135 
136  /* flags */
138 }
139 
140 /* ------------ */
141 
142 /* invoke callback which presents a list of bone-groups for the user to choose from */
143 static int pose_groups_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
144 {
146  bPose *pose;
147  PropertyRNA *prop = RNA_struct_find_property(op->ptr, "type");
148 
149  uiPopupMenu *pup;
150  uiLayout *layout;
151  bActionGroup *grp;
152  int i;
153 
154  /* only continue if there's an object, and a pose there too */
155  if (ELEM(NULL, ob, ob->pose)) {
156  return OPERATOR_CANCELLED;
157  }
158  pose = ob->pose;
159 
160  /* If group index is set, try to use it! */
161  if (RNA_property_is_set(op->ptr, prop)) {
162  const int num_groups = BLI_listbase_count(&pose->agroups);
163  const int group = RNA_property_int_get(op->ptr, prop);
164 
165  /* just use the active group index, and call the exec callback for the calling operator */
166  if (group > 0 && group <= num_groups) {
167  return op->type->exec(C, op);
168  }
169  }
170 
171  /* if there's no active group (or active is invalid), create a new menu to find it */
172  if (pose->active_group <= 0) {
173  /* create a new menu, and start populating it with group names */
174  pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
175  layout = UI_popup_menu_layout(pup);
176 
177  /* special entry - allow creating a new group, then using that
178  * (not to be used for removing though)
179  */
180  if (strstr(op->idname, "assign")) {
181  uiItemIntO(layout, "New Group", ICON_NONE, op->idname, "type", 0);
182  uiItemS(layout);
183  }
184 
185  /* add entries for each group */
186  for (grp = pose->agroups.first, i = 1; grp; grp = grp->next, i++) {
187  uiItemIntO(layout, grp->name, ICON_NONE, op->idname, "type", i);
188  }
189 
190  /* finish building the menu, and process it (should result in calling self again) */
191  UI_popup_menu_end(C, pup);
192 
193  return OPERATOR_INTERFACE;
194  }
195 
196  /* just use the active group index, and call the exec callback for the calling operator */
197  RNA_int_set(op->ptr, "type", pose->active_group);
198  return op->type->exec(C, op);
199 }
200 
201 /* Assign selected pchans to the bone group that the user selects */
203 {
205  bPose *pose;
206  bool done = false;
207 
208  /* only continue if there's an object, and a pose there too */
209  if (ELEM(NULL, ob, ob->pose)) {
210  return OPERATOR_CANCELLED;
211  }
212 
213  pose = ob->pose;
214 
215  /* set the active group number to the one from operator props
216  * - if 0 after this, make a new group...
217  */
218  pose->active_group = RNA_int_get(op->ptr, "type");
219  if (pose->active_group == 0) {
221  }
222 
223  /* add selected bones to group then */
225  pchan->agrp_index = pose->active_group;
226  done = true;
227  }
229 
230  /* notifiers for updates */
233 
234  /* report done status */
235  if (done) {
236  return OPERATOR_FINISHED;
237  }
238  return OPERATOR_CANCELLED;
239 }
240 
242 {
243  /* identifiers */
244  ot->name = "Add Selected to Bone Group";
245  ot->idname = "POSE_OT_group_assign";
246  ot->description = "Add selected bones to the chosen bone group";
247 
248  /* api callbacks */
252 
253  /* flags */
255 
256  /* properties */
257  RNA_def_int(ot->srna, "type", 0, 0, INT_MAX, "Bone Group Index", "", 0, 10);
258 }
259 
261 {
263  bool done = false;
264 
265  /* only continue if there's an object, and a pose there too */
266  if (ELEM(NULL, ob, ob->pose)) {
267  return OPERATOR_CANCELLED;
268  }
269 
270  /* find selected bones to remove from all bone groups */
272  if (pchan->agrp_index) {
273  pchan->agrp_index = 0;
274  done = true;
275  }
276  }
278 
279  /* notifiers for updates */
282 
283  /* report done status */
284  if (done) {
285  return OPERATOR_FINISHED;
286  }
287  return OPERATOR_CANCELLED;
288 }
289 
291 {
292  /* identifiers */
293  ot->name = "Remove Selected from Bone Groups";
294  ot->idname = "POSE_OT_group_unassign";
295  ot->description = "Remove selected bones from all bone groups";
296 
297  /* api callbacks */
300 
301  /* flags */
303 }
304 
306 {
308  bPose *pose = (ob) ? ob->pose : NULL;
309  bPoseChannel *pchan;
310  bActionGroup *grp;
311  int dir = RNA_enum_get(op->ptr, "direction");
312 
313  if (ELEM(NULL, ob, pose)) {
314  return OPERATOR_CANCELLED;
315  }
316  if (pose->active_group <= 0) {
317  return OPERATOR_CANCELLED;
318  }
319 
320  /* get group to move */
321  grp = BLI_findlink(&pose->agroups, pose->active_group - 1);
322  if (grp == NULL) {
323  return OPERATOR_CANCELLED;
324  }
325 
326  /* move bone group */
327  if (BLI_listbase_link_move(&pose->agroups, grp, dir)) {
328  int grpIndexA = pose->active_group;
329  int grpIndexB = grpIndexA + dir;
330 
331  pose->active_group += dir;
332  /* fix changed bone group indices in bones (swap grpIndexA with grpIndexB) */
333  for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
334  if (pchan->agrp_index == grpIndexB) {
335  pchan->agrp_index = grpIndexA;
336  }
337  else if (pchan->agrp_index == grpIndexA) {
338  pchan->agrp_index = grpIndexB;
339  }
340  }
341 
342  /* notifiers for updates */
344  }
345 
346  return OPERATOR_FINISHED;
347 }
348 
350 {
351  static const EnumPropertyItem group_slot_move[] = {
352  {-1, "UP", 0, "Up", ""},
353  {1, "DOWN", 0, "Down", ""},
354  {0, NULL, 0, NULL, NULL},
355  };
356 
357  /* identifiers */
358  ot->name = "Move Bone Group";
359  ot->idname = "POSE_OT_group_move";
360  ot->description = "Change position of active Bone Group in list of Bone Groups";
361 
362  /* api callbacks */
365 
366  /* flags */
368 
370  "direction",
371  group_slot_move,
372  0,
373  "Direction",
374  "Direction to move the active Bone Group towards");
375 }
376 
377 /* bone group sort element */
378 typedef struct tSortActionGroup {
380  int index;
382 
383 /* compare bone groups by name */
384 static int compare_agroup(const void *sgrp_a_ptr, const void *sgrp_b_ptr)
385 {
386  const tSortActionGroup *sgrp_a = sgrp_a_ptr;
387  const tSortActionGroup *sgrp_b = sgrp_b_ptr;
388 
389  return strcmp(sgrp_a->agrp->name, sgrp_b->agrp->name);
390 }
391 
393 {
395  bPose *pose = (ob) ? ob->pose : NULL;
396  bPoseChannel *pchan;
397  tSortActionGroup *agrp_array;
398  bActionGroup *agrp;
399 
400  if (ELEM(NULL, ob, pose)) {
401  return OPERATOR_CANCELLED;
402  }
403  if (pose->active_group <= 0) {
404  return OPERATOR_CANCELLED;
405  }
406 
407  /* create temporary array with bone groups and indices */
408  int agrp_count = BLI_listbase_count(&pose->agroups);
409  agrp_array = MEM_mallocN(sizeof(tSortActionGroup) * agrp_count, "sort bone groups");
410  int i;
411  for (agrp = pose->agroups.first, i = 0; agrp; agrp = agrp->next, i++) {
412  BLI_assert(i < agrp_count);
413  agrp_array[i].agrp = agrp;
414  agrp_array[i].index = i + 1;
415  }
416 
417  /* sort bone groups by name */
418  qsort(agrp_array, agrp_count, sizeof(tSortActionGroup), compare_agroup);
419 
420  /* create sorted bone group list from sorted array */
421  BLI_listbase_clear(&pose->agroups);
422  for (i = 0; i < agrp_count; i++) {
423  BLI_addtail(&pose->agroups, agrp_array[i].agrp);
424  }
425 
426  /* Fix changed bone group indices in bones. */
427  for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
428  for (i = 0; i < agrp_count; i++) {
429  if (pchan->agrp_index == agrp_array[i].index) {
430  pchan->agrp_index = i + 1;
431  break;
432  }
433  }
434  }
435 
436  /* free temp resources */
437  MEM_freeN(agrp_array);
438 
439  /* notifiers for updates */
442 
443  return OPERATOR_FINISHED;
444 }
445 
447 {
448  /* identifiers */
449  ot->name = "Sort Bone Groups";
450  ot->idname = "POSE_OT_group_sort";
451  ot->description = "Sort Bone Groups by their names in ascending order";
452 
453  /* api callbacks */
456 
457  /* flags */
459 }
460 
461 static void pose_group_select(Object *ob, bool select)
462 {
463  bPose *pose = ob->pose;
464 
466  if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0) {
467  if (select) {
468  if (pchan->agrp_index == pose->active_group) {
469  pchan->bone->flag |= BONE_SELECTED;
470  }
471  }
472  else {
473  if (pchan->agrp_index == pose->active_group) {
474  pchan->bone->flag &= ~BONE_SELECTED;
475  }
476  }
477  }
478  }
480 }
481 
483 {
485 
486  /* only continue if there's an object, and a pose there too */
487  if (ELEM(NULL, ob, ob->pose)) {
488  return OPERATOR_CANCELLED;
489  }
490 
491  pose_group_select(ob, 1);
492 
493  /* notifiers for updates */
494  bArmature *arm = ob->data;
498 
499  return OPERATOR_FINISHED;
500 }
501 
503 {
504  /* identifiers */
505  ot->name = "Select Bones of Bone Group";
506  ot->idname = "POSE_OT_group_select";
507  ot->description = "Select bones in active Bone Group";
508 
509  /* api callbacks */
512 
513  /* flags */
515 }
516 
518 {
520 
521  /* only continue if there's an object, and a pose there too */
522  if (ELEM(NULL, ob, ob->pose)) {
523  return OPERATOR_CANCELLED;
524  }
525 
526  pose_group_select(ob, 0);
527 
528  /* notifiers for updates */
529  bArmature *arm = ob->data;
533 
534  return OPERATOR_FINISHED;
535 }
536 
538 {
539  /* identifiers */
540  ot->name = "Deselect Bone Group";
541  ot->idname = "POSE_OT_group_deselect";
542  ot->description = "Deselect bones of active Bone Group";
543 
544  /* api callbacks */
547 
548  /* flags */
550 }
551 
552 /* ********************************************** */
Blender kernel action and pose functionality.
struct bActionGroup * BKE_pose_add_group(struct bPose *pose, const char *name)
Definition: action.c:1322
void BKE_pose_remove_group_index(struct bPose *pose, const int index)
Definition: action.c:1381
#define FOREACH_PCHAN_SELECTED_IN_OBJECT_END
Definition: BKE_armature.h:353
#define FOREACH_PCHAN_VISIBLE_IN_OBJECT_BEGIN(_ob, _pchan)
Definition: BKE_armature.h:358
#define FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN(_ob, _pchan)
Definition: BKE_armature.h:349
#define FOREACH_PCHAN_VISIBLE_IN_OBJECT_END
Definition: BKE_armature.h:361
void CTX_wm_operator_poll_msg_set(struct bContext *C, const char *msg)
Definition: context.c:1006
#define BLI_assert(a)
Definition: BLI_assert.h:58
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
Definition: BLI_listbase.h:128
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:110
void void void bool BLI_listbase_link_move(ListBase *listbase, void *vlink, int step) ATTR_NONNULL()
Definition: listbase.c:475
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:654
@ ID_RECALC_SELECT
Definition: DNA_ID.h:638
#define ID_IS_OVERRIDE_LIBRARY(_id)
Definition: DNA_ID.h:445
@ 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)
Definition: outliner_sync.c:68
bool ED_operator_posemode_context(struct bContext *C)
Definition: screen_ops.c:471
Read Guarded memory(de)allocation.
#define C
Definition: RandGen.cpp:39
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:155
@ OPTYPE_REGISTER
Definition: WM_types.h:153
#define ND_POSE
Definition: WM_types.h:359
#define NC_OBJECT
Definition: WM_types.h:280
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:47
Object * ED_pose_object_from_context(bContext *C)
Definition: pose_edit.c:76
void POSE_OT_group_unassign(wmOperatorType *ot)
Definition: pose_group.c:290
void POSE_OT_group_select(wmOperatorType *ot)
Definition: pose_group.c:502
static int pose_group_deselect_exec(bContext *C, wmOperator *UNUSED(op))
Definition: pose_group.c:517
static int pose_groups_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
Definition: pose_group.c:143
static int pose_group_assign_exec(bContext *C, wmOperator *op)
Definition: pose_group.c:202
struct tSortActionGroup tSortActionGroup
void POSE_OT_group_deselect(wmOperatorType *ot)
Definition: pose_group.c:537
static int pose_group_unassign_exec(bContext *C, wmOperator *UNUSED(op))
Definition: pose_group.c:260
void POSE_OT_group_assign(wmOperatorType *ot)
Definition: pose_group.c:241
static bool pose_group_poll(bContext *C)
Definition: pose_group.c:58
static int group_sort_exec(bContext *C, wmOperator *UNUSED(op))
Definition: pose_group.c:392
static int pose_group_remove_exec(bContext *C, wmOperator *UNUSED(op))
Definition: pose_group.c:106
static int compare_agroup(const void *sgrp_a_ptr, const void *sgrp_b_ptr)
Definition: pose_group.c:384
static void pose_group_select(Object *ob, bool select)
Definition: pose_group.c:461
void POSE_OT_group_remove(wmOperatorType *ot)
Definition: pose_group.c:125
void POSE_OT_group_move(wmOperatorType *ot)
Definition: pose_group.c:349
static int pose_group_select_exec(bContext *C, wmOperator *UNUSED(op))
Definition: pose_group.c:482
void POSE_OT_group_add(wmOperatorType *ot)
Definition: pose_group.c:91
static int pose_group_add_exec(bContext *C, wmOperator *UNUSED(op))
Definition: pose_group.c:73
static int group_move_exec(bContext *C, wmOperator *op)
Definition: pose_group.c:305
void POSE_OT_group_sort(wmOperatorType *ot)
Definition: pose_group.c:446
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
Definition: rna_access.c:6655
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
Definition: rna_access.c:6319
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
Definition: rna_access.c:866
int RNA_property_int_get(PointerRNA *ptr, PropertyRNA *prop)
Definition: rna_access.c:2607
int RNA_int_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:6308
int RNA_enum_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:6402
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:3585
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:3771
void * first
Definition: DNA_listBase.h:47
struct Object * proxy_group
struct bPose * pose
struct Object * proxy
void * data
struct bActionGroup * next
ListBase chanbase
ListBase agroups
int active_group
bActionGroup * agrp
Definition: pose_group.c:379
int(* invoke)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:752
const char * name
Definition: WM_types.h:721
const char * idname
Definition: WM_types.h:723
bool(* poll)(struct bContext *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:776
struct StructRNA * srna
Definition: WM_types.h:802
const char * description
Definition: WM_types.h:726
int(* exec)(struct bContext *, struct wmOperator *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:736
struct wmOperatorType * type
struct PointerRNA * ptr
__forceinline const avxb select(const avxb &m, const avxb &t, const avxb &f)
Definition: util_avxb.h:167
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition: wm_files.c:3156