== PoseLib - Pose-Library Tool for Blender ==
authorJoshua Leung <aligorith@gmail.com>
Wed, 26 Dec 2007 11:17:26 +0000 (11:17 +0000)
committerJoshua Leung <aligorith@gmail.com>
Wed, 26 Dec 2007 11:17:26 +0000 (11:17 +0000)
"A slightly late Christmas present for the Animators out there :-)"

This tool allows animators to store frequently used poses in an action, and be able to label those poses to help them retrieve them later. In a way, it acts as a glorified clipboard for poses.

One of the cool features with this is the ability to select which stored pose to use interactively in the 3d-view. Once a few poses have been stored in the PoseLib, simply use the "Ctrl L" hotkey to start previewing. Use the Mousewheel or the Page Up/Down keys to change poses, and confirm/cancel the preview in the same way as you do for transforms.

Usage Notes:
* Each Armature may get its own PoseLib. PoseLibs are simply actions with extra data, so they can get relinked.
* Manually editing actions used as PoseLibs is not a good idea, as some data may not be able to be found. Tools to automagically find poses in an action could be investigated...
* PoseLib will only apply/retrieve poses to/from selected bones
* A basic UI for this can be found in the "Links and Materials" panel. Most of the PoseLib tools are presented there.

Useful Hotkeys (also found in Pose->PoseLib menu):
* Ctrl L  - interactively preview poses
* Shift L - add a new pose or replace an existing pose in the PoseLib with the current pose
* Ctrl Shift L - rename an existing pose in the PoseLib
* Alt L - remove a pose from the poselib.c

source/blender/blenkernel/intern/action.c
source/blender/blenkernel/intern/object.c
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/writefile.c
source/blender/include/BIF_poselib.h [new file with mode: 0644]
source/blender/include/butspace.h
source/blender/makesdna/DNA_action_types.h
source/blender/src/buttons_editing.c
source/blender/src/header_view3d.c
source/blender/src/poselib.c [new file with mode: 0644]
source/blender/src/space.c

index 162ee582d744843590c70f9f62b2de55058c59d8..86e119f5e6f342f878b02380fec7a02fa36be647 100644 (file)
@@ -163,6 +163,15 @@ void make_local_action(bAction *act)
        }
 }
 
+static void free_act_poselib (bAction *act)
+{
+       if (act->poselib) {
+               bPoseLib *pl= act->poselib;
+               
+               BLI_freelistN(&pl->poses);
+               MEM_freeN(pl);
+       }
+}
 
 void free_action (bAction *act)
 {
@@ -177,6 +186,9 @@ void free_action (bAction *act)
        
        if (act->chanbase.first)
                BLI_freelistN(&act->chanbase);
+               
+       /* Free PoseLib */
+       free_act_poselib(act);
 }
 
 bAction *copy_action (bAction *src)
index d48be54fe10cc38c55ca09c16b013508eeeb12fd..b4512f3b1b3747cac5b4c42241c73282082c18e6 100644 (file)
@@ -230,6 +230,7 @@ void free_object(Object *ob)
                BLI_freelistN(&ob->defbase);
        if(ob->pose) {
                free_pose_channels(ob->pose);
+               if (ob->pose->poselib) ob->pose->poselib->id.us--;
                MEM_freeN(ob->pose);
        }
        free_effects(&ob->effect);
index d30f0da95d65e1f4d9cf10c63cf17865a7542635..f8f0395300c52d3e67552a385df59277e7734868 100644 (file)
@@ -1785,6 +1785,9 @@ static void lib_link_pose(FileData *fd, Object *ob, bPose *pose)
                }
        }
        
+       // ob->id.lib??? 
+       pose->poselib = newlibadr_us(fd, ob->id.lib, pose->poselib);
+       
        if(rebuild) {
                ob->recalc= OB_RECALC;
                pose->flag |= POSE_RECALC;
@@ -1814,12 +1817,12 @@ static void lib_link_action(FileData *fd, Main *main)
        while(act) {
                if(act->id.flag & LIB_NEEDLINK) {
                        act->id.flag -= LIB_NEEDLINK;
-
+                       
                        for (chan=act->chanbase.first; chan; chan=chan->next) {
                                chan->ipo= newlibadr_us(fd, act->id.lib, chan->ipo);
                                lib_link_constraint_channels(fd, &act->id, &chan->constraintChannels);
                        }
-
+                       
                }
                act= act->id.next;
        }
@@ -1847,7 +1850,10 @@ static void direct_link_action(FileData *fd, bAction *act)
 
        for (achan = act->chanbase.first; achan; achan=achan->next)
                link_list(fd, &achan->constraintChannels);
-
+               
+       act->poselib= newdataadr(fd, act->poselib);
+       if (act->poselib)
+               link_list(fd, &act->poselib->poses);
 }
 
 static void direct_link_armature(FileData *fd, bArmature *arm)
@@ -2942,7 +2948,6 @@ static void direct_link_pose(FileData *fd, bPose *pose) {
                pchan->iktree.first= pchan->iktree.last= NULL;
                pchan->path= NULL;
        }
-
 }
 
 static void direct_link_modifiers(FileData *fd, ListBase *lb)
@@ -7799,6 +7804,8 @@ static void expand_pose(FileData *fd, Main *mainvar, bPose *pose)
                expand_constraints(fd, mainvar, &chan->constraints);
                expand_doit(fd, mainvar, chan->custom);
        }
+       
+       expand_doit(fd, mainvar, pose->poselib);
 }
 
 static void expand_armature(FileData *fd, Main *mainvar, bArmature *arm)
