Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoshua Leung <aligorith@gmail.com>2007-12-26 14:17:26 +0300
committerJoshua Leung <aligorith@gmail.com>2007-12-26 14:17:26 +0300
commit10b8237eab7e41ef7563cedbf6a2e4e5dda13456 (patch)
tree38e69047fcea8ce20b4a2c97a9cabc318b8c2f5b /source/blender
parentf81bc543e7f274aa88ca0d0c00dc86b4d6381416 (diff)
== PoseLib - Pose-Library Tool for Blender ==
"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
Diffstat (limited to 'source/blender')
-rw-r--r--source/blender/blenkernel/intern/action.c12
-rw-r--r--source/blender/blenkernel/intern/object.c1
-rw-r--r--source/blender/blenloader/intern/readfile.c15
-rw-r--r--source/blender/blenloader/intern/writefile.c12
-rw-r--r--source/blender/include/BIF_poselib.h52
-rw-r--r--source/blender/include/butspace.h5
-rw-r--r--source/blender/makesdna/DNA_action_types.h24
-rw-r--r--source/blender/src/buttons_editing.c80
-rw-r--r--source/blender/src/header_view3d.c45
-rw-r--r--source/blender/src/poselib.c770
-rw-r--r--source/blender/src/space.c12
11 files changed, 1022 insertions, 6 deletions
diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c
index 162ee582d74..86e119f5e6f 100644
--- a/source/blender/blenkernel/intern/action.c
+++ b/source/blender/blenkernel/intern/action.c
@@ -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)
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index d48be54fe10..b4512f3b1b3 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -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);
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index d30f0da95d6..f8f0395300c 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -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)
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index 2e14fe55383..90f48868911 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -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
index 00000000000..213714cf09a
--- /dev/null
+++ b/source/blender/include/BIF_poselib.h
@@ -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
diff --git a/source/blender/include/butspace.h b/source/blender/include/butspace.h
index ec8ffb819f6..7b6213be38f 100644
--- a/source/blender/include/butspace.h
+++ b/source/blender/include/butspace.h
@@ -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
diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h
index db6a2bda53c..9921d784984 100644
--- a/source/blender/makesdna/DNA_action_types.h
+++ b/source/blender/makesdna/DNA_action_types.h
@@ -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 */
diff --git a/source/blender/src/buttons_editing.c b/source/blender/src/buttons_editing.c
index d74f045255d..0cc3a70d2d1 100644
--- a/source/blender/src/buttons_editing.c
+++ b/source/blender/src/buttons_editing.c
@@ -114,6 +114,7 @@
#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;
diff --git a/source/blender/src/header_view3d.c b/source/blender/src/header_view3d.c
index 95f24c34098..55740861998 100644
--- a/source/blender/src/header_view3d.c
+++ b/source/blender/src/header_view3d.c
@@ -111,6 +111,7 @@
#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
index 00000000000..6e088bbf03e
--- /dev/null
+++ b/source/blender/src/poselib.c
@@ -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");
+}
diff --git a/source/blender/src/space.c b/source/blender/src/space.c
index 72107ec7003..5415f03701e 100644
--- a/source/blender/src/space.c
+++ b/source/blender/src/space.c
@@ -125,6 +125,7 @@
#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) {