Merge branch 'master' into blender2.8
authorCampbell Barton <ideasman42@gmail.com>
Mon, 23 Apr 2018 10:59:57 +0000 (12:59 +0200)
committerCampbell Barton <ideasman42@gmail.com>
Mon, 23 Apr 2018 10:59:57 +0000 (12:59 +0200)
1  2 
source/blender/editors/transform/transform_generics.c

index a5ff091321e562556641d1ca7f552498385cb2bd,1ff602e075e82922cc68e0a01eb27797b1a1c36c..08f54a75dcf903df4ac8c78f9ec7979b03f584e3
@@@ -62,8 -62,8 +62,8 @@@
  
  #include "RNA_access.h"
  
 -#include "BIF_gl.h"
 -#include "BIF_glutil.h"
 +#include "GPU_immediate.h"
 +#include "GPU_matrix.h"
  
  #include "BIK_api.h"
  
@@@ -71,6 -71,7 +71,6 @@@
  #include "BKE_action.h"
  #include "BKE_armature.h"
  #include "BKE_curve.h"
 -#include "BKE_depsgraph.h"
  #include "BKE_fcurve.h"
  #include "BKE_lattice.h"
  #include "BKE_library.h"
  #include "BKE_editmesh.h"
  #include "BKE_tracking.h"
  #include "BKE_mask.h"
 +#include "BKE_workspace.h"
 +#include "BKE_layer.h"
 +#include "BKE_scene.h"
 +
 +#include "DEG_depsgraph.h"
  
  #include "ED_anim_api.h"
  #include "ED_armature.h"
  #include "WM_types.h"
  #include "WM_api.h"
  
 +#include "RE_engine.h"
 +
  #include "UI_resources.h"
  #include "UI_view2d.h"
  
