Blender V5.0
curves_sculpt_ops.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include <algorithm>
6
7#include "BLI_kdtree.h"
8#include "BLI_listbase.h"
9#include "BLI_rand.hh"
10#include "BLI_task.hh"
11#include "BLI_utildefines.h"
12#include "BLI_vector_set.hh"
13
14#include "BKE_attribute.hh"
15#include "BKE_brush.hh"
16#include "BKE_bvhutils.hh"
17#include "BKE_colortools.hh"
18#include "BKE_context.hh"
19#include "BKE_curves.hh"
20#include "BKE_modifier.hh"
21#include "BKE_object.hh"
22#include "BKE_paint.hh"
23#include "BKE_paint_types.hh"
24
25#include "BLT_translation.hh"
26
27#include "WM_api.hh"
28#include "WM_message.hh"
29#include "WM_toolsystem.hh"
30
31#include "ED_curves.hh"
32#include "ED_curves_sculpt.hh"
33#include "ED_image.hh"
34#include "ED_object.hh"
35#include "ED_screen.hh"
36#include "ED_space_api.hh"
37#include "ED_view3d.hh"
38
39#include "DEG_depsgraph.hh"
41
42#include "DNA_brush_types.h"
43#include "DNA_curves_types.h"
44#include "DNA_mesh_types.h"
45#include "DNA_screen_types.h"
46
47#include "RNA_access.hh"
48#include "RNA_define.hh"
49#include "RNA_enum_types.hh"
50
52#include "paint_intern.hh"
53
54#include "UI_interface.hh"
56#include "UI_resources.hh"
57
58#include "GPU_immediate.hh"
59#include "GPU_immediate_util.hh"
60#include "GPU_matrix.hh"
61#include "GPU_state.hh"
62
64
65/* -------------------------------------------------------------------- */
68
70{
71 const Object *ob = CTX_data_active_object(C);
72 return ob && ob->mode & OB_MODE_SCULPT_CURVES;
73}
74
76{
77 if (!curves_sculpt_poll(C)) {
78 return false;
79 }
80 if (CTX_wm_region_view3d(C) == nullptr) {
81 return false;
82 }
83 return true;
84}
85
87
88/* -------------------------------------------------------------------- */
91
92float brush_radius_factor(const Brush &brush, const StrokeExtension &stroke_extension)
93{
94 if (BKE_brush_use_size_pressure(&brush)) {
95 return BKE_curvemapping_evaluateF(brush.curve_size, 0, stroke_extension.pressure);
96 }
97 return 1.0f;
98}
99
101 const Brush &brush,
102 const StrokeExtension &stroke_extension)
103{
104 return BKE_brush_radius_get(&paint, &brush) * brush_radius_factor(brush, stroke_extension);
105}
106
107float brush_strength_factor(const Brush &brush, const StrokeExtension &stroke_extension)
108{
109 if (BKE_brush_use_alpha_pressure(&brush)) {
110 return BKE_curvemapping_evaluateF(brush.curve_strength, 0, stroke_extension.pressure);
111 }
112 return 1.0f;
113}
114
116 const Brush &brush,
117 const StrokeExtension &stroke_extension)
118{
119 return BKE_brush_alpha_get(&paint, &brush) * brush_strength_factor(brush, stroke_extension);
120}
121
122static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(
123 bContext &C, wmOperator &op, const StrokeExtension &stroke_start)
124{
125 const BrushStrokeMode mode = BrushStrokeMode(RNA_enum_get(op.ptr, "mode"));
126
127 const Scene &scene = *CTX_data_scene(&C);
128 const CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt;
129 const Brush &brush = *BKE_paint_brush_for_read(&curves_sculpt.paint);
130 switch (brush.curves_sculpt_brush_type) {
132 return new_comb_operation();
134 return new_delete_operation();
138 return new_add_operation();
140 return new_grow_shrink_operation(mode, C);
142 return new_selection_paint_operation(mode, C);
144 return new_pinch_operation(mode, C);
146 return new_smooth_operation();
148 return new_puff_operation();
150 return new_density_operation(mode, C, stroke_start);
152 return new_slide_operation();
153 }
155 return {};
156}
157
159 std::unique_ptr<CurvesSculptStrokeOperation> operation;
161};
162
164 float out[3],
165 const float mouse[2],
166 bool /*force_original*/)
167{
168 out[0] = mouse[0];
169 out[1] = mouse[1];
170 out[2] = 0;
171 UNUSED_VARS(C);
172 return true;
173}
174
175static bool stroke_test_start(bContext *C, wmOperator *op, const float mouse[2])
176{
177 UNUSED_VARS(C, op, mouse);
178 return true;
179}
180
182 wmOperator *op,
183 PaintStroke * /*stroke*/,
184 PointerRNA *stroke_element)
185{
187 op->customdata);
188
189 StrokeExtension stroke_extension;
190 RNA_float_get_array(stroke_element, "mouse", stroke_extension.mouse_position);
191 stroke_extension.pressure = RNA_float_get(stroke_element, "pressure");
192 stroke_extension.reports = op->reports;
193
194 if (!op_data->operation) {
195 stroke_extension.is_first = true;
196 op_data->operation = start_brush_operation(*C, *op, stroke_extension);
197 }
198 else {
199 stroke_extension.is_first = false;
200 }
201
202 if (op_data->operation) {
203 op_data->operation->on_stroke_extended(*C, stroke_extension);
204 }
205}
206
207static void stroke_done(const bContext *C, PaintStroke *stroke)
208{
209 UNUSED_VARS(C, stroke);
210}
211
213 wmOperator *op,
214 const wmEvent *event)
215{
216 Scene *scene = CTX_data_scene(C);
218 const Brush *brush = paint ? BKE_paint_brush_for_read(paint) : nullptr;
219 if (brush == nullptr) {
220 return OPERATOR_CANCELLED;
221 }
222
223 SculptCurvesBrushStrokeData *op_data = MEM_new<SculptCurvesBrushStrokeData>(__func__);
224 op_data->stroke = paint_stroke_new(C,
225 op,
229 nullptr,
231 event->type);
232 op->customdata = op_data;
233
234 const wmOperatorStatus retval = op->type->modal(C, op, event);
235 OPERATOR_RETVAL_CHECK(retval);
236
237 if (retval == OPERATOR_FINISHED) {
238 if (op->customdata != nullptr) {
239 paint_stroke_free(C, op, op_data->stroke);
240 MEM_delete(op_data);
241 }
242 return OPERATOR_FINISHED;
243 }
244
247}
248
250 wmOperator *op,
251 const wmEvent *event)
252{
254 op->customdata);
255 wmOperatorStatus retval = paint_stroke_modal(C, op, event, &op_data->stroke);
257 MEM_delete(op_data);
258 op->customdata = nullptr;
259 }
260 return retval;
261}
262
264{
265 if (op->customdata != nullptr) {
267 op->customdata);
268 paint_stroke_cancel(C, op, op_data->stroke);
269 MEM_delete(op_data);
270 }
271}
272
274{
275 ot->name = "Stroke Curves Sculpt";
276 ot->idname = "SCULPT_CURVES_OT_brush_stroke";
277 ot->description = "Sculpt curves using a brush";
278
282
284
286}
287
289
290/* -------------------------------------------------------------------- */
293
295{
296 Scene *scene = CTX_data_scene(C);
298
301 CurvesSculpt *curves_sculpt = scene->toolsettings->curves_sculpt;
302
304
306
308
310 paint_init_pivot(ob, scene, paint);
311
312 /* Necessary to change the object mode on the evaluated object. */
314 WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
316}
317
319{
321 ob->mode = OB_MODE_OBJECT;
322}
323
325{
328
329 const bool is_mode_set = ob->mode == OB_MODE_SCULPT_CURVES;
330
331 if (is_mode_set) {
333 return OPERATOR_CANCELLED;
334 }
335 }
336
337 if (is_mode_set) {
339 }
340 else {
342 }
343
345
346 /* Necessary to change the object mode on the evaluated object. */
348 WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
350 return OPERATOR_FINISHED;
351}
352
354{
355 ot->name = "Curve Sculpt Mode Toggle";
356 ot->idname = "CURVES_OT_sculptmode_toggle";
357 ot->description = "Enter/Exit sculpt mode for curves";
358
360 ot->poll = curves::curves_poll;
361
363}
364
366
367namespace select_random {
368
370{
372
373 const int seed = RNA_int_get(op->ptr, "seed");
374 RandomNumberGenerator rng{uint32_t(seed)};
375
376 const bool partial = RNA_boolean_get(op->ptr, "partial");
377 const bool constant_per_curve = RNA_boolean_get(op->ptr, "constant_per_curve");
378 const float probability = RNA_float_get(op->ptr, "probability");
379 const float min_value = RNA_float_get(op->ptr, "min");
380 const auto next_partial_random_value = [&]() {
381 return rng.get_float() * (1.0f - min_value) + min_value;
382 };
383 const auto next_bool_random_value = [&]() { return rng.get_float() <= probability; };
384
385 for (Curves *curves_id : unique_curves) {
386 CurvesGeometry &curves = curves_id->geometry.wrap();
387 const bool was_anything_selected = curves::has_anything_selected(curves);
388
390 MutableSpan<float> selection = attribute.span;
391 if (!was_anything_selected) {
392 selection.fill(1.0f);
393 }
394 const OffsetIndices points_by_curve = curves.points_by_curve();
395 switch (bke::AttrDomain(curves_id->selection_domain)) {
397 if (partial) {
398 if (constant_per_curve) {
399 for (const int curve_i : curves.curves_range()) {
400 const float random_value = next_partial_random_value();
401 const IndexRange points = points_by_curve[curve_i];
402 for (const int point_i : points) {
403 selection[point_i] *= random_value;
404 }
405 }
406 }
407 else {
408 for (const int point_i : selection.index_range()) {
409 const float random_value = next_partial_random_value();
410 selection[point_i] *= random_value;
411 }
412 }
413 }
414 else {
415 if (constant_per_curve) {
416 for (const int curve_i : curves.curves_range()) {
417 const bool random_value = next_bool_random_value();
418 const IndexRange points = points_by_curve[curve_i];
419 if (!random_value) {
420 selection.slice(points).fill(0.0f);
421 }
422 }
423 }
424 else {
425 for (const int point_i : selection.index_range()) {
426 const bool random_value = next_bool_random_value();
427 if (!random_value) {
428 selection[point_i] = 0.0f;
429 }
430 }
431 }
432 }
433 break;
434 }
436 if (partial) {
437 for (const int curve_i : curves.curves_range()) {
438 const float random_value = next_partial_random_value();
439 selection[curve_i] *= random_value;
440 }
441 }
442 else {
443 for (const int curve_i : curves.curves_range()) {
444 const bool random_value = next_bool_random_value();
445 if (!random_value) {
446 selection[curve_i] = 0.0f;
447 }
448 }
449 }
450 break;
451 }
452 default:
454 break;
455 }
456
457 attribute.finish();
458
459 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
460 * attribute for now. */
461 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
462 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
463 }
464 return OPERATOR_FINISHED;
465}
466
467static void select_random_ui(bContext * /*C*/, wmOperator *op)
468{
469 uiLayout *layout = op->layout;
470
471 layout->prop(op->ptr, "seed", UI_ITEM_NONE, std::nullopt, ICON_NONE);
472 layout->prop(op->ptr, "constant_per_curve", UI_ITEM_NONE, std::nullopt, ICON_NONE);
473 layout->prop(op->ptr, "partial", UI_ITEM_NONE, std::nullopt, ICON_NONE);
474
475 if (RNA_boolean_get(op->ptr, "partial")) {
476 layout->prop(op->ptr, "min", UI_ITEM_R_SLIDER, IFACE_("Min"), ICON_NONE);
477 }
478 else {
479 layout->prop(op->ptr, "probability", UI_ITEM_R_SLIDER, IFACE_("Probability"), ICON_NONE);
480 }
481}
482
483} // namespace select_random
484
486{
487 ot->name = "Select Random";
488 ot->idname = __func__;
489 ot->description = "Randomizes existing selection or create new random selection";
490
494
496
497 RNA_def_int(ot->srna,
498 "seed",
499 0,
500 INT32_MIN,
501 INT32_MAX,
502 "Seed",
503 "Source of randomness",
504 INT32_MIN,
505 INT32_MAX);
507 ot->srna, "partial", false, "Partial", "Allow points or curves to be selected partially");
508 RNA_def_float(ot->srna,
509 "probability",
510 0.5f,
511 0.0f,
512 1.0f,
513 "Probability",
514 "Chance of every point or curve being included in the selection",
515 0.0f,
516 1.0f);
517 RNA_def_float(ot->srna,
518 "min",
519 0.0f,
520 0.0f,
521 1.0f,
522 "Min",
523 "Minimum value for the random selection",
524 0.0f,
525 1.0f);
526 RNA_def_boolean(ot->srna,
527 "constant_per_curve",
528 true,
529 "Constant per Curve",
530 "The generated random number is the same for every control point of a curve");
531}
532namespace select_grow {
533
546
551
553 const float distance,
554 MutableSpan<float> points_selection)
555{
556 if (distance > 0.0f) {
557 data.unselected_points.foreach_index(
558 GrainSize(256), [&](const int point_i, const int index_pos) {
559 const float distance_to_selected = data.distances_to_selected[index_pos];
560 const float selection = distance_to_selected <= distance ? 1.0f : 0.0f;
561 points_selection[point_i] = selection;
562 });
563 data.selected_points.foreach_index(
564 GrainSize(512), [&](const int point_i) { points_selection[point_i] = 1.0f; });
565 }
566 else {
567 data.selected_points.foreach_index(
568 GrainSize(256), [&](const int point_i, const int index_pos) {
569 const float distance_to_unselected = data.distances_to_unselected[index_pos];
570 const float selection = distance_to_unselected <= -distance ? 0.0f : 1.0f;
571 points_selection[point_i] = selection;
572 });
573 data.unselected_points.foreach_index(
574 GrainSize(512), [&](const int point_i) { points_selection[point_i] = 0.0f; });
575 }
576}
577
578static int select_grow_update(bContext *C, wmOperator *op, const float mouse_diff_x)
579{
580 GrowOperatorData &op_data = *static_cast<GrowOperatorData *>(op->customdata);
581
582 for (std::unique_ptr<GrowOperatorDataPerCurve> &curve_op_data : op_data.per_curve) {
583 Curves &curves_id = *curve_op_data->curves_id;
584 CurvesGeometry &curves = curves_id.geometry.wrap();
585 const float distance = curve_op_data->pixel_to_distance_factor * mouse_diff_x;
586
588 const OffsetIndices points_by_curve = curves.points_by_curve();
589
590 /* Grow or shrink selection based on precomputed distances. */
591 switch (selection.domain) {
593 update_points_selection(*curve_op_data, distance, selection.span);
594 break;
595 }
597 Array<float> new_points_selection(curves.points_num());
598 update_points_selection(*curve_op_data, distance, new_points_selection);
599 /* Propagate grown point selection to the curve selection. */
600 MutableSpan<float> curves_selection = selection.span;
601 for (const int curve_i : curves.curves_range()) {
602 const IndexRange points = points_by_curve[curve_i];
603 const Span<float> points_selection = new_points_selection.as_span().slice(points);
604 const float max_selection = *std::max_element(points_selection.begin(),
605 points_selection.end());
606 curves_selection[curve_i] = max_selection;
607 }
608 break;
609 }
610 default:
612 }
613
614 selection.finish();
615
616 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
617 * attribute for now. */
619 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &curves_id);
620 }
621
622 return OPERATOR_FINISHED;
623}
624
625static void select_grow_invoke_per_curve(const Curves &curves_id,
626 const Object &curves_ob,
627 const ARegion &region,
628 const View3D &v3d,
629 const RegionView3D &rv3d,
630 GrowOperatorDataPerCurve &curve_op_data)
631{
632 const CurvesGeometry &curves = curves_id.geometry.wrap();
633 const Span<float3> positions = curves.positions();
634
635 if (const bke::GAttributeReader original_selection = curves.attributes().lookup(".selection")) {
636 curve_op_data.original_selection = GArray<>(original_selection.varray.type(),
637 original_selection.varray.size());
638 original_selection.varray.materialize(curve_op_data.original_selection.data());
639 }
640
641 /* Find indices of selected and unselected points. */
643 curves_id, curve_op_data.selected_points_memory);
644 curve_op_data.unselected_points = curve_op_data.selected_points.complement(
645 curves.points_range(), curve_op_data.unselected_points_memory);
646
648 1024 < curve_op_data.selected_points.size() + curve_op_data.unselected_points.size(),
649 [&]() {
650 /* Build KD-tree for the selected points. */
651 KDTree_3d *kdtree = BLI_kdtree_3d_new(curve_op_data.selected_points.size());
652 BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(kdtree); });
653 curve_op_data.selected_points.foreach_index([&](const int point_i) {
654 const float3 &position = positions[point_i];
655 BLI_kdtree_3d_insert(kdtree, point_i, position);
656 });
657 BLI_kdtree_3d_balance(kdtree);
658
659 /* For each unselected point, compute the distance to the closest selected point. */
660 curve_op_data.distances_to_selected.reinitialize(curve_op_data.unselected_points.size());
662 curve_op_data.unselected_points.index_range(), 256, [&](const IndexRange range) {
663 for (const int i : range) {
664 const int point_i = curve_op_data.unselected_points[i];
665 const float3 &position = positions[point_i];
666 KDTreeNearest_3d nearest;
667 BLI_kdtree_3d_find_nearest(kdtree, position, &nearest);
668 curve_op_data.distances_to_selected[i] = nearest.dist;
669 }
670 });
671 },
672 [&]() {
673 /* Build KD-tree for the unselected points. */
674 KDTree_3d *kdtree = BLI_kdtree_3d_new(curve_op_data.unselected_points.size());
675 BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(kdtree); });
676 curve_op_data.unselected_points.foreach_index([&](const int point_i) {
677 const float3 &position = positions[point_i];
678 BLI_kdtree_3d_insert(kdtree, point_i, position);
679 });
680 BLI_kdtree_3d_balance(kdtree);
681
682 /* For each selected point, compute the distance to the closest unselected point. */
683 curve_op_data.distances_to_unselected.reinitialize(curve_op_data.selected_points.size());
685 curve_op_data.selected_points.index_range(), 256, [&](const IndexRange range) {
686 for (const int i : range) {
687 const int point_i = curve_op_data.selected_points[i];
688 const float3 &position = positions[point_i];
689 KDTreeNearest_3d nearest;
690 BLI_kdtree_3d_find_nearest(kdtree, position, &nearest);
691 curve_op_data.distances_to_unselected[i] = nearest.dist;
692 }
693 });
694 });
695
696 const float4x4 &curves_to_world_mat = curves_ob.object_to_world();
697 float4x4 world_to_curves_mat = math::invert(curves_to_world_mat);
698
699 const float4x4 projection = ED_view3d_ob_project_mat_get(&rv3d, &curves_ob);
700
701 /* Compute how mouse movements in screen space are converted into grow/shrink distances in
702 * object space. */
703 curve_op_data.pixel_to_distance_factor = threading::parallel_reduce(
704 curve_op_data.selected_points.index_range(),
705 256,
706 FLT_MAX,
707 [&](const IndexRange range, float pixel_to_distance_factor) {
708 for (const int i : range) {
709 const int point_i = curve_op_data.selected_points[i];
710 const float3 &pos_cu = positions[point_i];
711
712 const float2 pos_re = ED_view3d_project_float_v2_m4(&region, pos_cu, projection);
713 if (pos_re.x < 0 || pos_re.y < 0 || pos_re.x > region.winx || pos_re.y > region.winy) {
714 continue;
715 }
716 /* Compute how far this point moves in curve space when it moves one unit in screen
717 * space. */
718 const float2 pos_offset_re = pos_re + float2(1, 0);
719 float3 pos_offset_wo;
720 ED_view3d_win_to_3d(&v3d,
721 &region,
722 math::transform_point(curves_to_world_mat, pos_cu),
723 pos_offset_re,
724 pos_offset_wo);
725 const float3 pos_offset_cu = math::transform_point(world_to_curves_mat, pos_offset_wo);
726 const float dist_cu = math::distance(pos_cu, pos_offset_cu);
727 const float dist_re = math::distance(pos_re, pos_offset_re);
728 const float factor = dist_cu / dist_re;
729 math::min_inplace(pixel_to_distance_factor, factor);
730 }
731 return pixel_to_distance_factor;
732 },
733 [](const float a, const float b) { return std::min(a, b); });
734}
735
737{
738 Object *active_ob = CTX_data_active_object(C);
739 ARegion *region = CTX_wm_region(C);
740 View3D *v3d = CTX_wm_view3d(C);
742
743 GrowOperatorData *op_data = MEM_new<GrowOperatorData>(__func__);
744 op->customdata = op_data;
745
746 op_data->initial_mouse_x = event->xy[0];
747
748 Curves &curves_id = *static_cast<Curves *>(active_ob->data);
749 auto curve_op_data = std::make_unique<GrowOperatorDataPerCurve>();
750 curve_op_data->curves_id = &curves_id;
751 select_grow_invoke_per_curve(curves_id, *active_ob, *region, *v3d, *rv3d, *curve_op_data);
752 op_data->per_curve.append(std::move(curve_op_data));
753
756}
757
759{
760 GrowOperatorData &op_data = *static_cast<GrowOperatorData *>(op->customdata);
761 const int mouse_x = event->xy[0];
762 const int mouse_diff_x = mouse_x - op_data.initial_mouse_x;
763 switch (event->type) {
764 case MOUSEMOVE: {
765 select_grow_update(C, op, mouse_diff_x);
766 break;
767 }
768 case LEFTMOUSE: {
769 MEM_delete(&op_data);
770 return OPERATOR_FINISHED;
771 }
772 case EVT_ESCKEY:
773 case RIGHTMOUSE: {
774 /* Undo operator by resetting the selection to the original value. */
775 for (std::unique_ptr<GrowOperatorDataPerCurve> &curve_op_data : op_data.per_curve) {
776 Curves &curves_id = *curve_op_data->curves_id;
777 CurvesGeometry &curves = curves_id.geometry.wrap();
778 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
779
780 attributes.remove(".selection");
781 if (!curve_op_data->original_selection.is_empty()) {
782 attributes.add(
783 ".selection",
785 bke::cpp_type_to_attribute_type(curve_op_data->original_selection.type()),
786 bke::AttributeInitVArray(GVArray::from_span(curve_op_data->original_selection)));
787 }
788
789 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
790 * attribute for now. */
792 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &curves_id);
793 }
794 MEM_delete(&op_data);
795 return OPERATOR_CANCELLED;
796 }
797 default: {
798 break;
799 }
800 }
802}
803
804} // namespace select_grow
805
807{
808 ot->name = "Select Grow";
809 ot->idname = __func__;
810 ot->description = "Select curves which are close to curves that are selected already";
811
815
817
818 PropertyRNA *prop;
819 prop = RNA_def_float(ot->srna,
820 "distance",
821 0.1f,
822 -FLT_MAX,
823 FLT_MAX,
824 "Distance",
825 "By how much to grow the selection",
826 -10.0f,
827 10.0f);
829}
830
832
834{
836 return false;
837 }
838 Scene *scene = CTX_data_scene(C);
840 if (brush == nullptr) {
841 return false;
842 }
844 return false;
845 }
846 return true;
847}
848
869
871{
873 ARegion *region = op_data.region;
874
875 const float min_distance = op_data.brush->curves_sculpt_settings->minimum_distance;
876 const float brush_radius = BKE_brush_radius_get(paint, op_data.brush);
877
878 float3 tangent_x_cu = math::cross(op_data.normal_cu, float3{0, 0, 1});
879 if (math::is_zero(tangent_x_cu)) {
880 tangent_x_cu = math::cross(op_data.normal_cu, float3{0, 1, 0});
881 }
882 tangent_x_cu = math::normalize(tangent_x_cu);
883 const float3 tangent_y_cu = math::normalize(math::cross(op_data.normal_cu, tangent_x_cu));
884
885 /* Sample a few points to get a good estimate of how large the grid has to be. */
886 Vector<float3> points_wo;
887 points_wo.append(op_data.pos_cu + min_distance * tangent_x_cu);
888 points_wo.append(op_data.pos_cu + min_distance * tangent_y_cu);
889 points_wo.append(op_data.pos_cu - min_distance * tangent_x_cu);
890 points_wo.append(op_data.pos_cu - min_distance * tangent_y_cu);
891
892 Vector<float2> points_re;
893 for (const float3 &pos_wo : points_wo) {
894 float2 pos_re;
895 ED_view3d_project_v2(region, pos_wo, pos_re);
896 points_re.append(pos_re);
897 }
898
899 float2 origin_re;
900 ED_view3d_project_v2(region, op_data.pos_cu, origin_re);
901
902 int needed_points = 0;
903 for (const float2 &pos_re : points_re) {
904 const float distance = math::length(pos_re - origin_re);
905 const int needed_points_iter = (brush_radius * 2.0f) / distance;
906
907 needed_points = std::max(needed_points_iter, needed_points);
908 }
909
910 /* Limit to a hard-coded number since it only adds noise at some point. */
911 return std::min(300, needed_points);
912}
913
915 const blender::int2 & /*xy*/,
916 const blender::float2 & /*tilt*/,
917 void *customdata)
918{
920 MinDistanceEditData &op_data = *static_cast<MinDistanceEditData *>(customdata);
921
922 const float min_distance = op_data.brush->curves_sculpt_settings->minimum_distance;
923
924 float3 tangent_x_cu = math::cross(op_data.normal_cu, float3{0, 0, 1});
925 if (math::is_zero(tangent_x_cu)) {
926 tangent_x_cu = math::cross(op_data.normal_cu, float3{0, 1, 0});
927 }
928 tangent_x_cu = math::normalize(tangent_x_cu);
929 const float3 tangent_y_cu = math::normalize(math::cross(op_data.normal_cu, tangent_x_cu));
930
931 const int points_per_side = calculate_points_per_side(C, op_data);
932 const int points_per_axis_num = 2 * points_per_side + 1;
933
934 Vector<float3> points_wo;
935 for (const int x_i : IndexRange(points_per_axis_num)) {
936 for (const int y_i : IndexRange(points_per_axis_num)) {
937 const float x_iter = min_distance * (x_i - (points_per_axis_num - 1) / 2.0f);
938 const float y_iter = min_distance * (y_i - (points_per_axis_num - 1) / 2.0f);
939
940 const float3 point_pos_cu = op_data.pos_cu + op_data.normal_cu * 0.0001f +
941 x_iter * tangent_x_cu + y_iter * tangent_y_cu;
942 const float3 point_pos_wo = math::transform_point(op_data.curves_to_world_mat, point_pos_cu);
943 points_wo.append(point_pos_wo);
944 }
945 }
946
947 float4 circle_col = float4(op_data.brush->add_col);
948 float circle_alpha = op_data.brush->cursor_overlay_alpha;
949 float brush_radius_re = BKE_brush_radius_get(paint, op_data.brush);
950
951 /* Draw the grid. */
955
956 ARegion *region = op_data.region;
957 RegionView3D *rv3d = op_data.rv3d;
958 wmWindow *win = CTX_wm_window(C);
959
960 /* It does the same as: `view3d_operator_needs_gpu(C);`. */
961 wmViewport(&region->winrct);
963 GPU_matrix_set(rv3d->viewmat);
964
965 GPUVertFormat *format3d = immVertexFormat();
966
967 const uint pos3d = GPU_vertformat_attr_add(
968 format3d, "pos", blender::gpu::VertAttrType::SFLOAT_32_32_32);
969 const uint col3d = GPU_vertformat_attr_add(
970 format3d, "color", blender::gpu::VertAttrType::SFLOAT_32_32_32_32);
971 const uint siz3d = GPU_vertformat_attr_add(
972 format3d, "size", blender::gpu::VertAttrType::SFLOAT_32);
973
976 immBegin(GPU_PRIM_POINTS, points_wo.size());
977
978 float3 brush_origin_wo = math::transform_point(op_data.curves_to_world_mat, op_data.pos_cu);
979 float2 brush_origin_re;
980 ED_view3d_project_v2(region, brush_origin_wo, brush_origin_re);
981
982 /* Smooth alpha transition until the brush edge. */
983 const int alpha_border_re = 20;
984 const float dist_to_inner_border_re = brush_radius_re - alpha_border_re;
985
986 for (const float3 &pos_wo : points_wo) {
987 float2 pos_re;
988 ED_view3d_project_v2(region, pos_wo, pos_re);
989
990 const float dist_to_point_re = math::distance(pos_re, brush_origin_re);
991 const float alpha = 1.0f - ((dist_to_point_re - dist_to_inner_border_re) / alpha_border_re);
992
993 immAttr1f(siz3d, 3.0f);
994 immAttr4f(col3d, 0.9f, 0.9f, 0.9f, alpha);
995 immVertex3fv(pos3d, pos_wo);
996 }
997 immEnd();
999
1000 /* Reset the drawing settings. */
1001 GPU_point_size(1.0f);
1004
1005 int4 scissor;
1006 GPU_scissor_get(scissor);
1007 wmWindowViewport(win);
1008 GPU_scissor(scissor[0], scissor[1], scissor[2], scissor[3]);
1009
1010 /* Draw the brush circle. */
1011 GPU_matrix_translate_2f(float(op_data.initial_mouse.x), float(op_data.initial_mouse.y));
1012
1014 uint pos2d = GPU_vertformat_attr_add(format, "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
1015
1017
1018 immUniformColor3fvAlpha(circle_col, circle_alpha);
1019 imm_draw_circle_wire_2d(pos2d, 0.0f, 0.0f, brush_radius_re, 80);
1020
1023}
1024
1026{
1028 ARegion *region = CTX_wm_region(C);
1029 View3D *v3d = CTX_wm_view3d(C);
1030 Scene *scene = CTX_data_scene(C);
1031
1032 Object &curves_ob_orig = *CTX_data_active_object(C);
1033 Curves &curves_id_orig = *static_cast<Curves *>(curves_ob_orig.data);
1034 Object &surface_ob_orig = *curves_id_orig.surface;
1035 Object *surface_ob_eval = DEG_get_evaluated(depsgraph, &surface_ob_orig);
1036 if (surface_ob_eval == nullptr) {
1037 return OPERATOR_CANCELLED;
1038 }
1039 Mesh *surface_me_eval = BKE_object_get_evaluated_mesh(surface_ob_eval);
1040 if (surface_me_eval == nullptr) {
1041 return OPERATOR_CANCELLED;
1042 }
1043
1044 bke::BVHTreeFromMesh surface_bvh_eval = surface_me_eval->bvh_corner_tris();
1045
1046 const int2 mouse_pos_int_re{event->mval};
1047 const float2 mouse_pos_re{mouse_pos_int_re};
1048
1049 float3 ray_start_wo, ray_end_wo;
1051 depsgraph, region, v3d, mouse_pos_re, ray_start_wo, ray_end_wo, true);
1052
1053 const CurvesSurfaceTransforms transforms{curves_ob_orig, &surface_ob_orig};
1054
1055 const float3 ray_start_su = math::transform_point(transforms.world_to_surface, ray_start_wo);
1056 const float3 ray_end_su = math::transform_point(transforms.world_to_surface, ray_end_wo);
1057 const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su);
1058
1059 BVHTreeRayHit ray_hit;
1060 ray_hit.dist = FLT_MAX;
1061 ray_hit.index = -1;
1062 BLI_bvhtree_ray_cast(surface_bvh_eval.tree,
1063 ray_start_su,
1064 ray_direction_su,
1065 0.0f,
1066 &ray_hit,
1067 surface_bvh_eval.raycast_callback,
1068 &surface_bvh_eval);
1069 if (ray_hit.index == -1) {
1070 WM_global_report(RPT_ERROR, "Cursor must be over the surface mesh");
1071 return OPERATOR_CANCELLED;
1072 }
1073
1074 const float3 hit_pos_su = ray_hit.co;
1075 const float3 hit_normal_su = ray_hit.no;
1076
1077 const float3 hit_pos_cu = math::transform_point(transforms.surface_to_curves, hit_pos_su);
1078 const float3 hit_normal_cu = math::normalize(
1079 math::transform_direction(transforms.surface_to_curves_normal, hit_normal_su));
1080
1081 MinDistanceEditData *op_data = MEM_new<MinDistanceEditData>(__func__);
1082 op_data->curves_to_world_mat = transforms.curves_to_world;
1083 op_data->normal_cu = hit_normal_cu;
1084 op_data->pos_cu = hit_pos_cu;
1085 op_data->initial_mouse = event->xy;
1088
1089 if (op_data->initial_minimum_distance <= 0.0f) {
1090 op_data->initial_minimum_distance = 0.01f;
1091 }
1092
1093 op->customdata = op_data;
1094
1095 /* Temporarily disable other paint cursors. */
1097 op_data->orig_paintcursors = wm->runtime->paintcursors;
1098 BLI_listbase_clear(&wm->runtime->paintcursors);
1099
1100 /* Add minimum distance paint cursor. */
1103
1104 op_data->region = CTX_wm_region(C);
1105 op_data->rv3d = CTX_wm_region_view3d(C);
1106
1108 ED_region_tag_redraw(region);
1110}
1111
1113{
1114 ARegion *region = CTX_wm_region(C);
1115 MinDistanceEditData &op_data = *static_cast<MinDistanceEditData *>(op->customdata);
1116
1117 auto finish = [&]() {
1119
1120 /* Remove cursor. */
1121 WM_paint_cursor_end(static_cast<wmPaintCursor *>(op_data.cursor));
1122 /* Restore original paint cursors. */
1123 wm->runtime->paintcursors = op_data.orig_paintcursors;
1124
1125 ED_region_tag_redraw(region);
1126 MEM_delete(&op_data);
1127 };
1128
1129 switch (event->type) {
1130 case MOUSEMOVE: {
1131 const int2 mouse_pos_int_re{event->xy};
1132 const float2 mouse_pos_re{mouse_pos_int_re};
1133
1134 const float mouse_diff_x = mouse_pos_int_re.x - op_data.initial_mouse.x;
1135 const float factor = powf(2, mouse_diff_x / UI_UNIT_X / 10.0f);
1137 factor;
1138
1139 ED_region_tag_redraw(region);
1141 break;
1142 }
1143 case LEFTMOUSE: {
1144 if (event->val == KM_PRESS) {
1146 finish();
1147 return OPERATOR_FINISHED;
1148 }
1149 break;
1150 }
1151 case RIGHTMOUSE:
1152 case EVT_ESCKEY: {
1154 finish();
1156 return OPERATOR_CANCELLED;
1157 }
1158 default: {
1159 break;
1160 }
1161 }
1162
1164}
1165
1166} // namespace min_distance_edit
1167
1169{
1170 ot->name = "Edit Minimum Distance";
1171 ot->idname = __func__;
1172 ot->description = "Change the minimum distance used by the density brush";
1173
1177
1179}
1180
1181} // namespace blender::ed::sculpt_paint
1182
1183/* -------------------------------------------------------------------- */
1186
1196
bool BKE_brush_use_alpha_pressure(const Brush *brush)
Definition brush.cc:1290
float BKE_brush_alpha_get(const Paint *paint, const Brush *brush)
Definition brush.cc:1357
bool BKE_brush_use_size_pressure(const Brush *brush)
Definition brush.cc:1285
float BKE_brush_radius_get(const Paint *paint, const Brush *brush)
Definition brush.cc:1272
void BKE_brush_tag_unsaved_changes(Brush *brush)
Definition brush.cc:764
float BKE_curvemapping_evaluateF(const CurveMapping *cumap, int cur, float value)
wmWindow * CTX_wm_window(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)
Main * CTX_data_main(const bContext *C)
RegionView3D * CTX_wm_region_view3d(const bContext *C)
ARegion * CTX_wm_region(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)
Low-level operations for curves.
General operations, lookup, etc. for blender objects.
Mesh * BKE_object_get_evaluated_mesh(const Object *object_eval)
void BKE_paint_brushes_ensure(Main *bmain, Paint *paint)
Definition paint.cc:1823
const Brush * BKE_paint_brush_for_read(const Paint *paint)
Definition paint.cc:650
Paint * BKE_paint_get_active_from_paintmode(Scene *sce, PaintMode mode)
Definition paint.cc:374
Paint * BKE_paint_get_active_from_context(const bContext *C)
Definition paint.cc:476
bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint)
Definition paint.cc:1738
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:645
@ RPT_ERROR
Definition BKE_report.hh:39
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
int BLI_bvhtree_ray_cast(const BVHTree *tree, const float co[3], const float dir[3], float radius, BVHTreeRayHit *hit, BVHTree_RayCastCallback callback, void *userdata)
A KD-tree for nearest neighbor search.
BLI_INLINE void BLI_listbase_clear(ListBase *lb)
#define BLI_SCOPED_DEFER(function_to_defer)
unsigned int uint
#define UNUSED_VARS(...)
#define ELEM(...)
#define IFACE_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1118
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ CURVES_SCULPT_BRUSH_TYPE_SMOOTH
@ CURVES_SCULPT_BRUSH_TYPE_PUFF
@ CURVES_SCULPT_BRUSH_TYPE_GROW_SHRINK
@ CURVES_SCULPT_BRUSH_TYPE_PINCH
@ CURVES_SCULPT_BRUSH_TYPE_SNAKE_HOOK
@ CURVES_SCULPT_BRUSH_TYPE_ADD
@ CURVES_SCULPT_BRUSH_TYPE_COMB
@ CURVES_SCULPT_BRUSH_TYPE_DENSITY
@ CURVES_SCULPT_BRUSH_TYPE_DELETE
@ CURVES_SCULPT_BRUSH_TYPE_SLIDE
@ CURVES_SCULPT_BRUSH_TYPE_SELECTION_PAINT
@ OB_MODE_SCULPT_CURVES
@ OB_MODE_OBJECT
#define RGN_TYPE_ANY
#define SPACE_TYPE_ANY
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
#define OPERATOR_RETVAL_CHECK(ret)
void ED_paint_cursor_start(Paint *paint, bool(*poll)(bContext *C))
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:618
void ED_view3d_project_v2(const ARegion *region, const float world[3], float r_region_co[2])
blender::float4x4 ED_view3d_ob_project_mat_get(const RegionView3D *rv3d, const Object *ob)
bool ED_view3d_win_to_segment_clipped(const Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, const float mval[2], float r_ray_start[3], float r_ray_end[3], bool do_clip_planes)
void immEnd()
void immUnbindProgram()
void immBindBuiltinProgram(GPUBuiltinShader shader_id)
void immAttr1f(uint attr_id, float x)
GPUVertFormat * immVertexFormat()
void immAttr4f(uint attr_id, float x, float y, float z, float w)
void immVertex3fv(uint attr_id, const float data[3])
void immBegin(GPUPrimType, uint vertex_len)
void immUniformColor3fvAlpha(const float rgb[3], float a)
void imm_draw_circle_wire_2d(uint shdr_pos, float x, float y, float radius, int nsegments)
#define GPU_matrix_set(x)
void GPU_matrix_push()
void GPU_matrix_push_projection()
void GPU_matrix_pop_projection()
#define GPU_matrix_projection_set(x)
void GPU_matrix_pop()
void GPU_matrix_translate_2f(float x, float y)
@ GPU_PRIM_POINTS
@ GPU_SHADER_3D_POINT_VARYING_SIZE_VARYING_COLOR
@ GPU_SHADER_3D_UNIFORM_COLOR
void GPU_program_point_size(bool enable)
Definition gpu_state.cc:180
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(GPUBlend blend)
Definition gpu_state.cc:42
void GPU_scissor(int x, int y, int width, int height)
Definition gpu_state.cc:193
void GPU_point_size(float size)
Definition gpu_state.cc:172
void GPU_scissor_get(int coords[4])
Definition gpu_state.cc:268
uint GPU_vertformat_attr_add(GPUVertFormat *format, blender::StringRef name, blender::gpu::VertAttrType type)
@ PROP_DISTANCE
Definition RNA_types.hh:256
#define C
Definition RandGen.cpp:29
#define UI_UNIT_X
@ UI_ITEM_R_SLIDER
#define UI_ITEM_NONE
#define NC_GEOM
Definition WM_types.hh:393
#define ND_DATA
Definition WM_types.hh:509
@ KM_PRESS
Definition WM_types.hh:311
#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 ND_TOOLSETTINGS
Definition WM_types.hh:449
BMesh const char void * data
BPy_StructRNA * depsgraph
static unsigned long seed
Definition btSoftBody.h:39
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:419
Span< T > as_span() const
Definition BLI_array.hh:243
const void * data() const
static GVArray from_span(GSpan span)
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
constexpr void fill(const T &value) const
Definition BLI_span.hh:517
constexpr IndexRange index_range() const
Definition BLI_span.hh:670
NonCopyable(const NonCopyable &other)=delete
NonMovable(NonMovable &&other)=delete
constexpr const T * end() const
Definition BLI_span.hh:224
constexpr const T * begin() const
Definition BLI_span.hh:220
int64_t size() const
void append(const T &value)
bool add(const StringRef attribute_id, const AttrDomain domain, const AttrType data_type, const AttributeInit &initializer)
bool remove(const StringRef attribute_id)
IndexMask complement(const IndexMask &universe, IndexMaskMemory &memory) const
void foreach_index(Fn &&fn) const
void ED_operatortypes_sculpt_curves()
#define powf(x, y)
#define INT32_MAX
#define INT32_MIN
#define out
float distance(VecOp< float, D >, VecOp< float, D >) RET
MatBase< 4, 4 > float4x4
VecBase< float, 4 > float4
format
AttrType cpp_type_to_attribute_type(const CPPType &type)
static bool has_anything_selected(const Span< Curves * > curves_ids)
VectorSet< Curves * > get_unique_editable_curves(const bContext &C)
Definition curves_ops.cc:91
bool editable_curves_poll(bContext *C)
bool curves_with_surface_poll(bContext *C)
bool curves_poll(bContext *C)
IndexMask retrieve_selected_points(const bke::CurvesGeometry &curves, IndexMaskMemory &memory)
bool mode_compat_set(bContext *C, Object *ob, eObjectMode mode, ReportList *reports)
static wmOperatorStatus min_distance_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void min_distance_edit_draw(bContext *C, const blender::int2 &, const blender::float2 &, void *customdata)
static int calculate_points_per_side(bContext *C, MinDistanceEditData &op_data)
static wmOperatorStatus min_distance_edit_modal(bContext *C, wmOperator *op, const wmEvent *event)
static int select_grow_update(bContext *C, wmOperator *op, const float mouse_diff_x)
static void update_points_selection(const GrowOperatorDataPerCurve &data, const float distance, MutableSpan< float > points_selection)
static wmOperatorStatus select_grow_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void select_grow_invoke_per_curve(const Curves &curves_id, const Object &curves_ob, const ARegion &region, const View3D &v3d, const RegionView3D &rv3d, GrowOperatorDataPerCurve &curve_op_data)
static wmOperatorStatus select_grow_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus select_random_exec(bContext *C, wmOperator *op)
static void select_random_ui(bContext *, wmOperator *op)
bool curves_sculpt_poll(bContext *C)
static bool stroke_test_start(bContext *C, wmOperator *op, const float mouse[2])
std::unique_ptr< CurvesSculptStrokeOperation > new_add_operation()
static wmOperatorStatus sculpt_curves_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void curves_sculptmode_exit(bContext *C)
std::unique_ptr< CurvesSculptStrokeOperation > new_pinch_operation(const BrushStrokeMode brush_mode, const bContext &C)
static void stroke_update_step(bContext *C, wmOperator *op, PaintStroke *, PointerRNA *stroke_element)
std::unique_ptr< CurvesSculptStrokeOperation > new_comb_operation()
float brush_strength_get(const Paint &paint, const Brush &brush, const StrokeExtension &stroke_extension)
static bool stroke_get_location(bContext *C, float out[3], const float mouse[2], bool)
void paint_stroke_cancel(bContext *C, wmOperator *op, PaintStroke *stroke)
static void SCULPT_CURVES_OT_select_grow(wmOperatorType *ot)
bke::SpanAttributeWriter< float > float_selection_ensure(Curves &curves_id)
static std::unique_ptr< CurvesSculptStrokeOperation > start_brush_operation(bContext &C, wmOperator &op, const StrokeExtension &stroke_start)
static void stroke_done(const bContext *C, PaintStroke *stroke)
static wmOperatorStatus curves_sculptmode_toggle_exec(bContext *C, wmOperator *op)
static void CURVES_OT_sculptmode_toggle(wmOperatorType *ot)
static void curves_sculptmode_enter(bContext *C)
std::unique_ptr< CurvesSculptStrokeOperation > new_snake_hook_operation()
std::unique_ptr< CurvesSculptStrokeOperation > new_grow_shrink_operation(const BrushStrokeMode brush_mode, const bContext &C)
bool curves_sculpt_poll_view3d(bContext *C)
wmOperatorStatus paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintStroke **stroke_p)
static wmOperatorStatus sculpt_curves_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event)
std::unique_ptr< CurvesSculptStrokeOperation > new_smooth_operation()
std::unique_ptr< CurvesSculptStrokeOperation > new_delete_operation()
float brush_radius_get(const Paint &paint, const Brush &brush, const StrokeExtension &stroke_extension)
std::unique_ptr< CurvesSculptStrokeOperation > new_selection_paint_operation(const BrushStrokeMode brush_mode, const bContext &C)
static void SCULPT_CURVES_OT_select_random(wmOperatorType *ot)
static void SCULPT_CURVES_OT_min_distance_edit(wmOperatorType *ot)
static void sculpt_curves_stroke_cancel(bContext *C, wmOperator *op)
void paint_stroke_free(bContext *C, wmOperator *op, PaintStroke *stroke)
PaintStroke * paint_stroke_new(bContext *C, wmOperator *op, StrokeGetLocation get_location, StrokeTestStart test_start, StrokeUpdateStep update_step, StrokeRedraw redraw, StrokeDone done, int event_type)
std::unique_ptr< CurvesSculptStrokeOperation > new_slide_operation()
std::unique_ptr< CurvesSculptStrokeOperation > new_puff_operation()
float brush_strength_factor(const Brush &brush, const StrokeExtension &stroke_extension)
float brush_radius_factor(const Brush &brush, const StrokeExtension &stroke_extension)
std::unique_ptr< CurvesSculptStrokeOperation > new_density_operation(const BrushStrokeMode brush_mode, const bContext &C, const StrokeExtension &stroke_start)
static void SCULPT_CURVES_OT_brush_stroke(wmOperatorType *ot)
T distance(const T &a, const T &b)
T length(const VecBase< T, Size > &a)
bool is_zero(const T &a)
CartesianBasis invert(const CartesianBasis &basis)
AxisSigned cross(const AxisSigned a, const AxisSigned b)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
VecBase< T, 3 > transform_direction(const MatBase< T, 3, 3 > &mat, const VecBase< T, 3 > &direction)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
void parallel_invoke(Functions &&...functions)
Definition BLI_task.hh:221
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:93
VecBase< int32_t, 4 > int4
MatBase< float, 4, 4 > float4x4
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
VecBase< float, 3 > float3
void paint_init_pivot(Object *ob, Scene *scene, Paint *paint)
BrushStrokeMode
void paint_stroke_operator_properties(wmOperatorType *ot)
void RNA_float_get_array(PointerRNA *ptr, const char *name, float *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)
int RNA_enum_get(PointerRNA *ptr, const char *name)
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_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_subtype(PropertyRNA *prop, PropertySubType subtype)
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)
#define FLT_MAX
Definition stdcycles.h:14
float add_col[4]
int cursor_overlay_alpha
struct BrushCurvesSculptSettings * curves_sculpt_settings
char curves_sculpt_brush_type
struct CurveMapping * curve_size
struct CurveMapping * curve_strength
CurvesGeometry geometry
char selection_domain
struct Object * surface
float viewmat[4][4]
float winmat[4][4]
struct ToolSettings * toolsettings
CurvesSculpt * curves_sculpt
VecBase< T, 2 > xy() const
BVHTree_RayCastCallback raycast_callback
std::unique_ptr< CurvesSculptStrokeOperation > operation
Vector< std::unique_ptr< GrowOperatorDataPerCurve > > per_curve
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)
wmEventType type
Definition WM_types.hh:757
short val
Definition WM_types.hh:759
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1089
wmOperatorStatus(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1081
struct ReportList * reports
struct uiLayout * layout
struct wmOperatorType * type
struct PointerRNA * ptr
void WM_global_report(eReportType type, const char *message)
void WM_main_add_notifier(uint type, void *reference)
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
@ RIGHTMOUSE
@ MOUSEMOVE
@ LEFTMOUSE
@ EVT_ESCKEY
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 *))
bool WM_paint_cursor_end(wmPaintCursor *handle)
wmPaintCursor * WM_paint_cursor_activate(short space_type, short region_type, bool(*poll)(bContext *C), wmPaintCursorDraw draw, void *customdata)
void wmViewport(const rcti *winrct)
void wmWindowViewport(const wmWindow *win)
void WM_toolsystem_update_from_context_view3d(bContext *C)