Merge branch 'master' into blender2.8
authorCampbell Barton <ideasman42@gmail.com>
Mon, 9 Jul 2018 15:57:37 +0000 (17:57 +0200)
committerCampbell Barton <ideasman42@gmail.com>
Mon, 9 Jul 2018 15:57:37 +0000 (17:57 +0200)
1  2 
source/blender/editors/transform/transform.h
source/blender/editors/transform/transform_snap.c

index 744170d9aba8a4972cd935cf9ea1a5970ef8fac4,5f7388045791e537ecb17b667b0d6390cc3d24df..6685f78524789a85c9ed4eabdc68d8adb2b5a7f5
  #include "ED_numinput.h"
  #include "ED_view3d.h"
  
 +#include "RE_engine.h"
 +
  #include "DNA_listBase.h"
  
 +#include "DEG_depsgraph.h"
 +
  /* ************************** Types ***************************** */
  
 +struct Depsgraph;
  struct TransInfo;
 +struct TransDataContainer;
  struct TransData;
  struct TransformOrientation;
  struct TransSnap;
@@@ -56,7 -50,6 +56,7 @@@ struct Object
  struct View3D;
  struct ScrArea;
  struct Scene;
 +struct ViewLayer;
  struct bConstraint;
  struct wmKeyMap;
  struct wmKeyConfig;
@@@ -65,13 -58,9 +65,13 @@@ struct wmEvent
  struct wmTimer;
  struct ARegion;
  struct ReportList;
 +struct RNG;
  struct EditBone;
 +struct RenderEngineType;
  struct SnapObjectContext;
  
 +#include "DNA_object_enums.h"
 +
  /* transinfo->redraw */
  typedef enum {
        TREDRAW_NOTHING   = 0,
@@@ -111,7 -100,7 +111,7 @@@ typedef struct TransSnap 
         * \note Return value can be anything,
         * where the smallest absolute value defines whats closest.
         */
 -      float  (*distance)(struct TransInfo *, const float p1[3], const float p2[3]);
 +      float  (*distance)(struct TransInfo *t, const float p1[3], const float p2[3]);
  
        /**
         * Re-usable snap context data.
@@@ -129,16 -118,14 +129,16 @@@ typedef struct TransCon 
                             /* the one in TransInfo is not garanty to stay the same (Rotates change it)  */
        int   mode;          /* Mode flags of the Constraint                                              */
        void  (*drawExtra)(struct TransInfo *t);
 +
 +                           /* Note: if 'tc' is NULL, 'td' must also be NULL. */
                             /* For constraints that needs to draw differently from the other
                              * uses this instead of the generic draw function                            */
 -      void  (*applyVec)(struct TransInfo *t, struct TransData *td, const float in[3], float out[3], float pvec[3]);
 +      void  (*applyVec)(struct TransInfo *t, struct TransDataContainer *tc, struct TransData *td, const float in[3], float out[3], float pvec[3]);
                             /* Apply function pointer for linear vectorial transformation                */
                             /* The last three parameters are pointers to the in/out/printable vectors    */
 -      void  (*applySize)(struct TransInfo *t, struct TransData *td, float smat[3][3]);
 +      void  (*applySize)(struct TransInfo *t, struct TransDataContainer *tc, struct TransData *td, float smat[3][3]);
                             /* Apply function pointer for size transformation */
 -      void  (*applyRot)(struct TransInfo *t, struct TransData *td, float vec[3], float *angle);
 +      void  (*applyRot)(struct TransInfo *t, struct TransDataContainer *tc, struct TransData *td, float vec[3], float *angle);
                             /* Apply function pointer for rotation transformation */
  } TransCon;
  
