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) 2007, Blender Foundation
17 * This is a new part of Blender
18 */
19
20 /** \file
21 * \ingroup edarmature
22 */
23
24 #include <math.h>
25 #include <string.h>
26
27 #include "MEM_guardedalloc.h"
28
29 #include "BLI_blenlib.h"
30 #include "BLI_dlrbTree.h"
31 #include "BLI_string_utils.h"
32
33 #include "BLT_translation.h"
34
35 #include "DNA_anim_types.h"
36 #include "DNA_armature_types.h"
37 #include "DNA_object_types.h"
38 #include "DNA_scene_types.h"
39
40 #include "BKE_action.h"
41 #include "BKE_animsys.h"
42 #include "BKE_armature.h"
43 #include "BKE_idprop.h"
44 #include "BKE_lib_id.h"
45 #include "BKE_main.h"
46 #include "BKE_object.h"
47
48 #include "BKE_context.h"
49 #include "BKE_report.h"
50
51 #include "DEG_depsgraph.h"
52
53 #include "RNA_access.h"
54 #include "RNA_define.h"
55 #include "RNA_enum_types.h"
56
57 #include "WM_api.h"
58 #include "WM_types.h"
59
60 #include "UI_interface.h"
61 #include "UI_resources.h"
62
63 #include "ED_anim_api.h"
64 #include "ED_armature.h"
65 #include "ED_keyframes_draw.h"
66 #include "ED_keyframes_edit.h"
67 #include "ED_keyframing.h"
68 #include "ED_object.h"
69 #include "ED_screen.h"
70
71 #include "armature_intern.h"
72
73 /* ******* XXX ********** */
74
action_set_activemarker(void * UNUSED (a),void * UNUSED (b),void * UNUSED (c))75 static void action_set_activemarker(void *UNUSED(a), void *UNUSED(b), void *UNUSED(c))
76 {
77 }
78
79 /* ************************************************************* */
80 /**
81 * Pose-Library Tool for Blender
82 * =============================
83 *
84 * Overview:
85 * This tool allows animators to store a set of frequently used poses to dump into
86 * the active action to help in "budget" productions to quickly block out new actions.
87 * It acts as a kind of "glorified clipboard for poses", allowing for naming of poses.
88 *
89 * Features:
90 * - Pose-libs are simply normal Actions.
91 * - Each "pose" is simply a set of key-frames that occur on a particular frame.
92 * - A set of #TimeMarker that belong to each Action, help 'label' where a 'pose' can be
93 * found in the Action.
94 * - The Scroll-wheel or PageUp/Down buttons when used in a special mode or after pressing/holding
95 * [a modifier] key, cycles through the poses available for the active pose's pose-lib,
96 * allowing the animator to preview what action best suits that pose.
97 */
98 /* ************************************************************* */
99
100 /* gets the first available frame in poselib to store a pose on
101 * - frames start from 1, and a pose should occur on every frame... 0 is error!
102 */
poselib_get_free_index(bAction * act)103 static int poselib_get_free_index(bAction *act)
104 {
105 TimeMarker *marker;
106 int low = 0, high = 0;
107 bool changed = false;
108
109 /* sanity checks */
110 if (ELEM(NULL, act, act->markers.first)) {
111 return 1;
112 }
113
114 /* As poses are not stored in chronological order, we must iterate over this list
115 * a few times until we don't make any new discoveries (mostly about the lower bound).
116 * Prevents problems with deleting then trying to add new poses T27412.
117 */
118 do {
119 changed = false;
120
121 for (marker = act->markers.first; marker; marker = marker->next) {
122 /* only increase low if value is 1 greater than low, to find "gaps" where
123 * poses were removed from the poselib
124 */
125 if (marker->frame == (low + 1)) {
126 low++;
127 changed = true;
128 }
129
130 /* value replaces high if it is the highest value encountered yet */
131 if (marker->frame > high) {
132 high = marker->frame;
133 changed = true;
134 }
135 }
136 } while (changed != 0);
137
138 /* - if low is not equal to high, then low+1 is a gap
139 * - if low is equal to high, then high+1 is the next index (add at end)
140 */
141 if (low < high) {
142 return (low + 1);
143 }
144 return (high + 1);
145 }
146
147 /* returns the active pose for a poselib */
poselib_get_active_pose(bAction * act)148 static TimeMarker *poselib_get_active_pose(bAction *act)
149 {
150 if ((act) && (act->active_marker)) {
151 return BLI_findlink(&act->markers, act->active_marker - 1);
152 }
153 return NULL;
154 }
155
156 /* Get object that Pose Lib should be found on */
157 /* XXX C can be zero */
get_poselib_object(bContext * C)158 static Object *get_poselib_object(bContext *C)
159 {
160 ScrArea *area;
161
162 /* sanity check */
163 if (C == NULL) {
164 return NULL;
165 }
166
167 area = CTX_wm_area(C);
168
169 if (area && (area->spacetype == SPACE_PROPERTIES)) {
170 return ED_object_context(C);
171 }
172 return BKE_object_pose_armature_get(CTX_data_active_object(C));
173 }
174
175 /* Poll callback for operators that require existing PoseLib data (with poses) to work */
has_poselib_pose_data_poll(bContext * C)176 static bool has_poselib_pose_data_poll(bContext *C)
177 {
178 Object *ob = get_poselib_object(C);
179 return (ob && ob->poselib);
180 }
181
182 /* Poll callback for operators that require existing PoseLib data (with poses)
183 * as they need to do some editing work on those poses (i.e. not on lib-linked actions)
184 */
has_poselib_pose_data_for_editing_poll(bContext * C)185 static bool has_poselib_pose_data_for_editing_poll(bContext *C)
186 {
187 Object *ob = get_poselib_object(C);
188 return (ob && ob->poselib && !ID_IS_LINKED(ob->poselib));
189 }
190
191 /* ----------------------------------- */
192
193 /* Initialize a new poselib (whether it is needed or not) */
poselib_init_new(Main * bmain,Object * ob)194 static bAction *poselib_init_new(Main *bmain, Object *ob)
195 {
196 /* sanity checks - only for armatures */
197 if (ELEM(NULL, ob, ob->pose)) {
198 return NULL;
199 }
200
201 /* init object's poselib action (unlink old one if there) */
202 if (ob->poselib) {
203 id_us_min(&ob->poselib->id);
204 }
205
206 ob->poselib = BKE_action_add(bmain, "PoseLib");
207 ob->poselib->idroot = ID_OB;
208
209 return ob->poselib;
210 }
211
212 /* Initialize a new poselib (checks if that needs to happen) */
poselib_validate(Main * bmain,Object * ob)213 static bAction *poselib_validate(Main *bmain, Object *ob)
214 {
215 if (ELEM(NULL, ob, ob->pose)) {
216 return NULL;
217 }
218 if (ob->poselib == NULL) {
219 return poselib_init_new(bmain, ob);
220 }
221 return ob->poselib;
222 }
223
224 /* ************************************************************* */
225 /* Pose Lib UI Operators */
226
poselib_new_exec(bContext * C,wmOperator * UNUSED (op))227 static int poselib_new_exec(bContext *C, wmOperator *UNUSED(op))
228 {
229 Main *bmain = CTX_data_main(C);
230 Object *ob = get_poselib_object(C);
231
232 /* sanity checks */
233 if (ob == NULL) {
234 return OPERATOR_CANCELLED;
235 }
236
237 /* new method here deals with the rest... */
238 poselib_init_new(bmain, ob);
239
240 /* notifier here might evolve? */
241 WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
242
243 return OPERATOR_FINISHED;
244 }
245
POSELIB_OT_new(wmOperatorType * ot)246 void POSELIB_OT_new(wmOperatorType *ot)
247 {
248 /* identifiers */
249 ot->name = "New Pose Library";
250 ot->idname = "POSELIB_OT_new";
251 ot->description = "Add New Pose Library to active Object";
252
253 /* callbacks */
254 ot->exec = poselib_new_exec;
255 ot->poll = ED_operator_posemode;
256
257 /* flags */
258 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
259 }
260
261 /* ------------------------------------------------ */
262
poselib_unlink_exec(bContext * C,wmOperator * UNUSED (op))263 static int poselib_unlink_exec(bContext *C, wmOperator *UNUSED(op))
264 {
265 Object *ob = get_poselib_object(C);
266
267 /* sanity checks */
268 if (ELEM(NULL, ob, ob->poselib)) {
269 return OPERATOR_CANCELLED;
270 }
271
272 /* there should be a poselib (we just checked above!), so just lower its user count and remove */
273 id_us_min(&ob->poselib->id);
274 ob->poselib = NULL;
275
276 /* notifier here might evolve? */
277 WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
278
279 return OPERATOR_FINISHED;
280 }
281
POSELIB_OT_unlink(wmOperatorType * ot)282 void POSELIB_OT_unlink(wmOperatorType *ot)
283 {
284 /* identifiers */
285 ot->name = "Unlink Pose Library";
286 ot->idname = "POSELIB_OT_unlink";
287 ot->description = "Remove Pose Library from active Object";
288
289 /* callbacks */
290 ot->exec = poselib_unlink_exec;
291 ot->poll = has_poselib_pose_data_poll;
292
293 /* flags */
294 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
295 }
296
297 /* ************************************************************* */
298 /* Pose Editing Operators */
299
300 /* This tool automagically generates/validates poselib data so that it corresponds to the data
301 * in the action. This is for use in making existing actions usable as poselibs.
302 */
poselib_sanitize_exec(bContext * C,wmOperator * op)303 static int poselib_sanitize_exec(bContext *C, wmOperator *op)
304 {
305 Object *ob = get_poselib_object(C);
306 bAction *act = (ob) ? ob->poselib : NULL;
307 DLRBT_Tree keys;
308 ActKeyColumn *ak;
309 TimeMarker *marker, *markern;
310
311 /* validate action */
312 if (act == NULL) {
313 BKE_report(op->reports, RPT_WARNING, "No action to validate");
314 return OPERATOR_CANCELLED;
315 }
316
317 /* determine which frames have keys */
318 BLI_dlrbTree_init(&keys);
319 action_to_keylist(NULL, act, &keys, 0);
320
321 /* for each key, make sure there is a corresponding pose */
322 for (ak = keys.first; ak; ak = ak->next) {
323 /* check if any pose matches this */
324 /* TODO: don't go looking through the list like this every time... */
325 for (marker = act->markers.first; marker; marker = marker->next) {
326 if (IS_EQ((double)marker->frame, (double)ak->cfra)) {
327 marker->flag = -1;
328 break;
329 }
330 }
331
332 /* add new if none found */
333 if (marker == NULL) {
334 /* add pose to poselib */
335 marker = MEM_callocN(sizeof(TimeMarker), "ActionMarker");
336
337 BLI_snprintf(marker->name, sizeof(marker->name), "F%d Pose", (int)ak->cfra);
338
339 marker->frame = (int)ak->cfra;
340 marker->flag = -1;
341
342 BLI_addtail(&act->markers, marker);
343 }
344 }
345
346 /* remove all untagged poses (unused), and remove all tags */
347 for (marker = act->markers.first; marker; marker = markern) {
348 markern = marker->next;
349
350 if (marker->flag != -1) {
351 BLI_freelinkN(&act->markers, marker);
352 }
353 else {
354 marker->flag = 0;
355 }
356 }
357
358 /* free temp memory */
359 BLI_dlrbTree_free(&keys);
360
361 /* send notifiers for this - using keyframe editing notifiers, since action
362 * may be being shown in anim editors as active action
363 */
364 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
365
366 return OPERATOR_FINISHED;
367 }
368
POSELIB_OT_action_sanitize(wmOperatorType * ot)369 void POSELIB_OT_action_sanitize(wmOperatorType *ot)
370 {
371 /* identifiers */
372 ot->name = "Sanitize Pose Library Action";
373 ot->idname = "POSELIB_OT_action_sanitize";
374 ot->description = "Make action suitable for use as a Pose Library";
375
376 /* callbacks */
377 ot->exec = poselib_sanitize_exec;
378 ot->poll = has_poselib_pose_data_for_editing_poll;
379
380 /* flags */
381 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
382 }
383
384 /* ------------------------------------------ */
385
386 /* Poll callback for adding poses to a PoseLib */
poselib_add_poll(bContext * C)387 static bool poselib_add_poll(bContext *C)
388 {
389 /* There are 2 cases we need to be careful with:
390 * 1) When this operator is invoked from a hotkey, there may be no PoseLib yet
391 * 2) If a PoseLib already exists, we can't edit the action if it is a lib-linked
392 * actions, as data will be lost when saving the file
393 */
394 if (ED_operator_posemode(C)) {
395 Object *ob = get_poselib_object(C);
396 if (ob) {
397 if ((ob->poselib == NULL) || !ID_IS_LINKED(ob->poselib)) {
398 return true;
399 }
400 }
401 }
402 return false;
403 }
404
poselib_add_menu_invoke__replacemenu(bContext * C,uiLayout * layout,void * UNUSED (arg))405 static void poselib_add_menu_invoke__replacemenu(bContext *C, uiLayout *layout, void *UNUSED(arg))
406 {
407 Object *ob = get_poselib_object(C);
408 bAction *act = ob->poselib; /* never NULL */
409 TimeMarker *marker;
410
411 wmOperatorType *ot = WM_operatortype_find("POSELIB_OT_pose_add", 1);
412
413 BLI_assert(ot != NULL);
414
415 /* set the operator execution context correctly */
416 uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT);
417
418 /* add each marker to this menu */
419 for (marker = act->markers.first; marker; marker = marker->next) {
420 PointerRNA props_ptr;
421 uiItemFullO_ptr(
422 layout, ot, marker->name, ICON_ARMATURE_DATA, NULL, WM_OP_EXEC_DEFAULT, 0, &props_ptr);
423 RNA_int_set(&props_ptr, "frame", marker->frame);
424 RNA_string_set(&props_ptr, "name", marker->name);
425 }
426 }
427
poselib_add_menu_invoke(bContext * C,wmOperator * op,const wmEvent * UNUSED (event))428 static int poselib_add_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
429 {
430 Scene *scene = CTX_data_scene(C);
431 Object *ob = get_poselib_object(C);
432 bPose *pose = (ob) ? ob->pose : NULL;
433 uiPopupMenu *pup;
434 uiLayout *layout;
435
436 /* sanity check */
437 if (ELEM(NULL, ob, pose)) {
438 return OPERATOR_CANCELLED;
439 }
440
441 /* start building */
442 pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
443 layout = UI_popup_menu_layout(pup);
444 uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT);
445
446 /* add new (adds to the first unoccupied frame) */
447 uiItemIntO(layout,
448 IFACE_("Add New"),
449 ICON_NONE,
450 "POSELIB_OT_pose_add",
451 "frame",
452 poselib_get_free_index(ob->poselib));
453
454 /* check if we have any choices to add a new pose in any other way */
455 if ((ob->poselib) && (ob->poselib->markers.first)) {
456 /* add new (on current frame) */
457 uiItemIntO(layout,
458 IFACE_("Add New (Current Frame)"),
459 ICON_NONE,
460 "POSELIB_OT_pose_add",
461 "frame",
462 CFRA);
463
464 /* replace existing - submenu */
465 uiItemMenuF(
466 layout, IFACE_("Replace Existing..."), 0, poselib_add_menu_invoke__replacemenu, NULL);
467 }
468
469 UI_popup_menu_end(C, pup);
470
471 /* this operator is only for a menu, not used further */
472 return OPERATOR_INTERFACE;
473 }
474
poselib_add_exec(bContext * C,wmOperator * op)475 static int poselib_add_exec(bContext *C, wmOperator *op)
476 {
477 Main *bmain = CTX_data_main(C);
478 Object *ob = get_poselib_object(C);
479 bAction *act = poselib_validate(bmain, ob);
480 bPose *pose = (ob) ? ob->pose : NULL;
481 TimeMarker *marker;
482 KeyingSet *ks;
483 int frame = RNA_int_get(op->ptr, "frame");
484 char name[64];
485
486 /* sanity check (invoke should have checked this anyway) */
487 if (ELEM(NULL, ob, pose)) {
488 return OPERATOR_CANCELLED;
489 }
490
491 /* get name to give to pose */
492 RNA_string_get(op->ptr, "name", name);
493
494 /* add pose to poselib - replaces any existing pose there
495 * - for the 'replace' option, this should end up finding the appropriate marker,
496 * so no new one will be added
497 */
498 for (marker = act->markers.first; marker; marker = marker->next) {
499 if (marker->frame == frame) {
500 BLI_strncpy(marker->name, name, sizeof(marker->name));
501 break;
502 }
503 }
504 if (marker == NULL) {
505 marker = MEM_callocN(sizeof(TimeMarker), "ActionMarker");
506
507 BLI_strncpy(marker->name, name, sizeof(marker->name));
508 marker->frame = frame;
509
510 BLI_addtail(&act->markers, marker);
511 }
512
513 /* validate name */
514 BLI_uniquename(
515 &act->markers, marker, DATA_("Pose"), '.', offsetof(TimeMarker, name), sizeof(marker->name));
516
517 /* use Keying Set to determine what to store for the pose */
518
519 /* this includes custom props :)*/
520 ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_WHOLE_CHARACTER_SELECTED_ID);
521
522 ANIM_apply_keyingset(C, NULL, act, ks, MODIFYKEY_MODE_INSERT, (float)frame);
523
524 /* store new 'active' pose number */
525 act->active_marker = BLI_listbase_count(&act->markers);
526 DEG_id_tag_update(&act->id, ID_RECALC_COPY_ON_WRITE);
527
528 /* done */
529 return OPERATOR_FINISHED;
530 }
531
POSELIB_OT_pose_add(wmOperatorType * ot)532 void POSELIB_OT_pose_add(wmOperatorType *ot)
533 {
534 /* identifiers */
535 ot->name = "PoseLib Add Pose";
536 ot->idname = "POSELIB_OT_pose_add";
537 ot->description = "Add the current Pose to the active Pose Library";
538
539 /* api callbacks */
540 ot->invoke = poselib_add_menu_invoke;
541 ot->exec = poselib_add_exec;
542 ot->poll = poselib_add_poll;
543
544 /* flags */
545 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
546
547 /* properties */
548 RNA_def_int(ot->srna, "frame", 1, 0, INT_MAX, "Frame", "Frame to store pose on", 0, INT_MAX);
549 RNA_def_string(ot->srna, "name", "Pose", 64, "Pose Name", "Name of newly added Pose");
550 }
551
552 /* ----- */
553
554 /* can be called with C == NULL */
poselib_stored_pose_itemf(bContext * C,PointerRNA * UNUSED (ptr),PropertyRNA * UNUSED (prop),bool * r_free)555 static const EnumPropertyItem *poselib_stored_pose_itemf(bContext *C,
556 PointerRNA *UNUSED(ptr),
557 PropertyRNA *UNUSED(prop),
558 bool *r_free)
559 {
560 Object *ob = get_poselib_object(C);
561 bAction *act = (ob) ? ob->poselib : NULL;
562 TimeMarker *marker;
563 EnumPropertyItem *item = NULL, item_tmp = {0};
564 int totitem = 0;
565 int i = 0;
566
567 if (C == NULL) {
568 return DummyRNA_NULL_items;
569 }
570
571 /* check that the action exists */
572 if (act) {
573 /* add each marker to the list */
574 for (marker = act->markers.first, i = 0; marker; marker = marker->next, i++) {
575 item_tmp.identifier = item_tmp.name = marker->name;
576 item_tmp.icon = ICON_ARMATURE_DATA;
577 item_tmp.value = i;
578 RNA_enum_item_add(&item, &totitem, &item_tmp);
579 }
580 }
581
582 RNA_enum_item_end(&item, &totitem);
583 *r_free = true;
584
585 return item;
586 }
587
poselib_remove_exec(bContext * C,wmOperator * op)588 static int poselib_remove_exec(bContext *C, wmOperator *op)
589 {
590 Object *ob = get_poselib_object(C);
591 bAction *act = (ob) ? ob->poselib : NULL;
592 TimeMarker *marker;
593 int marker_index;
594 FCurve *fcu;
595 PropertyRNA *prop;
596
597 /* check if valid poselib */
598 if (act == NULL) {
599 BKE_report(op->reports, RPT_ERROR, "Object does not have pose lib data");
600 return OPERATOR_CANCELLED;
601 }
602
603 prop = RNA_struct_find_property(op->ptr, "pose");
604 if (RNA_property_is_set(op->ptr, prop)) {
605 marker_index = RNA_property_enum_get(op->ptr, prop);
606 }
607 else {
608 marker_index = act->active_marker - 1;
609 }
610
611 /* get index (and pointer) of pose to remove */
612 marker = BLI_findlink(&act->markers, marker_index);
613 if (marker == NULL) {
614 BKE_reportf(op->reports, RPT_ERROR, "Invalid pose specified %d", marker_index);
615 return OPERATOR_CANCELLED;
616 }
617
618 /* remove relevant keyframes */
619 for (fcu = act->curves.first; fcu; fcu = fcu->next) {
620 BezTriple *bezt;
621 uint i;
622
623 if (fcu->bezt) {
624 for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
625 /* check if remove */
626 if (IS_EQF(bezt->vec[1][0], (float)marker->frame)) {
627 delete_fcurve_key(fcu, i, 1);
628 break;
629 }
630 }
631 }
632 }
633
634 /* remove poselib from list */
635 BLI_freelinkN(&act->markers, marker);
636
637 /* fix active pose number */
638 act->active_marker = 0;
639
640 /* send notifiers for this - using keyframe editing notifiers, since action
641 * may be being shown in anim editors as active action
642 */
643 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
644 DEG_id_tag_update(&act->id, ID_RECALC_COPY_ON_WRITE);
645
646 /* done */
647 return OPERATOR_FINISHED;
648 }
649
POSELIB_OT_pose_remove(wmOperatorType * ot)650 void POSELIB_OT_pose_remove(wmOperatorType *ot)
651 {
652 PropertyRNA *prop;
653
654 /* identifiers */
655 ot->name = "PoseLib Remove Pose";
656 ot->idname = "POSELIB_OT_pose_remove";
657 ot->description = "Remove nth pose from the active Pose Library";
658
659 /* api callbacks */
660 ot->invoke = WM_menu_invoke;
661 ot->exec = poselib_remove_exec;
662 ot->poll = has_poselib_pose_data_for_editing_poll;
663
664 /* flags */
665 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
666
667 /* properties */
668 prop = RNA_def_enum(ot->srna, "pose", DummyRNA_NULL_items, 0, "Pose", "The pose to remove");
669 RNA_def_enum_funcs(prop, poselib_stored_pose_itemf);
670 RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
671 ot->prop = prop;
672 }
673
poselib_rename_invoke(bContext * C,wmOperator * op,const wmEvent * event)674 static int poselib_rename_invoke(bContext *C, wmOperator *op, const wmEvent *event)
675 {
676 Object *ob = get_poselib_object(C);
677 bAction *act = (ob) ? ob->poselib : NULL;
678 TimeMarker *marker;
679
680 /* check if valid poselib */
681 if (act == NULL) {
682 BKE_report(op->reports, RPT_ERROR, "Object does not have pose lib data");
683 return OPERATOR_CANCELLED;
684 }
685
686 /* get index (and pointer) of pose to remove */
687 marker = BLI_findlink(&act->markers, act->active_marker - 1);
688 if (marker == NULL) {
689 BKE_report(op->reports, RPT_ERROR, "Invalid index for pose");
690 return OPERATOR_CANCELLED;
691 }
692
693 /* Use the existing name of the marker as the name,
694 * and use the active marker as the one to rename. */
695 RNA_enum_set(op->ptr, "pose", act->active_marker - 1);
696 RNA_string_set(op->ptr, "name", marker->name);
697
698 /* part to sync with other similar operators... */
699 return WM_operator_props_popup_confirm(C, op, event);
700 }
701
poselib_rename_exec(bContext * C,wmOperator * op)702 static int poselib_rename_exec(bContext *C, wmOperator *op)
703 {
704 Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
705 bAction *act = (ob) ? ob->poselib : NULL;
706 TimeMarker *marker;
707 char newname[64];
708
709 /* check if valid poselib */
710 if (act == NULL) {
711 BKE_report(op->reports, RPT_ERROR, "Object does not have pose lib data");
712 return OPERATOR_CANCELLED;
713 }
714
715 /* get index (and pointer) of pose to remove */
716 marker = BLI_findlink(&act->markers, RNA_enum_get(op->ptr, "pose"));
717 if (marker == NULL) {
718 BKE_report(op->reports, RPT_ERROR, "Invalid index for pose");
719 return OPERATOR_CANCELLED;
720 }
721
722 /* get new name */
723 RNA_string_get(op->ptr, "name", newname);
724
725 /* copy name and validate it */
726 BLI_strncpy(marker->name, newname, sizeof(marker->name));
727 BLI_uniquename(
728 &act->markers, marker, DATA_("Pose"), '.', offsetof(TimeMarker, name), sizeof(marker->name));
729
730 /* send notifiers for this - using keyframe editing notifiers, since action
731 * may be being shown in anim editors as active action
732 */
733 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
734
735 /* done */
736 return OPERATOR_FINISHED;
737 }
738
POSELIB_OT_pose_rename(wmOperatorType * ot)739 void POSELIB_OT_pose_rename(wmOperatorType *ot)
740 {
741 PropertyRNA *prop;
742
743 /* identifiers */
744 ot->name = "PoseLib Rename Pose";
745 ot->idname = "POSELIB_OT_pose_rename";
746 ot->description = "Rename specified pose from the active Pose Library";
747
748 /* api callbacks */
749 ot->invoke = poselib_rename_invoke;
750 ot->exec = poselib_rename_exec;
751 ot->poll = has_poselib_pose_data_for_editing_poll;
752
753 /* flags */
754 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
755
756 /* properties */
757 /* NOTE: name not pose is the operator's "main" property,
758 * so that it will get activated in the popup for easy renaming */
759 ot->prop = RNA_def_string(
760 ot->srna, "name", "RenamedPose", 64, "New Pose Name", "New name for pose");
761 prop = RNA_def_enum(ot->srna, "pose", DummyRNA_NULL_items, 0, "Pose", "The pose to rename");
762 RNA_def_enum_funcs(prop, poselib_stored_pose_itemf);
763 RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
764 }
765
poselib_move_exec(bContext * C,wmOperator * op)766 static int poselib_move_exec(bContext *C, wmOperator *op)
767 {
768 Object *ob = get_poselib_object(C);
769 bAction *act = (ob) ? ob->poselib : NULL;
770 TimeMarker *marker;
771 int marker_index;
772 int dir;
773 PropertyRNA *prop;
774
775 /* check if valid poselib */
776 if (act == NULL) {
777 BKE_report(op->reports, RPT_ERROR, "Object does not have pose lib data");
778 return OPERATOR_CANCELLED;
779 }
780
781 prop = RNA_struct_find_property(op->ptr, "pose");
782 if (RNA_property_is_set(op->ptr, prop)) {
783 marker_index = RNA_property_enum_get(op->ptr, prop);
784 }
785 else {
786 marker_index = act->active_marker - 1;
787 }
788
789 /* get index (and pointer) of pose to remove */
790 marker = BLI_findlink(&act->markers, marker_index);
791 if (marker == NULL) {
792 BKE_reportf(op->reports, RPT_ERROR, "Invalid pose specified %d", marker_index);
793 return OPERATOR_CANCELLED;
794 }
795
796 dir = RNA_enum_get(op->ptr, "direction");
797
798 /* move pose */
799 if (BLI_listbase_link_move(&act->markers, marker, dir)) {
800 act->active_marker = marker_index + dir + 1;
801
802 /* send notifiers for this - using keyframe editing notifiers, since action
803 * may be being shown in anim editors as active action
804 */
805 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
806 }
807 else {
808 return OPERATOR_CANCELLED;
809 }
810
811 /* done */
812 return OPERATOR_FINISHED;
813 }
814
POSELIB_OT_pose_move(wmOperatorType * ot)815 void POSELIB_OT_pose_move(wmOperatorType *ot)
816 {
817 PropertyRNA *prop;
818 static const EnumPropertyItem pose_lib_pose_move[] = {
819 {-1, "UP", 0, "Up", ""},
820 {1, "DOWN", 0, "Down", ""},
821 {0, NULL, 0, NULL, NULL},
822 };
823
824 /* identifiers */
825 ot->name = "PoseLib Move Pose";
826 ot->idname = "POSELIB_OT_pose_move";
827 ot->description = "Move the pose up or down in the active Pose Library";
828
829 /* api callbacks */
830 ot->invoke = WM_menu_invoke;
831 ot->exec = poselib_move_exec;
832 ot->poll = has_poselib_pose_data_for_editing_poll;
833
834 /* flags */
835 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
836
837 /* properties */
838 prop = RNA_def_enum(ot->srna, "pose", DummyRNA_NULL_items, 0, "Pose", "The pose to move");
839 RNA_def_enum_funcs(prop, poselib_stored_pose_itemf);
840 RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
841 ot->prop = prop;
842
843 RNA_def_enum(ot->srna,
844 "direction",
845 pose_lib_pose_move,
846 0,
847 "Direction",
848 "Direction to move the chosen pose towards");
849 }
850
851 /* ************************************************************* */
852 /* Pose-Lib Browsing/Previewing Operator */
853
854 /* Simple struct for storing settings/data for use during PoseLib preview */
855 typedef struct tPoseLib_PreviewData {
856 /** tPoseLib_Backup structs for restoring poses. */
857 ListBase backups;
858 /** LinkData structs storing list of poses which match the current search-string. */
859 ListBase searchp;
860
861 /** active scene. */
862 Scene *scene;
863 /** active area. */
864 ScrArea *area;
865
866 /** RNA-Pointer to Object 'ob' .*/
867 PointerRNA rna_ptr;
868 /** object to work on. */
869 Object *ob;
870 /** object's armature data. */
871 bArmature *arm;
872 /** object's pose. */
873 bPose *pose;
874 /** poselib to use. */
875 bAction *act;
876 /** 'active' pose. */
877 TimeMarker *marker;
878
879 /** total number of elements to work on. */
880 int totcount;
881
882 /** state of main loop. */
883 short state;
884 /** redraw/update settings during main loop. */
885 short redraw;
886 /** flags for various settings. */
887 short flag;
888
889 /** position of cursor in searchstr (cursor occurs before the item at the nominated index) */
890 short search_cursor;
891 /** (Part of) Name to search for to filter poses that get shown. */
892 char searchstr[64];
893 /** Previously set searchstr (from last loop run),
894 * so that we can detected when to rebuild searchp. */
895 char searchold[64];
896
897 /** Info-text to print in header. */
898 char headerstr[UI_MAX_DRAW_STR];
899 } tPoseLib_PreviewData;
900
901 /* defines for tPoseLib_PreviewData->state values */
902 enum {
903 PL_PREVIEW_ERROR = -1,
904 PL_PREVIEW_RUNNING,
905 PL_PREVIEW_CONFIRM,
906 PL_PREVIEW_CANCEL,
907 PL_PREVIEW_RUNONCE,
908 };
909
910 /* defines for tPoseLib_PreviewData->redraw values */
911 enum {
912 PL_PREVIEW_NOREDRAW = 0,
913 PL_PREVIEW_REDRAWALL,
914 PL_PREVIEW_REDRAWHEADER,
915 };
916
917 /* defines for tPoseLib_PreviewData->flag values */
918 enum {
919 PL_PREVIEW_FIRSTTIME = (1 << 0),
920 PL_PREVIEW_SHOWORIGINAL = (1 << 1),
921 PL_PREVIEW_ANY_BONE_SELECTED = (1 << 2),
922 };
923
924 /* ---------------------------- */
925
926 /* simple struct for storing backup info */
927 typedef struct tPoseLib_Backup {
928 struct tPoseLib_Backup *next, *prev;
929
930 bPoseChannel *pchan; /* pose channel backups are for */
931
932 bPoseChannel olddata; /* copy of pose channel's old data (at start) */
933 IDProperty *oldprops; /* copy (needs freeing) of pose channel's properties (at start) */
934 } tPoseLib_Backup;
935
936 /* Makes a copy of the current pose for restoration purposes - doesn't do constraints currently */
poselib_backup_posecopy(tPoseLib_PreviewData * pld)937 static void poselib_backup_posecopy(tPoseLib_PreviewData *pld)
938 {
939 bActionGroup *agrp;
940 bPoseChannel *pchan;
941 bool selected = false;
942
943 /* determine whether any bone is selected. */
944 LISTBASE_FOREACH (bPoseChannel *, bchan, &pld->pose->chanbase) {
945 selected = bchan->bone != NULL && bchan->bone->flag & BONE_SELECTED;
946 if (selected) {
947 pld->flag |= PL_PREVIEW_ANY_BONE_SELECTED;
948 break;
949 }
950 }
951 if (!selected) {
952 pld->flag &= ~PL_PREVIEW_ANY_BONE_SELECTED;
953 }
954
955 /* for each posechannel that has an actionchannel in */
956 for (agrp = pld->act->groups.first; agrp; agrp = agrp->next) {
957 /* try to find posechannel */
958 pchan = BKE_pose_channel_find_name(pld->pose, agrp->name);
959
960 /* backup data if available */
961 if (pchan) {
962 tPoseLib_Backup *plb;
963
964 /* store backup */
965 plb = MEM_callocN(sizeof(tPoseLib_Backup), "tPoseLib_Backup");
966
967 plb->pchan = pchan;
968 memcpy(&plb->olddata, plb->pchan, sizeof(bPoseChannel));
969
970 if (pchan->prop) {
971 plb->oldprops = IDP_CopyProperty(pchan->prop);
972 }
973
974 BLI_addtail(&pld->backups, plb);
975
976 /* mark as being affected */
977 pld->totcount++;
978 }
979 }
980 }
981
982 /* Restores original pose */
poselib_backup_restore(tPoseLib_PreviewData * pld)983 static void poselib_backup_restore(tPoseLib_PreviewData *pld)
984 {
985 tPoseLib_Backup *plb;
986
987 for (plb = pld->backups.first; plb; plb = plb->next) {
988 /* copy most of data straight back */
989 memcpy(plb->pchan, &plb->olddata, sizeof(bPoseChannel));
990
991 /* just overwrite values of properties from the stored copies (there should be some) */
992 if (plb->oldprops) {
993 IDP_SyncGroupValues(plb->pchan->prop, plb->oldprops);
994 }
995
996 /* TODO: constraints settings aren't restored yet,
997 * even though these could change (though not that likely) */
998 }
999 }
1000
1001 /* Free list of backups, including any side data it may use */
poselib_backup_free_data(tPoseLib_PreviewData * pld)1002 static void poselib_backup_free_data(tPoseLib_PreviewData *pld)
1003 {
1004 tPoseLib_Backup *plb, *plbn;
1005
1006 for (plb = pld->backups.first; plb; plb = plbn) {
1007 plbn = plb->next;
1008
1009 /* free custom data */
1010 if (plb->oldprops) {
1011 IDP_FreeProperty(plb->oldprops);
1012 }
1013
1014 /* free backup element now */
1015 BLI_freelinkN(&pld->backups, plb);
1016 }
1017 }
1018
1019 /* ---------------------------- */
1020
1021 /* Applies the appropriate stored pose from the pose-library to the current pose
1022 * - assumes that a valid object, with a poselib has been supplied
1023 * - gets the string to print in the header
1024 * - this code is based on the code for extract_pose_from_action in blenkernel/action.c
1025 */
poselib_apply_pose(tPoseLib_PreviewData * pld,const AnimationEvalContext * anim_eval_context)1026 static void poselib_apply_pose(tPoseLib_PreviewData *pld,
1027 const AnimationEvalContext *anim_eval_context)
1028 {
1029 PointerRNA *ptr = &pld->rna_ptr;
1030 bArmature *arm = pld->arm;
1031 bPose *pose = pld->pose;
1032 bPoseChannel *pchan;
1033 bAction *act = pld->act;
1034 bActionGroup *agrp;
1035
1036 KeyframeEditData ked = {{NULL}};
1037 KeyframeEditFunc group_ok_cb;
1038 int frame = 1;
1039 const bool any_bone_selected = pld->flag & PL_PREVIEW_ANY_BONE_SELECTED;
1040
1041 /* get the frame */
1042 if (pld->marker) {
1043 frame = pld->marker->frame;
1044 }
1045 else {
1046 return;
1047 }
1048
1049 /* init settings for testing groups for keyframes */
1050 group_ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE);
1051 ked.f1 = ((float)frame) - 0.5f;
1052 ked.f2 = ((float)frame) + 0.5f;
1053 AnimationEvalContext anim_context_at_frame = BKE_animsys_eval_context_construct_at(
1054 anim_eval_context, frame);
1055
1056 /* start applying - only those channels which have a key at this point in time! */
1057 for (agrp = act->groups.first; agrp; agrp = agrp->next) {
1058 /* check if group has any keyframes */
1059 if (ANIM_animchanneldata_keyframes_loop(
1060 &ked, NULL, agrp, ALE_GROUP, NULL, group_ok_cb, NULL)) {
1061 /* has keyframe on this frame, so try to get a PoseChannel with this name */
1062 pchan = BKE_pose_channel_find_name(pose, agrp->name);
1063
1064 if (pchan) {
1065 bool ok = 0;
1066
1067 /* check if this bone should get any animation applied */
1068 if (!any_bone_selected) {
1069 /* if no bones are selected, then any bone is ok */
1070 ok = 1;
1071 }
1072 else if (pchan->bone) {
1073 /* only ok if bone is visible and selected */
1074 if ((pchan->bone->flag & BONE_SELECTED) && (pchan->bone->flag & BONE_HIDDEN_P) == 0 &&
1075 (pchan->bone->layer & arm->layer)) {
1076 ok = 1;
1077 }
1078 }
1079
1080 if (ok) {
1081 animsys_evaluate_action_group(ptr, act, agrp, &anim_context_at_frame);
1082 }
1083 }
1084 }
1085 }
1086 }
1087
1088 /* Auto-keys/tags bones affected by the pose used from the poselib */
poselib_keytag_pose(bContext * C,Scene * scene,tPoseLib_PreviewData * pld)1089 static void poselib_keytag_pose(bContext *C, Scene *scene, tPoseLib_PreviewData *pld)
1090 {
1091 bPose *pose = pld->pose;
1092 bPoseChannel *pchan;
1093 bAction *act = pld->act;
1094 bActionGroup *agrp;
1095
1096 KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_WHOLE_CHARACTER_ID);
1097 ListBase dsources = {NULL, NULL};
1098 bool autokey = autokeyframe_cfra_can_key(scene, &pld->ob->id);
1099 const bool any_bone_selected = pld->flag & PL_PREVIEW_ANY_BONE_SELECTED;
1100
1101 /* start tagging/keying */
1102 for (agrp = act->groups.first; agrp; agrp = agrp->next) {
1103 /* only for selected bones unless there aren't any selected, in which case all are included */
1104 pchan = BKE_pose_channel_find_name(pose, agrp->name);
1105
1106 if (pchan) {
1107 if (!any_bone_selected || ((pchan->bone) && (pchan->bone->flag & BONE_SELECTED))) {
1108 if (autokey) {
1109 /* add datasource override for the PoseChannel, to be used later */
1110 ANIM_relative_keyingset_add_source(&dsources, &pld->ob->id, &RNA_PoseBone, pchan);
1111
1112 /* clear any unkeyed tags */
1113 if (pchan->bone) {
1114 pchan->bone->flag &= ~BONE_UNKEYED;
1115 }
1116 }
1117 else {
1118 /* add unkeyed tags */
1119 if (pchan->bone) {
1120 pchan->bone->flag |= BONE_UNKEYED;
1121 }
1122 }
1123 }
1124 }
1125 }
1126
1127 /* perform actual auto-keying now */
1128 if (autokey) {
1129 /* insert keyframes for all relevant bones in one go */
1130 ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
1131 BLI_freelistN(&dsources);
1132 }
1133
1134 /* send notifiers for this */
1135 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
1136 }
1137
1138 /* Apply the relevant changes to the pose */
poselib_preview_apply(bContext * C,wmOperator * op)1139 static void poselib_preview_apply(bContext *C, wmOperator *op)
1140 {
1141 tPoseLib_PreviewData *pld = (tPoseLib_PreviewData *)op->customdata;
1142
1143 /* only recalc pose (and its dependencies) if pose has changed */
1144 if (pld->redraw == PL_PREVIEW_REDRAWALL) {
1145 /* don't clear pose if firsttime */
1146 if ((pld->flag & PL_PREVIEW_FIRSTTIME) == 0) {
1147 poselib_backup_restore(pld);
1148 }
1149 else {
1150 pld->flag &= ~PL_PREVIEW_FIRSTTIME;
1151 }
1152
1153 /* pose should be the right one to draw (unless we're temporarily not showing it) */
1154 if ((pld->flag & PL_PREVIEW_SHOWORIGINAL) == 0) {
1155 RNA_int_set(op->ptr, "pose_index", BLI_findindex(&pld->act->markers, pld->marker));
1156 struct Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
1157
1158 const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(
1159 depsgraph, 0.0f /* poselib_apply_pose() determines its own evaluation time. */);
1160 poselib_apply_pose(pld, &anim_eval_context);
1161 }
1162 else {
1163 RNA_int_set(op->ptr, "pose_index", -2); /* -2 means don't apply any pose */
1164 }
1165
1166 DEG_id_tag_update(&pld->ob->id, ID_RECALC_GEOMETRY);
1167 }
1168
1169 /* do header print - if interactively previewing */
1170 if (pld->state == PL_PREVIEW_RUNNING) {
1171 if (pld->flag & PL_PREVIEW_SHOWORIGINAL) {
1172 ED_area_status_text(pld->area, TIP_("PoseLib Previewing Pose: [Showing Original Pose]"));
1173 ED_workspace_status_text(C, TIP_("Use Tab to start previewing poses again"));
1174 }
1175 else if (pld->searchstr[0]) {
1176 char tempstr[65];
1177 char markern[64];
1178 short index;
1179
1180 /* get search-string */
1181 index = pld->search_cursor;
1182
1183 if (index >= 0 && index < sizeof(tempstr) - 1) {
1184 memcpy(&tempstr[0], &pld->searchstr[0], index);
1185 tempstr[index] = '|';
1186 memcpy(&tempstr[index + 1], &pld->searchstr[index], (sizeof(tempstr) - 1) - index);
1187 }
1188 else {
1189 BLI_strncpy(tempstr, pld->searchstr, sizeof(tempstr));
1190 }
1191
1192 /* get marker name */
1193 BLI_strncpy(markern, pld->marker ? pld->marker->name : "No Matches", sizeof(markern));
1194
1195 BLI_snprintf(pld->headerstr,
1196 sizeof(pld->headerstr),
1197 TIP_("PoseLib Previewing Pose: Filter - [%s] | "
1198 "Current Pose - \"%s\""),
1199 tempstr,
1200 markern);
1201 ED_area_status_text(pld->area, pld->headerstr);
1202 ED_workspace_status_text(C, TIP_("Use ScrollWheel or PageUp/Down to change pose"));
1203 }
1204 else {
1205 BLI_snprintf(pld->headerstr,
1206 sizeof(pld->headerstr),
1207 TIP_("PoseLib Previewing Pose: \"%s\""),
1208 pld->marker->name);
1209 ED_area_status_text(pld->area, pld->headerstr);
1210 ED_workspace_status_text(C, NULL);
1211 }
1212 }
1213
1214 /* request drawing of view + clear redraw flag */
1215 WM_event_add_notifier(C, NC_OBJECT | ND_POSE, pld->ob);
1216 pld->redraw = PL_PREVIEW_NOREDRAW;
1217 }
1218
1219 /* ---------------------------- */
1220
1221 /* This helper function is called during poselib_preview_poses to find the
1222 * pose to preview next (after a change event)
1223 */
poselib_preview_get_next(tPoseLib_PreviewData * pld,int step)1224 static void poselib_preview_get_next(tPoseLib_PreviewData *pld, int step)
1225 {
1226 /* stop if not going anywhere, as we assume that there is a direction to move in */
1227 if (step == 0) {
1228 return;
1229 }
1230
1231 /* search-string dictates a special approach */
1232 if (pld->searchstr[0]) {
1233 TimeMarker *marker;
1234 LinkData *ld, *ldn, *ldc;
1235
1236 /* free and rebuild if needed (i.e. if search-str changed) */
1237 if (!STREQ(pld->searchstr, pld->searchold)) {
1238 /* free list of temporary search matches */
1239 BLI_freelistN(&pld->searchp);
1240
1241 /* generate a new list of search matches */
1242 for (marker = pld->act->markers.first; marker; marker = marker->next) {
1243 /* does the name partially match?
1244 * - don't worry about case, to make it easier for users to quickly input a name (or
1245 * part of one), which is the whole point of this feature
1246 */
1247 if (BLI_strcasestr(marker->name, pld->searchstr)) {
1248 /* make link-data to store reference to it */
1249 ld = MEM_callocN(sizeof(LinkData), "PoseMatch");
1250 ld->data = marker;
1251 BLI_addtail(&pld->searchp, ld);
1252 }
1253 }
1254
1255 /* set current marker to NULL (so that we start from first) */
1256 pld->marker = NULL;
1257 }
1258
1259 /* check if any matches */
1260 if (BLI_listbase_is_empty(&pld->searchp)) {
1261 pld->marker = NULL;
1262 return;
1263 }
1264
1265 /* find first match */
1266 for (ldc = pld->searchp.first; ldc; ldc = ldc->next) {
1267 if (ldc->data == pld->marker) {
1268 break;
1269 }
1270 }
1271 if (ldc == NULL) {
1272 ldc = pld->searchp.first;
1273 }
1274
1275 /* Loop through the matches in a cyclic fashion, incrementing/decrementing step as appropriate
1276 * until step == 0. At this point, marker should be the correct marker.
1277 */
1278 if (step > 0) {
1279 for (ld = ldc; ld && step; ld = ldn, step--) {
1280 ldn = (ld->next) ? ld->next : pld->searchp.first;
1281 }
1282 }
1283 else {
1284 for (ld = ldc; ld && step; ld = ldn, step++) {
1285 ldn = (ld->prev) ? ld->prev : pld->searchp.last;
1286 }
1287 }
1288
1289 /* set marker */
1290 if (ld) {
1291 pld->marker = ld->data;
1292 }
1293 }
1294 else {
1295 TimeMarker *marker, *next;
1296
1297 /* if no marker, because we just ended searching, then set that to the start of the list */
1298 if (pld->marker == NULL) {
1299 pld->marker = pld->act->markers.first;
1300 }
1301
1302 /* Loop through the markers in a cyclic fashion, incrementing/decrementing step as appropriate
1303 * until step == 0. At this point, marker should be the correct marker.
1304 */
1305 if (step > 0) {
1306 for (marker = pld->marker; marker && step; marker = next, step--) {
1307 next = (marker->next) ? marker->next : pld->act->markers.first;
1308 }
1309 }
1310 else {
1311 for (marker = pld->marker; marker && step; marker = next, step++) {
1312 next = (marker->prev) ? marker->prev : pld->act->markers.last;
1313 }
1314 }
1315
1316 /* it should be fairly impossible for marker to be NULL */
1317 if (marker) {
1318 pld->marker = marker;
1319 }
1320 }
1321 }
1322
1323 /* specially handle events for searching */
poselib_preview_handle_search(tPoseLib_PreviewData * pld,ushort event,char ascii)1324 static void poselib_preview_handle_search(tPoseLib_PreviewData *pld, ushort event, char ascii)
1325 {
1326 /* try doing some form of string manipulation first */
1327 switch (event) {
1328 case EVT_BACKSPACEKEY:
1329 if (pld->searchstr[0] && pld->search_cursor) {
1330 short len = strlen(pld->searchstr);
1331 short index = pld->search_cursor;
1332 short i;
1333
1334 for (i = index; i <= len; i++) {
1335 pld->searchstr[i - 1] = pld->searchstr[i];
1336 }
1337
1338 pld->search_cursor--;
1339
1340 poselib_preview_get_next(pld, 1);
1341 pld->redraw = PL_PREVIEW_REDRAWALL;
1342 return;
1343 }
1344 break;
1345
1346 case EVT_DELKEY:
1347 if (pld->searchstr[0] && pld->searchstr[1]) {
1348 short len = strlen(pld->searchstr);
1349 short index = pld->search_cursor;
1350 int i;
1351
1352 if (index < len) {
1353 for (i = index; i < len; i++) {
1354 pld->searchstr[i] = pld->searchstr[i + 1];
1355 }
1356
1357 poselib_preview_get_next(pld, 1);
1358 pld->redraw = PL_PREVIEW_REDRAWALL;
1359 return;
1360 }
1361 }
1362 break;
1363 }
1364
1365 if (ascii) {
1366 /* character to add to the string */
1367 short index = pld->search_cursor;
1368 short len = (pld->searchstr[0]) ? strlen(pld->searchstr) : 0;
1369 short i;
1370
1371 if (len) {
1372 for (i = len; i > index; i--) {
1373 pld->searchstr[i] = pld->searchstr[i - 1];
1374 }
1375 }
1376 else {
1377 pld->searchstr[1] = 0;
1378 }
1379
1380 pld->searchstr[index] = ascii;
1381 pld->search_cursor++;
1382
1383 poselib_preview_get_next(pld, 1);
1384 pld->redraw = PL_PREVIEW_REDRAWALL;
1385 }
1386 }
1387
1388 /* handle events for poselib_preview_poses */
poselib_preview_handle_event(bContext * UNUSED (C),wmOperator * op,const wmEvent * event)1389 static int poselib_preview_handle_event(bContext *UNUSED(C), wmOperator *op, const wmEvent *event)
1390 {
1391 tPoseLib_PreviewData *pld = op->customdata;
1392 int ret = OPERATOR_RUNNING_MODAL;
1393
1394 /* only accept 'press' event, and ignore 'release', so that we don't get double actions */
1395 if (ELEM(event->val, KM_PRESS, KM_NOTHING) == 0) {
1396 #if 0
1397 printf("PoseLib: skipping event with type '%s' and val %d\n",
1398 WM_key_event_string(event->type, false),
1399 event->val);
1400 #endif
1401 return ret;
1402 }
1403
1404 /* backup stuff that needs to occur before every operation
1405 * - make a copy of searchstr, so that we know if cache needs to be rebuilt
1406 */
1407 BLI_strncpy(pld->searchold, pld->searchstr, sizeof(pld->searchold));
1408
1409 /* if we're currently showing the original pose, only certain events are handled */
1410 if (pld->flag & PL_PREVIEW_SHOWORIGINAL) {
1411 switch (event->type) {
1412 /* exit - cancel */
1413 case EVT_ESCKEY:
1414 case RIGHTMOUSE:
1415 pld->state = PL_PREVIEW_CANCEL;
1416 break;
1417
1418 /* exit - confirm */
1419 case LEFTMOUSE:
1420 case EVT_RETKEY:
1421 case EVT_PADENTER:
1422 case EVT_SPACEKEY:
1423 pld->state = PL_PREVIEW_CONFIRM;
1424 break;
1425
1426 /* view manipulation */
1427 /* we add pass through here, so that the operators responsible for these can still run,
1428 * even though we still maintain control (as RUNNING_MODAL flag is still set too)
1429 */
1430 case EVT_PAD0:
1431 case EVT_PAD1:
1432 case EVT_PAD2:
1433 case EVT_PAD3:
1434 case EVT_PAD4:
1435 case EVT_PAD5:
1436 case EVT_PAD6:
1437 case EVT_PAD7:
1438 case EVT_PAD8:
1439 case EVT_PAD9:
1440 case EVT_PADPLUSKEY:
1441 case EVT_PADMINUS:
1442 case MIDDLEMOUSE:
1443 case MOUSEMOVE:
1444 // pld->redraw = PL_PREVIEW_REDRAWHEADER;
1445 ret = OPERATOR_PASS_THROUGH;
1446 break;
1447
1448 /* quicky compare to original */
1449 case EVT_TABKEY:
1450 pld->flag &= ~PL_PREVIEW_SHOWORIGINAL;
1451 pld->redraw = PL_PREVIEW_REDRAWALL;
1452 break;
1453 }
1454
1455 /* EXITS HERE... */
1456 return ret;
1457 }
1458
1459 /* NORMAL EVENT HANDLING... */
1460 /* searching takes priority over normal activity */
1461 switch (event->type) {
1462 /* exit - cancel */
1463 case EVT_ESCKEY:
1464 case RIGHTMOUSE:
1465 pld->state = PL_PREVIEW_CANCEL;
1466 break;
1467
1468 /* exit - confirm */
1469 case LEFTMOUSE:
1470 case EVT_RETKEY:
1471 case EVT_PADENTER:
1472 case EVT_SPACEKEY:
1473 pld->state = PL_PREVIEW_CONFIRM;
1474 break;
1475
1476 /* toggle between original pose and poselib pose*/
1477 case EVT_TABKEY:
1478 pld->flag |= PL_PREVIEW_SHOWORIGINAL;
1479 pld->redraw = PL_PREVIEW_REDRAWALL;
1480 break;
1481
1482 /* change to previous pose (cyclic) */
1483 case EVT_PAGEUPKEY:
1484 case WHEELUPMOUSE:
1485 poselib_preview_get_next(pld, -1);
1486 pld->redraw = PL_PREVIEW_REDRAWALL;
1487 break;
1488
1489 /* change to next pose (cyclic) */
1490 case EVT_PAGEDOWNKEY:
1491 case WHEELDOWNMOUSE:
1492 poselib_preview_get_next(pld, 1);
1493 pld->redraw = PL_PREVIEW_REDRAWALL;
1494 break;
1495
1496 /* jump 5 poses (cyclic, back) */
1497 case EVT_DOWNARROWKEY:
1498 poselib_preview_get_next(pld, -5);
1499 pld->redraw = PL_PREVIEW_REDRAWALL;
1500 break;
1501
1502 /* jump 5 poses (cyclic, forward) */
1503 case EVT_UPARROWKEY:
1504 poselib_preview_get_next(pld, 5);
1505 pld->redraw = PL_PREVIEW_REDRAWALL;
1506 break;
1507
1508 /* change to next pose or searching cursor control */
1509 case EVT_RIGHTARROWKEY:
1510 if (pld->searchstr[0]) {
1511 /* move text-cursor to the right */
1512 if (pld->search_cursor < strlen(pld->searchstr)) {
1513 pld->search_cursor++;
1514 }
1515 pld->redraw = PL_PREVIEW_REDRAWHEADER;
1516 }
1517 else {
1518 /* change to next pose (cyclic) */
1519 poselib_preview_get_next(pld, 1);
1520 pld->redraw = PL_PREVIEW_REDRAWALL;
1521 }
1522 break;
1523
1524 /* change to next pose or searching cursor control */
1525 case EVT_LEFTARROWKEY:
1526 if (pld->searchstr[0]) {
1527 /* move text-cursor to the left */
1528 if (pld->search_cursor) {
1529 pld->search_cursor--;
1530 }
1531 pld->redraw = PL_PREVIEW_REDRAWHEADER;
1532 }
1533 else {
1534 /* change to previous pose (cyclic) */
1535 poselib_preview_get_next(pld, -1);
1536 pld->redraw = PL_PREVIEW_REDRAWALL;
1537 }
1538 break;
1539
1540 /* change to first pose or start of searching string */
1541 case EVT_HOMEKEY:
1542 if (pld->searchstr[0]) {
1543 pld->search_cursor = 0;
1544 pld->redraw = PL_PREVIEW_REDRAWHEADER;
1545 }
1546 else {
1547 /* change to first pose */
1548 pld->marker = pld->act->markers.first;
1549 pld->act->active_marker = 1;
1550
1551 pld->redraw = PL_PREVIEW_REDRAWALL;
1552 }
1553 break;
1554
1555 /* change to last pose or start of searching string */
1556 case EVT_ENDKEY:
1557 if (pld->searchstr[0]) {
1558 pld->search_cursor = strlen(pld->searchstr);
1559 pld->redraw = PL_PREVIEW_REDRAWHEADER;
1560 }
1561 else {
1562 /* change to last pose */
1563 pld->marker = pld->act->markers.last;
1564 pld->act->active_marker = BLI_listbase_count(&pld->act->markers);
1565
1566 pld->redraw = PL_PREVIEW_REDRAWALL;
1567 }
1568 break;
1569
1570 /* view manipulation */
1571 /* we add pass through here, so that the operators responsible for these can still run,
1572 * even though we still maintain control (as RUNNING_MODAL flag is still set too)
1573 */
1574 case MIDDLEMOUSE:
1575 case MOUSEMOVE:
1576 // pld->redraw = PL_PREVIEW_REDRAWHEADER;
1577 ret = OPERATOR_PASS_THROUGH;
1578 break;
1579
1580 /* view manipulation, or searching */
1581 case EVT_PAD0:
1582 case EVT_PAD1:
1583 case EVT_PAD2:
1584 case EVT_PAD3:
1585 case EVT_PAD4:
1586 case EVT_PAD5:
1587 case EVT_PAD6:
1588 case EVT_PAD7:
1589 case EVT_PAD8:
1590 case EVT_PAD9:
1591 case EVT_PADPLUSKEY:
1592 case EVT_PADMINUS:
1593 if (pld->searchstr[0]) {
1594 /* searching... */
1595 poselib_preview_handle_search(pld, event->type, event->ascii);
1596 }
1597 else {
1598 /* view manipulation (see above) */
1599 // pld->redraw = PL_PREVIEW_REDRAWHEADER;
1600 ret = OPERATOR_PASS_THROUGH;
1601 }
1602 break;
1603
1604 /* otherwise, assume that searching might be able to handle it */
1605 default:
1606 poselib_preview_handle_search(pld, event->type, event->ascii);
1607 break;
1608 }
1609
1610 return ret;
1611 }
1612
1613 /* ---------------------------- */
1614
1615 /* Init PoseLib Previewing data */
poselib_preview_init_data(bContext * C,wmOperator * op)1616 static void poselib_preview_init_data(bContext *C, wmOperator *op)
1617 {
1618 tPoseLib_PreviewData *pld;
1619 Object *ob = get_poselib_object(C);
1620 int pose_index = RNA_int_get(op->ptr, "pose_index");
1621
1622 /* set up preview state info */
1623 op->customdata = pld = MEM_callocN(sizeof(tPoseLib_PreviewData), "PoseLib Preview Data");
1624
1625 /* get basic data */
1626 pld->ob = ob;
1627 pld->arm = (ob) ? (ob->data) : NULL;
1628 pld->pose = (ob) ? (ob->pose) : NULL;
1629 pld->act = (ob) ? (ob->poselib) : NULL;
1630
1631 pld->scene = CTX_data_scene(C);
1632 pld->area = CTX_wm_area(C);
1633
1634 /* get starting pose based on RNA-props for this operator */
1635 if (pose_index == -1) {
1636 pld->marker = poselib_get_active_pose(pld->act);
1637 }
1638 else if (pose_index == -2) {
1639 pld->flag |= PL_PREVIEW_SHOWORIGINAL;
1640 }
1641 else {
1642 pld->marker = (pld->act) ? BLI_findlink(&pld->act->markers, pose_index) : NULL;
1643 }
1644
1645 /* check if valid poselib */
1646 if (ELEM(NULL, pld->ob, pld->pose, pld->arm)) {
1647 BKE_report(op->reports, RPT_ERROR, "Pose lib is only for armatures in pose mode");
1648 pld->state = PL_PREVIEW_ERROR;
1649 return;
1650 }
1651 if (pld->act == NULL) {
1652 BKE_report(op->reports, RPT_ERROR, "Object does not have a valid pose lib");
1653 pld->state = PL_PREVIEW_ERROR;
1654 return;
1655 }
1656 if (pld->marker == NULL) {
1657 if (pld->act->markers.first) {
1658 /* just use first one then... */
1659 pld->marker = pld->act->markers.first;
1660 if (pose_index > -2) {
1661 BKE_report(op->reports, RPT_WARNING, "Pose lib had no active pose");
1662 }
1663 }
1664 else {
1665 BKE_report(op->reports, RPT_ERROR, "Pose lib has no poses to preview/apply");
1666 pld->state = PL_PREVIEW_ERROR;
1667 return;
1668 }
1669 }
1670
1671 /* get ID pointer for applying poses */
1672 RNA_id_pointer_create(&ob->id, &pld->rna_ptr);
1673
1674 /* make backups for restoring pose */
1675 poselib_backup_posecopy(pld);
1676
1677 /* set flags for running */
1678 pld->state = PL_PREVIEW_RUNNING;
1679 pld->redraw = PL_PREVIEW_REDRAWALL;
1680 pld->flag |= PL_PREVIEW_FIRSTTIME;
1681
1682 /* set depsgraph flags */
1683 /* make sure the lock is set OK, unlock can be accidentally saved? */
1684 pld->pose->flag |= POSE_LOCKED;
1685 pld->pose->flag &= ~POSE_DO_UNLOCK;
1686
1687 /* clear strings + search */
1688 pld->headerstr[0] = pld->searchstr[0] = pld->searchold[0] = '\0';
1689 pld->search_cursor = 0;
1690 }
1691
1692 /* After previewing poses */
poselib_preview_cleanup(bContext * C,wmOperator * op)1693 static void poselib_preview_cleanup(bContext *C, wmOperator *op)
1694 {
1695 tPoseLib_PreviewData *pld = (tPoseLib_PreviewData *)op->customdata;
1696 Scene *scene = pld->scene;
1697 Object *ob = pld->ob;
1698 bPose *pose = pld->pose;
1699 bAction *act = pld->act;
1700 TimeMarker *marker = pld->marker;
1701
1702 /* redraw the header so that it doesn't show any of our stuff anymore */
1703 ED_area_status_text(pld->area, NULL);
1704 ED_workspace_status_text(C, NULL);
1705
1706 /* this signal does one recalc on pose, then unlocks, so ESC or edit will work */
1707 pose->flag |= POSE_DO_UNLOCK;
1708
1709 /* clear pose if canceled */
1710 if (pld->state == PL_PREVIEW_CANCEL) {
1711 poselib_backup_restore(pld);
1712
1713 DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
1714 }
1715 else if (pld->state == PL_PREVIEW_CONFIRM) {
1716 /* tag poses as appropriate */
1717 poselib_keytag_pose(C, scene, pld);
1718
1719 /* change active pose setting */
1720 act->active_marker = BLI_findindex(&act->markers, marker) + 1;
1721 action_set_activemarker(act, marker, NULL);
1722
1723 /* Update event for pose and deformation children */
1724 DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
1725
1726 /* updates */
1727 if (IS_AUTOKEY_MODE(scene, NORMAL)) {
1728 // remake_action_ipos(ob->action);
1729 }
1730 }
1731
1732 /* Request final redraw of the view. */
1733 WM_event_add_notifier(C, NC_OBJECT | ND_POSE, pld->ob);
1734
1735 /* free memory used for backups and searching */
1736 poselib_backup_free_data(pld);
1737 BLI_freelistN(&pld->searchp);
1738
1739 /* free temp data for operator */
1740 MEM_freeN(pld);
1741 op->customdata = NULL;
1742 }
1743
1744 /* End previewing operation */
poselib_preview_exit(bContext * C,wmOperator * op)1745 static int poselib_preview_exit(bContext *C, wmOperator *op)
1746 {
1747 tPoseLib_PreviewData *pld = op->customdata;
1748 int exit_state = pld->state;
1749
1750 /* finish up */
1751 poselib_preview_cleanup(C, op);
1752
1753 if (ELEM(exit_state, PL_PREVIEW_CANCEL, PL_PREVIEW_ERROR)) {
1754 return OPERATOR_CANCELLED;
1755 }
1756 return OPERATOR_FINISHED;
1757 }
1758
1759 /* Cancel previewing operation (called when exiting Blender) */
poselib_preview_cancel(bContext * C,wmOperator * op)1760 static void poselib_preview_cancel(bContext *C, wmOperator *op)
1761 {
1762 poselib_preview_exit(C, op);
1763 }
1764
1765 /* main modal status check */
poselib_preview_modal(bContext * C,wmOperator * op,const wmEvent * event)1766 static int poselib_preview_modal(bContext *C, wmOperator *op, const wmEvent *event)
1767 {
1768 tPoseLib_PreviewData *pld = op->customdata;
1769 int ret;
1770
1771 /* 1) check state to see if we're still running */
1772 if (pld->state != PL_PREVIEW_RUNNING) {
1773 return poselib_preview_exit(C, op);
1774 }
1775
1776 /* 2) handle events */
1777 ret = poselib_preview_handle_event(C, op, event);
1778
1779 /* 3) apply changes and redraw, otherwise, confirming goes wrong */
1780 if (pld->redraw) {
1781 poselib_preview_apply(C, op);
1782 }
1783
1784 return ret;
1785 }
1786
1787 /* Modal Operator init */
poselib_preview_invoke(bContext * C,wmOperator * op,const wmEvent * UNUSED (event))1788 static int poselib_preview_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1789 {
1790 tPoseLib_PreviewData *pld;
1791
1792 /* check if everything is ok, and init settings for modal operator */
1793 poselib_preview_init_data(C, op);
1794 pld = (tPoseLib_PreviewData *)op->customdata;
1795
1796 if (pld->state == PL_PREVIEW_ERROR) {
1797 /* an error occurred, so free temp mem used */
1798 poselib_preview_cleanup(C, op);
1799 return OPERATOR_CANCELLED;
1800 }
1801
1802 /* do initial apply to have something to look at */
1803 poselib_preview_apply(C, op);
1804
1805 /* add temp handler if we're running as a modal operator */
1806 WM_event_add_modal_handler(C, op);
1807
1808 return OPERATOR_RUNNING_MODAL;
1809 }
1810
1811 /* Repeat operator */
poselib_preview_exec(bContext * C,wmOperator * op)1812 static int poselib_preview_exec(bContext *C, wmOperator *op)
1813 {
1814 tPoseLib_PreviewData *pld;
1815
1816 /* check if everything is ok, and init settings for modal operator */
1817 poselib_preview_init_data(C, op);
1818 pld = (tPoseLib_PreviewData *)op->customdata;
1819
1820 if (pld->state == PL_PREVIEW_ERROR) {
1821 /* an error occurred, so free temp mem used */
1822 poselib_preview_cleanup(C, op);
1823 return OPERATOR_CANCELLED;
1824 }
1825
1826 /* the exec() callback is effectively a 'run-once' scenario, so set the state to that
1827 * so that everything draws correctly
1828 */
1829 pld->state = PL_PREVIEW_RUNONCE;
1830
1831 /* apply the active pose */
1832 poselib_preview_apply(C, op);
1833
1834 /* now, set the status to exit */
1835 pld->state = PL_PREVIEW_CONFIRM;
1836
1837 /* cleanup */
1838 return poselib_preview_exit(C, op);
1839 }
1840
POSELIB_OT_browse_interactive(wmOperatorType * ot)1841 void POSELIB_OT_browse_interactive(wmOperatorType *ot)
1842 {
1843 /* identifiers */
1844 ot->name = "PoseLib Browse Poses";
1845 ot->idname = "POSELIB_OT_browse_interactive";
1846 ot->description = "Interactively browse poses in 3D-View";
1847
1848 /* callbacks */
1849 ot->invoke = poselib_preview_invoke;
1850 ot->modal = poselib_preview_modal;
1851 ot->cancel = poselib_preview_cancel;
1852 ot->exec = poselib_preview_exec;
1853 ot->poll = has_poselib_pose_data_poll;
1854
1855 /* flags */
1856 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
1857
1858 /* properties */
1859 /* TODO: make the pose_index into a proper enum instead of a cryptic int. */
1860 ot->prop = RNA_def_int(
1861 ot->srna,
1862 "pose_index",
1863 -1,
1864 -2,
1865 INT_MAX,
1866 "Pose",
1867 "Index of the pose to apply (-2 for no change to pose, -1 for poselib active pose)",
1868 0,
1869 INT_MAX);
1870
1871 /* XXX: percentage vs factor? */
1872 /* not used yet */
1873 #if 0
1874 RNA_def_float_factor(ot->srna,
1875 "blend_factor",
1876 1.0f,
1877 0.0f,
1878 1.0f,
1879 "Blend Factor",
1880 "Amount that the pose is applied on top of the existing poses",
1881 0.0f,
1882 1.0f);
1883 #endif
1884 }
1885
POSELIB_OT_apply_pose(wmOperatorType * ot)1886 void POSELIB_OT_apply_pose(wmOperatorType *ot)
1887 {
1888 /* identifiers */
1889 ot->name = "Apply Pose Library Pose";
1890 ot->idname = "POSELIB_OT_apply_pose";
1891 ot->description = "Apply specified Pose Library pose to the rig";
1892
1893 /* callbacks */
1894 ot->exec = poselib_preview_exec;
1895 ot->poll = has_poselib_pose_data_poll;
1896
1897 /* flags */
1898 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1899
1900 /* properties */
1901 /* TODO: make the pose_index into a proper enum instead of a cryptic int... */
1902 ot->prop = RNA_def_int(
1903 ot->srna,
1904 "pose_index",
1905 -1,
1906 -2,
1907 INT_MAX,
1908 "Pose",
1909 "Index of the pose to apply (-2 for no change to pose, -1 for poselib active pose)",
1910 0,
1911 INT_MAX);
1912 }
1913