diff options
author | Germano Cavalcante <germano.costa@ig.com.br> | 2020-06-10 02:27:40 +0300 |
---|---|---|
committer | Germano Cavalcante <germano.costa@ig.com.br> | 2020-06-10 02:27:51 +0300 |
commit | 46e0ec05ef1fb75fb3e5b4a713a5302ace49e546 (patch) | |
tree | 8eff449f33050c445ee3367c941da57b8b61882e /source | |
parent | 63a40ed422bc61e9cd952282a636c53a9d8f3fde (diff) |
Cleanup: Move each special_aftertrans_update to their respective TransData file
Diffstat (limited to 'source')
12 files changed, 1819 insertions, 1760 deletions
diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index d71843346f1..fcca9b06451 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -22,66 +22,36 @@ */ #include "DNA_anim_types.h" -#include "DNA_armature_types.h" #include "DNA_constraint_types.h" -#include "DNA_gpencil_types.h" -#include "DNA_mask_types.h" -#include "DNA_space_types.h" #include "MEM_guardedalloc.h" #include "BLI_kdtree.h" #include "BLI_listbase.h" #include "BLI_math.h" -#include "BLI_string.h" #include "BKE_action.h" #include "BKE_anim_data.h" -#include "BKE_animsys.h" -#include "BKE_armature.h" #include "BKE_context.h" -#include "BKE_editmesh.h" #include "BKE_fcurve.h" #include "BKE_global.h" -#include "BKE_gpencil.h" -#include "BKE_key.h" #include "BKE_layer.h" #include "BKE_lib_id.h" #include "BKE_main.h" -#include "BKE_mask.h" #include "BKE_modifier.h" #include "BKE_nla.h" -#include "BKE_node.h" -#include "BKE_pointcache.h" -#include "BKE_rigidbody.h" #include "BKE_scene.h" -#include "BKE_tracking.h" -#include "BIK_api.h" - -#include "ED_anim_api.h" -#include "ED_armature.h" -#include "ED_clip.h" -#include "ED_image.h" #include "ED_keyframes_edit.h" #include "ED_keyframing.h" -#include "ED_markers.h" -#include "ED_mask.h" -#include "ED_mesh.h" -#include "ED_node.h" -#include "ED_object.h" #include "ED_particle.h" #include "ED_screen.h" #include "ED_screen_types.h" #include "UI_view2d.h" -#include "WM_api.h" /* for WM_event_add_notifier to deal with stabilization nodes */ #include "WM_types.h" -#include "RNA_access.h" - -#include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" #include "transform.h" @@ -376,190 +346,6 @@ static void set_prop_dist(TransInfo *t, const bool with_dist) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Pose Mode - * \{ */ - -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; -} - -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; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Pose Mode (Auto-IK) * \{ */ @@ -642,50 +428,6 @@ void transform_autoik_update(TransInfo *t, short mode) } } -/* 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); - } -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -900,92 +642,6 @@ bool FrameOnMouseSide(char side, float frame, float cframe) /** \name Animation Editor * \{ */ -static int masklay_shape_cmp_frame(void *thunk, const void *a, const void *b) -{ - const MaskLayerShape *frame_a = a; - const MaskLayerShape *frame_b = b; - - if (frame_a->frame < frame_b->frame) { - return -1; - } - if (frame_a->frame > frame_b->frame) { - return 1; - } - *((bool *)thunk) = true; - /* selected last */ - if ((frame_a->flag & MASK_SHAPE_SELECT) && ((frame_b->flag & MASK_SHAPE_SELECT) == 0)) { - return 1; - } - return 0; -} - -/* Called by special_aftertrans_update to make sure selected gp-frames replace - * any other gp-frames which may reside on that frame (that are not selected). - * It also makes sure gp-frames are still stored in chronological order after - * transform. - */ -static void posttrans_gpd_clean(bGPdata *gpd) -{ - bGPDlayer *gpl; - - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - bGPDframe *gpf, *gpfn; - bool is_double = false; - - BKE_gpencil_layer_frames_sort(gpl, &is_double); - - if (is_double) { - for (gpf = gpl->frames.first; gpf; gpf = gpfn) { - gpfn = gpf->next; - if (gpfn && gpf->framenum == gpfn->framenum) { - BKE_gpencil_layer_frame_delete(gpl, gpf); - } - } - } - -#ifdef DEBUG - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { - BLI_assert(!gpf->next || gpf->framenum < gpf->next->framenum); - } -#endif - } - /* set cache flag to dirty */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - - WM_main_add_notifier(NC_GPENCIL | NA_EDITED, gpd); -} - -static void posttrans_mask_clean(Mask *mask) -{ - MaskLayer *masklay; - - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { - MaskLayerShape *masklay_shape, *masklay_shape_next; - bool is_double = false; - - BLI_listbase_sort_r(&masklay->splines_shapes, masklay_shape_cmp_frame, &is_double); - - if (is_double) { - for (masklay_shape = masklay->splines_shapes.first; masklay_shape; - masklay_shape = masklay_shape_next) { - masklay_shape_next = masklay_shape->next; - if (masklay_shape_next && masklay_shape->frame == masklay_shape_next->frame) { - BKE_mask_layer_shape_unlink(masklay, masklay_shape); - } - } - } - -#ifdef DEBUG - for (masklay_shape = masklay->splines_shapes.first; masklay_shape; - masklay_shape = masklay_shape->next) { - BLI_assert(!masklay_shape->next || masklay_shape->frame < masklay_shape->next->frame); - } -#endif - } - - WM_main_add_notifier(NC_MASK | NA_EDITED, mask); -} - /* Time + Average value */ typedef struct tRetainedKeyframe { struct tRetainedKeyframe *next, *prev; @@ -1004,9 +660,7 @@ typedef struct tRetainedKeyframe { * but may want to use a different one at times (if caller does not operate on * selection). */ -static void posttrans_fcurve_clean(FCurve *fcu, - const eBezTriple_Flag sel_flag, - const bool use_handle) +void posttrans_fcurve_clean(FCurve *fcu, const int sel_flag, const bool use_handle) { /* NOTE: We assume that all keys are sorted */ ListBase retained_keys = {NULL, NULL}; @@ -1124,259 +778,12 @@ static void posttrans_fcurve_clean(FCurve *fcu, BLI_freelistN(&retained_keys); } -/* Called by special_aftertrans_update to make sure selected keyframes replace - * any other keyframes which may reside on that frame (that is not selected). - * remake_action_ipos should have already been called - */ -static void posttrans_action_clean(bAnimContext *ac, bAction *act) -{ - ListBase anim_data = {NULL, NULL}; - bAnimListElem *ale; - int filter; - - /* filter data */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/); - ANIM_animdata_filter(ac, &anim_data, filter, act, ANIMCONT_ACTION); - - /* loop through relevant data, removing keyframes as appropriate - * - all keyframes are converted in/out of global time - */ - for (ale = anim_data.first; ale; ale = ale->next) { - AnimData *adt = ANIM_nla_mapping_get(ac, ale); - - if (adt) { - ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0); - posttrans_fcurve_clean(ale->key_data, SELECT, false); /* only use handles in graph editor */ - ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0); - } - else { - posttrans_fcurve_clean(ale->key_data, SELECT, false); /* only use handles in graph editor */ - } - } - - /* free temp data */ - ANIM_animdata_freelist(&anim_data); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Graph Editor - * \{ */ - -/* struct for use in re-sorting BezTriples during Graph Editor transform */ -typedef struct BeztMap { - BezTriple *bezt; - uint oldIndex; /* index of bezt in fcu->bezt array before sorting */ - uint newIndex; /* index of bezt in fcu->bezt array after sorting */ - short swapHs; /* swap order of handles (-1=clear; 0=not checked, 1=swap) */ - char pipo, cipo; /* interpolation of current and next segments */ -} BeztMap; - -/* This function converts an FCurve's BezTriple array to a BeztMap array - * NOTE: this allocates memory that will need to get freed later - */ -static BeztMap *bezt_to_beztmaps(BezTriple *bezts, int totvert) -{ - BezTriple *bezt = bezts; - BezTriple *prevbezt = NULL; - BeztMap *bezm, *bezms; - int i; - - /* allocate memory for this array */ - if (totvert == 0 || bezts == NULL) { - return NULL; - } - bezm = bezms = MEM_callocN(sizeof(BeztMap) * totvert, "BeztMaps"); - - /* assign beztriples to beztmaps */ - for (i = 0; i < totvert; i++, bezm++, prevbezt = bezt, bezt++) { - bezm->bezt = bezt; - - bezm->oldIndex = i; - bezm->newIndex = i; - - bezm->pipo = (prevbezt) ? prevbezt->ipo : bezt->ipo; - bezm->cipo = bezt->ipo; - } - - return bezms; -} - -/* This function copies the code of sort_time_ipocurve, but acts on BeztMap structs instead */ -static void sort_time_beztmaps(BeztMap *bezms, int totvert) -{ - BeztMap *bezm; - int i, ok = 1; - - /* keep repeating the process until nothing is out of place anymore */ - while (ok) { - ok = 0; - - bezm = bezms; - i = totvert; - while (i--) { - /* is current bezm out of order (i.e. occurs later than next)? */ - if (i > 0) { - if (bezm->bezt->vec[1][0] > (bezm + 1)->bezt->vec[1][0]) { - bezm->newIndex++; - (bezm + 1)->newIndex--; - - SWAP(BeztMap, *bezm, *(bezm + 1)); - - ok = 1; - } - } - - /* do we need to check if the handles need to be swapped? - * optimization: this only needs to be performed in the first loop - */ - if (bezm->swapHs == 0) { - if ((bezm->bezt->vec[0][0] > bezm->bezt->vec[1][0]) && - (bezm->bezt->vec[2][0] < bezm->bezt->vec[1][0])) { - /* handles need to be swapped */ - bezm->swapHs = 1; - } - else { - /* handles need to be cleared */ - bezm->swapHs = -1; - } - } - - bezm++; - } - } -} - -/* This function firstly adjusts the pointers that the transdata has to each BezTriple */ -static void beztmap_to_data(TransInfo *t, FCurve *fcu, BeztMap *bezms, int totvert) -{ - BezTriple *bezts = fcu->bezt; - BeztMap *bezm; - TransData2D *td2d; - TransData *td; - int i, j; - char *adjusted; - - TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); - - /* dynamically allocate an array of chars to mark whether an TransData's - * pointers have been fixed already, so that we don't override ones that are - * already done - */ - adjusted = MEM_callocN(tc->data_len, "beztmap_adjusted_map"); - - /* for each beztmap item, find if it is used anywhere */ - bezm = bezms; - for (i = 0; i < totvert; i++, bezm++) { - /* loop through transdata, testing if we have a hit - * for the handles (vec[0]/vec[2]), we must also check if they need to be swapped... - */ - td2d = tc->data_2d; - td = tc->data; - for (j = 0; j < tc->data_len; j++, td2d++, td++) { - /* skip item if already marked */ - if (adjusted[j] != 0) { - continue; - } - - /* update all transdata pointers, no need to check for selections etc, - * since only points that are really needed were created as transdata - */ - if (td2d->loc2d == bezm->bezt->vec[0]) { - if (bezm->swapHs == 1) { - td2d->loc2d = (bezts + bezm->newIndex)->vec[2]; - } - else { - td2d->loc2d = (bezts + bezm->newIndex)->vec[0]; - } - adjusted[j] = 1; - } - else if (td2d->loc2d == bezm->bezt->vec[2]) { - if (bezm->swapHs == 1) { - td2d->loc2d = (bezts + bezm->newIndex)->vec[0]; - } - else { - td2d->loc2d = (bezts + bezm->newIndex)->vec[2]; - } - adjusted[j] = 1; - } - else if (td2d->loc2d == bezm->bezt->vec[1]) { - td2d->loc2d = (bezts + bezm->newIndex)->vec[1]; - - /* if only control point is selected, the handle pointers need to be updated as well */ - if (td2d->h1) { - td2d->h1 = (bezts + bezm->newIndex)->vec[0]; - } - if (td2d->h2) { - td2d->h2 = (bezts + bezm->newIndex)->vec[2]; - } - - adjusted[j] = 1; - } - - /* the handle type pointer has to be updated too */ - if (adjusted[j] && td->flag & TD_BEZTRIPLE && td->hdata) { - if (bezm->swapHs == 1) { - td->hdata->h1 = &(bezts + bezm->newIndex)->h2; - td->hdata->h2 = &(bezts + bezm->newIndex)->h1; - } - else { - td->hdata->h1 = &(bezts + bezm->newIndex)->h1; - td->hdata->h2 = &(bezts + bezm->newIndex)->h2; - } - } - } - } - - /* free temp memory used for 'adjusted' array */ - MEM_freeN(adjusted); -} - /** \} */ /* -------------------------------------------------------------------- */ /** \name Transform Utilities * \{ */ -/* This function is called by recalcData during the Transform loop to recalculate - * the handles of curves and sort the keyframes so that the curves draw correctly. - * It is only called if some keyframes have moved out of order. - * - * anim_data is the list of channels (F-Curves) retrieved already containing the - * channels to work on. It should not be freed here as it may still need to be used. - */ -void remake_graph_transdata(TransInfo *t, ListBase *anim_data) -{ - SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first; - bAnimListElem *ale; - const bool use_handle = (sipo->flag & SIPO_NOHANDLES) == 0; - - /* sort and reassign verts */ - for (ale = anim_data->first; ale; ale = ale->next) { - FCurve *fcu = (FCurve *)ale->key_data; - - if (fcu->bezt) { - BeztMap *bezm; - - /* adjust transform-data pointers */ - /* note, none of these functions use 'use_handle', it could be removed */ - bezm = bezt_to_beztmaps(fcu->bezt, fcu->totvert); - sort_time_beztmaps(bezm, fcu->totvert); - beztmap_to_data(t, fcu, bezm, fcu->totvert); - - /* free mapping stuff */ - MEM_freeN(bezm); - - /* re-sort actual beztriples (perhaps this could be done using the beztmaps to save time?) */ - sort_time_fcurve(fcu); - - /* make sure handles are all set correctly */ - testhandles_fcurve(fcu, BEZT_FLAG_TEMP_TAG, use_handle); - } - } -} - /* Little helper function for ObjectToTransData used to give certain * constraints (ChildOf, FollowPath, and others that may be added) * inverse corrections for transform, so that they aren't in CrazySpace. @@ -1465,467 +872,9 @@ bool constraints_list_needinv(TransInfo *t, ListBase *list) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Transform (Auto-Keyframing) - * \{ */ - -/** - * Auto-keyframing feature - for objects - * - * \param tmode: A transform mode. - * - * \note Context may not always be available, - * so must check before using it as it's a luxury for a few cases. - */ -void autokeyframe_object(bContext *C, Scene *scene, ViewLayer *view_layer, Object *ob, int tmode) -{ - Main *bmain = CTX_data_main(C); - ID *id = &ob->id; - FCurve *fcu; - - // TODO: this should probably be done per channel instead... - if (autokeyframe_cfra_can_key(scene, id)) { - ReportList *reports = CTX_wm_reports(C); - ToolSettings *ts = scene->toolsettings; - KeyingSet *active_ks = ANIM_scene_get_active_keyingset(scene); - ListBase dsources = {NULL, NULL}; - float cfra = (float)CFRA; // xxx this will do for now - eInsertKeyFlags flag = 0; - - /* Get flags used for inserting keyframes. */ - flag = ANIM_get_keyframing_flags(scene, true); - - /* add datasource override for the object */ - ANIM_relative_keyingset_add_source(&dsources, id, NULL, NULL); - - if (IS_AUTOKEY_FLAG(scene, ONLYKEYINGSET) && (active_ks)) { - /* Only insert into active keyingset - * NOTE: we assume here that the active Keying Set - * does not need to have its iterator overridden. - */ - ANIM_apply_keyingset(C, &dsources, NULL, active_ks, MODIFYKEY_MODE_INSERT, cfra); - } - else if (IS_AUTOKEY_FLAG(scene, INSERTAVAIL)) { - AnimData *adt = ob->adt; - - /* only key on available channels */ - if (adt && adt->action) { - ListBase nla_cache = {NULL, NULL}; - for (fcu = adt->action->curves.first; fcu; fcu = fcu->next) { - insert_keyframe(bmain, - reports, - id, - adt->action, - (fcu->grp ? fcu->grp->name : NULL), - fcu->rna_path, - fcu->array_index, - cfra, - ts->keyframe_type, - &nla_cache, - flag); - } - - BKE_animsys_free_nla_keyframing_context_cache(&nla_cache); - } - } - 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_VIE3D) */ - if (tmode == TFM_TRANSLATION) { - do_loc = true; - } - else if (ELEM(tmode, TFM_ROTATION, TFM_TRACKBALL)) { - if (scene->toolsettings->transform_pivot_point == V3D_AROUND_ACTIVE) { - if (ob != OBACT(view_layer)) { - do_loc = true; - } - } - else if (scene->toolsettings->transform_pivot_point == V3D_AROUND_CURSOR) { - do_loc = true; - } - - if ((scene->toolsettings->transform_flag & SCE_XFORM_AXIS_ALIGN) == 0) { - do_rot = true; - } - } - else if (tmode == TFM_RESIZE) { - if (scene->toolsettings->transform_pivot_point == V3D_AROUND_ACTIVE) { - if (ob != OBACT(view_layer)) { - do_loc = true; - } - } - else if (scene->toolsettings->transform_pivot_point == V3D_AROUND_CURSOR) { - do_loc = true; - } - - if ((scene->toolsettings->transform_flag & SCE_XFORM_AXIS_ALIGN) == 0) { - do_scale = true; - } - } - - /* insert keyframes for the affected sets of channels using the builtin KeyingSets found */ - 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 { - 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); - } -} - -/* Return if we need to update motion paths, only if they already exist, - * and we will insert a keyframe at the end of transform. */ -bool motionpath_need_update_object(Scene *scene, Object *ob) -{ - /* XXX: there's potential here for problems with unkeyed rotations/scale, - * but for now (until proper data-locality for baking operations), - * this should be a better fix for T24451 and T37755 - */ - - if (autokeyframe_cfra_can_key(scene, &ob->id)) { - return (ob->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) != 0; - } - - return false; -} - -/** - * 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. - */ -void autokeyframe_pose(bContext *C, Scene *scene, Object *ob, int tmode, short targetless_ik) -{ - 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; - - // 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; - } - - 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); - - if (targetless_ik) { - flag |= INSERTKEY_MATRIX; - } - - 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; - } - - ListBase dsources = {NULL, NULL}; - - /* clear any 'unkeyed' flag it may have */ - pchan->bone->flag &= ~BONE_UNKEYED; - - /* add datasource override for the camera object */ - ANIM_relative_keyingset_add_source(&dsources, id, &RNA_PoseBone, pchan); - - /* 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); - } - - if (pchanName) { - MEM_freeN(pchanName); - } - } - } - } - /* 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; - } - - 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; - } - - 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 { - 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); - } - - BKE_animsys_free_nla_keyframing_context_cache(&nla_cache); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Transform (After-Transform Update) * \{ */ -/* Return if we need to update motion paths, only if they already exist, - * and we will insert a keyframe at the end of transform. */ -bool motionpath_need_update_pose(Scene *scene, Object *ob) -{ - if (autokeyframe_cfra_can_key(scene, &ob->id)) { - return (ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) != 0; - } - - return false; -} - -static void special_aftertrans_update__movieclip(bContext *C, TransInfo *t) -{ - SpaceClip *sc = t->area->spacedata.first; - MovieClip *clip = ED_space_clip_get_clip(sc); - ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(&clip->tracking); - const int framenr = ED_space_clip_get_clip_frame_number(sc); - /* Update coordinates of modified plane tracks. */ - LISTBASE_FOREACH (MovieTrackingPlaneTrack *, plane_track, plane_tracks_base) { - bool do_update = false; - if (plane_track->flag & PLANE_TRACK_HIDDEN) { - continue; - } - do_update |= PLANE_TRACK_VIEW_SELECTED(plane_track) != 0; - if (do_update == false) { - if ((plane_track->flag & PLANE_TRACK_AUTOKEY) == 0) { - int i; - for (i = 0; i < plane_track->point_tracksnr; i++) { - MovieTrackingTrack *track = plane_track->point_tracks[i]; - if (TRACK_VIEW_SELECTED(sc, track)) { - do_update = true; - break; - } - } - } - } - if (do_update) { - BKE_tracking_track_plane_from_existing_motion(plane_track, framenr); - } - } - if (t->scene->nodetree != NULL) { - /* Tracks can be used for stabilization nodes, - * flush update for such nodes. - */ - nodeUpdateID(t->scene->nodetree, &clip->id); - WM_event_add_notifier(C, NC_SCENE | ND_NODES, NULL); - } -} - -static void special_aftertrans_update__mask(bContext *C, TransInfo *t) -{ - Mask *mask = NULL; - - if (t->spacetype == SPACE_CLIP) { - SpaceClip *sc = t->area->spacedata.first; - mask = ED_space_clip_get_mask(sc); - } - else if (t->spacetype == SPACE_IMAGE) { - SpaceImage *sima = t->area->spacedata.first; - mask = ED_space_image_get_mask(sima); - } - else { - BLI_assert(0); - } - - if (t->scene->nodetree) { - /* tracks can be used for stabilization nodes, - * flush update for such nodes */ - // if (nodeUpdateID(t->scene->nodetree, &mask->id)) - { - WM_event_add_notifier(C, NC_MASK | ND_DATA, &mask->id); - } - } - - /* TODO - dont key all masks... */ - if (IS_AUTOKEY_ON(t->scene)) { - Scene *scene = t->scene; - - if (ED_mask_layer_shape_auto_key_select(mask, CFRA)) { - WM_event_add_notifier(C, NC_MASK | ND_DATA, &mask->id); - DEG_id_tag_update(&mask->id, 0); - } - } -} - -static void special_aftertrans_update__node(bContext *C, TransInfo *t) -{ - Main *bmain = CTX_data_main(C); - const bool canceled = (t->state == TRANS_CANCEL); - - if (canceled && t->remove_on_cancel) { - /* remove selected nodes on cancel */ - SpaceNode *snode = (SpaceNode *)t->area->spacedata.first; - bNodeTree *ntree = snode->edittree; - if (ntree) { - bNode *node, *node_next; - for (node = ntree->nodes.first; node; node = node_next) { - node_next = node->next; - if (node->flag & NODE_SELECT) { - nodeRemoveNode(bmain, ntree, node, true); - } - } - ntreeUpdateTree(bmain, ntree); - } - } -} - -static void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t) -{ - bool use_automerge = (t->flag & (T_AUTOMERGE | T_AUTOSPLIT)) != 0; - if (use_automerge && ((t->flag & T_EDIT) && t->obedit_type == OB_MESH)) { - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - - BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); - BMesh *bm = em->bm; - char hflag; - bool has_face_sel = (bm->totfacesel != 0); - - if (tc->use_mirror_axis_any) { - /* Rather then adjusting the selection (which the user would notice) - * tag all mirrored verts, then auto-merge those. */ - BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_TAG, false); - - TransDataMirror *td_mirror = tc->data_mirror; - for (int i = tc->data_mirror_len; i--; td_mirror++) { - BM_elem_flag_enable((BMVert *)td_mirror->extra, BM_ELEM_TAG); - } - - hflag = BM_ELEM_SELECT | BM_ELEM_TAG; - } - else { - hflag = BM_ELEM_SELECT; - } - - if (t->flag & T_AUTOSPLIT) { - EDBM_automerge_and_split( - tc->obedit, true, true, true, hflag, t->scene->toolsettings->doublimit); - } - else { - EDBM_automerge(tc->obedit, true, hflag, t->scene->toolsettings->doublimit); - } - - /* Special case, this is needed or faces won't re-select. - * Flush selected edges to faces. */ - if (has_face_sel && (em->selectmode == SCE_SELECT_FACE)) { - EDBM_selectmode_flush_ex(em, SCE_SELECT_EDGE); - } - } - } -} - /* inserting keys, pointcache, redraw events... */ /** * \note Sequencer freeing has its own function now because of a conflict @@ -1934,520 +883,60 @@ static void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t) */ void special_aftertrans_update(bContext *C, TransInfo *t) { - Main *bmain = CTX_data_main(t->context); - BLI_assert(bmain == CTX_data_main(C)); - - Object *ob; - // short redrawipo=0, resetslowpar=1; - const bool canceled = (t->state == TRANS_CANCEL); - const bool duplicate = (t->mode == TFM_TIME_DUPLICATE); - /* early out when nothing happened */ if (t->data_len_all == 0 || t->mode == TFM_DUMMY) { return; } - if (t->spacetype == SPACE_VIEW3D) { - if (t->flag & T_EDIT) { - /* Special Exception: - * We don't normally access 't->custom.mode' here, but its needed in this case. */ - - if (canceled == 0) { - /* we need to delete the temporary faces before automerging */ - if (t->mode == TFM_EDGE_SLIDE) { - /* handle multires re-projection, done - * on transform completion since it's - * really slow -joeedh */ - projectEdgeSlideData(t, true); - } - else if (t->mode == TFM_VERT_SLIDE) { - /* as above */ - projectVertSlideData(t, true); - } - - if (t->obedit_type == OB_MESH) { - special_aftertrans_update__mesh(C, t); - } - } - else { - if (t->mode == TFM_EDGE_SLIDE) { - projectEdgeSlideData(t, false); - } - else if (t->mode == TFM_VERT_SLIDE) { - projectVertSlideData(t, false); - } - } - } - } - - if (t->options & CTX_GPENCIL_STROKES) { - /* pass */ - } - else if (t->spacetype == SPACE_SEQ) { - /* freeSeqData in transform_conversions.c does this - * keep here so the else at the end wont run... */ - - SpaceSeq *sseq = (SpaceSeq *)t->area->spacedata.first; - - /* Marker transform, not especially nice but we may want to move markers - * at the same time as strips in the Video Sequencer. */ - if ((sseq->flag & SEQ_MARKER_TRANS) && (canceled == 0)) { - /* cant use TFM_TIME_EXTEND - * for some reason EXTEND is changed into TRANSLATE, so use frame_side instead */ - - if (t->mode == TFM_SEQ_SLIDE) { - if (t->frame_side == 'B') { - ED_markers_post_apply_transform( - &t->scene->markers, t->scene, TFM_TIME_TRANSLATE, t->values[0], t->frame_side); - } - } - else if (ELEM(t->frame_side, 'L', 'R')) { - ED_markers_post_apply_transform( - &t->scene->markers, t->scene, TFM_TIME_EXTEND, t->values[0], t->frame_side); - } - } - } - else if (t->spacetype == SPACE_IMAGE) { - if (t->options & CTX_MASK) { + BLI_assert(CTX_data_main(t->context) == CTX_data_main(C)); + switch (t->data_type) { + case TC_ACTION_DATA: + special_aftertrans_update__actedit(C, t); + break; + case TC_POSE: + special_aftertrans_update__pose(C, t); + break; + case TC_GRAPH_EDIT_DATA: + special_aftertrans_update__graph(C, t); + break; + case TC_MASKING_DATA: special_aftertrans_update__mask(C, t); - } - } - else if (t->spacetype == SPACE_NODE) { - SpaceNode *snode = (SpaceNode *)t->area->spacedata.first; - special_aftertrans_update__node(C, t); - if (canceled == 0) { - ED_node_post_apply_transform(C, snode->edittree); - - ED_node_link_insert(bmain, t->area); - } - - /* clear link line */ - ED_node_link_intersect_test(t->area, 0); - } - else if (t->spacetype == SPACE_CLIP) { - if (t->options & CTX_MOVIECLIP) { + break; + case TC_MESH_VERTS: + case TC_MESH_EDGES: + special_aftertrans_update__mesh(C, t); + break; + case TC_NLA_DATA: + special_aftertrans_update__nla(C, t); + break; + case TC_NODE_DATA: + special_aftertrans_update__node(C, t); + break; + case TC_OBJECT: + case TC_OBJECT_TEXSPACE: + special_aftertrans_update__object(C, t); + break; + case TC_SEQ_DATA: + special_aftertrans_update__sequencer(C, t); + break; + case TC_TRACKING_DATA: special_aftertrans_update__movieclip(C, t); - } - else if (t->options & CTX_MASK) { - special_aftertrans_update__mask(C, t); - } - } - else if (t->spacetype == SPACE_ACTION) { - SpaceAction *saction = (SpaceAction *)t->area->spacedata.first; - bAnimContext ac; - - /* initialize relevant anim-context 'context' data */ - if (ANIM_animdata_get_context(C, &ac) == 0) { - return; - } - - ob = ac.obact; - - if (ELEM(ac.datatype, ANIMCONT_DOPESHEET, ANIMCONT_SHAPEKEY, ANIMCONT_TIMELINE)) { - ListBase anim_data = {NULL, NULL}; - bAnimListElem *ale; - short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/); - - /* get channels to work on */ - ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); - - /* these should all be F-Curves */ - for (ale = anim_data.first; ale; ale = ale->next) { - AnimData *adt = ANIM_nla_mapping_get(&ac, ale); - FCurve *fcu = (FCurve *)ale->key_data; - - /* 3 cases here for curve cleanups: - * 1) NOTRANSKEYCULL on -> cleanup of duplicates shouldn't be done - * 2) canceled == 0 -> user confirmed the transform, - * so duplicates should be removed - * 3) canceled + duplicate -> user canceled the transform, - * but we made duplicates, so get rid of these - */ - if ((saction->flag & SACTION_NOTRANSKEYCULL) == 0 && ((canceled == 0) || (duplicate))) { - if (adt) { - ANIM_nla_mapping_apply_fcurve(adt, fcu, 0, 0); - posttrans_fcurve_clean(fcu, SELECT, false); /* only use handles in graph editor */ - ANIM_nla_mapping_apply_fcurve(adt, fcu, 1, 0); - } - else { - posttrans_fcurve_clean(fcu, SELECT, false); /* only use handles in graph editor */ - } - } - } - - /* free temp memory */ - ANIM_animdata_freelist(&anim_data); - } - else if (ac.datatype == ANIMCONT_ACTION) { // TODO: just integrate into the above... - /* Depending on the lock status, draw necessary views */ - // fixme... some of this stuff is not good - if (ob) { - if (ob->pose || BKE_key_from_object(ob)) { - DEG_id_tag_update(&ob->id, - ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); - } - else { - DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); - } - } - - /* 3 cases here for curve cleanups: - * 1) NOTRANSKEYCULL on -> cleanup of duplicates shouldn't be done - * 2) canceled == 0 -> user confirmed the transform, - * so duplicates should be removed. - * 3) canceled + duplicate -> user canceled the transform, - * but we made duplicates, so get rid of these. - */ - if ((saction->flag & SACTION_NOTRANSKEYCULL) == 0 && ((canceled == 0) || (duplicate))) { - posttrans_action_clean(&ac, (bAction *)ac.data); - } - } - else if (ac.datatype == ANIMCONT_GPENCIL) { - /* remove duplicate frames and also make sure points are in order! */ - /* 3 cases here for curve cleanups: - * 1) NOTRANSKEYCULL on -> cleanup of duplicates shouldn't be done - * 2) canceled == 0 -> user confirmed the transform, - * so duplicates should be removed - * 3) canceled + duplicate -> user canceled the transform, - * but we made duplicates, so get rid of these - */ - if ((saction->flag & SACTION_NOTRANSKEYCULL) == 0 && ((canceled == 0) || (duplicate))) { - ListBase anim_data = {NULL, NULL}; - const int filter = ANIMFILTER_DATA_VISIBLE; - ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); - - LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { - if (ale->datatype == ALE_GPFRAME) { - ale->id->tag |= LIB_TAG_DOIT; - } - } - LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { - if (ale->datatype == ALE_GPFRAME) { - if (ale->id->tag & LIB_TAG_DOIT) { - ale->id->tag &= ~LIB_TAG_DOIT; - posttrans_gpd_clean((bGPdata *)ale->id); - } - } - } - ANIM_animdata_freelist(&anim_data); - } - } - else if (ac.datatype == ANIMCONT_MASK) { - /* remove duplicate frames and also make sure points are in order! */ - /* 3 cases here for curve cleanups: - * 1) NOTRANSKEYCULL on: - * Cleanup of duplicates shouldn't be done. - * 2) canceled == 0: - * User confirmed the transform, so duplicates should be removed. - * 3) Canceled + duplicate: - * User canceled the transform, but we made duplicates, so get rid of these. - */ - if ((saction->flag & SACTION_NOTRANSKEYCULL) == 0 && ((canceled == 0) || (duplicate))) { - ListBase anim_data = {NULL, NULL}; - const int filter = ANIMFILTER_DATA_VISIBLE; - ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); - - LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { - if (ale->datatype == ALE_MASKLAY) { - ale->id->tag |= LIB_TAG_DOIT; - } - } - LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { - if (ale->datatype == ALE_MASKLAY) { - if (ale->id->tag & LIB_TAG_DOIT) { - ale->id->tag &= ~LIB_TAG_DOIT; - posttrans_mask_clean((Mask *)ale->id); - } - } - } - ANIM_animdata_freelist(&anim_data); - } - } - - /* marker transform, not especially nice but we may want to move markers - * at the same time as keyframes in the dope sheet. - */ - if ((saction->flag & SACTION_MARKERS_MOVE) && (canceled == 0)) { - if (t->mode == TFM_TIME_TRANSLATE) { -#if 0 - if (ELEM(t->frame_side, 'L', 'R')) { /* TFM_TIME_EXTEND */ - /* same as below */ - ED_markers_post_apply_transform( - ED_context_get_markers(C), t->scene, t->mode, t->values[0], t->frame_side); - } - else /* TFM_TIME_TRANSLATE */ -#endif - { - ED_markers_post_apply_transform( - ED_context_get_markers(C), t->scene, t->mode, t->values[0], t->frame_side); - } - } - else if (t->mode == TFM_TIME_SCALE) { - ED_markers_post_apply_transform( - ED_context_get_markers(C), t->scene, t->mode, t->values[0], t->frame_side); - } - } - - /* make sure all F-Curves are set correctly */ - if (!ELEM(ac.datatype, ANIMCONT_GPENCIL)) { - ANIM_editkeyframes_refresh(&ac); - } - - /* clear flag that was set for time-slide drawing */ - saction->flag &= ~SACTION_MOVING; - } - else if (t->spacetype == SPACE_GRAPH) { - SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first; - bAnimContext ac; - const bool use_handle = (sipo->flag & SIPO_NOHANDLES) == 0; - - /* initialize relevant anim-context 'context' data */ - if (ANIM_animdata_get_context(C, &ac) == 0) { - return; - } - - if (ac.datatype) { - ListBase anim_data = {NULL, NULL}; - bAnimListElem *ale; - short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE); - - /* get channels to work on */ - ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); - - for (ale = anim_data.first; ale; ale = ale->next) { - AnimData *adt = ANIM_nla_mapping_get(&ac, ale); - FCurve *fcu = (FCurve *)ale->key_data; - - /* 3 cases here for curve cleanups: - * 1) NOTRANSKEYCULL on -> cleanup of duplicates shouldn't be done - * 2) canceled == 0 -> user confirmed the transform, - * so duplicates should be removed - * 3) canceled + duplicate -> user canceled the transform, - * but we made duplicates, so get rid of these - */ - if ((sipo->flag & SIPO_NOTRANSKEYCULL) == 0 && ((canceled == 0) || (duplicate))) { - if (adt) { - ANIM_nla_mapping_apply_fcurve(adt, fcu, 0, 0); - posttrans_fcurve_clean(fcu, BEZT_FLAG_TEMP_TAG, use_handle); - ANIM_nla_mapping_apply_fcurve(adt, fcu, 1, 0); - } - else { - posttrans_fcurve_clean(fcu, BEZT_FLAG_TEMP_TAG, use_handle); - } - } - } - - /* free temp memory */ - ANIM_animdata_freelist(&anim_data); - } - - /* Make sure all F-Curves are set correctly, but not if transform was - * canceled, since then curves were already restored to initial state. - * Note: if the refresh is really needed after cancel then some way - * has to be added to not update handle types (see bug 22289). - */ - if (!canceled) { - ANIM_editkeyframes_refresh(&ac); - } - } - else if (t->spacetype == SPACE_NLA) { - bAnimContext ac; - - /* initialize relevant anim-context 'context' data */ - if (ANIM_animdata_get_context(C, &ac) == 0) { - return; - } - - if (ac.datatype) { - ListBase anim_data = {NULL, NULL}; - bAnimListElem *ale; - short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT); - - /* get channels to work on */ - ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); - - for (ale = anim_data.first; ale; ale = ale->next) { - NlaTrack *nlt = (NlaTrack *)ale->data; - - /* make sure strips are in order again */ - BKE_nlatrack_sort_strips(nlt); - - /* remove the temp metas */ - BKE_nlastrips_clear_metas(&nlt->strips, 0, 1); - } - - /* free temp memory */ - ANIM_animdata_freelist(&anim_data); - - /* perform after-transfrom validation */ - ED_nla_postop_refresh(&ac); - } - } - else if (t->flag & T_EDIT) { - if (t->obedit_type == OB_MESH) { - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - /* table needs to be created for each edit command, since vertices can move etc */ - ED_mesh_mirror_spatial_table_end(tc->obedit); - /* TODO(campbell): xform: We need support for many mirror objects at once! */ - break; - } - } - } - else if (t->flag & T_POSE && (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 if (t->flag & T_POSE) { - 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) { - 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); - } - else if (t->options & CTX_PAINT_CURVE) { - /* pass */ - } - else if (t->options & CTX_SCULPT) { - /* pass */ - } - else if ((t->view_layer->basact) && (ob = t->view_layer->basact->object) && - (ob->mode & OB_MODE_PARTICLE_EDIT) && PE_get_current(t->depsgraph, t->scene, ob)) { - /* do nothing */ - } - else if (t->flag & T_CURSOR) { - /* do nothing */ - } - else { /* Objects */ - BLI_assert(t->flag & (T_OBJECT | T_TEXTURE)); - - TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); - bool motionpath_update = false; - - for (int i = 0; i < tc->data_len; i++) { - TransData *td = tc->data + i; - ListBase pidlist; - PTCacheID *pid; - ob = td->ob; - - if (td->flag & TD_SKIP) { - continue; - } - - /* flag object caches as outdated */ - BKE_ptcache_ids_from_object(&pidlist, ob, t->scene, MAX_DUPLI_RECUR); - for (pid = pidlist.first; pid; pid = pid->next) { - if (pid->type != PTCACHE_TYPE_PARTICLES) { - /* particles don't need reset on geometry change */ - pid->cache->flag |= PTCACHE_OUTDATED; - } - } - BLI_freelistN(&pidlist); - - /* pointcache refresh */ - if (BKE_ptcache_object_reset(t->scene, ob, PTCACHE_RESET_OUTDATED)) { - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - } - - /* Needed for proper updating of "quick cached" dynamics. */ - /* Creates troubles for moving animated objects without */ - /* autokey though, probably needed is an anim sys override? */ - /* Please remove if some other solution is found. -jahka */ - DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); - - /* Set autokey if necessary */ - if (!canceled) { - autokeyframe_object(C, t->scene, t->view_layer, ob, t->mode); - } - - motionpath_update |= motionpath_need_update_object(t->scene, ob); - - /* restore rigid body transform */ - if (ob->rigidbody_object && canceled) { - float ctime = BKE_scene_frame_get(t->scene); - if (BKE_rigidbody_check_sim_running(t->scene->rigidbody_world, ctime)) { - BKE_rigidbody_aftertrans_update(ob, - td->ext->oloc, - td->ext->orot, - td->ext->oquat, - td->ext->orotAxis, - td->ext->orotAngle); - } - } - } - - if (motionpath_update) { - /* Update motion paths once for all transformed objects. */ - const eObjectPathCalcRange range = canceled ? OBJECT_PATH_CALC_RANGE_CURRENT_FRAME : - OBJECT_PATH_CALC_RANGE_CHANGED; - ED_objects_recalculate_paths(C, t->scene, range); - } + break; + case TC_ARMATURE_VERTS: + case TC_CURSOR_IMAGE: + case TC_CURSOR_VIEW3D: + case TC_CURVE_VERTS: + case TC_GPENCIL: + case TC_LATTICE_VERTS: + case TC_MBALL_VERTS: + case TC_MESH_UV: + case TC_PAINT_CURVE_VERTS: + case TC_PARTICLE_VERTS: + case TC_SCULPT: + case TC_NONE: + default: + break; } - - clear_trans_object_base_flags(t); } int special_transform_moving(TransInfo *t) diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h index 2465e0e99b3..872f05418c4 100644 --- a/source/blender/editors/transform/transform_convert.h +++ b/source/blender/editors/transform/transform_convert.h @@ -26,6 +26,7 @@ #define __TRANSFORM_CONVERT_H__ struct BezTriple; +struct FCurve; struct ListBase; struct Object; struct TransData; @@ -37,20 +38,7 @@ struct bKinematicConstraint; struct bPoseChannel; /* transform_convert.c */ -int transform_convert_pose_transflags_update(Object *ob, - const int mode, - const short around, - bool has_translate_rotate[2]); void transform_autoik_update(TransInfo *t, short mode); -void autokeyframe_object(struct bContext *C, - struct Scene *scene, - struct ViewLayer *view_layer, - struct Object *ob, - int tmode); -void autokeyframe_pose( - struct bContext *C, struct Scene *scene, struct Object *ob, int tmode, short targetless_ik); -bool motionpath_need_update_object(struct Scene *scene, struct Object *ob); -bool motionpath_need_update_pose(struct Scene *scene, struct Object *ob); int special_transform_moving(TransInfo *t); void special_aftertrans_update(struct bContext *C, TransInfo *t); void sort_trans_data_dist(TransInfo *t); @@ -97,7 +85,7 @@ typedef enum eTransConvertType { /* transform_convert.c */ bool transform_mode_use_local_origins(const TransInfo *t); void transform_around_single_fallback(TransInfo *t); -void remake_graph_transdata(TransInfo *t, struct ListBase *anim_data); +void posttrans_fcurve_clean(struct FCurve *fcu, const int sel_flag, const bool use_handle); bool constraints_list_needinv(TransInfo *t, ListBase *list); void calc_distanceCurveVerts(TransData *head, TransData *tail); struct TransDataCurveHandleFlags *initTransDataCurveHandles(TransData *td, struct BezTriple *bezt); @@ -109,13 +97,18 @@ void animrecord_check_state(TransInfo *t, struct Object *ob); /* transform_convert_action.c */ void createTransActionData(bContext *C, TransInfo *t); void recalcData_actedit(TransInfo *t); +void special_aftertrans_update__actedit(bContext *C, TransInfo *t); /* transform_convert_armature.c */ -struct bKinematicConstraint *has_targetless_ik(struct bPoseChannel *pchan); +int transform_convert_pose_transflags_update(Object *ob, + const int mode, + const short around, + bool has_translate_rotate[2]); void createTransPose(TransInfo *t); void createTransArmatureVerts(TransInfo *t); void recalcData_edit_armature(TransInfo *t); void recalcData_pose(TransInfo *t); +void special_aftertrans_update__pose(bContext *C, TransInfo *t); /* transform_convert_cursor.c */ void createTransCursor_image(TransInfo *t); @@ -128,6 +121,7 @@ void recalcData_curve(TransInfo *t); /* transform_convert_graph.c */ void createTransGraphEditData(bContext *C, TransInfo *t); void recalcData_graphedit(TransInfo *t); +void special_aftertrans_update__graph(bContext *C, TransInfo *t); /* transform_convert_gpencil.c */ void createTransGPencil(bContext *C, TransInfo *t); @@ -140,6 +134,7 @@ void recalcData_lattice(TransInfo *t); /* transform_convert_mask.c */ void createTransMaskingData(bContext *C, TransInfo *t); void recalcData_mask_common(TransInfo *t); +void special_aftertrans_update__mask(bContext *C, TransInfo *t); /* transform_convert_mball.c */ void createTransMBallVerts(TransInfo *t); @@ -147,6 +142,7 @@ void createTransMBallVerts(TransInfo *t); /* transform_convert_mesh.c */ void createTransEditVerts(TransInfo *t); void recalcData_mesh(TransInfo *t); +void special_aftertrans_update__mesh(bContext *C, TransInfo *t); /* transform_convert_mesh_edge.c */ void createTransEdge(TransInfo *t); @@ -158,16 +154,18 @@ void recalcData_uv(TransInfo *t); /* transform_convert_nla.c */ void createTransNlaData(bContext *C, TransInfo *t); void recalcData_nla(TransInfo *t); +void special_aftertrans_update__nla(bContext *C, TransInfo *t); /* transform_convert_node.c */ void createTransNodeData(TransInfo *t); void flushTransNodes(TransInfo *t); +void special_aftertrans_update__node(bContext *C, TransInfo *t); /* transform_convert_object.c */ -void clear_trans_object_base_flags(TransInfo *t); void createTransObject(bContext *C, TransInfo *t); void createTransTexspace(TransInfo *t); void recalcData_objects(TransInfo *t); +void special_aftertrans_update__object(bContext *C, TransInfo *t); /* transform_convert_paintcurve.c */ void createTransPaintCurveVerts(bContext *C, TransInfo *t); @@ -184,9 +182,10 @@ void recalcData_sculpt(TransInfo *t); /* transform_convert_sequencer.c */ void createTransSeqData(TransInfo *t); void recalcData_sequencer(TransInfo *t); +void special_aftertrans_update__sequencer(bContext *C, TransInfo *t); /* transform_convert_tracking.c */ void createTransTrackingData(bContext *C, TransInfo *t); -void cancelTransTracking(TransInfo *t); void recalcData_tracking(TransInfo *t); +void special_aftertrans_update__movieclip(bContext *C, TransInfo *t); #endif diff --git a/source/blender/editors/transform/transform_convert_action.c b/source/blender/editors/transform/transform_convert_action.c index 87256764e65..267acd5cb04 100644 --- a/source/blender/editors/transform/transform_convert_action.c +++ b/source/blender/editors/transform/transform_convert_action.c @@ -27,14 +27,23 @@ #include "MEM_guardedalloc.h" +#include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_rect.h" #include "BKE_context.h" +#include "BKE_gpencil.h" +#include "BKE_key.h" +#include "BKE_mask.h" #include "BKE_nla.h" #include "BKE_report.h" #include "ED_anim_api.h" +#include "ED_keyframes_edit.h" +#include "ED_markers.h" + +#include "WM_api.h" +#include "WM_types.h" #include "transform.h" #include "transform_convert.h" @@ -619,3 +628,297 @@ void recalcData_actedit(TransInfo *t) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Special After Transform Action + * \{ */ + +static int masklay_shape_cmp_frame(void *thunk, const void *a, const void *b) +{ + const MaskLayerShape *frame_a = a; + const MaskLayerShape *frame_b = b; + + if (frame_a->frame < frame_b->frame) { + return -1; + } + if (frame_a->frame > frame_b->frame) { + return 1; + } + *((bool *)thunk) = true; + /* selected last */ + if ((frame_a->flag & MASK_SHAPE_SELECT) && ((frame_b->flag & MASK_SHAPE_SELECT) == 0)) { + return 1; + } + return 0; +} + +static void posttrans_mask_clean(Mask *mask) +{ + MaskLayer *masklay; + + for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + MaskLayerShape *masklay_shape, *masklay_shape_next; + bool is_double = false; + + BLI_listbase_sort_r(&masklay->splines_shapes, masklay_shape_cmp_frame, &is_double); + + if (is_double) { + for (masklay_shape = masklay->splines_shapes.first; masklay_shape; + masklay_shape = masklay_shape_next) { + masklay_shape_next = masklay_shape->next; + if (masklay_shape_next && masklay_shape->frame == masklay_shape_next->frame) { + BKE_mask_layer_shape_unlink(masklay, masklay_shape); + } + } + } + +#ifdef DEBUG + for (masklay_shape = masklay->splines_shapes.first; masklay_shape; + masklay_shape = masklay_shape->next) { + BLI_assert(!masklay_shape->next || masklay_shape->frame < masklay_shape->next->frame); + } +#endif + } + + WM_main_add_notifier(NC_MASK | NA_EDITED, mask); +} + +/* Called by special_aftertrans_update to make sure selected gp-frames replace + * any other gp-frames which may reside on that frame (that are not selected). + * It also makes sure gp-frames are still stored in chronological order after + * transform. + */ +static void posttrans_gpd_clean(bGPdata *gpd) +{ + bGPDlayer *gpl; + + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + bGPDframe *gpf, *gpfn; + bool is_double = false; + + BKE_gpencil_layer_frames_sort(gpl, &is_double); + + if (is_double) { + for (gpf = gpl->frames.first; gpf; gpf = gpfn) { + gpfn = gpf->next; + if (gpfn && gpf->framenum == gpfn->framenum) { + BKE_gpencil_layer_frame_delete(gpl, gpf); + } + } + } + +#ifdef DEBUG + for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + BLI_assert(!gpf->next || gpf->framenum < gpf->next->framenum); + } +#endif + } + /* set cache flag to dirty */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + + WM_main_add_notifier(NC_GPENCIL | NA_EDITED, gpd); +} + +/* Called by special_aftertrans_update to make sure selected keyframes replace + * any other keyframes which may reside on that frame (that is not selected). + * remake_action_ipos should have already been called + */ +static void posttrans_action_clean(bAnimContext *ac, bAction *act) +{ + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + /* filter data */ + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/); + ANIM_animdata_filter(ac, &anim_data, filter, act, ANIMCONT_ACTION); + + /* loop through relevant data, removing keyframes as appropriate + * - all keyframes are converted in/out of global time + */ + for (ale = anim_data.first; ale; ale = ale->next) { + AnimData *adt = ANIM_nla_mapping_get(ac, ale); + + if (adt) { + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0); + posttrans_fcurve_clean(ale->key_data, SELECT, false); /* only use handles in graph editor */ + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0); + } + else { + posttrans_fcurve_clean(ale->key_data, SELECT, false); /* only use handles in graph editor */ + } + } + + /* free temp data */ + ANIM_animdata_freelist(&anim_data); +} + +void special_aftertrans_update__actedit(bContext *C, TransInfo *t) +{ + SpaceAction *saction = (SpaceAction *)t->area->spacedata.first; + bAnimContext ac; + + const bool canceled = (t->state == TRANS_CANCEL); + const bool duplicate = (t->mode == TFM_TIME_DUPLICATE); + + /* initialize relevant anim-context 'context' data */ + if (ANIM_animdata_get_context(C, &ac) == 0) { + return; + } + + Object *ob = ac.obact; + + if (ELEM(ac.datatype, ANIMCONT_DOPESHEET, ANIMCONT_SHAPEKEY, ANIMCONT_TIMELINE)) { + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/); + + /* get channels to work on */ + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* these should all be F-Curves */ + for (ale = anim_data.first; ale; ale = ale->next) { + AnimData *adt = ANIM_nla_mapping_get(&ac, ale); + FCurve *fcu = (FCurve *)ale->key_data; + + /* 3 cases here for curve cleanups: + * 1) NOTRANSKEYCULL on -> cleanup of duplicates shouldn't be done + * 2) canceled == 0 -> user confirmed the transform, + * so duplicates should be removed + * 3) canceled + duplicate -> user canceled the transform, + * but we made duplicates, so get rid of these + */ + if ((saction->flag & SACTION_NOTRANSKEYCULL) == 0 && ((canceled == 0) || (duplicate))) { + if (adt) { + ANIM_nla_mapping_apply_fcurve(adt, fcu, 0, 0); + posttrans_fcurve_clean(fcu, SELECT, false); /* only use handles in graph editor */ + ANIM_nla_mapping_apply_fcurve(adt, fcu, 1, 0); + } + else { + posttrans_fcurve_clean(fcu, SELECT, false); /* only use handles in graph editor */ + } + } + } + + /* free temp memory */ + ANIM_animdata_freelist(&anim_data); + } + else if (ac.datatype == ANIMCONT_ACTION) { // TODO: just integrate into the above... + /* Depending on the lock status, draw necessary views */ + // fixme... some of this stuff is not good + if (ob) { + if (ob->pose || BKE_key_from_object(ob)) { + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); + } + else { + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); + } + } + + /* 3 cases here for curve cleanups: + * 1) NOTRANSKEYCULL on -> cleanup of duplicates shouldn't be done + * 2) canceled == 0 -> user confirmed the transform, + * so duplicates should be removed. + * 3) canceled + duplicate -> user canceled the transform, + * but we made duplicates, so get rid of these. + */ + if ((saction->flag & SACTION_NOTRANSKEYCULL) == 0 && ((canceled == 0) || (duplicate))) { + posttrans_action_clean(&ac, (bAction *)ac.data); + } + } + else if (ac.datatype == ANIMCONT_GPENCIL) { + /* remove duplicate frames and also make sure points are in order! */ + /* 3 cases here for curve cleanups: + * 1) NOTRANSKEYCULL on -> cleanup of duplicates shouldn't be done + * 2) canceled == 0 -> user confirmed the transform, + * so duplicates should be removed + * 3) canceled + duplicate -> user canceled the transform, + * but we made duplicates, so get rid of these + */ + if ((saction->flag & SACTION_NOTRANSKEYCULL) == 0 && ((canceled == 0) || (duplicate))) { + ListBase anim_data = {NULL, NULL}; + const int filter = ANIMFILTER_DATA_VISIBLE; + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { + if (ale->datatype == ALE_GPFRAME) { + ale->id->tag |= LIB_TAG_DOIT; + } + } + LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { + if (ale->datatype == ALE_GPFRAME) { + if (ale->id->tag & LIB_TAG_DOIT) { + ale->id->tag &= ~LIB_TAG_DOIT; + posttrans_gpd_clean((bGPdata *)ale->id); + } + } + } + ANIM_animdata_freelist(&anim_data); + } + } + else if (ac.datatype == ANIMCONT_MASK) { + /* remove duplicate frames and also make sure points are in order! */ + /* 3 cases here for curve cleanups: + * 1) NOTRANSKEYCULL on: + * Cleanup of duplicates shouldn't be done. + * 2) canceled == 0: + * User confirmed the transform, so duplicates should be removed. + * 3) Canceled + duplicate: + * User canceled the transform, but we made duplicates, so get rid of these. + */ + if ((saction->flag & SACTION_NOTRANSKEYCULL) == 0 && ((canceled == 0) || (duplicate))) { + ListBase anim_data = {NULL, NULL}; + const int filter = ANIMFILTER_DATA_VISIBLE; + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { + if (ale->datatype == ALE_MASKLAY) { + ale->id->tag |= LIB_TAG_DOIT; + } + } + LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { + if (ale->datatype == ALE_MASKLAY) { + if (ale->id->tag & LIB_TAG_DOIT) { + ale->id->tag &= ~LIB_TAG_DOIT; + posttrans_mask_clean((Mask *)ale->id); + } + } + } + ANIM_animdata_freelist(&anim_data); + } + } + + /* marker transform, not especially nice but we may want to move markers + * at the same time as keyframes in the dope sheet. + */ + if ((saction->flag & SACTION_MARKERS_MOVE) && (canceled == 0)) { + if (t->mode == TFM_TIME_TRANSLATE) { +#if 0 + if (ELEM(t->frame_side, 'L', 'R')) { /* TFM_TIME_EXTEND */ + /* same as below */ + ED_markers_post_apply_transform( + ED_context_get_markers(C), t->scene, t->mode, t->values[0], t->frame_side); + } + else /* TFM_TIME_TRANSLATE */ +#endif + { + ED_markers_post_apply_transform( + ED_context_get_markers(C), t->scene, t->mode, t->values[0], t->frame_side); + } + } + else if (t->mode == TFM_TIME_SCALE) { + ED_markers_post_apply_transform( + ED_context_get_markers(C), t->scene, t->mode, t->values[0], t->frame_side); + } + } + + /* make sure all F-Curves are set correctly */ + if (!ELEM(ac.datatype, ANIMCONT_GPENCIL)) { + ANIM_editkeyframes_refresh(&ac); + } + + /* clear flag that was set for time-slide drawing */ + saction->flag &= ~SACTION_MOVING; +} + +/** \} */ 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); + } +} + +/** \} */ diff --git a/source/blender/editors/transform/transform_convert_graph.c b/source/blender/editors/transform/transform_convert_graph.c index 0e77c991300..8886be9ac85 100644 --- a/source/blender/editors/transform/transform_convert_graph.c +++ b/source/blender/editors/transform/transform_convert_graph.c @@ -34,6 +34,7 @@ #include "BKE_report.h" #include "ED_anim_api.h" +#include "ED_keyframes_edit.h" #include "ED_markers.h" #include "UI_view2d.h" @@ -751,6 +752,213 @@ static void flushTransGraphData(TransInfo *t) } } +/* struct for use in re-sorting BezTriples during Graph Editor transform */ +typedef struct BeztMap { + BezTriple *bezt; + uint oldIndex; /* index of bezt in fcu->bezt array before sorting */ + uint newIndex; /* index of bezt in fcu->bezt array after sorting */ + short swapHs; /* swap order of handles (-1=clear; 0=not checked, 1=swap) */ + char pipo, cipo; /* interpolation of current and next segments */ +} BeztMap; + +/* This function converts an FCurve's BezTriple array to a BeztMap array + * NOTE: this allocates memory that will need to get freed later + */ +static BeztMap *bezt_to_beztmaps(BezTriple *bezts, int totvert) +{ + BezTriple *bezt = bezts; + BezTriple *prevbezt = NULL; + BeztMap *bezm, *bezms; + int i; + + /* allocate memory for this array */ + if (totvert == 0 || bezts == NULL) { + return NULL; + } + bezm = bezms = MEM_callocN(sizeof(BeztMap) * totvert, "BeztMaps"); + + /* assign beztriples to beztmaps */ + for (i = 0; i < totvert; i++, bezm++, prevbezt = bezt, bezt++) { + bezm->bezt = bezt; + + bezm->oldIndex = i; + bezm->newIndex = i; + + bezm->pipo = (prevbezt) ? prevbezt->ipo : bezt->ipo; + bezm->cipo = bezt->ipo; + } + + return bezms; +} + +/* This function copies the code of sort_time_ipocurve, but acts on BeztMap structs instead */ +static void sort_time_beztmaps(BeztMap *bezms, int totvert) +{ + BeztMap *bezm; + int i, ok = 1; + + /* keep repeating the process until nothing is out of place anymore */ + while (ok) { + ok = 0; + + bezm = bezms; + i = totvert; + while (i--) { + /* is current bezm out of order (i.e. occurs later than next)? */ + if (i > 0) { + if (bezm->bezt->vec[1][0] > (bezm + 1)->bezt->vec[1][0]) { + bezm->newIndex++; + (bezm + 1)->newIndex--; + + SWAP(BeztMap, *bezm, *(bezm + 1)); + + ok = 1; + } + } + + /* do we need to check if the handles need to be swapped? + * optimization: this only needs to be performed in the first loop + */ + if (bezm->swapHs == 0) { + if ((bezm->bezt->vec[0][0] > bezm->bezt->vec[1][0]) && + (bezm->bezt->vec[2][0] < bezm->bezt->vec[1][0])) { + /* handles need to be swapped */ + bezm->swapHs = 1; + } + else { + /* handles need to be cleared */ + bezm->swapHs = -1; + } + } + + bezm++; + } + } +} + +/* This function firstly adjusts the pointers that the transdata has to each BezTriple */ +static void beztmap_to_data(TransInfo *t, FCurve *fcu, BeztMap *bezms, int totvert) +{ + BezTriple *bezts = fcu->bezt; + BeztMap *bezm; + TransData2D *td2d; + TransData *td; + int i, j; + char *adjusted; + + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + + /* dynamically allocate an array of chars to mark whether an TransData's + * pointers have been fixed already, so that we don't override ones that are + * already done + */ + adjusted = MEM_callocN(tc->data_len, "beztmap_adjusted_map"); + + /* for each beztmap item, find if it is used anywhere */ + bezm = bezms; + for (i = 0; i < totvert; i++, bezm++) { + /* loop through transdata, testing if we have a hit + * for the handles (vec[0]/vec[2]), we must also check if they need to be swapped... + */ + td2d = tc->data_2d; + td = tc->data; + for (j = 0; j < tc->data_len; j++, td2d++, td++) { + /* skip item if already marked */ + if (adjusted[j] != 0) { + continue; + } + + /* update all transdata pointers, no need to check for selections etc, + * since only points that are really needed were created as transdata + */ + if (td2d->loc2d == bezm->bezt->vec[0]) { + if (bezm->swapHs == 1) { + td2d->loc2d = (bezts + bezm->newIndex)->vec[2]; + } + else { + td2d->loc2d = (bezts + bezm->newIndex)->vec[0]; + } + adjusted[j] = 1; + } + else if (td2d->loc2d == bezm->bezt->vec[2]) { + if (bezm->swapHs == 1) { + td2d->loc2d = (bezts + bezm->newIndex)->vec[0]; + } + else { + td2d->loc2d = (bezts + bezm->newIndex)->vec[2]; + } + adjusted[j] = 1; + } + else if (td2d->loc2d == bezm->bezt->vec[1]) { + td2d->loc2d = (bezts + bezm->newIndex)->vec[1]; + + /* if only control point is selected, the handle pointers need to be updated as well */ + if (td2d->h1) { + td2d->h1 = (bezts + bezm->newIndex)->vec[0]; + } + if (td2d->h2) { + td2d->h2 = (bezts + bezm->newIndex)->vec[2]; + } + + adjusted[j] = 1; + } + + /* the handle type pointer has to be updated too */ + if (adjusted[j] && td->flag & TD_BEZTRIPLE && td->hdata) { + if (bezm->swapHs == 1) { + td->hdata->h1 = &(bezts + bezm->newIndex)->h2; + td->hdata->h2 = &(bezts + bezm->newIndex)->h1; + } + else { + td->hdata->h1 = &(bezts + bezm->newIndex)->h1; + td->hdata->h2 = &(bezts + bezm->newIndex)->h2; + } + } + } + } + + /* free temp memory used for 'adjusted' array */ + MEM_freeN(adjusted); +} + +/* This function is called by recalcData during the Transform loop to recalculate + * the handles of curves and sort the keyframes so that the curves draw correctly. + * It is only called if some keyframes have moved out of order. + * + * anim_data is the list of channels (F-Curves) retrieved already containing the + * channels to work on. It should not be freed here as it may still need to be used. + */ +static void remake_graph_transdata(TransInfo *t, ListBase *anim_data) +{ + SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first; + bAnimListElem *ale; + const bool use_handle = (sipo->flag & SIPO_NOHANDLES) == 0; + + /* sort and reassign verts */ + for (ale = anim_data->first; ale; ale = ale->next) { + FCurve *fcu = (FCurve *)ale->key_data; + + if (fcu->bezt) { + BeztMap *bezm; + + /* adjust transform-data pointers */ + /* note, none of these functions use 'use_handle', it could be removed */ + bezm = bezt_to_beztmaps(fcu->bezt, fcu->totvert); + sort_time_beztmaps(bezm, fcu->totvert); + beztmap_to_data(t, fcu, bezm, fcu->totvert); + + /* free mapping stuff */ + MEM_freeN(bezm); + + /* re-sort actual beztriples (perhaps this could be done using the beztmaps to save time?) */ + sort_time_fcurve(fcu); + + /* make sure handles are all set correctly */ + testhandles_fcurve(fcu, BEZT_FLAG_TEMP_TAG, use_handle); + } + } +} + /* helper for recalcData() - for Graph Editor transforms */ void recalcData_graphedit(TransInfo *t) { @@ -820,3 +1028,68 @@ void recalcData_graphedit(TransInfo *t) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Special After Transform Graph + * \{ */ + +void special_aftertrans_update__graph(bContext *C, TransInfo *t) +{ + SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first; + bAnimContext ac; + const bool use_handle = (sipo->flag & SIPO_NOHANDLES) == 0; + + const bool canceled = (t->state == TRANS_CANCEL); + const bool duplicate = (t->mode == TFM_TIME_DUPLICATE); + + /* initialize relevant anim-context 'context' data */ + if (ANIM_animdata_get_context(C, &ac) == 0) { + return; + } + + if (ac.datatype) { + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE); + + /* get channels to work on */ + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + for (ale = anim_data.first; ale; ale = ale->next) { + AnimData *adt = ANIM_nla_mapping_get(&ac, ale); + FCurve *fcu = (FCurve *)ale->key_data; + + /* 3 cases here for curve cleanups: + * 1) NOTRANSKEYCULL on -> cleanup of duplicates shouldn't be done + * 2) canceled == 0 -> user confirmed the transform, + * so duplicates should be removed + * 3) canceled + duplicate -> user canceled the transform, + * but we made duplicates, so get rid of these + */ + if ((sipo->flag & SIPO_NOTRANSKEYCULL) == 0 && ((canceled == 0) || (duplicate))) { + if (adt) { + ANIM_nla_mapping_apply_fcurve(adt, fcu, 0, 0); + posttrans_fcurve_clean(fcu, BEZT_FLAG_TEMP_TAG, use_handle); + ANIM_nla_mapping_apply_fcurve(adt, fcu, 1, 0); + } + else { + posttrans_fcurve_clean(fcu, BEZT_FLAG_TEMP_TAG, use_handle); + } + } + } + + /* free temp memory */ + ANIM_animdata_freelist(&anim_data); + } + + /* Make sure all F-Curves are set correctly, but not if transform was + * canceled, since then curves were already restored to initial state. + * Note: if the refresh is really needed after cancel then some way + * has to be added to not update handle types (see bug 22289). + */ + if (!canceled) { + ANIM_editkeyframes_refresh(&ac); + } +} + +/** \} */ diff --git a/source/blender/editors/transform/transform_convert_mask.c b/source/blender/editors/transform/transform_convert_mask.c index 07b8495d3bd..7be0d43d854 100644 --- a/source/blender/editors/transform/transform_convert_mask.c +++ b/source/blender/editors/transform/transform_convert_mask.c @@ -33,8 +33,13 @@ #include "BKE_report.h" #include "ED_clip.h" +#include "ED_image.h" +#include "ED_keyframing.h" #include "ED_mask.h" +#include "WM_api.h" +#include "WM_types.h" + #include "transform.h" #include "transform_convert.h" @@ -449,3 +454,45 @@ void recalcData_mask_common(TransInfo *t) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Special After Transform Mask + * \{ */ + +void special_aftertrans_update__mask(bContext *C, TransInfo *t) +{ + Mask *mask = NULL; + + if (t->spacetype == SPACE_CLIP) { + SpaceClip *sc = t->area->spacedata.first; + mask = ED_space_clip_get_mask(sc); + } + else if (t->spacetype == SPACE_IMAGE) { + SpaceImage *sima = t->area->spacedata.first; + mask = ED_space_image_get_mask(sima); + } + else { + BLI_assert(0); + } + + if (t->scene->nodetree) { + /* tracks can be used for stabilization nodes, + * flush update for such nodes */ + // if (nodeUpdateID(t->scene->nodetree, &mask->id)) + { + WM_event_add_notifier(C, NC_MASK | ND_DATA, &mask->id); + } + } + + /* TODO - dont key all masks... */ + if (IS_AUTOKEY_ON(t->scene)) { + Scene *scene = t->scene; + + if (ED_mask_layer_shape_auto_key_select(mask, CFRA)) { + WM_event_add_notifier(C, NC_MASK | ND_DATA, &mask->id); + DEG_id_tag_update(&mask->id, 0); + } + } +} + +/** \} */ diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index 710ddf60176..598604eac0d 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -1402,3 +1402,71 @@ void recalcData_mesh(TransInfo *t) } } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Special After Transform Mesh + * \{ */ + +void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t) +{ + const bool canceled = (t->state == TRANS_CANCEL); + if (t->mode == TFM_EDGE_SLIDE) { + /* handle multires re-projection, done + * on transform completion since it's + * really slow -joeedh */ + projectEdgeSlideData(t, !canceled); + } + else if (t->mode == TFM_VERT_SLIDE) { + /* as above */ + projectVertSlideData(t, !canceled); + } + + bool use_automerge = !canceled && (t->flag & (T_AUTOMERGE | T_AUTOSPLIT)) != 0; + if (use_automerge) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); + BMesh *bm = em->bm; + char hflag; + bool has_face_sel = (bm->totfacesel != 0); + + if (tc->use_mirror_axis_any) { + /* Rather then adjusting the selection (which the user would notice) + * tag all mirrored verts, then auto-merge those. */ + BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_TAG, false); + + TransDataMirror *td_mirror = tc->data_mirror; + for (int i = tc->data_mirror_len; i--; td_mirror++) { + BM_elem_flag_enable((BMVert *)td_mirror->extra, BM_ELEM_TAG); + } + + hflag = BM_ELEM_SELECT | BM_ELEM_TAG; + } + else { + hflag = BM_ELEM_SELECT; + } + + if (t->flag & T_AUTOSPLIT) { + EDBM_automerge_and_split( + tc->obedit, true, true, true, hflag, t->scene->toolsettings->doublimit); + } + else { + EDBM_automerge(tc->obedit, true, hflag, t->scene->toolsettings->doublimit); + } + + /* Special case, this is needed or faces won't re-select. + * Flush selected edges to faces. */ + if (has_face_sel && (em->selectmode == SCE_SELECT_FACE)) { + EDBM_selectmode_flush_ex(em, SCE_SELECT_EDGE); + } + } + } + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + /* table needs to be created for each edit command, since vertices can move etc */ + ED_mesh_mirror_spatial_table_end(tc->obedit); + /* TODO(campbell): xform: We need support for many mirror objects at once! */ + break; + } +} +/** \} */ diff --git a/source/blender/editors/transform/transform_convert_nla.c b/source/blender/editors/transform/transform_convert_nla.c index 6ee2e191e69..03da979bbd3 100644 --- a/source/blender/editors/transform/transform_convert_nla.c +++ b/source/blender/editors/transform/transform_convert_nla.c @@ -36,6 +36,8 @@ #include "ED_anim_api.h" #include "ED_markers.h" +#include "WM_api.h" + #include "RNA_access.h" #include "transform.h" @@ -513,3 +515,44 @@ void recalcData_nla(TransInfo *t) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Special After Transform NLA + * \{ */ + +void special_aftertrans_update__nla(bContext *C, TransInfo *UNUSED(t)) +{ + bAnimContext ac; + + /* initialize relevant anim-context 'context' data */ + if (ANIM_animdata_get_context(C, &ac) == 0) { + return; + } + + if (ac.datatype) { + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT); + + /* get channels to work on */ + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + for (ale = anim_data.first; ale; ale = ale->next) { + NlaTrack *nlt = (NlaTrack *)ale->data; + + /* make sure strips are in order again */ + BKE_nlatrack_sort_strips(nlt); + + /* remove the temp metas */ + BKE_nlastrips_clear_metas(&nlt->strips, 0, 1); + } + + /* free temp memory */ + ANIM_animdata_freelist(&anim_data); + + /* perform after-transfrom validation */ + ED_nla_postop_refresh(&ac); + } +} + +/** \} */ diff --git a/source/blender/editors/transform/transform_convert_node.c b/source/blender/editors/transform/transform_convert_node.c index d23a8f960c1..38db8708d0c 100644 --- a/source/blender/editors/transform/transform_convert_node.c +++ b/source/blender/editors/transform/transform_convert_node.c @@ -194,3 +194,39 @@ void flushTransNodes(TransInfo *t) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Special After Transform Node + * \{ */ + +void special_aftertrans_update__node(bContext *C, TransInfo *t) +{ + struct Main *bmain = CTX_data_main(C); + const bool canceled = (t->state == TRANS_CANCEL); + + SpaceNode *snode = (SpaceNode *)t->area->spacedata.first; + if (canceled && t->remove_on_cancel) { + /* remove selected nodes on cancel */ + bNodeTree *ntree = snode->edittree; + if (ntree) { + bNode *node, *node_next; + for (node = ntree->nodes.first; node; node = node_next) { + node_next = node->next; + if (node->flag & NODE_SELECT) { + nodeRemoveNode(bmain, ntree, node, true); + } + } + ntreeUpdateTree(bmain, ntree); + } + } + + if (!canceled) { + ED_node_post_apply_transform(C, snode->edittree); + ED_node_link_insert(bmain, t->area); + } + + /* clear link line */ + ED_node_link_intersect_test(t->area, 0); +} + +/** \} */ diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c index 1e33b9653f9..92ed477eb25 100644 --- a/source/blender/editors/transform/transform_convert_object.c +++ b/source/blender/editors/transform/transform_convert_object.c @@ -30,10 +30,12 @@ #include "BLI_listbase.h" #include "BLI_math.h" +#include "BKE_animsys.h" #include "BKE_context.h" #include "BKE_layer.h" #include "BKE_main.h" #include "BKE_object.h" +#include "BKE_pointcache.h" #include "BKE_report.h" #include "BKE_rigidbody.h" #include "BKE_scene.h" @@ -466,7 +468,7 @@ static int count_proportional_objects(TransInfo *t) return total; } -void clear_trans_object_base_flags(TransInfo *t) +static void clear_trans_object_base_flags(TransInfo *t) { ViewLayer *view_layer = t->view_layer; Base *base; @@ -768,6 +770,149 @@ void createTransTexspace(TransInfo *t) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Transform (Auto-Keyframing) + * \{ */ + +/** + * Auto-keyframing feature - for objects + * + * \param tmode: A transform mode. + * + * \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_object( + bContext *C, Scene *scene, ViewLayer *view_layer, Object *ob, int tmode) +{ + Main *bmain = CTX_data_main(C); + ID *id = &ob->id; + FCurve *fcu; + + // TODO: this should probably be done per channel instead... + if (autokeyframe_cfra_can_key(scene, id)) { + ReportList *reports = CTX_wm_reports(C); + ToolSettings *ts = scene->toolsettings; + KeyingSet *active_ks = ANIM_scene_get_active_keyingset(scene); + ListBase dsources = {NULL, NULL}; + float cfra = (float)CFRA; // xxx this will do for now + eInsertKeyFlags flag = 0; + + /* Get flags used for inserting keyframes. */ + flag = ANIM_get_keyframing_flags(scene, true); + + /* add datasource override for the object */ + ANIM_relative_keyingset_add_source(&dsources, id, NULL, NULL); + + if (IS_AUTOKEY_FLAG(scene, ONLYKEYINGSET) && (active_ks)) { + /* Only insert into active keyingset + * NOTE: we assume here that the active Keying Set + * does not need to have its iterator overridden. + */ + ANIM_apply_keyingset(C, &dsources, NULL, active_ks, MODIFYKEY_MODE_INSERT, cfra); + } + else if (IS_AUTOKEY_FLAG(scene, INSERTAVAIL)) { + AnimData *adt = ob->adt; + + /* only key on available channels */ + if (adt && adt->action) { + ListBase nla_cache = {NULL, NULL}; + for (fcu = adt->action->curves.first; fcu; fcu = fcu->next) { + insert_keyframe(bmain, + reports, + id, + adt->action, + (fcu->grp ? fcu->grp->name : NULL), + fcu->rna_path, + fcu->array_index, + cfra, + ts->keyframe_type, + &nla_cache, + flag); + } + + BKE_animsys_free_nla_keyframing_context_cache(&nla_cache); + } + } + 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_VIE3D) */ + if (tmode == TFM_TRANSLATION) { + do_loc = true; + } + else if (ELEM(tmode, TFM_ROTATION, TFM_TRACKBALL)) { + if (scene->toolsettings->transform_pivot_point == V3D_AROUND_ACTIVE) { + if (ob != OBACT(view_layer)) { + do_loc = true; + } + } + else if (scene->toolsettings->transform_pivot_point == V3D_AROUND_CURSOR) { + do_loc = true; + } + + if ((scene->toolsettings->transform_flag & SCE_XFORM_AXIS_ALIGN) == 0) { + do_rot = true; + } + } + else if (tmode == TFM_RESIZE) { + if (scene->toolsettings->transform_pivot_point == V3D_AROUND_ACTIVE) { + if (ob != OBACT(view_layer)) { + do_loc = true; + } + } + else if (scene->toolsettings->transform_pivot_point == V3D_AROUND_CURSOR) { + do_loc = true; + } + + if ((scene->toolsettings->transform_flag & SCE_XFORM_AXIS_ALIGN) == 0) { + do_scale = true; + } + } + + /* insert keyframes for the affected sets of channels using the builtin KeyingSets found */ + 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 { + 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); + } +} + +/* 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_object(Scene *scene, Object *ob) +{ + /* XXX: there's potential here for problems with unkeyed rotations/scale, + * but for now (until proper data-locality for baking operations), + * this should be a better fix for T24451 and T37755 + */ + + if (autokeyframe_cfra_can_key(scene, &ob->id)) { + return (ob->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) != 0; + } + + return false; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Recalc Data object * * \{ */ @@ -829,3 +974,81 @@ void recalcData_objects(TransInfo *t) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Special After Transform Object + * \{ */ + +void special_aftertrans_update__object(bContext *C, TransInfo *t) +{ + BLI_assert(t->flag & (T_OBJECT | T_TEXTURE)); + + Object *ob; + const bool canceled = (t->state == TRANS_CANCEL); + + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + bool motionpath_update = false; + + for (int i = 0; i < tc->data_len; i++) { + TransData *td = tc->data + i; + ListBase pidlist; + PTCacheID *pid; + ob = td->ob; + + if (td->flag & TD_SKIP) { + continue; + } + + /* flag object caches as outdated */ + BKE_ptcache_ids_from_object(&pidlist, ob, t->scene, MAX_DUPLI_RECUR); + for (pid = pidlist.first; pid; pid = pid->next) { + if (pid->type != PTCACHE_TYPE_PARTICLES) { + /* particles don't need reset on geometry change */ + pid->cache->flag |= PTCACHE_OUTDATED; + } + } + BLI_freelistN(&pidlist); + + /* pointcache refresh */ + if (BKE_ptcache_object_reset(t->scene, ob, PTCACHE_RESET_OUTDATED)) { + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + } + + /* Needed for proper updating of "quick cached" dynamics. */ + /* Creates troubles for moving animated objects without */ + /* autokey though, probably needed is an anim sys override? */ + /* Please remove if some other solution is found. -jahka */ + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); + + /* Set autokey if necessary */ + if (!canceled) { + autokeyframe_object(C, t->scene, t->view_layer, ob, t->mode); + } + + motionpath_update |= motionpath_need_update_object(t->scene, ob); + + /* restore rigid body transform */ + if (ob->rigidbody_object && canceled) { + float ctime = BKE_scene_frame_get(t->scene); + if (BKE_rigidbody_check_sim_running(t->scene->rigidbody_world, ctime)) { + BKE_rigidbody_aftertrans_update(ob, + td->ext->oloc, + td->ext->orot, + td->ext->oquat, + td->ext->orotAxis, + td->ext->orotAngle); + } + } + } + + if (motionpath_update) { + /* Update motion paths once for all transformed objects. */ + const eObjectPathCalcRange range = canceled ? OBJECT_PATH_CALC_RANGE_CURRENT_FRAME : + OBJECT_PATH_CALC_RANGE_CHANGED; + ED_objects_recalculate_paths(C, t->scene, range); + } + + clear_trans_object_base_flags(t); +} + +/** \} */ diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c index b0ef624192a..e298061cb6b 100644 --- a/source/blender/editors/transform/transform_convert_sequencer.c +++ b/source/blender/editors/transform/transform_convert_sequencer.c @@ -31,6 +31,8 @@ #include "BKE_report.h" #include "BKE_sequencer.h" +#include "ED_markers.h" + #include "UI_view2d.h" #include "transform.h" @@ -808,6 +810,41 @@ void recalcData_sequencer(TransInfo *t) flushTransSeq(t); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Special After Transform Sequencer + * \{ */ + +void special_aftertrans_update__sequencer(bContext *UNUSED(C), TransInfo *t) +{ + if (t->state == TRANS_CANCEL) { + return; + } + /* freeSeqData in transform_conversions.c does this + * keep here so the else at the end wont run... */ + + SpaceSeq *sseq = (SpaceSeq *)t->area->spacedata.first; + + /* Marker transform, not especially nice but we may want to move markers + * at the same time as strips in the Video Sequencer. */ + if (sseq->flag & SEQ_MARKER_TRANS) { + /* cant use TFM_TIME_EXTEND + * for some reason EXTEND is changed into TRANSLATE, so use frame_side instead */ + + if (t->mode == TFM_SEQ_SLIDE) { + if (t->frame_side == 'B') { + ED_markers_post_apply_transform( + &t->scene->markers, t->scene, TFM_TIME_TRANSLATE, t->values[0], t->frame_side); + } + } + else if (ELEM(t->frame_side, 'L', 'R')) { + ED_markers_post_apply_transform( + &t->scene->markers, t->scene, TFM_TIME_EXTEND, t->values[0], t->frame_side); + } + } +} + int transform_convert_sequencer_get_snap_bound(TransInfo *t) { TransSeq *ts = TRANS_DATA_CONTAINER_FIRST_SINGLE(t)->custom.type.data; diff --git a/source/blender/editors/transform/transform_convert_tracking.c b/source/blender/editors/transform/transform_convert_tracking.c index 0f00f5bc318..36c6116b575 100644 --- a/source/blender/editors/transform/transform_convert_tracking.c +++ b/source/blender/editors/transform/transform_convert_tracking.c @@ -25,15 +25,20 @@ #include "MEM_guardedalloc.h" +#include "BLI_listbase.h" #include "BLI_math.h" #include "BKE_context.h" #include "BKE_movieclip.h" +#include "BKE_node.h" #include "BKE_report.h" #include "BKE_tracking.h" #include "ED_clip.h" +#include "WM_api.h" +#include "WM_types.h" + #include "transform.h" #include "transform_convert.h" @@ -546,7 +551,14 @@ void createTransTrackingData(bContext *C, TransInfo *t) } } -void cancelTransTracking(TransInfo *t) +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name recalc Motion Tracking TransData + * + * \{ */ + +static void cancelTransTracking(TransInfo *t) { TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); SpaceClip *sc = t->area->spacedata.first; @@ -605,13 +617,6 @@ void cancelTransTracking(TransInfo *t) } } -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name recalc Motion Tracking TransData - * - * \{ */ - static void flushTransTracking(TransInfo *t) { TransData *td; @@ -738,3 +743,47 @@ void recalcData_tracking(TransInfo *t) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Special After Transform Tracking + * \{ */ + +void special_aftertrans_update__movieclip(bContext *C, TransInfo *t) +{ + SpaceClip *sc = t->area->spacedata.first; + MovieClip *clip = ED_space_clip_get_clip(sc); + ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(&clip->tracking); + const int framenr = ED_space_clip_get_clip_frame_number(sc); + /* Update coordinates of modified plane tracks. */ + LISTBASE_FOREACH (MovieTrackingPlaneTrack *, plane_track, plane_tracks_base) { + bool do_update = false; + if (plane_track->flag & PLANE_TRACK_HIDDEN) { + continue; + } + do_update |= PLANE_TRACK_VIEW_SELECTED(plane_track) != 0; + if (do_update == false) { + if ((plane_track->flag & PLANE_TRACK_AUTOKEY) == 0) { + int i; + for (i = 0; i < plane_track->point_tracksnr; i++) { + MovieTrackingTrack *track = plane_track->point_tracks[i]; + if (TRACK_VIEW_SELECTED(sc, track)) { + do_update = true; + break; + } + } + } + } + if (do_update) { + BKE_tracking_track_plane_from_existing_motion(plane_track, framenr); + } + } + if (t->scene->nodetree != NULL) { + /* Tracks can be used for stabilization nodes, + * flush update for such nodes. + */ + nodeUpdateID(t->scene->nodetree, &clip->id); + WM_event_add_notifier(C, NC_SCENE | ND_NODES, NULL); + } +} + +/** \} */ |