Edit Mesh: Poly build tool improvements
authorPablo Dobarro <pablodp606@gmail.com>
Tue, 27 Aug 2019 14:19:25 +0000 (16:19 +0200)
committerPablo Dobarro <pablodp606@gmail.com>
Tue, 27 Aug 2019 14:24:16 +0000 (16:24 +0200)
This commit changes the functionality of the Poly build tool to make it more suitable for retopology tasks:
  - Click and drag from a boundary edge extrudes a new quad
  - Click and drag on vertices tweaks the position
  - Ctrl + click adds geometry. There is a geometry preview in the gizmo. It also can automatically convert triangles to quads.
  - Shift + click deletes mesh elements (faces or vertices)
  - Updated preselection code. Different mesh elements take priority depending on the selected action.

Reviewed By: campbellbarton

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

release/scripts/presets/keyconfig/keymap_data/blender_default.py
release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
source/blender/editors/include/ED_mesh.h
source/blender/editors/mesh/editmesh_polybuild.c
source/blender/editors/mesh/editmesh_preselect_elem.c
source/blender/editors/mesh/editmesh_select.c
source/blender/editors/mesh/mesh_intern.h
source/blender/editors/mesh/mesh_ops.c
source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c

index be712056d10a5868fec0bc9c1e109af72574a122..7c963784ed420c41e5fcee0b4ef9e706ca65163f 100644 (file)
@@ -5454,11 +5454,11 @@ def km_3d_view_tool_edit_mesh_poly_build(params):
         "3D View Tool: Edit Mesh, Poly Build",
         {"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
         {"items": [
-            ("mesh.polybuild_face_at_cursor_move", {"type": params.tool_mouse, "value": 'PRESS'},
+            ("mesh.polybuild_extrude_at_cursor_move", {"type": params.tool_mouse, "value": 'PRESS'},
              {"properties": [("TRANSFORM_OT_translate", [("release_confirm", True)])]}),
-            ("mesh.polybuild_split_at_cursor_move", {"type": params.tool_mouse, "value": 'PRESS', "ctrl": True},
+            ("mesh.polybuild_face_at_cursor_move", {"type": params.tool_mouse, "value": 'PRESS', "ctrl": True},
              {"properties": [("TRANSFORM_OT_translate", [("release_confirm", True)])]}),
-            ("mesh.polybuild_dissolve_at_cursor", {"type": params.tool_mouse, "value": 'CLICK', "alt": True}, None),
+            ("mesh.polybuild_delete_at_cursor", {"type": params.tool_mouse, "value": 'CLICK', "shift": True}, None),
         ]},
     )
 
index fabf8abaeab6034dd8b3161e800ace5f51f3ad65..0af9b51597d99e9d4f155847c4cc5c60436c3bcc 100644 (file)
@@ -490,12 +490,17 @@ class _defs_edit_mesh:
 
     @ToolDef.from_fn
     def poly_build():
+        def draw_settings(context, layout, tool):
+            props = tool.operator_properties("mesh.polybuild_face_at_cursor_move")
+            props_macro = props.MESH_OT_polybuild_face_at_cursor
+            layout.prop(props_macro, "create_quads")
         return dict(
             idname="builtin.poly_build",
             label="Poly Build",
             icon="ops.mesh.polybuild_hover",
             widget="VIEW3D_GGT_mesh_preselect_elem",
             keymap=(),
+            draw_settings=draw_settings,
         )
 
     @ToolDef.from_fn
index 234f36a587b8a193391a061d3e214d7be4b57c9e..194378d6bb6aa98e553882e957308ac39f78281e 100644 (file)
@@ -194,8 +194,11 @@ bool EDBM_unified_findnearest(struct ViewContext *vc,
 bool EDBM_unified_findnearest_from_raycast(struct ViewContext *vc,
                                            struct Base **bases,
                                            const uint bases_len,
-                                           bool use_boundary,
-                                           int *r_base_index,
+                                           bool use_boundary_vertices,
+                                           bool use_boundary_edges,
+                                           int *r_base_index_vert,
+                                           int *r_base_index_edge,
+                                           int *r_base_index_face,
                                            struct BMVert **r_eve,
                                            struct BMEdge **r_eed,
                                            struct BMFace **r_efa);
@@ -245,15 +248,30 @@ void EDBM_preselect_edgering_update_from_edge(struct EditMesh_PreSelEdgeRing *ps
 
 /* editmesh_preselect_elem.c */
 struct EditMesh_PreSelElem;
+typedef enum eEditMesh_PreSelPreviewAction {
+  PRESELECT_ACTION_TRANSFORM = 1,
+  PRESELECT_ACTION_CREATE = 2,
+  PRESELECT_ACTION_DELETE = 3,
+} eEditMesh_PreSelPreviewAction;
+
 struct EditMesh_PreSelElem *EDBM_preselect_elem_create(void);
 void EDBM_preselect_elem_destroy(struct EditMesh_PreSelElem *psel);
 void EDBM_preselect_elem_clear(struct EditMesh_PreSelElem *psel);
+void EDBM_preselect_preview_clear(struct EditMesh_PreSelElem *psel);
 void EDBM_preselect_elem_draw(struct EditMesh_PreSelElem *psel, const float matrix[4][4]);
 void EDBM_preselect_elem_update_from_single(struct EditMesh_PreSelElem *psel,
                                             struct BMesh *bm,
                                             struct BMElem *ele,
                                             const float (*coords)[3]);
 
+void EDBM_preselect_elem_update_preview(struct EditMesh_PreSelElem *psel,
+                                        struct ViewContext *vc,
+                                        struct BMesh *bm,
+                                        struct BMElem *ele,
+                                        const int mval[2]);
+void EDBM_preselect_action_set(struct EditMesh_PreSelElem *psel,
+                               eEditMesh_PreSelPreviewAction action);
+eEditMesh_PreSelPreviewAction EDBM_preselect_action_get(struct EditMesh_PreSelElem *psel);
 /* mesh_ops.c */
 void ED_operatortypes_mesh(void);
 void ED_operatormacros_mesh(void);
index 088d1672cc98b13a042f084f60ba98e7907166f3..a182bfeb9450ae3877d6c8b190ad5d3c478aedc5 100644 (file)
@@ -122,15 +122,151 @@ static bool edbm_preselect_or_active_init_viewcontext(bContext *C,
   return ok;
 }
 
+static int edbm_polybuild_transform_at_cursor_invoke(bContext *C,
+                                                     wmOperator *UNUSED(op),
+                                                     const wmEvent *UNUSED(event))
+{
+  ViewContext vc;
+  Base *basact = NULL;
+  BMElem *ele_act = NULL;
+  edbm_preselect_or_active_init_viewcontext(C, &vc, &basact, &ele_act);
+  BMEditMesh *em = vc.em;
+  BMesh *bm = em->bm;
+
+  invert_m4_m4(vc.obedit->imat, vc.obedit->obmat);
+  ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d);
+
+  edbm_selectmode_ensure(vc.scene, vc.em, SCE_SELECT_VERTEX);
+
+  edbm_flag_disable_all_multi(vc.view_layer, vc.v3d, BM_ELEM_SELECT);
+  if (ele_act->head.htype == BM_VERT) {
+    BM_vert_select_set(bm, (BMVert *)ele_act, true);
+  }
+  if (ele_act->head.htype == BM_EDGE) {
+    BM_edge_select_set(bm, (BMEdge *)ele_act, true);
+  }
+  if (ele_act->head.htype == BM_FACE) {
+    BM_face_select_set(bm, (BMFace *)ele_act, true);
+  }
+
+  EDBM_mesh_normals_update(em);
+  EDBM_update_generic(em, true, true);
+  if (basact != NULL) {
+    if (vc.view_layer->basact != basact) {
+      ED_object_base_activate(C, basact);
+    }
+  }
+  BM_select_history_store(bm, ele_act);
+  WM_event_add_mousemove(C);
+  return OPERATOR_FINISHED;
+}
+
+void MESH_OT_polybuild_transform_at_cursor(wmOperatorType *ot)
+{
+  /* identifiers */
+  ot->name = "Poly Build Transform at Cursor";
+  ot->idname = "MESH_OT_polybuild_transform_at_cursor";
+
+  /* api callbacks */
+  ot->invoke = edbm_polybuild_transform_at_cursor_invoke;
+  ot->poll = EDBM_view3d_poll;
+
+  /* flags */
+  ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+  /* to give to transform */
+  Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR_DUMMY);
+}
+
+static int edbm_polybuild_delete_at_cursor_invoke(bContext *C,
+                                                  wmOperator *op,
+                                                  const wmEvent *UNUSED(event))
+{
+  bool changed = false;
+
+  ViewContext vc;
+  Base *basact = NULL;
+  BMElem *ele_act = NULL;
+  edbm_preselect_or_active_init_viewcontext(C, &vc, &basact, &ele_act);
+  BMEditMesh *em = vc.em;
+  BMesh *bm = em->bm;
+
+  invert_m4_m4(vc.obedit->imat, vc.obedit->obmat);
+  ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d);
+
+  edbm_selectmode_ensure(vc.scene, vc.em, SCE_SELECT_VERTEX);
+
+  if (ele_act->head.htype == BM_FACE) {
+    BMFace *f_act = (BMFace *)ele_act;
+    EDBM_flag_disable_all(em, BM_ELEM_TAG);
+    BM_elem_flag_enable(f_act, BM_ELEM_TAG);
+    if (!EDBM_op_callf(em, op, "delete geom=%hf context=%i", BM_ELEM_TAG, DEL_FACES)) {
+      return OPERATOR_CANCELLED;
+    }
+    changed = true;
+  }
+  if (ele_act->head.htype == BM_VERT) {
+    BMVert *v_act = (BMVert *)ele_act;
+    if (BM_vert_is_edge_pair(v_act)) {
+      BM_edge_collapse(bm, v_act->e, v_act, true, true);
+      changed = true;
+    }
+    else {
+      EDBM_flag_disable_all(em, BM_ELEM_TAG);
+      BM_elem_flag_enable(v_act, BM_ELEM_TAG);
+
+      if (!EDBM_op_callf(em,
+                         op,
+                         "dissolve_verts verts=%hv use_face_split=%b use_boundary_tear=%b",
+                         BM_ELEM_TAG,
+                         false,
+                         false)) {
+        return OPERATOR_CANCELLED;
+      }
+      changed = true;
+    }
+  }
+
+  if (changed) {
+    EDBM_mesh_normals_update(em);
+    EDBM_update_generic(em, true, true);
+    if (basact != NULL) {
+      if (vc.view_layer->basact != basact) {
+        ED_object_base_activate(C, basact);
+      }
+    }
+    WM_event_add_mousemove(C);
+    return OPERATOR_FINISHED;
+  }
+  else {
+    return OPERATOR_CANCELLED;
+  }
+}
+
+void MESH_OT_polybuild_delete_at_cursor(wmOperatorType *ot)
+{
+  /* identifiers */
+  ot->name = "Poly Build Delete at Cursor";
+  ot->idname = "MESH_OT_polybuild_delete_at_cursor";
+
+  /* api callbacks */
+  ot->invoke = edbm_polybuild_delete_at_cursor_invoke;
+  ot->poll = EDBM_view3d_poll;
+
+  /* flags */
+  ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+  /* to give to transform */
+  Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR_DUMMY);
+}
+
 /** \} */
 
 /* -------------------------------------------------------------------- */
 /** \name Face at Cursor
  * \{ */
 
