Transform: Add option to exclude back facing geometry from snapping
authormano-wii <germano.costa@ig.com.br>
Thu, 31 Oct 2019 17:09:53 +0000 (14:09 -0300)
committermano-wii <germano.costa@ig.com.br>
Thu, 31 Oct 2019 17:09:53 +0000 (14:09 -0300)
Add new `Backface Culling` option to the snapping properties.
This option has nothing to do with the view3d display or the
workbench `Backface Culling` option.

Limitation:
- In edit mode, this option only affects snap to faces.

Maniphest Tasks: T71217

Differential Revision: https://developer.blender.org/D6155

release/scripts/startup/bl_ui/space_view3d.py
source/blender/editors/include/ED_transform_snap_object_context.h
source/blender/editors/transform/transform_snap.c
source/blender/editors/transform/transform_snap_object.c
source/blender/makesdna/DNA_scene_types.h
source/blender/makesrna/intern/rna_scene.c

index ac1243561a39dbecb133572033903dcd9f5b7ab8..8ff0ba7bce4cb37577d7190d656aee1cdbb360af 100644 (file)
@@ -6082,6 +6082,8 @@ class VIEW3D_PT_snapping(Panel):
             row = col.row(align=True)
             row.prop(tool_settings, "snap_target", expand=True)
 
+            col.prop(tool_settings, "use_snap_backface_culling")
+
             if obj:
                 if object_mode == 'EDIT':
                     col.prop(tool_settings, "use_snap_self")
index 40e0005b48715f5a11acc3029c2cd11cfb206025..0e20abe42210f09a66bf672672e9fe4a0ee753b7 100644 (file)
@@ -68,6 +68,8 @@ struct SnapObjectParams {
   unsigned int use_object_edit_cage : 1;
   /* snap to the closest element, use when using more than one snap type */
   unsigned int use_occlusion_test : 1;
+  /* exclude back facing geometry from snapping */
+  unsigned int use_backface_culling : 1;
 };
 
 typedef struct SnapObjectContext SnapObjectContext;
index a66e76abc58450e052b8d2934fc350ff8b1c92c5..15208c1a7d2519b7821e3768f77323c838873e19 100644 (file)
@@ -379,6 +379,8 @@ void applyProject(TransInfo *t)
                       .snap_select = t->tsnap.modeSelect,
                       .use_object_edit_cage = (t->flag & T_EDIT) != 0,
                       .use_occlusion_test = false,
+                      .use_backface_culling = (t->scene->toolsettings->snap_flag &
+                                               SCE_SNAP_BACKFACE_CULLING) != 0,
                   },
                   mval_fl,
                   NULL,
@@ -1364,6 +1366,8 @@ short snapObjectsTransform(
           .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,
+          .use_backface_culling = (t->scene->toolsettings->snap_flag &
+                                   SCE_SNAP_BACKFACE_CULLING) != 0,
       },
       mval,
       t->tsnap.snapTarget,
index f35a2808f2254a335f34691b621027e3952d5610..14e8b8f97e02cd26cc0f6431f07a50c67653754b 100644 (file)
@@ -201,8 +201,12 @@ static SnapObjectData_EditMesh *snap_object_data_editmesh_get(SnapObjectContext
   return *sod_p;
 }
 
