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, ¶ms);
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, ¶ms);
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