Blender V4.5
sculpt_ops.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2006 by Nicholas Bishop. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9
10#include "MEM_guardedalloc.h"
11
13#include "BLI_math_matrix.hh"
14#include "BLI_math_vector.hh"
15
16#include "BLT_translation.hh"
17
18#include "DNA_brush_types.h"
19#include "DNA_listBase.h"
20#include "DNA_node_types.h"
21#include "DNA_object_types.h"
22#include "DNA_scene_types.h"
23
24#include "BKE_attribute.hh"
25#include "BKE_brush.hh"
26#include "BKE_ccg.hh"
27#include "BKE_context.hh"
28#include "BKE_layer.hh"
29#include "BKE_main.hh"
30#include "BKE_mesh.hh"
31#include "BKE_mesh_mirror.hh"
32#include "BKE_multires.hh"
33#include "BKE_object.hh"
34#include "BKE_paint.hh"
35#include "BKE_paint_bvh.hh"
36#include "BKE_report.hh"
37#include "BKE_scene.hh"
38#include "BKE_subdiv_ccg.hh"
39
40#include "DEG_depsgraph.hh"
41
43
44#include "WM_api.hh"
45#include "WM_message.hh"
46#include "WM_toolsystem.hh"
47#include "WM_types.hh"
48
49#include "ED_image.hh"
50#include "ED_object.hh"
51#include "ED_screen.hh"
52#include "ED_sculpt.hh"
53
54#include "mesh_brush_common.hh"
55#include "paint_intern.hh"
56#include "paint_mask.hh"
57#include "sculpt_automask.hh"
58#include "sculpt_color.hh"
59#include "sculpt_dyntopo.hh"
60#include "sculpt_face_set.hh"
61#include "sculpt_flood_fill.hh"
62#include "sculpt_intern.hh"
63#include "sculpt_islands.hh"
64#include "sculpt_undo.hh"
65
66#include "RNA_access.hh"
67#include "RNA_define.hh"
68
69#include "UI_interface.hh"
70#include "UI_resources.hh"
71
72#include "bmesh.hh"
73
74#include <cmath>
75#include <cstdlib>
76#include <cstring>
77
79
80/* -------------------------------------------------------------------- */
83
85{
88 SculptSession *ss = ob.sculpt;
89
90 const View3D *v3d = CTX_wm_view3d(C);
91 const Base *base = CTX_data_active_base(C);
92 if (!BKE_base_is_visible(v3d, base)) {
93 return OPERATOR_CANCELLED;
94 }
95
96 if (!ss) {
97 return OPERATOR_CANCELLED;
98 }
99
101
102 switch (bke::object::pbvh_get(ob)->type()) {
104 Mesh &mesh = *static_cast<Mesh *>(ob.data);
105 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
106 attributes.remove(".sculpt_persistent_co");
107 attributes.remove(".sculpt_persistent_no");
108 attributes.remove(".sculpt_persistent_disp");
109
110 const bke::AttributeReader positions = attributes.lookup<float3>("position");
111 if (positions.sharing_info && positions.varray.is_span()) {
112 attributes.add<float3>(
113 ".sculpt_persistent_co",
115 bke::AttributeInitShared(positions.varray.get_internal_span().data(),
116 *positions.sharing_info));
117 }
118 else {
119 attributes.add<float3>(".sculpt_persistent_co",
122 }
123
124 const Span<float3> vert_normals = bke::pbvh::vert_normals_eval(*depsgraph, ob);
125 attributes.add<float3>(".sculpt_persistent_no",
128 break;
129 }
131 const SubdivCCG &subdiv_ccg = *ss->subdiv_ccg;
133 ss->persistent.sculpt_persistent_no = subdiv_ccg.normals;
134 ss->persistent.sculpt_persistent_disp = Array<float>(subdiv_ccg.positions.size(), 0.0f);
135 ss->persistent.grid_size = subdiv_ccg.grid_size;
136 ss->persistent.grids_num = subdiv_ccg.grids_num;
137 break;
138 }
140 return OPERATOR_CANCELLED;
141 }
142 }
143
144 return OPERATOR_FINISHED;
145}
146
148{
149 ot->name = "Set Persistent Base";
150 ot->idname = "SCULPT_OT_set_persistent_base";
151 ot->description = "Reset the copy of the mesh that is being sculpted on";
152
154 ot->poll = SCULPT_mode_poll;
155
157}
158
160
161/* -------------------------------------------------------------------- */
164
176
177/* The BVH gets less optimal more quickly with dynamic topology than
178 * regular sculpting. There is no doubt more clever stuff we can do to
179 * optimize it on the fly, but for now this gives the user a nicer way
180 * to recalculate it than toggling modes. */
182{
183 ot->name = "Rebuild BVH";
184 ot->idname = "SCULPT_OT_optimize";
185 ot->description = "Recalculate the sculpt BVH to improve performance";
186
187 ot->exec = optimize_exec;
188 ot->poll = SCULPT_mode_poll;
189
190 ot->flag = OPTYPE_REGISTER;
191}
192
194
195/* -------------------------------------------------------------------- */
198
200{
202 if (!ob) {
203 return false;
204 }
205 if (ob->type != OB_MESH) {
206 return false;
207 }
208 const bke::pbvh::Tree *pbvh = bke::object::pbvh_get(*ob);
209 if (SCULPT_mode_poll(C) && ob->sculpt && pbvh) {
210 return pbvh->type() != bke::pbvh::Type::Grids;
211 }
212 return false;
213}
214
216{
217 Main *bmain = CTX_data_main(C);
218 const Scene &scene = *CTX_data_scene(C);
219 const Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
221 const Sculpt &sd = *CTX_data_tool_settings(C)->sculpt;
222 SculptSession &ss = *ob.sculpt;
223 const bke::pbvh::Tree *pbvh = bke::object::pbvh_get(ob);
224 const float dist = RNA_float_get(op->ptr, "merge_tolerance");
225
226 if (!pbvh) {
227 return OPERATOR_CANCELLED;
228 }
229
230 const View3D *v3d = CTX_wm_view3d(C);
231 const Base *base = CTX_data_active_base(C);
232 if (!BKE_base_is_visible(v3d, base)) {
233 return OPERATOR_CANCELLED;
234 }
235
236 switch (pbvh->type()) {
238 /* Dyntopo Symmetrize. */
239
240 /* To simplify undo for symmetrize, all BMesh elements are logged
241 * as deleted, then after symmetrize operation all BMesh elements
242 * are logged as added (as opposed to attempting to store just the
243 * parts that symmetrize modifies). */
244 undo::push_begin(scene, ob, op);
247
248 BM_mesh_toolflags_set(ss.bm, true);
249
250 /* Symmetrize and re-triangulate. */
251 BMO_op_callf(ss.bm,
253 "symmetrize input=%avef direction=%i dist=%f use_shapekey=%b",
255 dist,
256 true);
258
259 /* Bisect operator flags edges (keep tags clean for edge queue). */
261
262 BM_mesh_toolflags_set(ss.bm, false);
263
264 /* Finish undo. */
265 BM_log_all_added(ss.bm, ss.bm_log);
266 undo::push_end(ob);
267
268 break;
269 }
271 /* Mesh Symmetrize. */
272 undo::geometry_begin(scene, ob, op);
273 Mesh *mesh = static_cast<Mesh *>(ob.data);
274
276
279
280 break;
281 }
283 return OPERATOR_CANCELLED;
284 }
285
289
290 return OPERATOR_FINISHED;
291}
292
294{
295 ot->name = "Symmetrize";
296 ot->idname = "SCULPT_OT_symmetrize";
297 ot->description = "Symmetrize the topology modifications";
298
299 ot->exec = symmetrize_exec;
300 ot->poll = no_multires_poll;
301
302 PropertyRNA *prop = RNA_def_float(ot->srna,
303 "merge_tolerance",
304 0.0005f,
305 0.0f,
306 std::numeric_limits<float>::max(),
307 "Merge Distance",
308 "Distance within which symmetrical vertices are merged",
309 0.0f,
310 1.0f);
311
312 RNA_def_property_ui_range(prop, 0.0, std::numeric_limits<float>::max(), 0.001, 5);
313}
314
316
317/* -------------------------------------------------------------------- */
320
321static void init_sculpt_mode_session(Main &bmain, Depsgraph &depsgraph, Scene &scene, Object &ob)
322{
323 /* Create persistent sculpt mode data. */
325
326 /* Create sculpt mode session data. */
327 if (ob.sculpt != nullptr) {
329 }
330 ob.sculpt = MEM_new<SculptSession>(__func__);
332
333 /* Trigger evaluation of modifier stack to ensure
334 * multires modifier sets .runtime.ccg in
335 * the evaluated mesh.
336 */
338
340
341 /* This function expects a fully evaluated depsgraph. */
343
344 Mesh &mesh = *static_cast<Mesh *>(ob.data);
345 if (mesh.attributes().contains(".sculpt_face_set")) {
346 /* Here we can detect geometry that was just added to Sculpt Mode as it has the
347 * SCULPT_FACE_SET_NONE assigned, so we can create a new Face Set for it. */
348 /* In sculpt mode all geometry that is assigned to SCULPT_FACE_SET_NONE is considered as not
349 * initialized, which is used is some operators that modify the mesh topology to perform
350 * certain actions in the new faces. After these operations are finished, all faces should have
351 * a valid face set ID assigned (different from SCULPT_FACE_SET_NONE) to manage their
352 * visibility correctly. */
353 /* TODO(pablodp606): Based on this we can improve the UX in future tools for creating new
354 * objects, like moving the transform pivot position to the new area or masking existing
355 * geometry. */
356 const int new_face_set = face_set::find_next_available_id(ob);
357 face_set::initialize_none_to_id(static_cast<Mesh *>(ob.data), new_face_set);
358 }
359}
360
361void ensure_valid_pivot(const Object &ob, Scene &scene)
362{
364 const bke::pbvh::Tree *pbvh = bke::object::pbvh_get(ob);
365
366 /* Account for the case where no objects are evaluated. */
367 if (!pbvh) {
368 return;
369 }
370
371 /* No valid pivot? Use bounding box center. */
372 if (ups.average_stroke_counter == 0 || !ups.last_stroke_valid) {
374 const float3 center = math::midpoint(bounds.min, bounds.max);
375 const float3 location = math::transform_point(ob.object_to_world(), center);
376
377 copy_v3_v3(ups.average_stroke_accum, location);
379
380 /* Update last stroke position. */
381 ups.last_stroke_valid = true;
382 }
383}
384
386 Depsgraph &depsgraph,
387 Scene &scene,
388 Object &ob,
389 const bool force_dyntopo,
391{
392 const int mode_flag = OB_MODE_SCULPT;
394
395 /* Re-triangulating the mesh for position changes in sculpt mode isn't worth the performance
396 * impact, so delay triangulation updates until the user exits sculpt mode. */
397 mesh->runtime->corner_tris_cache.freeze();
398
399 /* Enter sculpt mode. */
400 ob.mode |= mode_flag;
401
402 init_sculpt_mode_session(bmain, depsgraph, scene, ob);
403
404 if (!(fabsf(ob.scale[0] - ob.scale[1]) < 1e-4f && fabsf(ob.scale[1] - ob.scale[2]) < 1e-4f)) {
406 reports, RPT_WARNING, "Object has non-uniform scale, sculpting may be unpredictable");
407 }
408 else if (is_negative_m4(ob.object_to_world().ptr())) {
409 BKE_report(reports, RPT_WARNING, "Object has negative scale, sculpting may be unpredictable");
410 }
411
414
416
417 /* Check dynamic-topology flag; re-enter dynamic-topology mode when changing modes,
418 * As long as no data was added that is not supported. */
419 if (mesh->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) {
421
422 const char *message_unsupported = nullptr;
423 if (mesh->corners_num != mesh->faces_num * 3) {
424 message_unsupported = RPT_("non-triangle face");
425 }
426 else if (mmd != nullptr) {
427 message_unsupported = RPT_("multi-res modifier");
428 }
429 else {
431 if (flag == 0) {
432 /* pass */
433 }
434 else if (flag & dyntopo::VDATA) {
435 message_unsupported = RPT_("vertex data");
436 }
437 else if (flag & dyntopo::EDATA) {
438 message_unsupported = RPT_("edge data");
439 }
440 else if (flag & dyntopo::LDATA) {
441 message_unsupported = RPT_("face data");
442 }
443 else if (flag & dyntopo::MODIFIER) {
444 message_unsupported = RPT_("constructive modifier");
445 }
446 else {
448 }
449 }
450
451 if ((message_unsupported == nullptr) || force_dyntopo) {
452 /* Needed because we may be entering this mode before the undo system loads. */
453 wmWindowManager *wm = static_cast<wmWindowManager *>(bmain.wm.first);
454 const bool has_undo = wm->undo_stack != nullptr;
455 /* Undo push is needed to prevent memory leak. */
456 if (has_undo) {
457 undo::push_begin_ex(scene, ob, "Dynamic topology enable");
458 }
459 dyntopo::enable_ex(bmain, depsgraph, ob);
460 if (has_undo) {
462 undo::push_end(ob);
463 }
464 }
465 else {
467 reports, RPT_WARNING, "Dynamic Topology found: %s, disabled", message_unsupported);
469 }
470 }
471
472 ensure_valid_pivot(ob, scene);
473
474 /* Flush object mode. */
476}
477
479{
480 Main &bmain = *CTX_data_main(C);
481 Scene &scene = *CTX_data_scene(C);
482 ViewLayer &view_layer = *CTX_data_view_layer(C);
483 BKE_view_layer_synced_ensure(&scene, &view_layer);
484 Object &ob = *BKE_view_layer_active_object_get(&view_layer);
485 object_sculpt_mode_enter(bmain, depsgraph, scene, ob, false, reports);
486}
487
488void object_sculpt_mode_exit(Main &bmain, Depsgraph &depsgraph, Scene &scene, Object &ob)
489{
490 const int mode_flag = OB_MODE_SCULPT;
492
493 mesh->runtime->corner_tris_cache.unfreeze();
494
496
497 /* Not needed for now. */
498#if 0
500 const int flush_recalc = ed_object_sculptmode_flush_recalc_flag(scene, ob, mmd);
501#endif
502
503 /* Always for now, so leaving sculpt mode always ensures scene is in
504 * a consistent state. */
505 if (true || /* flush_recalc || */ (ob.sculpt && ob.sculpt->bm)) {
507 }
508
509 if (mesh->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) {
510 /* Dynamic topology must be disabled before exiting sculpt
511 * mode to ensure the undo stack stays in a consistent
512 * state. */
513 dyntopo::disable_with_undo(bmain, depsgraph, scene, ob);
514
515 /* Store so we know to re-enable when entering sculpt mode. */
517 }
518
519 /* Leave sculpt mode. */
520 ob.mode &= ~mode_flag;
521
523
525
526 /* Never leave derived meshes behind. */
528
529 /* Flush object mode. */
531}
532
534{
535 Main &bmain = *CTX_data_main(C);
536 Scene &scene = *CTX_data_scene(C);
537 ViewLayer &view_layer = *CTX_data_view_layer(C);
538 BKE_view_layer_synced_ensure(&scene, &view_layer);
539 Object &ob = *BKE_view_layer_active_object_get(&view_layer);
540 object_sculpt_mode_exit(bmain, depsgraph, scene, ob);
541}
542
544{
546 Main &bmain = *CTX_data_main(C);
548 Scene &scene = *CTX_data_scene(C);
549 ToolSettings &ts = *scene.toolsettings;
550 ViewLayer &view_layer = *CTX_data_view_layer(C);
551 BKE_view_layer_synced_ensure(&scene, &view_layer);
552 Object &ob = *BKE_view_layer_active_object_get(&view_layer);
553 const int mode_flag = OB_MODE_SCULPT;
554 const bool is_mode_set = (ob.mode & mode_flag) != 0;
555
556 if (!is_mode_set) {
557 /* Being in sculpt mode on an invisible object is a confusing state; while switching the
558 * visibility of the current object shouldn't inherently change the mode, we prevent entering
559 * sculpt mode on an object that is already invisible to better align with how the mode toggle
560 * works currently. */
561 const View3D *v3d = CTX_wm_view3d(C);
562 const Base *base = BKE_view_layer_base_find(&view_layer, &ob);
563 if (!BKE_base_is_visible(v3d, base)) {
564 return OPERATOR_CANCELLED;
565 }
566
567 if (!object::mode_compat_set(C, &ob, eObjectMode(mode_flag), op->reports)) {
568 return OPERATOR_CANCELLED;
569 }
570 }
571
572 if (is_mode_set) {
573 object_sculpt_mode_exit(bmain, *depsgraph, scene, ob);
574 }
575 else {
576 if (depsgraph) {
578 }
579 object_sculpt_mode_enter(bmain, *depsgraph, scene, ob, false, op->reports);
581
582 if (ob.mode & mode_flag) {
583 Mesh *mesh = static_cast<Mesh *>(ob.data);
584 /* Dyntopo adds its own undo step. */
585 if ((mesh->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) == 0) {
586 /* Without this the memfile undo step is used,
587 * while it works it causes lag when undoing the first undo step, see #71564. */
589 if (wm->op_undo_depth <= 1) {
590 undo::push_enter_sculpt_mode(scene, ob, op);
591 undo::push_end(ob);
592 }
593 }
594 }
595 }
596
598
599 WM_msg_publish_rna_prop(mbus, &ob.id, &ob, Object, mode);
600
602
603 return OPERATOR_FINISHED;
604}
605
607{
608 ot->name = "Sculpt Mode";
609 ot->idname = "SCULPT_OT_sculptmode_toggle";
610 ot->description = "Toggle sculpt mode in 3D view";
611
614
616}
617
619
620/* -------------------------------------------------------------------- */
623
625{
627 Scene &scene = *CTX_data_scene(C);
629 Brush &brush = *BKE_paint_brush(&sd.paint);
630 SculptSession &ss = *ob.sculpt;
631
632 if (!color_supported_check(scene, ob, op->reports)) {
633 return OPERATOR_CANCELLED;
634 }
635
636 const View3D *v3d = CTX_wm_view3d(C);
637 const Base *base = CTX_data_active_base(C);
638 if (!BKE_base_is_visible(v3d, base)) {
639 return OPERATOR_CANCELLED;
640 }
641
643
644 const Mesh &mesh = *static_cast<const Mesh *>(ob.data);
645 const OffsetIndices<int> faces = mesh.faces();
646 const Span<int> corner_verts = mesh.corner_verts();
647 const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
649
650 float4 active_vertex_color;
651 if (!color_attribute || std::holds_alternative<std::monostate>(ss.active_vert())) {
652 active_vertex_color = float4(1.0f);
653 }
654 else {
655 const GVArraySpan colors = *color_attribute;
656 active_vertex_color = color::color_vert_get(faces,
657 corner_verts,
658 vert_to_face_map,
659 colors,
660 color_attribute.domain,
661 std::get<int>(ss.active_vert()));
662 }
663
664 float color_srgb[3];
665 IMB_colormanagement_scene_linear_to_srgb_v3(color_srgb, active_vertex_color);
666 BKE_brush_color_set(&scene, &sd.paint, &brush, color_srgb);
667
669
670 return OPERATOR_FINISHED;
671}
672
674{
675 ot->name = "Sample Color";
676 ot->idname = "SCULPT_OT_sample_color";
677 ot->description = "Sample the vertex color of the active vertex";
678
679 ot->invoke = sample_color_invoke;
680 ot->poll = SCULPT_mode_poll;
681
683}
684
686
687namespace mask {
688
689/* -------------------------------------------------------------------- */
692
702#define MASK_BY_COLOR_SLOPE 0.25f
703
704static float color_delta_get(const float3 &color_a,
705 const float3 &color_b,
706 const float threshold,
707 const bool invert)
708{
709 float len = math::distance(color_a, color_b);
710 /* Normalize len to the (0, 1) range. */
712
713 if (len < threshold - MASK_BY_COLOR_SLOPE) {
714 len = 1.0f;
715 }
716 else if (len >= threshold) {
717 len = 0.0f;
718 }
719 else {
720 len = (-len + threshold) / MASK_BY_COLOR_SLOPE;
721 }
722
723 if (invert) {
724 return 1.0f - len;
725 }
726 return len;
727}
728
729static float final_mask_get(const float current_mask,
730 const float new_mask,
731 const bool invert,
732 const bool preserve_mask)
733{
734 if (preserve_mask) {
735 if (invert) {
736 return std::min(current_mask, new_mask);
737 }
738 return std::max(current_mask, new_mask);
739 }
740 return new_mask;
741}
742
743static void mask_by_color_contiguous_mesh(const Depsgraph &depsgraph,
744 Object &object,
745 const int vert,
746 const float threshold,
747 const bool invert,
748 const bool preserve_mask)
749{
750 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
751 const Mesh &mesh = *static_cast<const Mesh *>(object.data);
752 const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
753 const bke::AttributeAccessor attributes = mesh.attributes();
754 const VArraySpan colors = *attributes.lookup_or_default<ColorGeometry4f>(
755 mesh.active_color_attribute, bke::AttrDomain::Point, {});
756 const float4 active_color = float4(colors[vert]);
757
758 Array<float> new_mask(mesh.verts_num, invert ? 1.0f : 0.0f);
759
760 flood_fill::FillDataMesh flood(mesh.verts_num);
761 flood.add_initial(vert);
762
763 flood.execute(object, vert_to_face_map, [&](int /*from_v*/, int to_v) {
764 const float4 current_color = float4(colors[to_v]);
765
766 float new_vertex_mask = color_delta_get(
767 current_color.xyz(), active_color.xyz(), threshold, invert);
768 new_mask[to_v] = new_vertex_mask;
769
770 float len = math::distance(current_color.xyz(), active_color.xyz());
772 return len <= threshold;
773 });
774
775 IndexMaskMemory memory;
776 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
777
779 depsgraph, object, node_mask, [&](MutableSpan<float> node_masks, const Span<int> verts) {
780 for (const int i : verts.index_range()) {
781 node_masks[i] = final_mask_get(node_masks[i], new_mask[verts[i]], invert, preserve_mask);
782 }
783 });
784}
785
786static void mask_by_color_full_mesh(const Depsgraph &depsgraph,
787 Object &object,
788 const int vert,
789 const float threshold,
790 const bool invert,
791 const bool preserve_mask)
792{
793 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
794 const Mesh &mesh = *static_cast<const Mesh *>(object.data);
795 const bke::AttributeAccessor attributes = mesh.attributes();
796 const VArraySpan colors = *attributes.lookup_or_default<ColorGeometry4f>(
797 mesh.active_color_attribute, bke::AttrDomain::Point, {});
798 const float4 active_color = float4(colors[vert]);
799
800 IndexMaskMemory memory;
801 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
802
804 depsgraph, object, node_mask, [&](MutableSpan<float> node_masks, const Span<int> verts) {
805 for (const int i : verts.index_range()) {
806 const float4 current_color = float4(colors[verts[i]]);
807 const float current_mask = node_masks[i];
808 const float new_mask = color_delta_get(
809 active_color.xyz(), current_color.xyz(), threshold, invert);
810 node_masks[i] = final_mask_get(current_mask, new_mask, invert, preserve_mask);
811 }
812 });
813}
814
815static wmOperatorStatus mask_by_color(bContext *C, wmOperator *op, const float2 region_location)
816{
817 const Scene &scene = *CTX_data_scene(C);
820 SculptSession &ss = *ob.sculpt;
821 View3D *v3d = CTX_wm_view3d(C);
822
823 {
824 if (v3d && v3d->shading.type == OB_SOLID) {
826 }
827 }
828
829 const Base *base = CTX_data_active_base(C);
830 if (!BKE_base_is_visible(v3d, base)) {
831 return OPERATOR_CANCELLED;
832 }
833
834 /* Color data is not available in multi-resolution or dynamic topology. */
835 if (!color_supported_check(scene, ob, op->reports)) {
836 return OPERATOR_CANCELLED;
837 }
838
840
841 /* Tools that are not brushes do not have the brush gizmo to update the vertex as the mouse move,
842 * so it needs to be updated here. */
844 cursor_geometry_info_update(C, &cgi, region_location, false);
845
846 if (std::holds_alternative<std::monostate>(ss.active_vert())) {
847 return OPERATOR_CANCELLED;
848 }
849
850 undo::push_begin(scene, ob, op);
852
853 const float threshold = RNA_float_get(op->ptr, "threshold");
854 const bool invert = RNA_boolean_get(op->ptr, "invert");
855 const bool preserve_mask = RNA_boolean_get(op->ptr, "preserve_previous_mask");
856
857 const int active_vert = std::get<int>(ss.active_vert());
858 if (RNA_boolean_get(op->ptr, "contiguous")) {
859 mask_by_color_contiguous_mesh(*depsgraph, ob, active_vert, threshold, invert, preserve_mask);
860 }
861 else {
862 mask_by_color_full_mesh(*depsgraph, ob, active_vert, threshold, invert, preserve_mask);
863 }
864
865 undo::push_end(ob);
866
869
870 return OPERATOR_FINISHED;
871}
872
874{
875 int2 mval;
876 RNA_int_get_array(op->ptr, "location", mval);
877 return mask_by_color(C, op, float2(mval[0], mval[1]));
878}
879
881{
882 RNA_int_set_array(op->ptr, "location", event->mval);
883 return mask_by_color(C, op, float2(event->mval[0], event->mval[1]));
884}
885
887{
888 ot->name = "Mask by Color";
889 ot->idname = "SCULPT_OT_mask_by_color";
890 ot->description = "Creates a mask based on the active color attribute";
891
892 ot->invoke = mask_by_color_invoke;
893 ot->exec = mask_by_color_exec;
894 ot->poll = SCULPT_mode_poll;
895
897
898 ot->prop = RNA_def_boolean(
899 ot->srna, "contiguous", false, "Contiguous", "Mask only contiguous color areas");
900
901 ot->prop = RNA_def_boolean(ot->srna, "invert", false, "Invert", "Invert the generated mask");
902 ot->prop = RNA_def_boolean(
903 ot->srna,
904 "preserve_previous_mask",
905 false,
906 "Preserve Previous Mask",
907 "Preserve the previous mask and add or subtract the new one generated by the colors");
908
909 RNA_def_float(ot->srna,
910 "threshold",
911 0.35f,
912 0.0f,
913 1.0f,
914 "Threshold",
915 "How much changes in color affect the mask generation",
916 0.0f,
917 1.0f);
918
919 ot->prop = RNA_def_int_array(ot->srna,
920 "location",
921 2,
922 nullptr,
923 0,
924 SHRT_MAX,
925 "Location",
926 "Region coordinates of sampling",
927 0,
928 SHRT_MAX);
930}
931
933
934/* -------------------------------------------------------------------- */
937
945
947 {int(ApplyMaskMode::Mix), "MIX", ICON_NONE, "Mix", ""},
948 {int(ApplyMaskMode::Multiply), "MULTIPLY", ICON_NONE, "Multiply", ""},
949 {int(ApplyMaskMode::Divide), "DIVIDE", ICON_NONE, "Divide", ""},
950 {int(ApplyMaskMode::Add), "ADD", ICON_NONE, "Add", ""},
951 {int(ApplyMaskMode::Subtract), "SUBTRACT", ICON_NONE, "Subtract", ""},
952 {0, nullptr, 0, nullptr, nullptr},
953};
954
955enum class MaskSettingsSource : int8_t { Operator, Scene, Brush };
956
959 "OPERATOR",
960 ICON_NONE,
961 "Operator",
962 "Use settings from operator properties"},
963 {int(MaskSettingsSource::Brush), "BRUSH", ICON_NONE, "Brush", "Use settings from brush"},
964 {int(MaskSettingsSource::Scene), "SCENE", ICON_NONE, "Scene", "Use settings from scene"},
965 {0, nullptr, 0, nullptr, nullptr}};
966
972
973static void calc_new_masks(const ApplyMaskMode mode,
974 const Span<float> node_mask,
975 const MutableSpan<float> new_mask)
976{
977 switch (mode) {
979 break;
981 for (const int i : node_mask.index_range()) {
982 new_mask[i] = node_mask[i] * new_mask[i];
983 }
984 break;
986 for (const int i : node_mask.index_range()) {
987 new_mask[i] = new_mask[i] > 0.00001f ? node_mask[i] / new_mask[i] : 0.0f;
988 }
989 break;
991 for (const int i : node_mask.index_range()) {
992 new_mask[i] = node_mask[i] + new_mask[i];
993 }
994 break;
996 for (const int i : node_mask.index_range()) {
997 new_mask[i] = node_mask[i] - new_mask[i];
998 }
999 break;
1000 }
1001 mask::clamp_mask(new_mask);
1002}
1003
1004static void apply_mask_mesh(const Depsgraph &depsgraph,
1005 const Object &object,
1006 const Span<bool> hide_vert,
1007 const auto_mask::Cache &automasking,
1008 const ApplyMaskMode mode,
1009 const float factor,
1010 const bool invert_automask,
1011 const bke::pbvh::MeshNode &node,
1012 LocalData &tls,
1014{
1015 const Span<int> verts = node.verts();
1016
1017 tls.factors.resize(verts.size());
1018 const MutableSpan<float> factors = tls.factors;
1019 fill_factor_from_hide(hide_vert, verts, factors);
1020 scale_factors(factors, factor);
1021
1022 tls.new_mask.resize(verts.size());
1023 const MutableSpan<float> new_mask = tls.new_mask;
1024 new_mask.fill(1.0f);
1025 auto_mask::calc_vert_factors(depsgraph, object, automasking, node, verts, new_mask);
1026
1027 if (invert_automask) {
1028 mask::invert_mask(new_mask);
1029 }
1030
1031 tls.mask.resize(verts.size());
1032 const MutableSpan<float> node_mask = tls.mask;
1033 gather_data_mesh(mask.as_span(), verts, node_mask);
1034
1035 calc_new_masks(mode, node_mask, new_mask);
1036 mix_new_masks(new_mask, factors, node_mask);
1037
1038 scatter_data_mesh(node_mask.as_span(), verts, mask);
1039}
1040
1041static void apply_mask_grids(const Depsgraph &depsgraph,
1042 Object &object,
1043 const auto_mask::Cache &automasking,
1044 const ApplyMaskMode mode,
1045 const float factor,
1046 const bool invert_automask,
1047 const bke::pbvh::GridsNode &node,
1048 LocalData &tls)
1049{
1050 SculptSession &ss = *object.sculpt;
1051 SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
1052 const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
1053
1054 const Span<int> grids = node.grids();
1055 const int grid_verts_num = grids.size() * key.grid_area;
1056
1057 tls.factors.resize(grid_verts_num);
1058 const MutableSpan<float> factors = tls.factors;
1059 fill_factor_from_hide(subdiv_ccg, grids, factors);
1060 scale_factors(factors, factor);
1061
1062 tls.new_mask.resize(grid_verts_num);
1063 const MutableSpan<float> new_mask = tls.new_mask;
1064 new_mask.fill(1.0f);
1065 auto_mask::calc_grids_factors(depsgraph, object, automasking, node, grids, new_mask);
1066
1067 if (invert_automask) {
1068 mask::invert_mask(new_mask);
1069 }
1070
1071 tls.mask.resize(grid_verts_num);
1072 const MutableSpan<float> node_mask = tls.mask;
1073 gather_mask_grids(subdiv_ccg, grids, node_mask);
1074
1075 calc_new_masks(mode, node_mask, new_mask);
1076 mix_new_masks(new_mask, factors, node_mask);
1077
1078 scatter_mask_grids(node_mask.as_span(), subdiv_ccg, grids);
1079}
1080
1081static void apply_mask_bmesh(const Depsgraph &depsgraph,
1082 Object &object,
1083 const auto_mask::Cache &automasking,
1084 const ApplyMaskMode mode,
1085 const float factor,
1086 const float invert_automask,
1088 LocalData &tls)
1089{
1090 const SculptSession &ss = *object.sculpt;
1092
1093 tls.factors.resize(verts.size());
1094 const MutableSpan<float> factors = tls.factors;
1095 fill_factor_from_hide(verts, factors);
1096 scale_factors(factors, factor);
1097
1098 tls.new_mask.resize(verts.size());
1099 const MutableSpan<float> new_mask = tls.new_mask;
1100 new_mask.fill(1.0f);
1101 auto_mask::calc_vert_factors(depsgraph, object, automasking, node, verts, new_mask);
1102
1103 if (invert_automask) {
1104 mask::invert_mask(new_mask);
1105 }
1106
1107 tls.mask.resize(verts.size());
1108 const MutableSpan<float> node_mask = tls.mask;
1109 gather_mask_bmesh(*ss.bm, verts, node_mask);
1110
1111 calc_new_masks(mode, node_mask, new_mask);
1112 mix_new_masks(new_mask, factors, node_mask);
1113
1114 scatter_mask_bmesh(node_mask.as_span(), *ss.bm, verts);
1115}
1116
1117static void apply_mask_from_settings(const Depsgraph &depsgraph,
1118 Object &object,
1119 bke::pbvh::Tree &pbvh,
1120 const IndexMask &node_mask,
1121 const auto_mask::Cache &automasking,
1122 const ApplyMaskMode mode,
1123 const float factor,
1124 const bool invert_automask)
1125{
1127 switch (pbvh.type()) {
1128 case bke::pbvh::Type::Mesh: {
1129 Mesh &mesh = *static_cast<Mesh *>(object.data);
1130 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
1132 ".sculpt_mask", bke::AttrDomain::Point);
1133 const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
1135 node_mask.foreach_index(GrainSize(1), [&](const int i) {
1136 LocalData &tls = all_tls.local();
1138 object,
1139 hide_vert,
1140 automasking,
1141 mode,
1142 factor,
1143 invert_automask,
1144 nodes[i],
1145 tls,
1146 mask.span);
1148 });
1149 mask.finish();
1150 break;
1151 }
1153 SubdivCCG &subdiv_ccg = *object.sculpt->subdiv_ccg;
1154 const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
1155 MutableSpan<float> masks = subdiv_ccg.masks;
1157 node_mask.foreach_index(GrainSize(1), [&](const int i) {
1158 LocalData &tls = all_tls.local();
1160 depsgraph, object, automasking, mode, factor, invert_automask, nodes[i], tls);
1162 });
1163 break;
1164 }
1166 const int mask_offset = CustomData_get_offset_named(
1167 &object.sculpt->bm->vdata, CD_PROP_FLOAT, ".sculpt_mask");
1169 node_mask.foreach_index(GrainSize(1), [&](const int i) {
1170 LocalData &tls = all_tls.local();
1172 depsgraph, object, automasking, mode, factor, invert_automask, nodes[i], tls);
1174 });
1175 break;
1176 }
1177 }
1178}
1179
1181{
1182 const Scene &scene = *CTX_data_scene(C);
1185 const Sculpt &sd = *CTX_data_tool_settings(C)->sculpt;
1186 const Brush *brush = BKE_paint_brush_for_read(&sd.paint);
1187
1188 const View3D *v3d = CTX_wm_view3d(C);
1189 const Base *base = CTX_data_active_base(C);
1190 if (!BKE_base_is_visible(v3d, base)) {
1191 return OPERATOR_CANCELLED;
1192 }
1193
1196
1199
1200 const ApplyMaskMode mode = ApplyMaskMode(RNA_enum_get(op->ptr, "mix_mode"));
1201 const float factor = RNA_float_get(op->ptr, "mix_factor");
1202
1204
1205 IndexMaskMemory memory;
1206 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
1207
1208 /* Set up automasking settings. */
1209 Sculpt scene_copy = sd;
1210
1211 MaskSettingsSource src = (MaskSettingsSource)RNA_enum_get(op->ptr, "settings_source");
1212 switch (src) {
1214 if (RNA_boolean_get(op->ptr, "invert")) {
1216 }
1217 else {
1219 }
1220
1221 if (RNA_boolean_get(op->ptr, "use_curve")) {
1223 }
1224
1225 scene_copy.automasking_cavity_blur_steps = RNA_int_get(op->ptr, "blur_steps");
1226 scene_copy.automasking_cavity_factor = RNA_float_get(op->ptr, "factor");
1227
1229 break;
1231 if (brush) {
1232 scene_copy.automasking_flags = brush->automasking_flags;
1236
1237 /* Ensure only cavity masking is enabled. */
1240 }
1241 else {
1242 scene_copy.automasking_flags = 0;
1243 BKE_report(op->reports, RPT_WARNING, "No active brush");
1244
1245 return OPERATOR_CANCELLED;
1246 }
1247
1248 break;
1250 /* Ensure only cavity masking is enabled. */
1253 break;
1254 }
1255
1256 /* Ensure cavity mask is actually enabled. */
1257 if (!(scene_copy.automasking_flags & BRUSH_AUTOMASKING_CAVITY_ALL)) {
1259 }
1260
1261 /* Create copy of brush with cleared automasking settings. */
1262 Brush brush_copy = dna::shallow_copy(*brush);
1263 /* Set a brush type that doesn't change topology so automasking isn't "disabled". */
1265 brush_copy.automasking_flags = 0;
1267 brush_copy.automasking_cavity_curve = scene_copy.automasking_cavity_curve;
1268
1269 std::unique_ptr<auto_mask::Cache> automasking = auto_mask::cache_init(
1270 *depsgraph, scene_copy, &brush_copy, ob);
1271
1272 if (!automasking) {
1273 return OPERATOR_CANCELLED;
1274 }
1275
1276 undo::push_begin(scene, ob, op);
1278
1279 automasking->calc_cavity_factor(*depsgraph, ob, node_mask);
1280 apply_mask_from_settings(*depsgraph, ob, pbvh, node_mask, *automasking, mode, factor, false);
1281
1282 undo::push_end(ob);
1283
1284 pbvh.tag_masks_changed(node_mask);
1287
1288 return OPERATOR_FINISHED;
1289}
1290
1292{
1293 uiLayout *layout = op->layout;
1294 Scene *scene = CTX_data_scene(C);
1295 Sculpt *sd = scene->toolsettings ? scene->toolsettings->sculpt : nullptr;
1296
1297 uiLayoutSetPropSep(layout, true);
1298 uiLayoutSetPropDecorate(layout, false);
1299 MaskSettingsSource source = (MaskSettingsSource)RNA_enum_get(op->ptr, "settings_source");
1300
1301 if (!sd) {
1303 }
1304
1305 switch (source) {
1307 layout->prop(op->ptr, "mix_mode", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1308 layout->prop(op->ptr, "mix_factor", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1309 layout->prop(op->ptr, "factor", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1310 layout->prop(op->ptr, "blur_steps", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1311 layout->prop(op->ptr, "invert", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1312 layout->prop(op->ptr, "use_curve", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1313
1314 if (sd && RNA_boolean_get(op->ptr, "use_curve")) {
1315 PointerRNA sculpt_ptr = RNA_pointer_create_discrete(&scene->id, &RNA_Sculpt, sd);
1317 layout, &sculpt_ptr, "automasking_cavity_curve_op", 'v', false, false, false, false);
1318 }
1319 break;
1320 }
1323 layout->prop(op->ptr, "mix_mode", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1324 layout->prop(op->ptr, "mix_factor", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1325
1326 break;
1327 }
1328}
1329
1331{
1332 ot->name = "Mask From Cavity";
1333 ot->idname = "SCULPT_OT_mask_from_cavity";
1334 ot->description = "Creates a mask based on the curvature of the surface";
1335
1336 ot->ui = mask_from_cavity_ui;
1337 ot->exec = mask_from_cavity_exec;
1338 ot->poll = SCULPT_mode_poll;
1339
1340 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1341
1342 RNA_def_enum(ot->srna, "mix_mode", mix_modes, int(ApplyMaskMode::Mix), "Mode", "Mix mode");
1343 RNA_def_float(ot->srna, "mix_factor", 1.0f, 0.0f, 5.0f, "Mix Factor", "", 0.0f, 1.0f);
1344 RNA_def_enum(ot->srna,
1345 "settings_source",
1348 "Settings",
1349 "Use settings from here");
1350 RNA_def_float(ot->srna,
1351 "factor",
1352 0.5f,
1353 0.0f,
1354 5.0f,
1355 "Factor",
1356 "The contrast of the cavity mask",
1357 0.0f,
1358 1.0f);
1359 RNA_def_int(ot->srna,
1360 "blur_steps",
1361 2,
1362 0,
1363 25,
1364 "Blur",
1365 "The number of times the cavity mask is blurred",
1366 0,
1367 25);
1368 RNA_def_boolean(ot->srna, "use_curve", false, "Custom Curve", "");
1369 RNA_def_boolean(ot->srna, "invert", false, "Cavity (Inverted)", "");
1370}
1371
1372enum class MaskBoundaryMode : int8_t { Mesh, FaceSets };
1373
1375{
1378 const Sculpt &sd = *CTX_data_tool_settings(C)->sculpt;
1379 const Scene &scene = *CTX_data_scene(C);
1380 const Brush *brush = BKE_paint_brush_for_read(&sd.paint);
1381
1382 const View3D *v3d = CTX_wm_view3d(C);
1383 const Base *base = CTX_data_active_base(C);
1384 if (!BKE_base_is_visible(v3d, base)) {
1385 return OPERATOR_CANCELLED;
1386 }
1387
1390
1393
1394 const ApplyMaskMode mode = ApplyMaskMode(RNA_enum_get(op->ptr, "mix_mode"));
1395 const float factor = RNA_float_get(op->ptr, "mix_factor");
1396
1398
1399 IndexMaskMemory memory;
1400 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
1401
1402 /* Set up automasking settings. */
1403 Sculpt scene_copy = sd;
1404
1405 MaskSettingsSource src = (MaskSettingsSource)RNA_enum_get(op->ptr, "settings_source");
1406 switch (src) {
1408 const MaskBoundaryMode boundary_mode = MaskBoundaryMode(
1409 RNA_enum_get(op->ptr, "boundary_mode"));
1410 switch (boundary_mode) {
1413 break;
1416 break;
1417 }
1419 "propagation_steps");
1420 break;
1421 }
1423 if (brush) {
1424 scene_copy.automasking_flags = brush->automasking_flags;
1427
1430 }
1431 else {
1432 scene_copy.automasking_flags = 0;
1433 BKE_report(op->reports, RPT_WARNING, "No active brush");
1434
1435 return OPERATOR_CANCELLED;
1436 }
1437
1438 break;
1442 break;
1443 }
1444
1445 /* Create copy of brush with cleared automasking settings. */
1446 Brush brush_copy = dna::shallow_copy(*brush);
1447 /* Set a brush type that doesn't change topology so automasking isn't "disabled". */
1449 brush_copy.automasking_flags = 0;
1451
1452 std::unique_ptr<auto_mask::Cache> automasking = auto_mask::cache_init(
1453 *depsgraph, scene_copy, &brush_copy, ob);
1454
1455 if (!automasking) {
1456 return OPERATOR_CANCELLED;
1457 }
1458
1459 undo::push_begin(scene, ob, op);
1461
1462 apply_mask_from_settings(*depsgraph, ob, pbvh, node_mask, *automasking, mode, factor, true);
1463
1464 undo::push_end(ob);
1465
1466 pbvh.tag_masks_changed(node_mask);
1469
1470 return OPERATOR_FINISHED;
1471}
1472
1474{
1475 uiLayout *layout = op->layout;
1476 Scene *scene = CTX_data_scene(C);
1477 Sculpt *sd = scene->toolsettings ? scene->toolsettings->sculpt : nullptr;
1478
1479 uiLayoutSetPropSep(layout, true);
1480 uiLayoutSetPropDecorate(layout, false);
1481 MaskSettingsSource source = (MaskSettingsSource)RNA_enum_get(op->ptr, "settings_source");
1482
1483 if (!sd) {
1485 }
1486
1487 switch (source) {
1489 layout->prop(op->ptr, "mix_mode", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1490 layout->prop(op->ptr, "mix_factor", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1491 layout->prop(op->ptr, "boundary_mode", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1492 layout->prop(op->ptr, "propagation_steps", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1493 break;
1494 }
1497 layout->prop(op->ptr, "mix_mode", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1498 layout->prop(op->ptr, "mix_factor", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1499 break;
1500 }
1501}
1502
1504{
1505 ot->name = "Mask From Boundary";
1506 ot->idname = "SCULPT_OT_mask_from_boundary";
1507 ot->description = "Creates a mask based on the boundaries of the surface";
1508
1511 ot->poll = SCULPT_mode_poll;
1512
1513 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1514
1515 RNA_def_enum(ot->srna, "mix_mode", mix_modes, int(ApplyMaskMode::Mix), "Mode", "Mix mode");
1516 RNA_def_float(ot->srna, "mix_factor", 1.0f, 0.0f, 5.0f, "Mix Factor", "", 0.0f, 1.0f);
1517 RNA_def_enum(ot->srna,
1518 "settings_source",
1521 "Settings",
1522 "Use settings from here");
1523
1524 static EnumPropertyItem mask_boundary_modes[] = {
1526 "MESH",
1527 ICON_NONE,
1528 "Mesh",
1529 "Calculate the boundary mask based on disconnected mesh topology islands"},
1531 "FACE_SETS",
1532 ICON_NONE,
1533 "Face Sets",
1534 "Calculate the boundary mask between face sets"},
1535 {0, nullptr, 0, nullptr, nullptr}};
1536
1537 RNA_def_enum(ot->srna,
1538 "boundary_mode",
1539 mask_boundary_modes,
1541 "Mode",
1542 "Boundary type to mask");
1543 RNA_def_int(ot->srna, "propagation_steps", 1, 1, 20, "Propagation Steps", "", 1, 20);
1544}
1545
1547
1548} // namespace mask
1549
1551{
1578
1584
1588}
1589
1591{
1592 filter::modal_keymap(keyconf);
1593}
1594
1595} // namespace blender::ed::sculpt_paint
void BKE_brush_color_set(Scene *scene, const Paint *paint, Brush *brush, const float color[3])
Definition brush.cc:1182
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Depsgraph * CTX_data_depsgraph_pointer(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)
Main * CTX_data_main(const bContext *C)
ToolSettings * CTX_data_tool_settings(const bContext *C)
Depsgraph * CTX_data_depsgraph_on_load(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
wmMsgBus * CTX_wm_message_bus(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
int CustomData_get_offset_named(const CustomData *data, eCustomDataType type, blender::StringRef name)
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
bool BKE_base_is_visible(const View3D *v3d, const Base *base)
Object * BKE_view_layer_active_object_get(const ViewLayer *view_layer)
Base * BKE_view_layer_base_find(ViewLayer *view_layer, Object *ob)
void BKE_mesh_batch_cache_dirty_tag(Mesh *mesh, eMeshBatchDirtyMode mode)
@ BKE_MESH_BATCH_DIRTY_ALL
Definition BKE_mesh.h:38
Mesh * BKE_mesh_from_object(Object *ob)
void BKE_mesh_mirror_apply_mirror_on_axis(Main *bmain, Mesh *mesh, int axis, float dist)
void multires_flush_sculpt_updates(Object *object)
Definition multires.cc:388
General operations, lookup, etc. for blender objects.
void BKE_object_free_derived_caches(Object *ob)
void BKE_sculptsession_free(Object *ob)
Definition paint.cc:2192
const Brush * BKE_paint_brush_for_read(const Paint *paint)
Definition paint.cc:641
Paint * BKE_paint_get_active_from_paintmode(Scene *sce, PaintMode mode)
Definition paint.cc:365
void BKE_paint_init(Main *bmain, Scene *sce, PaintMode mode, const uchar col[3], bool ensure_brushes=true)
Definition paint.cc:1769
MultiresModifierData * BKE_sculpt_multires_active(const Scene *scene, Object *ob)
Definition paint.cc:2373
void BKE_sculpt_update_object_for_edit(Depsgraph *depsgraph, Object *ob_orig, bool is_paint_tool)
Definition paint.cc:2657
void BKE_sculpt_color_layer_create_if_needed(Object *object)
Definition paint.cc:2633
const uchar PAINT_CURSOR_SCULPT[3]
Definition paint.cc:240
void BKE_sculptsession_free_pbvh(Object &object)
Definition paint.cc:2146
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:636
void BKE_sculpt_toolsettings_data_ensure(Main *bmain, Scene *scene)
Definition paint.cc:2744
void BKE_sculpt_mask_layers_ensure(Depsgraph *depsgraph, Main *bmain, Object *ob, MultiresModifierData *mmd)
Definition paint.cc:2666
void BKE_paint_brushes_validate(Main *bmain, Paint *paint)
Definition paint.cc:1109
A BVH for high poly meshes.
const blender::Set< BMVert *, 0 > & BKE_pbvh_bmesh_node_unique_verts(blender::bke::pbvh::BMeshNode *node)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:126
void BKE_scene_graph_evaluated_ensure(Depsgraph *depsgraph, Main *bmain)
Definition scene.cc:2623
CCGKey BKE_subdiv_ccg_key_top_level(const SubdivCCG &subdiv_ccg)
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
bool is_negative_m4(const float mat[4][4])
MINLINE void copy_v3_v3(float r[3], const float a[3])
#define RPT_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1026
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:982
@ BRUSH_AUTOMASKING_CAVITY_NORMAL
@ BRUSH_AUTOMASKING_BOUNDARY_EDGES
@ BRUSH_AUTOMASKING_CAVITY_USE_CURVE
@ BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS
@ BRUSH_AUTOMASKING_CAVITY_ALL
@ BRUSH_AUTOMASKING_CAVITY_INVERTED
@ SCULPT_BRUSH_TYPE_SMOOTH
@ CD_PROP_FLOAT
These structs are the foundation for all linked lists in the library system.
@ ME_SCULPT_DYNAMIC_TOPOLOGY
@ OB_SOLID
eObjectMode
@ OB_MODE_SCULPT
Object is a sort of wrapper for general info.
@ OB_MESH
@ V3D_SHADING_VERTEX_COLOR
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
void ED_paint_cursor_start(Paint *paint, bool(*poll)(bContext *C))
bool ED_operator_object_active_editable_mesh(bContext *C)
BLI_INLINE void IMB_colormanagement_scene_linear_to_srgb_v3(float srgb[3], const float scene_linear[3])
Read Guarded memory(de)allocation.
@ PROP_SKIP_SAVE
Definition RNA_types.hh:330
@ PROP_HIDDEN
Definition RNA_types.hh:324
#define C
Definition RandGen.cpp:29
void uiTemplateCurveMapping(uiLayout *layout, PointerRNA *ptr, blender::StringRefNull propname, int type, bool levels, bool brush, bool neg_slope, bool tone)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_ITEM_NONE
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
#define ND_DRAW
Definition WM_types.hh:458
#define NC_BRUSH
Definition WM_types.hh:382
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:218
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_MODE
Definition WM_types.hh:442
#define NC_SCENE
Definition WM_types.hh:375
#define NA_EDITED
Definition WM_types.hh:581
ReportList * reports
Definition WM_types.hh:1025
#define NC_OBJECT
Definition WM_types.hh:376
@ BM_ELEM_TAG
void BM_log_all_added(BMesh *bm, BMLog *log)
Definition bmesh_log.cc:806
void BM_log_before_all_removed(BMesh *bm, BMLog *log)
Definition bmesh_log.cc:834
void BM_mesh_elem_hflag_disable_all(BMesh *bm, const char htype, const char hflag, const bool respecthide)
void BM_mesh_toolflags_set(BMesh *bm, bool use_toolflags)
#define BM_EDGE
bool BMO_op_callf(BMesh *bm, int flag, const char *fmt,...)
#define BMO_FLAG_DEFAULTS
@ BMO_FLAG_RESPECT_HIDE
BPy_StructRNA * depsgraph
static btMatrix3x3 Add(const btMatrix3x3 &a, const btMatrix3x3 &b)
void resize(const int64_t new_size)
constexpr void fill(const T &value) const
Definition BLI_span.hh:517
constexpr Span< T > as_span() const
Definition BLI_span.hh:661
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
static VArray ForSpan(Span< T > values)
GAttributeReader lookup(const StringRef attribute_id) const
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const void *default_value=nullptr) const
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
bool remove(const StringRef attribute_id)
bool add(const StringRef attribute_id, const AttrDomain domain, const eCustomDataType data_type, const AttributeInit &initializer)
Span< NodeT > nodes() const
void tag_masks_changed(const IndexMask &node_mask)
Definition pbvh.cc:593
void foreach_index(Fn &&fn) const
#define fabsf(x)
static float verts[][3]
CCL_NAMESPACE_BEGIN ccl_device float invert(const float color, const float factor)
Definition invert.h:11
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
static char faces[256]
pbvh::Tree * pbvh_get(Object &object)
Definition paint.cc:2912
IndexMask all_leaf_nodes(const Tree &pbvh, IndexMaskMemory &memory)
Definition pbvh.cc:2544
void node_update_mask_bmesh(int mask_offset, BMeshNode &node)
Definition pbvh.cc:1295
void node_update_mask_mesh(Span< float > mask, MeshNode &node)
Definition pbvh.cc:1237
void node_update_mask_grids(const CCGKey &key, Span< float > masks, GridsNode &node)
Definition pbvh.cc:1265
Bounds< float3 > bounds_get(const Tree &pbvh)
Definition pbvh.cc:1477
Span< float3 > vert_normals_eval(const Depsgraph &depsgraph, const Object &object_orig)
Definition pbvh.cc:2435
bool mode_compat_set(bContext *C, Object *ob, eObjectMode mode, ReportList *reports)
void calc_vert_factors(const Depsgraph &depsgraph, const Object &object, const Cache &automasking, const bke::pbvh::MeshNode &node, Span< int > verts, MutableSpan< float > factors)
void calc_grids_factors(const Depsgraph &depsgraph, const Object &object, const Cache &automasking, const bke::pbvh::GridsNode &node, Span< int > grids, MutableSpan< float > factors)
std::unique_ptr< Cache > cache_init(const Depsgraph &depsgraph, const Sculpt &sd, const Brush *brush, Object &ob)
void SCULPT_OT_cloth_filter(wmOperatorType *ot)
float4 color_vert_get(OffsetIndices< int > faces, Span< int > corner_verts, GroupedSpan< int > vert_to_face_map, GSpan color_attribute, bke::AttrDomain color_domain, int vert)
void SCULPT_OT_color_filter(wmOperatorType *ot)
bke::GAttributeReader active_color_attribute(const Mesh &mesh)
void enable_ex(Main &bmain, Depsgraph &depsgraph, Object &ob)
void SCULPT_OT_detail_flood_fill(wmOperatorType *ot)
void disable_with_undo(Main &bmain, Depsgraph &depsgraph, Scene &scene, Object &ob)
void SCULPT_OT_sample_detail_size(wmOperatorType *ot)
WarnFlag check_attribute_warning(Scene &scene, Object &ob)
void SCULPT_OT_dynamic_topology_toggle(wmOperatorType *ot)
void SCULPT_OT_dyntopo_detail_size_edit(wmOperatorType *ot)
void SCULPT_OT_expand(wmOperatorType *ot)
void SCULPT_OT_face_set_lasso_gesture(wmOperatorType *ot)
void initialize_none_to_id(Mesh *mesh, int new_id)
void SCULPT_OT_face_set_line_gesture(wmOperatorType *ot)
void SCULPT_OT_face_set_polyline_gesture(wmOperatorType *ot)
void SCULPT_OT_face_sets_init(wmOperatorType *ot)
void SCULPT_OT_face_sets_create(wmOperatorType *ot)
void SCULPT_OT_face_set_change_visibility(wmOperatorType *ot)
void SCULPT_OT_face_sets_randomize_colors(wmOperatorType *ot)
void SCULPT_OT_face_sets_edit(wmOperatorType *ot)
void SCULPT_OT_face_set_box_gesture(wmOperatorType *ot)
void SCULPT_OT_mesh_filter(wmOperatorType *ot)
wmKeyMap * modal_keymap(wmKeyConfig *keyconf)
static float color_delta_get(const float3 &color_a, const float3 &color_b, const float threshold, const bool invert)
static void apply_mask_from_settings(const Depsgraph &depsgraph, Object &object, bke::pbvh::Tree &pbvh, const IndexMask &node_mask, const auto_mask::Cache &automasking, const ApplyMaskMode mode, const float factor, const bool invert_automask)
static wmOperatorStatus mask_from_boundary_exec(bContext *C, wmOperator *op)
void SCULPT_OT_mask_filter(wmOperatorType *ot)
void scatter_mask_grids(const Span< float > mask, SubdivCCG &subdiv_ccg, const Span< int > grids)
static wmOperatorStatus mask_by_color_exec(bContext *C, wmOperator *op)
static void apply_mask_mesh(const Depsgraph &depsgraph, const Object &object, const Span< bool > hide_vert, const auto_mask::Cache &automasking, const ApplyMaskMode mode, const float factor, const bool invert_automask, const bke::pbvh::MeshNode &node, LocalData &tls, const MutableSpan< float > mask)
static void apply_mask_grids(const Depsgraph &depsgraph, Object &object, const auto_mask::Cache &automasking, const ApplyMaskMode mode, const float factor, const bool invert_automask, const bke::pbvh::GridsNode &node, LocalData &tls)
static wmOperatorStatus mask_by_color(bContext *C, wmOperator *op, const float2 region_location)
static EnumPropertyItem mix_modes[]
static void mask_from_cavity_ui(bContext *C, wmOperator *op)
void update_mask_mesh(const Depsgraph &depsgraph, Object &object, const IndexMask &node_mask, FunctionRef< void(MutableSpan< float >, Span< int >)> update_fn)
void SCULPT_OT_mask_init(wmOperatorType *ot)
static void SCULPT_OT_mask_by_color(wmOperatorType *ot)
void scatter_mask_bmesh(const Span< float > mask, const BMesh &bm, const Set< BMVert *, 0 > &verts)
void gather_mask_bmesh(const BMesh &bm, const Set< BMVert *, 0 > &verts, const MutableSpan< float > r_mask)
void mix_new_masks(const Span< float > new_masks, const float factor, const MutableSpan< float > masks)
Definition paint_mask.cc:97
static void mask_by_color_full_mesh(const Depsgraph &depsgraph, Object &object, const int vert, const float threshold, const bool invert, const bool preserve_mask)
static void calc_new_masks(const ApplyMaskMode mode, const Span< float > node_mask, const MutableSpan< float > new_mask)
static wmOperatorStatus mask_by_color_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus mask_from_cavity_exec(bContext *C, wmOperator *op)
static void mask_from_boundary_ui(bContext *C, wmOperator *op)
static void apply_mask_bmesh(const Depsgraph &depsgraph, Object &object, const auto_mask::Cache &automasking, const ApplyMaskMode mode, const float factor, const float invert_automask, bke::pbvh::BMeshNode &node, LocalData &tls)
static void mask_by_color_contiguous_mesh(const Depsgraph &depsgraph, Object &object, const int vert, const float threshold, const bool invert, const bool preserve_mask)
void invert_mask(const MutableSpan< float > masks)
void gather_mask_grids(const SubdivCCG &subdiv_ccg, const Span< int > grids, const MutableSpan< float > r_mask)
static void SCULPT_OT_mask_from_cavity(wmOperatorType *ot)
static EnumPropertyItem settings_sources[]
static void SCULPT_OT_mask_from_boundary(wmOperatorType *ot)
static float final_mask_get(const float current_mask, const float new_mask, const bool invert, const bool preserve_mask)
void clamp_mask(const MutableSpan< float > masks)
void SCULPT_OT_project_line_gesture(wmOperatorType *ot)
void SCULPT_OT_trim_lasso_gesture(wmOperatorType *ot)
void SCULPT_OT_trim_box_gesture(wmOperatorType *ot)
void SCULPT_OT_trim_line_gesture(wmOperatorType *ot)
void SCULPT_OT_trim_polyline_gesture(wmOperatorType *ot)
void push_nodes(const Depsgraph &depsgraph, Object &object, const IndexMask &node_mask, const Type type)
void push_begin_ex(const Scene &, Object &ob, const char *name)
void push_enter_sculpt_mode(const Scene &, Object &ob, const wmOperator *op)
void geometry_begin(const Scene &scene, Object &ob, const wmOperator *op)
void push_node(const Depsgraph &depsgraph, const Object &object, const bke::pbvh::Node *node, const Type type)
void push_begin(const Scene &scene, Object &ob, const wmOperator *op)
void object_sculpt_mode_enter(Main &bmain, Depsgraph &depsgraph, Scene &scene, Object &ob, bool force_dyntopo, ReportList *reports)
static bool no_multires_poll(bContext *C)
static void init_sculpt_mode_session(Main &bmain, Depsgraph &depsgraph, Scene &scene, Object &ob)
static wmOperatorStatus set_persistent_base_exec(bContext *C, wmOperator *)
Definition sculpt_ops.cc:84
void vert_random_access_ensure(Object &object)
Definition sculpt.cc:142
static void SCULPT_OT_symmetrize(wmOperatorType *ot)
static wmOperatorStatus optimize_exec(bContext *C, wmOperator *)
void keymap_sculpt(wmKeyConfig *keyconf)
static void SCULPT_OT_sculptmode_toggle(wmOperatorType *ot)
void ensure_valid_pivot(const Object &ob, Scene &scene)
bool color_supported_check(const Scene &scene, Object &object, ReportList *reports)
Definition sculpt.cc:5457
static wmOperatorStatus sculpt_mode_toggle_exec(bContext *C, wmOperator *op)
void scale_factors(MutableSpan< float > factors, float strength)
Definition sculpt.cc:7493
static void SCULPT_OT_sample_color(wmOperatorType *ot)
void object_sculpt_mode_exit(Main &bmain, Depsgraph &depsgraph, Scene &scene, Object &ob)
static void SCULPT_OT_set_persistent_base(wmOperatorType *ot)
static void SCULPT_OT_optimize(wmOperatorType *ot)
bool cursor_geometry_info_update(bContext *C, CursorGeometryInfo *out, const float2 &mval, const bool use_sampled_normal)
Definition sculpt.cc:4662
void flush_update_done(const bContext *C, Object &ob, const UpdateType update_type)
Definition sculpt.cc:5129
void scatter_data_mesh(Span< T > src, Span< int > indices, MutableSpan< T > dst)
Definition sculpt.cc:6419
void SCULPT_OT_brush_stroke(wmOperatorType *ot)
Definition sculpt.cc:5751
void gather_data_mesh(Span< T > src, Span< int > indices, MutableSpan< T > dst)
Definition sculpt.cc:6379
static wmOperatorStatus symmetrize_exec(bContext *C, wmOperator *op)
void fill_factor_from_hide(Span< bool > hide_vert, Span< int > verts, MutableSpan< float > r_factors)
Definition sculpt.cc:6759
void SCULPT_OT_set_pivot_position(wmOperatorType *ot)
static wmOperatorStatus sample_color_invoke(bContext *C, wmOperator *op, const wmEvent *)
T distance(const T &a, const T &b)
T midpoint(const T &a, const T &b)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
Definition BLI_color.hh:342
VecBase< float, 3 > float3
void paint_cursor_delete_textures()
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)
int RNA_int_get(PointerRNA *ptr, const char *name)
float RNA_float_get(PointerRNA *ptr, const char *name)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_int_array(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_float(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
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_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)
void RNA_def_property_ui_range(PropertyRNA *prop, double min, double max, double step, int precision)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
bool SCULPT_brush_cursor_poll(bContext *C)
Definition sculpt.cc:3717
bool SCULPT_mode_poll(bContext *C)
Definition sculpt.cc:3660
void SCULPT_tag_update_overlays(bContext *C)
Definition sculpt.cc:760
#define MASK_BY_COLOR_SLOPE
float automasking_cavity_factor
char sculpt_brush_type
int automasking_flags
int automasking_cavity_blur_steps
int automasking_boundary_edges_propagation_steps
struct CurveMapping * automasking_cavity_curve
int grid_area
Definition BKE_ccg.hh:35
void * first
ListBase wm
Definition BKE_main.hh:276
float scale[3]
struct SculptSession * sculpt
struct ToolSettings * toolsettings
BMLog * bm_log
Definition BKE_paint.hh:412
struct SculptSession::@334041245165342176141020054324004000173214370340 persistent
blender::Array< blender::float3 > sculpt_persistent_co
Definition BKE_paint.hh:471
SubdivCCG * subdiv_ccg
Definition BKE_paint.hh:415
blender::Array< blender::float3 > sculpt_persistent_no
Definition BKE_paint.hh:472
ActiveVert active_vert() const
Definition paint.cc:2227
blender::Array< float > sculpt_persistent_disp
Definition BKE_paint.hh:473
eObjectMode mode_type
Definition BKE_paint.hh:511
int automasking_boundary_edges_propagation_steps
float automasking_cavity_factor
int automasking_cavity_blur_steps
int symmetrize_direction
struct CurveMapping * automasking_cavity_curve_op
struct CurveMapping * automasking_cavity_curve
int automasking_flags
blender::Array< blender::float3 > normals
blender::Array< float > masks
blender::Array< blender::float3 > positions
struct UnifiedPaintSettings unified_paint_settings
View3DShading shading
VecBase< T, 3 > xyz() const
const ImplicitSharingInfo * sharing_info
void execute(Object &object, GroupedSpan< int > vert_to_face_map, FunctionRef< bool(int from_v, int to_v)> func)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
int mval[2]
Definition WM_types.hh:760
struct ReportList * reports
struct uiLayout * layout
struct PointerRNA * ptr
i
Definition text_draw.cc:230
uint len
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4225
#define WM_msg_publish_rna_prop(mbus, id_, data_, type_, prop_)
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))
void WM_toolsystem_update_from_context_view3d(bContext *C)
uint8_t flag
Definition wm_window.cc:139