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:
authorGermano Cavalcante <germano.costa@ig.com.br>2020-06-10 02:27:40 +0300
committerGermano Cavalcante <germano.costa@ig.com.br>2020-06-10 02:27:51 +0300
commit46e0ec05ef1fb75fb3e5b4a713a5302ace49e546 (patch)
tree8eff449f33050c445ee3367c941da57b8b61882e /source/blender/editors/transform/transform_convert_armature.c
parent63a40ed422bc61e9cd952282a636c53a9d8f3fde (diff)
Cleanup: Move each special_aftertrans_update to their respective TransData file
Diffstat (limited to 'source/blender/editors/transform/transform_convert_armature.c')
-rw-r--r--source/blender/editors/transform/transform_convert_armature.c844
1 files changed, 668 insertions, 176 deletions
diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c
index 92c79dc2b03..75b51b3d2c4 100644
--- a/source/blender/editors/transform/transform_convert_armature.c
+++ b/source/blender/editors/transform/transform_convert_armature.c
@@ -29,8 +29,10 @@
#include "BLI_ghash.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
+#include "BLI_string.h"
#include "BKE_action.h"
+#include "BKE_animsys.h"
#include "BKE_armature.h"
#include "BKE_constraint.h"
#include "BKE_context.h"
@@ -45,6 +47,8 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
+#include "RNA_access.h"
+
#include "transform.h"
#include "transform_snap.h"
@@ -63,204 +67,219 @@ typedef struct BoneInitData {
float zwidth;
} BoneInitData;
-static bConstraint *add_temporary_ik_constraint(bPoseChannel *pchan,
- bKinematicConstraint *targetless_con)
+/* Return if we need to update motion paths, only if they already exist,
+ * and we will insert a keyframe at the end of transform. */
+static bool motionpath_need_update_pose(Scene *scene, Object *ob)
{
- bConstraint *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);
-
- bKinematicConstraint *temp_con_data = con->data;
-
- if (targetless_con) {
- /* if exists, use values from last targetless (but disabled) IK-constraint as base */
- *temp_con_data = *targetless_con;
- }
- else {
- temp_con_data->flag = CONSTRAINT_IK_TIP;
+ if (autokeyframe_cfra_can_key(scene, &ob->id)) {
+ return (ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) != 0;
}
- temp_con_data->flag |= CONSTRAINT_IK_TEMP | CONSTRAINT_IK_AUTO | CONSTRAINT_IK_POS;
-
- return con;
-}
-
-static void update_deg_with_temporary_ik(Main *bmain, Object *ob)
-{
- BIK_clear_data(ob->pose);
- /* TODO(sergey): Consider doing partial update only. */
- DEG_relations_tag_update(bmain);
+ return false;
}
-static void add_pose_transdata(
- TransInfo *t, bPoseChannel *pchan, Object *ob, TransDataContainer *tc, TransData *td)
+/**
+ * Auto-keyframing feature - for poses/pose-channels
+ *
+ * \param tmode: A transform mode.
+ *
+ * targetless_ik: has targetless ik been done on any channels?
+ *
+ * \note Context may not always be available,
+ * so must check before using it as it's a luxury for a few cases.
+ */
+static void autokeyframe_pose(
+ bContext *C, Scene *scene, Object *ob, int tmode, short targetless_ik)
{
- 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;
- }
+ Main *bmain = CTX_data_main(C);
+ ID *id = &ob->id;
+ AnimData *adt = ob->adt;
+ bAction *act = (adt) ? adt->action : NULL;
+ bPose *pose = ob->pose;
+ bPoseChannel *pchan;
+ FCurve *fcu;
- if (bone->flag & BONE_TRANSFORM_CHILD) {
- td->flag |= TD_NOCENTER;
- td->flag |= TD_NO_LOC;
+ // TODO: this should probably be done per channel instead...
+ if (!autokeyframe_cfra_can_key(scene, id)) {
+ /* tag channels that should have unkeyed data */
+ for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
+ if (pchan->bone->flag & BONE_TRANSFORM) {
+ /* tag this channel */
+ pchan->bone->flag |= BONE_UNKEYED;
+ }
+ }
+ return;
}
- td->extra = pchan;
- 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;
+ ReportList *reports = CTX_wm_reports(C);
+ ToolSettings *ts = scene->toolsettings;
+ KeyingSet *active_ks = ANIM_scene_get_active_keyingset(scene);
+ ListBase nla_cache = {NULL, NULL};
+ float cfra = (float)CFRA;
+ eInsertKeyFlags flag = 0;
+
+ /* flag is initialized from UserPref keyframing settings
+ * - special exception for targetless IK - INSERTKEY_MATRIX keyframes should get
+ * visual keyframes even if flag not set, as it's not that useful otherwise
+ * (for quick animation recording)
+ */
+ flag = ANIM_get_keyframing_flags(scene, true);
- copy_v3_v3(td->ext->irot, pchan->eul);
+ if (targetless_ik) {
+ flag |= INSERTKEY_MATRIX;
}
- 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;
+ for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
+ if ((pchan->bone->flag & BONE_TRANSFORM) == 0 &&
+ !((pose->flag & POSE_MIRROR_EDIT) && (pchan->bone->flag & BONE_TRANSFORM_MIRROR))) {
+ continue;
+ }
- copy_qt_qt(td->ext->iquat, pchan->quat);
- }
- td->ext->rotOrder = pchan->rotmode;
+ ListBase dsources = {NULL, NULL};
- /* proper way to get parent transform + own transform + constraints transform */
- copy_m3_m4(omat, ob->obmat);
+ /* clear any 'unkeyed' flag it may have */
+ pchan->bone->flag &= ~BONE_UNKEYED;
- /* New code, using "generic" BKE_bone_parent_transform_calc_from_pchan(). */
- {
- BoneParentTransform bpt;
- float rpmat[3][3];
+ /* add datasource override for the camera object */
+ ANIM_relative_keyingset_add_source(&dsources, id, &RNA_PoseBone, pchan);
- 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);
+ /* only insert into active keyingset? */
+ if (IS_AUTOKEY_FLAG(scene, ONLYKEYINGSET) && (active_ks)) {
+ /* run the active Keying Set on the current datasource */
+ ANIM_apply_keyingset(C, &dsources, NULL, active_ks, MODIFYKEY_MODE_INSERT, cfra);
}
+ /* only insert into available channels? */
+ else if (IS_AUTOKEY_FLAG(scene, INSERTAVAIL)) {
+ if (act) {
+ for (fcu = act->curves.first; fcu; fcu = fcu->next) {
+ /* only insert keyframes for this F-Curve if it affects the current bone */
+ if (strstr(fcu->rna_path, "bones") == NULL) {
+ continue;
+ }
+ char *pchanName = BLI_str_quoted_substrN(fcu->rna_path, "bones[");
+
+ /* only if bone name matches too...
+ * NOTE: this will do constraints too, but those are ok to do here too?
+ */
+ if (pchanName && STREQ(pchanName, pchan->name)) {
+ insert_keyframe(bmain,
+ reports,
+ id,
+ act,
+ ((fcu->grp) ? (fcu->grp->name) : (NULL)),
+ fcu->rna_path,
+ fcu->array_index,
+ cfra,
+ ts->keyframe_type,
+ &nla_cache,
+ flag);
+ }
- /* 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);
+ if (pchanName) {
+ MEM_freeN(pchanName);
+ }
+ }
+ }
}
- invert_m3_m3(td->ext->r_smtx, td->ext->r_mtx);
- }
+ /* only insert keyframe if needed? */
+ else if (IS_AUTOKEY_FLAG(scene, INSERTNEEDED)) {
+ bool do_loc = false, do_rot = false, do_scale = false;
+
+ /* Filter the conditions when this happens
+ * (assume that 'curarea->spacetype == SPACE_VIEW3D'). */
+ if (tmode == TFM_TRANSLATION) {
+ if (targetless_ik) {
+ do_rot = true;
+ }
+ else {
+ do_loc = true;
+ }
+ }
+ else if (ELEM(tmode, TFM_ROTATION, TFM_TRACKBALL)) {
+ if (ELEM(scene->toolsettings->transform_pivot_point,
+ V3D_AROUND_CURSOR,
+ V3D_AROUND_ACTIVE)) {
+ do_loc = true;
+ }
- pseudoinverse_m3_m3(td->smtx, td->mtx, PSEUDOINVERSE_EPSILON);
+ if ((scene->toolsettings->transform_flag & SCE_XFORM_AXIS_ALIGN) == 0) {
+ do_rot = true;
+ }
+ }
+ else if (tmode == TFM_RESIZE) {
+ if (ELEM(scene->toolsettings->transform_pivot_point,
+ V3D_AROUND_CURSOR,
+ V3D_AROUND_ACTIVE)) {
+ do_loc = true;
+ }
- /* 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);
+ if ((scene->toolsettings->transform_flag & SCE_XFORM_AXIS_ALIGN) == 0) {
+ do_scale = true;
+ }
+ }
+
+ if (do_loc) {
+ KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOCATION_ID);
+ ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra);
+ }
+ if (do_rot) {
+ KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_ROTATION_ID);
+ ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra);
+ }
+ if (do_scale) {
+ KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_SCALING_ID);
+ ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra);
+ }
}
+ /* insert keyframe in all (transform) channels */
else {
- td->flag |= TD_PBONE_LOCAL_MTX_P;
+ KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOC_ROT_SCALE_ID);
+ ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra);
}
+
+ /* free temp info */
+ BLI_freelistN(&dsources);
}
- /* 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);
+ BKE_animsys_free_nla_keyframing_context_cache(&nla_cache);
+}
- if (ELEM(t->mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) {
- bArmature *arm = tc->poseobj->data;
+static bConstraint *add_temporary_ik_constraint(bPoseChannel *pchan,
+ bKinematicConstraint *targetless_con)
+{
+ bConstraint *con = BKE_constraint_add_for_pose(
+ NULL, pchan, "TempConstraint", CONSTRAINT_TYPE_KINEMATIC);
- 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;
- }
- }
+ /* for draw, but also for detecting while pose solving */
+ pchan->constflag |= (PCHAN_HAS_IK | PCHAN_HAS_TARGET);
- /* 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);
+ bKinematicConstraint *temp_con_data = con->data;
- data->flag |= CONSTRAINT_IK_AUTO;
+ if (targetless_con) {
+ /* if exists, use values from last targetless (but disabled) IK-constraint as base */
+ *temp_con_data = *targetless_con;
+ }
+ else {
+ temp_con_data->flag = CONSTRAINT_IK_TIP;
+ }
- /* Add a temporary auto IK constraint here, as we will only temporarily active this
- * targetless bone during transform. (Targetless IK constraints are treated as if they are
- * disabled unless they are transformed). */
- add_temporary_ik_constraint(pchan, data);
- Main *bmain = CTX_data_main(t->context);
- update_deg_with_temporary_ik(bmain, ob);
+ temp_con_data->flag |= CONSTRAINT_IK_TEMP | CONSTRAINT_IK_AUTO | CONSTRAINT_IK_POS;
- /* only object matrix correction */
- copy_m3_m3(td->mtx, omat);
- pseudoinverse_m3_m3(td->smtx, td->mtx, PSEUDOINVERSE_EPSILON);
- }
- }
+ return con;
+}
- /* store reference to first constraint */
- td->con = pchan->constraints.first;
+static void update_deg_with_temporary_ik(Main *bmain, Object *ob)
+{
+ BIK_clear_data(ob->pose);
+ /* TODO(sergey): Consider doing partial update only. */
+ DEG_relations_tag_update(bmain);
}
/* -------------------------------------------------------------------- */
/** \name Pose Auto-IK
* \{ */
-bKinematicConstraint *has_targetless_ik(bPoseChannel *pchan)
+static bKinematicConstraint *has_targetless_ik(bPoseChannel *pchan)
{
bConstraint *con = pchan->constraints.first;
@@ -528,33 +547,173 @@ static void pose_mirror_info_init(PoseInitData_Mirror *pid,
}
}
-static void pose_mirror_info_restore(const PoseInitData_Mirror *pid)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Convert Armature
+ * \{ */
+
+static void add_pose_transdata(
+ TransInfo *t, bPoseChannel *pchan, Object *ob, TransDataContainer *tc, TransData *td)
{
- 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;
+ 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->extra = pchan;
+ 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) {
- copy_v3_v3(pchan->eul, pid->orig.eul);
+ 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) {
- copy_v3_v3(pchan->rotAxis, pid->orig.axis_angle);
- pchan->rotAngle = pid->orig.axis_angle[3];
+ 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 {
- copy_qt_qt(pchan->quat, pid->orig.quat);
+ 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);
-/* -------------------------------------------------------------------- */
-/** \name Convert Armature
- * \{ */
+ /* 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;
+
+ /* Add a temporary auto IK constraint here, as we will only temporarily active this
+ * targetless bone during transform. (Targetless IK constraints are treated as if they are
+ * disabled unless they are transformed). */
+ add_temporary_ik_constraint(pchan, data);
+ Main *bmain = CTX_data_main(t->context);
+ update_deg_with_temporary_ik(bmain, ob);
+
+ /* 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;
+}
/**
* When objects array is NULL, use 't->data_container' as is.
@@ -1203,6 +1362,28 @@ static void pose_transform_mirror_update(TransInfo *t, TransDataContainer *tc, O
}
}
+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);
+ }
+}
+
static void restoreMirrorPoseBones(TransDataContainer *tc)
{
bPose *pose = tc->poseobj->pose;
@@ -1296,3 +1477,314 @@ void recalcData_pose(TransInfo *t)
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Special After Transform Pose
+ * \{ */
+
+static void bone_children_clear_transflag(int mode, short around, ListBase *lb)
+{
+ Bone *bone = lb->first;
+
+ for (; bone; bone = bone->next) {
+ if ((bone->flag & BONE_HINGE) && (bone->flag & BONE_CONNECTED)) {
+ bone->flag |= BONE_HINGE_CHILD_TRANSFORM;
+ }
+ else if ((bone->flag & BONE_TRANSFORM) && (mode == TFM_ROTATION || mode == TFM_TRACKBALL) &&
+ (around == V3D_AROUND_LOCAL_ORIGINS)) {
+ bone->flag |= BONE_TRANSFORM_CHILD;
+ }
+ else {
+ bone->flag &= ~BONE_TRANSFORM;
+ }
+
+ bone_children_clear_transflag(mode, around, &bone->childbase);
+ }
+}
+
+/* Sets transform flags in the bones.
+ * Returns total number of bones with `BONE_TRANSFORM`. */
+int transform_convert_pose_transflags_update(Object *ob,
+ const int mode,
+ const short around,
+ bool has_translate_rotate[2])
+{
+ bArmature *arm = ob->data;
+ bPoseChannel *pchan;
+ Bone *bone;
+ int total = 0;
+
+ for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
+ bone = pchan->bone;
+ 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 */
+ /* since pchans are depsgraph sorted, the parents are in beginning of list */
+ if (!ELEM(mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) {
+ for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
+ bone = pchan->bone;
+ if (bone->flag & BONE_TRANSFORM) {
+ bone_children_clear_transflag(mode, around, &bone->childbase);
+ }
+ }
+ }
+ /* now count, and check if we have autoIK or have to switch from translate to rotate */
+ for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
+ bone = pchan->bone;
+ if (bone->flag & BONE_TRANSFORM) {
+ total++;
+
+ if (has_translate_rotate != NULL) {
+ if (has_targetless_ik(pchan) == NULL) {
+ if (pchan->parent && (pchan->bone->flag & BONE_CONNECTED)) {
+ if (pchan->bone->flag & BONE_HINGE_CHILD_TRANSFORM) {
+ has_translate_rotate[0] = true;
+ }
+ }
+ else {
+ if ((pchan->protectflag & OB_LOCK_LOC) != OB_LOCK_LOC) {
+ has_translate_rotate[0] = true;
+ }
+ }
+ if ((pchan->protectflag & OB_LOCK_ROT) != OB_LOCK_ROT) {
+ has_translate_rotate[1] = true;
+ }
+ }
+ else {
+ has_translate_rotate[0] = true;
+ }
+ }
+ }
+ }
+
+ return total;
+}
+
+static short apply_targetless_ik(Object *ob)
+{
+ bPoseChannel *pchan, *parchan, *chanlist[256];
+ bKinematicConstraint *data;
+ int segcount, apply = 0;
+
+ /* now we got a difficult situation... we have to find the
+ * target-less IK pchans, and apply transformation to the all
+ * pchans that were in the chain */
+
+ for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
+ data = has_targetless_ik(pchan);
+ if (data && (data->flag & CONSTRAINT_IK_AUTO)) {
+
+ /* fill the array with the bones of the chain (armature.c does same, keep it synced) */
+ segcount = 0;
+
+ /* exclude tip from chain? */
+ if (!(data->flag & CONSTRAINT_IK_TIP)) {
+ parchan = pchan->parent;
+ }
+ else {
+ parchan = pchan;
+ }
+
+ /* Find the chain's root & count the segments needed */
+ for (; parchan; parchan = parchan->parent) {
+ chanlist[segcount] = parchan;
+ segcount++;
+
+ if (segcount == data->rootbone || segcount > 255) {
+ break; // 255 is weak
+ }
+ }
+ for (; segcount; segcount--) {
+ Bone *bone;
+ float mat[4][4];
+
+ /* pose_mat(b) = pose_mat(b-1) * offs_bone * channel * constraint * IK */
+ /* we put in channel the entire result of mat = (channel * constraint * IK) */
+ /* pose_mat(b) = pose_mat(b-1) * offs_bone * mat */
+ /* mat = pose_mat(b) * inv(pose_mat(b-1) * offs_bone ) */
+
+ parchan = chanlist[segcount - 1];
+ bone = parchan->bone;
+ bone->flag |= BONE_TRANSFORM; /* ensures it gets an auto key inserted */
+
+ BKE_armature_mat_pose_to_bone(parchan, parchan->pose_mat, mat);
+ /* apply and decompose, doesn't work for constraints or non-uniform scale well */
+ {
+ float rmat3[3][3], qrmat[3][3], imat3[3][3], smat[3][3];
+
+ copy_m3_m4(rmat3, mat);
+ /* Make sure that our rotation matrix only contains rotation and not scale. */
+ normalize_m3(rmat3);
+
+ /* rotation */
+ /* [#22409] is partially caused by this, as slight numeric error introduced during
+ * the solving process leads to locked-axis values changing. However, we cannot modify
+ * the values here, or else there are huge discrepancies between IK-solver (interactive)
+ * and applied poses. */
+ BKE_pchan_mat3_to_rot(parchan, rmat3, false);
+
+ /* for size, remove rotation */
+ /* causes problems with some constraints (so apply only if needed) */
+ if (data->flag & CONSTRAINT_IK_STRETCH) {
+ BKE_pchan_rot_to_mat3(parchan, qrmat);
+ invert_m3_m3(imat3, qrmat);
+ mul_m3_m3m3(smat, rmat3, imat3);
+ mat3_to_size(parchan->size, smat);
+ }
+
+ /* causes problems with some constraints (e.g. childof), so disable this */
+ /* as it is IK shouldn't affect location directly */
+ /* copy_v3_v3(parchan->loc, mat[3]); */
+ }
+ }
+
+ apply = 1;
+ data->flag &= ~CONSTRAINT_IK_AUTO;
+ }
+ }
+
+ return apply;
+}
+
+/* frees temporal IKs */
+static void pose_grab_with_ik_clear(Main *bmain, Object *ob)
+{
+ bKinematicConstraint *data;
+ bPoseChannel *pchan;
+ bConstraint *con, *next;
+ bool relations_changed = false;
+
+ for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
+ /* clear all temporary lock flags */
+ pchan->ikflag &= ~(BONE_IK_NO_XDOF_TEMP | BONE_IK_NO_YDOF_TEMP | BONE_IK_NO_ZDOF_TEMP);
+
+ pchan->constflag &= ~(PCHAN_HAS_IK | PCHAN_HAS_TARGET);
+
+ /* remove all temporary IK-constraints added */
+ for (con = pchan->constraints.first; con; con = next) {
+ next = con->next;
+ if (con->type == CONSTRAINT_TYPE_KINEMATIC) {
+ data = con->data;
+ if (data->flag & CONSTRAINT_IK_TEMP) {
+ relations_changed = true;
+
+ /* iTaSC needs clear for removed constraints */
+ BIK_clear_data(ob->pose);
+
+ BLI_remlink(&pchan->constraints, con);
+ MEM_freeN(con->data);
+ MEM_freeN(con);
+ continue;
+ }
+ pchan->constflag |= PCHAN_HAS_IK;
+ if (data->tar == NULL || (data->tar->type == OB_ARMATURE && data->subtarget[0] == 0)) {
+ pchan->constflag |= PCHAN_HAS_TARGET;
+ }
+ }
+ }
+ }
+
+ if (relations_changed) {
+ /* TODO(sergey): Consider doing partial update only. */
+ DEG_relations_tag_update(bmain);
+ }
+}
+
+void special_aftertrans_update__pose(bContext *C, TransInfo *t)
+{
+ Object *ob;
+
+ if (t->mode == TFM_BONESIZE) {
+ /* Handle the exception where for TFM_BONESIZE in edit mode we pretend to be
+ * in pose mode (to use bone orientation matrix),
+ * in that case we don't do operations like auto-keyframing. */
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ ob = tc->poseobj;
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ }
+ }
+ else {
+ const bool canceled = (t->state == TRANS_CANCEL);
+ GSet *motionpath_updates = BLI_gset_ptr_new("motionpath updates");
+
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+
+ bPoseChannel *pchan;
+ short targetless_ik = 0;
+
+ ob = tc->poseobj;
+
+ if ((t->flag & T_AUTOIK) && (t->options & CTX_AUTOCONFIRM)) {
+ /* when running transform non-interactively (operator exec),
+ * we need to update the pose otherwise no updates get called during
+ * transform and the auto-ik is not applied. see [#26164] */
+ struct Object *pose_ob = tc->poseobj;
+ BKE_pose_where_is(t->depsgraph, t->scene, pose_ob);
+ }
+
+ /* set BONE_TRANSFORM flags for autokey, gizmo draw might have changed them */
+ if (!canceled && (t->mode != TFM_DUMMY)) {
+ transform_convert_pose_transflags_update(ob, t->mode, t->around, NULL);
+ }
+
+ /* if target-less IK grabbing, we calculate the pchan transforms and clear flag */
+ if (!canceled && t->mode == TFM_TRANSLATION) {
+ targetless_ik = apply_targetless_ik(ob);
+ }
+ else {
+ /* not forget to clear the auto flag */
+ for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
+ bKinematicConstraint *data = has_targetless_ik(pchan);
+ if (data) {
+ data->flag &= ~CONSTRAINT_IK_AUTO;
+ }
+ }
+ }
+
+ if (t->mode == TFM_TRANSLATION) {
+ struct Main *bmain = CTX_data_main(t->context);
+ pose_grab_with_ik_clear(bmain, ob);
+ }
+
+ /* automatic inserting of keys and unkeyed tagging -
+ * only if transform wasn't canceled (or TFM_DUMMY) */
+ if (!canceled && (t->mode != TFM_DUMMY)) {
+ autokeyframe_pose(C, t->scene, ob, t->mode, targetless_ik);
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ }
+ else {
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ }
+
+ if (t->mode != TFM_DUMMY && motionpath_need_update_pose(t->scene, ob)) {
+ BLI_gset_insert(motionpath_updates, ob);
+ }
+ }
+
+ /* Update motion paths once for all transformed bones in an object. */
+ GSetIterator gs_iter;
+ GSET_ITER (gs_iter, motionpath_updates) {
+ const ePosePathCalcRange range = canceled ? POSE_PATH_CALC_RANGE_CURRENT_FRAME :
+ POSE_PATH_CALC_RANGE_CHANGED;
+ ob = BLI_gsetIterator_getKey(&gs_iter);
+ ED_pose_recalculate_paths(C, t->scene, ob, range);
+ }
+ BLI_gset_free(motionpath_updates, NULL);
+ }
+}
+
+/** \} */