2.5 - Copy/Paste Operators for Armatures
* Buttons in header now use operators too. The paste-flipped button needs attention though, since the flipped argument isn't set yet * Assigned Ctrl-C, Ctrl-V, and Ctrl-Shift-V to Copy/Paste/Paste-Flipped respectively for now. * Auto-Keying for this doesn't work again yet. On todo for later... --- * Also, new armatures now get the flag to show custom bone colours enabled by default.
This commit is contained in:
@@ -83,6 +83,7 @@ bArmature *add_armature(char *name)
|
||||
|
||||
arm= alloc_libblock (&G.main->armature, ID_AR, name);
|
||||
arm->deformflag = ARM_DEF_VGROUP|ARM_DEF_ENVELOPE;
|
||||
arm->flag = ARM_COL_CUSTOM; /* custom bone-group colors */
|
||||
arm->layer= 1;
|
||||
return arm;
|
||||
}
|
||||
|
@@ -64,6 +64,9 @@ void POSE_OT_rot_clear(struct wmOperatorType *ot);
|
||||
void POSE_OT_loc_clear(struct wmOperatorType *ot);
|
||||
void POSE_OT_scale_clear(struct wmOperatorType *ot);
|
||||
|
||||
void POSE_OT_copy(struct wmOperatorType *ot);
|
||||
void POSE_OT_paste(struct wmOperatorType *ot);
|
||||
|
||||
void POSE_OT_select_all_toggle(struct wmOperatorType *ot);
|
||||
void POSE_OT_select_inverse(struct wmOperatorType *ot);
|
||||
void POSE_OT_select_parent(struct wmOperatorType *ot);
|
||||
|
@@ -147,6 +147,9 @@ void ED_operatortypes_armature(void)
|
||||
WM_operatortype_append(POSE_OT_loc_clear);
|
||||
WM_operatortype_append(POSE_OT_scale_clear);
|
||||
|
||||
WM_operatortype_append(POSE_OT_copy);
|
||||
WM_operatortype_append(POSE_OT_paste);
|
||||
|
||||
WM_operatortype_append(POSE_OT_select_all_toggle);
|
||||
WM_operatortype_append(POSE_OT_select_inverse);
|
||||
|
||||
@@ -238,6 +241,12 @@ void ED_keymap_armature(wmWindowManager *wm)
|
||||
WM_keymap_add_item(keymap, "POSE_OT_loc_clear", GKEY, KM_PRESS, KM_ALT, 0);
|
||||
WM_keymap_add_item(keymap, "POSE_OT_scale_clear", SKEY, KM_PRESS, KM_ALT, 0);
|
||||
|
||||
// for now, we include hotkeys for copy/paste
|
||||
WM_keymap_add_item(keymap, "POSE_OT_copy", CKEY, KM_PRESS, KM_CTRL, 0);
|
||||
WM_keymap_add_item(keymap, "POSE_OT_paste", VKEY, KM_PRESS, KM_CTRL, 0);
|
||||
kmi= WM_keymap_add_item(keymap, "POSE_OT_paste", VKEY, KM_PRESS, KM_CTRL|KM_SHIFT, 0);
|
||||
RNA_boolean_set(kmi->ptr, "flipped", 1);
|
||||
|
||||
WM_keymap_add_item(keymap, "POSE_OT_select_all_toggle", AKEY, KM_PRESS, 0, 0);
|
||||
WM_keymap_add_item(keymap, "POSE_OT_select_inverse", IKEY, KM_PRESS, KM_CTRL, 0);
|
||||
|
||||
|
@@ -63,6 +63,7 @@
|
||||
#include "BKE_modifier.h"
|
||||
#include "BKE_object.h"
|
||||
#include "BKE_utildefines.h"
|
||||
#include "BKE_report.h"
|
||||
|
||||
#include "BIF_gl.h"
|
||||
|
||||
@@ -779,81 +780,132 @@ void pose_copy_menu(Scene *scene)
|
||||
|
||||
/* ******************** copy/paste pose ********************** */
|
||||
|
||||
static bPose *g_posebuf=NULL;
|
||||
/* Global copy/paste buffer for pose - cleared on start/end session + before every copy operation */
|
||||
static bPose *g_posebuf = NULL;
|
||||
|
||||
void free_posebuf(void)
|
||||
{
|
||||
if (g_posebuf) {
|
||||
// was copied without constraints
|
||||
BLI_freelistN (&g_posebuf->chanbase);
|
||||
MEM_freeN (g_posebuf);
|
||||
/* was copied without constraints */
|
||||
BLI_freelistN(&g_posebuf->chanbase);
|
||||
MEM_freeN(g_posebuf);
|
||||
}
|
||||
|
||||
g_posebuf=NULL;
|
||||
}
|
||||
|
||||
void copy_posebuf (Scene *scene)
|
||||
{
|
||||
Object *ob= OBACT;
|
||||
/* ---- */
|
||||
|
||||
if (!ob || !ob->pose){
|
||||
error ("No Pose");
|
||||
return;
|
||||
static int pose_copy_exec (bContext *C, wmOperator *op)
|
||||
{
|
||||
Object *ob= CTX_data_active_object(C);
|
||||
|
||||
/* sanity checking */
|
||||
if ELEM(NULL, ob, ob->pose) {
|
||||
BKE_report(op->reports, RPT_ERROR, "No Pose to Copy");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* free existing pose buffer */
|
||||
free_posebuf();
|
||||
|
||||
set_pose_keys(ob); // sets chan->flag to POSE_KEY if bone selected
|
||||
/* sets chan->flag to POSE_KEY if bone selected, then copy those bones to the buffer */
|
||||
set_pose_keys(ob);
|
||||
copy_pose(&g_posebuf, ob->pose, 0);
|
||||
|
||||
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void paste_posebuf (Scene *scene, int flip)
|
||||
void POSE_OT_copy (wmOperatorType *ot)
|
||||
{
|
||||
Object *ob= OBACT;
|
||||
bPoseChannel *chan, *pchan;
|
||||
float eul[4];
|
||||
char name[32];
|
||||
/* identifiers */
|
||||
ot->name= "Copy Pose";
|
||||
ot->idname= "POSE_OT_copy";
|
||||
ot->description= "Copies the current pose of the selected bones to copy/paste buffer.";
|
||||
|
||||
if (!ob || !ob->pose)
|
||||
return;
|
||||
/* api callbacks */
|
||||
ot->exec= pose_copy_exec;
|
||||
ot->poll= ED_operator_posemode;
|
||||
|
||||
/* flag */
|
||||
ot->flag= OPTYPE_REGISTER;
|
||||
}
|
||||
|
||||
if (!g_posebuf){
|
||||
error ("Copy buffer is empty");
|
||||
return;
|
||||
/* ---- */
|
||||
|
||||
static int pose_paste_exec (bContext *C, wmOperator *op)
|
||||
{
|
||||
Scene *scene= CTX_data_scene(C);
|
||||
Object *ob= CTX_data_active_object(C);
|
||||
bPoseChannel *chan, *pchan;
|
||||
char name[32];
|
||||
int flip= RNA_boolean_get(op->ptr, "flipped");
|
||||
|
||||
/* sanity checks */
|
||||
if ELEM(NULL, ob, ob->pose)
|
||||
return OPERATOR_CANCELLED;
|
||||
|
||||
if (g_posebuf == NULL) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Copy buffer is empty");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/*
|
||||
// disabled until protected bones in proxies follow the rules everywhere else!
|
||||
if(pose_has_protected_selected(ob, 1, 1))
|
||||
return;
|
||||
*/
|
||||
|
||||
/* Safely merge all of the channels in this pose into
|
||||
any existing pose */
|
||||
for (chan=g_posebuf->chanbase.first; chan; chan=chan->next) {
|
||||
/* Safely merge all of the channels in the buffer pose into any existing pose */
|
||||
for (chan= g_posebuf->chanbase.first; chan; chan=chan->next) {
|
||||
if (chan->flag & POSE_KEY) {
|
||||
/* get the name - if flipping, we must flip this first */
|
||||
BLI_strncpy(name, chan->name, sizeof(name));
|
||||
if (flip)
|
||||
bone_flip_name (name, 0); // 0 = don't strip off number extensions
|
||||
bone_flip_name(name, 0); /* 0 = don't strip off number extensions */
|
||||
|
||||
/* only copy when channel exists, poses are not meant to add random channels to anymore */
|
||||
pchan= get_pose_channel(ob->pose, name);
|
||||
|
||||
if (pchan) {
|
||||
/* only loc rot size */
|
||||
/* only copies transform info for the pose */
|
||||
/* only loc rot size
|
||||
* - only copies transform info for the pose
|
||||
*/
|
||||
VECCOPY(pchan->loc, chan->loc);
|
||||
VECCOPY(pchan->size, chan->size);
|
||||
QUATCOPY(pchan->quat, chan->quat);
|
||||
pchan->flag= chan->flag;
|
||||
|
||||
/* check if rotation modes are compatible (i.e. do they need any conversions) */
|
||||
if (pchan->rotmode == chan->rotmode) {
|
||||
/* copy the type of rotation in use */
|
||||
if (pchan->rotmode) {
|
||||
VECCOPY(pchan->eul, chan->eul);
|
||||
}
|
||||
else {
|
||||
QUATCOPY(pchan->quat, chan->quat);
|
||||
}
|
||||
}
|
||||
else if (pchan->rotmode) {
|
||||
/* quat to euler */
|
||||
QuatToEul(chan->quat, pchan->eul);
|
||||
}
|
||||
else {
|
||||
/* euler to quat */
|
||||
EulToQuat(chan->eul, pchan->quat);
|
||||
}
|
||||
|
||||
/* paste flipped pose? */
|
||||
if (flip) {
|
||||
pchan->loc[0]*= -1;
|
||||
|
||||
QuatToEul(pchan->quat, eul);
|
||||
eul[1]*= -1;
|
||||
eul[2]*= -1;
|
||||
EulToQuat(eul, pchan->quat);
|
||||
/* has to be done as eulers... */
|
||||
if (pchan->rotmode) {
|
||||
pchan->eul[1] *= -1;
|
||||
pchan->eul[2] *= -1;
|
||||
}
|
||||
else {
|
||||
float eul[3];
|
||||
|
||||
QuatToEul(pchan->quat, eul);
|
||||
eul[1]*= -1;
|
||||
eul[2]*= -1;
|
||||
EulToQuat(eul, pchan->quat);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 // XXX old animation system
|
||||
@@ -861,6 +913,7 @@ void paste_posebuf (Scene *scene, int flip)
|
||||
ID *id= &ob->id;
|
||||
|
||||
/* Set keys on pose */
|
||||
// TODO: make these use keyingsets....
|
||||
if (chan->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);
|
||||
@@ -903,8 +956,29 @@ void paste_posebuf (Scene *scene, int flip)
|
||||
where_is_pose(scene, ob);
|
||||
ob->recalc= 0;
|
||||
}
|
||||
|
||||
/* notifiers for updates */
|
||||
WM_event_add_notifier(C, NC_OBJECT|ND_POSE|ND_TRANSFORM, ob);
|
||||
|
||||
BIF_undo_push("Paste Action Pose");
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void POSE_OT_paste (wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name= "Paste Pose";
|
||||
ot->idname= "POSE_OT_paste";
|
||||
ot->description= "Pastes the stored pose on to the current pose.";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec= pose_paste_exec;
|
||||
ot->poll= ED_operator_posemode;
|
||||
|
||||
/* flag */
|
||||
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
|
||||
|
||||
/* properties */
|
||||
RNA_def_boolean(ot->srna, "flipped", 0, "Flipped on X-Axis", "");
|
||||
}
|
||||
|
||||
/* ********************************************** */
|
||||
|
@@ -3175,13 +3175,13 @@ static void view3d_pose_armaturemenu(bContext *C, uiLayout *layout, void *arg_un
|
||||
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Apply Pose as Restpose|Ctrl A", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 19, "");
|
||||
|
||||
uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
|
||||
|
||||
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Copy Current Pose", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 1, "");
|
||||
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Paste Pose", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 2, "");
|
||||
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Paste Flipped Pose", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 3, "");
|
||||
|
||||
uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
|
||||
#endif
|
||||
|
||||
uiItemO(layout, "Copy Current Pose", 0, "POSE_OT_copy");
|
||||
uiItemO(layout, "Paste Pose", 0, "POSE_OT_paste");
|
||||
uiItemBooleanO(layout, "Paste X-Flipped Pose", 0, "POSE_OT_paste", "flipped", 1);
|
||||
|
||||
uiItemS(layout);
|
||||
|
||||
uiItemMenuF(layout, "Pose Library", 0, view3d_pose_armature_poselibmenu);
|
||||
//uiItemMenuF(layout, "Motion Paths", 0, view3d_pose_armature_motionpathsmenu);
|
||||
@@ -4492,24 +4492,18 @@ void uiTemplateHeader3D(uiLayout *layout, struct bContext *C)
|
||||
|
||||
uiDefIconBut(block, BUT, B_VIEWRENDER, ICON_SCENE, xco,yco,XIC,YIC, NULL, 0, 1.0, 0, 0, "Render this window (Ctrl Click for anim)");
|
||||
|
||||
|
||||
if (ob && (ob->flag & OB_POSEMODE)) {
|
||||
xco+= XIC/2;
|
||||
xco+= XIC;
|
||||
uiBlockBeginAlign(block);
|
||||
|
||||
uiDefIconBut(block, BUT, B_ACTCOPY, ICON_COPYDOWN,
|
||||
xco,yco,XIC,YIC, 0, 0, 0, 0, 0,
|
||||
"Copies the current pose to the buffer");
|
||||
uiDefIconButO(block, BUT, "POSE_OT_copy", WM_OP_INVOKE_REGION_WIN, ICON_COPYDOWN, xco,yco,XIC,YIC, NULL);
|
||||
uiBlockSetButLock(block, object_data_is_libdata(ob), "Can't edit external libdata");
|
||||
xco+= XIC;
|
||||
|
||||
uiDefIconBut(block, BUT, B_ACTPASTE, ICON_PASTEDOWN,
|
||||
xco,yco,XIC,YIC, 0, 0, 0, 0, 0,
|
||||
"Pastes the pose from the buffer");
|
||||
|
||||
uiDefIconButO(block, BUT, "POSE_OT_paste", WM_OP_INVOKE_REGION_WIN, ICON_PASTEDOWN, xco,yco,XIC,YIC, NULL);
|
||||
xco+= XIC;
|
||||
uiDefIconBut(block, BUT, B_ACTPASTEFLIP, ICON_PASTEFLIPDOWN,
|
||||
xco,yco,XIC,YIC, 0, 0, 0, 0, 0,
|
||||
"Pastes the mirrored pose from the buffer");
|
||||
// FIXME: this needs an extra arg...
|
||||
uiDefIconButO(block, BUT, "POSE_OT_paste", WM_OP_INVOKE_REGION_WIN, ICON_PASTEFLIPDOWN, xco,yco,XIC,YIC, NULL);
|
||||
uiBlockEndAlign(block);
|
||||
header_xco_step(ar, &xco, &yco, &maxco, XIC);
|
||||
|
||||
|
Reference in New Issue
Block a user