1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2019 by Blender Foundation.
17  * All rights reserved.
18  */
19 
20 /** \file
21  * \ingroup edmesh
22  */
23 
24 #include "DNA_mesh_types.h"
25 #include "DNA_modifier_types.h"
26 #include "DNA_object_types.h"
27 
28 #include "BLI_math.h"
29 
30 #include "BLT_translation.h"
31 
32 #include "BKE_context.h"
33 #include "BKE_editmesh.h"
34 #include "BKE_layer.h"
35 #include "BKE_lib_id.h"
36 #include "BKE_mesh.h"
37 #include "BKE_modifier.h"
38 #include "BKE_paint.h"
39 #include "BKE_report.h"
40 #include "BKE_screen.h"
41 #include "BKE_shrinkwrap.h"
42 
43 #include "DEG_depsgraph.h"
44 #include "DEG_depsgraph_build.h"
45 
46 #include "RNA_access.h"
47 #include "RNA_define.h"
48 
49 #include "WM_api.h"
50 #include "WM_types.h"
51 
52 #include "ED_mesh.h"
53 #include "ED_object.h"
54 #include "ED_screen.h"
55 #include "ED_sculpt.h"
56 #include "ED_view3d.h"
57 
58 #include "bmesh_tools.h"
59 
60 #include "MEM_guardedalloc.h"
61 
62 #include "mesh_intern.h" /* own include */
63 
geometry_extract_poll(bContext * C)64 static bool geometry_extract_poll(bContext *C)
65 {
66   Object *ob = CTX_data_active_object(C);
67   if (ob != NULL && ob->mode == OB_MODE_SCULPT) {
68     if (ob->sculpt->bm) {
69       CTX_wm_operator_poll_msg_set(C, "The geometry can not be extracted with dyntopo activated");
70       return false;
71     }
72     return ED_operator_object_active_editable_mesh(C);
73   }
74   return false;
75 }
76 
77 typedef struct GeometryExtactParams {
78   /* For extracting Face Sets. */
79   int active_face_set;
80 
81   /* For extracting Mask. */
82   float mask_threshold;
83 
84   /* Common paramenters. */
85   bool add_boundary_loop;
86   int num_smooth_iterations;
87   bool apply_shrinkwrap;
88   bool add_solidify;
89 } GeometryExtractParams;
90 
91 /* Function that tags in BMesh the faces that should be deleted in the extracted object. */
92 typedef void(GeometryExtractTagMeshFunc)(struct BMesh *, GeometryExtractParams *);
93 
geometry_extract_apply(bContext * C,wmOperator * op,GeometryExtractTagMeshFunc * tag_fn,GeometryExtractParams * params)94 static int geometry_extract_apply(bContext *C,
95                                   wmOperator *op,
96                                   GeometryExtractTagMeshFunc *tag_fn,
97                                   GeometryExtractParams *params)
98 {
99   struct Main *bmain = CTX_data_main(C);
100   Object *ob = CTX_data_active_object(C);
101   View3D *v3d = CTX_wm_view3d(C);
102   Scene *scene = CTX_data_scene(C);
103   Depsgraph *depsgraph = CTX_data_depsgraph_on_load(C);
104 
105   ED_object_sculptmode_exit(C, depsgraph);
106 
107   BKE_sculpt_mask_layers_ensure(ob, NULL);
108 
109   Mesh *mesh = ob->data;
110   Mesh *new_mesh = (Mesh *)BKE_id_copy(bmain, &mesh->id);
111 
112   const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(new_mesh);
113   BMesh *bm;
114   bm = BM_mesh_create(&allocsize,
115                       &((struct BMeshCreateParams){
116                           .use_toolflags = true,
117                       }));
118 
119   BM_mesh_bm_from_me(bm,
120                      new_mesh,
121                      (&(struct BMeshFromMeshParams){
122                          .calc_face_normal = true,
123                      }));
124 
125   BMEditMesh *em = BKE_editmesh_create(bm, false);
126 
127   /* Generate the tags for deleting geometry in the extracted object. */
128   tag_fn(bm, params);
129 
130   /* Delete all tagged faces. */
131   BM_mesh_delete_hflag_context(bm, BM_ELEM_TAG, DEL_FACES);
132   BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false);
133 
134   BMVert *v;
135   BMEdge *ed;
136   BMIter iter;
137   BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
138     mul_v3_v3(v->co, ob->scale);
139   }
140 
141   if (params->add_boundary_loop) {
142     BM_ITER_MESH (ed, &iter, bm, BM_EDGES_OF_MESH) {
143       BM_elem_flag_set(ed, BM_ELEM_TAG, BM_edge_is_boundary(ed));
144     }
145     edbm_extrude_edges_indiv(em, op, BM_ELEM_TAG, false);
146 
147     for (int repeat = 0; repeat < params->num_smooth_iterations; repeat++) {
148       BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false);
149       BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
150         BM_elem_flag_set(v, BM_ELEM_TAG, !BM_vert_is_boundary(v));
151       }
152       for (int i = 0; i < 3; i++) {
153         if (!EDBM_op_callf(em,
154                            op,
155                            "smooth_vert verts=%hv factor=%f mirror_clip_x=%b mirror_clip_y=%b "
156                            "mirror_clip_z=%b "
157                            "clip_dist=%f use_axis_x=%b use_axis_y=%b use_axis_z=%b",
158                            BM_ELEM_TAG,
159                            1.0,
160                            false,
161                            false,
162                            false,
163                            0.1,
164                            true,
165                            true,
166                            true)) {
167           continue;
168         }
169       }
170 
171       BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false);
172       BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
173         BM_elem_flag_set(v, BM_ELEM_TAG, BM_vert_is_boundary(v));
174       }
175       for (int i = 0; i < 1; i++) {
176         if (!EDBM_op_callf(em,
177                            op,
178                            "smooth_vert verts=%hv factor=%f mirror_clip_x=%b mirror_clip_y=%b "
179                            "mirror_clip_z=%b "
180                            "clip_dist=%f use_axis_x=%b use_axis_y=%b use_axis_z=%b",
181                            BM_ELEM_TAG,
182                            0.5,
183                            false,
184                            false,
185                            false,
186                            0.1,
187                            true,
188                            true,
189                            true)) {
190           continue;
191         }
192       }
193     }
194   }
195 
196   BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false);
197 
198   BKE_id_free(bmain, new_mesh);
199   new_mesh = BKE_mesh_from_bmesh_nomain(bm,
200                                         (&(struct BMeshToMeshParams){
201                                             .calc_object_remap = false,
202                                         }),
203                                         mesh);
204 
205   BKE_editmesh_free(em);
206   MEM_freeN(em);
207 
208   if (new_mesh->totvert == 0) {
209     BKE_id_free(bmain, new_mesh);
210     return OPERATOR_FINISHED;
211   }
212 
213   ushort local_view_bits = 0;
214   if (v3d && v3d->localvd) {
215     local_view_bits = v3d->local_view_uuid;
216   }
217   Object *new_ob = ED_object_add_type(C, OB_MESH, NULL, ob->loc, ob->rot, false, local_view_bits);
218   BKE_mesh_nomain_to_mesh(new_mesh, new_ob->data, new_ob, &CD_MASK_EVERYTHING, true);
219 
220   /* Remove the Face Sets as they need to be recreated when entering Sculpt Mode in the new object.
221    * TODO(pablodobarro): In the future we can try to preserve them from the original mesh. */
222   Mesh *new_ob_mesh = new_ob->data;
223   CustomData_free_layers(&new_ob_mesh->pdata, CD_SCULPT_FACE_SETS, new_ob_mesh->totpoly);
224 
225   /* Remove the mask from the new object so it can be sculpted directly after extracting. */
226   CustomData_free_layers(&new_ob_mesh->vdata, CD_PAINT_MASK, new_ob_mesh->totvert);
227 
228   BKE_mesh_copy_settings(new_ob_mesh, mesh);
229 
230   if (params->apply_shrinkwrap) {
231     BKE_shrinkwrap_mesh_nearest_surface_deform(C, new_ob, ob);
232   }
233 
234   if (params->add_solidify) {
235     ED_object_modifier_add(
236         op->reports, bmain, scene, new_ob, "geometry_extract_solidify", eModifierType_Solidify);
237     SolidifyModifierData *sfmd = (SolidifyModifierData *)BKE_modifiers_findby_name(
238         new_ob, "mask_extract_solidify");
239     if (sfmd) {
240       sfmd->offset = -0.05f;
241     }
242   }
243 
244   BKE_mesh_calc_normals(new_ob->data);
245 
246   WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, new_ob);
247   BKE_mesh_batch_cache_dirty_tag(new_ob->data, BKE_MESH_BATCH_DIRTY_ALL);
248   DEG_relations_tag_update(bmain);
249   DEG_id_tag_update(&new_ob->id, ID_RECALC_GEOMETRY);
250   WM_event_add_notifier(C, NC_GEOM | ND_DATA, new_ob->data);
251 
252   return OPERATOR_FINISHED;
253 }
254 
geometry_extract_tag_masked_faces(BMesh * bm,GeometryExtractParams * params)255 static void geometry_extract_tag_masked_faces(BMesh *bm, GeometryExtractParams *params)
256 {
257   const float threshold = params->mask_threshold;
258 
259   BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false);
260   const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK);
261 
262   BMFace *f;
263   BMIter iter;
264   BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
265     bool keep_face = true;
266     BMVert *v;
267     BMIter face_iter;
268     BM_ITER_ELEM (v, &face_iter, f, BM_VERTS_OF_FACE) {
269       const float mask = BM_ELEM_CD_GET_FLOAT(v, cd_vert_mask_offset);
270       if (mask < threshold) {
271         keep_face = false;
272         break;
273       }
274     }
275     BM_elem_flag_set(f, BM_ELEM_TAG, !keep_face);
276   }
277 }
278 
geometry_extract_tag_face_set(BMesh * bm,GeometryExtractParams * params)279 static void geometry_extract_tag_face_set(BMesh *bm, GeometryExtractParams *params)
280 {
281   const int tag_face_set_id = params->active_face_set;
282 
283   BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false);
284   const int cd_face_sets_offset = CustomData_get_offset(&bm->pdata, CD_SCULPT_FACE_SETS);
285 
286   BMFace *f;
287   BMIter iter;
288   BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
289     const int face_set_id = abs(BM_ELEM_CD_GET_INT(f, cd_face_sets_offset));
290     BM_elem_flag_set(f, BM_ELEM_TAG, face_set_id != tag_face_set_id);
291   }
292 }
293 
paint_mask_extract_exec(bContext * C,wmOperator * op)294 static int paint_mask_extract_exec(bContext *C, wmOperator *op)
295 {
296   GeometryExtractParams params;
297   params.mask_threshold = RNA_float_get(op->ptr, "mask_threshold");
298   params.num_smooth_iterations = RNA_int_get(op->ptr, "smooth_iterations");
299   params.add_boundary_loop = RNA_boolean_get(op->ptr, "add_boundary_loop");
300   params.apply_shrinkwrap = RNA_boolean_get(op->ptr, "apply_shrinkwrap");
301   params.add_solidify = RNA_boolean_get(op->ptr, "add_solidify");
302   return geometry_extract_apply(C, op, geometry_extract_tag_masked_faces, &params);
303 }
304 
paint_mask_extract_invoke(bContext * C,wmOperator * op,const wmEvent * e)305 static int paint_mask_extract_invoke(bContext *C, wmOperator *op, const wmEvent *e)
306 {
307   return WM_operator_props_popup_confirm(C, op, e);
308 }
309 
geometry_extract_props(StructRNA * srna)310 static void geometry_extract_props(StructRNA *srna)
311 {
312   RNA_def_boolean(srna,
313                   "add_boundary_loop",
314                   true,
315                   "Add Boundary Loop",
316                   "Add an extra edge loop to better preserve the shape when applying a "
317                   "subdivision surface modifier");
318   RNA_def_int(srna,
319               "smooth_iterations",
320               4,
321               0,
322               INT_MAX,
323               "Smooth Iterations",
324               "Smooth iterations applied to the extracted mesh",
325               0,
326               20);
327   RNA_def_boolean(srna,
328                   "apply_shrinkwrap",
329                   true,
330                   "Project to Sculpt",
331                   "Project the extracted mesh into the original sculpt");
332   RNA_def_boolean(srna,
333                   "add_solidify",
334                   true,
335                   "Extract as Solid",
336                   "Extract the mask as a solid object with a solidify modifier");
337 }
338 
MESH_OT_paint_mask_extract(wmOperatorType * ot)339 void MESH_OT_paint_mask_extract(wmOperatorType *ot)
340 {
341   /* identifiers */
342   ot->name = "Mask Extract";
343   ot->description = "Create a new mesh object from the current paint mask";
344   ot->idname = "MESH_OT_paint_mask_extract";
345 
346   /* api callbacks */
347   ot->poll = geometry_extract_poll;
348   ot->invoke = paint_mask_extract_invoke;
349   ot->exec = paint_mask_extract_exec;
350 
351   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
352 
353   RNA_def_float(
354       ot->srna,
355       "mask_threshold",
356       0.5f,
357       0.0f,
358       1.0f,
359       "Threshold",
360       "Minimum mask value to consider the vertex valid to extract a face from the original mesh",
361       0.0f,
362       1.0f);
363 
364   geometry_extract_props(ot->srna);
365 }
366 
face_set_extract_invoke(bContext * C,wmOperator * op,const wmEvent * UNUSED (e))367 static int face_set_extract_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e))
368 {
369   ED_workspace_status_text(C, TIP_("Click on the mesh to select a Face Set"));
370   WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_EYEDROPPER);
371   WM_event_add_modal_handler(C, op);
372   return OPERATOR_RUNNING_MODAL;
373 }
374 
face_set_extract_modal(bContext * C,wmOperator * op,const wmEvent * event)375 static int face_set_extract_modal(bContext *C, wmOperator *op, const wmEvent *event)
376 {
377   switch (event->type) {
378     case LEFTMOUSE:
379       if (event->val == KM_PRESS) {
380         WM_cursor_modal_restore(CTX_wm_window(C));
381         ED_workspace_status_text(C, NULL);
382 
383         /* This modal operator uses and eyedropper to pick a Face Set from the mesh. This ensures
384          * that the mouse clicked in a viewport region and its coordinates can be used to raycast
385          * the PBVH and update the active Face Set ID. */
386         bScreen *screen = CTX_wm_screen(C);
387         ARegion *region = BKE_screen_find_main_region_at_xy(
388             screen, SPACE_VIEW3D, event->x, event->y);
389 
390         if (!region) {
391           return OPERATOR_CANCELLED;
392         }
393 
394         const float mval[2] = {event->x - region->winrct.xmin, event->y - region->winrct.ymin};
395 
396         Object *ob = CTX_data_active_object(C);
397         const int face_set_id = ED_sculpt_face_sets_active_update_and_get(C, ob, mval);
398         if (face_set_id == SCULPT_FACE_SET_NONE) {
399           return OPERATOR_CANCELLED;
400         }
401 
402         GeometryExtractParams params;
403         params.active_face_set = face_set_id;
404         params.num_smooth_iterations = 0;
405         params.add_boundary_loop = false;
406         params.apply_shrinkwrap = true;
407         params.add_solidify = true;
408         return geometry_extract_apply(C, op, geometry_extract_tag_face_set, &params);
409       }
410       break;
411     case EVT_ESCKEY:
412     case RIGHTMOUSE: {
413       WM_cursor_modal_restore(CTX_wm_window(C));
414       ED_workspace_status_text(C, NULL);
415 
416       return OPERATOR_CANCELLED;
417     }
418   }
419 
420   return OPERATOR_RUNNING_MODAL;
421 }
422 
MESH_OT_face_set_extract(wmOperatorType * ot)423 void MESH_OT_face_set_extract(wmOperatorType *ot)
424 {
425   /* identifiers */
426   ot->name = "Face Set Extract";
427   ot->description = "Create a new mesh object from the selected Face Set";
428   ot->idname = "MESH_OT_face_set_extract";
429 
430   /* api callbacks */
431   ot->poll = geometry_extract_poll;
432   ot->invoke = face_set_extract_invoke;
433   ot->modal = face_set_extract_modal;
434 
435   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
436 
437   geometry_extract_props(ot->srna);
438 }
439 
slice_paint_mask(BMesh * bm,bool invert,bool fill_holes,float mask_threshold)440 static void slice_paint_mask(BMesh *bm, bool invert, bool fill_holes, float mask_threshold)
441 {
442   BMVert *v;
443   BMFace *f;
444   BMIter iter;
445   BMIter face_iter;
446 
447   /* Delete all masked faces */
448   const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK);
449   BLI_assert(cd_vert_mask_offset != -1);
450   BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false);
451 
452   BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
453     bool keep_face = true;
454     BM_ITER_ELEM (v, &face_iter, f, BM_VERTS_OF_FACE) {
455       const float mask = BM_ELEM_CD_GET_FLOAT(v, cd_vert_mask_offset);
456       if (mask < mask_threshold) {
457         keep_face = false;
458         break;
459       }
460     }
461     if (invert) {
462       keep_face = !keep_face;
463     }
464     BM_elem_flag_set(f, BM_ELEM_TAG, keep_face);
465   }
466 
467   BM_mesh_delete_hflag_context(bm, BM_ELEM_TAG, DEL_FACES);
468   BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false);
469   BM_mesh_elem_hflag_enable_all(bm, BM_EDGE, BM_ELEM_TAG, false);
470 
471   if (fill_holes) {
472     BM_mesh_edgenet(bm, false, true);
473     BM_mesh_normals_update(bm);
474     BMO_op_callf(bm,
475                  (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE),
476                  "triangulate faces=%hf quad_method=%i ngon_method=%i",
477                  BM_ELEM_TAG,
478                  0,
479                  0);
480 
481     BM_mesh_elem_hflag_enable_all(bm, BM_FACE, BM_ELEM_TAG, false);
482     BMO_op_callf(bm,
483                  (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE),
484                  "recalc_face_normals faces=%hf",
485                  BM_ELEM_TAG);
486     BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false);
487   }
488 }
489 
paint_mask_slice_exec(bContext * C,wmOperator * op)490 static int paint_mask_slice_exec(bContext *C, wmOperator *op)
491 {
492   struct Main *bmain = CTX_data_main(C);
493   Object *ob = CTX_data_active_object(C);
494   View3D *v3d = CTX_wm_view3d(C);
495 
496   BKE_sculpt_mask_layers_ensure(ob, NULL);
497 
498   Mesh *mesh = ob->data;
499   Mesh *new_mesh = (Mesh *)BKE_id_copy(bmain, &mesh->id);
500 
501   if (ob->mode == OB_MODE_SCULPT) {
502     ED_sculpt_undo_geometry_begin(ob, "mask slice");
503   }
504 
505   BMesh *bm;
506   const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(new_mesh);
507   bm = BM_mesh_create(&allocsize,
508                       &((struct BMeshCreateParams){
509                           .use_toolflags = true,
510                       }));
511 
512   BM_mesh_bm_from_me(bm,
513                      new_mesh,
514                      (&(struct BMeshFromMeshParams){
515                          .calc_face_normal = true,
516                      }));
517 
518   slice_paint_mask(
519       bm, false, RNA_boolean_get(op->ptr, "fill_holes"), RNA_float_get(op->ptr, "mask_threshold"));
520   BKE_id_free(bmain, new_mesh);
521   new_mesh = BKE_mesh_from_bmesh_nomain(bm,
522                                         (&(struct BMeshToMeshParams){
523                                             .calc_object_remap = false,
524                                         }),
525                                         mesh);
526   BM_mesh_free(bm);
527 
528   if (RNA_boolean_get(op->ptr, "new_object")) {
529     ushort local_view_bits = 0;
530     if (v3d && v3d->localvd) {
531       local_view_bits = v3d->local_view_uuid;
532     }
533     Object *new_ob = ED_object_add_type(
534         C, OB_MESH, NULL, ob->loc, ob->rot, false, local_view_bits);
535     Mesh *new_ob_mesh = (Mesh *)BKE_id_copy(bmain, &mesh->id);
536 
537     const BMAllocTemplate allocsize_new_ob = BMALLOC_TEMPLATE_FROM_ME(new_ob_mesh);
538     bm = BM_mesh_create(&allocsize_new_ob,
539                         &((struct BMeshCreateParams){
540                             .use_toolflags = true,
541                         }));
542 
543     BM_mesh_bm_from_me(bm,
544                        new_ob_mesh,
545                        (&(struct BMeshFromMeshParams){
546                            .calc_face_normal = true,
547                        }));
548 
549     slice_paint_mask(bm,
550                      true,
551                      RNA_boolean_get(op->ptr, "fill_holes"),
552                      RNA_float_get(op->ptr, "mask_threshold"));
553     BKE_id_free(bmain, new_ob_mesh);
554     new_ob_mesh = BKE_mesh_from_bmesh_nomain(bm,
555                                              (&(struct BMeshToMeshParams){
556                                                  .calc_object_remap = false,
557                                              }),
558                                              mesh);
559     BM_mesh_free(bm);
560 
561     /* Remove the mask from the new object so it can be sculpted directly after slicing. */
562     CustomData_free_layers(&new_ob_mesh->vdata, CD_PAINT_MASK, new_ob_mesh->totvert);
563 
564     BKE_mesh_nomain_to_mesh(new_ob_mesh, new_ob->data, new_ob, &CD_MASK_MESH, true);
565     BKE_mesh_calc_normals(new_ob->data);
566     BKE_mesh_copy_settings(new_ob->data, mesh);
567     WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, new_ob);
568     BKE_mesh_batch_cache_dirty_tag(new_ob->data, BKE_MESH_BATCH_DIRTY_ALL);
569     DEG_relations_tag_update(bmain);
570     DEG_id_tag_update(&new_ob->id, ID_RECALC_GEOMETRY);
571     WM_event_add_notifier(C, NC_GEOM | ND_DATA, new_ob->data);
572   }
573 
574   BKE_mesh_nomain_to_mesh(new_mesh, ob->data, ob, &CD_MASK_MESH, true);
575   BKE_mesh_calc_normals(ob->data);
576 
577   if (ob->mode == OB_MODE_SCULPT) {
578     SculptSession *ss = ob->sculpt;
579     ss->face_sets = CustomData_get_layer(&((Mesh *)ob->data)->pdata, CD_SCULPT_FACE_SETS);
580     if (ss->face_sets) {
581       /* Assign a new Face Set ID to the new faces created by the slice operation. */
582       const int next_face_set_id = ED_sculpt_face_sets_find_next_available_id(ob->data);
583       ED_sculpt_face_sets_initialize_none_to_id(ob->data, next_face_set_id);
584     }
585     ED_sculpt_undo_geometry_end(ob);
586   }
587 
588   BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL);
589   DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
590   WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
591 
592   return OPERATOR_FINISHED;
593 }
594 
MESH_OT_paint_mask_slice(wmOperatorType * ot)595 void MESH_OT_paint_mask_slice(wmOperatorType *ot)
596 {
597   PropertyRNA *prop;
598   /* identifiers */
599   ot->name = "Mask Slice";
600   ot->description = "Slices the paint mask from the mesh";
601   ot->idname = "MESH_OT_paint_mask_slice";
602 
603   /* api callbacks */
604   ot->poll = geometry_extract_poll;
605   ot->exec = paint_mask_slice_exec;
606 
607   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
608 
609   RNA_def_float(
610       ot->srna,
611       "mask_threshold",
612       0.5f,
613       0.0f,
614       1.0f,
615       "Threshold",
616       "Minimum mask value to consider the vertex valid to extract a face from the original mesh",
617       0.0f,
618       1.0f);
619   prop = RNA_def_boolean(
620       ot->srna, "fill_holes", true, "Fill Holes", "Fill holes after slicing the mask");
621   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
622   prop = RNA_def_boolean(ot->srna,
623                          "new_object",
624                          true,
625                          "Slice to New Object",
626                          "Create a new object from the sliced mask");
627   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
628 }
629