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= alloc_libblock (&G.main->armature, ID_AR, name);
|
||||||
arm->deformflag = ARM_DEF_VGROUP|ARM_DEF_ENVELOPE;
|
arm->deformflag = ARM_DEF_VGROUP|ARM_DEF_ENVELOPE;
|
||||||
|
arm->flag = ARM_COL_CUSTOM; /* custom bone-group colors */
|
||||||
arm->layer= 1;
|
arm->layer= 1;
|
||||||
return arm;
|
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_loc_clear(struct wmOperatorType *ot);
|
||||||
void POSE_OT_scale_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_all_toggle(struct wmOperatorType *ot);
|
||||||
void POSE_OT_select_inverse(struct wmOperatorType *ot);
|
void POSE_OT_select_inverse(struct wmOperatorType *ot);
|
||||||
void POSE_OT_select_parent(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_loc_clear);
|
||||||
WM_operatortype_append(POSE_OT_scale_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_all_toggle);
|
||||||
WM_operatortype_append(POSE_OT_select_inverse);
|
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_loc_clear", GKEY, KM_PRESS, KM_ALT, 0);
|
||||||
WM_keymap_add_item(keymap, "POSE_OT_scale_clear", SKEY, 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_all_toggle", AKEY, KM_PRESS, 0, 0);
|
||||||
WM_keymap_add_item(keymap, "POSE_OT_select_inverse", IKEY, KM_PRESS, KM_CTRL, 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_modifier.h"
|
||||||
#include "BKE_object.h"
|
#include "BKE_object.h"
|
||||||
#include "BKE_utildefines.h"
|
#include "BKE_utildefines.h"
|
||||||
|
#include "BKE_report.h"
|
||||||
|
|
||||||
#include "BIF_gl.h"
|
#include "BIF_gl.h"
|
||||||
|
|
||||||
@@ -779,81 +780,132 @@ void pose_copy_menu(Scene *scene)
|
|||||||
|
|
||||||
/* ******************** copy/paste pose ********************** */
|
/* ******************** 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)
|
void free_posebuf(void)
|
||||||
{
|
{
|
||||||
if (g_posebuf) {
|
if (g_posebuf) {
|
||||||
// was copied without constraints
|
/* was copied without constraints */
|
||||||
BLI_freelistN (&g_posebuf->chanbase);
|
BLI_freelistN(&g_posebuf->chanbase);
|
||||||
MEM_freeN (g_posebuf);
|
MEM_freeN(g_posebuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_posebuf=NULL;
|
g_posebuf=NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void copy_posebuf (Scene *scene)
|
/* ---- */
|
||||||
{
|
|
||||||
Object *ob= OBACT;
|
|
||||||
|
|
||||||
if (!ob || !ob->pose){
|
static int pose_copy_exec (bContext *C, wmOperator *op)
|
||||||
error ("No Pose");
|
{
|
||||||
return;
|
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();
|
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);
|
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;
|
/* identifiers */
|
||||||
bPoseChannel *chan, *pchan;
|
ot->name= "Copy Pose";
|
||||||
float eul[4];
|
ot->idname= "POSE_OT_copy";
|
||||||
char name[32];
|
ot->description= "Copies the current pose of the selected bones to copy/paste buffer.";
|
||||||
|
|
||||||
if (!ob || !ob->pose)
|
/* api callbacks */
|
||||||
return;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* Safely merge all of the channels in the buffer pose into any existing pose */
|
||||||
// disabled until protected bones in proxies follow the rules everywhere else!
|
for (chan= g_posebuf->chanbase.first; chan; chan=chan->next) {
|
||||||
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) {
|
|
||||||
if (chan->flag & POSE_KEY) {
|
if (chan->flag & POSE_KEY) {
|
||||||
|
/* get the name - if flipping, we must flip this first */
|
||||||
BLI_strncpy(name, chan->name, sizeof(name));
|
BLI_strncpy(name, chan->name, sizeof(name));
|
||||||
if (flip)
|
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 */
|
/* only copy when channel exists, poses are not meant to add random channels to anymore */
|
||||||
pchan= get_pose_channel(ob->pose, name);
|
pchan= get_pose_channel(ob->pose, name);
|
||||||
|
|
||||||
if (pchan) {
|
if (pchan) {
|
||||||
/* only loc rot size */
|
/* only loc rot size
|
||||||
/* only copies transform info for the pose */
|
* - only copies transform info for the pose
|
||||||
|
*/
|
||||||
VECCOPY(pchan->loc, chan->loc);
|
VECCOPY(pchan->loc, chan->loc);
|
||||||
VECCOPY(pchan->size, chan->size);
|
VECCOPY(pchan->size, chan->size);
|
||||||
QUATCOPY(pchan->quat, chan->quat);
|
|
||||||
pchan->flag= chan->flag;
|
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) {
|
if (flip) {
|
||||||
pchan->loc[0]*= -1;
|
pchan->loc[0]*= -1;
|
||||||
|
|
||||||
QuatToEul(pchan->quat, eul);
|
/* has to be done as eulers... */
|
||||||
eul[1]*= -1;
|
if (pchan->rotmode) {
|
||||||
eul[2]*= -1;
|
pchan->eul[1] *= -1;
|
||||||
EulToQuat(eul, pchan->quat);
|
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
|
#if 0 // XXX old animation system
|
||||||
@@ -861,6 +913,7 @@ void paste_posebuf (Scene *scene, int flip)
|
|||||||
ID *id= &ob->id;
|
ID *id= &ob->id;
|
||||||
|
|
||||||
/* Set keys on pose */
|
/* Set keys on pose */
|
||||||
|
// TODO: make these use keyingsets....
|
||||||
if (chan->flag & POSE_ROT) {
|
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_X, 0);
|
||||||
insertkey(id, ID_PO, pchan->name, NULL, AC_QUAT_Y, 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);
|
where_is_pose(scene, ob);
|
||||||
ob->recalc= 0;
|
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, "");
|
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, "");
|
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
|
#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, "Pose Library", 0, view3d_pose_armature_poselibmenu);
|
||||||
//uiItemMenuF(layout, "Motion Paths", 0, view3d_pose_armature_motionpathsmenu);
|
//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)");
|
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)) {
|
if (ob && (ob->flag & OB_POSEMODE)) {
|
||||||
xco+= XIC/2;
|
xco+= XIC;
|
||||||
uiBlockBeginAlign(block);
|
uiBlockBeginAlign(block);
|
||||||
|
|
||||||
uiDefIconBut(block, BUT, B_ACTCOPY, ICON_COPYDOWN,
|
uiDefIconButO(block, BUT, "POSE_OT_copy", WM_OP_INVOKE_REGION_WIN, ICON_COPYDOWN, xco,yco,XIC,YIC, NULL);
|
||||||
xco,yco,XIC,YIC, 0, 0, 0, 0, 0,
|
|
||||||
"Copies the current pose to the buffer");
|
|
||||||
uiBlockSetButLock(block, object_data_is_libdata(ob), "Can't edit external libdata");
|
uiBlockSetButLock(block, object_data_is_libdata(ob), "Can't edit external libdata");
|
||||||
xco+= XIC;
|
xco+= XIC;
|
||||||
|
|
||||||
uiDefIconBut(block, BUT, B_ACTPASTE, ICON_PASTEDOWN,
|
uiDefIconButO(block, BUT, "POSE_OT_paste", WM_OP_INVOKE_REGION_WIN, ICON_PASTEDOWN, xco,yco,XIC,YIC, NULL);
|
||||||
xco,yco,XIC,YIC, 0, 0, 0, 0, 0,
|
|
||||||
"Pastes the pose from the buffer");
|
|
||||||
xco+= XIC;
|
xco+= XIC;
|
||||||
uiDefIconBut(block, BUT, B_ACTPASTEFLIP, ICON_PASTEFLIPDOWN,
|
// FIXME: this needs an extra arg...
|
||||||
xco,yco,XIC,YIC, 0, 0, 0, 0, 0,
|
uiDefIconButO(block, BUT, "POSE_OT_paste", WM_OP_INVOKE_REGION_WIN, ICON_PASTEFLIPDOWN, xco,yco,XIC,YIC, NULL);
|
||||||
"Pastes the mirrored pose from the buffer");
|
|
||||||
uiBlockEndAlign(block);
|
uiBlockEndAlign(block);
|
||||||
header_xco_step(ar, &xco, &yco, &maxco, XIC);
|
header_xco_step(ar, &xco, &yco, &maxco, XIC);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user