index 2e14fe55383dfe239b64cbbc9b7d4bf54cf42bf1..90f488689114ef3cfa0e2593f7e980d4020c4bfc 100644 (file)
@@ -1743,11 +1743,21 @@ static void write_actions(WriteData *wd, ListBase *idbase)
                if (act->id.us>0 || wd->current) {
                        writestruct(wd, ID_AC, "bAction", 1, act);
                        if (act->id.properties) IDP_WriteProperty(act->id.properties, wd);
-
+                       
                        for (chan=act->chanbase.first; chan; chan=chan->next) {
                                writestruct(wd, DATA, "bActionChannel", 1, chan);
                                write_constraint_channels(wd, &chan->constraintChannels);
                        }
+                       
+                       if (act->poselib) {
+                               bPoseLib *pl= act->poselib;
+                               bPoseLibRef *plr;
+                               
+                               writestruct(wd, DATA, "bPoseLib", 1, pl);
+                               
+                               for (plr= pl->poses.first; plr; plr= plr->next)
+                                       writestruct(wd, DATA, "bPoseLibRef", 1, plr);
+                       }
                }
        }
 }
diff --git a/source/blender/include/BIF_poselib.h b/source/blender/include/BIF_poselib.h
new file mode 100644 (file)
index 0000000..213714c
--- /dev/null
@@ -0,0 +1,52 @@
+/**
+ * $Id$
+ *
+ * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version. The Blender
+ * Foundation also sells licenses for use in proprietary software under
+ * the Blender License.  See http://www.blender.org/BL/ for information
+ * about this.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2007 Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: this is a new part of Blender
+ *
+ * Contributor(s): Joshua Leung
+ *
+ * ***** END GPL/BL DUAL LICENSE BLOCK *****
+ */
+#ifndef BIF_POSELIB_H
+#define BIF_POSELIB_H
+
+struct Object;
+struct bPoseLib;
+struct bPoseLibRef;
+
+char *poselib_build_poses_menu(struct bPoseLib *pl, char title[]);
+void poselib_unique_pose_name(struct bPoseLib *pl, char name[]);
+int poselib_get_free_index(struct bPoseLib *pl);
+
+struct bPoseLib *poselib_init_new(struct Object *ob);
+
+void poselib_remove_pose(struct Object *ob, struct bPoseLibRef *plr);
+void poselib_rename_pose(struct Object *ob);
+void poselib_add_current_pose(struct Object *ob, int mode);
+
+void poselib_preview_poses(struct Object *ob);
+
+#endif
index ec8ffb819f640bc3675be69ccb8aedf34bd50da3..7b6213be38f954981f608a8671be898bc0fa21fe 100644 (file)
@@ -501,6 +501,11 @@ void curvemap_buttons(struct uiBlock *block, struct CurveMapping *cumap, char la
 #define B_ARM_CALCPATHS                2303
 #define B_ARM_CLEARPATHS       2304
 
+#define B_POSELIB_NEW                  2310
+#define B_POSELIB_ADDPOSE              2311
+#define B_POSELIB_REPLACEP             2312
+#define B_POSELIB_REMOVEP              2313
+
 /* *********************** */
 #define B_CAMBUTS              2500
 
index db6a2bda53c2f0ffead182699c3138f6e87d13a4..9921d784984421532ea2f4bfe76c2299d275c6a0 100644 (file)
@@ -90,12 +90,35 @@ typedef struct bPoseChannel {
  */
 typedef struct bPose {
        ListBase chanbase;                      /* list of pose channels */
+       struct bAction *poselib;        /* poselib-action for this pose */
+       
        short flag, proxy_layer;        /* proxy layer: copy from armature, gets synced */
+       
        float ctime;                            /* local action time of this pose */
        float stride_offset[3];         /* applied to object */
        float cyclic_offset[3];         /* result of match and cycles, applied in where_is_pose() */
 } bPose;
 
+
+/* "Pose" reference for PoseLib */
+typedef struct bPoseLibRef {
+       struct bPoseLibRef *next, *prev;
+       int frame;                                      /* frame in the action to look for this pose */
+       char name[32];                          /* name of the pose */
+       int pad;
+} bPoseLibRef;
+
+/* PoseLib data for Action 
+ *     PoseLib data is stored in actions so that poselibs can easily be assigned/removed from
+ *     a pose. Currently the only data that is stored for a poselib is a set of references to which
+ *     frame a named pose occurs in the action.
+ */
+typedef struct bPoseLib {
+       ListBase        poses;                  /* List of poses for an action (arranged in chronological order) */
+       int     flag;                           /* Settings (not used yet) */
+       int     active_nr;                      /* Index of the poselib's active pose (for UI) */
+} bPoseLib;
+
 /* Action Channels belong to Actions. They are linked with an IPO block, and can also own 
  * Constraint Channels in certain situations. 
  */
@@ -115,6 +138,7 @@ typedef struct bActionChannel {
 typedef struct bAction {
        ID                              id;
        ListBase                chanbase;       /* Action Channels in this Action */
+       bPoseLib                *poselib;       /* PoseLib data of this Action (only if the action is used as poselib) */
 } bAction;
 
 /* Action Editor Space. This is defined here instead of in DNA_space_types.h */
index d74f045255deb197de083b1d5374aa2348577b03..0cc3a70d2d198d75696fe9b201ecd63e6c3445ef 100644 (file)
 #include "BIF_interface.h"
 #include "BIF_meshtools.h"
 #include "BIF_mywindow.h"
+#include "BIF_poselib.h"
 #include "BIF_poseobject.h"
 #include "BIF_renderwin.h"
 #include "BIF_resources.h"
@@ -3729,6 +3730,32 @@ void do_armbuts(unsigned short event)
                if (ob && ob->pose)
                        pose_clear_paths(ob);
                break;
+               
+       case B_POSELIB_NEW:
+               if (ob && ob->pose)
+                       poselib_init_new(ob);
+               allqueue(REDRAWBUTSEDIT, 0);
+               break;
+       case B_POSELIB_ADDPOSE:
+               if (ob && ob->pose)
+                       poselib_add_current_pose(ob, 1);
+               allqueue(REDRAWBUTSEDIT, 0);
+               break;
+       case B_POSELIB_REPLACEP:
+               if (ob && ob->pose)
+                       poselib_add_current_pose(ob, 2);
+               allqueue(REDRAWBUTSEDIT, 0);
+               break;
+       case B_POSELIB_REMOVEP:
+               if (ob && ob->pose) {
+                       bAction *act= ob->pose->poselib;
+                       bPoseLib *pl= act->poselib;
+                       bPoseLibRef *plr= BLI_findlink(&pl->poses, pl->active_nr-1);
+                       
+                       poselib_remove_pose(ob, plr);
+               }
+               allqueue(REDRAWBUTSEDIT, 0);
+               break;
        }
 }
 
@@ -4937,6 +4964,59 @@ static void editing_panel_links(Object *ob)
                return;
        }
        
