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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorGermano Cavalcante <germano.costa@ig.com.br>2020-06-10 02:27:40 +0300
committerGermano Cavalcante <germano.costa@ig.com.br>2020-06-10 02:27:51 +0300
commit46e0ec05ef1fb75fb3e5b4a713a5302ace49e546 (patch)
tree8eff449f33050c445ee3367c941da57b8b61882e /source
parent63a40ed422bc61e9cd952282a636c53a9d8f3fde (diff)
Cleanup: Move each special_aftertrans_update to their respective TransData file
Diffstat (limited to 'source')
-rw-r--r--source/blender/editors/transform/transform_convert.c1605
-rw-r--r--source/blender/editors/transform/transform_convert.h33
-rw-r--r--source/blender/editors/transform/transform_convert_action.c303
-rw-r--r--source/blender/editors/transform/transform_convert_armature.c844
-rw-r--r--source/blender/editors/transform/transform_convert_graph.c273
-rw-r--r--source/blender/editors/transform/transform_convert_mask.c47
-rw-r--r--source/blender/editors/transform/transform_convert_mesh.c68
-rw-r--r--source/blender/editors/transform/transform_convert_nla.c43
-rw-r--r--source/blender/editors/transform/transform_convert_node.c36
-rw-r--r--source/blender/editors/transform/transform_convert_object.c225
-rw-r--r--source/blender/editors/transform/transform_convert_sequencer.c37
-rw-r--r--source/blender/editors/transform/transform_convert_tracking.c65
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);
+ }
+}
+
+/** \} */