diff options
author | Joshua Leung <aligorith@gmail.com> | 2007-12-26 14:17:26 +0300 |
---|---|---|
committer | Joshua Leung <aligorith@gmail.com> | 2007-12-26 14:17:26 +0300 |
commit | 10b8237eab7e41ef7563cedbf6a2e4e5dda13456 (patch) | |
tree | 38e69047fcea8ce20b4a2c97a9cabc318b8c2f5b | |
parent | f81bc543e7f274aa88ca0d0c00dc86b4d6381416 (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
-rw-r--r-- | source/blender/blenkernel/intern/action.c | 12 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/object.c | 1 | ||||
-rw-r--r-- | source/blender/blenloader/intern/readfile.c | 15 | ||||
-rw-r--r-- | source/blender/blenloader/intern/writefile.c | 12 | ||||
-rw-r--r-- | source/blender/include/BIF_poselib.h | 52 | ||||
-rw-r--r-- | source/blender/include/butspace.h | 5 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_action_types.h | 24 | ||||
-rw-r--r-- | source/blender/src/buttons_editing.c | 80 | ||||
-rw-r--r-- | source/blender/src/header_view3d.c | 45 | ||||
-rw-r--r-- | source/blender/src/poselib.c | 770 | ||||
-rw-r--r-- | source/blender/src/space.c | 12 |
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) { |