Transform: Pose: Partial support for Auto IK + X-Mirror
Fix T69572 TODO: support `Relative-Mirror` as well. Maniphest Tasks: T69572 Differential Revision: https://developer.blender.org/D5862
This commit is contained in:
parent
2e06a6bec3
commit
de530a95dc
Notes:
blender-bot
2023-02-14 11:24:03 +01:00
Referenced by commit08048f7cce
, Fix T75810: Child bone frozen when both Auto IK and X-Axis mirror are Referenced by commita6a9a12e8f
, Fix T75142: No autokeying with pose mode X-Mirror Referenced by issue #74032, Pose Mode X-Axis Mirror is not working properly with IK when skeleton is moved Referenced by issue #69572, Auto IK fails with X-Axis Mirror
|
@ -313,7 +313,7 @@ class VIEW3D_PT_tools_posemode_options(View3DPanel, Panel):
|
|||
layout.prop(pose, "use_auto_ik")
|
||||
layout.prop(pose, "use_mirror_x")
|
||||
col = layout.column()
|
||||
col.active = pose.use_mirror_x
|
||||
col.active = pose.use_mirror_x and not pose.use_auto_ik
|
||||
col.prop(pose, "use_mirror_relative")
|
||||
|
||||
layout.label(text="Affect Only")
|
||||
|
|
|
@ -450,20 +450,15 @@ int count_set_pose_transflags(Object *ob,
|
|||
|
||||
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
|
||||
bone = pchan->bone;
|
||||
bone->flag &= ~(BONE_TRANSFORM | BONE_TRANSFORM_MIRROR);
|
||||
if (PBONE_VISIBLE(arm, bone)) {
|
||||
if ((bone->flag & BONE_SELECTED)) {
|
||||
bone->flag |= BONE_TRANSFORM;
|
||||
}
|
||||
else {
|
||||
bone->flag &= ~BONE_TRANSFORM;
|
||||
}
|
||||
|
||||
bone->flag &= ~BONE_HINGE_CHILD_TRANSFORM;
|
||||
bone->flag &= ~BONE_TRANSFORM_CHILD;
|
||||
}
|
||||
else {
|
||||
bone->flag &= ~BONE_TRANSFORM;
|
||||
}
|
||||
}
|
||||
|
||||
/* make sure no bone can be transformed when a parent is transformed */
|
||||
|
|
|
@ -86,6 +86,7 @@ static void add_pose_transdata(
|
|||
td->flag |= TD_NO_LOC;
|
||||
}
|
||||
|
||||
td->extra = pchan;
|
||||
td->protectflag = pchan->protectflag;
|
||||
|
||||
td->loc = pchan->loc;
|
||||
|
@ -364,7 +365,7 @@ static short pose_grab_with_ik(Main *bmain, Object *ob)
|
|||
* (but they must be selected, and only one ik-solver per chain should get added) */
|
||||
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
|
||||
if (pchan->bone->layer & arm->layer) {
|
||||
if (pchan->bone->flag & BONE_SELECTED) {
|
||||
if (pchan->bone->flag & (BONE_SELECTED | BONE_TRANSFORM_MIRROR)) {
|
||||
/* Rule: no IK for solitatry (unconnected) bones */
|
||||
for (bonec = pchan->bone->childbase.first; bonec; bonec = bonec->next) {
|
||||
if (bonec->flag & BONE_CONNECTED) {
|
||||
|
@ -379,7 +380,7 @@ static short pose_grab_with_ik(Main *bmain, Object *ob)
|
|||
if (pchan->parent) {
|
||||
/* only adds if there's no IK yet (and no parent bone was selected) */
|
||||
for (parent = pchan->parent; parent; parent = parent->parent) {
|
||||
if (parent->bone->flag & BONE_SELECTED) {
|
||||
if (parent->bone->flag & (BONE_SELECTED | BONE_TRANSFORM_MIRROR)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -513,14 +514,6 @@ void createTransPose(TransInfo *t)
|
|||
}
|
||||
}
|
||||
|
||||
/* do we need to add temporal IK chains? */
|
||||
if ((pose->flag & POSE_AUTO_IK) && t->mode == TFM_TRANSLATION) {
|
||||
if (pose_grab_with_ik(bmain, ob)) {
|
||||
t->flag |= T_AUTOIK;
|
||||
has_translate_rotate[0] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (mirror) {
|
||||
int total_mirrored = 0;
|
||||
for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
|
||||
|
@ -541,16 +534,6 @@ void createTransPose(TransInfo *t)
|
|||
}
|
||||
}
|
||||
|
||||
/* if there are no translatable bones, do rotation */
|
||||
if ((t->mode == TFM_TRANSLATION) && !has_translate_rotate[0]) {
|
||||
if (has_translate_rotate[1]) {
|
||||
t->mode = TFM_ROTATION;
|
||||
}
|
||||
else {
|
||||
t->mode = TFM_RESIZE;
|
||||
}
|
||||
}
|
||||
|
||||
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
||||
if (tc->data_len == 0) {
|
||||
continue;
|
||||
|
@ -582,20 +565,32 @@ void createTransPose(TransInfo *t)
|
|||
td->val = NULL;
|
||||
}
|
||||
|
||||
if (mirror) {
|
||||
for (bPoseChannel *pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
|
||||
if (pchan->bone->flag & BONE_TRANSFORM) {
|
||||
bPoseChannel *pchan_mirror = BKE_pose_channel_get_mirrored(ob->pose, pchan->name);
|
||||
if (pchan_mirror) {
|
||||
pchan_mirror->bone->flag |= BONE_TRANSFORM_MIRROR;
|
||||
pose_mirror_info_init(&pid[pid_index], pchan_mirror, pchan, is_mirror_relative);
|
||||
pid_index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* do we need to add temporal IK chains? */
|
||||
if ((pose->flag & POSE_AUTO_IK) && t->mode == TFM_TRANSLATION) {
|
||||
if (pose_grab_with_ik(bmain, ob)) {
|
||||
t->flag |= T_AUTOIK;
|
||||
has_translate_rotate[0] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* use pose channels to fill trans data */
|
||||
td = tc->data;
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
@ -603,10 +598,20 @@ void createTransPose(TransInfo *t)
|
|||
if (td != (tc->data + tc->data_len)) {
|
||||
BKE_report(t->reports, RPT_DEBUG, "Bone selection count error");
|
||||
}
|
||||
}
|
||||
|
||||
/* initialize initial auto=ik chainlen's? */
|
||||
if (t->flag & T_AUTOIK) {
|
||||
transform_autoik_update(t, 0);
|
||||
/* initialize initial auto=ik chainlen's? */
|
||||
if (t->flag & T_AUTOIK) {
|
||||
transform_autoik_update(t, 0);
|
||||
}
|
||||
|
||||
/* if there are no translatable bones, do rotation */
|
||||
if ((t->mode == TFM_TRANSLATION) && !has_translate_rotate[0]) {
|
||||
if (has_translate_rotate[1]) {
|
||||
t->mode = TFM_ROTATION;
|
||||
}
|
||||
else {
|
||||
t->mode = TFM_RESIZE;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "DNA_anim_types.h"
|
||||
#include "DNA_armature_types.h"
|
||||
#include "DNA_brush_types.h"
|
||||
#include "DNA_constraint_types.h"
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_lattice_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
|
@ -785,50 +786,61 @@ 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.
|
||||
* \param pid: Optional, apply relative transform when set (has no effect on mirrored bones).
|
||||
*/
|
||||
static void pose_transform_mirror_update(Object *ob, PoseInitData_Mirror *pid)
|
||||
static void pose_transform_mirror_update(TransInfo *t,
|
||||
TransDataContainer *tc,
|
||||
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) {
|
||||
/* Clear the MIRROR flag from previous runs */
|
||||
pchan_orig->bone->flag &= ~BONE_TRANSFORM_MIRROR;
|
||||
}
|
||||
TransData *td = tc->data;
|
||||
for (int i = tc->data_len; i--; td++) {
|
||||
bPoseChannel *pchan_orig = td->extra;
|
||||
BLI_assert(pchan_orig->bone->flag & BONE_TRANSFORM);
|
||||
/* No layer check, correct mirror is more important. */
|
||||
bPoseChannel *pchan = BKE_pose_channel_get_mirrored(ob->pose, pchan_orig->name);
|
||||
if (pchan == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
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);
|
||||
/* Also do bbone scaling. */
|
||||
pchan->bone->xwidth = pchan_orig->bone->xwidth;
|
||||
pchan->bone->zwidth = pchan_orig->bone->zwidth;
|
||||
|
||||
if (pchan) {
|
||||
/* also do bbone scaling */
|
||||
pchan->bone->xwidth = pchan_orig->bone->xwidth;
|
||||
pchan->bone->zwidth = pchan_orig->bone->zwidth;
|
||||
/* 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?
|
||||
|
||||
/* 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);
|
||||
}
|
||||
BKE_pchan_apply_mat4(pchan, pchan_mtx_final, false);
|
||||
|
||||
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);
|
||||
|
||||
/* set flag to let autokeyframe know to keyframe the mirrred bone */
|
||||
pchan->bone->flag |= BONE_TRANSFORM_MIRROR;
|
||||
/* In this case we can do target-less IK grabbing. */
|
||||
if (t->mode == TFM_TRANSLATION) {
|
||||
bKinematicConstraint *data = has_targetless_ik(pchan);
|
||||
if (data == NULL) {
|
||||
continue;
|
||||
}
|
||||
mul_v3_m4v3(data->grabtarget, flip_mtx, td->loc);
|
||||
if (pid) {
|
||||
/* TODO(germano): Realitve Mirror support */
|
||||
}
|
||||
data->flag |= CONSTRAINT_IK_AUTO;
|
||||
}
|
||||
|
||||
if (pid) {
|
||||
pid++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1045,7 +1057,7 @@ static void recalcData_objects(TransInfo *t)
|
|||
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
||||
bPose *pose = ob->pose;
|
||||
if (arm->flag & ARM_MIRROR_EDIT || pose->flag & POSE_MIRROR_EDIT) {
|
||||
pose_transform_mirror_update(ob, NULL);
|
||||
pose_transform_mirror_update(t, tc, ob, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1063,7 +1075,7 @@ static void recalcData_objects(TransInfo *t)
|
|||
if (pose->flag & POSE_MIRROR_RELATIVE) {
|
||||
pid = tc->custom.type.data;
|
||||
}
|
||||
pose_transform_mirror_update(ob, pid);
|
||||
pose_transform_mirror_update(t, tc, ob, pid);
|
||||
}
|
||||
else {
|
||||
restoreMirrorPoseBones(tc);
|
||||
|
|
|
@ -413,7 +413,7 @@ static int count_bone_select(bArmature *arm, ListBase *lb, const bool do_it)
|
|||
int total = 0;
|
||||
|
||||
for (bone = lb->first; bone; bone = bone->next) {
|
||||
bone->flag &= ~BONE_TRANSFORM;
|
||||
bone->flag &= ~(BONE_TRANSFORM | BONE_TRANSFORM_MIRROR);
|
||||
do_next = do_it;
|
||||
if (do_it) {
|
||||
if (bone->layer & arm->layer) {
|
||||
|
|
|
@ -1663,7 +1663,9 @@ static void rna_def_pose(BlenderRNA *brna)
|
|||
prop = RNA_def_property(srna, "use_mirror_relative", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", POSE_MIRROR_RELATIVE);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Relative Mirror", "Apply relative transformations in X-mirror mode");
|
||||
prop,
|
||||
"Relative Mirror",
|
||||
"Apply relative transformations in X-mirror mode (not supported with Auto IK)");
|
||||
RNA_def_struct_path_func(srna, "rna_Pose_path");
|
||||
RNA_def_property_update(prop, 0, "rna_Pose_update");
|
||||
RNA_def_property_flag(prop, PROP_LIB_EXCEPTION);
|
||||
|
|
Loading…
Reference in New Issue