== 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
This commit is contained in:
Joshua Leung
2007-12-26 11:17:26 +00:00
parent f81bc543e7
commit 10b8237eab
11 changed files with 1022 additions and 6 deletions

View 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)

View 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);

View 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)

View 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);
}
}
}
}

View File

@@ -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

View 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

View 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 */

View File

@@ -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;

View File

@@ -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, "");

View File

@@ -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");
}

View File

@@ -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) {