@@@ -129,101 -123,98 +129,101 @@@ void getViewVector(TransInfo *t, float 
  /* ************************** GENERICS **************************** */
  
  
 -static void clipMirrorModifier(TransInfo *t, Object *ob)
 +static void clipMirrorModifier(TransInfo *t)
  {
 -      ModifierData *md = ob->modifiers.first;
 -      float tolerance[3] = {0.0f, 0.0f, 0.0f};
 -      int axis = 0;
 -      
 -      for (; md; md = md->next) {
 -              if ((md->type == eModifierType_Mirror) && (md->mode & eModifierMode_Realtime)) {
 -                      MirrorModifierData *mmd = (MirrorModifierData *) md;
 -                      
 -                      if (mmd->flag & MOD_MIR_CLIPPING) {
 -                              axis = 0;
 -                              if (mmd->flag & MOD_MIR_AXIS_X) {
 -                                      axis |= 1;
 -                                      tolerance[0] = mmd->tolerance;
 -                              }
 -                              if (mmd->flag & MOD_MIR_AXIS_Y) {
 -                                      axis |= 2;
 -                                      tolerance[1] = mmd->tolerance;
 -                              }
 -                              if (mmd->flag & MOD_MIR_AXIS_Z) {
 -                                      axis |= 4;
 -                                      tolerance[2] = mmd->tolerance;
 -                              }
 -                              if (axis) {
 -                                      float mtx[4][4], imtx[4][4];
 -                                      int i;
 -                                      TransData *td = t->data;
 -                                      
 -                                      if (mmd->mirror_ob) {
 -                                              float obinv[4][4];
 -                                              
 -                                              invert_m4_m4(obinv, mmd->mirror_ob->obmat);
 -                                              mul_m4_m4m4(mtx, obinv, ob->obmat);
 -                                              invert_m4_m4(imtx, mtx);
 +      FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 +              Object *ob = tc->obedit;
 +              ModifierData *md = ob->modifiers.first;
 +              float tolerance[3] = {0.0f, 0.0f, 0.0f};
 +              int axis = 0;
 +
 +              for (; md; md = md->next) {
 +                      if ((md->type == eModifierType_Mirror) && (md->mode & eModifierMode_Realtime)) {
 +                              MirrorModifierData *mmd = (MirrorModifierData *) md;
 +
 +                              if (mmd->flag & MOD_MIR_CLIPPING) {
 +                                      axis = 0;
 +                                      if (mmd->flag & MOD_MIR_AXIS_X) {
 +                                              axis |= 1;
 +                                              tolerance[0] = mmd->tolerance;
                                        }
 -                                      
 -                                      for (i = 0; i < t->total; i++, td++) {
 -                                              int clip;
 -                                              float loc[3], iloc[3];
 -                                              
 -                                              if (td->flag & TD_NOACTION)
 -                                                      break;
 -                                              if (td->loc == NULL)
 -                                                      break;
 -                                              
 -                                              if (td->flag & TD_SKIP)
 -                                                      continue;
 -                                              
 -                                              copy_v3_v3(loc,  td->loc);
 -                                              copy_v3_v3(iloc, td->iloc);
 -                                              
 +                                      if (mmd->flag & MOD_MIR_AXIS_Y) {
 +                                              axis |= 2;
 +                                              tolerance[1] = mmd->tolerance;
 +                                      }
 +                                      if (mmd->flag & MOD_MIR_AXIS_Z) {
 +                                              axis |= 4;
 +                                              tolerance[2] = mmd->tolerance;
 +                                      }
 +                                      if (axis) {
 +                                              float mtx[4][4], imtx[4][4];
 +                                              int i;
 +
                                                if (mmd->mirror_ob) {
 -                                                      mul_m4_v3(mtx, loc);
 -                                                      mul_m4_v3(mtx, iloc);
 +                                                      float obinv[4][4];
 +
 +                                                      invert_m4_m4(obinv, mmd->mirror_ob->obmat);
 +                                                      mul_m4_m4m4(mtx, obinv, ob->obmat);
 +                                                      invert_m4_m4(imtx, mtx);
                                                }
 -                                              
 -                                              clip = 0;
 -                                              if (axis & 1) {
 -                                                      if (fabsf(iloc[0]) <= tolerance[0] ||
 -                                                          loc[0] * iloc[0] < 0.0f)
 -                                                      {
 -                                                              loc[0] = 0.0f;
 -                                                              clip = 1;
 +
 +                                              TransData *td = tc->data;
 +                                              for (i = 0; i < tc->data_len; i++, td++) {
 +                                                      int clip;
 +                                                      float loc[3], iloc[3];
 +
 +                                                      if (td->flag & TD_NOACTION)
 +                                                              break;
 +                                                      if (td->loc == NULL)
 +                                                              break;
 +
 +                                                      if (td->flag & TD_SKIP)
 +                                                              continue;
 +
 +                                                      copy_v3_v3(loc,  td->loc);
 +                                                      copy_v3_v3(iloc, td->iloc);
 +
 +                                                      if (mmd->mirror_ob) {
 +                                                              mul_m4_v3(mtx, loc);
 +                                                              mul_m4_v3(mtx, iloc);
                                                        }
 -                                              }
 -                                              
 -                                              if (axis & 2) {
 -                                                      if (fabsf(iloc[1]) <= tolerance[1] ||
 -                                                          loc[1] * iloc[1] < 0.0f)
 -                                                      {
 -                                                              loc[1] = 0.0f;
 -                                                              clip = 1;
 +
 +                                                      clip = 0;
 +                                                      if (axis & 1) {
 +                                                              if (fabsf(iloc[0]) <= tolerance[0] ||
 +                                                                  loc[0] * iloc[0] < 0.0f)
 +                                                              {
 +                                                                      loc[0] = 0.0f;
 +                                                                      clip = 1;
 +                                                              }
                                                        }
 -                                              }
 -                                              if (axis & 4) {
 -                                                      if (fabsf(iloc[2]) <= tolerance[2] ||
 -                                                          loc[2] * iloc[2] < 0.0f)
 -                                                      {
 -                                                              loc[2] = 0.0f;
 -                                                              clip = 1;
 +
 +                                                      if (axis & 2) {
 +                                                              if (fabsf(iloc[1]) <= tolerance[1] ||
 +                                                                  loc[1] * iloc[1] < 0.0f)
 +                                                              {
 +                                                                      loc[1] = 0.0f;
 +                                                                      clip = 1;
 +                                                              }
                                                        }
 -                                              }
 -                                              if (clip) {
 -                                                      if (mmd->mirror_ob) {
 -                                                              mul_m4_v3(imtx, loc);
 +                                                      if (axis & 4) {
 +                                                              if (fabsf(iloc[2]) <= tolerance[2] ||
 +                                                                  loc[2] * iloc[2] < 0.0f)
 +                                                              {
 +                                                                      loc[2] = 0.0f;
 +                                                                      clip = 1;
 +                                                              }
 +                                                      }
 +                                                      if (clip) {
 +                                                              if (mmd->mirror_ob) {
 +                                                                      mul_m4_v3(imtx, loc);
 +                                                              }
 +                                                              copy_v3_v3(td->loc, loc);
                                                        }
 -                                                      copy_v3_v3(td->loc, loc);
                                                }
                                        }
 +
                                }
 -                              
                        }
                }
        }
  /* assumes obedit set to mesh object */
  static void editbmesh_apply_to_mirror(TransInfo *t)
  {
 -      TransData *td = t->data;
 -      BMVert *eve;
 -      int i;
 -      
 -      for (i = 0; i < t->total; i++, td++) {
 -              if (td->flag & TD_NOACTION)
 -                      break;
 -              if (td->loc == NULL)
 -                      break;
 -              if (td->flag & TD_SKIP)
 -                      continue;
 -              
 -              eve = td->extra;
 -              if (eve) {
 -                      eve->co[0] = -td->loc[0];
 -                      eve->co[1] = td->loc[1];
 -                      eve->co[2] = td->loc[2];
 -              }
 -              
 -              if (td->flag & TD_MIRROR_EDGE) {
 -                      td->loc[0] = 0;
 +      FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 +              TransData *td = tc->data;
 +              BMVert *eve;
 +              int i;
 +
 +              for (i = 0; i < tc->data_len; i++, td++) {
 +                      if (td->flag & TD_NOACTION)
 +                              break;
 +                      if (td->loc == NULL)
 +                              break;
 +                      if (td->flag & TD_SKIP)
 +                              continue;
 +
 +                      eve = td->extra;
 +                      if (eve) {
 +                              eve->co[0] = -td->loc[0];
 +                              eve->co[1] = td->loc[1];
 +                              eve->co[2] = td->loc[2];
 +                      }
 +
 +                      if (td->flag & TD_MIRROR_EDGE) {
 +                              td->loc[0] = 0;
 +                      }
                }
        }
  }
@@@ -277,6 -266,7 +277,7 @@@ static void animrecord_check_state(Scen
                /* if playback has just looped around, we need to add a new NLA track+strip to allow a clean pass to occur */
                if ((sad) && (sad->flag & ANIMPLAY_FLAG_JUMPED)) {
                        AnimData *adt = BKE_animdata_from_id(id);
+                       const bool is_first = (adt) && (adt->nla_tracks.first == NULL);
                        
                        /* perform push-down manually with some differences 
                         * NOTE: BKE_nla_action_pushdown() sync warning...
                                        strip->extendmode = NLASTRIP_EXTEND_NOTHING;
                                        strip->flag &= ~(NLASTRIP_FLAG_AUTO_BLENDS | NLASTRIP_FLAG_SELECT | NLASTRIP_FLAG_ACTIVE);
                                        
+                                       /* copy current "action blending" settings from adt to the strip,
+                                        * as it was keyframed with these settings, so omitting them will
+                                        * change the effect  [T54766]
+                                        */
+                                       if (is_first == false) {
+                                               strip->blendmode = adt->act_blendmode;
+                                               strip->influence = adt->act_influence;
+                                               
+                                               if (adt->act_influence < 1.0f) {
+                                                       /* enable "user-controlled" influence (which will insert a default keyframe)
+                                                        * so that the influence doesn't get lost on the new update
+                                                        *
+                                                        * NOTE: An alternative way would have been to instead hack the influence
+                                                        * to not get always get reset to full strength if NLASTRIP_FLAG_USR_INFLUENCE
+                                                        * is disabled but auto-blending isn't being used. However, that approach
+                                                        * is a bit hacky/hard to discover, and may cause backwards compatability issues,
+                                                        * so it's better to just do it this way.
+                                                        */
+                                                       strip->flag |= NLASTRIP_FLAG_USR_INFLUENCE;
+                                                       BKE_nlastrip_validate_fcurves(strip);
+                                               }
+                                       }
+                                       
                                        /* also, adjust the AnimData's action extend mode to be on 
                                         * 'nothing' so that previous result still play 
                                         */
@@@ -325,7 -338,7 +349,7 @@@ static bool fcu_test_selected(FCurve *f
  /* helper for recalcData() - for Action Editor transforms */
  static void recalcData_actedit(TransInfo *t)
  {
 -      Scene *scene = t->scene;
 +      ViewLayer *view_layer = t->view_layer;
        SpaceAction *saction = (SpaceAction *)t->sa->spacedata.first;
        
        bAnimContext ac = {NULL};
        /* initialize relevant anim-context 'context' data from TransInfo data */
        /* NOTE: sync this with the code in ANIM_animdata_get_context() */
        ac.scene = t->scene;
 -      ac.obact = OBACT;
 +      ac.view_layer = t->view_layer;
 +      ac.obact = OBACT(view_layer);
        ac.sa = t->sa;
        ac.ar = t->ar;
        ac.sl = (t->sa) ? t->sa->spacedata.first : NULL;
  static void recalcData_graphedit(TransInfo *t)
  {
        SpaceIpo *sipo = (SpaceIpo *)t->sa->spacedata.first;
 -      Scene *scene;
 +      ViewLayer *view_layer = t->view_layer;
        
        ListBase anim_data = {NULL, NULL};
        bAnimContext ac = {NULL};
  
        /* initialize relevant anim-context 'context' data from TransInfo data */
        /* NOTE: sync this with the code in ANIM_animdata_get_context() */
 -      scene = ac.scene = t->scene;
 -      ac.obact = OBACT;
 +      ac.scene = t->scene;
 +      ac.view_layer = t->view_layer;
 +      ac.obact = OBACT(view_layer);
        ac.sa = t->sa;
        ac.ar = t->ar;
        ac.sl = (t->sa) ? t->sa->spacedata.first : NULL;
@@@ -439,14 -450,12 +463,14 @@@ static void recalcData_nla(TransInfo *t
        Scene *scene = t->scene;
        double secf = FPS;
        int i;
 -      
 +
 +      TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
 +
        /* for each strip we've got, perform some additional validation of the values that got set before
         * using RNA to set the value (which does some special operations when setting these values to make
         * sure that everything works ok)
         */
 -      for (i = 0; i < t->total; i++, tdn++) {
 +      for (i = 0; i < tc->data_len; i++, tdn++) {
                NlaStrip *strip = tdn->strip;
                PointerRNA strip_ptr;
                short pExceeded, nExceeded, iter;
@@@ -651,7 -660,7 +675,7 @@@ static void recalcData_mask_common(Tran
  
        flushTransMasking(t);
  
 -      DAG_id_tag_update(&mask->id, 0);
 +      DEG_id_tag_update(&mask->id, 0);
  }
  
  /* helper for recalcData() - for Image Editor transforms */
@@@ -663,18 -672,14 +687,18 @@@ static void recalcData_image(TransInfo 
        else if (t->options & CTX_PAINT_CURVE) {
                flushTransPaintCurve(t);
        }
 -      else if (t->obedit && t->obedit->type == OB_MESH) {
 +      else if ((t->flag & T_EDIT) && t->obedit_type == OB_MESH) {
                SpaceImage *sima = t->sa->spacedata.first;
                
                flushTransUVs(t);
                if (sima->flag & SI_LIVE_UNWRAP)
                        ED_uvedit_live_unwrap_re_solve();
 -              
 -              DAG_id_tag_update(t->obedit->data, 0);
 +
 +              FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 +                      if (tc->data_len) {
 +                              DEG_id_tag_update(tc->obedit->data, 0);
 +                      }
 +              }
        }
  }
  
@@@ -717,7 -722,7 +741,7 @@@ static void recalcData_spaceclip(TransI
                        track = track->next;
                }
  
 -              DAG_id_tag_update(&clip->id, 0);
 +              DEG_id_tag_update(&clip->id, 0);
        }
        else if (t->options & CTX_MASK) {
                recalcData_mask_common(t);
  /* helper for recalcData() - for object transforms, typically in the 3D view */
  static void recalcData_objects(TransInfo *t)
  {
 -      Base *base = t->scene->basact;
 +      Base *base = t->view_layer->basact;
 +
 +      if (t->obedit_type != -1) {
 +              if (ELEM(t->obedit_type, OB_CURVE, OB_SURF)) {
  
 -      if (t->obedit) {
 -              if (ELEM(t->obedit->type, OB_CURVE, OB_SURF)) {
 -                      Curve *cu = t->obedit->data;
 -                      ListBase *nurbs = BKE_curve_editNurbs_get(cu);
 -                      Nurb *nu = nurbs->first;
 -                      
                        if (t->state != TRANS_CANCEL) {
 -                              clipMirrorModifier(t, t->obedit);
 +                              clipMirrorModifier(t);
                                applyProject(t);
                        }
 -                      
 -                      DAG_id_tag_update(t->obedit->data, 0);  /* sets recalc flags */
 -                              
 -                      if (t->state == TRANS_CANCEL) {
 -                              while (nu) {
 -                                      BKE_nurb_handles_calc(nu); /* Cant do testhandlesNurb here, it messes up the h1 and h2 flags */
 -                                      nu = nu->next;
 +
 +                      FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 +                              Curve *cu = tc->obedit->data;
 +                              ListBase *nurbs = BKE_curve_editNurbs_get(cu);
 +                              Nurb *nu = nurbs->first;
 +
 +                              DEG_id_tag_update(tc->obedit->data, 0);  /* sets recalc flags */
 +
 +                              if (t->state == TRANS_CANCEL) {
 +                                      while (nu) {
 +                                              BKE_nurb_handles_calc(nu); /* Cant do testhandlesNurb here, it messes up the h1 and h2 flags */
 +                                              nu = nu->next;
 +                                      }
                                }
 -                      }
 -                      else {
 -                              /* Normal updating */
 -                              while (nu) {
 -                                      BKE_nurb_test2D(nu);
 -                                      BKE_nurb_handles_calc(nu);
 -                                      nu = nu->next;
 +                              else {
 +                                      /* Normal updating */
 +                                      while (nu) {
 +                                              BKE_nurb_test2D(nu);
 +                                              BKE_nurb_handles_calc(nu);
 +                                              nu = nu->next;
 +                                      }
                                }
                        }
                }
 -              else if (t->obedit->type == OB_LATTICE) {
 -                      Lattice *la = t->obedit->data;
 -                      
 +              else if (t->obedit_type == OB_LATTICE) {
 +
                        if (t->state != TRANS_CANCEL) {
                                applyProject(t);
                        }
 -                      
 -                      DAG_id_tag_update(t->obedit->data, 0);  /* sets recalc flags */
 -                      
 -                      if (la->editlatt->latt->flag & LT_OUTSIDE) outside_lattice(la->editlatt->latt);
 +
 +                      FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 +                              Lattice *la = tc->obedit->data;
 +                              DEG_id_tag_update(tc->obedit->data, 0);  /* sets recalc flags */
 +                              if (la->editlatt->latt->flag & LT_OUTSIDE) {
 +                                      outside_lattice(la->editlatt->latt);
 +                              }
 +                      }
                }
 -              else if (t->obedit->type == OB_MESH) {
 -                      BMEditMesh *em = BKE_editmesh_from_object(t->obedit);
 +              else if (t->obedit_type == OB_MESH) {
                        /* mirror modifier clipping? */
                        if (t->state != TRANS_CANCEL) {
                                /* apply clipping after so we never project past the clip plane [#25423] */
                                applyProject(t);
 -                              clipMirrorModifier(t, t->obedit);
 +                              clipMirrorModifier(t);
                        }
                        if ((t->options & CTX_NO_MIRROR) == 0 && (t->flag & T_MIRROR))
                                editbmesh_apply_to_mirror(t);
                                projectVertSlideData(t, false);
                        }
  
 -                      DAG_id_tag_update(t->obedit->data, 0);  /* sets recalc flags */
 -                      
 -                      EDBM_mesh_normals_update(em);
 -                      BKE_editmesh_tessface_calc(em);
 -              }
 -              else if (t->obedit->type == OB_ARMATURE) { /* no recalc flag, does pose */
 -                      bArmature *arm = t->obedit->data;
 -                      ListBase *edbo = arm->edbo;
 -                      EditBone *ebo, *ebo_parent;
 -                      TransData *td = t->data;
 -                      int i;
 -                      
 +                      FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 +                              DEG_id_tag_update(tc->obedit->data, 0);  /* sets recalc flags */
 +                              BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
 +                              EDBM_mesh_normals_update(em);
 +                              BKE_editmesh_tessface_calc(em);
 +                      }
 +              }
 +              else if (t->obedit_type == OB_ARMATURE) { /* no recalc flag, does pose */
 +
                        if (t->state != TRANS_CANCEL) {
                                applyProject(t);
                        }
 -                      
 -                      /* Ensure all bones are correctly adjusted */
 -                      for (ebo = edbo->first; ebo; ebo = ebo->next) {
 -                              ebo_parent = (ebo->flag & BONE_CONNECTED) ? ebo->parent : NULL;
 -                              
 -                              if (ebo_parent) {
 -                                      /* If this bone has a parent tip that has been moved */
 -                                      if (ebo_parent->flag & BONE_TIPSEL) {
 -                                              copy_v3_v3(ebo->head, ebo_parent->tail);
 -                                              if (t->mode == TFM_BONE_ENVELOPE) ebo->rad_head = ebo_parent->rad_tail;
 +
 +                      FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 +                              bArmature *arm = tc->obedit->data;
 +                              ListBase *edbo = arm->edbo;
 +                              EditBone *ebo, *ebo_parent;
 +                              TransData *td = tc->data;
 +                              int i;
 +
 +                              /* Ensure all bones are correctly adjusted */
 +                              for (ebo = edbo->first; ebo; ebo = ebo->next) {
 +                                      ebo_parent = (ebo->flag & BONE_CONNECTED) ? ebo->parent : NULL;
 +
 +                                      if (ebo_parent) {
 +                                              /* If this bone has a parent tip that has been moved */
 +                                              if (ebo_parent->flag & BONE_TIPSEL) {
 +                                                      copy_v3_v3(ebo->head, ebo_parent->tail);
 +                                                      if (t->mode == TFM_BONE_ENVELOPE) ebo->rad_head = ebo_parent->rad_tail;
 +                                              }
 +                                              /* If this bone has a parent tip that has NOT been moved */
 +                                              else {
 +                                                      copy_v3_v3(ebo_parent->tail, ebo->head);
 +                                                      if (t->mode == TFM_BONE_ENVELOPE) ebo_parent->rad_tail = ebo->rad_head;
 +                                              }
                                        }
 -                                      /* If this bone has a parent tip that has NOT been moved */
 -                                      else {
 -                                              copy_v3_v3(ebo_parent->tail, ebo->head);
 -                                              if (t->mode == TFM_BONE_ENVELOPE) ebo_parent->rad_tail = ebo->rad_head;
 +
 +                                      /* on extrude bones, oldlength==0.0f, so we scale radius of points */
 +                                      ebo->length = len_v3v3(ebo->head, ebo->tail);
 +                                      if (ebo->oldlength == 0.0f) {
 +                                              ebo->rad_head = 0.25f * ebo->length;
 +                                              ebo->rad_tail = 0.10f * ebo->length;
 +                                              ebo->dist = 0.25f * ebo->length;
 +                                              if (ebo->parent) {
 +                                                      if (ebo->rad_head > ebo->parent->rad_tail)
 +                                                              ebo->rad_head = ebo->parent->rad_tail;
 +                                              }
                                        }
 -                              }
 -                              
 -                              /* on extrude bones, oldlength==0.0f, so we scale radius of points */
 -                              ebo->length = len_v3v3(ebo->head, ebo->tail);
 -                              if (ebo->oldlength == 0.0f) {
 -                                      ebo->rad_head = 0.25f * ebo->length;
 -                                      ebo->rad_tail = 0.10f * ebo->length;
 -                                      ebo->dist = 0.25f * ebo->length;
 -                                      if (ebo->parent) {
 -                                              if (ebo->rad_head > ebo->parent->rad_tail)
 -                                                      ebo->rad_head = ebo->parent->rad_tail;
 +                                      else if (t->mode != TFM_BONE_ENVELOPE) {
 +                                              /* if bones change length, lets do that for the deform distance as well */
 +                                              ebo->dist *= ebo->length / ebo->oldlength;
 +                                              ebo->rad_head *= ebo->length / ebo->oldlength;
 +                                              ebo->rad_tail *= ebo->length / ebo->oldlength;
 +                                              ebo->oldlength = ebo->length;
 +
 +                                              if (ebo_parent) {
 +                                                      ebo_parent->rad_tail = ebo->rad_head;
 +                                              }
                                        }
                                }
 -                              else if (t->mode != TFM_BONE_ENVELOPE) {
 -                                      /* if bones change length, lets do that for the deform distance as well */
 -                                      ebo->dist *= ebo->length / ebo->oldlength;
 -                                      ebo->rad_head *= ebo->length / ebo->oldlength;
 -                                      ebo->rad_tail *= ebo->length / ebo->oldlength;
 -                                      ebo->oldlength = ebo->length;
  
 -                                      if (ebo_parent) {
 -                                              ebo_parent->rad_tail = ebo->rad_head;
 -                                      }
 -                              }
 -                      }
 -                      
 -                      if (!ELEM(t->mode, TFM_BONE_ROLL, TFM_BONE_ENVELOPE, TFM_BONE_ENVELOPE_DIST, TFM_BONESIZE)) {
 -                              /* fix roll */
 -                              for (i = 0; i < t->total; i++, td++) {
 -                                      if (td->extra) {
 -                                              float vec[3], up_axis[3];
 -                                              float qrot[4];
 -                                              float roll;
 -                                              
 -                                              ebo = td->extra;
 +                              if (!ELEM(t->mode, TFM_BONE_ROLL, TFM_BONE_ENVELOPE, TFM_BONE_ENVELOPE_DIST, TFM_BONESIZE)) {
 +                                      /* fix roll */
 +                                      for (i = 0; i < tc->data_len; i++, td++) {
 +                                              if (td->extra) {
 +                                                      float vec[3], up_axis[3];
 +                                                      float qrot[4];
 +                                                      float roll;
  
 -                                              if (t->state == TRANS_CANCEL) {
 -                                                      /* restore roll */
 -                                                      ebo->roll = td->ival;
 -                                              }
 -                                              else {
 -                                                      copy_v3_v3(up_axis, td->axismtx[2]);
 +                                                      ebo = td->extra;
  
 -                                                      sub_v3_v3v3(vec, ebo->tail, ebo->head);
 -                                                      normalize_v3(vec);
 -                                                      rotation_between_vecs_to_quat(qrot, td->axismtx[1], vec);
 -                                                      mul_qt_v3(qrot, up_axis);
 +                                                      if (t->state == TRANS_CANCEL) {
 +                                                              /* restore roll */
 +                                                              ebo->roll = td->ival;
 +                                                      }
 +                                                      else {
 +                                                              copy_v3_v3(up_axis, td->axismtx[2]);
 +
 +                                                              sub_v3_v3v3(vec, ebo->tail, ebo->head);
 +                                                              normalize_v3(vec);
 +                                                              rotation_between_vecs_to_quat(qrot, td->axismtx[1], vec);
 +                                                              mul_qt_v3(qrot, up_axis);
  
 -                                                      /* roll has a tendency to flip in certain orientations - [#34283], [#33974] */
 -                                                      roll = ED_armature_ebone_roll_to_vector(ebo, up_axis, false);
 -                                                      ebo->roll = angle_compat_rad(roll, td->ival);
 +                                                              /* roll has a tendency to flip in certain orientations - [#34283], [#33974] */
 +                                                              roll = ED_armature_ebone_roll_to_vector(ebo, up_axis, false);
 +                                                              ebo->roll = angle_compat_rad(roll, td->ival);
 +                                                      }
                                                }
                                        }
                                }
 -                      }
 -                      
 -                      if (arm->flag & ARM_MIRROR_EDIT) {
 -                              if (t->state != TRANS_CANCEL)
 -                                      ED_armature_edit_transform_mirror_update(t->obedit);
 -                              else
 -                                      restoreBones(t);
 +
 +                              if (arm->flag & ARM_MIRROR_EDIT) {
 +                                      if (t->state != TRANS_CANCEL) {
 +                                              ED_armature_edit_transform_mirror_update(tc->obedit);
 +                                      }
 +                                      else {
 +                                              restoreBones(tc);
 +                                      }
 +                              }
                        }
                }
                else {
                        if (t->state != TRANS_CANCEL) {
                                applyProject(t);
                        }
 -                      DAG_id_tag_update(t->obedit->data, 0);  /* sets recalc flags */
 +                      FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 +                              if (tc->data_len) {
 +                                      DEG_id_tag_update(tc->obedit->data, 0);  /* sets recalc flags */
 +                              }
 +                      }
                }
 +
        }
 -      else if ((t->flag & T_POSE) && t->poseobj) {
 -              Object *ob = t->poseobj;
 -              bArmature *arm = ob->data;
 -              
 -              /* if animtimer is running, and the object already has animation data,
 -               * check if the auto-record feature means that we should record 'samples'
 -               * (i.e. uneditable animation values)
 -               *
 -               * context is needed for keying set poll() functions.
 -               */
 -              // TODO: autokeyframe calls need some setting to specify to add samples (FPoints) instead of keyframes?
 -              if ((t->animtimer) && (t->context) && IS_AUTOKEY_ON(t->scene)) {
 -                      int targetless_ik = (t->flag & T_AUTOIK); // XXX this currently doesn't work, since flags aren't set yet!
 -                      
 -                      animrecord_check_state(t->scene, &ob->id, t->animtimer);
 -                      autokeyframe_pose_cb_func(t->context, t->scene, (View3D *)t->view, ob, t->mode, targetless_ik);
 -              }
 -              
 -              /* old optimize trick... this enforces to bypass the depgraph */
 -              if (!(arm->flag & ARM_DELAYDEFORM)) {
 -                      DAG_id_tag_update(&ob->id, OB_RECALC_DATA);  /* sets recalc flags */
 -                      /* transformation of pose may affect IK tree, make sure it is rebuilt */
 -                      BIK_clear_data(ob->pose);
 +      else if (t->flag & T_POSE) {
 +              FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 +                      Object *ob = tc->poseobj;
 +                      bArmature *arm = ob->data;
 +
 +                      /* if animtimer is running, and the object already has animation data,
 +                       * check if the auto-record feature means that we should record 'samples'
 +                       * (i.e. uneditable animation values)
 +                       *
 +                       * context is needed for keying set poll() functions.
 +                       */
 +                      // TODO: autokeyframe calls need some setting to specify to add samples (FPoints) instead of keyframes?
 +                      if ((t->animtimer) && (t->context) && IS_AUTOKEY_ON(t->scene)) {
 +                              int targetless_ik = (t->flag & T_AUTOIK); // XXX this currently doesn't work, since flags aren't set yet!
 +
 +                              animrecord_check_state(t->scene, &ob->id, t->animtimer);
 +                              autokeyframe_pose_cb_func(t->context, t->scene, (View3D *)t->view, ob, t->mode, targetless_ik);
 +                      }
 +
 +                      /* old optimize trick... this enforces to bypass the depgraph */
 +                      if (!(arm->flag & ARM_DELAYDEFORM)) {
 +                              DEG_id_tag_update(&ob->id, OB_RECALC_DATA);  /* sets recalc flags */
 +                              /* transformation of pose may affect IK tree, make sure it is rebuilt */
 +                              BIK_clear_data(ob->pose);
 +                      }
 +                      else {
 +                              BKE_pose_where_is(t->depsgraph, t->scene, ob);
 +                      }
                }
 -              else
 -                      BKE_pose_where_is(t->scene, ob);
        }
 -      else if (base && (base->object->mode & OB_MODE_PARTICLE_EDIT) && PE_get_current(t->scene, base->object)) {
 +      else if (base && (base->object->mode & OB_MODE_PARTICLE_EDIT) &&
 +               PE_get_current(t->scene, base->object))
 +      {
                if (t->state != TRANS_CANCEL) {
                        applyProject(t);
                }
                if (t->state != TRANS_CANCEL) {
                        applyProject(t);
                }
 -              
 -              for (i = 0; i < t->total; i++) {
 -                      TransData *td = t->data + i;
 -                      Object *ob = td->ob;
 -                      
 -                      if (td->flag & TD_NOACTION)
 -                              break;
 -                      
 -                      if (td->flag & TD_SKIP)
 -                              continue;
 -                      
 -                      /* if animtimer is running, and the object already has animation data,
 -                       * check if the auto-record feature means that we should record 'samples'
 -                       * (i.e. uneditable animation values)
 -                       */
 -                      // TODO: autokeyframe calls need some setting to specify to add samples (FPoints) instead of keyframes?
 -                      if ((t->animtimer) && IS_AUTOKEY_ON(t->scene)) {
 -                              animrecord_check_state(t->scene, &ob->id, t->animtimer);
 -                              autokeyframe_ob_cb_func(t->context, t->scene, (View3D *)t->view, ob, t->mode);
 -                      }
 -                      
 -                      /* sets recalc flags fully, instead of flushing existing ones 
 -                       * otherwise proxies don't function correctly
 -                       */
 -                      DAG_id_tag_update(&ob->id, OB_RECALC_OB);
  
 -                      if (t->flag & T_TEXTURE)
 -                              DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
 +              FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 +                      TransData *td = tc->data;
 +
 +                      for (i = 0; i < tc->data_len; i++, td++) {
 +                              Object *ob = td->ob;
 +
 +                              if (td->flag & TD_NOACTION)
 +                                      break;
 +
 +                              if (td->flag & TD_SKIP)
 +                                      continue;
 +
 +                              /* if animtimer is running, and the object already has animation data,
 +                               * check if the auto-record feature means that we should record 'samples'
 +                               * (i.e. uneditable animation values)
 +                               */
 +                              // TODO: autokeyframe calls need some setting to specify to add samples (FPoints) instead of keyframes?
 +                              if ((t->animtimer) && IS_AUTOKEY_ON(t->scene)) {
 +                                      animrecord_check_state(t->scene, &ob->id, t->animtimer);
 +                                      autokeyframe_ob_cb_func(t->context, t->scene, t->view_layer, (View3D *)t->view, ob, t->mode);
 +                              }
 +
 +                              /* sets recalc flags fully, instead of flushing existing ones
 +                               * otherwise proxies don't function correctly
 +                               */
 +                              DEG_id_tag_update(&ob->id, OB_RECALC_OB);
 +
 +                              if (t->flag & T_TEXTURE)
 +                                      DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
 +                      }
                }
        }
  }
@@@ -990,9 -970,7 +1014,9 @@@ static void recalcData_sequencer(TransI
        int a;
        Sequence *seq_prev = NULL;
  
 -      for (a = 0, td = t->data; a < t->total; a++, td++) {
 +      TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
 +
 +      for (a = 0, td = tc->data; a < tc->data_len; a++, td++) {
                TransDataSeq *tdsq = (TransDataSeq *) td->extra;
                Sequence *seq = tdsq->seq;
  
  /* force recalculation of triangles during transformation */
  static void recalcData_gpencil_strokes(TransInfo *t)
  {
 -      TransData *td = t->data;
 -      for (int i = 0; i < t->total; i++, td++) {
 +      TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
 +
 +      TransData *td = tc->data;
 +      for (int i = 0; i < tc->data_len; i++, td++) {
                bGPDstroke *gps = td->extra;
                if (gps != NULL) {
                        gps->flag |= GP_STROKE_RECALC_CACHES;
@@@ -1079,10 -1055,11 +1103,10 @@@ void drawLine(TransInfo *t, const floa
        if (t->spacetype == SPACE_VIEW3D) {
                View3D *v3d = t->view;
                
 -              glPushMatrix();
 -              
 -              //if (t->obedit) glLoadMatrixf(t->obedit->obmat);       // sets opengl viewing
 -              
 -              
 +              gpuPushMatrix();
 +
 +              // if (t->obedit) gpuLoadMatrix(t->obedit->obmat); // sets opengl viewing
 +
                copy_v3_v3(v3, dir);
                mul_v3_fl(v3, v3d->far);
                
                        UI_GetThemeColor3ubv(TH_GRID, col);
                }
                UI_make_axis_color(col, col2, axis);
 -              glColor3ubv(col2);
 -              
 -              setlinestyle(0);
 -              glBegin(GL_LINES);
 -              glVertex3fv(v1);
 -              glVertex3fv(v2);
 -              glEnd();
 -              
 -              glPopMatrix();
 +
 +              unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
 +
 +              immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
 +              immUniformColor3ubv(col2);
 +
 +              immBegin(GWN_PRIM_LINES, 2);
 +              immVertex3fv(pos, v1);
 +              immVertex3fv(pos, v2);
 +              immEnd();
 +
 +              immUnbindProgram();
 +
 +              gpuPopMatrix();
        }
  }
  
   */
  void resetTransModal(TransInfo *t)
  {
 -      if (t->mode == TFM_EDGE_SLIDE) {
 -              freeEdgeSlideVerts(t, &t->custom.mode);
 -      }
 -      else if (t->mode == TFM_VERT_SLIDE) {
 -              freeVertSlideVerts(t, &t->custom.mode);
 +      FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 +              if (t->mode == TFM_EDGE_SLIDE) {
 +                      freeEdgeSlideVerts(t, tc, &tc->custom.mode);
 +              }
 +              else if (t->mode == TFM_VERT_SLIDE) {
 +                      freeVertSlideVerts(t, tc, &tc->custom.mode);
 +              }
 +              else {
 +                      /* no need to keep looping... */
 +                      break;
 +              }
        }
  }
  
@@@ -1151,40 -1117,6 +1175,40 @@@ static int initTransInfo_edit_pet_to_fl
        }
  }
  
 +void initTransDataContainers_FromObjectData(TransInfo *t)
 +{
 +      const eObjectMode object_mode = OBACT(t->view_layer) ? OBACT(t->view_layer)->mode : OB_MODE_OBJECT;
 +      const short object_type = OBACT(t->view_layer) ? OBACT(t->view_layer)->type : -1;
 +
 +      if ((object_mode & OB_MODE_EDIT) ||
 +          ((object_mode & OB_MODE_POSE) && (object_type == OB_ARMATURE)))
 +      {
 +              if (t->data_container) {
 +                      MEM_freeN(t->data_container);
 +              }
 +              uint objects_len;
 +              Object **objects = BKE_view_layer_array_from_objects_in_mode(
 +                      t->view_layer, &objects_len, {
 +                          .object_mode = object_mode,
 +                          .no_dup_data = true});
 +              t->data_container = MEM_callocN(sizeof(*t->data_container) * objects_len, __func__);
 +              t->data_container_len = objects_len;
 +
 +              for (int i = 0; i < objects_len; i++) {
 +                      TransDataContainer *tc = &t->data_container[i];
 +                      if (object_mode & OB_MODE_EDIT) {
 +                              tc->obedit = objects[i];
 +                              copy_m3_m4(tc->obedit_mat, tc->obedit->obmat);
 +                              normalize_m3(tc->obedit_mat);
 +                      }
 +                      else if (object_mode & OB_MODE_POSE) {
 +                              tc->poseobj = objects[i];
 +                      }
 +              }
 +              MEM_freeN(objects);
 +      }
 +}
 +
  /**
   * Setup internal data, mouse, vectors
   *
   */
  void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *event)
  {
 +      Depsgraph *depsgraph = CTX_data_depsgraph(C);
        Scene *sce = CTX_data_scene(C);
 +      ViewLayer *view_layer = CTX_data_view_layer(C);
 +      const eObjectMode object_mode = OBACT(view_layer) ? OBACT(view_layer)->mode : OB_MODE_OBJECT;
 +      const short object_type = OBACT(view_layer) ? OBACT(view_layer)->type : -1;
        ToolSettings *ts = CTX_data_tool_settings(C);
        ARegion *ar = CTX_wm_region(C);
        ScrArea *sa = CTX_wm_area(C);
 -      Object *obedit = CTX_data_edit_object(C);
 -      Object *ob = CTX_data_active_object(C);
 +
        bGPdata *gpd = CTX_data_gpencil_data(C);
        PropertyRNA *prop;
 -      
 +
 +      t->depsgraph = depsgraph;
        t->scene = sce;
 +      t->view_layer = view_layer;
        t->sa = sa;
        t->ar = ar;
 -      t->obedit = obedit;
        t->settings = ts;
        t->reports = op ? op->reports : NULL;
  
 -      if (obedit) {
 -              copy_m3_m4(t->obedit_mat, obedit->obmat);
 -              normalize_m3(t->obedit_mat);
 -      }
 -
 -      t->data = NULL;
 -      t->ext = NULL;
 -      
        t->helpline = HLP_NONE;
        
        t->flag = 0;
 -      
 +
 +      t->obedit_type = (object_mode == OB_MODE_EDIT) ? object_type : -1;
 +
 +      /* Many kinds of transform only use a single handle. */
 +      if (t->data_container == NULL) {
 +              t->data_container = MEM_callocN(sizeof(*t->data_container), __func__);
 +              t->data_container_len = 1;
 +      }
 +
        t->redraw = TREDRAW_HARD;  /* redraw first time */
        
        if (event) {
        t->transform        = NULL;
        t->handleEvent      = NULL;
        
 -      t->total            = 0;
 +      t->data_len_all = 0;
        
        t->val = 0.0f;
  
        zero_v3(t->vec);
 -      zero_v3(t->center);
        zero_v3(t->center_global);
  
        unit_m3(t->mat);
                t->animtimer = (animscreen) ? animscreen->animtimer : NULL;
                
                /* turn manipulator off during transform */
 -              // FIXME: but don't do this when USING the manipulator...
                if (t->flag & T_MODAL) {
 -                      t->twtype = v3d->twtype;
 -                      v3d->twtype = 0;
 +                      t->twflag = v3d->twflag;
 +                      v3d->twflag = 0;
                }
  
                if (v3d->flag & V3D_ALIGN) t->flag |= T_V3D_ALIGN;
                        t->around = V3D_AROUND_CURSOR;
                }
  
 -              t->current_orientation = v3d->twmode;
 +              t->current_orientation = t->scene->orientation_type;
 +              t->custom_orientation = BKE_scene_transform_orientation_find(
 +                      t->scene, t->scene->orientation_index_custom);
  
                /* exceptional case */
                if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
                        if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL)) {
                                const bool use_island = transdata_check_local_islands(t, t->around);
  
 -                              if (obedit && !use_island) {
 +                              if ((t->obedit_type != -1) && !use_island) {
                                        t->options |= CTX_NO_PET;
                                }
                        }
                }
  
 -              if (ob && ob->mode & OB_MODE_ALL_PAINT) {
 +              if (object_mode & OB_MODE_ALL_PAINT) {
                        Paint *p = BKE_paint_get_active_from_context(C);
                        if (p && p->brush && (p->brush->flag & BRUSH_CURVE)) {
                                t->options |= CTX_PAINT_CURVE;
                t->view = &ar->v2d;
                t->around = sima->around;
  
 -              if (ED_space_image_show_uvedit(sima, t->obedit)) {
 +              if (ED_space_image_show_uvedit(sima, OBACT(t->view_layer))) {
                        /* UV transform */
                }
                else if (sima->mode == SI_MODE_MASK) {
        if (op && ((prop = RNA_struct_find_property(op->ptr, "constraint_orientation")) &&
                   RNA_property_is_set(op->ptr, prop)))
        {
 -              t->current_orientation = RNA_property_enum_get(op->ptr, prop);
 +              short orientation = RNA_property_enum_get(op->ptr, prop);
 +              TransformOrientation *custom_orientation = NULL;
  
 -              if (t->current_orientation >= V3D_MANIP_CUSTOM + BIF_countTransformOrientation(C)) {
 -                      t->current_orientation = V3D_MANIP_GLOBAL;
 +              if (orientation >= V3D_MANIP_CUSTOM) {
 +                      if (orientation >= V3D_MANIP_CUSTOM + BIF_countTransformOrientation(C)) {
 +                              orientation = V3D_MANIP_GLOBAL;
 +                      }
 +                      else {
 +                              custom_orientation = BKE_scene_transform_orientation_find(
 +                                      t->scene, orientation - V3D_MANIP_CUSTOM);
 +                              orientation = V3D_MANIP_CUSTOM;
 +                      }
                }
 +
 +              t->current_orientation = orientation;
 +              t->custom_orientation = custom_orientation;
        }
  
        if (op && ((prop = RNA_struct_find_property(op->ptr, "release_confirm")) &&
        }
        // Need stuff to take it from edit mesh or whatnot here
        else if (t->spacetype == SPACE_VIEW3D) {
 -              if (t->obedit && t->obedit->type == OB_MESH && (((Mesh *)t->obedit->data)->editflag & ME_EDIT_MIRROR_X)) {
 +              /* TODO(campbell): xform, get mirror from each object. */
 +              if (t->obedit_type == OB_MESH && (((Mesh *)OBACT(t->view_layer)->data)->editflag & ME_EDIT_MIRROR_X)) {
                        t->flag |= T_MIRROR;
                        t->mirror = 1;
                }
                                        else if (t->spacetype == SPACE_ACTION) {
                                                t->flag |= initTransInfo_edit_pet_to_flag(ts->proportional_action);
                                        }
 -                                      else if (t->obedit) {
 +                                      else if (t->obedit_type != -1) {
                                                t->flag |= initTransInfo_edit_pet_to_flag(ts->proportional);
                                        }
                                        else if (t->options & CTX_GPENCIL_STROKES) {
                                                        }
                                                }
                                        }
 -                                      else if (t->obedit == NULL && ts->proportional_objects) {
 +                                      else if ((t->obedit_type == -1) && ts->proportional_objects) {
                                                t->flag |= T_PROP_EDIT;
                                        }
                                }
        setTransformViewAspect(t, t->aspect);
  
        if (op && (prop = RNA_struct_find_property(op->ptr, "center_override")) && RNA_property_is_set(op->ptr, prop)) {
 -              RNA_property_float_get_array(op->ptr, prop, t->center);
 -              mul_v3_v3(t->center, t->aspect);
 +              RNA_property_float_get_array(op->ptr, prop, t->center_global);
 +              mul_v3_v3(t->center_global, t->aspect);
                t->flag |= T_OVERRIDE_CENTER;
        }
  
        initNumInput(&t->num);
  }
  
 +static void freeTransCustomDataContainer(TransInfo *t, TransDataContainer *tc, TransCustomDataContainer *tcdc)
 +{
 +      TransCustomData *custom_data = &tcdc->first_elem;
 +      for (int i = 0; i < TRANS_CUSTOM_DATA_ELEM_MAX; i++, custom_data++) {
 +              if (custom_data->free_cb) {
 +                      /* Can take over freeing t->data and data_2d etc... */
 +                      custom_data->free_cb(t, tc, custom_data);
 +                      BLI_assert(custom_data->data == NULL);
 +              }
 +              else if ((custom_data->data != NULL) && custom_data->use_free) {
 +                      MEM_freeN(custom_data->data);
 +                      custom_data->data = NULL;
 +              }
 +      }
 +}
 +
  /* Here I would suggest only TransInfo related issues, like free data & reset vars. Not redraws */
  void postTrans(bContext *C, TransInfo *t)
  {
 -      TransData *td;
 -      
        if (t->draw_handle_view)
                ED_region_draw_cb_exit(t->ar->type, t->draw_handle_view);
        if (t->draw_handle_apply)
                WM_paint_cursor_end(CTX_wm_manager(C), t->draw_handle_cursor);
  
        /* Free all custom-data */
 -      {
 -              TransCustomData *custom_data = &t->custom.first_elem;
 -              for (int i = 0; i < TRANS_CUSTOM_DATA_ELEM_MAX; i++, custom_data++) {
 -                      if (custom_data->free_cb) {
 -                              /* Can take over freeing t->data and data2d etc... */
 -                              custom_data->free_cb(t, custom_data);
 -                              BLI_assert(custom_data->data == NULL);
 -                      }
 -                      else if ((custom_data->data != NULL) && custom_data->use_free) {
 -                              MEM_freeN(custom_data->data);
 -                              custom_data->data = NULL;
 -                      }
 -              }
 +      freeTransCustomDataContainer(t, NULL, &t->custom);
 +      FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 +              freeTransCustomDataContainer(t, tc, &tc->custom);
        }
  
        /* postTrans can be called when nothing is selected, so data is NULL already */
 -      if (t->data) {
 -              
 -              /* free data malloced per trans-data */
 -              if ((t->obedit && ELEM(t->obedit->type, OB_CURVE, OB_SURF)) ||
 -                  (t->spacetype == SPACE_IPO))
 -              {
 -                      int a;
 -                      for (a = 0, td = t->data; a < t->total; a++, td++) {
 -                              if (td->flag & TD_BEZTRIPLE) {
 -                                      MEM_freeN(td->hdata);
 +      if (t->data_len_all != 0) {
 +              FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 +                      /* free data malloced per trans-data */
 +                      if (ELEM(t->obedit_type, OB_CURVE, OB_SURF) ||
 +                          (t->spacetype == SPACE_IPO))
 +                      {
 +                              TransData *td = tc->data;
 +                              for (int a = 0; a < tc->data_len; a++, td++) {
 +                                      if (td->flag & TD_BEZTRIPLE) {
 +                                              MEM_freeN(td->hdata);
 +                                      }
                                }
                        }
 +                      MEM_freeN(tc->data);
 +      
 +                      MEM_SAFE_FREE(tc->data_ext);
 +                      MEM_SAFE_FREE(tc->data_2d);
                }
 -              MEM_freeN(t->data);
        }
        
 +      MEM_SAFE_FREE(t->data_container);
 +      t->data_container = NULL;
 +
        BLI_freelistN(&t->tsnap.points);
  
 -      if (t->ext) MEM_freeN(t->ext);
 -      if (t->data2d) {
 -              MEM_freeN(t->data2d);
 -              t->data2d = NULL;
 -      }
 -      
        if (t->spacetype == SPACE_IMAGE) {
                if (t->options & (CTX_MASK | CTX_PAINT_CURVE)) {
                        /* pass */
                View3D *v3d = t->sa->spacedata.first;
                /* restore manipulator */
                if (t->flag & T_MODAL) {
 -                      v3d->twtype = t->twtype;
 +                      v3d->twflag = t->twflag;
                }
        }
        
  
  void applyTransObjects(TransInfo *t)
  {
 +      TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
 +
        TransData *td;
 -      
 -      for (td = t->data; td < t->data + t->total; td++) {
 +
 +      for (td = tc->data; td < tc->data + tc->data_len; td++) {
                copy_v3_v3(td->iloc, td->loc);
                if (td->ext->rot) {
                        copy_v3_v3(td->ext->irot, td->ext->rot);
@@@ -1693,29 -1602,25 +1717,29 @@@ static void restoreElement(TransData *t
  
  void restoreTransObjects(TransInfo *t)
  {
 -      TransData *td;
 -      TransData2D *td2d;
 +      FOREACH_TRANS_DATA_CONTAINER (t, tc) {
  
 -      for (td = t->data; td < t->data + t->total; td++) {
 -              restoreElement(td);
 -      }
 -      
 -      for (td2d = t->data2d; t->data2d && td2d < t->data2d + t->total; td2d++) {
 -              if (td2d->h1) {
 -                      td2d->h1[0] = td2d->ih1[0];
 -                      td2d->h1[1] = td2d->ih1[1];
 +              TransData *td;
 +              TransData2D *td2d;
 +
 +              for (td = tc->data; td < tc->data + tc->data_len; td++) {
 +                      restoreElement(td);
                }
 -              if (td2d->h2) {
 -                      td2d->h2[0] = td2d->ih2[0];
 -                      td2d->h2[1] = td2d->ih2[1];
 +
 +              for (td2d = tc->data_2d; tc->data_2d && td2d < tc->data_2d + tc->data_len; td2d++) {
 +                      if (td2d->h1) {
 +                              td2d->h1[0] = td2d->ih1[0];
 +                              td2d->h1[1] = td2d->ih1[1];
 +                      }
 +                      if (td2d->h2) {
 +                              td2d->h2[0] = td2d->ih2[0];
 +                              td2d->h2[1] = td2d->ih2[1];
 +                      }
                }
 -      }
  
 -      unit_m3(t->mat);
 +              unit_m3(t->mat);
 +
 +      }
        
        recalcData(t);
  }
  void calculateCenter2D(TransInfo *t)
  {
        BLI_assert(!is_zero_v3(t->aspect));
 -
 -      if (t->flag & (T_EDIT | T_POSE)) {
 -              Object *ob = t->obedit ? t->obedit : t->poseobj;
 -              float vec[3];
 -              
 -              copy_v3_v3(vec, t->center);
 -              mul_m4_v3(ob->obmat, vec);
 -              projectFloatView(t, vec, t->center2d);
 -      }
 -      else {
 -              projectFloatView(t, t->center, t->center2d);
 -      }
 +      projectFloatView(t, t->center_global, t->center2d);
  }
  
 -void calculateCenterGlobal(
 -        TransInfo *t, const float center_local[3],
 -        float r_center_global[3])
 +void calculateCenterLocal(
 +        TransInfo *t, const float center_global[3])
  {
        /* setting constraint center */
        /* note, init functions may over-ride t->center */
        if (t->flag & (T_EDIT | T_POSE)) {
 -              Object *ob = t->obedit ? t->obedit : t->poseobj;
 -              mul_v3_m4v3(r_center_global, ob->obmat, center_local);
 +              FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 +                      float obinv[4][4];
 +                      Object *ob = tc->obedit ? tc->obedit : tc->poseobj;
 +                      invert_m4_m4(obinv, ob->obmat);
 +                      mul_v3_m4v3(tc->center_local, obinv, center_global);
 +              }
        }
        else {
 -              copy_v3_v3(r_center_global, center_local);
 +              FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 +                      copy_v3_v3(tc->center_local, center_global);
 +              }
        }
  }
  
@@@ -1754,7 -1665,16 +1778,7 @@@ void calculateCenterCursor(TransInfo *t
        copy_v3_v3(r_center, cursor);
        
        /* If edit or pose mode, move cursor in local space */
 -      if (t->flag & (T_EDIT | T_POSE)) {
 -              Object *ob = t->obedit ? t->obedit : t->poseobj;
 -              float mat[3][3], imat[3][3];
 -              
 -              sub_v3_v3v3(r_center, r_center, ob->obmat[3]);
 -              copy_m3_m4(mat, ob->obmat);
 -              invert_m3_m3(imat, mat);
 -              mul_m3_v3(imat, r_center);
 -      }
 -      else if (t->options & CTX_PAINT_CURVE) {
 +      if (t->options & CTX_PAINT_CURVE) {
                if (ED_view3d_project_float_global(t->ar, cursor, r_center, V3D_PROJ_TEST_NOP) != V3D_PROJ_RET_OK) {
                        r_center[0] = t->ar->winx / 2.0f;
                        r_center[1] = t->ar->winy / 2.0f;
@@@ -1828,23 -1748,13 +1852,23 @@@ void calculateCenterMedian(TransInfo *t
  {
        float partial[3] = {0.0f, 0.0f, 0.0f};
        int total = 0;
 -      int i;
 -      
 -      for (i = 0; i < t->total; i++) {
 -              if (t->data[i].flag & TD_SELECTED) {
 -                      if (!(t->data[i].flag & TD_NOCENTER)) {
 -                              add_v3_v3(partial, t->data[i].center);
 -                              total++;
 +
 +      FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 +              Object *ob_xform = tc->obedit ? tc->obedit : tc->poseobj;
 +              int i;
 +              for (i = 0; i < tc->data_len; i++) {
 +                      if (tc->data[i].flag & TD_SELECTED) {
 +                              if (!(tc->data[i].flag & TD_NOCENTER)) {
 +                                      if (ob_xform) {
 +                                              float v[3];
 +                                              mul_v3_m4v3(v, ob_xform->obmat, tc->data[i].center);
 +                                              add_v3_v3(partial, v);
 +                                      }
 +                                      else {
 +                                              add_v3_v3(partial, tc->data[i].center);
 +                                      }
 +                                      total++;
 +                              }
                        }
                }
        }
@@@ -1859,29 -1769,16 +1883,29 @@@ void calculateCenterBound(TransInfo *t
        float max[3];
        float min[3];
        int i;
 -      for (i = 0; i < t->total; i++) {
 -              if (i) {
 -                      if (t->data[i].flag & TD_SELECTED) {
 -                              if (!(t->data[i].flag & TD_NOCENTER))
 -                                      minmax_v3v3_v3(min, max, t->data[i].center);
 +      bool is_first = true;
 +      FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 +              Object *ob_xform = tc->obedit ? tc->obedit : tc->poseobj;
 +              for (i = 0; i < tc->data_len; i++) {
 +                      if (is_first == false) {
 +                              if (tc->data[i].flag & TD_SELECTED) {
 +                                      if (!(tc->data[i].flag & TD_NOCENTER)) {
 +                                              if (ob_xform) {
 +                                                      float v[3];
 +                                                      mul_v3_m4v3(v, ob_xform->obmat, tc->data[i].center);
 +                                                      minmax_v3v3_v3(min, max, v);
 +                                              }
 +                                              else {
 +                                                      minmax_v3v3_v3(min, max, tc->data[i].center);
 +                                              }
 +                                      }
 +                              }
 +                              is_first = false;
 +                      }
 +                      else {
 +                              copy_v3_v3(max, tc->data[i].center);
 +                              copy_v3_v3(min, tc->data[i].center);
                        }
 -              }
 -              else {
 -                      copy_v3_v3(max, t->data[i].center);
 -                      copy_v3_v3(min, t->data[i].center);
                }
        }
        mid_v3_v3v3(r_center, min, max);
   */
  bool calculateCenterActive(TransInfo *t, bool select_only, float r_center[3])
  {
 +      TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_OK(t);
 +
        bool ok = false;
  
 -      if (t->obedit) {
 -              if (ED_object_editmode_calc_active_center(t->obedit, select_only, r_center)) {
 +      if (tc->obedit) {
 +              if (ED_object_editmode_calc_active_center(tc->obedit, select_only, r_center)) {
 +                      mul_m4_v3(tc->obedit->obmat, r_center);
                        ok = true;
                }
        }
        else if (t->flag & T_POSE) {
 -              Scene *scene = t->scene;
 -              Object *ob = OBACT;
 +              ViewLayer *view_layer = t->view_layer;
 +              Object *ob = OBACT(view_layer);
                if (ob) {
                        bPoseChannel *pchan = BKE_pose_channel_active(ob);
                        if (pchan && (!select_only || (pchan->bone->flag & BONE_SELECTED))) {
                                copy_v3_v3(r_center, pchan->pose_head);
 +                              mul_m4_v3(tc->obedit->obmat, r_center);
                                ok = true;
                        }
                }
        }
        else if (t->options & CTX_PAINT_CURVE) {
 -              Paint *p = BKE_paint_get_active(t->scene);
 +              Paint *p = BKE_paint_get_active(t->scene, t->view_layer);
                Brush *br = p->brush;
                PaintCurve *pc = br->paint_curve;
                copy_v3_v3(r_center, pc->points[pc->add_index - 1].bez.vec[1]);
        }
        else {
                /* object mode */
 -              Scene *scene = t->scene;
 -              Object *ob = OBACT;
 -              if (ob && (!select_only || (ob->flag & SELECT))) {
 +              ViewLayer *view_layer = t->view_layer;
 +              Object *ob = OBACT(view_layer);
 +              Base *base = BASACT(view_layer);
 +              if (ob && ((!select_only) || ((base->flag & BASE_SELECTED) != 0))) {
                        copy_v3_v3(r_center, ob->obmat[3]);
                        ok = true;
                }
@@@ -1974,13 -1866,14 +1998,13 @@@ static void calculateCenter_FromAround(
  void calculateCenter(TransInfo *t)
  {
        if ((t->flag & T_OVERRIDE_CENTER) == 0) {
 -              calculateCenter_FromAround(t, t->around, t->center);
 +              calculateCenter_FromAround(t, t->around, t->center_global);
        }
 -      calculateCenterGlobal(t, t->center, t->center_global);
 +      calculateCenterLocal(t, t->center_global);
  
        /* avoid calculating again */
        {
                TransCenterData *cd = &t->center_cache[t->around];
 -              copy_v3_v3(cd->local, t->center);
                copy_v3_v3(cd->global, t->center_global);
                cd->is_set = true;
        }
                                normalize_v3(axis);
                                
                                /* 6.0 = 6 grid units */
 -                              axis[0] = t->center[0] - 6.0f * axis[0];
 -                              axis[1] = t->center[1] - 6.0f * axis[1];
 -                              axis[2] = t->center[2] - 6.0f * axis[2];
 +                              axis[0] = t->center_global[0] - 6.0f * axis[0];
 +                              axis[1] = t->center_global[1] - 6.0f * axis[1];
 +                              axis[2] = t->center_global[2] - 6.0f * axis[2];
                                
                                projectFloatView(t, axis, t->center2d);
                                
                                /* rotate only needs correct 2d center, grab needs ED_view3d_calc_zfac() value */
                                if (t->mode == TFM_TRANSLATION) {
 -                                      copy_v3_v3(t->center, axis);
 -                                      copy_v3_v3(t->center_global, t->center);
 +                                      copy_v3_v3(t->center_global, axis);
                                }
                        }
                }
@@@ -2040,7 -1934,8 +2064,7 @@@ const TransCenterData *transformCenter_
        BLI_assert(around <= V3D_AROUND_ACTIVE);
        TransCenterData *cd = &t->center_cache[around];
        if (cd->is_set == false) {
 -              calculateCenter_FromAround(t, around, cd->local);
 -              calculateCenterGlobal(t, cd->local, cd->global);
 +              calculateCenter_FromAround(t, around, cd->global);
                cd->is_set = true;
        }
        return cd;
  
  void calculatePropRatio(TransInfo *t)
  {
 -      TransData *td = t->data;
        int i;
        float dist;
        const bool connected = (t->flag & T_PROP_CONNECTED) != 0;
  
        if (t->flag & T_PROP_EDIT) {
                const char *pet_id = NULL;
 -              for (i = 0; i < t->total; i++, td++) {
 -                      if (td->flag & TD_SELECTED) {
 -                              td->factor = 1.0f;
 -                      }
 -                      else if (t->flag & T_MIRROR && td->loc[0] * t->mirror < -0.00001f) {
 -                              td->flag |= TD_SKIP;
 -                              td->factor = 0.0f;
 -                              restoreElement(td);
 -                      }
 -                      else if ((connected && (td->flag & TD_NOTCONNECTED || td->dist > t->prop_size)) ||
 -                               (connected == 0 && td->rdist > t->prop_size))
 -                      {
 -                              /*
 -                               * The elements are sorted according to their dist member in the array,
 -                               * that means we can stop when it finds one element outside of the propsize.
 -                               * do not set 'td->flag |= TD_NOACTION', the prop circle is being changed.
 -                               */
 -                              
 -                              td->factor = 0.0f;
 -                              restoreElement(td);
 -                      }
 -                      else {
 -                              /* Use rdist for falloff calculations, it is the real distance */
 -                              td->flag &= ~TD_NOACTION;
 -                              
 -                              if (connected)
 -                                      dist = (t->prop_size - td->dist) / t->prop_size;
 -                              else
 -                                      dist = (t->prop_size - td->rdist) / t->prop_size;
 -
 -                              /*
 -                               * Clamp to positive numbers.
 -                               * Certain corner cases with connectivity and individual centers
 -                               * can give values of rdist larger than propsize.
 -                               */
 -                              if (dist < 0.0f)
 -                                      dist = 0.0f;
 -                              
 -                              switch (t->prop_mode) {
 -                                      case PROP_SHARP:
 -                                              td->factor = dist * dist;
 -                                              break;
 -                                      case PROP_SMOOTH:
 -                                              td->factor = 3.0f * dist * dist - 2.0f * dist * dist * dist;
 -                                              break;
 -                                      case PROP_ROOT:
 -                                              td->factor = sqrtf(dist);
 -                                              break;
 -                                      case PROP_LIN:
 -                                              td->factor = dist;
 -                                              break;
 -                                      case PROP_CONST:
 -                                              td->factor = 1.0f;
 -                                              break;
 -                                      case PROP_SPHERE:
 -                                              td->factor = sqrtf(2 * dist - dist * dist);
 -                                              break;
 -                                      case PROP_RANDOM:
 -                                              td->factor = BLI_frand() * dist;
 -                                              break;
 -                                      case PROP_INVSQUARE:
 -                                              td->factor = dist * (2.0f - dist);
 -                                              break;
 -                                      default:
 -                                              td->factor = 1;
 -                                              break;
 +              FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 +                      TransData *td = tc->data;
 +                      for (i = 0; i < tc->data_len; i++, td++) {
 +                              if (td->flag & TD_SELECTED) {
 +                                      td->factor = 1.0f;
 +                              }
 +                              else if (t->flag & T_MIRROR && td->loc[0] * t->mirror < -0.00001f) {
 +                                      td->flag |= TD_SKIP;
 +                                      td->factor = 0.0f;
 +                                      restoreElement(td);
 +                              }
 +                              else if ((connected && (td->flag & TD_NOTCONNECTED || td->dist > t->prop_size)) ||
 +                                       (connected == 0 && td->rdist > t->prop_size))
 +                              {
 +                                      /*
 +                                       * The elements are sorted according to their dist member in the array,
 +                                       * that means we can stop when it finds one element outside of the propsize.
 +                                       * do not set 'td->flag |= TD_NOACTION', the prop circle is being changed.
 +                                       */
 +
 +                                      td->factor = 0.0f;
 +                                      restoreElement(td);
 +                              }
 +                              else {
 +                                      /* Use rdist for falloff calculations, it is the real distance */
 +                                      td->flag &= ~TD_NOACTION;
 +
 +                                      if (connected)
 +                                              dist = (t->prop_size - td->dist) / t->prop_size;
 +                                      else
 +                                              dist = (t->prop_size - td->rdist) / t->prop_size;
 +
 +                                      /*
 +                                       * Clamp to positive numbers.
 +                                       * Certain corner cases with connectivity and individual centers
 +                                       * can give values of rdist larger than propsize.
 +                                       */
 +                                      if (dist < 0.0f)
 +                                              dist = 0.0f;
 +
 +                                      switch (t->prop_mode) {
 +                                              case PROP_SHARP:
 +                                                      td->factor = dist * dist;
 +                                                      break;
 +                                              case PROP_SMOOTH:
 +                                                      td->factor = 3.0f * dist * dist - 2.0f * dist * dist * dist;
 +                                                      break;
 +                                              case PROP_ROOT:
 +                                                      td->factor = sqrtf(dist);
 +                                                      break;
 +                                              case PROP_LIN:
 +                                                      td->factor = dist;
 +                                                      break;
 +                                              case PROP_CONST:
 +                                                      td->factor = 1.0f;
 +                                                      break;
 +                                              case PROP_SPHERE:
 +                                                      td->factor = sqrtf(2 * dist - dist * dist);
 +                                                      break;
 +                                              case PROP_RANDOM:
 +                                                      td->factor = BLI_frand() * dist;
 +                                                      break;
 +                                              case PROP_INVSQUARE:
 +                                                      td->factor = dist * (2.0f - dist);
 +                                                      break;
 +                                              default:
 +                                                      td->factor = 1;
 +                                                      break;
 +                                      }
                                }
                        }
                }
 +
                switch (t->prop_mode) {
                        case PROP_SHARP:
                                pet_id = N_("(Sharp)");
                }
        }
        else {
 -              for (i = 0; i < t->total; i++, td++) {
 -                      td->factor = 1.0;
 +              FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 +                      TransData *td = tc->data;
 +                      for (i = 0; i < tc->data_len; i++, td++) {
 +                              td->factor = 1.0;
 +                      }
                }
        }
  }