-static int edbm_polybuild_face_at_cursor_invoke(bContext *C,
-                                                wmOperator *UNUSED(op),
-                                                const wmEvent *event)
+static int edbm_polybuild_face_at_cursor_invoke(bContext *C, wmOperator *op, const wmEvent *event)
 {
   float center[3];
   bool changed = false;
@@ -168,20 +304,27 @@ static int edbm_polybuild_face_at_cursor_invoke(bContext *C,
     mul_m4_v3(vc.obedit->obmat, center);
     ED_view3d_win_to_3d_int(vc.v3d, vc.ar, center, event->mval, center);
     mul_m4_v3(vc.obedit->imat, center);
-
-    BMVert *v_tri[3];
-    v_tri[0] = e_act->v1;
-    v_tri[1] = e_act->v2;
-    v_tri[2] = BM_vert_create(bm, center, NULL, BM_CREATE_NOP);
-    if (e_act->l && e_act->l->v == v_tri[0]) {
-      SWAP(BMVert *, v_tri[0], v_tri[1]);
+    if (f_reference->len == 3 && RNA_boolean_get(op->ptr, "create_quads")) {
+      const float fac = line_point_factor_v3(center, e_act->v1->co, e_act->v2->co);
+      BMVert *v_new = BM_edge_split(bm, e_act, e_act->v1, NULL, CLAMPIS(fac, 0.0f, 1.0f));
+      copy_v3_v3(v_new->co, center);
+      edbm_flag_disable_all_multi(vc.view_layer, vc.v3d, BM_ELEM_SELECT);
+      BM_vert_select_set(bm, v_new, true);
+      BM_select_history_store(bm, v_new);
+    }
+    else {
+      BMVert *v_tri[3];
+      v_tri[0] = e_act->v1;
+      v_tri[1] = e_act->v2;
+      v_tri[2] = BM_vert_create(bm, center, NULL, BM_CREATE_NOP);
+      if (e_act->l && e_act->l->v == v_tri[0]) {
+        SWAP(BMVert *, v_tri[0], v_tri[1]);
+      }
+      BM_face_create_verts(bm, v_tri, 3, f_reference, BM_CREATE_NOP, true);
+      edbm_flag_disable_all_multi(vc.view_layer, vc.v3d, BM_ELEM_SELECT);
+      BM_vert_select_set(bm, v_tri[2], true);
+      BM_select_history_store(bm, v_tri[2]);
     }
-    // BMFace *f_new =
-    BM_face_create_verts(bm, v_tri, 3, f_reference, BM_CREATE_NOP, true);
-
-    edbm_flag_disable_all_multi(vc.view_layer, vc.v3d, BM_ELEM_SELECT);
-    BM_vert_select_set(bm, v_tri[2], true);
-    BM_select_history_store(bm, v_tri[2]);
     changed = true;
   }
   else if (ele_act->head.htype == BM_VERT) {
@@ -281,6 +424,11 @@ void MESH_OT_polybuild_face_at_cursor(wmOperatorType *ot)
   /* flags */
   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 
+  RNA_def_boolean(ot->srna,
+                  "create_quads",
+                  true,
+                  "Create quads",
+                  "Automatically split edges in triangles to maintain quad topology");
   /* to give to transform */
   Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR_DUMMY);
 }
index a3e684a5493c2e36cf2fe8c26662f050c327b2b7..05c4da68355a76327c43b1ecdc2a4c303c0a29af 100644 (file)
@@ -75,20 +75,49 @@ struct EditMesh_PreSelElem {
 
   float (*verts)[3];
   int verts_len;
+
+  float (*preview_tris)[3][3];
+  int preview_tris_len;
+  float (*preview_lines)[2][3];
+  int preview_lines_len;
+
+  eEditMesh_PreSelPreviewAction preview_action;
 };
 
+void EDBM_preselect_action_set(struct EditMesh_PreSelElem *psel,
+                               eEditMesh_PreSelPreviewAction action)
+{
+  psel->preview_action = action;
+}
+
+eEditMesh_PreSelPreviewAction EDBM_preselect_action_get(struct EditMesh_PreSelElem *psel)
+{
+  return psel->preview_action;
+}
+
 struct EditMesh_PreSelElem *EDBM_preselect_elem_create(void)
 {
   struct EditMesh_PreSelElem *psel = MEM_callocN(sizeof(*psel), __func__);
+  psel->preview_action = PRESELECT_ACTION_TRANSFORM;
   return psel;
 }
 
 void EDBM_preselect_elem_destroy(struct EditMesh_PreSelElem *psel)
 {
   EDBM_preselect_elem_clear(psel);
+  EDBM_preselect_preview_clear(psel);
   MEM_freeN(psel);
 }
 
+void EDBM_preselect_preview_clear(struct EditMesh_PreSelElem *psel)
+{
+  MEM_SAFE_FREE(psel->preview_tris);
+  psel->preview_tris_len = 0;
+
+  MEM_SAFE_FREE(psel->preview_lines);
+  psel->preview_lines_len = 0;
+}
+
 void EDBM_preselect_elem_clear(struct EditMesh_PreSelElem *psel)
 {
   MEM_SAFE_FREE(psel->edges);
@@ -112,9 +141,42 @@ void EDBM_preselect_elem_draw(struct EditMesh_PreSelElem *psel, const float matr
   uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
 
   immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
-  immUniformColor3ub(255, 0, 255);
+
+  immUniformColor4ub(141, 171, 186, 100);
+  if (psel->preview_action != PRESELECT_ACTION_TRANSFORM) {
+    if (psel->preview_tris_len > 0) {
+      immBegin(GPU_PRIM_TRIS, psel->preview_tris_len * 3);
+
+      for (int i = 0; i < psel->preview_tris_len; i++) {
+        immVertex3fv(pos, psel->preview_tris[i][0]);
+        immVertex3fv(pos, psel->preview_tris[i][1]);
+        immVertex3fv(pos, psel->preview_tris[i][2]);
+      }
+      immEnd();
+    }
+
+    if (psel->preview_lines_len > 0) {
+
+      immUniformColor4ub(3, 161, 252, 200);
+      GPU_line_width(2.0f);
+      immBegin(GPU_PRIM_LINES, psel->preview_lines_len * 2);
+      for (int i = 0; i < psel->preview_lines_len; i++) {
+        immVertex3fv(pos, psel->preview_lines[i][0]);
+        immVertex3fv(pos, psel->preview_lines[i][1]);
+      }
+      immEnd();
+    }
+  }
+
+  if (psel->preview_action == PRESELECT_ACTION_DELETE) {
+    immUniformColor4ub(252, 49, 10, 200);
+  }
+  else {
+    immUniformColor4ub(3, 161, 252, 200);
+  }
 
   if (psel->edges_len > 0) {
+    GPU_line_width(3.0f);
     immBegin(GPU_PRIM_LINES, psel->edges_len * 2);
 
     for (int i = 0; i < psel->edges_len; i++) {
@@ -126,7 +188,7 @@ void EDBM_preselect_elem_draw(struct EditMesh_PreSelElem *psel, const float matr
   }
 
   if (psel->verts_len > 0) {
-    GPU_point_size(3.0f);
+    GPU_point_size(4.0f);
 
     immBegin(GPU_PRIM_POINTS, psel->verts_len);
 
@@ -167,6 +229,122 @@ static void view3d_preselect_mesh_elem_update_from_edge(struct EditMesh_PreSelEl
   psel->edges_len = 1;
 }
 
+static void view3d_preselect_update_preview_triangle_from_vert(struct EditMesh_PreSelElem *psel,
+                                                               ViewContext *vc,
+                                                               BMesh *UNUSED(bm),
+                                                               BMVert *eed,
+                                                               const int mval[2])
+{
+  BMVert *v_act = eed;
+  BMEdge *e_pair[2] = {NULL};
+  float center[3];
+
+  if (v_act->e != NULL) {
+    for (uint allow_wire = 0; allow_wire < 2 && (e_pair[1] == NULL); allow_wire++) {
+      int i = 0;
+      BMEdge *e_iter = v_act->e;
+      do {
+        if ((BM_elem_flag_test(e_iter, BM_ELEM_HIDDEN) == false) &&
+            (allow_wire ? BM_edge_is_wire(e_iter) : BM_edge_is_boundary(e_iter))) {
+          if (i == 2) {
+            e_pair[0] = e_pair[1] = NULL;
+            break;
+          }
+          e_pair[i++] = e_iter;
+        }
+      } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v_act)) != v_act->e);
+    }
+  }
+
+  if (e_pair[1] != NULL) {
+    mul_v3_m4v3(center, vc->obedit->obmat, v_act->co);
+    ED_view3d_win_to_3d_int(vc->v3d, vc->ar, center, mval, center);
+    mul_m4_v3(vc->obedit->imat, center);
+
+    psel->preview_tris = MEM_mallocN(sizeof(*psel->preview_tris) * 2, __func__);
+    psel->preview_lines = MEM_mallocN(sizeof(*psel->preview_lines) * 4, __func__);
+
+    copy_v3_v3(psel->preview_tris[0][0], e_pair[0]->v1->co);
+    copy_v3_v3(psel->preview_tris[0][1], e_pair[0]->v2->co);
+    copy_v3_v3(psel->preview_tris[0][2], center);
+
+    copy_v3_v3(psel->preview_tris[1][0], e_pair[1]->v1->co);
+    copy_v3_v3(psel->preview_tris[1][1], e_pair[1]->v2->co);
+    copy_v3_v3(psel->preview_tris[1][2], center);
+
+    copy_v3_v3(psel->preview_lines[0][0], e_pair[0]->v1->co);
+    copy_v3_v3(psel->preview_lines[0][1], e_pair[0]->v2->co);
+
+    copy_v3_v3(psel->preview_lines[1][0], e_pair[1]->v1->co);
+    copy_v3_v3(psel->preview_lines[1][1], e_pair[1]->v2->co);
+
+    copy_v3_v3(psel->preview_lines[2][0], center);
+    if (e_pair[0]->v1 == v_act) {
+      copy_v3_v3(psel->preview_lines[2][1], e_pair[0]->v2->co);
+    }
+    else {
+      copy_v3_v3(psel->preview_lines[2][1], e_pair[0]->v1->co);
+    }
+
+    copy_v3_v3(psel->preview_lines[3][0], center);
+    if (e_pair[1]->v1 == v_act) {
+      copy_v3_v3(psel->preview_lines[3][1], e_pair[1]->v2->co);
+    }
+    else {
+      copy_v3_v3(psel->preview_lines[3][1], e_pair[1]->v1->co);
+    }
+    psel->preview_tris_len = 2;
+    psel->preview_lines_len = 4;
+  }
+}
+
+static void view3d_preselect_update_preview_triangle_from_face(struct EditMesh_PreSelElem *psel,
+                                                               ViewContext *UNUSED(vc),
+                                                               BMesh *UNUSED(bm),
+                                                               BMFace *efa,
+                                                               const int UNUSED(mval[2]))
+{
+  float(*preview_lines)[2][3] = MEM_mallocN(sizeof(*psel->edges) * efa->len, __func__);
+  BMLoop *l_iter, *l_first;
+  l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
+  int i = 0;
+  do {
+    vcos_get_pair(&l_iter->e->v1, preview_lines[i++], NULL);
+  } while ((l_iter = l_iter->next) != l_first);
+  psel->preview_lines = preview_lines;
+  psel->preview_lines_len = efa->len;
+}
+
+static void view3d_preselect_update_preview_triangle_from_edge(struct EditMesh_PreSelElem *psel,
+                                                               ViewContext *vc,
+                                                               BMesh *UNUSED(bm),
+                                                               BMEdge *eed,
+                                                               const int mval[2])
+{
+  float center[3];
+  psel->preview_tris = MEM_mallocN(sizeof(*psel->preview_tris), __func__);
+  psel->preview_lines = MEM_mallocN(sizeof(*psel->preview_lines) * 3, __func__);
+  mid_v3_v3v3(center, eed->v1->co, eed->v2->co);
+  mul_m4_v3(vc->obedit->obmat, center);
+  ED_view3d_win_to_3d_int(vc->v3d, vc->ar, center, mval, center);
+  mul_m4_v3(vc->obedit->imat, center);
+
+  copy_v3_v3(psel->preview_tris[0][0], eed->v1->co);
+  copy_v3_v3(psel->preview_tris[0][1], eed->v2->co);
+  copy_v3_v3(psel->preview_tris[0][2], center);
+
+  copy_v3_v3(psel->preview_lines[0][0], eed->v1->co);
+  copy_v3_v3(psel->preview_lines[0][1], eed->v2->co);
+
+  copy_v3_v3(psel->preview_lines[1][0], eed->v2->co);
+  copy_v3_v3(psel->preview_lines[1][1], center);
+
+  copy_v3_v3(psel->preview_lines[2][0], center);
+  copy_v3_v3(psel->preview_lines[2][1], eed->v1->co);
+  psel->preview_tris_len = 1;
+  psel->preview_lines_len = 3;
+}
+
 static void view3d_preselect_mesh_elem_update_from_face(struct EditMesh_PreSelElem *psel,
                                                         BMesh *UNUSED(bm),
                                                         BMFace *efa,
@@ -209,4 +387,28 @@ void EDBM_preselect_elem_update_from_single(struct EditMesh_PreSelElem *psel,
   }
 }
 
+void EDBM_preselect_elem_update_preview(struct EditMesh_PreSelElem *psel,
+                                        struct ViewContext *vc,
+                                        struct BMesh *bm,
+                                        struct BMElem *ele,
+                                        const int mval[2])
+{
+  EDBM_preselect_preview_clear(psel);
+
+  switch (ele->head.htype) {
+    case BM_VERT:
+      if (EDBM_preselect_action_get(psel) == PRESELECT_ACTION_CREATE) {
+        view3d_preselect_update_preview_triangle_from_vert(psel, vc, bm, (BMVert *)ele, mval);
+      }
+      break;
+    case BM_EDGE:
+      view3d_preselect_update_preview_triangle_from_edge(psel, vc, bm, (BMEdge *)ele, mval);
+      break;
+    case BM_FACE:
+      view3d_preselect_update_preview_triangle_from_face(psel, vc, bm, (BMFace *)ele, mval);
+      break;
+    default:
+      BLI_assert(0);
+  }
+}
 /** \} */
index f1055103d16a25473b976e7f129e66f6ba2b5fd9..4d511d456423c59af89ea2314bd167af355db091 100644 (file)
@@ -1448,8 +1448,11 @@ bool EDBM_unified_findnearest(ViewContext *vc,
 bool EDBM_unified_findnearest_from_raycast(ViewContext *vc,
                                            Base **bases,
                                            const uint bases_len,
-                                           bool use_boundary,
-                                           int *r_base_index,
+                                           bool use_boundary_vertices,
+                                           bool use_boundary_edges,
+                                           int *r_base_index_vert,
+                                           int *r_base_index_edge,
+                                           int *r_base_index_face,
                                            struct BMVert **r_eve,
                                            struct BMEdge **r_eed,
                                            struct BMFace **r_efa)
@@ -1463,9 +1466,27 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc,
     BMElem *ele;
   } best = {0, NULL};
 
+  struct {
+    uint base_index;
+    BMElem *ele;
+  } best_vert = {0, NULL};
+
+  struct {
+    uint base_index;
+    BMElem *ele;
+  } best_edge = {0, NULL};
+
+  struct {
+    uint base_index;
+    BMElem *ele;
+  } best_face = {0, NULL};
+
   if (ED_view3d_win_to_ray_clipped(
           vc->depsgraph, vc->ar, vc->v3d, mval_fl, ray_origin, ray_direction, true)) {
     float dist_sq_best = FLT_MAX;
+    float dist_sq_best_vert = FLT_MAX;
+    float dist_sq_best_edge = FLT_MAX;
+    float dist_sq_best_face = FLT_MAX;
 
     const bool use_vert = (r_eve != NULL);
     const bool use_edge = (r_eed != NULL);
@@ -1495,18 +1516,23 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc,
         BM_mesh_elem_index_ensure(bm, BM_VERT);
       }
 
-      if (use_boundary && (use_vert || use_edge)) {
+      if ((use_boundary_vertices || use_boundary_edges) && (use_vert || use_edge)) {
         BMEdge *e;
         BMIter eiter;
         BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
           if ((BM_elem_flag_test(e, BM_ELEM_HIDDEN) == false) && (BM_edge_is_boundary(e))) {
-            if (use_vert) {
+            if (use_vert && use_boundary_vertices) {
               for (uint j = 0; j < 2; j++) {
                 BMVert *v = *((&e->v1) + j);
                 float point[3];
                 mul_v3_m4v3(point, obedit->obmat, coords ? coords[BM_elem_index_get(v)] : v->co);
                 const float dist_sq_test = dist_squared_to_ray_v3_normalized(
                     ray_origin, ray_direction, point);
+                if (dist_sq_test < dist_sq_best_vert) {
+                  dist_sq_best_vert = dist_sq_test;
+                  best_vert.base_index = base_index;
+                  best_vert.ele = (BMElem *)v;
+                }
                 if (dist_sq_test < dist_sq_best) {
                   dist_sq_best = dist_sq_test;
                   best.base_index = base_index;
@@ -1515,7 +1541,7 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc,
               }
             }
 
-            if (use_edge) {
+            if (use_edge && use_boundary_edges) {
               float point[3];
 #if 0
               const float dist_sq_test = dist_squared_ray_to_seg_v3(
@@ -1531,6 +1557,11 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc,
               mul_m4_v3(obedit->obmat, point);
               const float dist_sq_test = dist_squared_to_ray_v3_normalized(
                   ray_origin, ray_direction, point);
+              if (dist_sq_test < dist_sq_best_edge) {
+                dist_sq_best_edge = dist_sq_test;
+                best_edge.base_index = base_index;
+                best_edge.ele = (BMElem *)e;
+              }
               if (dist_sq_test < dist_sq_best) {
                 dist_sq_best = dist_sq_test;
                 best.base_index = base_index;
@@ -1541,46 +1572,55 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc,
           }
         }
       }
-      else {
-        /* Non boundary case. */
-        if (use_vert) {
-          BMVert *v;
-          BMIter viter;
-          BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
-            if (BM_elem_flag_test(v, BM_ELEM_HIDDEN) == false) {
-              float point[3];
-              mul_v3_m4v3(point, obedit->obmat, v->co);
-              const float dist_sq_test = dist_squared_to_ray_v3_normalized(
-                  ray_origin, ray_direction, v->co);
-              if (dist_sq_test < dist_sq_best) {
-                dist_sq_best = dist_sq_test;
-                best.base_index = base_index;
-                best.ele = (BMElem *)v;
-              }
+      /* Non boundary case. */
+      if (use_vert && !use_boundary_vertices) {
+        BMVert *v;
+        BMIter viter;
+        BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
+          if (BM_elem_flag_test(v, BM_ELEM_HIDDEN) == false) {
+            float point[3];
+            mul_v3_m4v3(point, obedit->obmat, coords ? coords[BM_elem_index_get(v)] : v->co);
+            const float dist_sq_test = dist_squared_to_ray_v3_normalized(
+                ray_origin, ray_direction, point);
+            if (dist_sq_test < dist_sq_best_vert) {
+              dist_sq_best_vert = dist_sq_test;
+              best_vert.base_index = base_index;
+              best_vert.ele = (BMElem *)v;
+            }
+            if (dist_sq_test < dist_sq_best) {
+              dist_sq_best = dist_sq_test;
+              best.base_index = base_index;
+              best.ele = (BMElem *)v;
             }
           }
         }
-        if (use_edge) {
-          BMEdge *e;
-          BMIter eiter;
-          BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
-            if (BM_elem_flag_test(e, BM_ELEM_HIDDEN) == false) {
-              float point[3];
-              if (coords) {
-                mid_v3_v3v3(
-                    point, coords[BM_elem_index_get(e->v1)], coords[BM_elem_index_get(e->v2)]);
-              }
-              else {
-                mid_v3_v3v3(point, e->v1->co, e->v2->co);
-              }
-              mul_m4_v3(obedit->obmat, point);
-              const float dist_sq_test = dist_squared_to_ray_v3_normalized(
-                  ray_origin, ray_direction, point);
-              if (dist_sq_test < dist_sq_best) {
-                dist_sq_best = dist_sq_test;
-                best.base_index = base_index;
-                best.ele = (BMElem *)e;
-              }
+      }
+
+      if (use_edge && !use_boundary_edges) {
+        BMEdge *e;
+        BMIter eiter;
+        BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
+          if (BM_elem_flag_test(e, BM_ELEM_HIDDEN) == false) {
+            float point[3];
+            if (coords) {
+              mid_v3_v3v3(
+                  point, coords[BM_elem_index_get(e->v1)], coords[BM_elem_index_get(e->v2)]);
+            }
+            else {
+              mid_v3_v3v3(point, e->v1->co, e->v2->co);
+            }
+            mul_m4_v3(obedit->obmat, point);
+            const float dist_sq_test = dist_squared_to_ray_v3_normalized(
+                ray_origin, ray_direction, point);
+            if (dist_sq_test < dist_sq_best_edge) {
+              dist_sq_best_edge = dist_sq_test;
+              best_edge.base_index = base_index;
+              best_edge.ele = (BMElem *)e;
+            }
+            if (dist_sq_test < dist_sq_best) {
+              dist_sq_best = dist_sq_test;
+              best.base_index = base_index;
+              best.ele = (BMElem *)e;
             }
           }
         }
@@ -1601,6 +1641,11 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc,
             mul_m4_v3(obedit->obmat, point);
             const float dist_sq_test = dist_squared_to_ray_v3_normalized(
                 ray_origin, ray_direction, point);
+            if (dist_sq_test < dist_sq_best_face) {
+              dist_sq_best_face = dist_sq_test;
+              best_face.base_index = base_index;
+              best_face.ele = (BMElem *)f;
+            }
             if (dist_sq_test < dist_sq_best) {
               dist_sq_best = dist_sq_test;
               best.base_index = base_index;
@@ -1612,7 +1657,10 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc,
     }
   }
 
-  *r_base_index = best.base_index;
+  *r_base_index_vert = best_vert.base_index;
+  *r_base_index_edge = best_edge.base_index;
+  *r_base_index_face = best_face.base_index;
+
   if (r_eve) {
     *r_eve = NULL;
   }
@@ -1623,22 +1671,17 @@ bool EDBM_unified_findnearest_from_raycast(ViewContext *vc,
     *r_efa = NULL;
   }
 
-  if (best.ele) {
-    switch (best.ele->head.htype) {
-      case BM_VERT:
-        *r_eve = (BMVert *)best.ele;
-        break;
-      case BM_EDGE:
-        *r_eed = (BMEdge *)best.ele;
-        break;
-      case BM_FACE:
-        *r_efa = (BMFace *)best.ele;
-        break;
-      default:
-        BLI_assert(0);
-    }
+  if (best_vert.ele) {
+    *r_eve = (BMVert *)best_vert.ele;
+  }
+  if (best_edge.ele) {
+    *r_eed = (BMEdge *)best_edge.ele;
   }
-  return (best.ele != NULL);
+  if (best_face.ele) {
+    *r_efa = (BMFace *)best_face.ele;
+  }
+
+  return (best_vert.ele != NULL || best_edge.ele != NULL || best_face.ele != NULL);
 }
 
 /** \} */
index 1a03879ed176f8069fd8bad76424e4c012d16876..8332cb71f95296165e953cf0a68849a5f6027f33 100644 (file)
@@ -122,6 +122,8 @@ void MESH_GGT_spin_redo(struct wmGizmoGroupType *gzgt);
 void MESH_OT_polybuild_face_at_cursor(struct wmOperatorType *ot);
 void MESH_OT_polybuild_split_at_cursor(struct wmOperatorType *ot);
 void MESH_OT_polybuild_dissolve_at_cursor(struct wmOperatorType *ot);
+void MESH_OT_polybuild_transform_at_cursor(struct wmOperatorType *ot);
+void MESH_OT_polybuild_delete_at_cursor(struct wmOperatorType *ot);
 
 /* *** editmesh_inset.c *** */
 void MESH_OT_inset(struct wmOperatorType *ot);
index 102ce3efc220761d9ce9f26d6c4efed79f7c2b5f..28c55afbf2e04629c99c173c8edbb58c17642cfc 100644 (file)
@@ -148,6 +148,8 @@ void ED_operatortypes_mesh(void)
   WM_operatortype_append(MESH_OT_polybuild_face_at_cursor);
   WM_operatortype_append(MESH_OT_polybuild_split_at_cursor);
   WM_operatortype_append(MESH_OT_polybuild_dissolve_at_cursor);
+  WM_operatortype_append(MESH_OT_polybuild_transform_at_cursor);
+  WM_operatortype_append(MESH_OT_polybuild_delete_at_cursor);
 
   WM_operatortype_append(MESH_OT_uv_texture_add);
   WM_operatortype_append(MESH_OT_uv_texture_remove);
@@ -334,6 +336,25 @@ void ED_operatormacros_mesh(void)
   otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
   RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
   RNA_boolean_set(otmacro->ptr, "mirror", false);
+
+  ot = WM_operatortype_append_macro("MESH_OT_polybuild_transform_at_cursor_move",
+                                    "Transform at Cursor Move",
+                                    "",
+                                    OPTYPE_UNDO | OPTYPE_REGISTER);
+  WM_operatortype_macro_define(ot, "MESH_OT_polybuild_transform_at_cursor");
+  otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
+  RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
+  RNA_boolean_set(otmacro->ptr, "mirror", false);
+
+  ot = WM_operatortype_append_macro("MESH_OT_polybuild_extrude_at_cursor_move",
+                                    "Extrude at Cursor Move",
+                                    "",
+                                    OPTYPE_UNDO | OPTYPE_REGISTER);
+  WM_operatortype_macro_define(ot, "MESH_OT_polybuild_transform_at_cursor");
+  otmacro = WM_operatortype_macro_define(ot, "MESH_OT_extrude_edges_indiv");
+  otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
+  RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
+  RNA_boolean_set(otmacro->ptr, "mirror", false);
 }
 
 /* note mesh keymap also for other space? */
index a984e3393058eeceea7eb97ea59703721aad657c..bfc5956bb94c5a6d8bc700a0c0a351ec951a1785 100644 (file)
@@ -77,7 +77,20 @@ static void gizmo_preselect_elem_draw(const bContext *UNUSED(C), wmGizmo *gz)
 
 static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int mval[2])
 {
+  wmEvent *event = CTX_wm_window(C)->eventstate;
   MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz;
+
+  /* Hack: Switch action mode based on key input */
+  const bool is_ctrl_pressed = WM_event_modifier_flag(event) & KM_CTRL;
+  const bool is_shift_pressed = WM_event_modifier_flag(event) & KM_SHIFT;
+  EDBM_preselect_action_set(gz_ele->psel, PRESELECT_ACTION_TRANSFORM);
+  if (is_ctrl_pressed && !is_shift_pressed) {
+    EDBM_preselect_action_set(gz_ele->psel, PRESELECT_ACTION_CREATE);
+  }
+  if (!is_ctrl_pressed && is_shift_pressed) {
+    EDBM_preselect_action_set(gz_ele->psel, PRESELECT_ACTION_DELETE);
+  }
+
   struct {
     Object *ob;
     BMElem *ele;
@@ -87,18 +100,6 @@ static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int
       .dist = ED_view3d_select_dist_px(),
   };
 
-  struct {
-    int base_index;
-    int vert_index;
-    int edge_index;
-    int face_index;
-  } prev = {
-      .base_index = gz_ele->base_index,
-      .vert_index = gz_ele->vert_index,
-      .edge_index = gz_ele->edge_index,
-      .face_index = gz_ele->face_index,
-  };
-
   {
     ViewLayer *view_layer = CTX_data_view_layer(C);
     View3D *v3d = CTX_wm_view3d(C);
@@ -115,32 +116,66 @@ static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int
 
   {
     /* TODO: support faces. */
-    int base_index = -1;
+    int base_index_vert = -1;
+    int base_index_edge = -1;
+    int base_index_face = -1;
     BMVert *eve_test;
     BMEdge *eed_test;
+    BMFace *efa_test;
 
     if (EDBM_unified_findnearest_from_raycast(&vc,
                                               gz_ele->bases,
                                               gz_ele->bases_len,
+                                              false,
                                               true,
-                                              &base_index,
+                                              &base_index_vert,
+                                              &base_index_edge,
+                                              &base_index_face,
                                               &eve_test,
                                               &eed_test,
-                                              NULL)) {
-      Base *base = gz_ele->bases[base_index];
-      best.ob = base->object;
-      if (eve_test) {
-        best.ele = (BMElem *)eve_test;
-      }
-      else if (eed_test) {
-        best.ele = (BMElem *)eed_test;
+                                              &efa_test)) {
+      if (EDBM_preselect_action_get(gz_ele->psel) == PRESELECT_ACTION_DELETE) {
+        /* Delete action */
+        if (efa_test) {
+          best.ele = (BMElem *)efa_test;
+          best.base_index = base_index_face;
+        }
       }
+
       else {
-        BLI_assert(0);
+        /* Transform and create action */
+        if (eed_test) {
+          best.ele = (BMElem *)eed_test;
+          best.base_index = base_index_edge;
+        }
+      }
+
+      /* All actions use same vertex preselection */
+      /* Retopology should always prioritize edge preselection. Only preselct a vertex when the
+       * cursor is really close to it*/
+      if (eve_test) {
+        BMVert *vert = (BMVert *)eve_test;
+        float vert_p_co[3], vert_co[3];
+        float mval_f[2] = {UNPACK2(vc.mval)};
+        mul_v3_m4v3(vert_co, gz_ele->bases[base_index_vert]->object->obmat, vert->co);
+        ED_view3d_project(vc.ar, vert_co, vert_p_co);
+        float len = len_v2v2(vert_p_co, mval_f);
+        if (len < 35) {
+          best.ele = (BMElem *)eve_test;
+          best.base_index = base_index_vert;
+        }
+        if (!BM_vert_is_boundary(vert) &&
+            EDBM_preselect_action_get(gz_ele->psel) != PRESELECT_ACTION_DELETE) {
+          best.ele = (BMElem *)eve_test;
+          best.base_index = base_index_vert;
+        }
       }
-      best.base_index = base_index;
+
       /* Check above should never fail, if it does it's an internal error. */
       BLI_assert(best.base_index != -1);
+
+      Base *base = gz_ele->bases[best.base_index];
+      best.ob = base->object;
     }
   }
 
@@ -167,32 +202,30 @@ static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int
     }
   }
 
-  if ((prev.base_index == gz_ele->base_index) && (prev.vert_index == gz_ele->vert_index) &&
-      (prev.edge_index == gz_ele->edge_index) && (prev.face_index == gz_ele->face_index)) {
-    /* pass (only recalculate on change) */
-  }
-  else {
-    if (best.ele) {
-      const float(*coords)[3] = NULL;
-      {
-        Object *ob = gz_ele->bases[gz_ele->base_index]->object;
-        Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
-        Mesh *me_eval = (Mesh *)DEG_get_evaluated_id(depsgraph, ob->data);
-        if (me_eval->runtime.edit_data) {
-          coords = me_eval->runtime.edit_data->vertexCos;
-        }
+  if (best.ele) {
+    const float(*coords)[3] = NULL;
+    {
+      Object *ob = gz_ele->bases[gz_ele->base_index]->object;
+      Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+      Mesh *me_eval = (Mesh *)DEG_get_evaluated_id(depsgraph, ob->data);
+      if (me_eval->runtime.edit_data) {
+        coords = me_eval->runtime.edit_data->vertexCos;
       }
-      EDBM_preselect_elem_update_from_single(gz_ele->psel, bm, best.ele, coords);
-    }
-    else {
-      EDBM_preselect_elem_clear(gz_ele->psel);
     }
+    EDBM_preselect_elem_update_from_single(gz_ele->psel, bm, best.ele, coords);
+    EDBM_preselect_elem_update_preview(gz_ele->psel, &vc, bm, best.ele, mval);
+  }
+  else {
+    EDBM_preselect_elem_clear(gz_ele->psel);
+    EDBM_preselect_preview_clear(gz_ele->psel);
+  }
 
-    RNA_int_set(gz->ptr, "object_index", gz_ele->base_index);
-    RNA_int_set(gz->ptr, "vert_index", gz_ele->vert_index);
-    RNA_int_set(gz->ptr, "edge_index", gz_ele->edge_index);
-    RNA_int_set(gz->ptr, "face_index", gz_ele->face_index);
+  RNA_int_set(gz->ptr, "object_index", gz_ele->base_index);
+  RNA_int_set(gz->ptr, "vert_index", gz_ele->vert_index);
+  RNA_int_set(gz->ptr, "edge_index", gz_ele->edge_index);
+  RNA_int_set(gz->ptr, "face_index", gz_ele->face_index);
 
+  if (best.ele) {
     ARegion *ar = CTX_wm_region(C);
     ED_region_tag_redraw(ar);
   }
@@ -471,5 +504,4 @@ void ED_view3d_gizmo_mesh_preselect_get_active(bContext *C,
     }
   }
 }
-
 /** \} */