Blender V5.0
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_paint_types.hh"
37#include "BKE_report.hh"
38#include "BKE_scene.hh"
39#include "BKE_subdiv_ccg.hh"
40
41#include "DEG_depsgraph.hh"
42
44
45#include "WM_api.hh"
46#include "WM_message.hh"
47#include "WM_toolsystem.hh"
48#include "WM_types.hh"
49
50#include "ED_image.hh"
51#include "ED_object.hh"
52#include "ED_screen.hh"
53#include "ED_sculpt.hh"
54
55#include "mesh_brush_common.hh"
56#include "paint_intern.hh"
57#include "paint_mask.hh"
58#include "sculpt_automask.hh"
59#include "sculpt_color.hh"
60#include "sculpt_dyntopo.hh"
61#include "sculpt_flood_fill.hh"
62#include "sculpt_intern.hh"
63#include "sculpt_undo.hh"
64
65#include "RNA_access.hh"
66#include "RNA_define.hh"
67
68#include "UI_interface.hh"
70#include "UI_resources.hh"
71
72#include "bmesh.hh"
73
74#include <cmath>
75#include <cstring>
76
78
79/* -------------------------------------------------------------------- */
82
84{
87 SculptSession *ss = ob.sculpt;
88
89 const View3D *v3d = CTX_wm_view3d(C);
90 const Base *base = CTX_data_active_base(C);
91 if (!BKE_base_is_visible(v3d, base)) {
92 return OPERATOR_CANCELLED;
93 }
94
95 if (!ss) {
96 return OPERATOR_CANCELLED;
97 }
98
100
101 switch (bke::object::pbvh_get(ob)->type()) {
103 Mesh &mesh = *static_cast<Mesh *>(ob.data);
104 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
105 attributes.remove(".sculpt_persistent_co");
106 attributes.remove(".sculpt_persistent_no");
107 attributes.remove(".sculpt_persistent_disp");
108
109 const bke::AttributeReader positions = attributes.lookup<float3>("position");
110 if (positions.sharing_info && positions.varray.is_span()) {
111 attributes.add<float3>(
112 ".sculpt_persistent_co",
114 bke::AttributeInitShared(positions.varray.get_internal_span().data(),
115 *positions.sharing_info));
116 }
117 else {
118 attributes.add<float3>(".sculpt_persistent_co",
121 }
122
123 const Span<float3> vert_normals = bke::pbvh::vert_normals_eval(*depsgraph, ob);
124 attributes.add<float3>(".sculpt_persistent_no",
127 break;
128 }
130 const SubdivCCG &subdiv_ccg = *ss->subdiv_ccg;
132 ss->persistent.sculpt_persistent_no = subdiv_ccg.normals;
133 ss->persistent.sculpt_persistent_disp = Array<float>(subdiv_ccg.positions.size(), 0.0f);
134 ss->persistent.grid_size = subdiv_ccg.grid_size;
135 ss->persistent.grids_num = subdiv_ccg.grids_num;
136 break;
137 }
139 return OPERATOR_CANCELLED;
140 }
141 }
142
143 return OPERATOR_FINISHED;
144}
145
147{
148 ot->name = "Set Persistent Base";
149 ot->idname = "SCULPT_OT_set_persistent_base";
150 ot->description = "Reset the copy of the mesh that is being sculpted on";
151
153 ot->poll = SCULPT_mode_poll;
154
156}
157
159
160/* -------------------------------------------------------------------- */
163
175
176/* The BVH gets less optimal more quickly with dynamic topology than
177 * regular sculpting. There is no doubt more clever stuff we can do to
178 * optimize it on the fly, but for now this gives the user a nicer way
179 * to recalculate it than toggling modes. */
181{
182 ot->name = "Rebuild BVH";
183 ot->idname = "SCULPT_OT_optimize";
184 ot->description = "Recalculate the sculpt BVH to improve performance";
185
186 ot->exec = optimize_exec;
187 ot->poll = SCULPT_mode_poll;
188
189 ot->flag = OPTYPE_REGISTER;
190}
191
193
194/* -------------------------------------------------------------------- */
197
199{
201 if (!ob) {
202 return false;
203 }
204 if (ob->type != OB_MESH) {
205 return false;
206 }
207 const bke::pbvh::Tree *pbvh = bke::object::pbvh_get(*ob);
208 if (SCULPT_mode_poll(C) && ob->sculpt && pbvh) {
209 return pbvh->type() != bke::pbvh::Type::Grids;
210 }
211 return false;
212}
213
215{
216 Main *bmain = CTX_data_main(C);
217 const Scene &scene = *CTX_data_scene(C);
218 const Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
220 const Sculpt &sd = *CTX_data_tool_settings(C)->sculpt;
221 SculptSession &ss = *ob.sculpt;
222 const bke::pbvh::Tree *pbvh = bke::object::pbvh_get(ob);
223 const float dist = RNA_float_get(op->ptr, "merge_tolerance");
224
225 if (!pbvh) {
226 return OPERATOR_CANCELLED;
227 }
228
229 const View3D *v3d = CTX_wm_view3d(C);
230 const Base *base = CTX_data_active_base(C);
231 if (!BKE_base_is_visible(v3d, base)) {
232 return OPERATOR_CANCELLED;
233 }
234
235 switch (pbvh->type()) {
237 /* Dyntopo Symmetrize. */
238
239 /* To simplify undo for symmetrize, all BMesh elements are logged
240 * as deleted, then after symmetrize operation all BMesh elements
241 * are logged as added (as opposed to attempting to store just the
242 * parts that symmetrize modifies). */
243 undo::push_begin(scene, ob, op);
246
247 BM_mesh_toolflags_set(ss.bm, true);
248
249 /* Symmetrize and re-triangulate. */
250 BMO_op_callf(ss.bm,
252 "symmetrize input=%avef direction=%i dist=%f use_shapekey=%b",
254 dist,
255 true);
257
258 /* Bisect operator flags edges (keep tags clean for edge queue). */
260
261 BM_mesh_toolflags_set(ss.bm, false);
262
263 /* Finish undo. */
264 BM_log_all_added(ss.bm, ss.bm_log);
265 undo::push_end(ob);
266
267 break;
268 }
270 /* Mesh Symmetrize. */
271 undo::geometry_begin(scene, ob, op);
272 Mesh *mesh = static_cast<Mesh *>(ob.data);
273
275
278
279 break;
280 }
282 return OPERATOR_CANCELLED;
283 }
284
288
289 return OPERATOR_FINISHED;
290}
291
293{
294 ot->name = "Symmetrize";
295 ot->idname = "SCULPT_OT_symmetrize";
296 ot->description = "Symmetrize the topology modifications";
297
298 ot->exec = symmetrize_exec;
299 ot->poll = no_multires_poll;
300
301 PropertyRNA *prop = RNA_def_float(ot->srna,
302 "merge_tolerance",
303 0.0005f,
304 0.0f,
305 std::numeric_limits<float>::max(),
306 "Merge Distance",
307 "Distance within which symmetrical vertices are merged",
308 0.0f,
309 1.0f);
310
311 RNA_def_property_ui_range(prop, 0.0, std::numeric_limits<float>::max(), 0.001, 5);
312}
313
315
316/* -------------------------------------------------------------------- */
319
320static void init_sculpt_mode_session(Main &bmain, Depsgraph &depsgraph, Scene &scene, Object &ob)
321{
322 /* Create persistent sculpt mode data. */
324
325 /* Create sculpt mode session data. */
326 if (ob.sculpt != nullptr) {
328 }
329 ob.sculpt = MEM_new<SculptSession>(__func__);
331
332 /* Trigger evaluation of modifier stack to ensure
333 * multires modifier sets .runtime.ccg in
334 * the evaluated mesh.
335 */
337
339
340 /* This function expects a fully evaluated depsgraph. */
342
343 Mesh &mesh = *static_cast<Mesh *>(ob.data);
344 if (mesh.attributes().contains(".sculpt_face_set")) {
345 /* Here we can detect geometry that was just added to Sculpt Mode as it has the
346 * SCULPT_FACE_SET_NONE assigned, so we can create a new Face Set for it. */
347 /* In sculpt mode all geometry that is assigned to SCULPT_FACE_SET_NONE is considered as not
348 * initialized, which is used is some operators that modify the mesh topology to perform
349 * certain actions in the new faces. After these operations are finished, all faces should have
350 * a valid face set ID assigned (different from SCULPT_FACE_SET_NONE) to manage their
351 * visibility correctly. */
352 /* TODO(pablodp606): Based on this we can improve the UX in future tools for creating new
353 * objects, like moving the transform pivot position to the new area or masking existing
354 * geometry. */
355 const int new_face_set = face_set::find_next_available_id(ob);
356 face_set::initialize_none_to_id(static_cast<Mesh *>(ob.data), new_face_set);
357 }
358}
359
361{
362 bke::PaintRuntime &paint_runtime = *paint.runtime;
363 const bke::pbvh::Tree *pbvh = bke::object::pbvh_get(ob);
364
365 /* Account for the case where no objects are evaluated. */
366 if (!pbvh) {
367 return;
368 }
369
370 /* No valid pivot? Use bounding box center. */
371 if (paint_runtime.average_stroke_counter == 0 || !paint_runtime.last_stroke_valid) {
373 const float3 center = math::midpoint(bounds.min, bounds.max);
374 const float3 location = math::transform_point(ob.object_to_world(), center);
375
376 copy_v3_v3(paint_runtime.average_stroke_accum, location);
377 paint_runtime.average_stroke_counter = 1;
378
379 /* Update last stroke position. */
380 paint_runtime.last_stroke_valid = true;
381 }
382}
383
385 Depsgraph &depsgraph,
386 Scene &scene,
387 Object &ob,
388 const bool force_dyntopo,
389 ReportList *reports)
390{
391 const int mode_flag = OB_MODE_SCULPT;
393
394 /* Re-triangulating the mesh for position changes in sculpt mode isn't worth the performance
395 * impact, so delay triangulation updates until the user exits sculpt mode. */
396 mesh->runtime->corner_tris_cache.freeze();
397
398 /* Enter sculpt mode. */
399 ob.mode |= mode_flag;
400
401 init_sculpt_mode_session(bmain, depsgraph, scene, ob);
402
403 if (!(fabsf(ob.scale[0] - ob.scale[1]) < 1e-4f && fabsf(ob.scale[1] - ob.scale[2]) < 1e-4f)) {
405 reports, RPT_WARNING, "Object has non-uniform scale, sculpting may be unpredictable");
406 }
407 else if (is_negative_m4(ob.object_to_world().ptr())) {
408 BKE_report(reports, RPT_WARNING, "Object has negative scale, sculpting may be unpredictable");
409 }
410
412 BKE_paint_init(&bmain, &scene, PaintMode::Sculpt);
413
415
416 /* Check dynamic-topology flag; re-enter dynamic-topology mode when changing modes,
417 * As long as no data was added that is not supported. */
418 if (mesh->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) {
420
421 const char *message_unsupported = nullptr;
422 if (mesh->corners_num != mesh->faces_num * 3) {
423 message_unsupported = RPT_("non-triangle face");
424 }
425 else if (mmd != nullptr) {
426 message_unsupported = RPT_("multi-res modifier");
427 }
428 else {
430 if (flag == 0) {
431 /* pass */
432 }
433 else if (flag & dyntopo::VDATA) {
434 message_unsupported = RPT_("vertex data");
435 }
436 else if (flag & dyntopo::EDATA) {
437 message_unsupported = RPT_("edge data");
438 }
439 else if (flag & dyntopo::LDATA) {
440 message_unsupported = RPT_("face data");
441 }
442 else if (flag & dyntopo::MODIFIER) {
443 message_unsupported = RPT_("constructive modifier");
444 }
445 else {
447 }
448 }
449
450 if ((message_unsupported == nullptr) || force_dyntopo) {
451 /* Needed because we may be entering this mode before the undo system loads. */
452 wmWindowManager *wm = static_cast<wmWindowManager *>(bmain.wm.first);
453 const bool has_undo = wm->runtime->undo_stack != nullptr;
454 /* Undo push is needed to prevent memory leak. */
455 if (has_undo) {
456 undo::push_begin_ex(scene, ob, "Dynamic topology enable");
457 }
458 dyntopo::enable_ex(bmain, depsgraph, ob);
459 if (has_undo) {
461 undo::push_end(ob);
462 }
463 }
464 else {
466 reports, RPT_WARNING, "Dynamic Topology found: %s, disabled", message_unsupported);
468 }
469 }
470
472
473 /* Flush object mode. */
475}
476
478{
479 Main &bmain = *CTX_data_main(C);
480 Scene &scene = *CTX_data_scene(C);
481 ViewLayer &view_layer = *CTX_data_view_layer(C);
482 BKE_view_layer_synced_ensure(&scene, &view_layer);
483 Object &ob = *BKE_view_layer_active_object_get(&view_layer);
484 object_sculpt_mode_enter(bmain, depsgraph, scene, ob, false, reports);
485}
486
487void object_sculpt_mode_exit(Main &bmain, Depsgraph &depsgraph, Scene &scene, Object &ob)
488{
489 const int mode_flag = OB_MODE_SCULPT;
491
492 mesh->runtime->corner_tris_cache.unfreeze();
493
495
496 /* Not needed for now. */
497#if 0
499 const int flush_recalc = ed_object_sculptmode_flush_recalc_flag(scene, ob, mmd);
500#endif
501
502 /* Always for now, so leaving sculpt mode always ensures scene is in
503 * a consistent state. */
504 if (true || /* flush_recalc || */ (ob.sculpt && ob.sculpt->bm)) {
506 }
507
508 if (mesh->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) {
509 /* Dynamic topology must be disabled before exiting sculpt
510 * mode to ensure the undo stack stays in a consistent
511 * state. */
512 dyntopo::disable_with_undo(bmain, depsgraph, scene, ob);
513
514 /* Store so we know to re-enable when entering sculpt mode. */
516 }
517
518 /* Leave sculpt mode. */
519 ob.mode &= ~mode_flag;
520
522
524
525 /* Never leave derived meshes behind. */
527
528 /* Flush object mode. */
530}
531
533{
534 Main &bmain = *CTX_data_main(C);
535 Scene &scene = *CTX_data_scene(C);
536 ViewLayer &view_layer = *CTX_data_view_layer(C);
537 BKE_view_layer_synced_ensure(&scene, &view_layer);
538 Object &ob = *BKE_view_layer_active_object_get(&view_layer);
539 object_sculpt_mode_exit(bmain, depsgraph, scene, ob);
540}
541
543{
545 Main &bmain = *CTX_data_main(C);
547 Scene &scene = *CTX_data_scene(C);
548 ToolSettings &ts = *scene.toolsettings;
549 ViewLayer &view_layer = *CTX_data_view_layer(C);
550 BKE_view_layer_synced_ensure(&scene, &view_layer);
551 Object &ob = *BKE_view_layer_active_object_get(&view_layer);
552 const int mode_flag = OB_MODE_SCULPT;
553 const bool is_mode_set = (ob.mode & mode_flag) != 0;
554
555 if (!is_mode_set) {
556 if (!object::mode_compat_set(C, &ob, eObjectMode(mode_flag), op->reports)) {
557 return OPERATOR_CANCELLED;
558 }
559 }
560
561 if (is_mode_set) {
562 object_sculpt_mode_exit(bmain, *depsgraph, scene, ob);
563 }
564 else {
565 if (depsgraph) {
567 }
568 object_sculpt_mode_enter(bmain, *depsgraph, scene, ob, false, op->reports);
570
571 if (ob.mode & mode_flag) {
572 Mesh *mesh = static_cast<Mesh *>(ob.data);
573 /* Dyntopo adds its own undo step. */
574 if ((mesh->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) == 0) {
575 /* Without this the memfile undo step is used,
576 * while it works it causes lag when undoing the first undo step, see #71564. */
578 if (wm->op_undo_depth <= 1) {
579 undo::push_enter_sculpt_mode(scene, ob, op);
580 undo::push_end(ob);
581 }
582 }
583 }
584 }
585
587
588 WM_msg_publish_rna_prop(mbus, &ob.id, &ob, Object, mode);
589
591
592 return OPERATOR_FINISHED;
593}
594
596{
597 ot->name = "Sculpt Mode";
598 ot->idname = "SCULPT_OT_sculptmode_toggle";
599 ot->description = "Toggle sculpt mode in 3D view";
600
603
605}
606
608
609/* -------------------------------------------------------------------- */
612
614{
616 Scene &scene = *CTX_data_scene(C);
618 Brush &brush = *BKE_paint_brush(&sd.paint);
619 SculptSession &ss = *ob.sculpt;
620
621 if (!color_supported_check(scene, ob, op->reports)) {
622 return OPERATOR_CANCELLED;
623 }
624
625 const View3D *v3d = CTX_wm_view3d(C);
626 const Base *base = CTX_data_active_base(C);
627 if (!BKE_base_is_visible(v3d, base)) {
628 return OPERATOR_CANCELLED;
629 }
630
632
633 const Mesh &mesh = *static_cast<const Mesh *>(ob.data);
634 const OffsetIndices<int> faces = mesh.faces();
635 const Span<int> corner_verts = mesh.corner_verts();
636 const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
638
639 float4 active_vertex_color;
640 if (!color_attribute || std::holds_alternative<std::monostate>(ss.active_vert())) {
641 active_vertex_color = float4(1.0f);
642 }
643 else {
644 const GVArraySpan colors = *color_attribute;
645 active_vertex_color = color::color_vert_get(faces,
646 corner_verts,
647 vert_to_face_map,
648 colors,
649 color_attribute.domain,
650 std::get<int>(ss.active_vert()));
651 }
652
653 BKE_brush_color_set(&sd.paint, &brush, active_vertex_color);
654
656
657 return OPERATOR_FINISHED;
658}
659
661{
662 ot->name = "Sample Color";
663 ot->idname = "SCULPT_OT_sample_color";
664 ot->description = "Sample the vertex color of the active vertex";
665
666 ot->invoke = sample_color_invoke;
667 ot->poll = SCULPT_mode_poll;
668
670}
671
673
674namespace mask {
675
676/* -------------------------------------------------------------------- */
679
689#define MASK_BY_COLOR_SLOPE 0.25f
690
691static float color_delta_get(const float3 &color_a,
692 const float3 &color_b,
693 const float threshold,
694 const bool invert)
695{
696 float len = math::distance(color_a, color_b);
697 /* Normalize len to the (0, 1) range. */
699
700 if (len < threshold - MASK_BY_COLOR_SLOPE) {
701 len = 1.0f;
702 }
703 else if (len >= threshold) {
704 len = 0.0f;
705 }
706 else {
707 len = (-len + threshold) / MASK_BY_COLOR_SLOPE;
708 }
709
710 if (invert) {
711 return 1.0f - len;
712 }
713 return len;
714}
715
716static float final_mask_get(const float current_mask,
717 const float new_mask,
718 const bool invert,
719 const bool preserve_mask)
720{
721 if (preserve_mask) {
722 if (invert) {
723 return std::min(current_mask, new_mask);
724 }
725 return std::max(current_mask, new_mask);
726 }
727 return new_mask;
728}
729
730static void mask_by_color_contiguous_mesh(const Depsgraph &depsgraph,
731 Object &object,
732 const int vert,
733 const float threshold,
734 const bool invert,
735 const bool preserve_mask)
736{
737 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
738 const Mesh &mesh = *static_cast<const Mesh *>(object.data);
739 const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
740 const bke::AttributeAccessor attributes = mesh.attributes();
741 const VArraySpan colors = *attributes.lookup_or_default<ColorGeometry4f>(
742 mesh.active_color_attribute, bke::AttrDomain::Point, {});
743 const float4 active_color = float4(colors[vert]);
744
745 Array<float> new_mask(mesh.verts_num, invert ? 1.0f : 0.0f);
746
747 flood_fill::FillDataMesh flood(mesh.verts_num);
748 flood.add_initial(vert);
749
750 flood.execute(object, vert_to_face_map, [&](int /*from_v*/, int to_v) {
751 const float4 current_color = float4(colors[to_v]);
752
753 float new_vertex_mask = color_delta_get(
754 current_color.xyz(), active_color.xyz(), threshold, invert);
755 new_mask[to_v] = new_vertex_mask;
756
757 float len = math::distance(current_color.xyz(), active_color.xyz());
759 return len <= threshold;
760 });
761
762 IndexMaskMemory memory;
763 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
764
766 depsgraph, object, node_mask, [&](MutableSpan<float> node_masks, const Span<int> verts) {
767 for (const int i : verts.index_range()) {
768 node_masks[i] = final_mask_get(node_masks[i], new_mask[verts[i]], invert, preserve_mask);
769 }
770 });
771}
772
773static void mask_by_color_full_mesh(const Depsgraph &depsgraph,
774 Object &object,
775 const int vert,
776 const float threshold,
777 const bool invert,
778 const bool preserve_mask)
779{
780 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
781 const Mesh &mesh = *static_cast<const Mesh *>(object.data);
782 const bke::AttributeAccessor attributes = mesh.attributes();
783 const VArraySpan colors = *attributes.lookup_or_default<ColorGeometry4f>(
784 mesh.active_color_attribute, bke::AttrDomain::Point, {});
785 const float4 active_color = float4(colors[vert]);
786
787 IndexMaskMemory memory;
788 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
789
791 depsgraph, object, node_mask, [&](MutableSpan<float> node_masks, const Span<int> verts) {
792 for (const int i : verts.index_range()) {
793 const float4 current_color = float4(colors[verts[i]]);
794 const float current_mask = node_masks[i];
795 const float new_mask = color_delta_get(
796 active_color.xyz(), current_color.xyz(), threshold, invert);
797 node_masks[i] = final_mask_get(current_mask, new_mask, invert, preserve_mask);
798 }
799 });
800}
801
802static wmOperatorStatus mask_by_color(bContext *C, wmOperator *op, const float2 region_location)
803{
804 const Scene &scene = *CTX_data_scene(C);
807 SculptSession &ss = *ob.sculpt;
808 View3D *v3d = CTX_wm_view3d(C);
809
810 {
811 if (v3d && v3d->shading.type == OB_SOLID) {
813 }
814 }
815
816 const Base *base = CTX_data_active_base(C);
817 if (!BKE_base_is_visible(v3d, base)) {
818 return OPERATOR_CANCELLED;
819 }
820
821 /* Color data is not available in multi-resolution or dynamic topology. */
822 if (!color_supported_check(scene, ob, op->reports)) {
823 return OPERATOR_CANCELLED;
824 }
825
827
828 /* Tools that are not brushes do not have the brush gizmo to update the vertex as the mouse move,
829 * so it needs to be updated here. */
831 cursor_geometry_info_update(C, &cgi, region_location, false);
832
833 if (std::holds_alternative<std::monostate>(ss.active_vert())) {
834 return OPERATOR_CANCELLED;
835 }
836
837 undo::push_begin(scene, ob, op);
839
840 const float threshold = RNA_float_get(op->ptr, "threshold");
841 const bool invert = RNA_boolean_get(op->ptr, "invert");
842 const bool preserve_mask = RNA_boolean_get(op->ptr, "preserve_previous_mask");
843
844 const int active_vert = std::get<int>(ss.active_vert());
845 if (RNA_boolean_get(op->ptr, "contiguous")) {
846 mask_by_color_contiguous_mesh(*depsgraph, ob, active_vert, threshold, invert, preserve_mask);
847 }
848 else {
849 mask_by_color_full_mesh(*depsgraph, ob, active_vert, threshold, invert, preserve_mask);
850 }
851
852 undo::push_end(ob);
853
856
857 return OPERATOR_FINISHED;
858}
859
861{
862 int2 mval;
863 RNA_int_get_array(op->ptr, "location", mval);
864 return mask_by_color(C, op, float2(mval[0], mval[1]));
865}
866
868{
869 RNA_int_set_array(op->ptr, "location", event->mval);
870 return mask_by_color(C, op, float2(event->mval[0], event->mval[1]));
871}
872
874{
875 ot->name = "Mask by Color";
876 ot->idname = "SCULPT_OT_mask_by_color";
877 ot->description = "Creates a mask based on the active color attribute";
878
879 ot->invoke = mask_by_color_invoke;
880 ot->exec = mask_by_color_exec;
881 ot->poll = SCULPT_mode_poll;
882
884
885 ot->prop = RNA_def_boolean(
886 ot->srna, "contiguous", false, "Contiguous", "Mask only contiguous color areas");
887
888 ot->prop = RNA_def_boolean(ot->srna, "invert", false, "Invert", "Invert the generated mask");
889 ot->prop = RNA_def_boolean(
890 ot->srna,
891 "preserve_previous_mask",
892 false,
893 "Preserve Previous Mask",
894 "Preserve the previous mask and add or subtract the new one generated by the colors");
895
896 RNA_def_float(ot->srna,
897 "threshold",
898 0.35f,
899 0.0f,
900 1.0f,
901 "Threshold",
902 "How much changes in color affect the mask generation",
903 0.0f,
904 1.0f);
905
906 ot->prop = RNA_def_int_array(ot->srna,
907 "location",
908 2,
909 nullptr,
910 0,
911 SHRT_MAX,
912 "Location",
913 "Region coordinates of sampling",
914 0,
915 SHRT_MAX);
917}
918
920
921/* -------------------------------------------------------------------- */
924
932
934 {int(ApplyMaskMode::Mix), "MIX", ICON_NONE, "Mix", ""},
935 {int(ApplyMaskMode::Multiply), "MULTIPLY", ICON_NONE, "Multiply", ""},
936 {int(ApplyMaskMode::Divide), "DIVIDE", ICON_NONE, "Divide", ""},
937 {int(ApplyMaskMode::Add), "ADD", ICON_NONE, "Add", ""},
938 {int(ApplyMaskMode::Subtract), "SUBTRACT", ICON_NONE, "Subtract", ""},
939 {0, nullptr, 0, nullptr, nullptr},
940};
941
942enum class MaskSettingsSource : int8_t { Operator, Scene, Brush };
943
946 "OPERATOR",
947 ICON_NONE,
948 "Operator",
949 "Use settings from operator properties"},
950 {int(MaskSettingsSource::Brush), "BRUSH", ICON_NONE, "Brush", "Use settings from brush"},
951 {int(MaskSettingsSource::Scene), "SCENE", ICON_NONE, "Scene", "Use settings from scene"},
952 {0, nullptr, 0, nullptr, nullptr}};
953
959
960static void calc_new_masks(const ApplyMaskMode mode,
961 const Span<float> node_mask,
962 const MutableSpan<float> new_mask)
963{
964 switch (mode) {
966 break;
968 for (const int i : node_mask.index_range()) {
969 new_mask[i] = node_mask[i] * new_mask[i];
970 }
971 break;
973 for (const int i : node_mask.index_range()) {
974 new_mask[i] = new_mask[i] > 0.00001f ? node_mask[i] / new_mask[i] : 0.0f;
975 }
976 break;
978 for (const int i : node_mask.index_range()) {
979 new_mask[i] = node_mask[i] + new_mask[i];
980 }
981 break;
983 for (const int i : node_mask.index_range()) {
984 new_mask[i] = node_mask[i] - new_mask[i];
985 }
986 break;
987 }
988 mask::clamp_mask(new_mask);
989}
990
991static void apply_mask_mesh(const Depsgraph &depsgraph,
992 const Object &object,
993 const Span<bool> hide_vert,
994 const auto_mask::Cache &automasking,
995 const ApplyMaskMode mode,
996 const float factor,
997 const bool invert_automask,
998 const bke::pbvh::MeshNode &node,
999 LocalData &tls,
1001{
1002 const Span<int> verts = node.verts();
1003
1004 tls.factors.resize(verts.size());
1005 const MutableSpan<float> factors = tls.factors;
1006 fill_factor_from_hide(hide_vert, verts, factors);
1007 scale_factors(factors, factor);
1008
1009 tls.new_mask.resize(verts.size());
1010 const MutableSpan<float> new_mask = tls.new_mask;
1011 new_mask.fill(1.0f);
1012 auto_mask::calc_vert_factors(depsgraph, object, automasking, node, verts, new_mask);
1013
1014 if (invert_automask) {
1015 mask::invert_mask(new_mask);
1016 }
1017
1018 tls.mask.resize(verts.size());
1019 const MutableSpan<float> node_mask = tls.mask;
1020 gather_data_mesh(mask.as_span(), verts, node_mask);
1021
1022 calc_new_masks(mode, node_mask, new_mask);
1023 mix_new_masks(new_mask, factors, node_mask);
1024
1025 scatter_data_mesh(node_mask.as_span(), verts, mask);
1026}
1027
1028static void apply_mask_grids(const Depsgraph &depsgraph,
1029 Object &object,
1030 const auto_mask::Cache &automasking,
1031 const ApplyMaskMode mode,
1032 const float factor,
1033 const bool invert_automask,
1034 const bke::pbvh::GridsNode &node,
1035 LocalData &tls)
1036{
1037 SculptSession &ss = *object.sculpt;
1038 SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
1039 const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
1040
1041 const Span<int> grids = node.grids();
1042 const int grid_verts_num = grids.size() * key.grid_area;
1043
1044 tls.factors.resize(grid_verts_num);
1045 const MutableSpan<float> factors = tls.factors;
1046 fill_factor_from_hide(subdiv_ccg, grids, factors);
1047 scale_factors(factors, factor);
1048
1049 tls.new_mask.resize(grid_verts_num);
1050 const MutableSpan<float> new_mask = tls.new_mask;
1051 new_mask.fill(1.0f);
1052 auto_mask::calc_grids_factors(depsgraph, object, automasking, node, grids, new_mask);
1053
1054 if (invert_automask) {
1055 mask::invert_mask(new_mask);
1056 }
1057
1058 tls.mask.resize(grid_verts_num);
1059 const MutableSpan<float> node_mask = tls.mask;
1060 gather_mask_grids(subdiv_ccg, grids, node_mask);
1061
1062 calc_new_masks(mode, node_mask, new_mask);
1063 mix_new_masks(new_mask, factors, node_mask);
1064
1065 scatter_mask_grids(node_mask.as_span(), subdiv_ccg, grids);
1066}
1067
1068static void apply_mask_bmesh(const Depsgraph &depsgraph,
1069 Object &object,
1070 const auto_mask::Cache &automasking,
1071 const ApplyMaskMode mode,
1072 const float factor,
1073 const float invert_automask,
1075 LocalData &tls)
1076{
1077 const SculptSession &ss = *object.sculpt;
1079
1080 tls.factors.resize(verts.size());
1081 const MutableSpan<float> factors = tls.factors;
1082 fill_factor_from_hide(verts, factors);
1083 scale_factors(factors, factor);
1084
1085 tls.new_mask.resize(verts.size());
1086 const MutableSpan<float> new_mask = tls.new_mask;
1087 new_mask.fill(1.0f);
1088 auto_mask::calc_vert_factors(depsgraph, object, automasking, node, verts, new_mask);
1089
1090 if (invert_automask) {
1091 mask::invert_mask(new_mask);
1092 }
1093
1094 tls.mask.resize(verts.size());
1095 const MutableSpan<float> node_mask = tls.mask;
1096 gather_mask_bmesh(*ss.bm, verts, node_mask);
1097
1098 calc_new_masks(mode, node_mask, new_mask);
1099 mix_new_masks(new_mask, factors, node_mask);
1100
1101 scatter_mask_bmesh(node_mask.as_span(), *ss.bm, verts);
1102}
1103
1104static void apply_mask_from_settings(const Depsgraph &depsgraph,
1105 Object &object,
1106 bke::pbvh::Tree &pbvh,
1107 const IndexMask &node_mask,
1108 const auto_mask::Cache &automasking,
1109 const ApplyMaskMode mode,
1110 const float factor,
1111 const bool invert_automask)
1112{
1114 switch (pbvh.type()) {
1115 case bke::pbvh::Type::Mesh: {
1116 Mesh &mesh = *static_cast<Mesh *>(object.data);
1117 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
1119 ".sculpt_mask", bke::AttrDomain::Point);
1120 const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
1122 node_mask.foreach_index(GrainSize(1), [&](const int i) {
1123 LocalData &tls = all_tls.local();
1125 object,
1126 hide_vert,
1127 automasking,
1128 mode,
1129 factor,
1130 invert_automask,
1131 nodes[i],
1132 tls,
1133 mask.span);
1135 });
1136 mask.finish();
1137 break;
1138 }
1140 SubdivCCG &subdiv_ccg = *object.sculpt->subdiv_ccg;
1141 const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
1142 MutableSpan<float> masks = subdiv_ccg.masks;
1144 node_mask.foreach_index(GrainSize(1), [&](const int i) {
1145 LocalData &tls = all_tls.local();
1147 depsgraph, object, automasking, mode, factor, invert_automask, nodes[i], tls);
1149 });
1150 break;
1151 }
1153 const int mask_offset = CustomData_get_offset_named(
1154 &object.sculpt->bm->vdata, CD_PROP_FLOAT, ".sculpt_mask");
1156 node_mask.foreach_index(GrainSize(1), [&](const int i) {
1157 LocalData &tls = all_tls.local();
1159 depsgraph, object, automasking, mode, factor, invert_automask, nodes[i], tls);
1161 });
1162 break;
1163 }
1164 }
1165}
1166
1168{
1169 const Scene &scene = *CTX_data_scene(C);
1172 const Sculpt &sd = *CTX_data_tool_settings(C)->sculpt;
1173 const Brush *brush = BKE_paint_brush_for_read(&sd.paint);
1174
1175 const View3D *v3d = CTX_wm_view3d(C);
1176 const Base *base = CTX_data_active_base(C);
1177 if (!BKE_base_is_visible(v3d, base)) {
1178 return OPERATOR_CANCELLED;
1179 }
1180
1183
1186
1187 const ApplyMaskMode mode = ApplyMaskMode(RNA_enum_get(op->ptr, "mix_mode"));
1188 const float factor = RNA_float_get(op->ptr, "mix_factor");
1189
1191
1192 IndexMaskMemory memory;
1193 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
1194
1195 /* Set up automasking settings. */
1196 Sculpt scene_copy = dna::shallow_copy(sd);
1197
1198 MaskSettingsSource src = (MaskSettingsSource)RNA_enum_get(op->ptr, "settings_source");
1199 switch (src) {
1201 if (RNA_boolean_get(op->ptr, "invert")) {
1203 }
1204 else {
1206 }
1207
1208 if (RNA_boolean_get(op->ptr, "use_curve")) {
1210 }
1211
1212 scene_copy.automasking_cavity_blur_steps = RNA_int_get(op->ptr, "blur_steps");
1213 scene_copy.automasking_cavity_factor = RNA_float_get(op->ptr, "factor");
1214
1216 break;
1218 if (brush) {
1219 scene_copy.automasking_flags = brush->automasking_flags;
1223
1224 /* Ensure only cavity masking is enabled. */
1227 }
1228 else {
1229 scene_copy.automasking_flags = 0;
1230 BKE_report(op->reports, RPT_WARNING, "No active brush");
1231
1232 return OPERATOR_CANCELLED;
1233 }
1234
1235 break;
1237 /* Ensure only cavity masking is enabled. */
1240 break;
1241 }
1242
1243 /* Ensure cavity mask is actually enabled. */
1244 if (!(scene_copy.automasking_flags & BRUSH_AUTOMASKING_CAVITY_ALL)) {
1246 }
1247
1248 /* Create copy of brush with cleared automasking settings. */
1249 Brush brush_copy = dna::shallow_copy(*brush);
1250 /* Set a brush type that doesn't change topology so automasking isn't "disabled". */
1252 brush_copy.automasking_flags = 0;
1254 brush_copy.automasking_cavity_curve = scene_copy.automasking_cavity_curve;
1255
1256 std::unique_ptr<auto_mask::Cache> automasking = auto_mask::cache_init(
1257 *depsgraph, scene_copy, &brush_copy, ob);
1258
1259 if (!automasking) {
1260 return OPERATOR_CANCELLED;
1261 }
1262
1263 undo::push_begin(scene, ob, op);
1265
1266 automasking->calc_cavity_factor(*depsgraph, ob, node_mask);
1267 apply_mask_from_settings(*depsgraph, ob, pbvh, node_mask, *automasking, mode, factor, false);
1268
1269 undo::push_end(ob);
1270
1271 pbvh.tag_masks_changed(node_mask);
1274
1275 return OPERATOR_FINISHED;
1276}
1277
1279{
1280 uiLayout *layout = op->layout;
1281 Scene *scene = CTX_data_scene(C);
1282 Sculpt *sd = scene->toolsettings ? scene->toolsettings->sculpt : nullptr;
1283
1284 layout->use_property_split_set(true);
1285 layout->use_property_decorate_set(false);
1286 MaskSettingsSource source = (MaskSettingsSource)RNA_enum_get(op->ptr, "settings_source");
1287
1288 if (!sd) {
1290 }
1291
1292 switch (source) {
1294 layout->prop(op->ptr, "mix_mode", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1295 layout->prop(op->ptr, "mix_factor", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1296 layout->prop(op->ptr, "factor", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1297 layout->prop(op->ptr, "blur_steps", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1298 layout->prop(op->ptr, "invert", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1299 layout->prop(op->ptr, "use_curve", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1300
1301 if (sd && RNA_boolean_get(op->ptr, "use_curve")) {
1302 PointerRNA sculpt_ptr = RNA_pointer_create_discrete(&scene->id, &RNA_Sculpt, sd);
1304 &sculpt_ptr,
1305 "automasking_cavity_curve_op",
1306 'v',
1307 false,
1308 false,
1309 false,
1310 false,
1311 false);
1312 }
1313 break;
1314 }
1317 layout->prop(op->ptr, "mix_mode", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1318 layout->prop(op->ptr, "mix_factor", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1319
1320 break;
1321 }
1322}
1323
1325{
1326 ot->name = "Mask From Cavity";
1327 ot->idname = "SCULPT_OT_mask_from_cavity";
1328 ot->description = "Creates a mask based on the curvature of the surface";
1329
1330 ot->ui = mask_from_cavity_ui;
1331 ot->exec = mask_from_cavity_exec;
1332 ot->poll = SCULPT_mode_poll;
1333
1334 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1335
1336 RNA_def_enum(ot->srna, "mix_mode", mix_modes, int(ApplyMaskMode::Mix), "Mode", "Mix mode");
1337 RNA_def_float(ot->srna, "mix_factor", 1.0f, 0.0f, 5.0f, "Mix Factor", "", 0.0f, 1.0f);
1338 RNA_def_enum(ot->srna,
1339 "settings_source",
1342 "Settings",
1343 "Use settings from here");
1344 RNA_def_float(ot->srna,
1345 "factor",
1346 0.5f,
1347 0.0f,
1348 5.0f,
1349 "Factor",
1350 "The contrast of the cavity mask",
1351 0.0f,
1352 1.0f);
1353 RNA_def_int(ot->srna,
1354 "blur_steps",
1355 2,
1356 0,
1357 25,
1358 "Blur",
1359 "The number of times the cavity mask is blurred",
1360 0,
1361 25);
1362 RNA_def_boolean(ot->srna, "use_curve", false, "Custom Curve", "");
1363 RNA_def_boolean(ot->srna, "invert", false, "Cavity (Inverted)", "");
1364}
1365
1366enum class MaskBoundaryMode : int8_t { Mesh, FaceSets };
1367
1369{
1372 const Sculpt &sd = *CTX_data_tool_settings(C)->sculpt;
1373 const Scene &scene = *CTX_data_scene(C);
1374 const Brush *brush = BKE_paint_brush_for_read(&sd.paint);
1375
1376 const View3D *v3d = CTX_wm_view3d(C);
1377 const Base *base = CTX_data_active_base(C);
1378 if (!BKE_base_is_visible(v3d, base)) {
1379 return OPERATOR_CANCELLED;
1380 }
1381
1384
1387
1388 const ApplyMaskMode mode = ApplyMaskMode(RNA_enum_get(op->ptr, "mix_mode"));
1389 const float factor = RNA_float_get(op->ptr, "mix_factor");
1390
1392
1393 IndexMaskMemory memory;
1394 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
1395
1396 /* Set up automasking settings. */
1397 Sculpt scene_copy = dna::shallow_copy(sd);
1398
1399 MaskSettingsSource src = (MaskSettingsSource)RNA_enum_get(op->ptr, "settings_source");
1400 switch (src) {
1402 const MaskBoundaryMode boundary_mode = MaskBoundaryMode(
1403 RNA_enum_get(op->ptr, "boundary_mode"));
1404 switch (boundary_mode) {
1407 break;
1410 break;
1411 }
1413 "propagation_steps");
1414 break;
1415 }
1417 if (brush) {
1418 scene_copy.automasking_flags = brush->automasking_flags;
1421
1424 }
1425 else {
1426 scene_copy.automasking_flags = 0;
1427 BKE_report(op->reports, RPT_WARNING, "No active brush");
1428
1429 return OPERATOR_CANCELLED;
1430 }
1431
1432 break;
1436 break;
1437 }
1438
1439 /* Create copy of brush with cleared automasking settings. */
1440 Brush brush_copy = dna::shallow_copy(*brush);
1441 /* Set a brush type that doesn't change topology so automasking isn't "disabled". */
1443 brush_copy.automasking_flags = 0;
1445
1446 std::unique_ptr<auto_mask::Cache> automasking = auto_mask::cache_init(
1447 *depsgraph, scene_copy, &brush_copy, ob);
1448
1449 if (!automasking) {
1450 return OPERATOR_CANCELLED;
1451 }
1452
1453 undo::push_begin(scene, ob, op);
1455
1456 apply_mask_from_settings(*depsgraph, ob, pbvh, node_mask, *automasking, mode, factor, true);
1457
1458 undo::push_end(ob);
1459
1460 pbvh.tag_masks_changed(node_mask);
1463
1464 return OPERATOR_FINISHED;
1465}
1466
1468{
1469 uiLayout *layout = op->layout;
1470 Scene *scene = CTX_data_scene(C);
1471 Sculpt *sd = scene->toolsettings ? scene->toolsettings->sculpt : nullptr;
1472
1473 layout->use_property_split_set(true);
1474 layout->use_property_decorate_set(false);
1475 MaskSettingsSource source = (MaskSettingsSource)RNA_enum_get(op->ptr, "settings_source");
1476
1477 if (!sd) {
1479 }
1480
1481 switch (source) {
1483 layout->prop(op->ptr, "mix_mode", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1484 layout->prop(op->ptr, "mix_factor", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1485 layout->prop(op->ptr, "boundary_mode", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1486 layout->prop(op->ptr, "propagation_steps", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1487 break;
1488 }
1491 layout->prop(op->ptr, "mix_mode", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1492 layout->prop(op->ptr, "mix_factor", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1493 break;
1494 }
1495}
1496
1498{
1499 ot->name = "Mask From Boundary";
1500 ot->idname = "SCULPT_OT_mask_from_boundary";
1501 ot->description = "Creates a mask based on the boundaries of the surface";
1502
1505 ot->poll = SCULPT_mode_poll;
1506
1507 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1508
1509 RNA_def_enum(ot->srna, "mix_mode", mix_modes, int(ApplyMaskMode::Mix), "Mode", "Mix mode");
1510 RNA_def_float(ot->srna, "mix_factor", 1.0f, 0.0f, 5.0f, "Mix Factor", "", 0.0f, 1.0f);
1511 RNA_def_enum(ot->srna,
1512 "settings_source",
1515 "Settings",
1516 "Use settings from here");
1517
1518 static EnumPropertyItem mask_boundary_modes[] = {
1520 "MESH",
1521 ICON_NONE,
1522 "Mesh",
1523 "Calculate the boundary mask based on disconnected mesh topology islands"},
1525 "FACE_SETS",
1526 ICON_NONE,
1527 "Face Sets",
1528 "Calculate the boundary mask between face sets"},
1529 {0, nullptr, 0, nullptr, nullptr}};
1530
1531 RNA_def_enum(ot->srna,
1532 "boundary_mode",
1533 mask_boundary_modes,
1535 "Mode",
1536 "Boundary type to mask");
1537 RNA_def_int(ot->srna, "propagation_steps", 1, 1, 20, "Propagation Steps", "", 1, 20);
1538}
1539
1541
1542} // namespace mask
1543
1545{
1572
1578
1582}
1583
1585{
1586 filter::modal_keymap(keyconf);
1587}
1588
1589} // namespace blender::ed::sculpt_paint
void BKE_brush_color_set(Paint *paint, Brush *brush, const float color[3])
Definition brush.cc:1213
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)
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:270
General operations, lookup, etc. for blender objects.
void BKE_object_free_derived_caches(Object *ob)
void BKE_sculptsession_free(Object *ob)
Definition paint.cc:2332
const Brush * BKE_paint_brush_for_read(const Paint *paint)
Definition paint.cc:650
void BKE_paint_init(Main *bmain, Scene *sce, PaintMode mode, bool ensure_brushes=true)
Definition paint.cc:1840
Paint * BKE_paint_get_active_from_paintmode(Scene *sce, PaintMode mode)
Definition paint.cc:374
MultiresModifierData * BKE_sculpt_multires_active(const Scene *scene, Object *ob)
Definition paint.cc:2513
void BKE_sculpt_update_object_for_edit(Depsgraph *depsgraph, Object *ob_orig, bool is_paint_tool)
Definition paint.cc:2797
void BKE_sculpt_color_layer_create_if_needed(Object *object)
Definition paint.cc:2773
void BKE_sculptsession_free_pbvh(Object &object)
Definition paint.cc:2286
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:645
void BKE_sculpt_toolsettings_data_ensure(Main *bmain, Scene *scene)
Definition paint.cc:2884
void BKE_sculpt_mask_layers_ensure(Depsgraph *depsgraph, Main *bmain, Object *ob, MultiresModifierData *mmd)
Definition paint.cc:2806
void BKE_paint_brushes_validate(Main *bmain, Paint *paint)
Definition paint.cc:1125
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
@ RPT_WARNING
Definition BKE_report.hh:38
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
void BKE_scene_graph_evaluated_ensure(Depsgraph *depsgraph, Main *bmain)
Definition scene.cc:2626
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:1118
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ 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)
Read Guarded memory(de)allocation.
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
@ PROP_HIDDEN
Definition RNA_types.hh:338
#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, bool presets)
#define UI_ITEM_NONE
#define ND_DRAW
Definition WM_types.hh:461
#define NC_BRUSH
Definition WM_types.hh:385
#define ND_MODE
Definition WM_types.hh:445
#define NC_SCENE
Definition WM_types.hh:378
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:218
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define NA_EDITED
Definition WM_types.hh:584
#define NC_OBJECT
Definition WM_types.hh:379
@ BM_ELEM_TAG
void BM_log_all_added(BMesh *bm, BMLog *log)
Definition bmesh_log.cc:737
void BM_log_before_all_removed(BMesh *bm, BMLog *log)
Definition bmesh_log.cc:765
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 from_span(Span< T > values)
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, AttrType data_type, const void *default_value=nullptr) const
GAttributeReader lookup(const StringRef attribute_id) const
bool add(const StringRef attribute_id, const AttrDomain domain, const AttrType data_type, const AttributeInit &initializer)
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, AttrType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
bool remove(const StringRef attribute_id)
Span< NodeT > nodes() const
void tag_masks_changed(const IndexMask &node_mask)
Definition pbvh.cc:669
void foreach_index(Fn &&fn) const
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:3052
IndexMask all_leaf_nodes(const Tree &pbvh, IndexMaskMemory &memory)
Definition pbvh.cc:2628
void node_update_mask_bmesh(int mask_offset, BMeshNode &node)
Definition pbvh.cc:1479
void node_update_mask_mesh(Span< float > mask, MeshNode &node)
Definition pbvh.cc:1421
void node_update_mask_grids(const CCGKey &key, Span< float > masks, GridsNode &node)
Definition pbvh.cc:1449
Bounds< float3 > bounds_get(const Tree &pbvh)
Definition pbvh.cc:1661
Span< float3 > vert_normals_eval(const Depsgraph &depsgraph, const Object &object_orig)
Definition pbvh.cc:1059
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:95
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:83
void vert_random_access_ensure(Object &object)
Definition sculpt.cc:141
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, Paint &paint)
bool color_supported_check(const Scene &scene, Object &object, ReportList *reports)
Definition sculpt.cc:5474
static wmOperatorStatus sculpt_mode_toggle_exec(bContext *C, wmOperator *op)
void scale_factors(MutableSpan< float > factors, float strength)
Definition sculpt.cc:7512
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:4685
void flush_update_done(const bContext *C, Object &ob, const UpdateType update_type)
Definition sculpt.cc:5146
void scatter_data_mesh(Span< T > src, Span< int > indices, MutableSpan< T > dst)
Definition sculpt.cc:6435
void SCULPT_OT_brush_stroke(wmOperatorType *ot)
Definition sculpt.cc:5767
void gather_data_mesh(Span< T > src, Span< int > indices, MutableSpan< T > dst)
Definition sculpt.cc:6395
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:6775
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
VecBase< float, 3 > float3
void paint_cursor_delete_textures()
#define fabsf
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:3734
bool SCULPT_mode_poll(bContext *C)
Definition sculpt.cc:3677
void SCULPT_tag_update_overlays(bContext *C)
Definition sculpt.cc:759
#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:307
float scale[3]
struct SculptSession * sculpt
struct ToolSettings * toolsettings
BMLog * bm_log
Definition BKE_paint.hh:392
struct SculptSession::@345375365225333342236070250113232025260070150165 persistent
blender::Array< blender::float3 > sculpt_persistent_co
Definition BKE_paint.hh:451
SubdivCCG * subdiv_ccg
Definition BKE_paint.hh:395
blender::Array< blender::float3 > sculpt_persistent_no
Definition BKE_paint.hh:452
ActiveVert active_vert() const
Definition paint.cc:2367
blender::Array< float > sculpt_persistent_disp
Definition BKE_paint.hh:453
eObjectMode mode_type
Definition BKE_paint.hh:491
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
View3DShading shading
VecBase< T, 3 > xyz() const
const ImplicitSharingInfo * sharing_info
blender::float3 average_stroke_accum
void execute(Object &object, GroupedSpan< int > vert_to_face_map, FunctionRef< bool(int from_v, int to_v)> func)
void use_property_decorate_set(bool is_sep)
void use_property_split_set(bool value)
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:763
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:4237
#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:145