Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py8
-rw-r--r--source/blender/blenloader/intern/versioning_280.c2
-rw-r--r--source/blender/editors/transform/transform.h25
-rw-r--r--source/blender/editors/transform/transform_conversions.c132
-rw-r--r--source/blender/editors/transform/transform_generics.c52
-rw-r--r--source/blender/makesdna/DNA_armature_types.h2
-rw-r--r--source/blender/makesrna/intern/rna_armature.c7
7 files changed, 224 insertions, 4 deletions
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index 81804f6a399..d672984ca49 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -192,9 +192,13 @@ class VIEW3D_PT_tools_posemode_options(View3DPanel, Panel):
def draw(self, context):
arm = context.active_object.data
+ layout = self.layout
- self.layout.prop(arm, "use_auto_ik")
- self.layout.prop(arm, "use_mirror_x")
+ layout.prop(arm, "use_auto_ik")
+ 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 ****************
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index d8d100db022..afbb512fe2f 100644
--- a/source/blender/blenloader/intern/versioning_280.c
+++ b/source/blender/blenloader/intern/versioning_280.c
@@ -3009,7 +3009,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
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);
}
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)
diff --git a/source/blender/makesdna/DNA_armature_types.h b/source/blender/makesdna/DNA_armature_types.h
index 483ff3483d3..fd40459503e 100644
--- a/source/blender/makesdna/DNA_armature_types.h
+++ b/source/blender/makesdna/DNA_armature_types.h
@@ -144,7 +144,7 @@ typedef enum eArmature_Flag {
ARM_POSEMODE = (1 << 4),
ARM_FLAG_UNUSED_5 = (1 << 5), /* cleared */
ARM_DELAYDEFORM = (1 << 6),
- ARM_FLAG_UNUSED_7 = (1 << 7), /* cleared */
+ ARM_MIRROR_RELATIVE = (1 << 7),
ARM_MIRROR_EDIT = (1 << 8),
ARM_AUTO_IK = (1 << 9),
/** made option negative, for backwards compat */
diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.c
index 5461aaa0f1a..fe197faecce 100644
--- a/source/blender/makesrna/intern/rna_armature.c
+++ b/source/blender/makesrna/intern/rna_armature.c
@@ -1359,6 +1359,13 @@ static void rna_def_armature(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_Armature_redraw_data");
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);
RNA_def_property_boolean_sdna(prop, NULL, "flag", ARM_AUTO_IK);
RNA_def_property_ui_text(