-typedef void (*IterSnapObjsCallback)(
-    SnapObjectContext *sctx, bool is_obedit, Object *ob, float obmat[4][4], void *data);
+typedef void (*IterSnapObjsCallback)(SnapObjectContext *sctx,
+                                     bool is_obedit,
+                                     bool use_backface_culling,
+                                     Object *ob,
+                                     float obmat[4][4],
+                                     void *data);
 
 /**
  * Walks through all objects in the scene to create the list of objects to snap.
@@ -219,6 +223,7 @@ static void iter_snap_objects(SnapObjectContext *sctx,
   const View3D *v3d = sctx->v3d_data.v3d;
   const eSnapSelect snap_select = params->snap_select;
   const bool use_object_edit_cage = params->use_object_edit_cage;
+  const bool use_backface_culling = params->use_backface_culling;
 
   Base *base_act = view_layer->basact;
   for (Base *base = view_layer->object_bases.first; base != NULL; base = base->next) {
@@ -250,12 +255,14 @@ static void iter_snap_objects(SnapObjectContext *sctx,
       DupliObject *dupli_ob;
       ListBase *lb = object_duplilist(sctx->depsgraph, sctx->scene, obj_eval);
       for (dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next) {
-        sob_callback(sctx, use_object_edit_cage, dupli_ob->ob, dupli_ob->mat, data);
+        sob_callback(
+            sctx, use_object_edit_cage, use_backface_culling, dupli_ob->ob, dupli_ob->mat, data);
       }
       free_object_duplilist(lb);
     }
 
-    sob_callback(sctx, use_object_edit_cage, obj_eval, obj_eval->obmat, data);
+    sob_callback(
+        sctx, use_object_edit_cage, use_backface_culling, obj_eval, obj_eval->obmat, data);
   }
 }
 
@@ -350,6 +357,70 @@ static void raycast_all_cb(void *userdata, int index, const BVHTreeRay *ray, BVH
   }
 }
 
+static bool raycast_tri_backface_culling_test(
+    const float dir[3], const float v0[3], const float v1[3], const float v2[3], float no[3])
+{
+  cross_tri_v3(no, v0, v1, v2);
+  return dot_v3v3(no, dir) < 0.0f;
+}
+
+/* Callback to raycast with backface culling (Mesh). */
+static void mesh_looptri_raycast_backface_culling_cb(void *userdata,
+                                                     int index,
+                                                     const BVHTreeRay *ray,
+                                                     BVHTreeRayHit *hit)
+{
+  const BVHTreeFromMesh *data = (BVHTreeFromMesh *)userdata;
+  const MVert *vert = data->vert;
+  const MLoopTri *lt = &data->looptri[index];
+  const float *vtri_co[3] = {
+      vert[data->loop[lt->tri[0]].v].co,
+      vert[data->loop[lt->tri[1]].v].co,
+      vert[data->loop[lt->tri[2]].v].co,
+  };
+  float dist = bvhtree_ray_tri_intersection(ray, hit->dist, UNPACK3(vtri_co));
+
+  if (dist >= 0 && dist < hit->dist) {
+    float no[3];
+    if (raycast_tri_backface_culling_test(ray->direction, UNPACK3(vtri_co), no)) {
+      hit->index = index;
+      hit->dist = dist;
+      madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist);
+      normalize_v3_v3(hit->no, no);
+    }
+  }
+}
+
+/* Callback to raycast with backface culling (EditMesh). */
+static void editmesh_looptri_raycast_backface_culling_cb(void *userdata,
+                                                         int index,
+                                                         const BVHTreeRay *ray,
+                                                         BVHTreeRayHit *hit)
+{
+  const BVHTreeFromEditMesh *data = (BVHTreeFromEditMesh *)userdata;
+  BMEditMesh *em = data->em;
+  const BMLoop **ltri = (const BMLoop **)em->looptris[index];
+
+  const float *t0, *t1, *t2;
+  t0 = ltri[0]->v->co;
+  t1 = ltri[1]->v->co;
+  t2 = ltri[2]->v->co;
+
+  {
+    float dist = bvhtree_ray_tri_intersection(ray, hit->dist, t0, t1, t2);
+
+    if (dist >= 0 && dist < hit->dist) {
+      float no[3];
+      if (raycast_tri_backface_culling_test(ray->direction, t0, t1, t2, no)) {
+        hit->index = index;
+        hit->dist = dist;
+        madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist);
+        normalize_v3_v3(hit->no, no);
+      }
+    }
+  }
+}
+
 static bool raycastMesh(SnapObjectContext *sctx,
                         const float ray_start[3],
                         const float ray_dir[3],
@@ -358,6 +429,7 @@ static bool raycastMesh(SnapObjectContext *sctx,
                         const float obmat[4][4],
                         const unsigned int ob_index,
                         bool use_hide,
+                        bool use_backface_culling,
                         /* read/write args */
                         float *ray_depth,
                         /* return args */
@@ -494,7 +566,8 @@ static bool raycastMesh(SnapObjectContext *sctx,
                              ray_normal_local,
                              0.0f,
                              &hit,
-                             treedata->raycast_callback,
+                             use_backface_culling ? mesh_looptri_raycast_backface_culling_cb :
+                                                    treedata->raycast_callback,
                              treedata) != -1) {
       hit.dist += len_diff;
       hit.dist /= local_scale;
@@ -530,6 +603,7 @@ static bool raycastEditMesh(SnapObjectContext *sctx,
                             BMEditMesh *em,
                             const float obmat[4][4],
                             const unsigned int ob_index,
+                            bool use_backface_culling,
                             /* read/write args */
                             float *ray_depth,
                             /* return args */
@@ -670,7 +744,8 @@ static bool raycastEditMesh(SnapObjectContext *sctx,
                              ray_normal_local,
                              0.0f,
                              &hit,
-                             treedata->raycast_callback,
+                             use_backface_culling ? editmesh_looptri_raycast_backface_culling_cb :
+                                                    treedata->raycast_callback,
                              treedata) != -1) {
       hit.dist += len_diff;
       hit.dist /= local_scale;
@@ -715,6 +790,7 @@ static bool raycastObj(SnapObjectContext *sctx,
                        const unsigned int ob_index,
                        bool use_obedit,
                        bool use_occlusion_test,
+                       bool use_backface_culling,
                        /* read/write args */
                        float *ray_depth,
                        /* return args */
@@ -753,6 +829,7 @@ static bool raycastObj(SnapObjectContext *sctx,
                                    em,
                                    obmat,
                                    ob_index,
+                                   use_backface_culling,
                                    ray_depth,
                                    r_loc,
                                    r_no,
@@ -773,6 +850,7 @@ static bool raycastObj(SnapObjectContext *sctx,
                            obmat,
                            ob_index,
                            use_hide,
+                           use_backface_culling,
                            ray_depth,
                            r_loc,
                            r_no,
@@ -792,6 +870,7 @@ static bool raycastObj(SnapObjectContext *sctx,
                              obmat,
                              ob_index,
                              false,
+                             use_backface_culling,
                              ray_depth,
                              r_loc,
                              r_no,
@@ -832,8 +911,12 @@ struct RaycastObjUserData {
   bool ret;
 };
 
-static void raycast_obj_cb(
-    SnapObjectContext *sctx, bool use_obedit, Object *ob, float obmat[4][4], void *data)
+static void raycast_obj_cb(SnapObjectContext *sctx,
+                           bool use_obedit,
+                           bool use_backface_culling,
+                           Object *ob,
+                           float obmat[4][4],
+                           void *data)
 {
   struct RaycastObjUserData *dt = data;
 
@@ -845,6 +928,7 @@ static void raycast_obj_cb(
                         dt->ob_index++,
                         use_obedit,
                         dt->use_occlusion_test,
+                        use_backface_culling,
                         dt->ray_depth,
                         dt->r_loc,
                         dt->r_no,
@@ -1088,8 +1172,6 @@ typedef void (*Nearest2DGetTriEdgesCallback)(const int index, int e_index[3], vo
 typedef void (*Nearest2DCopyVertNoCallback)(const int index, float r_no[3], void *data);
 
 typedef struct Nearest2dUserData {
-  bool is_persp;
-
   void *userdata;
   Nearest2DGetVertCoCallback get_vert_co;
   Nearest2DGetEdgeVertsCallback get_edge_verts_index;
@@ -1097,6 +1179,8 @@ typedef struct Nearest2dUserData {
   Nearest2DGetTriEdgesCallback get_tri_edges_index;
   Nearest2DCopyVertNoCallback copy_vert_no;
 
+  bool is_persp;
+  bool use_backface_culling;
 } Nearest2dUserData;
 
 static void cb_snap_vert(void *userdata,
@@ -1181,6 +1265,20 @@ static void cb_snap_tri_edges(void *userdata,
 {
   struct Nearest2dUserData *data = userdata;
 
+  if (data->use_backface_culling) {
+    int vindex[3];
+    data->get_tri_verts_index(index, vindex, data->userdata);
+
+    const float *t0, *t1, *t2;
+    data->get_vert_co(vindex[0], &t0, data->userdata);
+    data->get_vert_co(vindex[1], &t1, data->userdata);
+    data->get_vert_co(vindex[2], &t2, data->userdata);
+    float dummy[3];
+    if (raycast_tri_backface_culling_test(precalc->ray_direction, t0, t1, t2, dummy)) {
+      return;
+    }
+  }
+
   int eindex[3];
   data->get_tri_edges_index(index, eindex, data->userdata);
   for (int i = 3; i--;) {
@@ -1204,6 +1302,18 @@ static void cb_snap_tri_verts(void *userdata,
 
   int vindex[3];
   data->get_tri_verts_index(index, vindex, data->userdata);
+
+  if (data->use_backface_culling) {
+    const float *t0, *t1, *t2;
+    data->get_vert_co(vindex[0], &t0, data->userdata);
+    data->get_vert_co(vindex[1], &t1, data->userdata);
+    data->get_vert_co(vindex[2], &t2, data->userdata);
+    float dummy[3];
+    if (raycast_tri_backface_culling_test(precalc->ray_direction, t0, t1, t2, dummy)) {
+      return;
+    }
+  }
+
   for (int i = 3; i--;) {
     if (vindex[i] == nearest->index) {
       continue;
@@ -1222,6 +1332,7 @@ static short snap_mesh_polygon(SnapObjectContext *sctx,
                                SnapData *snapdata,
                                Object *ob,
                                const float obmat[4][4],
+                               bool use_backface_culling,
                                /* read/write args */
                                float *dist_px,
                                /* return args */
@@ -1246,6 +1357,7 @@ static short snap_mesh_polygon(SnapObjectContext *sctx,
 
   Nearest2dUserData nearest2d = {
       .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP,
+      .use_backface_culling = use_backface_culling,
   };
 
   BVHTreeNearest nearest = {
@@ -1366,6 +1478,7 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx,
                                         const float obmat[4][4],
                                         float original_dist_px,
                                         const float prev_co[3],
+                                        bool use_backface_culling,
                                         /* read/write args */
                                         float *dist_px,
                                         /* return args */
@@ -1392,6 +1505,7 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx,
   Nearest2dUserData nearest2d;
   {
     nearest2d.is_persp = snapdata->view_proj == VIEW_PROJ_PERSP;
+    nearest2d.use_backface_culling = use_backface_culling;
     if (sod->type == SNAP_MESH) {
       nearest2d.userdata = &((SnapObjectData_Mesh *)sod)->treedata;
       nearest2d.get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get;
@@ -1995,6 +2109,7 @@ static short snapMesh(SnapObjectContext *sctx,
                       Object *ob,
                       Mesh *me,
                       const float obmat[4][4],
+                      bool use_backface_culling,
                       /* read/write args */
                       float *dist_px,
                       /* return args */
@@ -2107,13 +2222,14 @@ static short snapMesh(SnapObjectContext *sctx,
   }
 
   Nearest2dUserData nearest2d = {
-      .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP,
       .userdata = treedata,
       .get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get,
       .get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_medge_verts_get,
       .get_tri_verts_index = (Nearest2DGetTriVertsCallback)cb_mlooptri_verts_get,
       .get_tri_edges_index = (Nearest2DGetTriEdgesCallback)cb_mlooptri_edges_get,
       .copy_vert_no = (Nearest2DCopyVertNoCallback)cb_mvert_no_copy,
+      .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP,
+      .use_backface_culling = use_backface_culling,
   };
 
   BVHTreeNearest nearest = {
@@ -2233,6 +2349,7 @@ static short snapEditMesh(SnapObjectContext *sctx,
                           Object *ob,
                           BMEditMesh *em,
                           const float obmat[4][4],
+                          bool use_backface_culling,
                           /* read/write args */
                           float *dist_px,
                           /* return args */
@@ -2346,11 +2463,12 @@ static short snapEditMesh(SnapObjectContext *sctx,
   }
 
   Nearest2dUserData nearest2d = {
-      .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP,
       .userdata = em,
       .get_vert_co = (Nearest2DGetVertCoCallback)cb_bvert_co_get,
       .get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_bedge_verts_get,
       .copy_vert_no = (Nearest2DCopyVertNoCallback)cb_bvert_no_copy,
+      .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP,
+      .use_backface_culling = use_backface_culling,
   };
 
   BVHTreeNearest nearest = {
@@ -2436,6 +2554,7 @@ static short snapObject(SnapObjectContext *sctx,
                         Object *ob,
                         float obmat[4][4],
                         bool use_obedit,
+                        bool use_backface_culling,
                         /* read/write args */
                         float *dist_px,
                         /* return args */
@@ -2453,7 +2572,8 @@ static short snapObject(SnapObjectContext *sctx,
       if (BKE_object_is_in_editmode(ob)) {
         BMEditMesh *em = BKE_editmesh_from_object(ob);
         if (use_obedit) {
-          retval = snapEditMesh(sctx, snapdata, ob, em, obmat, dist_px, r_loc, r_no, r_index);
+          retval = snapEditMesh(
+              sctx, snapdata, ob, em, obmat, use_backface_culling, dist_px, r_loc, r_no, r_index);
           break;
         }
         else if (em->mesh_eval_final) {
@@ -2465,7 +2585,8 @@ static short snapObject(SnapObjectContext *sctx,
         return 0;
       }
 
-      retval = snapMesh(sctx, snapdata, ob, me, obmat, dist_px, r_loc, r_no, r_index);
+      retval = snapMesh(
+          sctx, snapdata, ob, me, obmat, use_backface_culling, dist_px, r_loc, r_no, r_index);
       break;
     }
     case OB_ARMATURE:
@@ -2477,8 +2598,16 @@ static short snapObject(SnapObjectContext *sctx,
     case OB_SURF:
     case OB_FONT: {
       if (ob->runtime.mesh_eval) {
-        retval |= snapMesh(
-            sctx, snapdata, ob, ob->runtime.mesh_eval, obmat, dist_px, r_loc, r_no, r_index);
+        retval |= snapMesh(sctx,
+                           snapdata,
+                           ob,
+                           ob->runtime.mesh_eval,
+                           obmat,
+                           use_backface_culling,
+                           dist_px,
+                           r_loc,
+                           r_no,
+                           r_index);
       }
       break;
     }
@@ -2519,8 +2648,12 @@ struct SnapObjUserData {
   short ret;
 };
 
-static void sanp_obj_cb(
-    SnapObjectContext *sctx, bool is_obedit, Object *ob, float obmat[4][4], void *data)
+static void sanp_obj_cb(SnapObjectContext *sctx,
+                        bool is_obedit,
+                        bool use_backface_culling,
+                        Object *ob,
+                        float obmat[4][4],
+                        void *data)
 {
   struct SnapObjUserData *dt = data;
 
@@ -2529,6 +2662,7 @@ static void sanp_obj_cb(
                           ob,
                           obmat,
                           is_obedit,
+                          use_backface_culling,
                           /* read/write args */
                           dt->dist_px,
                           /* return args */
@@ -2881,7 +3015,8 @@ static short transform_snap_context_project_view3d_mixed_impl(
       new_clipplane[3] += 0.01f;
 
       /* Try to snap only to the polygon. */
-      elem_test = snap_mesh_polygon(sctx, &snapdata, ob, obmat, &dist_px_tmp, loc, no, &index);
+      elem_test = snap_mesh_polygon(
+          sctx, &snapdata, ob, obmat, params->use_backface_culling, &dist_px_tmp, loc, no, &index);
       if (elem_test) {
         elem = elem_test;
       }
@@ -2904,8 +3039,17 @@ static short transform_snap_context_project_view3d_mixed_impl(
         (snap_to_flag & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE_MIDPOINT |
                          SCE_SNAP_MODE_EDGE_PERPENDICULAR))) {
       snapdata.snap_to_flag = snap_to_flag;
-      elem = snap_mesh_edge_verts_mixed(
-          sctx, &snapdata, ob, obmat, *dist_px, prev_co, &dist_px_tmp, loc, no, &index);
+      elem = snap_mesh_edge_verts_mixed(sctx,
+                                        &snapdata,
+                                        ob,
+                                        obmat,
+                                        *dist_px,
+                                        prev_co,
+                                        params->use_backface_culling,
+                                        &dist_px_tmp,
+                                        loc,
+                                        no,
+                                        &index);
     }
 
     if (elem & snap_to_flag) {
index 3794508d5a2e60f88823e1abd9f415747484e657..2ca9e3b2ef731022d14734e236332efd76196326 100644 (file)
@@ -2035,6 +2035,7 @@ enum {
 #define SCE_SNAP_PROJECT (1 << 3)
 #define SCE_SNAP_NO_SELF (1 << 4)
 #define SCE_SNAP_ABS_GRID (1 << 5)
+#define SCE_SNAP_BACKFACE_CULLING (1 << 6)
 
 /* ToolSettings.snap_target */
 #define SCE_SNAP_TARGET_CLOSEST 0
index f1e20b32ddc77bcb9181be07a51016de07af70db..b9bc7b2bbf526c6afa59a15e5a3e919f4114d97f 100644 (file)
@@ -3035,6 +3035,11 @@ static void rna_def_tool_settings(BlenderRNA *brna)
                            "Project individual elements on the surface of other objects");
   RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
 
+  prop = RNA_def_property(srna, "use_snap_backface_culling", PROP_BOOLEAN, PROP_NONE);
+  RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SCE_SNAP_BACKFACE_CULLING);
+  RNA_def_property_ui_text(prop, "Backface Culling", "Exclude back facing geometry from snapping");
+  RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
+
   prop = RNA_def_property(srna, "use_snap_self", PROP_BOOLEAN, PROP_NONE);
   RNA_def_property_boolean_negative_sdna(prop, NULL, "snap_flag", SCE_SNAP_NO_SELF);
   RNA_def_property_ui_text(prop, "Project onto Self", "Snap onto itself (Edit Mode Only)");