+       /* poselib for armatures */
+       if (ob->type==OB_ARMATURE) {
+               if ((ob->pose) && (ob->flag & OB_POSEMODE) && (G.obedit != ob)) {
+                       bPose *pose= ob->pose;
+                       bAction *act= pose->poselib;
+                       
+                       xco= 143;
+                       
+                       uiDefBut(block, LABEL,0,"PoseLib:", xco, 154, 130,20, 0, 0, 0, 0, 0, "");
+                       
+                       /* PoseLib Action */
+                       if (act) {
+                               if (act->poselib==NULL) {
+                                       uiBlockSetCol(block, TH_REDALERT);
+                                       uiDefIDPoinBut(block, test_actionpoin_but, ID_AC, REDRAWBUTSEDIT, "AC:",        xco, 130, 140, 20, &pose->poselib, "Action to use as PoseLib - (Warning: this Action doesn't have a PoseLib)"); 
+                                       uiBlockSetCol(block, TH_AUTO);  
+                               }
+                               else {
+                                       uiDefIDPoinBut(block, test_actionpoin_but, ID_AC, REDRAWBUTSEDIT, "AC:",        xco, 130, 140, 20, &pose->poselib, "Action to use as PoseLib"); 
+                               }
+                       }
+                       uiDefBut(block, BUT, B_POSELIB_NEW, "New PoseLib",      xco,110,140,20, 0, 0, 0, 0, 0, "Creates a new PoseLib");
+                       
+                       
+                       /* poselib pose editing controls */
+                       if ((act) && (act->poselib)) {
+                               bPoseLib *pl= act->poselib;
+                               bPoseLibRef *plr= BLI_findlink(&pl->poses, pl->active_nr-1);
+                               int plr_count= BLI_countlist(&pl->poses);
+                               char *menustr= poselib_build_poses_menu(pl, "PoseLib Poses");
+                               
+                               uiBlockBeginAlign(block);
+                                       /* currently 'active' pose */
+                                       uiDefButI(block, MENU, REDRAWBUTSEDIT, menustr, xco, 85,18,20, &pl->active_nr, 1, plr_count, 0, 0, "Browses Poses in PoseLib");
+                                       MEM_freeN(menustr);
+                                       
+                                       if (pl->active_nr) {
+                                               but= uiDefBut(block, TEX, REDRAWBUTSEDIT,"",            161,85,140-18-20,20, plr->name, 0, 31, 0, 0, "Displays current PoseLib Pose name. Click to change.");
+                                               //uiButSetFunc(but, verify_vertexgroup_name_func, defGroup, NULL);
+                                               //uiButSetCompleteFunc(but, autocomplete_vgroup, (void *)ob);
+                                               
+                                               but = uiDefIconBut(block, BUT, B_POSELIB_REMOVEP, VICON_X, 263, 85, 20, 20, NULL, 0.0, 0.0, 0.0, 0.0, "Remove this PoseLib Pose from PoseLib");
+                                       }
+                                       
+                                       /* add new poses */
+                                       uiDefBut(block, BUT, B_POSELIB_ADDPOSE, "Add Pose",     xco,65,70,20, 0, 0, 0, 0, 0, "Add current pose to PoseLib");
+                                       uiDefBut(block, BUT, B_POSELIB_REPLACEP, "Replace Pose",        xco+70,65,70,20, 0, 0, 0, 0, 0, "Replace existing PoseLib Pose with current pose");
+                               uiBlockEndAlign(block); 
+                       }
+               }       
+               return;
+       }
+       
        /* vertex group... partially editmode... */
        if(ob->type==OB_MESH || ob->type==OB_LATTICE) {
                bDeformGroup *defGroup;
index 95f24c340987f6ee5330d5762f23d96d09b61346..55740861998136fb790c1c08d61a1899494b6fef 100644 (file)
 #include "BIF_interface.h"
 #include "BIF_mainqueue.h"
 #include "BIF_meshtools.h"
+#include "BIF_poselib.h"
 #include "BIF_poseobject.h"
 #include "BIF_renderwin.h"
 #include "BIF_resources.h"
@@ -4048,6 +4049,49 @@ static uiBlock *view3d_pose_armature_motionpathsmenu(void *arg_unused)
        return block;
 }
 
