forked from bartvdbraak/blender
Implement mirroring in pose mode (absolute and relative)
Added working X-mirroring in pose mode with an optional relative mirror mode. Reviewed By: Campbell Barton Differential Revision: http://developer.blender.org/D4765
This commit is contained in:
parent
f999c40d77
commit
09181540b5
@ -192,9 +192,13 @@ class VIEW3D_PT_tools_posemode_options(View3DPanel, Panel):
|
|||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
arm = context.active_object.data
|
arm = context.active_object.data
|
||||||
|
layout = self.layout
|
||||||
|
|
||||||
self.layout.prop(arm, "use_auto_ik")
|
layout.prop(arm, "use_auto_ik")
|
||||||
self.layout.prop(arm, "use_mirror_x")
|
layout.prop(arm, "use_mirror_x")
|
||||||
|
col = layout.column()
|
||||||
|
col.active = arm.use_mirror_x
|
||||||
|
col.prop(arm, "use_mirror_relative")
|
||||||
|
|
||||||
# ********** default tools for paint modes ****************
|
# ********** default tools for paint modes ****************
|
||||||
|
|
||||||
|
@ -3009,7 +3009,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
|
|||||||
}
|
}
|
||||||
|
|
||||||
LISTBASE_FOREACH (bArmature *, arm, &bmain->armatures) {
|
LISTBASE_FOREACH (bArmature *, arm, &bmain->armatures) {
|
||||||
arm->flag &= ~(ARM_FLAG_UNUSED_1 | ARM_FLAG_UNUSED_5 | ARM_FLAG_UNUSED_7 |
|
arm->flag &= ~(ARM_FLAG_UNUSED_1 | ARM_FLAG_UNUSED_5 | ARM_MIRROR_RELATIVE |
|
||||||
ARM_FLAG_UNUSED_12);
|
ARM_FLAG_UNUSED_12);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,6 +383,30 @@ typedef struct BoneInitData {
|
|||||||
float zwidth;
|
float zwidth;
|
||||||
} BoneInitData;
|
} BoneInitData;
|
||||||
|
|
||||||
|
typedef struct PoseInitData_Mirror {
|
||||||
|
/** Points to the bone which this info is initialized & restored to.
|
||||||
|
* A NULL value is used to terminate the array. */
|
||||||
|
struct bPoseChannel *pchan;
|
||||||
|
struct {
|
||||||
|
float loc[3];
|
||||||
|
float size[3];
|
||||||
|
union {
|
||||||
|
float eul[3];
|
||||||
|
float quat[4];
|
||||||
|
float axis_angle[4];
|
||||||
|
};
|
||||||
|
float curve_in_x;
|
||||||
|
float curve_out_x;
|
||||||
|
float roll1;
|
||||||
|
float roll2;
|
||||||
|
} orig;
|
||||||
|
/**
|
||||||
|
* An extra offset to apply after mirroring.
|
||||||
|
* Use with #ARM_MIRROR_RELATIVE.
|
||||||
|
*/
|
||||||
|
float offset_mtx[4][4];
|
||||||
|
} PoseInitData_Mirror;
|
||||||
|
|
||||||
typedef struct TransData {
|
typedef struct TransData {
|
||||||
/** Distance needed to affect element (for Proportionnal Editing). */
|
/** Distance needed to affect element (for Proportionnal Editing). */
|
||||||
float dist;
|
float dist;
|
||||||
@ -892,6 +916,7 @@ void flushTransSeq(TransInfo *t);
|
|||||||
void flushTransTracking(TransInfo *t);
|
void flushTransTracking(TransInfo *t);
|
||||||
void flushTransMasking(TransInfo *t);
|
void flushTransMasking(TransInfo *t);
|
||||||
void flushTransPaintCurve(TransInfo *t);
|
void flushTransPaintCurve(TransInfo *t);
|
||||||
|
void restoreMirrorPoseBones(TransDataContainer *tc);
|
||||||
void restoreBones(TransDataContainer *tc);
|
void restoreBones(TransDataContainer *tc);
|
||||||
|
|
||||||
/*********************** transform_gizmo.c ********** */
|
/*********************** transform_gizmo.c ********** */
|
||||||
|
@ -1201,6 +1201,74 @@ static short pose_grab_with_ik(Main *bmain, Object *ob)
|
|||||||
return (tot_ik) ? 1 : 0;
|
return (tot_ik) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void pose_mirror_info_init(PoseInitData_Mirror *pid,
|
||||||
|
bPoseChannel *pchan,
|
||||||
|
bPoseChannel *pchan_orig,
|
||||||
|
bool is_mirror_relative)
|
||||||
|
{
|
||||||
|
pid->pchan = pchan;
|
||||||
|
copy_v3_v3(pid->orig.loc, pchan->loc);
|
||||||
|
copy_v3_v3(pid->orig.size, pchan->size);
|
||||||
|
pid->orig.curve_in_x = pchan->curve_in_x;
|
||||||
|
pid->orig.curve_out_x = pchan->curve_out_x;
|
||||||
|
pid->orig.roll1 = pchan->roll1;
|
||||||
|
pid->orig.roll2 = pchan->roll2;
|
||||||
|
|
||||||
|
if (pchan->rotmode > 0) {
|
||||||
|
copy_v3_v3(pid->orig.eul, pchan->eul);
|
||||||
|
}
|
||||||
|
else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
|
||||||
|
copy_v3_v3(pid->orig.axis_angle, pchan->rotAxis);
|
||||||
|
pid->orig.axis_angle[3] = pchan->rotAngle;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
copy_qt_qt(pid->orig.quat, pchan->quat);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_mirror_relative) {
|
||||||
|
float pchan_mtx[4][4];
|
||||||
|
float pchan_mtx_mirror[4][4];
|
||||||
|
|
||||||
|
float flip_mtx[4][4];
|
||||||
|
unit_m4(flip_mtx);
|
||||||
|
flip_mtx[0][0] = -1;
|
||||||
|
|
||||||
|
BKE_pchan_to_mat4(pchan_orig, pchan_mtx_mirror);
|
||||||
|
BKE_pchan_to_mat4(pchan, pchan_mtx);
|
||||||
|
|
||||||
|
mul_m4_m4m4(pchan_mtx_mirror, pchan_mtx_mirror, flip_mtx);
|
||||||
|
mul_m4_m4m4(pchan_mtx_mirror, flip_mtx, pchan_mtx_mirror);
|
||||||
|
|
||||||
|
invert_m4(pchan_mtx_mirror);
|
||||||
|
mul_m4_m4m4(pid->offset_mtx, pchan_mtx, pchan_mtx_mirror);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
unit_m4(pid->offset_mtx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pose_mirror_info_restore(const PoseInitData_Mirror *pid)
|
||||||
|
{
|
||||||
|
bPoseChannel *pchan = pid->pchan;
|
||||||
|
copy_v3_v3(pchan->loc, pid->orig.loc);
|
||||||
|
copy_v3_v3(pchan->size, pid->orig.size);
|
||||||
|
pchan->curve_in_x = pid->orig.curve_in_x;
|
||||||
|
pchan->curve_out_x = pid->orig.curve_out_x;
|
||||||
|
pchan->roll1 = pid->orig.roll1;
|
||||||
|
pchan->roll2 = pid->orig.roll2;
|
||||||
|
|
||||||
|
if (pchan->rotmode > 0) {
|
||||||
|
copy_v3_v3(pchan->eul, pid->orig.eul);
|
||||||
|
}
|
||||||
|
else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
|
||||||
|
copy_v3_v3(pchan->rotAxis, pid->orig.axis_angle);
|
||||||
|
pchan->rotAngle = pid->orig.axis_angle[3];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
copy_qt_qt(pchan->quat, pid->orig.quat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When objects array is NULL, use 't->data_container' as is.
|
* When objects array is NULL, use 't->data_container' as is.
|
||||||
*/
|
*/
|
||||||
@ -1225,6 +1293,8 @@ static void createTransPose(TransInfo *t)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool mirror = ((arm->flag & ARM_MIRROR_EDIT) != 0);
|
||||||
|
|
||||||
/* set flags and count total */
|
/* set flags and count total */
|
||||||
tc->data_len = count_set_pose_transflags(ob, t->mode, t->around, has_translate_rotate);
|
tc->data_len = count_set_pose_transflags(ob, t->mode, t->around, has_translate_rotate);
|
||||||
if (tc->data_len == 0) {
|
if (tc->data_len == 0) {
|
||||||
@ -1247,6 +1317,25 @@ static void createTransPose(TransInfo *t)
|
|||||||
has_translate_rotate[0] = true;
|
has_translate_rotate[0] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mirror) {
|
||||||
|
int total_mirrored = 0;
|
||||||
|
for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
|
||||||
|
if ((pchan->bone->flag & BONE_TRANSFORM) &&
|
||||||
|
BKE_pose_channel_get_mirrored(ob->pose, pchan->name)) {
|
||||||
|
total_mirrored++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PoseInitData_Mirror *pid = MEM_mallocN((total_mirrored + 1) * sizeof(PoseInitData_Mirror),
|
||||||
|
"PoseInitData_Mirror");
|
||||||
|
|
||||||
|
/* Trick to terminate iteration. */
|
||||||
|
pid[total_mirrored].pchan = NULL;
|
||||||
|
|
||||||
|
tc->custom.type.data = pid;
|
||||||
|
tc->custom.type.use_free = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if there are no translatable bones, do rotation */
|
/* if there are no translatable bones, do rotation */
|
||||||
@ -1269,6 +1358,19 @@ static void createTransPose(TransInfo *t)
|
|||||||
short ik_on = 0;
|
short ik_on = 0;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
PoseInitData_Mirror *pid = tc->custom.type.data;
|
||||||
|
int pid_index = 0;
|
||||||
|
bArmature *arm;
|
||||||
|
|
||||||
|
/* check validity of state */
|
||||||
|
arm = BKE_armature_from_object(tc->poseobj);
|
||||||
|
if ((arm == NULL) || (ob->pose == NULL)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool mirror = ((arm->flag & ARM_MIRROR_EDIT) != 0);
|
||||||
|
const bool is_mirror_relative = ((arm->flag & ARM_MIRROR_RELATIVE) != 0);
|
||||||
|
|
||||||
tc->poseobj = ob; /* we also allow non-active objects to be transformed, in weightpaint */
|
tc->poseobj = ob; /* we also allow non-active objects to be transformed, in weightpaint */
|
||||||
|
|
||||||
/* init trans data */
|
/* init trans data */
|
||||||
@ -1285,6 +1387,15 @@ static void createTransPose(TransInfo *t)
|
|||||||
for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
|
for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
|
||||||
if (pchan->bone->flag & BONE_TRANSFORM) {
|
if (pchan->bone->flag & BONE_TRANSFORM) {
|
||||||
add_pose_transdata(t, pchan, ob, tc, td);
|
add_pose_transdata(t, pchan, ob, tc, td);
|
||||||
|
|
||||||
|
if (mirror) {
|
||||||
|
bPoseChannel *pchan_mirror = BKE_pose_channel_get_mirrored(ob->pose, pchan->name);
|
||||||
|
if (pchan_mirror) {
|
||||||
|
pose_mirror_info_init(&pid[pid_index], pchan_mirror, pchan, is_mirror_relative);
|
||||||
|
pid_index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
td++;
|
td++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1304,6 +1415,27 @@ static void createTransPose(TransInfo *t)
|
|||||||
t->flag &= ~T_PROP_EDIT_ALL;
|
t->flag &= ~T_PROP_EDIT_ALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void restoreMirrorPoseBones(TransDataContainer *tc)
|
||||||
|
{
|
||||||
|
bArmature *arm;
|
||||||
|
|
||||||
|
if (tc->obedit) {
|
||||||
|
arm = tc->obedit->data;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
BLI_assert(tc->poseobj != NULL);
|
||||||
|
arm = tc->poseobj->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(arm->flag & ARM_MIRROR_EDIT)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PoseInitData_Mirror *pid = tc->custom.type.data; pid->pchan; pid++) {
|
||||||
|
pose_mirror_info_restore(pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void restoreBones(TransDataContainer *tc)
|
void restoreBones(TransDataContainer *tc)
|
||||||
{
|
{
|
||||||
bArmature *arm;
|
bArmature *arm;
|
||||||
|
@ -781,6 +781,45 @@ static void recalcData_spaceclip(TransInfo *t)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* if pose bone (partial) selected, copy data.
|
||||||
|
* context; posemode armature, with mirror editing enabled.
|
||||||
|
*
|
||||||
|
* \param pid: Optional, apply relative transform when set.
|
||||||
|
*/
|
||||||
|
static void pose_transform_mirror_update(Object *ob, PoseInitData_Mirror *pid)
|
||||||
|
{
|
||||||
|
float flip_mtx[4][4];
|
||||||
|
unit_m4(flip_mtx);
|
||||||
|
flip_mtx[0][0] = -1;
|
||||||
|
|
||||||
|
for (bPoseChannel *pchan_orig = ob->pose->chanbase.first; pchan_orig;
|
||||||
|
pchan_orig = pchan_orig->next) {
|
||||||
|
/* no layer check, correct mirror is more important */
|
||||||
|
if (pchan_orig->bone->flag & BONE_TRANSFORM) {
|
||||||
|
bPoseChannel *pchan = BKE_pose_channel_get_mirrored(ob->pose, pchan_orig->name);
|
||||||
|
|
||||||
|
if (pchan) {
|
||||||
|
/* we assume X-axis flipping for now */
|
||||||
|
pchan->curve_in_x = pchan_orig->curve_in_x * -1;
|
||||||
|
pchan->curve_out_x = pchan_orig->curve_out_x * -1;
|
||||||
|
pchan->roll1 = pchan_orig->roll1 * -1; // XXX?
|
||||||
|
pchan->roll2 = pchan_orig->roll2 * -1; // XXX?
|
||||||
|
|
||||||
|
float pchan_mtx_final[4][4];
|
||||||
|
BKE_pchan_to_mat4(pchan_orig, pchan_mtx_final);
|
||||||
|
mul_m4_m4m4(pchan_mtx_final, pchan_mtx_final, flip_mtx);
|
||||||
|
mul_m4_m4m4(pchan_mtx_final, flip_mtx, pchan_mtx_final);
|
||||||
|
if (pid) {
|
||||||
|
mul_m4_m4m4(pchan_mtx_final, pid->offset_mtx, pchan_mtx_final);
|
||||||
|
pid++;
|
||||||
|
}
|
||||||
|
BKE_pchan_apply_mat4(pchan, pchan_mtx_final, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* helper for recalcData() - for object transforms, typically in the 3D view */
|
/* helper for recalcData() - for object transforms, typically in the 3D view */
|
||||||
static void recalcData_objects(TransInfo *t)
|
static void recalcData_objects(TransInfo *t)
|
||||||
{
|
{
|
||||||
@ -992,6 +1031,19 @@ static void recalcData_objects(TransInfo *t)
|
|||||||
Object *ob = tc->poseobj;
|
Object *ob = tc->poseobj;
|
||||||
bArmature *arm = ob->data;
|
bArmature *arm = ob->data;
|
||||||
|
|
||||||
|
if (arm->flag & ARM_MIRROR_EDIT) {
|
||||||
|
if (t->state != TRANS_CANCEL) {
|
||||||
|
PoseInitData_Mirror *pid = NULL;
|
||||||
|
if (arm->flag & ARM_MIRROR_RELATIVE) {
|
||||||
|
pid = tc->custom.type.data;
|
||||||
|
}
|
||||||
|
pose_transform_mirror_update(ob, pid);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
restoreMirrorPoseBones(tc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* if animtimer is running, and the object already has animation data,
|
/* if animtimer is running, and the object already has animation data,
|
||||||
* check if the auto-record feature means that we should record 'samples'
|
* check if the auto-record feature means that we should record 'samples'
|
||||||
* (i.e. un-editable animation values)
|
* (i.e. un-editable animation values)
|
||||||
|
@ -144,7 +144,7 @@ typedef enum eArmature_Flag {
|
|||||||
ARM_POSEMODE = (1 << 4),
|
ARM_POSEMODE = (1 << 4),
|
||||||
ARM_FLAG_UNUSED_5 = (1 << 5), /* cleared */
|
ARM_FLAG_UNUSED_5 = (1 << 5), /* cleared */
|
||||||
ARM_DELAYDEFORM = (1 << 6),
|
ARM_DELAYDEFORM = (1 << 6),
|
||||||
ARM_FLAG_UNUSED_7 = (1 << 7), /* cleared */
|
ARM_MIRROR_RELATIVE = (1 << 7),
|
||||||
ARM_MIRROR_EDIT = (1 << 8),
|
ARM_MIRROR_EDIT = (1 << 8),
|
||||||
ARM_AUTO_IK = (1 << 9),
|
ARM_AUTO_IK = (1 << 9),
|
||||||
/** made option negative, for backwards compat */
|
/** made option negative, for backwards compat */
|
||||||
|
@ -1359,6 +1359,13 @@ static void rna_def_armature(BlenderRNA *brna)
|
|||||||
RNA_def_property_update(prop, 0, "rna_Armature_redraw_data");
|
RNA_def_property_update(prop, 0, "rna_Armature_redraw_data");
|
||||||
RNA_def_property_flag(prop, PROP_LIB_EXCEPTION);
|
RNA_def_property_flag(prop, PROP_LIB_EXCEPTION);
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "use_mirror_relative", PROP_BOOLEAN, PROP_NONE);
|
||||||
|
RNA_def_property_boolean_sdna(prop, NULL, "flag", ARM_MIRROR_RELATIVE);
|
||||||
|
RNA_def_property_ui_text(
|
||||||
|
prop, "Relative Mirror", "Apply relative transformations in X-mirror mode");
|
||||||
|
RNA_def_property_update(prop, 0, "rna_Armature_redraw_data");
|
||||||
|
|
||||||
|
RNA_def_property_flag(prop, PROP_LIB_EXCEPTION);
|
||||||
prop = RNA_def_property(srna, "use_auto_ik", PROP_BOOLEAN, PROP_NONE);
|
prop = RNA_def_property(srna, "use_auto_ik", PROP_BOOLEAN, PROP_NONE);
|
||||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", ARM_AUTO_IK);
|
RNA_def_property_boolean_sdna(prop, NULL, "flag", ARM_AUTO_IK);
|
||||||
RNA_def_property_ui_text(
|
RNA_def_property_ui_text(
|
||||||
|
Loading…
Reference in New Issue
Block a user