diff options
author | Sebastian Parborg <darkdefende@gmail.com> | 2019-05-06 10:47:45 +0300 |
---|---|---|
committer | Sebastian Parborg <darkdefende@gmail.com> | 2019-05-06 10:47:45 +0300 |
commit | 09181540b5aa889df6aa24bea4ce9a4823e0c18d (patch) | |
tree | 9d4daaf5f0f41883d64b8e6f21bca49e7cfcea84 /source/blender/editors | |
parent | f999c40d77f32594e3fdd5800758e70aa9a314f3 (diff) |
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
Diffstat (limited to 'source/blender/editors')
-rw-r--r-- | source/blender/editors/transform/transform.h | 25 | ||||
-rw-r--r-- | source/blender/editors/transform/transform_conversions.c | 132 | ||||
-rw-r--r-- | source/blender/editors/transform/transform_generics.c | 52 |
3 files changed, 209 insertions, 0 deletions
diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 50fc1a276b9..268af8dff45 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -383,6 +383,30 @@ typedef struct BoneInitData { float zwidth; } 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 { /** Distance needed to affect element (for Proportionnal Editing). */ float dist; @@ -892,6 +916,7 @@ void flushTransSeq(TransInfo *t); void flushTransTracking(TransInfo *t); void flushTransMasking(TransInfo *t); void flushTransPaintCurve(TransInfo *t); +void restoreMirrorPoseBones(TransDataContainer *tc); void restoreBones(TransDataContainer *tc); /*********************** transform_gizmo.c ********** */ diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c index 6c1da5ae825..6964783d7c4 100644 --- a/source/blender/editors/transform/transform_conversions.c +++ b/source/blender/editors/transform/transform_conversions.c @@ -1201,6 +1201,74 @@ static short pose_grab_with_ik(Main *bmain, Object *ob) 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. */ @@ -1225,6 +1293,8 @@ static void createTransPose(TransInfo *t) continue; } + const bool mirror = ((arm->flag & ARM_MIRROR_EDIT) != 0); + /* set flags and count total */ tc->data_len = count_set_pose_transflags(ob, t->mode, t->around, has_translate_rotate); if (tc->data_len == 0) { @@ -1247,6 +1317,25 @@ static void createTransPose(TransInfo *t) 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 */ @@ -1269,6 +1358,19 @@ static void createTransPose(TransInfo *t) short ik_on = 0; 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 */ /* init trans data */ @@ -1285,6 +1387,15 @@ static void createTransPose(TransInfo *t) for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { if (pchan->bone->flag & BONE_TRANSFORM) { 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++; } } @@ -1304,6 +1415,27 @@ static void createTransPose(TransInfo *t) 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) { bArmature *arm; diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index a840c04ab5a..05bb9785112 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -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 */ static void recalcData_objects(TransInfo *t) { @@ -992,6 +1031,19 @@ static void recalcData_objects(TransInfo *t) Object *ob = tc->poseobj; 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, * check if the auto-record feature means that we should record 'samples' * (i.e. un-editable animation values) |