Blender V4.3
sculpt_trim.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "BLI_math_geom.h"
10#include "BLI_math_matrix.h"
11#include "BLI_math_matrix.hh"
12#include "BLI_math_vector.h"
13#include "BLI_math_vector.hh"
14#include "BLI_polyfill_2d.h"
15
16#include "BKE_brush.hh"
17#include "BKE_context.hh"
18#include "BKE_layer.hh"
19#include "BKE_lib_id.hh"
20#include "BKE_mesh.hh"
21#include "BKE_object.hh"
22#include "BKE_report.hh"
23
24#include "DNA_modifier_types.h"
25
26#include "DEG_depsgraph.hh"
27
28#include "ED_sculpt.hh"
29
30#include "RNA_access.hh"
31#include "RNA_define.hh"
32
33#include "WM_api.hh"
34#include "WM_types.hh"
35
36#include "bmesh.hh"
39
40#include "paint_intern.hh"
41#include "sculpt_face_set.hh"
42#include "sculpt_gesture.hh"
43#include "sculpt_intern.hh"
44#include "sculpt_islands.hh"
45
47
48enum class OperationType {
49 Intersect = 0,
50 Difference = 1,
51 Union = 2,
52 Join = 3,
53};
54
55/* Intersect is not exposed in the UI because it does not work correctly with symmetry (it deletes
56 * the symmetrical part of the mesh in the first symmetry pass). */
59 "DIFFERENCE",
60 0,
61 "Difference",
62 "Use a difference boolean operation"},
63 {int(OperationType::Union), "UNION", 0, "Union", "Use a union boolean operation"},
65 "JOIN",
66 0,
67 "Join",
68 "Join the new mesh as separate geometry, without performing any boolean operation"},
69 {0, nullptr, 0, nullptr, nullptr},
70};
71
72enum class OrientationType {
73 View = 0,
74 Surface = 1,
75};
78 "VIEW",
79 0,
80 "View",
81 "Use the view to orientate the trimming shape"},
83 "SURFACE",
84 0,
85 "Surface",
86 "Use the surface normal to orientate the trimming shape"},
87 {0, nullptr, 0, nullptr, nullptr},
88};
89
90enum class ExtrudeMode {
91 Project = 0,
92 Fixed = 1,
93};
94
97 "PROJECT",
98 0,
99 "Project",
100 "Align trim geometry with the perspective of the current view for a tapered shape"},
102 "FIXED",
103 0,
104 "Fixed",
105 "Align trim geometry orthogonally for a shape with 90 degree angles"},
106 {0, nullptr, 0, nullptr, nullptr},
107};
108
109enum class SolverMode {
110 Exact = 0,
111 Fast = 1,
112};
113
115 {int(SolverMode::Exact), "EXACT", 0, "Exact", "Use the exact boolean solver"},
116 {int(SolverMode::Fast), "FAST", 0, "Fast", "Use the fast float boolean solver"},
117 {0, nullptr, 0, nullptr, nullptr},
118};
119
139
140/* Recalculate the mesh normals for the generated trim mesh. */
141static void update_normals(gesture::GestureData &gesture_data)
142{
143 TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
144 Mesh *trim_mesh = trim_operation->mesh;
145
146 const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(trim_mesh);
147
148 BMeshCreateParams bm_create_params{};
149 bm_create_params.use_toolflags = true;
150 BMesh *bm = BM_mesh_create(&allocsize, &bm_create_params);
151
152 BMeshFromMeshParams bm_from_me_params{};
153 bm_from_me_params.calc_face_normal = true;
154 bm_from_me_params.calc_vert_normal = true;
155 BM_mesh_bm_from_me(bm, trim_mesh, &bm_from_me_params);
156
160 "recalc_face_normals faces=%hf",
163
164 BMeshToMeshParams convert_params{};
165 convert_params.calc_object_remap = false;
166 Mesh *result = BKE_mesh_from_bmesh_nomain(bm, &convert_params, trim_mesh);
167
169 BKE_id_free(nullptr, trim_mesh);
170 trim_operation->mesh = result;
171}
172
173/* Get the origin and normal that are going to be used for calculating the depth and position of
174 * the trimming geometry. */
176 float *r_origin,
177 float *r_normal)
178{
179 TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
180 /* Use the view origin and normal in world space. The trimming mesh coordinates are
181 * calculated in world space, aligned to the view, and then converted to object space to
182 * store them in the final trimming mesh which is going to be used in the boolean operation.
183 */
184 switch (trim_operation->orientation) {
186 mul_v3_m4v3(r_origin,
187 gesture_data.vc.obact->object_to_world().ptr(),
188 trim_operation->initial_location);
189 copy_v3_v3(r_normal, gesture_data.world_space_view_normal);
190 negate_v3(r_normal);
191 break;
193 mul_v3_m4v3(r_origin,
194 gesture_data.vc.obact->object_to_world().ptr(),
195 trim_operation->initial_location);
196 /* Transforming the normal does not take non uniform scaling into account. Sculpt mode is not
197 * expected to work on object with non uniform scaling. */
198 copy_v3_v3(r_normal, trim_operation->initial_normal);
199 mul_mat3_m4_v3(gesture_data.vc.obact->object_to_world().ptr(), r_normal);
200 break;
201 }
202}
203
204/* Calculates the depth of the drawn shape inside the scene.*/
205static void calculate_depth(gesture::GestureData &gesture_data,
206 float &r_depth_front,
207 float &r_depth_back)
208{
209 TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
210
211 SculptSession &ss = *gesture_data.ss;
212 ViewContext &vc = gesture_data.vc;
213
214 float shape_plane[4];
215 float shape_origin[3];
216 float shape_normal[3];
217 get_origin_and_normal(gesture_data, shape_origin, shape_normal);
218 plane_from_point_normal_v3(shape_plane, shape_origin, shape_normal);
219
220 float depth_front = FLT_MAX;
221 float depth_back = -FLT_MAX;
222
224 const float4x4 &object_to_world = vc.obact->object_to_world();
225
226 for (const int i : positions.index_range()) {
227 /* Convert the coordinates to world space to calculate the depth. When generating the trimming
228 * mesh, coordinates are first calculated in world space, then converted to object space to
229 * store them. */
230 const float3 world_space_vco = math::transform_point(object_to_world, positions[i]);
231 const float dist = dist_signed_to_plane_v3(world_space_vco, shape_plane);
232 depth_front = std::min(dist, depth_front);
233 depth_back = std::max(dist, depth_back);
234 }
235
236 if (trim_operation->use_cursor_depth) {
237 float world_space_gesture_initial_location[3];
238 mul_v3_m4v3(world_space_gesture_initial_location,
239 object_to_world.ptr(),
240 trim_operation->initial_location);
241
242 float mid_point_depth;
243 if (trim_operation->orientation == OrientationType::View) {
244 mid_point_depth = trim_operation->initial_hit ?
245 dist_signed_to_plane_v3(world_space_gesture_initial_location,
246 shape_plane) :
247 (depth_back + depth_front) * 0.5f;
248 }
249 else {
250 /* When using normal orientation, if the stroke started over the mesh, position the mid point
251 * at 0 distance from the shape plane. This positions the trimming shape half inside of the
252 * surface. */
253 mid_point_depth = trim_operation->initial_hit ? 0.0f : (depth_back + depth_front) * 0.5f;
254 }
255
256 float depth_radius;
257
258 if (trim_operation->initial_hit) {
259 depth_radius = ss.cursor_radius;
260 }
261 else {
262 /* ss.cursor_radius is only valid if the stroke started
263 * over the sculpt mesh. If it's not we must
264 * compute the radius ourselves. See #81452.
265 */
266
268 Brush *brush = BKE_paint_brush(&sd->paint);
269 Scene *scene = CTX_data_scene(vc.C);
270
271 if (!BKE_brush_use_locked_size(scene, brush)) {
272 depth_radius = paint_calc_object_space_radius(
273 vc, trim_operation->initial_location, BKE_brush_size_get(scene, brush));
274 }
275 else {
276 depth_radius = BKE_brush_unprojected_radius_get(scene, brush);
277 }
278 }
279
280 depth_front = mid_point_depth - depth_radius;
281 depth_back = mid_point_depth + depth_radius;
282 }
283
284 r_depth_front = depth_front;
285 r_depth_back = depth_back;
286}
287
288/* Calculates a scalar factor to use to ensure a drawn line gesture
289 * encompasses the entire object to be acted on. */
290static float calc_expand_factor(const gesture::GestureData &gesture_data)
291{
292 Object &object = *gesture_data.vc.obact;
293
294 rcti rect;
295 const Bounds<float3> bounds = *BKE_object_boundbox_get(&object);
297 &rect, bounds.min, bounds.max, *gesture_data.vc.region, *gesture_data.vc.rv3d, object);
298
299 const float2 min_corner(rect.xmin, rect.ymin);
300 const float2 max_corner(rect.xmax, rect.ymax);
301
302 /* Multiply the screen space bounds by an arbitrary factor to ensure the created points are
303 * sufficiently far and enclose the mesh to be operated on. */
304 return math::distance(min_corner, max_corner) * 2.0f;
305}
306
307/* Converts a line gesture's points into usable screen points. */
309{
310 if (gesture_data.shape_type != gesture::ShapeType::Line) {
311 return gesture_data.gesture_points;
312 }
313
314 const float expand_factor = calc_expand_factor(gesture_data);
315
316 float2 start(gesture_data.gesture_points[0]);
317 float2 end(gesture_data.gesture_points[1]);
318
319 const float2 dir = math::normalize(end - start);
320
321 if (!gesture_data.line.use_side_planes) {
322 end = end + dir * expand_factor;
323 start = start - dir * expand_factor;
324 }
325
326 float2 perp(dir.y, -dir.x);
327
328 if (gesture_data.line.flip) {
329 perp *= -1;
330 }
331
332 const float2 parallel_start = start + perp * expand_factor;
333 const float2 parallel_end = end + perp * expand_factor;
334
335 return {start, end, parallel_end, parallel_start};
336}
337
338static void generate_geometry(gesture::GestureData &gesture_data)
339{
340 TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
341 ViewContext &vc = gesture_data.vc;
342 ARegion *region = vc.region;
343
344 const Array<float2> screen_points = gesture_to_screen_points(gesture_data);
345 BLI_assert(screen_points.size() > 1);
346
347 const int trim_totverts = screen_points.size() * 2;
348 const int trim_faces_nums = (2 * (screen_points.size() - 2)) + (2 * screen_points.size());
349 trim_operation->mesh = BKE_mesh_new_nomain(
350 trim_totverts, 0, trim_faces_nums, trim_faces_nums * 3);
351 trim_operation->true_mesh_co = static_cast<float(*)[3]>(
352 MEM_malloc_arrayN(trim_totverts, sizeof(float[3]), "mesh orco"));
353
354 float shape_origin[3];
355 float shape_normal[3];
356 float shape_plane[4];
357 get_origin_and_normal(gesture_data, shape_origin, shape_normal);
358 plane_from_point_normal_v3(shape_plane, shape_origin, shape_normal);
359
360 const float(*ob_imat)[4] = vc.obact->world_to_object().ptr();
361
362 /* Write vertices coordinates OperationType::Difference for the front face. */
363 MutableSpan<float3> positions = trim_operation->mesh->vert_positions_for_write();
364
365 float depth_front;
366 float depth_back;
367 calculate_depth(gesture_data, depth_front, depth_back);
368
369 if (!trim_operation->use_cursor_depth) {
370 float pad_factor = (depth_back - depth_front) * 0.01f + 0.001f;
371
372 /* When using cursor depth, don't modify the depth set by the cursor radius. If full depth is
373 * used, adding a little padding to the trimming shape can help avoiding booleans with coplanar
374 * faces. */
375 depth_front -= pad_factor;
376 depth_back += pad_factor;
377 }
378
379 float depth_point[3];
380
381 /* Get origin point for OrientationType::View.
382 * NOTE: for projection extrusion we add depth_front here
383 * instead of in the loop.
384 */
385 if (trim_operation->extrude_mode == ExtrudeMode::Fixed) {
386 copy_v3_v3(depth_point, shape_origin);
387 }
388 else {
389 madd_v3_v3v3fl(depth_point, shape_origin, shape_normal, depth_front);
390 }
391
392 for (const int i : screen_points.index_range()) {
393 float new_point[3];
394 if (trim_operation->orientation == OrientationType::View) {
395 ED_view3d_win_to_3d(vc.v3d, region, depth_point, screen_points[i], new_point);
396
397 /* For fixed mode we add the shape normal here to avoid projection errors. */
398 if (trim_operation->extrude_mode == ExtrudeMode::Fixed) {
399 madd_v3_v3fl(new_point, shape_normal, depth_front);
400 }
401 }
402 else {
403 ED_view3d_win_to_3d_on_plane(region, shape_plane, screen_points[i], false, new_point);
404 madd_v3_v3fl(new_point, shape_normal, depth_front);
405 }
406
407 copy_v3_v3(positions[i], new_point);
408 }
409
410 /* Write vertices coordinates for the back face. */
411 madd_v3_v3v3fl(depth_point, shape_origin, shape_normal, depth_back);
412 for (const int i : screen_points.index_range()) {
413 float new_point[3];
414
415 if (trim_operation->extrude_mode == ExtrudeMode::Project) {
416 if (trim_operation->orientation == OrientationType::View) {
417 ED_view3d_win_to_3d(vc.v3d, region, depth_point, screen_points[i], new_point);
418 }
419 else {
420 ED_view3d_win_to_3d_on_plane(region, shape_plane, screen_points[i], false, new_point);
421 madd_v3_v3fl(new_point, shape_normal, depth_back);
422 }
423 }
424 else {
425 copy_v3_v3(new_point, positions[i]);
426 float dist = dist_signed_to_plane_v3(new_point, shape_plane);
427
428 madd_v3_v3fl(new_point, shape_normal, depth_back - dist);
429 }
430
431 copy_v3_v3(positions[i + screen_points.size()], new_point);
432 }
433
434 /* Project to object space. */
435 for (int i = 0; i < screen_points.size() * 2; i++) {
436 float new_point[3];
437
438 copy_v3_v3(new_point, positions[i]);
439 mul_v3_m4v3(positions[i], ob_imat, new_point);
440 mul_v3_m4v3(trim_operation->true_mesh_co[i], ob_imat, new_point);
441 }
442
443 /* Get the triangulation for the front/back poly. */
444 const int face_tris_num = bke::mesh::face_triangles_num(screen_points.size());
445 Array<uint3> tris(face_tris_num);
446 BLI_polyfill_calc(reinterpret_cast<const float(*)[2]>(screen_points.data()),
447 screen_points.size(),
448 0,
449 reinterpret_cast<uint(*)[3]>(tris.data()));
450
451 /* Write the front face triangle indices. */
452 MutableSpan<int> face_offsets = trim_operation->mesh->face_offsets_for_write();
453 MutableSpan<int> corner_verts = trim_operation->mesh->corner_verts_for_write();
454 int face_index = 0;
455 int loop_index = 0;
456 for (const int i : tris.index_range()) {
457 face_offsets[face_index] = loop_index;
458 corner_verts[loop_index + 0] = tris[i][0];
459 corner_verts[loop_index + 1] = tris[i][1];
460 corner_verts[loop_index + 2] = tris[i][2];
461 face_index++;
462 loop_index += 3;
463 }
464
465 /* Write the back face triangle indices. */
466 for (const int i : tris.index_range()) {
467 face_offsets[face_index] = loop_index;
468 corner_verts[loop_index + 0] = tris[i][0] + screen_points.size();
469 corner_verts[loop_index + 1] = tris[i][1] + screen_points.size();
470 corner_verts[loop_index + 2] = tris[i][2] + screen_points.size();
471 face_index++;
472 loop_index += 3;
473 }
474
475 /* Write the indices for the lateral triangles. */
476 for (const int i : screen_points.index_range()) {
477 face_offsets[face_index] = loop_index;
478 int current_index = i;
479 int next_index = current_index + 1;
480 if (next_index >= screen_points.size()) {
481 next_index = 0;
482 }
483 corner_verts[loop_index + 0] = next_index + screen_points.size();
484 corner_verts[loop_index + 1] = next_index;
485 corner_verts[loop_index + 2] = current_index;
486 face_index++;
487 loop_index += 3;
488 }
489
490 for (const int i : screen_points.index_range()) {
491 face_offsets[face_index] = loop_index;
492 int current_index = i;
493 int next_index = current_index + 1;
494 if (next_index >= screen_points.size()) {
495 next_index = 0;
496 }
497 corner_verts[loop_index + 0] = current_index;
498 corner_verts[loop_index + 1] = current_index + screen_points.size();
499 corner_verts[loop_index + 2] = next_index + screen_points.size();
500 face_index++;
501 loop_index += 3;
502 }
503
504 bke::mesh_smooth_set(*trim_operation->mesh, false);
505 bke::mesh_calc_edges(*trim_operation->mesh, false, false);
506 update_normals(gesture_data);
507}
508
509static void gesture_begin(bContext &C, wmOperator &op, gesture::GestureData &gesture_data)
510{
511 const Scene &scene = *CTX_data_scene(&C);
512 Object *object = gesture_data.vc.obact;
513 SculptSession &ss = *object->sculpt;
514 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(*object);
515
516 switch (pbvh.type()) {
519 break;
520 default:
522 }
523
525 generate_geometry(gesture_data);
528 undo::geometry_begin(scene, *gesture_data.vc.obact, &op);
529}
530
531static int bm_face_isect_pair(BMFace *f, void * /*user_data*/)
532{
533 return BM_elem_flag_test(f, BM_ELEM_DRAW) ? 1 : 0;
534}
535
536static void apply_trim(gesture::GestureData &gesture_data)
537{
538 TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
539 Mesh *sculpt_mesh = BKE_mesh_from_object(gesture_data.vc.obact);
540 Mesh *trim_mesh = trim_operation->mesh;
541
542 const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(sculpt_mesh, trim_mesh);
543
544 BMeshCreateParams bm_create_params{};
545 bm_create_params.use_toolflags = false;
546 BMesh *bm = BM_mesh_create(&allocsize, &bm_create_params);
547
548 BMeshFromMeshParams bm_from_me_params{};
549 bm_from_me_params.calc_face_normal = true;
550 bm_from_me_params.calc_vert_normal = true;
551 BM_mesh_bm_from_me(bm, trim_mesh, &bm_from_me_params);
552 BM_mesh_bm_from_me(bm, sculpt_mesh, &bm_from_me_params);
553
554 const int corner_tris_tot = poly_to_tri_count(bm->totface, bm->totloop);
555 Array<std::array<BMLoop *, 3>> corner_tris(corner_tris_tot);
557
558 BMIter iter;
559 int i;
560 const int i_faces_end = trim_mesh->faces_num;
561
562 /* We need face normals because of 'BM_face_split_edgenet'
563 * we could calculate on the fly too (before calling split). */
564
565 const short ob_src_totcol = trim_mesh->totcol;
566 Array<short> material_remap(ob_src_totcol ? ob_src_totcol : 1);
567
568 BMFace *efa;
569 i = 0;
570 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
571 normalize_v3(efa->no);
572
573 /* Temp tag to test which side split faces are from. */
575
576 /* Remap material. */
577 if (efa->mat_nr < ob_src_totcol) {
578 efa->mat_nr = material_remap[efa->mat_nr];
579 }
580
581 if (++i == i_faces_end) {
582 break;
583 }
584 }
585
586 /* Join does not do a boolean operation, it just adds the geometry. */
587 if (trim_operation->mode != OperationType::Join) {
588 int boolean_mode = 0;
589 switch (trim_operation->mode) {
591 boolean_mode = eBooleanModifierOp_Intersect;
592 break;
594 boolean_mode = eBooleanModifierOp_Difference;
595 break;
597 boolean_mode = eBooleanModifierOp_Union;
598 break;
600 BLI_assert(false);
601 break;
602 }
603
604 if (trim_operation->solver_mode == SolverMode::Exact) {
606 bm, corner_tris, bm_face_isect_pair, nullptr, 2, true, true, false, boolean_mode);
607 }
608 else {
610 corner_tris,
612 nullptr,
613 false,
614 false,
615 true,
616 true,
617 false,
618 false,
619 boolean_mode,
620 1e-6f);
621 }
622 }
623
624 BMeshToMeshParams convert_params{};
625 convert_params.calc_object_remap = false;
626 Mesh *result = BKE_mesh_from_bmesh_nomain(bm, &convert_params, sculpt_mesh);
627
630 result, static_cast<Mesh *>(gesture_data.vc.obact->data), gesture_data.vc.obact);
631}
632
634{
635 TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
636 Mesh *trim_mesh = trim_operation->mesh;
637 MutableSpan<float3> positions = trim_mesh->vert_positions_for_write();
638 for (int i = 0; i < trim_mesh->verts_num; i++) {
639 positions[i] = symmetry_flip(trim_operation->true_mesh_co[i], gesture_data.symmpass);
640 }
641 update_normals(gesture_data);
642 apply_trim(gesture_data);
643}
644
645static void free_geometry(gesture::GestureData &gesture_data)
646{
647 TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
648 BKE_id_free(nullptr, trim_operation->mesh);
649 MEM_freeN(trim_operation->true_mesh_co);
650}
651
652static void gesture_end(bContext & /*C*/, gesture::GestureData &gesture_data)
653{
654 Object *object = gesture_data.vc.obact;
655 Mesh *mesh = (Mesh *)object->data;
656
657 /* Assign a new Face Set ID to the new faces created by the trim operation. */
658 const int next_face_set_id = face_set::find_next_available_id(*object);
659 face_set::initialize_none_to_id(mesh, next_face_set_id);
660
661 free_geometry(gesture_data);
662
663 undo::geometry_end(*object);
667}
668
669static void init_operation(gesture::GestureData &gesture_data, wmOperator &op)
670{
671 TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
672
673 trim_operation->op.begin = gesture_begin;
675 trim_operation->op.end = gesture_end;
676
677 trim_operation->mode = OperationType(RNA_enum_get(op.ptr, "trim_mode"));
678 trim_operation->use_cursor_depth = RNA_boolean_get(op.ptr, "use_cursor_depth");
679 trim_operation->orientation = OrientationType(RNA_enum_get(op.ptr, "trim_orientation"));
680 trim_operation->extrude_mode = ExtrudeMode(RNA_enum_get(op.ptr, "trim_extrude_mode"));
681 trim_operation->solver_mode = SolverMode(RNA_enum_get(op.ptr, "trim_solver"));
682
683 /* If the cursor was not over the mesh, force the orientation to view. */
684 if (!trim_operation->initial_hit) {
685 trim_operation->orientation = OrientationType::View;
686 }
687
688 if (gesture_data.shape_type == gesture::ShapeType::Line) {
689 /* Line gestures only support Difference, no extrusion. */
690 trim_operation->mode = OperationType::Difference;
691 }
692}
693
695{
696 PropertyRNA *prop;
697
698 prop = RNA_def_int_vector(ot->srna,
699 "location",
700 2,
701 nullptr,
702 INT_MIN,
703 INT_MAX,
704 "Location",
705 "Mouse location",
706 INT_MIN,
707 INT_MAX);
709
711 "trim_mode",
714 "Trim Mode",
715 nullptr);
717 ot->srna,
718 "use_cursor_depth",
719 false,
720 "Use Cursor for Depth",
721 "Use cursor location and radius for the dimensions and position of the trimming shape");
723 "trim_orientation",
726 "Shape Orientation",
727 nullptr);
729 "trim_extrude_mode",
732 "Extrude Mode",
733 nullptr);
734
735 RNA_def_enum(ot->srna, "trim_solver", solver_modes, int(SolverMode::Fast), "Solver", nullptr);
736}
737
738static bool can_invoke(const bContext &C)
739{
740 const View3D &v3d = *CTX_wm_view3d(&C);
741 const Base &base = *CTX_data_active_base(&C);
742 if (!BKE_base_is_visible(&v3d, &base)) {
743 return false;
744 }
745
746 return true;
747}
748
749static void report_invalid_mode(const blender::bke::pbvh::Type pbvh_type, ReportList &reports)
750{
751 if (pbvh_type == bke::pbvh::Type::BMesh) {
752 BKE_report(&reports, RPT_ERROR, "Not supported in dynamic topology mode");
753 }
754 else if (pbvh_type == bke::pbvh::Type::Grids) {
755 BKE_report(&reports, RPT_ERROR, "Not supported in multiresolution mode");
756 }
757 else {
759 }
760}
761
762static bool can_exec(const bContext &C, ReportList &reports)
763{
764 const Object &object = *CTX_data_active_object(&C);
765 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
766 if (pbvh.type() != bke::pbvh::Type::Mesh) {
767 /* Not supported in Multires and Dyntopo. */
768 report_invalid_mode(pbvh.type(), reports);
769 return false;
770 }
771
772 if (static_cast<const Mesh *>(object.data)->faces_num == 0) {
773 /* No geometry to trim or to detect a valid position for the trimming shape. */
774 return false;
775 }
776
777 return true;
778}
779
781 const wmOperator &op,
782 gesture::GestureData &gesture_data)
783{
785
787
788 int mval[2];
789 RNA_int_get_array(op.ptr, "location", mval);
790
792 const float mval_fl[2] = {float(mval[0]), float(mval[1])};
793
794 TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
795 trim_operation->initial_hit = SCULPT_cursor_geometry_info_update(&C, &sgi, mval_fl, false);
796 if (trim_operation->initial_hit) {
797 copy_v3_v3(trim_operation->initial_location, sgi.location);
798 copy_v3_v3(trim_operation->initial_normal, sgi.normal);
799 }
800}
801
803{
804 if (!can_exec(*C, *op->reports)) {
805 return OPERATOR_CANCELLED;
806 }
807
808 std::unique_ptr<gesture::GestureData> gesture_data = gesture::init_from_box(C, op);
809 if (!gesture_data) {
810 return OPERATOR_CANCELLED;
811 }
812
813 gesture_data->operation = reinterpret_cast<gesture::Operation *>(
814 MEM_cnew<TrimOperation>(__func__));
815 initialize_cursor_info(*C, *op, *gesture_data);
816 init_operation(*gesture_data, *op);
817
818 gesture::apply(*C, *gesture_data, *op);
819 return OPERATOR_FINISHED;
820}
821
822static int gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
823{
824 if (!can_invoke(*C)) {
825 return OPERATOR_CANCELLED;
826 }
827
828 RNA_int_set_array(op->ptr, "location", event->mval);
829
830 return WM_gesture_box_invoke(C, op, event);
831}
832
834{
835 if (!can_exec(*C, *op->reports)) {
836 return OPERATOR_CANCELLED;
837 }
838
839 std::unique_ptr<gesture::GestureData> gesture_data = gesture::init_from_lasso(C, op);
840 if (!gesture_data) {
841 return OPERATOR_CANCELLED;
842 }
843
844 gesture_data->operation = reinterpret_cast<gesture::Operation *>(
845 MEM_cnew<TrimOperation>(__func__));
846 initialize_cursor_info(*C, *op, *gesture_data);
847 init_operation(*gesture_data, *op);
848
849 gesture::apply(*C, *gesture_data, *op);
850 return OPERATOR_FINISHED;
851}
852
853static int gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *event)
854{
855 if (!can_invoke(*C)) {
856 return OPERATOR_CANCELLED;
857 }
858
859 RNA_int_set_array(op->ptr, "location", event->mval);
860
861 return WM_gesture_lasso_invoke(C, op, event);
862}
863
865{
866 if (!can_exec(*C, *op->reports)) {
867 return OPERATOR_CANCELLED;
868 }
869
870 std::unique_ptr<gesture::GestureData> gesture_data = gesture::init_from_line(C, op);
871 if (!gesture_data) {
872 return OPERATOR_CANCELLED;
873 }
874
875 gesture_data->operation = reinterpret_cast<gesture::Operation *>(
876 MEM_cnew<TrimOperation>(__func__));
877
878 initialize_cursor_info(*C, *op, *gesture_data);
879 init_operation(*gesture_data, *op);
880 gesture::apply(*C, *gesture_data, *op);
881 return OPERATOR_FINISHED;
882}
883
884static int gesture_line_invoke(bContext *C, wmOperator *op, const wmEvent *event)
885{
886 if (!can_invoke(*C)) {
887 return OPERATOR_CANCELLED;
888 }
889
890 RNA_int_set_array(op->ptr, "location", event->mval);
891
893}
894
896{
897 if (!can_exec(*C, *op->reports)) {
898 return OPERATOR_CANCELLED;
899 }
900
901 std::unique_ptr<gesture::GestureData> gesture_data = gesture::init_from_polyline(C, op);
902 if (!gesture_data) {
903 return OPERATOR_CANCELLED;
904 }
905
906 gesture_data->operation = reinterpret_cast<gesture::Operation *>(
907 MEM_cnew<TrimOperation>(__func__));
908 initialize_cursor_info(*C, *op, *gesture_data);
909 init_operation(*gesture_data, *op);
910
911 gesture::apply(*C, *gesture_data, *op);
912 return OPERATOR_FINISHED;
913}
914
915static int gesture_polyline_invoke(bContext *C, wmOperator *op, const wmEvent *event)
916{
917 if (!can_invoke(*C)) {
918 return OPERATOR_CANCELLED;
919 }
920
921 RNA_int_set_array(op->ptr, "location", event->mval);
922
923 return WM_gesture_polyline_invoke(C, op, event);
924}
925
927{
928 ot->name = "Trim Lasso Gesture";
929 ot->idname = "SCULPT_OT_trim_lasso_gesture";
930 ot->description = "Execute a boolean operation on the mesh and a shape defined by the cursor";
931
935
937
939
940 /* Properties. */
943
945}
946
948{
949 ot->name = "Trim Box Gesture";
950 ot->idname = "SCULPT_OT_trim_box_gesture";
951 ot->description =
952 "Execute a boolean operation on the mesh and a rectangle defined by the cursor";
953
957
959
961
962 /* Properties. */
965
967}
968
970{
971 ot->name = "Trim Line Gesture";
972 ot->idname = "SCULPT_OT_trim_line_gesture";
973 ot->description = "Remove a portion of the mesh on one side of a line";
974
978
980
982
983 /* Properties. */
986
988}
989
991{
992 ot->name = "Trim Polyline Gesture";
993 ot->idname = "SCULPT_OT_trim_polyline_gesture";
994 ot->description =
995 "Execute a boolean operation on the mesh and a polygonal shape defined by the cursor";
996
1000
1002
1004
1005 /* Properties. */
1008
1010}
1011} // namespace blender::ed::sculpt_paint::trim
float BKE_brush_unprojected_radius_get(const Scene *scene, const Brush *brush)
Definition brush.cc:1133
int BKE_brush_size_get(const Scene *scene, const Brush *brush)
Definition brush.cc:1075
bool BKE_brush_use_locked_size(const Scene *scene, const Brush *brush)
Definition brush.cc:1083
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Base * CTX_data_active_base(const bContext *C)
ToolSettings * CTX_data_tool_settings(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
bool BKE_base_is_visible(const View3D *v3d, const Base *base)
void BKE_id_free(Main *bmain, void *idv)
void BKE_mesh_batch_cache_dirty_tag(Mesh *mesh, eMeshBatchDirtyMode mode)
Mesh * BKE_mesh_from_bmesh_nomain(BMesh *bm, const BMeshToMeshParams *params, const Mesh *me_settings)
Mesh * BKE_mesh_new_nomain(int verts_num, int edges_num, int faces_num, int corners_num)
void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, Mesh *mesh_dst, Object *ob)
Mesh * BKE_mesh_from_object(Object *ob)
@ BKE_MESH_BATCH_DIRTY_ALL
Definition BKE_mesh.h:38
General operations, lookup, etc. for blender objects.
std::optional< blender::Bounds< blender::float3 > > BKE_object_boundbox_get(const Object *ob)
void BKE_sculpt_update_object_for_edit(Depsgraph *depsgraph, Object *ob_orig, bool is_paint_tool)
Definition paint.cc:2601
void BKE_sculptsession_free_pbvh(Object &object)
Definition paint.cc:2099
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:649
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
void plane_from_point_normal_v3(float r_plane[4], const float plane_co[3], const float plane_no[3])
Definition math_geom.cc:215
float dist_signed_to_plane_v3(const float p[3], const float plane[4])
Definition math_geom.cc:493
MINLINE int poly_to_tri_count(int poly_count, int corner_count)
void mul_v3_m4v3(float r[3], const float mat[4][4], const float vec[3])
void mul_mat3_m4_v3(const float mat[4][4], float r[3])
MINLINE void madd_v3_v3fl(float r[3], const float a[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void negate_v3(float r[3])
MINLINE void madd_v3_v3v3fl(float r[3], const float a[3], const float b[3], float f)
MINLINE float normalize_v3(float n[3])
void BLI_polyfill_calc(const float(*coords)[2], unsigned int coords_num, int coords_sign, unsigned int(*r_tris)[3])
unsigned int uint
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
@ eBooleanModifierOp_Intersect
@ eBooleanModifierOp_Union
@ eBooleanModifierOp_Difference
void ED_view3d_win_to_3d(const View3D *v3d, const ARegion *region, const float depth_pt[3], const float mval[2], float r_out[3])
bool ED_view3d_win_to_3d_on_plane(const ARegion *region, const float plane[4], const float mval[2], bool do_clip, float r_out[3])
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
@ PROP_HIDDEN
Definition RNA_types.hh:239
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:198
@ OPTYPE_REGISTER
Definition WM_types.hh:160
bool BM_mesh_boolean(BMesh *, blender::Span< std::array< BMLoop *, 3 > > looptris, int(*test_fn)(BMFace *, void *), void *, const int, const bool, const bool, const bool, const int)
@ BM_ELEM_TAG
@ BM_ELEM_DRAW
#define BM_elem_flag_test(ele, hflag)
#define BM_elem_flag_enable(ele, hflag)
bool BM_mesh_intersect(BMesh *bm, const blender::Span< std::array< BMLoop *, 3 > > looptris, int(*test_fn)(BMFace *f, void *user_data), void *user_data, const bool use_self, const bool use_separate, const bool use_dissolve, const bool use_island_connect, const bool use_partial_connect, const bool use_edge_tag, const int boolean_mode, const float eps)
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_FACES_OF_MESH
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BM_mesh_elem_hflag_enable_all(BMesh *bm, const char htype, const char hflag, const bool respecthide)
void BM_mesh_elem_hflag_disable_all(BMesh *bm, const char htype, const char hflag, const bool respecthide)
void BM_mesh_free(BMesh *bm)
BMesh Free Mesh.
BMesh * BM_mesh_create(const BMAllocTemplate *allocsize, const BMeshCreateParams *params)
BMesh Make Mesh.
#define BMALLOC_TEMPLATE_FROM_ME(...)
void BM_mesh_bm_from_me(BMesh *bm, const Mesh *mesh, const BMeshFromMeshParams *params)
void BM_mesh_calc_tessellation_beauty(BMesh *bm, MutableSpan< std::array< BMLoop *, 3 > > looptris)
#define BM_FACE
#define BM_EDGE
#define BM_VERT
bool BMO_op_callf(BMesh *bm, int flag, const char *fmt,...)
#define BMO_FLAG_DEFAULTS
@ BMO_FLAG_RESPECT_HIDE
int64_t size() const
Definition BLI_array.hh:245
const T * data() const
Definition BLI_array.hh:301
IndexRange index_range() const
Definition BLI_array.hh:349
const Depsgraph * depsgraph
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition mallocn.cc:45
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
int face_triangles_num(const int face_size)
Definition BKE_mesh.hh:287
pbvh::Tree * pbvh_get(Object &object)
Definition paint.cc:2846
Span< float3 > vert_positions_eval(const Depsgraph &depsgraph, const Object &object_orig)
Definition pbvh.cc:2482
void mesh_smooth_set(Mesh &mesh, bool use_smooth, bool keep_sharp_edges=false)
void mesh_calc_edges(Mesh &mesh, bool keep_existing_edges, bool select_new_edges)
void initialize_none_to_id(Mesh *mesh, int new_id)
std::unique_ptr< GestureData > init_from_box(bContext *C, wmOperator *op)
void operator_properties(wmOperatorType *ot, ShapeType shapeType)
std::unique_ptr< GestureData > init_from_polyline(bContext *C, wmOperator *op)
std::unique_ptr< GestureData > init_from_line(bContext *C, wmOperator *op)
void apply(bContext &C, GestureData &gesture_data, wmOperator &op)
std::unique_ptr< GestureData > init_from_lasso(bContext *C, wmOperator *op)
void invalidate(SculptSession &ss)
Definition sculpt.cc:5895
void SCULPT_OT_trim_lasso_gesture(wmOperatorType *ot)
static int gesture_box_exec(bContext *C, wmOperator *op)
static EnumPropertyItem solver_modes[]
static void init_operation(gesture::GestureData &gesture_data, wmOperator &op)
static void operator_properties(wmOperatorType *ot)
static void generate_geometry(gesture::GestureData &gesture_data)
static void initialize_cursor_info(bContext &C, const wmOperator &op, gesture::GestureData &gesture_data)
static EnumPropertyItem extrude_modes[]
static void get_origin_and_normal(gesture::GestureData &gesture_data, float *r_origin, float *r_normal)
static void gesture_end(bContext &, gesture::GestureData &gesture_data)
static void gesture_begin(bContext &C, wmOperator &op, gesture::GestureData &gesture_data)
static void apply_trim(gesture::GestureData &gesture_data)
static int gesture_polyline_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static int bm_face_isect_pair(BMFace *f, void *)
static bool can_exec(const bContext &C, ReportList &reports)
void SCULPT_OT_trim_box_gesture(wmOperatorType *ot)
static float calc_expand_factor(const gesture::GestureData &gesture_data)
static int gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void report_invalid_mode(const blender::bke::pbvh::Type pbvh_type, ReportList &reports)
static void gesture_apply_for_symmetry_pass(bContext &, gesture::GestureData &gesture_data)
static int gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static bool can_invoke(const bContext &C)
static void calculate_depth(gesture::GestureData &gesture_data, float &r_depth_front, float &r_depth_back)
static int gesture_line_exec(bContext *C, wmOperator *op)
static void free_geometry(gesture::GestureData &gesture_data)
static EnumPropertyItem operation_types[]
static int gesture_line_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void SCULPT_OT_trim_line_gesture(wmOperatorType *ot)
static EnumPropertyItem orientation_types[]
static int gesture_polyline_exec(bContext *C, wmOperator *op)
static int gesture_lasso_exec(bContext *C, wmOperator *op)
void SCULPT_OT_trim_polyline_gesture(wmOperatorType *ot)
static void update_normals(gesture::GestureData &gesture_data)
static Array< float2 > gesture_to_screen_points(gesture::GestureData &gesture_data)
void geometry_begin(const Scene &scene, Object &ob, const wmOperator *op)
float3 symmetry_flip(const float3 &src, const ePaintSymmetryFlags symm)
T distance(const T &a, const T &b)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
bool paint_convert_bb_to_rect(rcti *rect, const float bb_min[3], const float bb_max[3], const ARegion &region, const RegionView3D &rv3d, const Object &ob)
float paint_calc_object_space_radius(const ViewContext &vc, const blender::float3 &center, float pixel_radius)
void RNA_int_set_array(PointerRNA *ptr, const char *name, const int *values)
void RNA_int_get_array(PointerRNA *ptr, const char *name, int *values)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_int_vector(StructOrFunctionRNA *cont_, const char *identifier, const int len, const int *default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
bool SCULPT_cursor_geometry_info_update(bContext *C, SculptCursorGeometryInfo *out, const float mval[2], bool use_sampled_normal)
Definition sculpt.cc:4580
void SCULPT_vertex_random_access_ensure(Object &object)
Definition sculpt.cc:144
bool SCULPT_mode_poll_view3d(bContext *C)
Definition sculpt.cc:3566
#define FLT_MAX
Definition stdcycles.h:14
int totloop
int totface
short totcol
int faces_num
int verts_num
float cursor_radius
Definition BKE_paint.hh:439
RegionView3D * rv3d
Definition ED_view3d.hh:76
ARegion * region
Definition ED_view3d.hh:73
bContext * C
Definition ED_view3d.hh:62
View3D * v3d
Definition ED_view3d.hh:74
Object * obact
Definition ED_view3d.hh:71
Depsgraph * depsgraph
Definition ED_view3d.hh:68
const c_style_mat & ptr() const
void(* end)(bContext &, GestureData &)
void(* begin)(bContext &, wmOperator &, GestureData &)
void(* apply_for_symmetry_pass)(bContext &, GestureData &)
int ymin
int ymax
int xmin
int xmax
int mval[2]
Definition WM_types.hh:728
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1036
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
StructRNA * srna
Definition WM_types.hh:1080
struct ReportList * reports
struct PointerRNA * ptr
@ WM_CURSOR_EDIT
Definition wm_cursors.hh:19
wmOperatorType * ot
Definition wm_files.cc:4125
int WM_gesture_polyline_invoke(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_polyline_modal(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_straightline_oneshot_modal(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_straightline_active_side_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void WM_operator_properties_gesture_straightline(wmOperatorType *ot, int cursor)
void WM_operator_properties_border(wmOperatorType *ot)
void WM_operator_properties_gesture_lasso(wmOperatorType *ot)
void WM_operator_properties_gesture_polyline(wmOperatorType *ot)