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:
Diffstat (limited to 'source/blender/editors/transform/transform_convert_armature.c')
-rw-r--r--source/blender/editors/transform/transform_convert_armature.c933
1 files changed, 933 insertions, 0 deletions
diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c
new file mode 100644
index 00000000000..cc023688c8e
--- /dev/null
+++ b/source/blender/editors/transform/transform_convert_armature.c
@@ -0,0 +1,933 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup edtransform
+ */
+
+#include "DNA_armature_types.h"
+#include "DNA_constraint_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+
+#include "BKE_action.h"
+#include "BKE_armature.h"
+#include "BKE_constraint.h"
+#include "BKE_context.h"
+#include "BKE_main.h"
+#include "BKE_report.h"
+
+#include "BIK_api.h"
+
+#include "ED_armature.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
+
+#include "transform.h"
+#include "transform_convert.h"
+
+bKinematicConstraint *has_targetless_ik(bPoseChannel *pchan)
+{
+ bConstraint *con = pchan->constraints.first;
+
+ for (; con; con = con->next) {
+ if (con->type == CONSTRAINT_TYPE_KINEMATIC && (con->enforce != 0.0f)) {
+ bKinematicConstraint *data = con->data;
+
+ if (data->tar == NULL) {
+ return data;
+ }
+ if (data->tar->type == OB_ARMATURE && data->subtarget[0] == 0) {
+ return data;
+ }
+ }
+ }
+ return NULL;
+}
+
+static void add_pose_transdata(
+ TransInfo *t, bPoseChannel *pchan, Object *ob, TransDataContainer *tc, TransData *td)
+{
+ Bone *bone = pchan->bone;
+ float pmat[3][3], omat[3][3];
+ float cmat[3][3], tmat[3][3];
+ float vec[3];
+
+ copy_v3_v3(vec, pchan->pose_mat[3]);
+ copy_v3_v3(td->center, vec);
+
+ td->ob = ob;
+ td->flag = TD_SELECTED;
+ if (bone->flag & BONE_HINGE_CHILD_TRANSFORM) {
+ td->flag |= TD_NOCENTER;
+ }
+
+ if (bone->flag & BONE_TRANSFORM_CHILD) {
+ td->flag |= TD_NOCENTER;
+ td->flag |= TD_NO_LOC;
+ }
+
+ td->protectflag = pchan->protectflag;
+
+ td->loc = pchan->loc;
+ copy_v3_v3(td->iloc, pchan->loc);
+
+ td->ext->size = pchan->size;
+ copy_v3_v3(td->ext->isize, pchan->size);
+
+ if (pchan->rotmode > 0) {
+ td->ext->rot = pchan->eul;
+ td->ext->rotAxis = NULL;
+ td->ext->rotAngle = NULL;
+ td->ext->quat = NULL;
+
+ copy_v3_v3(td->ext->irot, pchan->eul);
+ }
+ else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
+ td->ext->rot = NULL;
+ td->ext->rotAxis = pchan->rotAxis;
+ td->ext->rotAngle = &pchan->rotAngle;
+ td->ext->quat = NULL;
+
+ td->ext->irotAngle = pchan->rotAngle;
+ copy_v3_v3(td->ext->irotAxis, pchan->rotAxis);
+ }
+ else {
+ td->ext->rot = NULL;
+ td->ext->rotAxis = NULL;
+ td->ext->rotAngle = NULL;
+ td->ext->quat = pchan->quat;
+
+ copy_qt_qt(td->ext->iquat, pchan->quat);
+ }
+ td->ext->rotOrder = pchan->rotmode;
+
+ /* proper way to get parent transform + own transform + constraints transform */
+ copy_m3_m4(omat, ob->obmat);
+
+ /* New code, using "generic" BKE_bone_parent_transform_calc_from_pchan(). */
+ {
+ BoneParentTransform bpt;
+ float rpmat[3][3];
+
+ BKE_bone_parent_transform_calc_from_pchan(pchan, &bpt);
+ if (t->mode == TFM_TRANSLATION) {
+ copy_m3_m4(pmat, bpt.loc_mat);
+ }
+ else {
+ copy_m3_m4(pmat, bpt.rotscale_mat);
+ }
+
+ /* Grrr! Exceptional case: When translating pose bones that are either Hinge or NoLocal,
+ * and want align snapping, we just need both loc_mat and rotscale_mat.
+ * So simply always store rotscale mat in td->ext, and always use it to apply rotations...
+ * Ugly to need such hacks! :/ */
+ copy_m3_m4(rpmat, bpt.rotscale_mat);
+
+ if (constraints_list_needinv(t, &pchan->constraints)) {
+ copy_m3_m4(tmat, pchan->constinv);
+ invert_m3_m3(cmat, tmat);
+ mul_m3_series(td->mtx, cmat, omat, pmat);
+ mul_m3_series(td->ext->r_mtx, cmat, omat, rpmat);
+ }
+ else {
+ mul_m3_series(td->mtx, omat, pmat);
+ mul_m3_series(td->ext->r_mtx, omat, rpmat);
+ }
+ invert_m3_m3(td->ext->r_smtx, td->ext->r_mtx);
+ }
+
+ pseudoinverse_m3_m3(td->smtx, td->mtx, PSEUDOINVERSE_EPSILON);
+
+ /* exceptional case: rotate the pose bone which also applies transformation
+ * when a parentless bone has BONE_NO_LOCAL_LOCATION [] */
+ if (!ELEM(t->mode, TFM_TRANSLATION, TFM_RESIZE) &&
+ (pchan->bone->flag & BONE_NO_LOCAL_LOCATION)) {
+ if (pchan->parent) {
+ /* same as td->smtx but without pchan->bone->bone_mat */
+ td->flag |= TD_PBONE_LOCAL_MTX_C;
+ mul_m3_m3m3(td->ext->l_smtx, pchan->bone->bone_mat, td->smtx);
+ }
+ else {
+ td->flag |= TD_PBONE_LOCAL_MTX_P;
+ }
+ }
+
+ /* for axismat we use bone's own transform */
+ copy_m3_m4(pmat, pchan->pose_mat);
+ mul_m3_m3m3(td->axismtx, omat, pmat);
+ normalize_m3(td->axismtx);
+
+ if (ELEM(t->mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) {
+ bArmature *arm = tc->poseobj->data;
+
+ if ((t->mode == TFM_BONE_ENVELOPE_DIST) || (arm->drawtype == ARM_ENVELOPE)) {
+ td->loc = NULL;
+ td->val = &bone->dist;
+ td->ival = bone->dist;
+ }
+ else {
+ // abusive storage of scale in the loc pointer :)
+ td->loc = &bone->xwidth;
+ copy_v3_v3(td->iloc, td->loc);
+ td->val = NULL;
+ }
+ }
+
+ /* in this case we can do target-less IK grabbing */
+ if (t->mode == TFM_TRANSLATION) {
+ bKinematicConstraint *data = has_targetless_ik(pchan);
+ if (data) {
+ if (data->flag & CONSTRAINT_IK_TIP) {
+ copy_v3_v3(data->grabtarget, pchan->pose_tail);
+ }
+ else {
+ copy_v3_v3(data->grabtarget, pchan->pose_head);
+ }
+ td->loc = data->grabtarget;
+ copy_v3_v3(td->iloc, td->loc);
+ data->flag |= CONSTRAINT_IK_AUTO;
+
+ /* only object matrix correction */
+ copy_m3_m3(td->mtx, omat);
+ pseudoinverse_m3_m3(td->smtx, td->mtx, PSEUDOINVERSE_EPSILON);
+ }
+ }
+
+ /* store reference to first constraint */
+ td->con = pchan->constraints.first;
+}
+
+/* adds the IK to pchan - returns if added */
+static short pose_grab_with_ik_add(bPoseChannel *pchan)
+{
+ bKinematicConstraint *targetless = NULL;
+ bKinematicConstraint *data;
+ bConstraint *con;
+
+ /* Sanity check */
+ if (pchan == NULL) {
+ return 0;
+ }
+
+ /* Rule: not if there's already an IK on this channel */
+ for (con = pchan->constraints.first; con; con = con->next) {
+ if (con->type == CONSTRAINT_TYPE_KINEMATIC) {
+ data = con->data;
+
+ if (data->tar == NULL || (data->tar->type == OB_ARMATURE && data->subtarget[0] == '\0')) {
+ /* make reference to constraint to base things off later
+ * (if it's the last targetless constraint encountered) */
+ targetless = (bKinematicConstraint *)con->data;
+
+ /* but, if this is a targetless IK, we make it auto anyway (for the children loop) */
+ if (con->enforce != 0.0f) {
+ data->flag |= CONSTRAINT_IK_AUTO;
+
+ /* if no chain length has been specified,
+ * just make things obey standard rotation locks too */
+ if (data->rootbone == 0) {
+ for (; pchan; pchan = pchan->parent) {
+ /* here, we set ik-settings for bone from pchan->protectflag */
+ // XXX: careful with quats/axis-angle rotations where we're locking 4d components
+ if (pchan->protectflag & OB_LOCK_ROTX) {
+ pchan->ikflag |= BONE_IK_NO_XDOF_TEMP;
+ }
+ if (pchan->protectflag & OB_LOCK_ROTY) {
+ pchan->ikflag |= BONE_IK_NO_YDOF_TEMP;
+ }
+ if (pchan->protectflag & OB_LOCK_ROTZ) {
+ pchan->ikflag |= BONE_IK_NO_ZDOF_TEMP;
+ }
+ }
+ }
+
+ return 0;
+ }
+ }
+
+ if ((con->flag & CONSTRAINT_DISABLE) == 0 && (con->enforce != 0.0f)) {
+ return 0;
+ }
+ }
+ }
+
+ con = BKE_constraint_add_for_pose(NULL, pchan, "TempConstraint", CONSTRAINT_TYPE_KINEMATIC);
+
+ /* for draw, but also for detecting while pose solving */
+ pchan->constflag |= (PCHAN_HAS_IK | PCHAN_HAS_TARGET);
+
+ data = con->data;
+ if (targetless) {
+ /* if exists, use values from last targetless (but disabled) IK-constraint as base */
+ *data = *targetless;
+ }
+ else {
+ data->flag = CONSTRAINT_IK_TIP;
+ }
+ data->flag |= CONSTRAINT_IK_TEMP | CONSTRAINT_IK_AUTO | CONSTRAINT_IK_POS;
+ copy_v3_v3(data->grabtarget, pchan->pose_tail);
+
+ /* watch-it! has to be 0 here, since we're still on the
+ * same bone for the first time through the loop T25885. */
+ data->rootbone = 0;
+
+ /* we only include bones that are part of a continual connected chain */
+ do {
+ /* here, we set ik-settings for bone from pchan->protectflag */
+ // XXX: careful with quats/axis-angle rotations where we're locking 4d components
+ if (pchan->protectflag & OB_LOCK_ROTX) {
+ pchan->ikflag |= BONE_IK_NO_XDOF_TEMP;
+ }
+ if (pchan->protectflag & OB_LOCK_ROTY) {
+ pchan->ikflag |= BONE_IK_NO_YDOF_TEMP;
+ }
+ if (pchan->protectflag & OB_LOCK_ROTZ) {
+ pchan->ikflag |= BONE_IK_NO_ZDOF_TEMP;
+ }
+
+ /* now we count this pchan as being included */
+ data->rootbone++;
+
+ /* continue to parent, but only if we're connected to it */
+ if (pchan->bone->flag & BONE_CONNECTED) {
+ pchan = pchan->parent;
+ }
+ else {
+ pchan = NULL;
+ }
+ } while (pchan);
+
+ /* make a copy of maximum chain-length */
+ data->max_rootbone = data->rootbone;
+
+ return 1;
+}
+
+/* bone is a candidate to get IK, but we don't do it if it has children connected */
+static short pose_grab_with_ik_children(bPose *pose, Bone *bone)
+{
+ Bone *bonec;
+ short wentdeeper = 0, added = 0;
+
+ /* go deeper if children & children are connected */
+ for (bonec = bone->childbase.first; bonec; bonec = bonec->next) {
+ if (bonec->flag & BONE_CONNECTED) {
+ wentdeeper = 1;
+ added += pose_grab_with_ik_children(pose, bonec);
+ }
+ }
+ if (wentdeeper == 0) {
+ bPoseChannel *pchan = BKE_pose_channel_find_name(pose, bone->name);
+ if (pchan) {
+ added += pose_grab_with_ik_add(pchan);
+ }
+ }
+
+ return added;
+}
+
+/* main call which adds temporal IK chains */
+static short pose_grab_with_ik(Main *bmain, Object *ob)
+{
+ bArmature *arm;
+ bPoseChannel *pchan, *parent;
+ Bone *bonec;
+ short tot_ik = 0;
+
+ if ((ob == NULL) || (ob->pose == NULL) || (ob->mode & OB_MODE_POSE) == 0) {
+ return 0;
+ }
+
+ arm = ob->data;
+
+ /* Rule: allow multiple Bones
+ * (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) {
+ /* Rule: no IK for solitatry (unconnected) bones */
+ for (bonec = pchan->bone->childbase.first; bonec; bonec = bonec->next) {
+ if (bonec->flag & BONE_CONNECTED) {
+ break;
+ }
+ }
+ if ((pchan->bone->flag & BONE_CONNECTED) == 0 && (bonec == NULL)) {
+ continue;
+ }
+
+ /* rule: if selected Bone is not a root bone, it gets a temporal IK */
+ 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) {
+ break;
+ }
+ }
+ if (parent == NULL) {
+ tot_ik += pose_grab_with_ik_add(pchan);
+ }
+ }
+ else {
+ /* rule: go over the children and add IK to the tips */
+ tot_ik += pose_grab_with_ik_children(ob->pose, pchan->bone);
+ }
+ }
+ }
+ }
+
+ /* iTaSC needs clear for new IK constraints */
+ if (tot_ik) {
+ BIK_clear_data(ob->pose);
+ /* TODO(sergey): Consider doing partial update only. */
+ DEG_relations_tag_update(bmain);
+ }
+
+ 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.
+ */
+void createTransPose(TransInfo *t)
+{
+ Main *bmain = CTX_data_main(t->context);
+
+ t->data_len_all = 0;
+
+ bool has_translate_rotate_buf[2] = {false, false};
+ bool *has_translate_rotate = (t->mode == TFM_TRANSLATION) ? has_translate_rotate_buf : NULL;
+
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ Object *ob = tc->poseobj;
+ bPose *pose = ob->pose;
+
+ bArmature *arm;
+
+ /* check validity of state */
+ arm = BKE_armature_from_object(tc->poseobj);
+ if ((arm == NULL) || (pose == NULL)) {
+ continue;
+ }
+
+ const bool mirror = ((pose->flag & POSE_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) {
+ continue;
+ }
+
+ if (arm->flag & ARM_RESTPOS) {
+ if (ELEM(t->mode, TFM_DUMMY, TFM_BONESIZE) == 0) {
+ BKE_report(t->reports, RPT_ERROR, "Cannot change Pose when 'Rest Position' is enabled");
+ tc->data_len = 0;
+ continue;
+ }
+ }
+
+ /* 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) {
+ 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 ((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;
+ }
+ Object *ob = tc->poseobj;
+ TransData *td;
+ TransDataExtension *tdx;
+ int i;
+
+ PoseInitData_Mirror *pid = tc->custom.type.data;
+ int pid_index = 0;
+ bPose *pose = ob->pose;
+
+ if (pose == NULL) {
+ continue;
+ }
+
+ const bool mirror = ((pose->flag & POSE_MIRROR_EDIT) != 0);
+ const bool is_mirror_relative = ((pose->flag & POSE_MIRROR_RELATIVE) != 0);
+
+ tc->poseobj = ob; /* we also allow non-active objects to be transformed, in weightpaint */
+
+ /* init trans data */
+ td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransPoseBone");
+ tdx = tc->data_ext = MEM_callocN(tc->data_len * sizeof(TransDataExtension),
+ "TransPoseBoneExt");
+ for (i = 0; i < tc->data_len; i++, td++, tdx++) {
+ td->ext = tdx;
+ td->val = NULL;
+ }
+
+ /* 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++;
+ }
+ }
+
+ 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);
+ }
+ }
+
+ t->flag |= T_POSE;
+ /* disable PET, its not usable in pose mode yet [#32444] */
+ t->flag &= ~T_PROP_EDIT_ALL;
+}
+
+void restoreMirrorPoseBones(TransDataContainer *tc)
+{
+ bPose *pose = tc->poseobj->pose;
+
+ if (!(pose->flag & POSE_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;
+ BoneInitData *bid = tc->custom.type.data;
+ EditBone *ebo;
+
+ if (tc->obedit) {
+ arm = tc->obedit->data;
+ }
+ else {
+ BLI_assert(tc->poseobj != NULL);
+ arm = tc->poseobj->data;
+ }
+
+ while (bid->bone) {
+ ebo = bid->bone;
+
+ ebo->dist = bid->dist;
+ ebo->rad_head = bid->rad_head;
+ ebo->rad_tail = bid->rad_tail;
+ ebo->roll = bid->roll;
+ ebo->xwidth = bid->xwidth;
+ ebo->zwidth = bid->zwidth;
+ copy_v3_v3(ebo->head, bid->head);
+ copy_v3_v3(ebo->tail, bid->tail);
+
+ if (arm->flag & ARM_MIRROR_EDIT) {
+ EditBone *ebo_child;
+
+ /* Also move connected ebo_child, in case ebo_child's name aren't mirrored properly */
+ for (ebo_child = arm->edbo->first; ebo_child; ebo_child = ebo_child->next) {
+ if ((ebo_child->flag & BONE_CONNECTED) && (ebo_child->parent == ebo)) {
+ copy_v3_v3(ebo_child->head, ebo->tail);
+ ebo_child->rad_head = ebo->rad_tail;
+ }
+ }
+
+ /* Also move connected parent, in case parent's name isn't mirrored properly */
+ if ((ebo->flag & BONE_CONNECTED) && ebo->parent) {
+ EditBone *parent = ebo->parent;
+ copy_v3_v3(parent->tail, ebo->head);
+ parent->rad_tail = ebo->rad_head;
+ }
+ }
+
+ bid++;
+ }
+}
+
+/* ********************* armature ************** */
+void createTransArmatureVerts(TransInfo *t)
+{
+ t->data_len_all = 0;
+
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ EditBone *ebo, *eboflip;
+ bArmature *arm = tc->obedit->data;
+ ListBase *edbo = arm->edbo;
+ bool mirror = ((arm->flag & ARM_MIRROR_EDIT) != 0);
+ int total_mirrored = 0;
+
+ tc->data_len = 0;
+ for (ebo = edbo->first; ebo; ebo = ebo->next) {
+ const int data_len_prev = tc->data_len;
+
+ if (EBONE_VISIBLE(arm, ebo) && !(ebo->flag & BONE_EDITMODE_LOCKED)) {
+ if (ELEM(t->mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) {
+ if (ebo->flag & BONE_SELECTED) {
+ tc->data_len++;
+ }
+ }
+ else if (t->mode == TFM_BONE_ROLL) {
+ if (ebo->flag & BONE_SELECTED) {
+ tc->data_len++;
+ }
+ }
+ else {
+ if (ebo->flag & BONE_TIPSEL) {
+ tc->data_len++;
+ }
+ if (ebo->flag & BONE_ROOTSEL) {
+ tc->data_len++;
+ }
+ }
+ }
+
+ if (mirror && (data_len_prev < tc->data_len)) {
+ eboflip = ED_armature_ebone_get_mirrored(arm->edbo, ebo);
+ if (eboflip) {
+ total_mirrored++;
+ }
+ }
+ }
+ if (!tc->data_len) {
+ continue;
+ }
+
+ if (mirror) {
+ BoneInitData *bid = MEM_mallocN((total_mirrored + 1) * sizeof(BoneInitData), "BoneInitData");
+
+ /* trick to terminate iteration */
+ bid[total_mirrored].bone = NULL;
+
+ tc->custom.type.data = bid;
+ tc->custom.type.use_free = true;
+ }
+ t->data_len_all += tc->data_len;
+ }
+
+ transform_around_single_fallback(t);
+ t->data_len_all = -1;
+
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ if (!tc->data_len) {
+ continue;
+ }
+
+ EditBone *ebo, *eboflip;
+ bArmature *arm = tc->obedit->data;
+ ListBase *edbo = arm->edbo;
+ TransData *td, *td_old;
+ float mtx[3][3], smtx[3][3], bonemat[3][3];
+ bool mirror = ((arm->flag & ARM_MIRROR_EDIT) != 0);
+ BoneInitData *bid = tc->custom.type.data;
+
+ copy_m3_m4(mtx, tc->obedit->obmat);
+ pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON);
+
+ td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransEditBone");
+ int i = 0;
+
+ for (ebo = edbo->first; ebo; ebo = ebo->next) {
+ td_old = td;
+ ebo->oldlength =
+ ebo->length; // length==0.0 on extrude, used for scaling radius of bone points
+
+ if (EBONE_VISIBLE(arm, ebo) && !(ebo->flag & BONE_EDITMODE_LOCKED)) {
+ if (t->mode == TFM_BONE_ENVELOPE) {
+ if (ebo->flag & BONE_ROOTSEL) {
+ td->val = &ebo->rad_head;
+ td->ival = *td->val;
+
+ copy_v3_v3(td->center, ebo->head);
+ td->flag = TD_SELECTED;
+
+ copy_m3_m3(td->smtx, smtx);
+ copy_m3_m3(td->mtx, mtx);
+
+ td->loc = NULL;
+ td->ext = NULL;
+ td->ob = tc->obedit;
+
+ td++;
+ }
+ if (ebo->flag & BONE_TIPSEL) {
+ td->val = &ebo->rad_tail;
+ td->ival = *td->val;
+ copy_v3_v3(td->center, ebo->tail);
+ td->flag = TD_SELECTED;
+
+ copy_m3_m3(td->smtx, smtx);
+ copy_m3_m3(td->mtx, mtx);
+
+ td->loc = NULL;
+ td->ext = NULL;
+ td->ob = tc->obedit;
+
+ td++;
+ }
+ }
+ else if (ELEM(t->mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) {
+ if (ebo->flag & BONE_SELECTED) {
+ if ((t->mode == TFM_BONE_ENVELOPE_DIST) || (arm->drawtype == ARM_ENVELOPE)) {
+ td->loc = NULL;
+ td->val = &ebo->dist;
+ td->ival = ebo->dist;
+ }
+ else {
+ // abusive storage of scale in the loc pointer :)
+ td->loc = &ebo->xwidth;
+ copy_v3_v3(td->iloc, td->loc);
+ td->val = NULL;
+ }
+ copy_v3_v3(td->center, ebo->head);
+ td->flag = TD_SELECTED;
+
+ /* use local bone matrix */
+ ED_armature_ebone_to_mat3(ebo, bonemat);
+ mul_m3_m3m3(td->mtx, mtx, bonemat);
+ invert_m3_m3(td->smtx, td->mtx);
+
+ copy_m3_m3(td->axismtx, td->mtx);
+ normalize_m3(td->axismtx);
+
+ td->ext = NULL;
+ td->ob = tc->obedit;
+
+ td++;
+ }
+ }
+ else if (t->mode == TFM_BONE_ROLL) {
+ if (ebo->flag & BONE_SELECTED) {
+ td->loc = NULL;
+ td->val = &(ebo->roll);
+ td->ival = ebo->roll;
+
+ copy_v3_v3(td->center, ebo->head);
+ td->flag = TD_SELECTED;
+
+ td->ext = NULL;
+ td->ob = tc->obedit;
+
+ td++;
+ }
+ }
+ else {
+ if (ebo->flag & BONE_TIPSEL) {
+ copy_v3_v3(td->iloc, ebo->tail);
+
+ /* Don't allow single selected tips to have a modified center,
+ * causes problem with snapping (see T45974).
+ * However, in rotation mode, we want to keep that 'rotate bone around root with
+ * only its tip selected' behavior (see T46325). */
+ if ((t->around == V3D_AROUND_LOCAL_ORIGINS) &&
+ ((t->mode == TFM_ROTATION) || (ebo->flag & BONE_ROOTSEL))) {
+ copy_v3_v3(td->center, ebo->head);
+ }
+ else {
+ copy_v3_v3(td->center, td->iloc);
+ }
+
+ td->loc = ebo->tail;
+ td->flag = TD_SELECTED;
+ if (ebo->flag & BONE_EDITMODE_LOCKED) {
+ td->protectflag = OB_LOCK_LOC | OB_LOCK_ROT | OB_LOCK_SCALE;
+ }
+
+ copy_m3_m3(td->smtx, smtx);
+ copy_m3_m3(td->mtx, mtx);
+
+ ED_armature_ebone_to_mat3(ebo, td->axismtx);
+
+ if ((ebo->flag & BONE_ROOTSEL) == 0) {
+ td->extra = ebo;
+ td->ival = ebo->roll;
+ }
+
+ td->ext = NULL;
+ td->val = NULL;
+ td->ob = tc->obedit;
+
+ td++;
+ }
+ if (ebo->flag & BONE_ROOTSEL) {
+ copy_v3_v3(td->iloc, ebo->head);
+ copy_v3_v3(td->center, td->iloc);
+ td->loc = ebo->head;
+ td->flag = TD_SELECTED;
+ if (ebo->flag & BONE_EDITMODE_LOCKED) {
+ td->protectflag = OB_LOCK_LOC | OB_LOCK_ROT | OB_LOCK_SCALE;
+ }
+
+ copy_m3_m3(td->smtx, smtx);
+ copy_m3_m3(td->mtx, mtx);
+
+ ED_armature_ebone_to_mat3(ebo, td->axismtx);
+
+ td->extra = ebo; /* to fix roll */
+ td->ival = ebo->roll;
+
+ td->ext = NULL;
+ td->val = NULL;
+ td->ob = tc->obedit;
+
+ td++;
+ }
+ }
+ }
+
+ if (mirror && (td_old != td)) {
+ eboflip = ED_armature_ebone_get_mirrored(arm->edbo, ebo);
+ if (eboflip) {
+ bid[i].bone = eboflip;
+ bid[i].dist = eboflip->dist;
+ bid[i].rad_head = eboflip->rad_head;
+ bid[i].rad_tail = eboflip->rad_tail;
+ bid[i].roll = eboflip->roll;
+ bid[i].xwidth = eboflip->xwidth;
+ bid[i].zwidth = eboflip->zwidth;
+ copy_v3_v3(bid[i].head, eboflip->head);
+ copy_v3_v3(bid[i].tail, eboflip->tail);
+ i++;
+ }
+ }
+ }
+
+ if (mirror) {
+ /* trick to terminate iteration */
+ BLI_assert(i + 1 == (MEM_allocN_len(bid) / sizeof(*bid)));
+ bid[i].bone = NULL;
+ }
+ }
+}