@@@ -274,6 -261,10 +274,6 @@@ typedef struct EdgeSlideData 
  
        SlideOrigData orig_data;
  
 -      float perc;
 -
 -      bool use_even;
 -      bool flipped;
  
        int curr_sv_index;
  
        int curr_side_unclamp;
  } EdgeSlideData;
  
 +typedef struct EdgeSlideParams {
 +      float perc;
 +
 +      bool use_even;
 +      bool flipped;
 +} EdgeSlideParams;
  
  typedef struct TransDataVertSlideVert {
        /* TransDataGenericSlideVert */
@@@ -308,19 -293,17 +308,19 @@@ typedef struct VertSlideData 
  
        SlideOrigData orig_data;
  
 -      float perc;
 -
 -      bool use_even;
 -      bool flipped;
 -
        int curr_sv_index;
  
        /* result of ED_view3d_ob_project_mat_get */
        float proj_mat[4][4];
  } VertSlideData;
  
 +typedef struct VertSlideParams {
 +      float perc;
 +
 +      bool use_even;
 +      bool flipped;
 +} VertSlideParams;
 +
  typedef struct BoneInitData {
        struct EditBone *bone;
        float tail[3];
@@@ -381,80 -364,16 +381,80 @@@ typedef struct MouseInput 
  
  typedef struct TransCustomData {
        void       *data;
 -      void      (*free_cb)(struct TransInfo *, struct TransCustomData *);
 +      void      (*free_cb)(struct TransInfo *, struct TransDataContainer *tc, struct TransCustomData *custom_data);
        unsigned int use_free : 1;
  } TransCustomData;
  
  typedef struct TransCenterData {
 -      float local[3], global[3];
 +      float global[3];
        unsigned int is_set : 1;
  } TransCenterData;
  
 +/**
 + * Rule of thumb for choosing between mode/type:
 + * - If transform mode uses the data, assign to `mode`
 + *   (typically in transform.c).
 + * - If conversion uses the data as an extension to the #TransData, assign to `type`
 + *   (typically in transform_conversion.c).
 + */
 +typedef struct TransCustomDataContainer {
 +      /** Owned by the mode (grab, scale, bend... ).*/
 +      union {
 +              TransCustomData mode, first_elem;
 +      };
 +      TransCustomData type;
 +} TransCustomDataContainer;
 +#define TRANS_CUSTOM_DATA_ELEM_MAX (sizeof(TransCustomDataContainer) / sizeof(TransCustomData))
 +
 +typedef struct TransDataContainer {
 +      /**
 +       * Use for cases we care about the active, eg: active vert of active mesh.
 +       * if set this will _always_ be the first item in the array.
 +       */
 +      bool is_active;
 +
 +      /** Transformed data (array). */
 +      TransData *data;
 +      /** Total number of transformed data. */
 +      int data_len;
 +
 +      /** Transformed data extension (array). */
 +      TransDataExtension *data_ext;
 +      /** Transformed data for 2d (array). */
 +      TransData2D *data_2d;
 +
 +      struct Object *obedit;
 +
 +      /**
 +       * Use when #T_LOCAL_MATRIX is set.
 +       * Typically: 'obedit->obmat' or 'poseobj->obmat', but may be used elsewhere too.
 +       */
 +      bool use_local_mat;
 +      float  mat[4][4];
 +      float imat[4][4];
 +      /** 3x3 copies of matrices above. */
 +      float  mat3[3][3];
 +      float imat3[3][3];
 +
 +      /** Normalized 'mat3' */
 +      float  mat3_unit[3][3];
 +
 +      /** if 't->flag & T_POSE', this denotes pose object */
 +      struct Object *poseobj;
 +
 +      /** Center of transformation (in local-space), Calculated from #TransInfo.center_global. */
 +      float center_local[3];
 +
 +      TransCustomDataContainer custom;
 +} TransDataContainer;
 +
  typedef struct TransInfo {
 +      TransDataContainer *data_container;
 +      int                 data_container_len;
 +      /** Combine length of all #TransDataContainer.data_len
 +       * Use to check if nothing is selected or if we have a single selection. */
 +      int data_len_all;
 +
        int         mode;           /* current mode                         */
        int             flag;           /* generic flags for special behaviors  */
        int                     modifiers;              /* special modifiers, by function, not key */
                                                                /* transform function pointer           */
        eRedrawFlag (*handleEvent)(struct TransInfo *, const struct wmEvent *);
                                                                /* event handler function pointer  RETURN 1 if redraw is needed */
 -      int         total;          /* total number of transformed data     */
 -      TransData  *data;           /* transformed data (array)             */
 -      TransDataExtension *ext;        /* transformed data extension (array)   */
 -      TransData2D *data2d;            /* transformed data for 2d (array)      */
        TransCon    con;            /* transformed constraint               */
        TransSnap       tsnap;
        NumInput    num;            /* numerical input                      */
        char            proptext[20];   /* proportional falloff text                    */
        float       aspect[3];      /* spaces using non 1:1 aspect, (uv's, f-curve, movie-clip... etc)
                                     * use for conversion and snapping. */
 -      float       center[3];      /* center of transformation (in local-space) */
        float       center_global[3];  /* center of transformation (in global-space) */
        float       center2d[2];    /* center in screen coordinates         */
        /* Lazy initialize center data for when we need other center values.
        short           persp;
        short           around;
        char            spacetype;              /* spacetype where transforming is      */
 -      char            helpline;               /* helpline modes (not to be confused with hotline) */
 +      char            helpline;               /* Choice of custom cursor with or without a help line from the manipulator to the mouse position. */
 +      short           obedit_type;    /* Avoid looking inside TransDataContainer obedit. */
  
        float           vec[3];                 /* translation, to show for widget      */
        float           mat[3][3];              /* rot/rescale, to show for widget      */
        float           spacemtx[3][3]; /* orientation matrix of the current space      */
        char            spacename[64];  /* name of the current space, MAX_NAME          */
  
 -      struct Object *poseobj;         /* if t->flag & T_POSE, this denotes pose object */
 -
 -      /**
 -       * Rule of thumb for choosing between mode/type:
 -       * - If transform mode uses the data, assign to `mode`
 -       *   (typically in transform.c).
 -       * - If conversion uses the data as an extension to the #TransData, assign to `type`
 -       *   (typically in transform_conversion.c).
 -       */
 -      struct {
 -              /* owned by the mode (grab, scale, bend... )*/
 -              union {
 -                      TransCustomData mode, first_elem;
 -              };
 -              /* owned by the type (mesh, armature, nla...) */
 -              TransCustomData type;
 -      } custom;
 -#define TRANS_CUSTOM_DATA_ELEM_MAX (sizeof(((TransInfo *)NULL)->custom) / sizeof(TransCustomData))
 -
        /*************** NEW STUFF *********************/
        short           launch_event;   /* event type used to launch transform */
  
        short           current_orientation;
 -      short           twtype;                 /* backup from view3d, to restore on end */
 +      TransformOrientation *custom_orientation; /* this gets used when current_orientation is V3D_MANIP_CUSTOM */
 +      short           twflag;                 /* backup from view3d, to restore on end */
  
        short           prop_mode;
  
        short           mirror;
  
        float           values[4];
 +      float           values_modal_offset[4];  /* Offset applied ontop of modal input. */
        float           auto_values[4];
        float           axis[3];
        float           axis_orig[3];   /* TransCon can change 'axis', store the original value here */
        struct bContext *context; /* Only valid (non null) during an operator called function. */
        struct ScrArea  *sa;
        struct ARegion  *ar;
 +      struct Depsgraph *depsgraph;
        struct Scene    *scene;
 +      struct ViewLayer *view_layer;
        struct ToolSettings *settings;
        struct wmTimer *animtimer;
        struct wmKeyMap *keymap;  /* so we can do lookups for header text */
        struct ReportList *reports;  /* assign from the operator, or can be NULL */
        int         mval[2];        /* current mouse position               */
        float       zfac;           /* use for 3d view */
 -      struct Object *obedit;
 -      float          obedit_mat[3][3]; /* normalized editmode matrix (T_EDIT only) */
        void            *draw_handle_apply;
        void            *draw_handle_view;
        void            *draw_handle_pixel;
        void            *draw_handle_cursor;
 +
 +      /** Currently only used for random curve of proportional editing. */
 +      struct RNG *rng;
 +
 +      /** Typically for mode settings. */
 +      TransCustomDataContainer custom;
  } TransInfo;
  
  
  
  /* transinfo->flag */
  #define T_OBJECT              (1 << 0)
 +/** \note We could remove 'T_EDIT' and use 'obedit_type', for now ensure they're in sync. */
  #define T_EDIT                        (1 << 1)
  #define T_POSE                        (1 << 2)
  #define T_TEXTURE             (1 << 3)
        /* transforming the camera while in camera view */
  #define T_CAMERA              (1 << 4)
 +      /* transforming the 3D cursor. */
 +#define T_CURSOR              (1 << 5)
                 // trans on points, having no rotation/scale
  #define T_POINTS              (1 << 6)
 -              // for manipulator exceptions, like scaling using center point, drawing help lines
 -#define T_USES_MANIPULATOR    (1 << 7)
 +/**
 + * Apply matrix #TransDataContainer.matrix, this avoids having to have duplicate check all over
 + * that happen to apply to spesiifc modes (edit & pose for eg). */
 +#define T_LOCAL_MATRIX (1 << 7)
  
        /* restrictions flags */
  #define T_ALL_RESTRICTIONS    ((1 << 8)|(1 << 9)|(1 << 10))
        /** #TransInfo.center has been set, don't change it. */
  #define T_OVERRIDE_CENTER     (1 << 25)
  
 +#define T_MODAL_CURSOR_SET    (1 << 26)
 +
  /* TransInfo->modifiers */
  #define       MOD_CONSTRAINT_SELECT   0x01
  #define       MOD_PRECISION                   0x02
@@@ -708,14 -635,10 +708,14 @@@ void flushTransSeq(TransInfo *t)
  void flushTransTracking(TransInfo *t);
  void flushTransMasking(TransInfo *t);
  void flushTransPaintCurve(TransInfo *t);
 -void restoreBones(TransInfo *t);
 +void restoreBones(TransDataContainer *tc);
  
 -/*********************** exported from transform_manipulator.c ********** */
 -bool gimbal_axis(struct Object *ob, float gmat[3][3]); /* return 0 when no gimbal for selection */
 +/*********************** transform_manipulator.c ********** */
 +
 +#define MANIPULATOR_AXIS_LINE_WIDTH 2.0f
 +
 +/* return 0 when no gimbal for selection */
 +bool gimbal_axis(struct Object *ob, float gmat[3][3]);
  
  /*********************** TransData Creation and General Handling *********** */
  void createTransData(struct bContext *C, TransInfo *t);
@@@ -726,13 -649,13 +726,13 @@@ int  special_transform_moving(TransInf
  void transform_autoik_update(TransInfo *t, short mode);
  bool transdata_check_local_islands(TransInfo *t, short around);
  
 -int count_set_pose_transflags(int *out_mode, short around, struct Object *ob);
 +int count_set_pose_transflags(struct Object *ob, const int mode, const short around, bool has_translate_rotate[2]);
  
  /* auto-keying stuff used by special_aftertrans_update */
  void autokeyframe_ob_cb_func(
 -        struct bContext *C, struct Scene *scene, struct View3D *v3d, struct Object *ob, int tmode);
 +        struct bContext *C, struct Scene *scene, struct ViewLayer *view_layer, struct Object *ob, int tmode);
  void autokeyframe_pose_cb_func(
 -        struct bContext *C, struct Scene *scene, struct View3D *v3d, struct Object *ob, int tmode, short targetless_ik);
 +        struct bContext *C, struct Scene *scene, struct Object *ob, int tmode, short targetless_ik);
  
  /*********************** Constraints *****************************/
  
