Blender V5.0
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
8
10#include "GEO_mesh_boolean.hh"
11
12#include "BLI_math_geom.h"
13#include "BLI_math_matrix.h"
14#include "BLI_math_matrix.hh"
15#include "BLI_math_vector.h"
16#include "BLI_math_vector.hh"
17#include "BLI_polyfill_2d.h"
18
19#include "BKE_brush.hh"
20#include "BKE_context.hh"
21#include "BKE_layer.hh"
22#include "BKE_lib_id.hh"
23#include "BKE_mesh.hh"
24#include "BKE_object.hh"
25#include "BKE_report.hh"
26
27#include "DEG_depsgraph.hh"
28
29#include "ED_sculpt.hh"
30
31#include "RNA_access.hh"
32#include "RNA_define.hh"
33
34#include "WM_api.hh"
35#include "WM_types.hh"
36
37#include "bmesh.hh"
38
39#include "paint_intern.hh"
40#include "sculpt_face_set.hh"
41#include "sculpt_gesture.hh"
42#include "sculpt_intern.hh"
43#include "sculpt_islands.hh"
44
46
47enum class OperationType {
50 Union = 2,
51 Join = 3,
52};
53
54/* Intersect is not exposed in the UI because it does not work correctly with symmetry (it deletes
55 * the symmetrical part of the mesh in the first symmetry pass). */
58 "DIFFERENCE",
59 0,
60 "Difference",
61 "Use a difference boolean operation"},
62 {int(OperationType::Union), "UNION", 0, "Union", "Use a union boolean operation"},
64 "JOIN",
65 0,
66 "Join",
67 "Join the new mesh as separate geometry, without performing any boolean operation"},
68 {0, nullptr, 0, nullptr, nullptr},
69};
70
71enum class OrientationType {
72 View = 0,
74};
77 "VIEW",
78 0,
79 "View",
80 "Use the view to orientate the trimming shape"},
82 "SURFACE",
83 0,
84 "Surface",
85 "Use the surface normal to orientate the trimming shape"},
86 {0, nullptr, 0, nullptr, nullptr},
87};
88
89enum class ExtrudeMode {
91 Fixed = 1,
92};
93
96 "PROJECT",
97 0,
98 "Project",
99 "Align trim geometry with the perspective of the current view for a tapered shape"},
100 {int(ExtrudeMode::Fixed),
101 "FIXED",
102 0,
103 "Fixed",
104 "Align trim geometry orthogonally for a shape with 90 degree angles"},
105 {0, nullptr, 0, nullptr, nullptr},
106};
107
110 "EXACT",
111 0,
112 "Exact",
113 "Slower solver with the best results for coplanar faces"},
115 "FLOAT",
116 0,
117 "Float",
118 "Simple solver with good performance, without support for overlapping geometry"},
120 "MANIFOLD",
121 0,
122 "Manifold",
123 "Fastest solver that works only on manifold meshes but gives better results"},
124 {0, nullptr, 0, nullptr, nullptr},
125};
126
147
148/* Recalculate the mesh normals for the generated trim mesh. */
149static void update_normals(gesture::GestureData &gesture_data)
150{
151 TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
152 Mesh *trim_mesh = trim_operation->mesh;
153
154 const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(trim_mesh);
155
156 BMeshCreateParams bm_create_params{};
157 bm_create_params.use_toolflags = true;
158 BMesh *bm = BM_mesh_create(&allocsize, &bm_create_params);
159
160 BMeshFromMeshParams bm_from_me_params{};
161 bm_from_me_params.calc_face_normal = true;
162 bm_from_me_params.calc_vert_normal = true;
163 BM_mesh_bm_from_me(bm, trim_mesh, &bm_from_me_params);
164
168 "recalc_face_normals faces=%hf",
171
172 BMeshToMeshParams convert_params{};
173 convert_params.calc_object_remap = false;
174 Mesh *result = BKE_mesh_from_bmesh_nomain(bm, &convert_params, trim_mesh);
175
177 BKE_id_free(nullptr, trim_mesh);
178 trim_operation->mesh = result;
179}
180
181/* Get the origin and normal that are going to be used for calculating the depth and position of
182 * the trimming geometry. */
184 float *r_origin,
185 float *r_normal)
186{
187 TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
188 /* Use the view origin and normal in world space. The trimming mesh coordinates are
189 * calculated in world space, aligned to the view, and then converted to object space to
190 * store them in the final trimming mesh which is going to be used in the boolean operation.
191 */
192 switch (trim_operation->orientation) {
194 mul_v3_m4v3(r_origin,
195 gesture_data.vc.obact->object_to_world().ptr(),
196 trim_operation->initial_location);
197 copy_v3_v3(r_normal, gesture_data.world_space_view_normal);
198 negate_v3(r_normal);
199 break;
201 mul_v3_m4v3(r_origin,
202 gesture_data.vc.obact->object_to_world().ptr(),
203 trim_operation->initial_location);
204 /* Transforming the normal does not take non uniform scaling into account. Sculpt mode is not
205 * expected to work on object with non uniform scaling. */
206 copy_v3_v3(r_normal, trim_operation->initial_normal);
207 mul_mat3_m4_v3(gesture_data.vc.obact->object_to_world().ptr(), r_normal);
208 break;
209 }
210}
211
212/* Calculates the depth of the drawn shape inside the scene. */
213static void calculate_depth(gesture::GestureData &gesture_data,
214 float &r_depth_front,
215 float &r_depth_back)
216{
217 TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
218
219 SculptSession &ss = *gesture_data.ss;
220 ViewContext &vc = gesture_data.vc;
221
222 float shape_plane[4];
223 float shape_origin[3];
224 float shape_normal[3];
225 get_origin_and_normal(gesture_data, shape_origin, shape_normal);
226 plane_from_point_normal_v3(shape_plane, shape_origin, shape_normal);
227
228 float depth_front = FLT_MAX;
229 float depth_back = -FLT_MAX;
230
232 const float4x4 &object_to_world = vc.obact->object_to_world();
233
234 for (const int i : positions.index_range()) {
235 /* Convert the coordinates to world space to calculate the depth. When generating the trimming
236 * mesh, coordinates are first calculated in world space, then converted to object space to
237 * store them. */
238 const float3 world_space_vco = math::transform_point(object_to_world, positions[i]);
239 const float dist = dist_signed_to_plane_v3(world_space_vco, shape_plane);
240 depth_front = std::min(dist, depth_front);
241 depth_back = std::max(dist, depth_back);
242 }
243
244 if (trim_operation->use_cursor_depth) {
245 float world_space_gesture_initial_location[3];
246 mul_v3_m4v3(world_space_gesture_initial_location,
247 object_to_world.ptr(),
248 trim_operation->initial_location);
249
250 float mid_point_depth;
251 if (trim_operation->orientation == OrientationType::View) {
252 mid_point_depth = trim_operation->initial_hit ?
253 dist_signed_to_plane_v3(world_space_gesture_initial_location,
254 shape_plane) :
255 (depth_back + depth_front) * 0.5f;
256 }
257 else {
258 /* When using normal orientation, if the stroke started over the mesh, position the mid point
259 * at 0 distance from the shape plane. This positions the trimming shape half inside of the
260 * surface. */
261 mid_point_depth = trim_operation->initial_hit ? 0.0f : (depth_back + depth_front) * 0.5f;
262 }
263
264 float depth_radius;
265
266 if (trim_operation->initial_hit) {
267 depth_radius = ss.cursor_radius;
268 }
269 else {
270 /* ss.cursor_radius is only valid if the stroke started
271 * over the sculpt mesh. If it's not we must
272 * compute the radius ourselves. See #81452.
273 */
274
276 Brush *brush = BKE_paint_brush(&sd->paint);
277
278 depth_radius = object_space_radius_get(
279 vc, sd->paint, *brush, trim_operation->initial_location);
280 }
281
282 depth_front = mid_point_depth - depth_radius;
283 depth_back = mid_point_depth + depth_radius;
284 }
285
286 r_depth_front = depth_front;
287 r_depth_back = depth_back;
288}
289
290/* Calculates a scalar factor to use to ensure a drawn line gesture
291 * encompasses the entire object to be acted on. */
292static float calc_expand_factor(const gesture::GestureData &gesture_data)
293{
294 Object &object = *gesture_data.vc.obact;
295
296 rcti rect;
299 &rect, bounds.min, bounds.max, *gesture_data.vc.region, *gesture_data.vc.rv3d, object);
300
301 const float2 min_corner(rect.xmin, rect.ymin);
302 const float2 max_corner(rect.xmax, rect.ymax);
303
304 /* Multiply the screen space bounds by an arbitrary factor to ensure the created points are
305 * sufficiently far and enclose the mesh to be operated on. */
306 return math::distance(min_corner, max_corner) * 2.0f;
307}
308
309/* Converts a line gesture's points into usable screen points. */
311{
312 if (gesture_data.shape_type != gesture::ShapeType::Line) {
313 return gesture_data.gesture_points;
314 }
315
316 const float expand_factor = calc_expand_factor(gesture_data);
317
318 float2 start(gesture_data.gesture_points[0]);
319 float2 end(gesture_data.gesture_points[1]);
320
321 const float2 dir = math::normalize(end - start);
322
323 if (!gesture_data.line.use_side_planes) {
324 end = end + dir * expand_factor;
325 start = start - dir * expand_factor;
326 }
327
328 float2 perp(dir.y, -dir.x);
329
330 if (gesture_data.line.flip) {
331 perp *= -1;
332 }
333
334 const float2 parallel_start = start + perp * expand_factor;
335 const float2 parallel_end = end + perp * expand_factor;
336
337 return {start, end, parallel_end, parallel_start};
338}
339
340static void generate_geometry(gesture::GestureData &gesture_data)
341{
342 TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
343 ViewContext &vc = gesture_data.vc;
344 ARegion *region = vc.region;
345
346 const Array<float2> screen_points = gesture_to_screen_points(gesture_data);
347 BLI_assert(screen_points.size() > 1);
348
349 const int trim_totverts = screen_points.size() * 2;
350 const int trim_faces_nums = (2 * (screen_points.size() - 2)) + (2 * screen_points.size());
351 trim_operation->mesh = BKE_mesh_new_nomain(
352 trim_totverts, 0, trim_faces_nums, trim_faces_nums * 3);
353 trim_operation->true_mesh_co = MEM_malloc_arrayN<float[3]>(trim_totverts, "mesh orco");
354
355 float shape_origin[3];
356 float shape_normal[3];
357 float shape_plane[4];
358 get_origin_and_normal(gesture_data, shape_origin, shape_normal);
359 plane_from_point_normal_v3(shape_plane, shape_origin, shape_normal);
360
361 const float (*ob_imat)[4] = vc.obact->world_to_object().ptr();
362
363 /* Write vertices coordinates OperationType::Difference for the front face. */
364 MutableSpan<float3> positions = trim_operation->mesh->vert_positions_for_write();
365
366 float depth_front;
367 float depth_back;
368 calculate_depth(gesture_data, depth_front, depth_back);
369
370 if (!trim_operation->use_cursor_depth) {
371 float pad_factor = (depth_back - depth_front) * 0.01f + 0.001f;
372
373 /* When using cursor depth, don't modify the depth set by the cursor radius. If full depth is
374 * used, adding a little padding to the trimming shape can help avoiding booleans with coplanar
375 * faces. */
376 depth_front -= pad_factor;
377 depth_back += pad_factor;
378 }
379
380 float depth_point[3];
381
382 /* Get origin point for OrientationType::View.
383 * NOTE: for projection extrusion we add depth_front here
384 * instead of in the loop.
385 */
386 if (trim_operation->extrude_mode == ExtrudeMode::Fixed) {
387 copy_v3_v3(depth_point, shape_origin);
388 }
389 else {
390 madd_v3_v3v3fl(depth_point, shape_origin, shape_normal, depth_front);
391 }
392
393 for (const int i : screen_points.index_range()) {
394 float new_point[3];
395 if (trim_operation->orientation == OrientationType::View) {
396 ED_view3d_win_to_3d(vc.v3d, region, depth_point, screen_points[i], new_point);
397
398 /* For fixed mode we add the shape normal here to avoid projection errors. */
399 if (trim_operation->extrude_mode == ExtrudeMode::Fixed) {
400 madd_v3_v3fl(new_point, shape_normal, depth_front);
401 }
402 }
403 else {
404 ED_view3d_win_to_3d_on_plane(region, shape_plane, screen_points[i], false, new_point);
405 madd_v3_v3fl(new_point, shape_normal, depth_front);
406 }
407
408 copy_v3_v3(positions[i], new_point);
409 }
410
411 /* Write vertices coordinates for the back face. */
412 madd_v3_v3v3fl(depth_point, shape_origin, shape_normal, depth_back);
413 for (const int i : screen_points.index_range()) {
414 float new_point[3];
415
416 if (trim_operation->extrude_mode == ExtrudeMode::Project) {
417 if (trim_operation->orientation == OrientationType::View) {
418 ED_view3d_win_to_3d(vc.v3d, region, depth_point, screen_points[i], new_point);
419 }
420 else {
421 ED_view3d_win_to_3d_on_plane(region, shape_plane, screen_points[i], false, new_point);
422 madd_v3_v3fl(new_point, shape_normal, depth_back);
423 }
424 }
425 else {
426 copy_v3_v3(new_point, positions[i]);
427 float dist = dist_signed_to_plane_v3(new_point, shape_plane);
428
429 madd_v3_v3fl(new_point, shape_normal, depth_back - dist);
430 }
431
432 copy_v3_v3(positions[i + screen_points.size()], new_point);
433 }
434
435 /* Project to object space. */
436 for (int i = 0; i < screen_points.size() * 2; i++) {
437 float new_point[3];
438
439 copy_v3_v3(new_point, positions[i]);
440 mul_v3_m4v3(positions[i], ob_imat, new_point);
441 mul_v3_m4v3(trim_operation->true_mesh_co[i], ob_imat, new_point);
442 }
443
444 /* Get the triangulation for the front/back poly. */
445 const int face_tris_num = bke::mesh::face_triangles_num(screen_points.size());
446 Array<uint3> tris(face_tris_num);
447 BLI_polyfill_calc(reinterpret_cast<const float (*)[2]>(screen_points.data()),
448 screen_points.size(),
449 0,
450 reinterpret_cast<uint(*)[3]>(tris.data()));
451
452 /* Write the front face triangle indices. */
453 MutableSpan<int> face_offsets = trim_operation->mesh->face_offsets_for_write();
454 MutableSpan<int> corner_verts = trim_operation->mesh->corner_verts_for_write();
455 int face_index = 0;
456 int corner = 0;
457 for (const int i : tris.index_range()) {
458 face_offsets[face_index] = corner;
459 corner_verts[corner + 0] = tris[i][0];
460 corner_verts[corner + 1] = tris[i][1];
461 corner_verts[corner + 2] = tris[i][2];
462 face_index++;
463 corner += 3;
464 }
465
466 /* Write the back face triangle indices. */
467 for (const int i : tris.index_range()) {
468 face_offsets[face_index] = corner;
469 corner_verts[corner + 0] = tris[i][0] + screen_points.size();
470 corner_verts[corner + 1] = tris[i][1] + screen_points.size();
471 corner_verts[corner + 2] = tris[i][2] + screen_points.size();
472 face_index++;
473 corner += 3;
474 }
475
476 /* Write the indices for the lateral triangles. */
477 for (const int i : screen_points.index_range()) {
478 face_offsets[face_index] = corner;
479 int current_index = i;
480 int next_index = current_index + 1;
481 if (next_index >= screen_points.size()) {
482 next_index = 0;
483 }
484 corner_verts[corner + 0] = next_index + screen_points.size();
485 corner_verts[corner + 1] = next_index;
486 corner_verts[corner + 2] = current_index;
487 face_index++;
488 corner += 3;
489 }
490
491 for (const int i : screen_points.index_range()) {
492 face_offsets[face_index] = corner;
493 int current_index = i;
494 int next_index = current_index + 1;
495 if (next_index >= screen_points.size()) {
496 next_index = 0;
497 }
498 corner_verts[corner + 0] = current_index;
499 corner_verts[corner + 1] = current_index + screen_points.size();
500 corner_verts[corner + 2] = next_index + screen_points.size();
501 face_index++;
502 corner += 3;
503 }
504
505 bke::mesh_smooth_set(*trim_operation->mesh, false);
506 bke::mesh_calc_edges(*trim_operation->mesh, false, false);
507 update_normals(gesture_data);
508}
509
510static void gesture_begin(bContext &C, wmOperator &op, gesture::GestureData &gesture_data)
511{
512 const Scene &scene = *CTX_data_scene(&C);
513 Object *object = gesture_data.vc.obact;
514 SculptSession &ss = *object->sculpt;
515 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(*object);
516
517 switch (pbvh.type()) {
520 break;
521 default:
523 }
524
525 generate_geometry(gesture_data);
527 undo::geometry_begin(scene, *gesture_data.vc.obact, &op);
528}
529
530static void apply_join_operation(Object &object, Mesh &sculpt_mesh, Mesh &trim_mesh)
531{
535 {});
537 BKE_mesh_nomain_to_mesh(result, &sculpt_mesh, &object);
538}
539
540static void apply_trim(gesture::GestureData &gesture_data)
541{
542 TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
543 Object *object = gesture_data.vc.obact;
544 Mesh &sculpt_mesh = *static_cast<Mesh *>(object->data);
545 Mesh &trim_mesh = *trim_operation->mesh;
546
548 switch (trim_operation->mode) {
551 break;
554 break;
557 break;
559 apply_join_operation(*object, sculpt_mesh, trim_mesh);
560 return;
561 }
562
564 op_params.boolean_mode = boolean_op;
565 op_params.no_self_intersections = true;
566 op_params.watertight = false;
567 op_params.no_nested_components = true;
569 Mesh *result = geometry::boolean::mesh_boolean({&sculpt_mesh, &trim_mesh},
572 op_params,
573 trim_operation->solver_mode,
574 nullptr,
575 &error);
577 BKE_report(trim_operation->reports, RPT_ERROR, "Solver requires a manifold mesh");
578 return;
579 }
582 trim_operation->reports, RPT_ERROR, "Boolean result is too big for solver to handle");
583 return;
584 }
587 trim_operation->reports, RPT_ERROR, "Boolean solver not available (compiled without it)");
588 return;
589 }
591 BKE_report(trim_operation->reports, RPT_ERROR, "Unknown boolean error");
592 return;
593 }
594
595 BKE_mesh_nomain_to_mesh(result, &sculpt_mesh, object);
596}
597
599{
600 TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
601 Mesh *trim_mesh = trim_operation->mesh;
602 MutableSpan<float3> positions = trim_mesh->vert_positions_for_write();
603 for (int i = 0; i < trim_mesh->verts_num; i++) {
604 positions[i] = symmetry_flip(trim_operation->true_mesh_co[i], gesture_data.symmpass);
605 }
606 update_normals(gesture_data);
607 apply_trim(gesture_data);
608}
609
610static void free_geometry(gesture::GestureData &gesture_data)
611{
612 TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
613 BKE_id_free(nullptr, trim_operation->mesh);
614 MEM_freeN(trim_operation->true_mesh_co);
615}
616
617static void gesture_end(bContext & /*C*/, gesture::GestureData &gesture_data)
618{
619 Object *object = gesture_data.vc.obact;
620 Mesh *mesh = (Mesh *)object->data;
621
622 /* Assign a new Face Set ID to the new faces created by the trim operation. */
623 const int next_face_set_id = face_set::find_next_available_id(*object);
624 face_set::initialize_none_to_id(mesh, next_face_set_id);
625
626 free_geometry(gesture_data);
627
628 undo::geometry_end(*object);
632}
633
634static void init_operation(gesture::GestureData &gesture_data, wmOperator &op)
635{
636 TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
637 trim_operation->reports = op.reports;
638 trim_operation->op.begin = gesture_begin;
640 trim_operation->op.end = gesture_end;
641
642 trim_operation->mode = OperationType(RNA_enum_get(op.ptr, "trim_mode"));
643 trim_operation->use_cursor_depth = RNA_boolean_get(op.ptr, "use_cursor_depth");
644 trim_operation->orientation = OrientationType(RNA_enum_get(op.ptr, "trim_orientation"));
645 trim_operation->extrude_mode = ExtrudeMode(RNA_enum_get(op.ptr, "trim_extrude_mode"));
646 trim_operation->solver_mode = geometry::boolean::Solver(RNA_enum_get(op.ptr, "trim_solver"));
647
648 /* If the cursor was not over the mesh, force the orientation to view. */
649 if (!trim_operation->initial_hit) {
650 trim_operation->orientation = OrientationType::View;
651 }
652
653 if (gesture_data.shape_type == gesture::ShapeType::Line) {
654 /* Line gestures only support Difference, no extrusion. */
655 trim_operation->mode = OperationType::Difference;
656 }
657}
658
660{
661 PropertyRNA *prop;
662
663 prop = RNA_def_int_vector(ot->srna,
664 "location",
665 2,
666 nullptr,
667 INT_MIN,
668 INT_MAX,
669 "Location",
670 "Mouse location",
671 INT_MIN,
672 INT_MAX);
674
675 RNA_def_enum(ot->srna,
676 "trim_mode",
679 "Trim Mode",
680 nullptr);
682 ot->srna,
683 "use_cursor_depth",
684 false,
685 "Use Cursor for Depth",
686 "Use cursor location and radius for the dimensions and position of the trimming shape");
687 RNA_def_enum(ot->srna,
688 "trim_orientation",
691 "Shape Orientation",
692 nullptr);
693 RNA_def_enum(ot->srna,
694 "trim_extrude_mode",
697 "Extrude Mode",
698 nullptr);
699
700 RNA_def_enum(ot->srna,
701 "trim_solver",
704 "Solver",
705 nullptr);
706}
707
708static bool can_invoke(const bContext &C)
709{
710 const View3D &v3d = *CTX_wm_view3d(&C);
711 const Base &base = *CTX_data_active_base(&C);
712 if (!BKE_base_is_visible(&v3d, &base)) {
713 return false;
714 }
715
716 return true;
717}
718
719static void report_invalid_mode(const blender::bke::pbvh::Type pbvh_type, ReportList &reports)
720{
721 if (pbvh_type == bke::pbvh::Type::BMesh) {
722 BKE_report(&reports, RPT_ERROR, "Not supported in dynamic topology mode");
723 }
724 else if (pbvh_type == bke::pbvh::Type::Grids) {
725 BKE_report(&reports, RPT_ERROR, "Not supported in multiresolution mode");
726 }
727 else {
729 }
730}
731
732static bool can_exec(const bContext &C, ReportList &reports)
733{
734 const Object &object = *CTX_data_active_object(&C);
735 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
736 if (pbvh.type() != bke::pbvh::Type::Mesh) {
737 /* Not supported in Multires and Dyntopo. */
738 report_invalid_mode(pbvh.type(), reports);
739 return false;
740 }
741
742 if (static_cast<const Mesh *>(object.data)->faces_num == 0) {
743 /* No geometry to trim or to detect a valid position for the trimming shape. */
744 return false;
745 }
746
747 return true;
748}
749
751 const wmOperator &op,
752 gesture::GestureData &gesture_data)
753{
755
757
758 int mval[2];
759 RNA_int_get_array(op.ptr, "location", mval);
760
762 const float mval_fl[2] = {float(mval[0]), float(mval[1])};
763
764 TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
765 trim_operation->initial_hit = cursor_geometry_info_update(&C, &cgi, mval_fl, false);
766 if (trim_operation->initial_hit) {
767 copy_v3_v3(trim_operation->initial_location, cgi.location);
768 copy_v3_v3(trim_operation->initial_normal, cgi.normal);
769 }
770}
771
773{
774 if (!can_exec(*C, *op->reports)) {
775 return OPERATOR_CANCELLED;
776 }
777
778 std::unique_ptr<gesture::GestureData> gesture_data = gesture::init_from_box(C, op);
779 if (!gesture_data) {
780 return OPERATOR_CANCELLED;
781 }
782
783 gesture_data->operation = reinterpret_cast<gesture::Operation *>(
785 initialize_cursor_info(*C, *op, *gesture_data);
786 init_operation(*gesture_data, *op);
787
788 gesture::apply(*C, *gesture_data, *op);
789 return OPERATOR_FINISHED;
790}
791
793{
794 if (!can_invoke(*C)) {
795 return OPERATOR_CANCELLED;
796 }
797
798 RNA_int_set_array(op->ptr, "location", event->mval);
799
800 return WM_gesture_box_invoke(C, op, event);
801}
802
804{
805 if (!can_exec(*C, *op->reports)) {
806 return OPERATOR_CANCELLED;
807 }
808
809 std::unique_ptr<gesture::GestureData> gesture_data = gesture::init_from_lasso(C, op);
810 if (!gesture_data) {
811 return OPERATOR_CANCELLED;
812 }
813
814 gesture_data->operation = reinterpret_cast<gesture::Operation *>(
816 initialize_cursor_info(*C, *op, *gesture_data);
817 init_operation(*gesture_data, *op);
818
819 gesture::apply(*C, *gesture_data, *op);
820 return OPERATOR_FINISHED;
821}
822
824{
825 if (!can_invoke(*C)) {
826 return OPERATOR_CANCELLED;
827 }
828
829 RNA_int_set_array(op->ptr, "location", event->mval);
830
831 return WM_gesture_lasso_invoke(C, op, event);
832}
833
835{
836 if (!can_exec(*C, *op->reports)) {
837 return OPERATOR_CANCELLED;
838 }
839
840 std::unique_ptr<gesture::GestureData> gesture_data = gesture::init_from_line(C, op);
841 if (!gesture_data) {
842 return OPERATOR_CANCELLED;
843 }
844
845 gesture_data->operation = reinterpret_cast<gesture::Operation *>(
847
848 initialize_cursor_info(*C, *op, *gesture_data);
849 init_operation(*gesture_data, *op);
850 gesture::apply(*C, *gesture_data, *op);
851 return OPERATOR_FINISHED;
852}
853
855{
856 if (!can_invoke(*C)) {
857 return OPERATOR_CANCELLED;
858 }
859
860 RNA_int_set_array(op->ptr, "location", event->mval);
861
863}
864
866{
867 if (!can_exec(*C, *op->reports)) {
868 return OPERATOR_CANCELLED;
869 }
870
871 std::unique_ptr<gesture::GestureData> gesture_data = gesture::init_from_polyline(C, op);
872 if (!gesture_data) {
873 return OPERATOR_CANCELLED;
874 }
875
876 gesture_data->operation = reinterpret_cast<gesture::Operation *>(
878 initialize_cursor_info(*C, *op, *gesture_data);
879 init_operation(*gesture_data, *op);
880
881 gesture::apply(*C, *gesture_data, *op);
882 return OPERATOR_FINISHED;
883}
884
886{
887 if (!can_invoke(*C)) {
888 return OPERATOR_CANCELLED;
889 }
890
891 RNA_int_set_array(op->ptr, "location", event->mval);
892
893 return WM_gesture_polyline_invoke(C, op, event);
894}
895
897{
898 ot->name = "Trim Lasso Gesture";
899 ot->idname = "SCULPT_OT_trim_lasso_gesture";
900 ot->description = "Execute a boolean operation on the mesh and a shape defined by the cursor";
901
902 ot->invoke = gesture_lasso_invoke;
903 ot->modal = WM_gesture_lasso_modal;
904 ot->exec = gesture_lasso_exec;
905
907
909
910 /* Properties. */
913
915}
916
918{
919 ot->name = "Trim Box Gesture";
920 ot->idname = "SCULPT_OT_trim_box_gesture";
921 ot->description =
922 "Execute a boolean operation on the mesh and a rectangle defined by the cursor";
923
924 ot->invoke = gesture_box_invoke;
925 ot->modal = WM_gesture_box_modal;
926 ot->exec = gesture_box_exec;
927
929
930 ot->flag = OPTYPE_REGISTER;
931
932 /* Properties. */
935
937}
938
940{
941 ot->name = "Trim Line Gesture";
942 ot->idname = "SCULPT_OT_trim_line_gesture";
943 ot->description = "Remove a portion of the mesh on one side of a line";
944
945 ot->invoke = gesture_line_invoke;
947 ot->exec = gesture_line_exec;
948
950
951 ot->flag = OPTYPE_REGISTER;
952
953 /* Properties. */
956
958}
959
961{
962 ot->name = "Trim Polyline Gesture";
963 ot->idname = "SCULPT_OT_trim_polyline_gesture";
964 ot->description =
965 "Execute a boolean operation on the mesh and a polygonal shape defined by the cursor";
966
967 ot->invoke = gesture_polyline_invoke;
970
972
973 ot->flag = OPTYPE_REGISTER;
974
975 /* Properties. */
978
980}
981} // namespace blender::ed::sculpt_paint::trim
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, bool process_shape_keys=true)
@ 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_sculptsession_free_pbvh(Object &object)
Definition paint.cc:2286
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:645
@ RPT_ERROR
Definition BKE_report.hh:39
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
void plane_from_point_normal_v3(float r_plane[4], const float plane_co[3], const float plane_no[3])
Definition math_geom.cc:217
float dist_signed_to_plane_v3(const float p[3], const float plane[4])
Definition math_geom.cc:495
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)
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:1074
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
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])
static void View(GHOST_IWindow *window, bool stereo, int eye=0)
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
@ PROP_HIDDEN
Definition RNA_types.hh:338
#define C
Definition RandGen.cpp:29
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:218
@ OPTYPE_REGISTER
Definition WM_types.hh:180
@ BM_ELEM_TAG
BMesh const char void * data
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)
#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
DBVT_INLINE bool Intersect(const btDbvtAabbMm &a, const btDbvtAabbMm &b)
Definition btDbvt.h:621
int64_t size() const
Definition BLI_array.hh:256
const T * data() const
Definition BLI_array.hh:312
IndexRange index_range() const
Definition BLI_array.hh:360
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
nullptr float
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static void error(const char *str)
int face_triangles_num(const int face_size)
Definition BKE_mesh.hh:350
pbvh::Tree * pbvh_get(Object &object)
Definition paint.cc:3052
Span< float3 > vert_positions_eval(const Depsgraph &depsgraph, const Object &object_orig)
Definition pbvh.cc:1040
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, const 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:6199
void SCULPT_OT_trim_lasso_gesture(wmOperatorType *ot)
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 apply_join_operation(Object &object, Mesh &sculpt_mesh, Mesh &trim_mesh)
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 wmOperatorStatus gesture_line_invoke(bContext *C, wmOperator *op, const wmEvent *event)
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 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 bool can_invoke(const bContext &C)
static void calculate_depth(gesture::GestureData &gesture_data, float &r_depth_front, float &r_depth_back)
static void free_geometry(gesture::GestureData &gesture_data)
static EnumPropertyItem operation_types[]
static wmOperatorStatus gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus gesture_polyline_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void SCULPT_OT_trim_line_gesture(wmOperatorType *ot)
static wmOperatorStatus gesture_lasso_exec(bContext *C, wmOperator *op)
static wmOperatorStatus gesture_line_exec(bContext *C, wmOperator *op)
static wmOperatorStatus gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus gesture_box_exec(bContext *C, wmOperator *op)
static const EnumPropertyItem solver_items[]
static EnumPropertyItem orientation_types[]
static wmOperatorStatus gesture_polyline_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)
void vert_random_access_ensure(Object &object)
Definition sculpt.cc:141
float3 symmetry_flip(const float3 &src, const ePaintSymmetryFlags symm)
bool cursor_geometry_info_update(bContext *C, CursorGeometryInfo *out, const float2 &mval, const bool use_sampled_normal)
Definition sculpt.cc:4685
float object_space_radius_get(const ViewContext &vc, const Paint &paint, const Brush &brush, const float3 &location, const float scale_factor)
Definition sculpt.cc:114
Mesh * mesh_boolean(Span< const Mesh * > meshes, Span< float4x4 > transforms, Span< Array< short > > material_remaps, BooleanOpParameters op_params, Solver solver, Vector< int > *r_intersecting_edges, BooleanError *r_error)
bke::GeometrySet join_geometries(Span< bke::GeometrySet > geometries, const bke::AttributeFilter &attribute_filter, const std::optional< Span< bke::GeometryComponent::Type > > &component_types_to_join=std::nullopt, bool allow_merging_instance_references=true)
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)
MatBase< float, 4, 4 > float4x4
VecBase< float, 2 > float2
VecBase< float, 3 > float3
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)
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_mode_poll_view3d(bContext *C)
Definition sculpt.cc:3683
#define FLT_MAX
Definition stdcycles.h:14
int verts_num
float cursor_radius
Definition BKE_paint.hh:429
RegionView3D * rv3d
Definition ED_view3d.hh:80
ARegion * region
Definition ED_view3d.hh:77
bContext * C
Definition ED_view3d.hh:66
View3D * v3d
Definition ED_view3d.hh:78
Object * obact
Definition ED_view3d.hh:75
Depsgraph * depsgraph
Definition ED_view3d.hh:72
const c_style_mat & ptr() const
GeometryComponent & get_component_for_write(GeometryComponent::Type component_type)
static GeometrySet from_mesh(Mesh *mesh, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
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:763
struct ReportList * reports
struct PointerRNA * ptr
i
Definition text_draw.cc:230
@ Fixed
@ WM_CURSOR_EDIT
Definition wm_cursors.hh:19
wmOperatorType * ot
Definition wm_files.cc:4237
wmOperatorStatus WM_gesture_polyline_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_straightline_active_side_invoke(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_polyline_invoke(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_straightline_oneshot_modal(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)