+static void do_view3d_pose_armature_poselibmenu(void *arg, int event)
+{
+       Object *ob= OBACT;
+       
+       switch(event) {
+               case 1:
+                       poselib_preview_poses(ob);
+                       break;
+               case 2:
+                       poselib_add_current_pose(ob, 0);
+                       break;
+               case 3:
+                       poselib_rename_pose(ob);
+                       break;
+               case 4:
+                       poselib_remove_pose(ob, NULL);
+                       break;
+       }
+       
+       allqueue(REDRAWVIEW3D, 0);
+}
+
+static uiBlock *view3d_pose_armature_poselibmenu(void *arg_unused)
+{
+       uiBlock *block;
+       short yco = 20, menuwidth = 120;
+
+       block= uiNewBlock(&curarea->uiblocks, "view3d_pose_armature_poselibmenu", UI_EMBOSSP, UI_HELV, G.curscreen->mainwin);
+       uiBlockSetButmFunc(block, do_view3d_pose_armature_poselibmenu, NULL);
+       
+       uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Apply Pose|Ctrl L",                      0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 1, "");
+       
+       uiDefBut(block, SEPR, 0, "",        0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
+       
+       uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Add/Replace Pose|Shift L",       0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 2, "");
+       uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Rename Pose|Ctrl Shift L",       0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 3, "");
+       uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Remove Pose|Alt L",                      0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 4, "");
+       
+       uiBlockSetDirection(block, UI_RIGHT);
+       uiTextBoundsBlock(block, 60);
+       return block;
+}
+
 static void do_view3d_pose_armaturemenu(void *arg, int event)
 {
        Object *ob;
@@ -4126,6 +4170,7 @@ static uiBlock *view3d_pose_armaturemenu(void *arg_unused)
        
        uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
        
+       uiDefIconTextBlockBut(block, view3d_pose_armature_poselibmenu, NULL, ICON_RIGHTARROW_THIN, "PoseLib", 0, yco-=20, 120, 19, "");
        uiDefIconTextBlockBut(block, view3d_pose_armature_motionpathsmenu, NULL, ICON_RIGHTARROW_THIN, "Motion Paths", 0, yco-=20, 120, 19, "");
        uiDefIconTextBlockBut(block, view3d_pose_armature_ikmenu, NULL, ICON_RIGHTARROW_THIN, "Inverse Kinematics", 0, yco-=20, 120, 19, "");
        uiDefIconTextBlockBut(block, view3d_pose_armature_constraintsmenu, NULL, ICON_RIGHTARROW_THIN, "Constraints", 0, yco-=20, 120, 19, "");
diff --git a/source/blender/src/poselib.c b/source/blender/src/poselib.c
new file mode 100644 (file)
index 0000000..6e088bb
--- /dev/null
@@ -0,0 +1,770 @@
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_arithb.h"
+#include "BLI_blenlib.h"
+#include "BLI_dynstr.h"
+
+#include "DNA_listBase.h"
+#include "DNA_action_types.h"
+#include "DNA_armature_types.h"
+#include "DNA_curve_types.h"
+#include "DNA_ipo_types.h"
+#include "DNA_object_types.h"
+#include "DNA_object_force.h"
+#include "DNA_scene_types.h"
+
+#include "BKE_action.h"
+#include "BKE_armature.h"
+#include "BKE_depsgraph.h"
+#include "BKE_ipo.h"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+
+#include "BKE_global.h"
+#include "BKE_utildefines.h"
+
+//#include "BIF_keyframing.h"
+#include "BSE_editipo.h"
+
+#include "BIF_poselib.h"
+#include "BIF_interface.h"
+#include "BIF_editaction.h"
+#include "BIF_space.h"
+#include "BIF_screen.h"
+#include "BIF_toolbox.h"
+
+#include "blendef.h"
+
+#include "PIL_time.h"                  /* sleep                                */
+#include "mydevice.h"
+
+/* ************************************************************* */
+/* == POSE-LIBRARY TOOL FOR BLENDER == 
+ *     
+ * Overview: 
+ *     This tool allows animators to store a set of frequently used poses to dump into
+ *     the active action to help in "budget" productions to quickly block out new actions.
+ *     It acts as a kind of "glorified clipboard for poses", allowing for naming of poses.
+ *
+ * Features:
+ *     - PoseLibs are simply normal Actions, but with a poselib
+ *     - Each "pose" is simply a set of keyframes that occur on a particular frame
+ *             -> a bPoseLibRef struct is used to identify and label poses in the Action
+ *             -> all bPoseLibRefs are stored in the order they were added
+ *             -> keys for poses should occur on each positively numbered frame (starting from frame 1)
+ *     - The Scrollwheel or PageUp/Down buttons when used in a special mode or after pressing/holding
+ *       [a modifier] key, cycles through the poses available for the active pose's poselib, allowing the
+ *       animator to preview what action best suits that pose
+ */
+/* ************************************************************* */
+
+/* gets list of poses in poselib as a string */
+char *poselib_build_poses_menu (bPoseLib *pl, char title[])
+{
+       DynStr *pupds= BLI_dynstr_new();
+       bPoseLibRef *plr;
+       char *str;
+       char buf[64];
+       int i;
+       
+       /* add title first */
+       sprintf(buf, "%s%%t|", title);
+       BLI_dynstr_append(pupds, buf);
+       
+       /* loop through keyingsets, adding them */
+       for (plr=pl->poses.first, i=1; plr; plr=plr->next, i++) {
+               BLI_dynstr_append(pupds, plr->name);
+               
+               sprintf(buf, "%%x%d", i);
+               BLI_dynstr_append(pupds, buf);
+               
+               if (plr->next)
+                       BLI_dynstr_append(pupds, "|");
+       }
+       
+       /* convert to normal MEM_malloc'd string */
+       str= BLI_dynstr_get_cstring(pupds);
+       BLI_dynstr_free(pupds);
+       
+       return str;
+}
+
+/* finds an unique name for pose to be added to poselib */
+void poselib_unique_pose_name (bPoseLib *pl, char name[])
+{
+       bPoseLibRef *plr;
+       char tempname[32];
+       int     number = 1, exists = 0;
+       char *dot;
+       
+       /* See if we are given an empty string */
+       if (name[0] == '\0') {
+               /* give it default name first */
+               strcpy(name, "Pose");
+       }
+       
+       /* See if we even need to do this */    
+       for (plr= pl->poses.first; plr; plr= plr->next) {
+               if (!strcmp(name, plr->name)) {
+                       exists = 1;
+                       break;
+               }
+       }
+       
+       if (exists == 0)
+               return;
+
+       /*      Strip off the suffix */
+       dot = strchr(name, '.');
+       if (dot)
+               *dot=0;
+       
+       for (number = 1; number <= 999; number++) {
+               sprintf(tempname, "%s.%03d", name, number);
+               
+               exists = 0;
+               for (plr= pl->poses.first; plr; plr= plr->next) {
+                       if (strcmp(name, tempname)==0) {
+                               exists = 1;
+                               break;
+                       }
+               }
+               if (exists == 0) {
+                       BLI_strncpy(name, tempname, 32);
+                       return;
+               }
+       }
+}
+
+/* gets the first available frame in poselib to store a pose on 
+ *     - frames start from 1, and a pose should occur on every frame... 0 is error!
+ */
+int poselib_get_free_index (bPoseLib *pl)
+{
+       bPoseLibRef *plr;
+       int low=0, high=0;
+       
+       /* sanity checks */
+       if (ELEM(NULL, pl, pl->poses.first)) return 1;
+       
+       /* loop over poses finding various values (poses are not stored in chronological order) */
+       for (plr= pl->poses.first; plr; plr= plr->next) {
+               /* only increase low if value is 1 greater than low, to find "gaps" where
+                * poses were removed from the poselib
+                */
+               if (plr->frame == (low + 1)) 
+                       low++;
+               
+               /* value replaces high if it is the highest value encountered yet */
+               if (plr->frame > high) 
+                       high= plr->frame;
+       }
+       
+       /* - if low is not equal to high, then low+1 is a gap 
+        * - if low is equal to high, then high+1 is the next index (add at end) 
+        */
+       if (low < high) 
+               return (low + 1);
+       else 
+               return (high + 1);
+}
+
+/* ************************************************************* */
+
+/* Initialise a new poselib */
+bPoseLib *poselib_init_new (Object *ob)
+{
+       bPose *pose= (ob) ? ob->pose : NULL;
+       bAction *act;
+       bPoseLib *pl;
+       
+       if (ELEM(NULL, ob, pose))
+               return NULL;
+       
+       /* init pose's poselib action */
+       if (pose->poselib == NULL)
+               pose->poselib= add_empty_action("PoseLib");
+       act= pose->poselib;
+       
+       /* init actions's poselib data */
+       if (act->poselib == NULL)
+               act->poselib= MEM_callocN(sizeof(bPoseLib), "bPoseLib");
+       pl= act->poselib;
+       
+       return pl;
+}
+
+
+/* This function adds an ipo-curve of the right type where it's needed */
+static IpoCurve *poselib_verify_icu (Ipo *ipo, int adrcode)
+{
+       IpoCurve *icu;
+       
+       for (icu= ipo->curve.first; icu; icu= icu->next) {
+               if (icu->adrcode==adrcode) break;
+       }
+       if (icu==NULL) {
+               icu= MEM_callocN(sizeof(IpoCurve), "ipocurve");
+               
+               icu->flag |= IPO_VISIBLE|IPO_AUTO_HORIZ;
+               if (ipo->curve.first==NULL) icu->flag |= IPO_ACTIVE;    /* first one added active */
+               
+               icu->blocktype= ID_PO;
+               icu->adrcode= adrcode;
+               
+               set_icu_vars(icu);
+               
+               BLI_addtail(&ipo->curve, icu);
+       }
+       
+       return icu;
+}
+
+/* This tool adds the current pose to the poselib 
+ *     Note: Standard insertkey cannot be used for this due to its limitations
+ */
+void poselib_add_current_pose (Object *ob, int val)
+{
+       bArmature *arm= (ob) ? ob->data : NULL;
+       bPose *pose= (ob) ? ob->pose : NULL;
+       bPoseChannel *pchan;
+       bPoseLib *pl;
+       bPoseLibRef *plr;
+       bAction *act;
+       bActionChannel *achan;
+       IpoCurve *icu;
+       int frame;
+       char name[32];
+       
+       /* sanity check */
+       if (ELEM3(NULL, ob, arm, pose)) 
+               return;
+       
+       /* mode - add new or replace existing */
+       if (val == 0) {
+               if (pose->poselib && pose->poselib->poselib->poses.first) {
+                       val= pupmenu("PoseLib Add Current Pose%t|Add New%x1|Replace Existing%x2");
+                       if (val <= 0) return;
+               }
+               else 
+                       val= 1;
+       }
+       
+       if ((pose->poselib) && (val == 2)) {
+               char *menustr;
+               
+               /* get poselib */
+               act= pose->poselib;
+               pl= act->poselib;
+               
+               /* get the pose to replace */
+               menustr= poselib_build_poses_menu(pl, "Replace PoseLib Pose");
+               val= pupmenu(menustr);
+               if (menustr) MEM_freeN(menustr);
+               
+               if (val <= 0) return;
+               plr= BLI_findlink(&pl->poses, val-1);
+               if (plr == NULL) return;
+               
+               /* get the frame from the poselib */
+               frame= plr->frame;
+       }
+       else {
+               /* get name of pose */
+               sprintf(name, "Pose");
+               if (sbutton(name, 0, sizeof(name)-1, "Name: ") == 0)
+                       return;
+                       
+               /* get/initialise poselib */
+               pl= poselib_init_new(ob);
+               act= pose->poselib;     
+               
+               /* validate name and get frame */
+               poselib_unique_pose_name(pl, name);
+               frame= poselib_get_free_index(pl);
+               
+               /* add pose to poselib */
+               plr= MEM_callocN(sizeof(bPoseLibRef), "bPoseLibRef");
+               BLI_strncpy(plr->name, name, sizeof(plr->name));
+               plr->frame= frame;
+               BLI_addtail(&pl->poses, plr);
+       }       
+       
+       /* loop through selected posechannels, keying their pose to the action */
+       for (pchan= pose->chanbase.first; pchan; pchan= pchan->next) {
+               /* check if available */
+               if ((pchan->bone) && (arm->layer & pchan->bone->layer)) {
+                       if (pchan->bone->flag & (BONE_SELECTED|BONE_ACTIVE)) {
+                               /* make action-channel if needed */
+                               achan= verify_action_channel(act, pchan->name);
+                               
+                               /* make ipo if needed... */
+                               if (achan->ipo == NULL)
+                                       achan->ipo= add_ipo(achan->name, ID_PO);
+                                       
+                               /* add missing ipo-curves and insert keys */
+                               #define INSERT_KEY_ICU(adrcode, data) {\
+                                               icu= poselib_verify_icu(achan->ipo, adrcode); \
+                                               insert_vert_icu(icu, frame, data, 1); \
+                                       }
+                                       
+                               INSERT_KEY_ICU(AC_LOC_X, pchan->loc[0])
+                               INSERT_KEY_ICU(AC_LOC_Y, pchan->loc[1])
+                               INSERT_KEY_ICU(AC_LOC_Z, pchan->loc[2])
+                               INSERT_KEY_ICU(AC_SIZE_X, pchan->size[0])
+                               INSERT_KEY_ICU(AC_SIZE_Y, pchan->size[1])
+                               INSERT_KEY_ICU(AC_SIZE_Z, pchan->size[2])
+                               INSERT_KEY_ICU(AC_QUAT_W, pchan->quat[0])
+                               INSERT_KEY_ICU(AC_QUAT_X, pchan->quat[1])
+                               INSERT_KEY_ICU(AC_QUAT_Y, pchan->quat[2])
+                               INSERT_KEY_ICU(AC_QUAT_Z, pchan->quat[3])
+                       }
+               }
+       }
+       
+       /* store new 'active' pose number */
+       pl->active_nr= BLI_countlist(&pl->poses);
+       
+       BIF_undo_push("PoseLib Add Pose");
+       allqueue(REDRAWBUTSEDIT, 0);
+}
+
+
+/* This tool removes the pose that the user selected from the poselib (or the provided pose) */
+void poselib_remove_pose (Object *ob, bPoseLibRef *plr)
+{
+       bPose *pose= (ob) ? ob->pose : NULL;
+       bAction *act= (pose) ? pose->poselib : NULL;
+       bActionChannel *achan;
+       bPoseLib *pl= (act) ? act->poselib : NULL;
+       char *menustr;
+       int val;
+       
+       /* check if valid poselib */
+       if (ELEM(NULL, ob, pose)) {
+               error("PoseLib is only for Armatures in PoseMode");
+               return;
+       }
+       if (ELEM(NULL, act, pl)) {
+               error("Pose doesn't have a valid PoseLib");
+               return;
+       }
+       
+       /* get index (and pointer) of pose to remove */
+       if (plr == NULL) {
+               menustr= poselib_build_poses_menu(pl, "Remove PoseLib Pose");
+               val= pupmenu(menustr);
+               if (menustr) MEM_freeN(menustr);
+               
+               if (val <= 0) return;
+               plr= BLI_findlink(&pl->poses, val-1);
+               if (plr == NULL) return;
+       }
+       else {
+               // TODO: we should really check if pose occurs in this poselib 
+       }
+       
+       /* remove relevant keyframes */
+       for (achan= act->chanbase.first; achan; achan= achan->next) {
+               Ipo *ipo= achan->ipo;
+               IpoCurve *icu;
+               BezTriple *bezt;
+               int i;
+               
+               for (icu= ipo->curve.first; icu; icu= icu->next) {
+                       for (i=0, bezt=icu->bezt; i < icu->totvert; i++, bezt++) {
+                               /* check if remove... */
+                               if (IS_EQ(bezt->vec[1][0], plr->frame)) {
+                                       delete_icu_key(icu, i);
+                                       break;
+                               }
+                       }       
+               }
+       }
+       
+       /* remove poselib from list */
+       BLI_freelinkN(&pl->poses, plr);
+       
+       /* fix active pose number */
+       pl->active_nr= 0;
+       
+       /* undo + redraw */
+       BIF_undo_push("PoseLib Remove Pose");
+       allqueue(REDRAWBUTSEDIT, 0);
+}
+
+
+/* This tool renames the pose that the user selected from the poselib */
+void poselib_rename_pose (Object *ob)
+{
+       bPose *pose= (ob) ? ob->pose : NULL;
+       bAction *act= (pose) ? pose->poselib : NULL;
+       bPoseLib *pl= (act) ? act->poselib : NULL;
+       bPoseLibRef *plr;
+       char *menustr, name[32];
+       int val;
+       
+       /* check if valid poselib */
+       if (ELEM(NULL, ob, pose)) {
+               error("PoseLib is only for Armatures in PoseMode");
+               return;
+       }
+       if (ELEM(NULL, act, pl)) {
+               error("Pose doesn't have a valid PoseLib");
+               return;
+       }
+       
+       /* get index of pose to remove */
+       menustr= poselib_build_poses_menu(pl, "Rename PoseLib Pose");
+       val= pupmenu(menustr);
+       if (menustr) MEM_freeN(menustr);
+       
+       if (val <= 0) return;
+       plr= BLI_findlink(&pl->poses, val-1);
+       if (plr == NULL) return;
+       
+       /* get name of pose */
+       sprintf(name, plr->name);
+       if (sbutton(name, 0, sizeof(name)-1, "Name: ") == 0)
+               return;
+               
+       /* validate name */
+       poselib_unique_pose_name(pl, name); // hmm what happens with the old pose's name...
+       
+       /* copy name */
+       BLI_strncpy(plr->name, name, sizeof(plr->name));
+       
+       /* undo and update */
+       BIF_undo_push("PoseLib Rename Pose");
+       allqueue(REDRAWBUTSEDIT, 0);
+}
+
+
+/* ************************************************************* */
+
+/* simple struct for storing backup info */
+typedef struct tPoseLib_Backup {
+       struct tPoseLib_Backup *next, *prev;
+       
+       float oldloc[3];
+       float oldsize[3];
+       float oldquat[4];
+       
+       float *loc, *size, *quat;
+} tPoseLib_Backup;
+
+/* Makes a copy of the current pose for restoration purposes - doesn't do constraints currently */
+static void poselib_backup_posecopy (ListBase *backups, bPose *pose)
+{
+       bAction *poselib= pose->poselib;
+       bActionChannel *achan;
+       bPoseChannel *pchan;
+       
+       /* for each posechannel that has an actionchannel in */
+       for (achan= poselib->chanbase.first; achan; achan= achan->next) {
+               /* try to find posechannel */
+               pchan= get_pose_channel(pose, achan->name);
+               
+               /* backup data if available */
+               if (pchan) {
+                       tPoseLib_Backup *plb;
+                       
+                       plb= MEM_callocN(sizeof(tPoseLib_Backup), "tPoseLib_Backup");
+                       
+                       VECCOPY(plb->oldloc, pchan->loc);
+                       VECCOPY(plb->oldsize, pchan->size);
+                       QUATCOPY(plb->oldquat, pchan->quat);
+                       
+                       plb->loc= pchan->loc;
+                       plb->size= pchan->size;
+                       plb->quat= pchan->quat;
+                       
+                       BLI_addtail(backups, plb);
+               }
+       }
+}
+
+/* Restores original pose - doesn't do constraints currently */
+static void poselib_backup_restore (ListBase *backups)
+{
+       tPoseLib_Backup *plb;
+       
+       for (plb= backups->first; plb; plb= plb->next) {
+               VECCOPY(plb->loc, plb->oldloc);
+               VECCOPY(plb->size, plb->oldsize);
+               VECCOPY(plb->quat, plb->oldquat);
+       }
+}
+
+/* ---------------------------- */
+
+/* Applies the appropriate stored pose from the pose-library to the current pose
+ *     - assumes that a valid object, with a poselib has been supplied
+ *     - gets the string to print in the header
+ *     - this code is based on the code for extract_pose_from_action in blenkernel/action.c
+ */
+static void poselib_apply_pose (Object *ob, bPoseLibRef *plr, char headerstr[])
+{
+       bPose *pose= ob->pose;
+       bPoseChannel *pchan;
+       bAction *act= pose->poselib;
+       bActionChannel *achan;
+       IpoCurve *icu;
+       int frame= plr->frame;
+       
+       /* start applying - only those channels which have a key at this point in time! */
+       for (achan= act->chanbase.first; achan; achan= achan->next) {
+               short found= 0;
+               
+               /* apply this achan? */
+               if (achan->ipo) {
+                       /* find a keyframe at this frame */
+                       for (icu= achan->ipo->curve.first; icu; icu= icu->next) {
+                               BezTriple *bezt;
+                               int i;
+                               
+                               for (i=0, bezt=icu->bezt; i < icu->totvert; i++, bezt++) {
+                                       if (IN_RANGE(bezt->vec[1][0], (frame-0.5f), (frame+0.5f))) {
+                                               found= 1;
+                                               break;
+                                       }
+                               }
+                               
+                               if (found) break;
+                       }
+                       
+                       /* apply pose */
+                       if (found) {
+                               pchan= get_pose_channel(pose, achan->name);
+                               
+                               if (pchan) {
+                                       /* Evaluates and sets the internal ipo values   */
+                                       calc_ipo(achan->ipo, frame);
+                                       /* This call also sets the pchan flags */
+                                       execute_action_ipo(achan, pchan);
+                               }
+                       }
+               }
+               
+               /* tag achan as having been used or not... */
+               if (found)
+                       achan->flag |= ACHAN_SELECTED;
+               else
+                       achan->flag &= ~ACHAN_SELECTED;
+       }
+}
+
+/* Auto-keys/tags bones affected by the pose used from the poselib */
+static void poselib_keytag_pose (Object *ob)
+{
+       bPose *pose= ob->pose;
+       bPoseChannel *pchan;
+       bAction *act= pose->poselib;
+       bActionChannel *achan;
+       
+       /* start tagging/keying */
+       for (achan= act->chanbase.first; achan; achan= achan->next) {
+               /* only for selected action channels */
+               if (achan->flag & ACHAN_SELECTED) {
+                       pchan= get_pose_channel(ob->pose, achan->name);
+                       
+                       if (pchan) {
+                               if (G.flags & G_RECORDKEYS) {
+                                       ID *id= &ob->id;
+                                       
+                                       /* Set keys on pose */
+                                       if (pchan->flag & POSE_ROT) {
+                                               insertkey(id, ID_PO, pchan->name, NULL, AC_QUAT_X, 0);
+                                               insertkey(id, ID_PO, pchan->name, NULL, AC_QUAT_Y, 0);
+                                               insertkey(id, ID_PO, pchan->name, NULL, AC_QUAT_Z, 0);
+                                               insertkey(id, ID_PO, pchan->name, NULL, AC_QUAT_W, 0);
+                                       }
+                                       if (pchan->flag & POSE_SIZE) {
+                                               insertkey(id, ID_PO, pchan->name, NULL, AC_SIZE_X, 0);
+                                               insertkey(id, ID_PO, pchan->name, NULL, AC_SIZE_Y, 0);
+                                               insertkey(id, ID_PO, pchan->name, NULL, AC_SIZE_Z, 0);
+                                       }
+                                       if (pchan->flag & POSE_LOC) {
+                                               insertkey(id, ID_PO, pchan->name, NULL, AC_LOC_X, 0);
+                                               insertkey(id, ID_PO, pchan->name, NULL, AC_LOC_Y, 0);
+                                               insertkey(id, ID_PO, pchan->name, NULL, AC_LOC_Z, 0);
+                                       }
+                                       
+                                       /* clear any unkeyed tags */
+                                       if (pchan->bone)
+                                               pchan->bone->flag &= ~BONE_UNKEYED;
+                               }
+                               else {
+                                       /* add unkeyed tags */
+                                       if (pchan->bone)
+                                               pchan->bone->flag |= BONE_UNKEYED;
+                               }
+                       }
+               }
+       }
+}
+
+/* ---------------------------- */
+
+/* defines for psoelib_preview_poses --> ret_val values */
+enum {
+       PL_PREVIEW_RUNNING = 0,
+       PL_PREVIEW_CONFIRM,
+       PL_PREVIEW_CANCEL
+};
+
+/* This tool allows users to preview the pose from the pose-lib using the mouse-scrollwheel/pageupdown */
+void poselib_preview_poses (Object *ob)
+{
+       ListBase backups = {NULL, NULL};
+       
+       bPose *pose= (ob) ? (ob->pose) : NULL;
+       bArmature *arm= (ob) ? (ob->data) : NULL;
+       bAction *act= (pose) ? (pose->poselib) : NULL; 
+       bPoseLib *pl= (act) ? (act->poselib) : NULL;
+       bPoseLibRef *plr= (pl->active_nr) ? BLI_findlink(&pl->poses, pl->active_nr-1) : pl->poses.first;
+       
+       short ret_val=PL_PREVIEW_RUNNING, val=0, redraw=1, firsttime=1;
+       unsigned short event;
+       char headerstr[200];
+       
+       /* check if valid poselib */
+       if (ELEM(NULL, ob, pose)) {
+               error("PoseLib is only for Armatures in PoseMode");
+               return;
+       }
+       if (ELEM(NULL, act, pl)) {
+               error("Pose doesn't have a valid PoseLib");
+               return;
+       }
+       if (plr == NULL) {
+               error("PoseLib has no poses to preview");
+               return;
+       }
+       
+       /* make backup of current pose for restoring pose */
+       poselib_backup_posecopy(&backups, pose);
+       
+       /* set depsgraph flags */
+               /* make sure the lock is set OK, unlock can be accidentally saved? */
+       pose->flag |= POSE_LOCKED;
+       pose->flag &= ~POSE_DO_UNLOCK;
+
+       
+       /* start preview loop */
+       while (ret_val == PL_PREVIEW_RUNNING) {
+               /* preview a pose */
+               if (redraw) {
+                       /* don't clear pose if firsttime */
+                       if (firsttime == 0)
+                               poselib_backup_restore(&backups);
+                       else
+                               firsttime = 0;
+                               
+                       /* pose should be the right one to draw */
+                       poselib_apply_pose(ob, plr, headerstr);
+                       
+                       /* old optimize trick... this enforces to bypass the depgraph 
+                        *      - note: code copied from transform_generics.c -> recalcData()
+                        */
+                       if ((arm->flag & ARM_DELAYDEFORM)==0) {
+                               Base *base;
+                               
+                               DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA);  /* sets recalc flags */
+                               
+                               /* bah, softbody exception... recalcdata doesnt reset */
+                               for (base= FIRSTBASE; base; base= base->next) {
+                                       if (base->object->recalc & OB_RECALC_DATA)
+                                               if (modifiers_isSoftbodyEnabled(base->object)) {
+                                                       base->object->softflag |= OB_SB_REDO;
+                                       }
+                               }
+                       }
+                       else
+                               where_is_pose(ob);
+                       
+                       /* do header print */
+                       sprintf(headerstr, "PoseLib Previewing Pose: \"%s\"  | Use ScrollWheel or PageUp/Down to change", plr->name);
+                       headerprint(headerstr);
+                       
+                       /* redraw... */
+                       force_draw(0);
+                       redraw= 0;
+               }
+               
+               /* essential for idling subloop */
+               if( qtest()==0) PIL_sleep_ms(2);
+               
+               /* emptying queue and reading events */
+               while ( qtest() ) {
+                       event= extern_qread(&val);
+                       
+                       /* event processing */
+                       if (val) {
+                               /* exit */
+                               if (ELEM(event, ESCKEY, RIGHTMOUSE))
+                                       ret_val= PL_PREVIEW_CANCEL;
+                               else if (ELEM3(event, LEFTMOUSE, RETKEY, SPACEKEY))
+                                       ret_val= PL_PREVIEW_CONFIRM;
+                               
+                               /* change pose */
+                               else if (ELEM(event, PAGEUPKEY, WHEELUPMOUSE)) {
+                                       /* find previous pose - go back to end of list if no previous (cyclic) */
+                                       plr= (plr->prev) ? plr->prev : pl->poses.last;
+                                       redraw= 1;
+                               }
+                               else if (ELEM(event, PAGEDOWNKEY, WHEELDOWNMOUSE)) {
+                                       /* find next pose - go back to start of list if no next (cyclic) */
+                                       plr= (plr->next) ? plr->next : pl->poses.first;
+                                       redraw= 1;
+                               }
+                       }
+               }
+       }
+       
+       /* clear pose if cancelled */
+       if (ret_val == PL_PREVIEW_CANCEL) {
+               poselib_backup_restore(&backups);
+               where_is_pose(ob);
+       }
+       BLI_freelistN(&backups);
+       
+       /* auto-keying if not cancelled */
+       if (ret_val == PL_PREVIEW_CONFIRM)
+               poselib_keytag_pose(ob);
+       
+       /* this signal does one recalc on pose, then unlocks, so ESC or edit will work */
+       pose->flag |= POSE_DO_UNLOCK;
+       
+       /* Update event for pose and deformation children */
+       DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA);
+       
+       /* updates */
+       if (G.flags & G_RECORDKEYS) {
+               remake_action_ipos(ob->action);
+               
+               allqueue(REDRAWIPO, 0);
+               allqueue(REDRAWVIEW3D, 0);
+               allqueue(REDRAWACTION, 0);              
+               allqueue(REDRAWNLA, 0);
+       }
+       else {
+               /* need to trick depgraph, action is not allowed to execute on pose */
+               where_is_pose(ob);
+               ob->recalc= 0;
+               
+               allqueue(REDRAWVIEW3D, 0);
+       }
+
+       BIF_undo_push("PoseLib Apply Pose");
+}
index 72107ec7003736462c50d64bdbed9aeed6be8739..5415f03701e8c669154bacdc05f739f1e8586df1 100644 (file)
 #include "BIF_meshtools.h"
 #include "BIF_mywindow.h"
 #include "BIF_oops.h"
+#include "BIF_poselib.h"
 #include "BIF_poseobject.h"
 #include "BIF_outliner.h"
 #include "BIF_resources.h"
@@ -2132,7 +2133,16 @@ static void winqreadview3dspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                                                selectconnected_nurb();
                                }
                                else if(ob && (ob->flag & OB_POSEMODE)) {
-                                       selectconnected_posearmature();
+                                       if (G.qual == LR_CTRLKEY) 
+                                               poselib_preview_poses(ob);
+                                       else if (G.qual == LR_SHIFTKEY) 
+                                               poselib_add_current_pose(ob, 0);
+                                       else if (G.qual == (LR_CTRLKEY|LR_SHIFTKEY))
+                                               poselib_rename_pose(ob);
+                                       else if (G.qual == LR_ALTKEY)
+                                               poselib_remove_pose(ob, NULL);
+                                       else
+                                               selectconnected_posearmature();
                                }
                                else {
                                        if(FACESEL_PAINT_TEST) {