Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/transform/transform_conversions_armature.c
- This file was added.
| /* | |||||
| * 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 "BLI_listbase.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_conversions.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; | |||||
| } | |||||
| } | |||||
| } | |||||