@@@ -772,8 -695,8 +772,8 @@@ void snapGridIncrementAction(TransInfo 
  
  void snapSequenceBounds(TransInfo *t, const int mval[2]);
  
- bool activeSnap(TransInfo *t);
- bool validSnap(TransInfo *t);
+ bool activeSnap(const TransInfo *t);
+ bool validSnap(const TransInfo *t);
  
  void initSnapping(struct TransInfo *t, struct wmOperator *op);
  void freeSnapping(struct TransInfo *t);
@@@ -783,10 -706,10 +783,10 @@@ void applySnapping(TransInfo *t, float 
  void resetSnapping(TransInfo *t);
  eRedrawFlag handleSnapping(TransInfo *t, const struct wmEvent *event);
  void drawSnapping(const struct bContext *C, TransInfo *t);
- bool usingSnappingNormal(TransInfo *t);
- bool validSnappingNormal(TransInfo *t);
+ bool usingSnappingNormal(const TransInfo *t);
+ bool validSnappingNormal(const TransInfo *t);
  
- void getSnapPoint(TransInfo *t, float vec[3]);
+ void getSnapPoint(const TransInfo *t, float vec[3]);
  void addSnapPoint(TransInfo *t);
  eRedrawFlag updateSelectedSnapPoint(TransInfo *t);
  void removeSnapPoint(TransInfo *t);
@@@ -820,9 -743,7 +820,9 @@@ void setInputPostFct(MouseInput *mi, vo
  
  /*********************** Generics ********************************/
  
 +void initTransDataContainers_FromObjectData(TransInfo *t);
  void initTransInfo(struct bContext *C, TransInfo *t, struct wmOperator *op, const struct wmEvent *event);
 +void freeTransCustomDataForMode(TransInfo *t);
  void postTrans(struct bContext *C, TransInfo *t);
  void resetTransModal(TransInfo *t);
  void resetTransRestrictions(TransInfo *t);
@@@ -837,7 -758,9 +837,7 @@@ void restoreTransObjects(TransInfo *t)
  void recalcData(TransInfo *t);
  
  void calculateCenter2D(TransInfo *t);
 -void calculateCenterGlobal(
 -        TransInfo *t, const float center_local[3],
 -        float r_center_global[3]);
 +void calculateCenterLocal(TransInfo *t, const float center_global[3]);
  
  const TransCenterData *transformCenter_from_type(TransInfo *t, int around);
  void calculateCenter(TransInfo *t);
@@@ -866,7 -789,7 +866,7 @@@ bool createSpaceNormalTangent(float mat
  
  struct TransformOrientation *addMatrixSpace(struct bContext *C, float mat[3][3],
                                              const char *name, const bool overwrite);
 -bool applyTransformOrientation(const struct bContext *C, float mat[3][3], char r_name[64], int index);
 +bool applyTransformOrientation(const struct TransformOrientation *ts, float r_mat[3][3], char r_name[64]);
  
  #define ORIENTATION_NONE      0
  #define ORIENTATION_NORMAL    1
@@@ -878,36 -801,19 +878,36 @@@ int getTransformOrientation_ex(const st
  int getTransformOrientation(const struct bContext *C, float normal[3], float plane[3]);
  
  void freeEdgeSlideTempFaces(EdgeSlideData *sld);
 -void freeEdgeSlideVerts(TransInfo *t, TransCustomData *custom_data);
 +void freeEdgeSlideVerts(TransInfo *t, TransDataContainer *tc, TransCustomData *custom_data);
  void projectEdgeSlideData(TransInfo *t, bool is_final);
  
  void freeVertSlideTempFaces(VertSlideData *sld);
 -void freeVertSlideVerts(TransInfo *t, TransCustomData *custom_data);
 +void freeVertSlideVerts(TransInfo *t, TransDataContainer *tc, TransCustomData *custom_data);
  void projectVertSlideData(TransInfo *t, bool is_final);
  
  
  /* TODO. transform_query.c */
  bool checkUseAxisMatrix(TransInfo *t);
  
 -#define TRANSFORM_DIST_MAX_PX 1000.0f
  #define TRANSFORM_SNAP_MAX_PX 100.0f
  #define TRANSFORM_DIST_INVALID -FLT_MAX
  
 +/* Temp macros. */
 +
 +/* This is to be replaced, just to get things compiling early on. */
 +#define TRANS_DATA_CONTAINER_FIRST_EVIL(t) (&(t)->data_container[0])
 +#define TRANS_DATA_CONTAINER_FIRST_OK(t) (&(t)->data_container[0])
 +/* For cases we _know_ there is only one handle. */
 +#define TRANS_DATA_CONTAINER_FIRST_SINGLE(t) (BLI_assert((t)->data_container_len == 1), (&(t)->data_container[0]))
 +
 +#define FOREACH_TRANS_DATA_CONTAINER(t, th) \
 +      for (TransDataContainer *tc = t->data_container, *tc_end = t->data_container + t->data_container_len; \
 +           th != tc_end; \
 +           th++)
 +
 +#define FOREACH_TRANS_DATA_CONTAINER_INDEX(t, th, i) \
 +      for (TransDataContainer *tc = ((i = 0), t->data_container), *tc_end = t->data_container + t->data_container_len; \
 +           th != tc_end; \
 +           th++, i++)
 +
  #endif
index 68b8b33fcb05f16fdfefcde34c14daff513a40d2,8bc53127b4ce1635c3293a1a7efe8a4601a3a05d..dc922b5c9badc6f5aebaa23c35d4cc3fa5f20d62
@@@ -49,9 -49,9 +49,9 @@@
  #include "BLI_blenlib.h"
  #include "BLI_utildefines.h"
  
 -#include "BIF_gl.h"
 +#include "GPU_immediate.h"
 +#include "GPU_state.h"
  
 -#include "BKE_DerivedMesh.h"
  #include "BKE_global.h"
  #include "BKE_object.h"
  #include "BKE_anim.h"  /* for duplis */
@@@ -70,8 -70,6 +70,8 @@@
  #include "ED_view3d.h"
  #include "ED_transform_snap_object_context.h"
  
 +#include "DEG_depsgraph.h"
 +
  #include "UI_resources.h"
  #include "UI_view2d.h"
  
@@@ -124,13 -122,13 +124,13 @@@ int BIF_snappingSupported(Object *obedi
  }
  #endif
  
- bool validSnap(TransInfo *t)
+ bool validSnap(const TransInfo *t)
  {
        return (t->tsnap.status & (POINT_INIT | TARGET_INIT)) == (POINT_INIT | TARGET_INIT) ||
               (t->tsnap.status & (MULTI_POINTS | TARGET_INIT)) == (MULTI_POINTS | TARGET_INIT);
  }
  
- bool activeSnap(TransInfo *t)
+ bool activeSnap(const TransInfo *t)
  {
        return ((t->modifiers & (MOD_SNAP | MOD_SNAP_INVERT)) == MOD_SNAP) ||
               ((t->modifiers & (MOD_SNAP | MOD_SNAP_INVERT)) == MOD_SNAP_INVERT);
@@@ -155,58 -153,82 +155,58 @@@ void drawSnapping(const struct bContex
        if (t->spacetype == SPACE_VIEW3D) {
                if (validSnap(t)) {
                        TransSnapPoint *p;
 -                      View3D *v3d = CTX_wm_view3d(C);
                        RegionView3D *rv3d = CTX_wm_region_view3d(C);
                        float imat[4][4];
                        float size;
  
 -                      glDisable(GL_DEPTH_TEST);
 +                      GPU_depth_test(false);
  
                        size = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE);
  
                        invert_m4_m4(imat, rv3d->viewmat);
  
 +                      uint pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
 +
 +                      immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
 +
                        for (p = t->tsnap.points.first; p; p = p->next) {
                                if (p == t->tsnap.selectedPoint) {
 -                                      glColor4ubv(selectedCol);
 +                                      immUniformColor4ubv(selectedCol);
                                }
                                else {
 -                                      glColor4ubv(col);
 +                                      immUniformColor4ubv(col);
                                }
  
 -                              drawcircball(GL_LINE_LOOP, p->co, ED_view3d_pixel_size(rv3d, p->co) * size * 0.75f, imat);
 +                              imm_drawcircball(p->co, ED_view3d_pixel_size(rv3d, p->co) * size * 0.75f, imat, pos);
                        }
  
                        if (t->tsnap.status & POINT_INIT) {
 -                              glColor4ubv(activeCol);
 +                              immUniformColor4ubv(activeCol);
  
 -                              drawcircball(GL_LINE_LOOP, t->tsnap.snapPoint, ED_view3d_pixel_size(rv3d, t->tsnap.snapPoint) * size, imat);
 +                              imm_drawcircball(t->tsnap.snapPoint, ED_view3d_pixel_size(rv3d, t->tsnap.snapPoint) * size, imat, pos);
                        }
  
                        /* draw normal if needed */
                        if (usingSnappingNormal(t) && validSnappingNormal(t)) {
 -                              glColor4ubv(activeCol);
 -
 -                              glBegin(GL_LINES);
 -                              glVertex3f(t->tsnap.snapPoint[0], t->tsnap.snapPoint[1], t->tsnap.snapPoint[2]);
 -                              glVertex3f(t->tsnap.snapPoint[0] + t->tsnap.snapNormal[0],
 -                                         t->tsnap.snapPoint[1] + t->tsnap.snapNormal[1],
 -                                         t->tsnap.snapPoint[2] + t->tsnap.snapNormal[2]);
 -                              glEnd();
 +                              immUniformColor4ubv(activeCol);
 +
 +                              immBegin(GWN_PRIM_LINES, 2);
 +                              immVertex3f(pos, t->tsnap.snapPoint[0], t->tsnap.snapPoint[1], t->tsnap.snapPoint[2]);
 +                              immVertex3f(pos, t->tsnap.snapPoint[0] + t->tsnap.snapNormal[0],
 +                                          t->tsnap.snapPoint[1] + t->tsnap.snapNormal[1],
 +                                          t->tsnap.snapPoint[2] + t->tsnap.snapNormal[2]);
 +                              immEnd();
                        }
  
 -                      if (v3d->zbuf)
 -                              glEnable(GL_DEPTH_TEST);
 +                      immUnbindProgram();
 +
 +                      GPU_depth_test(true);
                }
        }
        else if (t->spacetype == SPACE_IMAGE) {
                if (validSnap(t)) {
                        /* This will not draw, and Im nor sure why - campbell */
 -#if 0
 -                      float xuser_asp, yuser_asp;
 -                      int wi, hi;
 -                      float w, h;
 -
 -                      calc_image_view(G.sima, 'f');   // float
 -                      myortho2(G.v2d->cur.xmin, G.v2d->cur.xmax, G.v2d->cur.ymin, G.v2d->cur.ymax);
 -                      glLoadIdentity();
 -
 -                      ED_space_image_get_aspect(t->sa->spacedata.first, &xuser_aspx, &yuser_asp);
 -                      ED_space_image_width(t->sa->spacedata.first, &wi, &hi);
 -                      w = (((float)wi) / IMG_SIZE_FALLBACK) * G.sima->zoom * xuser_asp;
 -                      h = (((float)hi) / IMG_SIZE_FALLBACK) * G.sima->zoom * yuser_asp;
 -
 -                      cpack(0xFFFFFF);
 -                      glTranslate2fv(t->tsnap.snapPoint);
 -
 -                      //glRectf(0, 0, 1, 1);
 -
 -                      setlinestyle(0);
 -                      cpack(0x0);
 -                      fdrawline(-0.020 / w, 0, -0.1 / w, 0);
 -                      fdrawline(0.1 / w, 0, 0.020 / w, 0);
 -                      fdrawline(0, -0.020 / h, 0, -0.1 / h);
 -                      fdrawline(0, 0.1 / h, 0, 0.020 / h);
 -
 -                      glTranslatef(-t->tsnap.snapPoint[0], -t->tsnap.snapPoint[1], 0.0f);
 -                      setlinestyle(0);
 -#endif
 +                      /* TODO: see 2.7x for non-working code */
                }
        }
        else if (t->spacetype == SPACE_NODE) {
  
                        size = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE);
  
 -                      glEnable(GL_BLEND);
 +                      GPU_blend(true);
 +
 +                      uint pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
 +
 +                      immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
  
                        for (p = t->tsnap.points.first; p; p = p->next) {
                                if (p == t->tsnap.selectedPoint) {
 -                                      glColor4ubv(selectedCol);
 +                                      immUniformColor4ubv(selectedCol);
                                }
                                else {
 -                                      glColor4ubv(col);
 +                                      immUniformColor4ubv(col);
                                }
  
 -                              ED_node_draw_snap(&ar->v2d, p->co, size, 0);
 +                              ED_node_draw_snap(&ar->v2d, p->co, size, 0, pos);
                        }
  
                        if (t->tsnap.status & POINT_INIT) {
 -                              glColor4ubv(activeCol);
 +                              immUniformColor4ubv(activeCol);
  
 -                              ED_node_draw_snap(&ar->v2d, t->tsnap.snapPoint, size, t->tsnap.snapNodeBorder);
 +                              ED_node_draw_snap(&ar->v2d, t->tsnap.snapPoint, size, t->tsnap.snapNodeBorder, pos);
                        }
  
 -                      glDisable(GL_BLEND);
 +                      immUnbindProgram();
 +
 +                      GPU_blend(false);
                }
        }
  }
@@@ -268,88 -284,93 +268,88 @@@ eRedrawFlag handleSnapping(TransInfo *t
  
  void applyProject(TransInfo *t)
  {
 -      Main *bmain = CTX_data_main(t->context);
 -
        /* XXX FLICKER IN OBJECT MODE */
        if ((t->tsnap.project) && activeSnap(t) && (t->flag & T_NO_PROJECT) == 0) {
 -              TransData *td = t->data;
                float tvec[3];
 -              float imat[4][4];
                int i;
  
 -              if (t->flag & (T_EDIT | T_POSE)) {
 -                      Object *ob = t->obedit ? t->obedit : t->poseobj;
 -                      invert_m4_m4(imat, ob->obmat);
 -              }
 +              FOREACH_TRANS_DATA_CONTAINER(t, tc) {
 +                      TransData *td = tc->data;
 +                      for (i = 0; i < tc->data_len; i++, td++) {
 +                              float iloc[3], loc[3], no[3];
 +                              float mval_fl[2];
  
 -              for (i = 0; i < t->total; i++, td++) {
 -                      float iloc[3], loc[3], no[3];
 -                      float mval_fl[2];
 -                      float dist_px = TRANSFORM_DIST_MAX_PX;
 +                              if (td->flag & TD_NOACTION)
 +                                      break;
  
 -                      if (td->flag & TD_NOACTION)
 -                              break;
 +                              if (td->flag & TD_SKIP)
 +                                      continue;
  
 -                      if (td->flag & TD_SKIP)
 -                              continue;
 +                              if ((t->flag & T_PROP_EDIT) && (td->factor == 0.0f))
 +                                      continue;
  
 -                      if ((t->flag & T_PROP_EDIT) && (td->factor == 0.0f))
 -                              continue;
 -
 -                      copy_v3_v3(iloc, td->loc);
 -                      if (t->flag & (T_EDIT | T_POSE)) {
 -                              Object *ob = t->obedit ? t->obedit : t->poseobj;
 -                              mul_m4_v3(ob->obmat, iloc);
 -                      }
 -                      else if (t->flag & T_OBJECT) {
 -                              BKE_object_eval_transform_all(bmain->eval_ctx, t->scene, td->ob);
 -                              copy_v3_v3(iloc, td->ob->obmat[3]);
 -                      }
 +                              copy_v3_v3(iloc, td->loc);
 +                              if (tc->use_local_mat) {
 +                                      mul_m4_v3(tc->mat, iloc);
 +                              }
 +                              else if (t->flag & T_OBJECT) {
 +                                      BKE_object_eval_transform_all(t->depsgraph, t->scene, td->ob);
 +                                      copy_v3_v3(iloc, td->ob->obmat[3]);
 +                              }
  
 -                      if (ED_view3d_project_float_global(t->ar, iloc, mval_fl, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) {
 -                              if (snapObjectsTransform(
 -                                      t, mval_fl, &dist_px,
 -                                      loc, no))
 -                              {
 -//                                    if (t->flag & (T_EDIT|T_POSE)) {
 -//                                            mul_m4_v3(imat, loc);
 -//                                    }
 +                              if (ED_view3d_project_float_global(t->ar, iloc, mval_fl, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) {
 +                                      if (ED_transform_snap_object_project_view3d(
 +                                              t->tsnap.object_context,
 +                                              SCE_SNAP_MODE_FACE,
 +                                              &(const struct SnapObjectParams){
 +                                                  .snap_select = t->tsnap.modeSelect,
 +                                                  .use_object_edit_cage = (t->flag & T_EDIT) != 0,
 +                                                  .use_occlusion_test = false,
 +                                              },
 +                                              mval_fl, 0, loc, no))
 +                                      {
 +#if 0
 +                                              if (tc->use_local_mat) {
 +                                                      mul_m4_v3(tc->imat, loc);
 +                                              }
 +#endif
  
 -                                      sub_v3_v3v3(tvec, loc, iloc);
 +                                              sub_v3_v3v3(tvec, loc, iloc);
  
 -                                      mul_m3_v3(td->smtx, tvec);
 +                                              mul_m3_v3(td->smtx, tvec);
  
 -                                      add_v3_v3(td->loc, tvec);
 +                                              add_v3_v3(td->loc, tvec);
  
 -                                      if (t->tsnap.align && (t->flag & T_OBJECT)) {
 -                                              /* handle alignment as well */
 -                                              const float *original_normal;
 -                                              float mat[3][3];
 +                                              if (t->tsnap.align && (t->flag & T_OBJECT)) {
 +                                                      /* handle alignment as well */
 +                                                      const float *original_normal;
 +                                                      float mat[3][3];
  
 -                                              /* In pose mode, we want to align normals with Y axis of bones... */
 -                                              original_normal = td->axismtx[2];
 +                                                      /* In pose mode, we want to align normals with Y axis of bones... */
 +                                                      original_normal = td->axismtx[2];
  
 -                                              rotation_between_vecs_to_mat3(mat, original_normal, no);
 +                                                      rotation_between_vecs_to_mat3(mat, original_normal, no);
  
 -                                              transform_data_ext_rotate(td, mat, true);
 +                                                      transform_data_ext_rotate(td, mat, true);
  
 -                                              /* TODO support constraints for rotation too? see ElementRotation */
 +                                                      /* TODO support constraints for rotation too? see ElementRotation */
 +                                              }
                                        }
                                }
 -                      }
  
 -                      //XXX constraintTransLim(t, td);
 +                              //XXX constraintTransLim(t, td);
 +                      }
                }
        }
  }
  
  void applyGridAbsolute(TransInfo *t)
  {
 -      Main *bmain = CTX_data_main(t->context);
 -
        float grid_size = 0.0f;
        GearsType grid_action;
 -      TransData *td;
 -      float (*obmat)[4] = NULL;
 -      bool use_obmat = false;
        int i;
  
 -      if (!(activeSnap(t) && (ELEM(t->tsnap.mode, SCE_SNAP_MODE_INCREMENT, SCE_SNAP_MODE_GRID))))
 +      if (!(activeSnap(t) && (t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID))))
                return;
  
        grid_action = BIG_GEARS;
        if (grid_size == 0.0f)
                return;
  
 -      if (t->flag & (T_EDIT | T_POSE)) {
 -              Object *ob = t->obedit ? t->obedit : t->poseobj;
 -              obmat = ob->obmat;
 -              use_obmat = true;
 -      }
 +      FOREACH_TRANS_DATA_CONTAINER(t, tc) {
 +              TransData *td;
  
 -      for (i = 0, td = t->data; i < t->total; i++, td++) {
 -              float iloc[3], loc[3], tvec[3];
 +              for (i = 0, td = tc->data; i < tc->data_len; i++, td++) {
 +                      float iloc[3], loc[3], tvec[3];
  
 -              if (td->flag & TD_NOACTION)
 -                      break;
 +                      if (td->flag & TD_NOACTION)
 +                              break;
  
 -              if (td->flag & TD_SKIP)
 -                      continue;
 +                      if (td->flag & TD_SKIP)
 +                              continue;
  
 -              if ((t->flag & T_PROP_EDIT) && (td->factor == 0.0f))
 -                      continue;
 +                      if ((t->flag & T_PROP_EDIT) && (td->factor == 0.0f))
 +                              continue;
  
 -              copy_v3_v3(iloc, td->loc);
 -              if (use_obmat) {
 -                      mul_m4_v3(obmat, iloc);
 -              }
 -              else if (t->flag & T_OBJECT) {
 -                      BKE_object_eval_transform_all(bmain->eval_ctx, t->scene, td->ob);
 -                      copy_v3_v3(iloc, td->ob->obmat[3]);
 -              }
 +                      copy_v3_v3(iloc, td->loc);
 +                      if (tc->use_local_mat) {
 +                              mul_m4_v3(tc->mat, iloc);
 +                      }
 +                      else if (t->flag & T_OBJECT) {
 +                              BKE_object_eval_transform_all(t->depsgraph, t->scene, td->ob);
 +                              copy_v3_v3(iloc, td->ob->obmat[3]);
 +                      }
  
 -              mul_v3_v3fl(loc, iloc, 1.0f / grid_size);
 -              loc[0] = roundf(loc[0]);
 -              loc[1] = roundf(loc[1]);
 -              loc[2] = roundf(loc[2]);
 -              mul_v3_fl(loc, grid_size);
 +                      mul_v3_v3fl(loc, iloc, 1.0f / grid_size);
 +                      loc[0] = roundf(loc[0]);
 +                      loc[1] = roundf(loc[1]);
 +                      loc[2] = roundf(loc[2]);
 +                      mul_v3_fl(loc, grid_size);
  
 -              sub_v3_v3v3(tvec, loc, iloc);
 -              mul_m3_v3(td->smtx, tvec);
 -              add_v3_v3(td->loc, tvec);
 +                      sub_v3_v3v3(tvec, loc, iloc);
 +                      mul_m3_v3(td->smtx, tvec);
 +                      add_v3_v3(td->loc, tvec);
 +              }
        }
  }
  
  void applySnapping(TransInfo *t, float *vec)
  {
 -      /* project is not applied this way */
 -      if (t->tsnap.project)
 +      if (t->tsnap.project && t->tsnap.mode == SCE_SNAP_MODE_FACE) {
 +              /* Each Trans Data already makes the snap to face */
                return;
 +      }
  
        if (t->tsnap.status & SNAP_FORCED) {
                t->tsnap.targetSnap(t);
  
                t->tsnap.applySnap(t, vec);
        }
 -      else if (!ELEM(t->tsnap.mode, SCE_SNAP_MODE_INCREMENT, SCE_SNAP_MODE_GRID) && activeSnap(t)) {
 +      else if (((t->tsnap.mode & ~(SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)) != 0) &&
 +               activeSnap(t))
 +      {
                double current = PIL_check_seconds_timer();
  
                // Time base quirky code to go around findnearest slowness
@@@ -451,12 -471,12 +451,12 @@@ void resetSnapping(TransInfo *t
        t->tsnap.snapNodeBorder = 0;
  }
  
- bool usingSnappingNormal(TransInfo *t)
+ bool usingSnappingNormal(const TransInfo *t)
  {
        return t->tsnap.align;
  }
  
- bool validSnappingNormal(TransInfo *t)
+ bool validSnappingNormal(const TransInfo *t)
  {
        if (validSnap(t)) {
                if (!is_zero_v3(t->tsnap.snapNormal)) {
@@@ -500,10 -520,9 +500,10 @@@ static void initSnappingMode(TransInfo 
  {
        Main *bmain = CTX_data_main(t->context);
        ToolSettings *ts = t->settings;
 -      Object *obedit = t->obedit;
 -      Scene *scene = t->scene;
 -      Base *base_act = scene->basact;
 +      /* All obedit types will match. */
 +      const int obedit_type = t->data_container->obedit ? t->data_container->obedit->type : -1;
 +      ViewLayer *view_layer = t->view_layer;
 +      Base *base_act = view_layer->basact;
  
        if (t->spacetype == SPACE_NODE) {
                /* force project off when not supported */
        }
        else {
                /* force project off when not supported */
 -              if (ts->snap_mode != SCE_SNAP_MODE_FACE)
 +              if ((ts->snap_mode & SCE_SNAP_MODE_FACE) == 0)
                        t->tsnap.project = 0;
  
                t->tsnap.mode = ts->snap_mode;
  
                /* Edit mode */
                if (t->tsnap.applySnap != NULL && // A snapping function actually exist
 -                  (obedit != NULL && ELEM(obedit->type, OB_MESH, OB_ARMATURE, OB_CURVE, OB_LATTICE, OB_MBALL)) ) // Temporary limited to edit mode meshes, armature, curves, mballs
 +                  ((obedit_type != -1) && ELEM(obedit_type, OB_MESH, OB_ARMATURE, OB_CURVE, OB_LATTICE, OB_MBALL)) ) // Temporary limited to edit mode meshes, armature, curves, mballs
                {
                        /* Exclude editmesh if using proportional edit */
 -                      if ((obedit->type == OB_MESH) && (t->flag & T_PROP_EDIT)) {
 +                      if ((obedit_type == OB_MESH) && (t->flag & T_PROP_EDIT)) {
                                t->tsnap.modeSelect = SNAP_NOT_ACTIVE;
                        }
                        else {
                }
                /* Particles edit mode*/
                else if (t->tsnap.applySnap != NULL && // A snapping function actually exist
 -                       (obedit == NULL && base_act && base_act->object && base_act->object->mode & OB_MODE_PARTICLE_EDIT))
 +                       ((obedit_type == -1) && base_act && base_act->object && base_act->object->mode & OB_MODE_PARTICLE_EDIT))
                {
                        t->tsnap.modeSelect = SNAP_ALL;
                }
                /* Object mode */
                else if (t->tsnap.applySnap != NULL && // A snapping function actually exist
 -                       (obedit == NULL) ) // Object Mode
 +                       (obedit_type == -1) ) // Object Mode
                {
                        /* In "Edit Strokes" mode, Snap tool can perform snap to selected or active objects (see T49632)
                         * TODO: perform self snap in gpencil_strokes */
 -                      t->tsnap.modeSelect = ((t->options & CTX_GPENCIL_STROKES) != 0) ? SNAP_ALL : SNAP_NOT_SELECTED;
 +                      t->tsnap.modeSelect = (
 +                              ((t->options & (CTX_GPENCIL_STROKES | CTX_CURSOR)) != 0) ?
 +                              SNAP_ALL : SNAP_NOT_SELECTED);
                }
                else {
                        /* Grid if snap is not possible */
        if (t->spacetype == SPACE_VIEW3D) {
                if (t->tsnap.object_context == NULL) {
                        t->tsnap.object_context = ED_transform_snap_object_context_create_view3d(
 -                              bmain, t->scene, 0,
 -                              t->ar, t->view);
 +                              bmain, t->scene, t->depsgraph, 0, t->ar, t->view);
  
                        ED_transform_snap_object_context_set_editmesh_callbacks(
                                t->tsnap.object_context,
@@@ -789,7 -807,7 +789,7 @@@ void removeSnapPoint(TransInfo *t
        }
  }
  
- void getSnapPoint(TransInfo *t, float vec[3])
+ void getSnapPoint(const TransInfo *t, float vec[3])
  {
        if (t->tsnap.points.first) {
                TransSnapPoint *p;
@@@ -865,8 -883,7 +865,8 @@@ static float TranslationBetween(TransIn
        return len_squared_v3v3(p1, p2);
  }
  
 -static float RotationBetween(TransInfo *t, const float p1[3], const float p2[3])
 +static float RotationBetween(
 +        TransInfo *t, const float p1[3], const float p2[3])
  {
        float angle, start[3], end[3];
  
        if (t->con.applyRot != NULL && (t->con.mode & CON_APPLY)) {
                float axis[3], tmp[3];
  
 -              t->con.applyRot(t, NULL, axis, NULL);
 +              t->con.applyRot(t, NULL, NULL, axis, NULL);
  
                project_v3_v3v3(tmp, end, axis);
                sub_v3_v3v3(end, end, tmp);
@@@ -957,22 -974,18 +957,22 @@@ static void CalcSnapGeometry(TransInfo 
                mval[0] = t->mval[0];
                mval[1] = t->mval[1];
  
 -              if (t->tsnap.mode == SCE_SNAP_MODE_VOLUME) {
 -                      found = peelObjectsTransform(
 -                              t, mval,
 -                              (t->settings->snap_flag & SCE_SNAP_PEEL_OBJECT) != 0,
 -                              loc, no, NULL);
 -              }
 -              else {
 +              if (t->tsnap.mode &
 +                  (SCE_SNAP_MODE_VERTEX |
 +                   SCE_SNAP_MODE_EDGE   |
 +                   SCE_SNAP_MODE_FACE))
 +              {
                        zero_v3(no);  /* objects won't set this */
                        found = snapObjectsTransform(
                                t, mval, &dist_px,
                                loc, no);
                }
 +              if ((found == false) && (t->tsnap.mode & SCE_SNAP_MODE_VOLUME)) {
 +                      found = peelObjectsTransform(
 +                              t, mval,
 +                              (t->settings->snap_flag & SCE_SNAP_PEEL_OBJECT) != 0,
 +                              loc, no, NULL);
 +              }
  
                if (found == true) {
                        copy_v3_v3(t->tsnap.snapPoint, loc);
                        t->tsnap.status &= ~POINT_INIT;
                }
        }
 -      else if (t->spacetype == SPACE_IMAGE && t->obedit != NULL && t->obedit->type == OB_MESH) {
 -              /* same as above but for UV's */
 -              Image *ima = ED_space_image(t->sa->spacedata.first);
 -              float co[2];
 +      else if (t->spacetype == SPACE_IMAGE && t->obedit_type == OB_MESH) {
 +              if (t->tsnap.mode & SCE_SNAP_MODE_VERTEX) {
 +                      Image *ima = ED_space_image(t->sa->spacedata.first);
 +                      float co[2];
  
 -              UI_view2d_region_to_view(&t->ar->v2d, t->mval[0], t->mval[1], &co[0], &co[1]);
 +                      UI_view2d_region_to_view(&t->ar->v2d, t->mval[0], t->mval[1], &co[0], &co[1]);
  
 -              if (ED_uvedit_nearest_uv(t->scene, t->obedit, ima, co, t->tsnap.snapPoint)) {
 -                      t->tsnap.snapPoint[0] *= t->aspect[0];
 -                      t->tsnap.snapPoint[1] *= t->aspect[1];
 +                      if (ED_uvedit_nearest_uv(t->scene, TRANS_DATA_CONTAINER_FIRST_EVIL(t)->obedit, ima, co, t->tsnap.snapPoint)) {
 +                              t->tsnap.snapPoint[0] *= t->aspect[0];
 +                              t->tsnap.snapPoint[1] *= t->aspect[1];
  
 -                      t->tsnap.status |=  POINT_INIT;
 -              }
 -              else {
 -                      t->tsnap.status &= ~POINT_INIT;
 +                              t->tsnap.status |= POINT_INIT;
 +                      }
 +                      else {
 +                              t->tsnap.status &= ~POINT_INIT;
 +                      }
                }
        }
        else if (t->spacetype == SPACE_NODE) {
 -              float loc[2];
 -              float dist_px = SNAP_MIN_DISTANCE; // Use a user defined value here
 -              char node_border;
 +              if (t->tsnap.mode & (SCE_SNAP_MODE_NODE_X | SCE_SNAP_MODE_NODE_Y)) {
 +                      float loc[2];
 +                      float dist_px = SNAP_MIN_DISTANCE; // Use a user defined value here
 +                      char node_border;
  
 -              if (snapNodesTransform(t, t->mval, loc, &dist_px, &node_border)) {
 -                      copy_v2_v2(t->tsnap.snapPoint, loc);
 -                      t->tsnap.snapNodeBorder = node_border;
 +                      if (snapNodesTransform(t, t->mval, loc, &dist_px, &node_border)) {
 +                              copy_v2_v2(t->tsnap.snapPoint, loc);
 +                              t->tsnap.snapNodeBorder = node_border;
  
 -                      t->tsnap.status |=  POINT_INIT;
 -              }
 -              else {
 -                      t->tsnap.status &= ~POINT_INIT;
 +                              t->tsnap.status |= POINT_INIT;
 +                      }
 +                      else {
 +                              t->tsnap.status &= ~POINT_INIT;
 +                      }
                }
        }
  }
@@@ -1069,6 -1079,11 +1069,6 @@@ static void TargetSnapActive(TransInfo 
        /* Only need to calculate once */
        if ((t->tsnap.status & TARGET_INIT) == 0) {
                if (calculateCenterActive(t, true, t->tsnap.snapTarget)) {
 -                      if (t->flag & (T_EDIT | T_POSE)) {
 -                              Object *ob = t->obedit ? t->obedit : t->poseobj;
 -                              mul_m4_v3(ob->obmat, t->tsnap.snapTarget);
 -                      }
 -
                        TargetSnapOffset(t, NULL);
  
                        t->tsnap.status |= TARGET_INIT;
@@@ -1086,30 -1101,23 +1086,30 @@@ static void TargetSnapMedian(TransInfo 
  {
        // Only need to calculate once
        if ((t->tsnap.status & TARGET_INIT) == 0) {
 -              TransData *td = NULL;
 -              int i;
 +              int i_accum = 0;
  
                t->tsnap.snapTarget[0] = 0;
                t->tsnap.snapTarget[1] = 0;
                t->tsnap.snapTarget[2] = 0;
  
 -              for (td = t->data, i = 0; i < t->total && td->flag & TD_SELECTED; i++, td++) {
 -                      add_v3_v3(t->tsnap.snapTarget, td->center);
 +              FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 +                      TransData *td = tc->data;
 +                      int i;
 +                      for (i = 0; i < tc->data_len && td->flag & TD_SELECTED; i++, td++) {
 +                              /* TODO(campbell): perform the global transformation once per TransDataContainer */
 +                              if (tc->use_local_mat) {
 +                                      float v[3];
 +                                      mul_v3_m4v3(v, tc->mat, td->center);
 +                                      add_v3_v3(t->tsnap.snapTarget, v);
 +                              }
 +                              else {
 +                                      add_v3_v3(t->tsnap.snapTarget, td->center);
 +                              }
 +                      }
 +                      i_accum += i;
                }
  
 -              mul_v3_fl(t->tsnap.snapTarget, 1.0 / i);
 -
 -              if (t->flag & (T_EDIT | T_POSE)) {
 -                      Object *ob = t->obedit ? t->obedit : t->poseobj;
 -                      mul_m4_v3(ob->obmat, t->tsnap.snapTarget);
 -              }
 +              mul_v3_fl(t->tsnap.snapTarget, 1.0 / i_accum);
  
                TargetSnapOffset(t, NULL);
  
@@@ -1122,44 -1130,24 +1122,44 @@@ static void TargetSnapClosest(TransInf
        // Only valid if a snap point has been selected
        if (t->tsnap.status & POINT_INIT) {
                float dist_closest = 0.0f;
 -              TransData *closest = NULL, *td = NULL;
 +              TransData *closest = NULL;
  
                /* Object mode */
                if (t->flag & T_OBJECT) {
                        int i;
 -                      for (td = t->data, i = 0; i < t->total && td->flag & TD_SELECTED; i++, td++) {
 -                              struct BoundBox *bb = BKE_object_boundbox_get(td->ob);
 -
 -                              /* use boundbox if possible */
 -                              if (bb) {
 -                                      int j;
 -
 -                                      for (j = 0; j < 8; j++) {
 +                      FOREACH_TRANS_DATA_CONTAINER(t, tc) {
 +                              TransData *td = tc->data;
 +                              for (td = tc->data, i = 0; i < tc->data_len && td->flag & TD_SELECTED; i++, td++) {
 +                                      struct BoundBox *bb = BKE_object_boundbox_get(td->ob);
 +
 +                                      /* use boundbox if possible */
 +                                      if (bb) {
 +                                              int j;
 +
 +                                              for (j = 0; j < 8; j++) {
 +                                                      float loc[3];
 +                                                      float dist;
 +
 +                                                      copy_v3_v3(loc, bb->vec[j]);
 +                                                      mul_m4_v3(td->ext->obmat, loc);
 +
 +                                                      dist = t->tsnap.distance(t, loc, t->tsnap.snapPoint);
 +
 +                                                      if ((dist != TRANSFORM_DIST_INVALID) &&
 +                                                          (closest == NULL || fabsf(dist) < fabsf(dist_closest)))
 +                                                      {
 +                                                              copy_v3_v3(t->tsnap.snapTarget, loc);
 +                                                              closest = td;
 +                                                              dist_closest = dist;
 +                                                      }
 +                                              }
 +                                      }
 +                                      /* use element center otherwise */
 +                                      else {
                                                float loc[3];
                                                float dist;
  
 -                                              copy_v3_v3(loc, bb->vec[j]);
 -                                              mul_m4_v3(td->ext->obmat, loc);
 +                                              copy_v3_v3(loc, td->center);
  
                                                dist = t->tsnap.distance(t, loc, t->tsnap.snapPoint);
  
                                                {
                                                        copy_v3_v3(t->tsnap.snapTarget, loc);
                                                        closest = td;
 -                                                      dist_closest = dist;
                                                }
                                        }
                                }
 -                              /* use element center otherwise */
 -                              else {
 +                      }
 +              }
 +              else {
 +                      FOREACH_TRANS_DATA_CONTAINER(t, tc) {
 +                              TransData *td = tc->data;
 +                              int i;
 +                              for (i = 0; i < tc->data_len && td->flag & TD_SELECTED; i++, td++) {
                                        float loc[3];
                                        float dist;
  
                                        copy_v3_v3(loc, td->center);
  
 +                                      if (tc->use_local_mat) {
 +                                              mul_m4_v3(tc->mat, loc);
 +                                      }
 +
                                        dist = t->tsnap.distance(t, loc, t->tsnap.snapPoint);
  
                                        if ((dist != TRANSFORM_DIST_INVALID) &&
                                        {
                                                copy_v3_v3(t->tsnap.snapTarget, loc);
                                                closest = td;
 +                                              dist_closest = dist;
                                        }
                                }
                        }
                }
 -              else {
 -                      int i;
 -                      for (td = t->data, i = 0; i < t->total && td->flag & TD_SELECTED; i++, td++) {
 -                              float loc[3];
 -                              float dist;
 -
 -                              copy_v3_v3(loc, td->center);
 -
 -                              if (t->flag & (T_EDIT | T_POSE)) {
 -                                      Object *ob = t->obedit ? t->obedit : t->poseobj;
 -                                      mul_m4_v3(ob->obmat, loc);
 -                              }
 -
 -                              dist = t->tsnap.distance(t, loc, t->tsnap.snapPoint);
 -
 -                              if ((dist != TRANSFORM_DIST_INVALID) &&
 -                                  (closest == NULL || fabsf(dist) < fabsf(dist_closest)))
 -                              {
 -                                      copy_v3_v3(t->tsnap.snapTarget, loc);
 -                                      closest = td;
 -                                      dist_closest = dist;
 -                              }
 -                      }
 -              }
  
                TargetSnapOffset(t, closest);
  
@@@ -1211,15 -1214,16 +1211,15 @@@ bool snapObjectsTransform
          float *dist_px,
          float r_loc[3], float r_no[3])
  {
 -      return ED_transform_snap_object_project_view3d_ex(
 +      return ED_transform_snap_object_project_view3d(
                t->tsnap.object_context,
                t->scene->toolsettings->snap_mode,
                &(const struct SnapObjectParams){
                    .snap_select = t->tsnap.modeSelect,
                    .use_object_edit_cage = (t->flag & T_EDIT) != 0,
 +                  .use_occlusion_test = t->scene->toolsettings->snap_mode != SCE_SNAP_MODE_FACE,
                },
 -              mval, dist_px, NULL,
 -              r_loc, r_no, NULL,
 -              NULL, NULL);
 +              mval, dist_px, r_loc, r_no);
  }
  
  
@@@ -1325,14 -1329,15 +1325,14 @@@ static bool snapNodeTest(View2D *v2d, b
  
  static NodeBorder snapNodeBorder(int snap_node_mode)
  {
 -      switch (snap_node_mode) {
 -              case SCE_SNAP_MODE_NODE_X:
 -                      return NODE_LEFT | NODE_RIGHT;
 -              case SCE_SNAP_MODE_NODE_Y:
 -                      return NODE_TOP | NODE_BOTTOM;
 -              case SCE_SNAP_MODE_NODE_XY:
 -                      return NODE_LEFT | NODE_RIGHT | NODE_TOP | NODE_BOTTOM;
 +      NodeBorder flag = 0;
 +      if (snap_node_mode & SCE_SNAP_MODE_NODE_X) {
 +              flag |= NODE_LEFT | NODE_RIGHT;
        }
 -      return 0;
 +      if (snap_node_mode & SCE_SNAP_MODE_NODE_Y) {
 +              flag |= NODE_TOP | NODE_BOTTOM;
 +      }
 +      return flag;
  }
  
  static bool snapNode(
@@@ -1440,13 -1445,9 +1440,13 @@@ void snapGridIncrement(TransInfo *t, fl
  {
        GearsType action;
  
 -      /* only do something if using absolute or incremental grid snapping */
 -      if (!ELEM(t->tsnap.mode, SCE_SNAP_MODE_INCREMENT, SCE_SNAP_MODE_GRID))
 +      /* only do something if using absolute or incremental grid snapping
 +       * and there is no valid snap point */
 +      if (!(t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)) ||
 +          validSnap(t))
 +      {
                return;
 +      }
  
        action = activeSnap(t) ? BIG_GEARS : NO_GEARS;
  
@@@ -1486,7 -1487,7 +1486,7 @@@ static void applyGridIncrement(TransInf
        const float *asp = use_aspect ? t->aspect : asp_local;
        int i;
  
 -      BLI_assert(ELEM(t->tsnap.mode, SCE_SNAP_MODE_INCREMENT, SCE_SNAP_MODE_GRID));
 +      BLI_assert(t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID));
        BLI_assert(max_index <= 2);
  
        /* Early bailing out if no need to snap */