Blender V4.3
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 "BLI_kdtree.h"
6#include "BLI_rand.hh"
7#include "BLI_task.hh"
8#include "BLI_utildefines.h"
9#include "BLI_vector_set.hh"
10
11#include "BKE_attribute.hh"
12#include "BKE_brush.hh"
13#include "BKE_bvhutils.hh"
14#include "BKE_context.hh"
15#include "BKE_curves.hh"
16#include "BKE_modifier.hh"
17#include "BKE_object.hh"
18#include "BKE_paint.hh"
19
20#include "BLT_translation.hh"
21
22#include "WM_api.hh"
23#include "WM_message.hh"
24#include "WM_toolsystem.hh"
25
26#include "ED_curves.hh"
27#include "ED_curves_sculpt.hh"
28#include "ED_image.hh"
29#include "ED_object.hh"
30#include "ED_screen.hh"
31#include "ED_space_api.hh"
32#include "ED_view3d.hh"
33
34#include "DEG_depsgraph.hh"
36
37#include "DNA_brush_types.h"
38#include "DNA_curves_types.h"
39#include "DNA_screen_types.h"
40
41#include "RNA_access.hh"
42#include "RNA_define.hh"
43#include "RNA_enum_types.hh"
44
46#include "paint_intern.hh"
47
48#include "UI_interface.hh"
49#include "UI_resources.hh"
50
51#include "GPU_immediate.hh"
52#include "GPU_immediate_util.hh"
53#include "GPU_matrix.hh"
54#include "GPU_state.hh"
55
57
58/* -------------------------------------------------------------------- */
61
63{
64 const Object *ob = CTX_data_active_object(C);
65 return ob && ob->mode & OB_MODE_SCULPT_CURVES;
66}
67
69{
70 if (!curves_sculpt_poll(C)) {
71 return false;
72 }
73 if (CTX_wm_region_view3d(C) == nullptr) {
74 return false;
75 }
76 return true;
77}
78
80
81/* -------------------------------------------------------------------- */
84
85float brush_radius_factor(const Brush &brush, const StrokeExtension &stroke_extension)
86{
87 if (BKE_brush_use_size_pressure(&brush)) {
88 return stroke_extension.pressure;
89 }
90 return 1.0f;
91}
92
93float brush_radius_get(const Scene &scene,
94 const Brush &brush,
95 const StrokeExtension &stroke_extension)
96{
97 return BKE_brush_size_get(&scene, &brush) * brush_radius_factor(brush, stroke_extension);
98}
99
100float brush_strength_factor(const Brush &brush, const StrokeExtension &stroke_extension)
101{
102 if (BKE_brush_use_alpha_pressure(&brush)) {
103 return stroke_extension.pressure;
104 }
105 return 1.0f;
106}
107
108float brush_strength_get(const Scene &scene,
109 const Brush &brush,
110 const StrokeExtension &stroke_extension)
111{
112 return BKE_brush_alpha_get(&scene, &brush) * brush_strength_factor(brush, stroke_extension);
113}
114
115static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(
116 bContext &C, wmOperator &op, const StrokeExtension &stroke_start)
117{
118 const BrushStrokeMode mode = BrushStrokeMode(RNA_enum_get(op.ptr, "mode"));
119
120 const Scene &scene = *CTX_data_scene(&C);
121 const CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt;
122 const Brush &brush = *BKE_paint_brush_for_read(&curves_sculpt.paint);
123 switch (brush.curves_sculpt_brush_type) {
125 return new_comb_operation();
127 return new_delete_operation();
131 return new_add_operation();
133 return new_grow_shrink_operation(mode, C);
135 return new_selection_paint_operation(mode, C);
137 return new_pinch_operation(mode, C);
139 return new_smooth_operation();
141 return new_puff_operation();
143 return new_density_operation(mode, C, stroke_start);
145 return new_slide_operation();
146 }
148 return {};
149}
150
152 std::unique_ptr<CurvesSculptStrokeOperation> operation;
154};
155
157 float out[3],
158 const float mouse[2],
159 bool /*force_original*/)
160{
161 out[0] = mouse[0];
162 out[1] = mouse[1];
163 out[2] = 0;
164 UNUSED_VARS(C);
165 return true;
166}
167
168static bool stroke_test_start(bContext *C, wmOperator *op, const float mouse[2])
169{
170 UNUSED_VARS(C, op, mouse);
171 return true;
172}
173
175 wmOperator *op,
176 PaintStroke * /*stroke*/,
177 PointerRNA *stroke_element)
178{
180 op->customdata);
181
182 StrokeExtension stroke_extension;
183 RNA_float_get_array(stroke_element, "mouse", stroke_extension.mouse_position);
184 stroke_extension.pressure = RNA_float_get(stroke_element, "pressure");
185 stroke_extension.reports = op->reports;
186
187 if (!op_data->operation) {
188 stroke_extension.is_first = true;
189 op_data->operation = start_brush_operation(*C, *op, stroke_extension);
190 }
191 else {
192 stroke_extension.is_first = false;
193 }
194
195 if (op_data->operation) {
196 op_data->operation->on_stroke_extended(*C, stroke_extension);
197 }
198}
199
200static void stroke_done(const bContext *C, PaintStroke *stroke)
201{
202 UNUSED_VARS(C, stroke);
203}
204
206{
207 Scene *scene = CTX_data_scene(C);
209 const Brush *brush = paint ? BKE_paint_brush_for_read(paint) : nullptr;
210 if (brush == nullptr) {
211 return OPERATOR_CANCELLED;
212 }
213
214 SculptCurvesBrushStrokeData *op_data = MEM_new<SculptCurvesBrushStrokeData>(__func__);
215 op_data->stroke = paint_stroke_new(C,
216 op,
220 nullptr,
222 event->type);
223 op->customdata = op_data;
224
225 int return_value = op->type->modal(C, op, event);
226 if (return_value == OPERATOR_FINISHED) {
227 if (op->customdata != nullptr) {
228 paint_stroke_free(C, op, op_data->stroke);
229 MEM_delete(op_data);
230 }
231 return OPERATOR_FINISHED;
232 }
233
236}
237
239{
241 op->customdata);
242 int return_value = paint_stroke_modal(C, op, event, &op_data->stroke);
243 if (ELEM(return_value, OPERATOR_FINISHED, OPERATOR_CANCELLED)) {
244 MEM_delete(op_data);
245 op->customdata = nullptr;
246 }
247 return return_value;
248}
249
251{
252 if (op->customdata != nullptr) {
254 op->customdata);
255 paint_stroke_cancel(C, op, op_data->stroke);
256 MEM_delete(op_data);
257 }
258}
259
261{
262 ot->name = "Stroke Curves Sculpt";
263 ot->idname = "SCULPT_CURVES_OT_brush_stroke";
264 ot->description = "Sculpt curves using a brush";
265
269
271
273}
274
276
277/* -------------------------------------------------------------------- */
280
282{
283 Scene *scene = CTX_data_scene(C);
285
288 CurvesSculpt *curves_sculpt = scene->toolsettings->curves_sculpt;
289
291
293
295
296 /* Setup cursor color. BKE_paint_init() could be used, but creates an additional brush. */
298 paint->paint_cursor_col[3] = 128;
299
301 paint_init_pivot(ob, scene);
302
303 /* Necessary to change the object mode on the evaluated object. */
305 WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
307}
308
310{
312 ob->mode = OB_MODE_OBJECT;
313}
314
316{
319
320 const bool is_mode_set = ob->mode == OB_MODE_SCULPT_CURVES;
321
322 if (is_mode_set) {
324 return OPERATOR_CANCELLED;
325 }
326 }
327
328 if (is_mode_set) {
330 }
331 else {
333 }
334
336
337 /* Necessary to change the object mode on the evaluated object. */
339 WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
341 return OPERATOR_FINISHED;
342}
343
345{
346 ot->name = "Curve Sculpt Mode Toggle";
347 ot->idname = "CURVES_OT_sculptmode_toggle";
348 ot->description = "Enter/Exit sculpt mode for curves";
349
351 ot->poll = curves::curves_poll;
352
354}
355
357
358namespace select_random {
359
361{
363
364 const int seed = RNA_int_get(op->ptr, "seed");
366
367 const bool partial = RNA_boolean_get(op->ptr, "partial");
368 const bool constant_per_curve = RNA_boolean_get(op->ptr, "constant_per_curve");
369 const float probability = RNA_float_get(op->ptr, "probability");
370 const float min_value = RNA_float_get(op->ptr, "min");
371 const auto next_partial_random_value = [&]() {
372 return rng.get_float() * (1.0f - min_value) + min_value;
373 };
374 const auto next_bool_random_value = [&]() { return rng.get_float() <= probability; };
375
376 for (Curves *curves_id : unique_curves) {
377 CurvesGeometry &curves = curves_id->geometry.wrap();
378 const bool was_anything_selected = curves::has_anything_selected(curves);
379
382 if (!was_anything_selected) {
383 selection.fill(1.0f);
384 }
385 const OffsetIndices points_by_curve = curves.points_by_curve();
386 switch (bke::AttrDomain(curves_id->selection_domain)) {
388 if (partial) {
389 if (constant_per_curve) {
390 for (const int curve_i : curves.curves_range()) {
391 const float random_value = next_partial_random_value();
392 const IndexRange points = points_by_curve[curve_i];
393 for (const int point_i : points) {
394 selection[point_i] *= random_value;
395 }
396 }
397 }
398 else {
399 for (const int point_i : selection.index_range()) {
400 const float random_value = next_partial_random_value();
401 selection[point_i] *= random_value;
402 }
403 }
404 }
405 else {
406 if (constant_per_curve) {
407 for (const int curve_i : curves.curves_range()) {
408 const bool random_value = next_bool_random_value();
409 const IndexRange points = points_by_curve[curve_i];
410 if (!random_value) {
411 selection.slice(points).fill(0.0f);
412 }
413 }
414 }
415 else {
416 for (const int point_i : selection.index_range()) {
417 const bool random_value = next_bool_random_value();
418 if (!random_value) {
419 selection[point_i] = 0.0f;
420 }
421 }
422 }
423 }
424 break;
425 }
427 if (partial) {
428 for (const int curve_i : curves.curves_range()) {
429 const float random_value = next_partial_random_value();
430 selection[curve_i] *= random_value;
431 }
432 }
433 else {
434 for (const int curve_i : curves.curves_range()) {
435 const bool random_value = next_bool_random_value();
436 if (!random_value) {
437 selection[curve_i] = 0.0f;
438 }
439 }
440 }
441 break;
442 }
443 default:
445 break;
446 }
447
448 attribute.finish();
449
450 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
451 * attribute for now. */
452 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
453 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
454 }
455 return OPERATOR_FINISHED;
456}
457
458static void select_random_ui(bContext * /*C*/, wmOperator *op)
459{
460 uiLayout *layout = op->layout;
461
462 uiItemR(layout, op->ptr, "seed", UI_ITEM_NONE, nullptr, ICON_NONE);
463 uiItemR(layout, op->ptr, "constant_per_curve", UI_ITEM_NONE, nullptr, ICON_NONE);
464 uiItemR(layout, op->ptr, "partial", UI_ITEM_NONE, nullptr, ICON_NONE);
465
466 if (RNA_boolean_get(op->ptr, "partial")) {
467 uiItemR(layout, op->ptr, "min", UI_ITEM_R_SLIDER, IFACE_("Min"), ICON_NONE);
468 }
469 else {
470 uiItemR(layout, op->ptr, "probability", UI_ITEM_R_SLIDER, IFACE_("Probability"), ICON_NONE);
471 }
472}
473
474} // namespace select_random
475
477{
478 ot->name = "Select Random";
479 ot->idname = __func__;
480 ot->description = "Randomizes existing selection or create new random selection";
481
485
487
488 RNA_def_int(ot->srna,
489 "seed",
490 0,
491 INT32_MIN,
492 INT32_MAX,
493 "Seed",
494 "Source of randomness",
495 INT32_MIN,
496 INT32_MAX);
498 ot->srna, "partial", false, "Partial", "Allow points or curves to be selected partially");
499 RNA_def_float(ot->srna,
500 "probability",
501 0.5f,
502 0.0f,
503 1.0f,
504 "Probability",
505 "Chance of every point or curve being included in the selection",
506 0.0f,
507 1.0f);
508 RNA_def_float(ot->srna,
509 "min",
510 0.0f,
511 0.0f,
512 1.0f,
513 "Min",
514 "Minimum value for the random selection",
515 0.0f,
516 1.0f);
517 RNA_def_boolean(ot->srna,
518 "constant_per_curve",
519 true,
520 "Constant per Curve",
521 "The generated random number is the same for every control point of a curve");
522}
523namespace select_grow {
524
537
542
544 const float distance,
545 MutableSpan<float> points_selection)
546{
547 if (distance > 0.0f) {
548 data.unselected_points.foreach_index(
549 GrainSize(256), [&](const int point_i, const int index_pos) {
550 const float distance_to_selected = data.distances_to_selected[index_pos];
551 const float selection = distance_to_selected <= distance ? 1.0f : 0.0f;
552 points_selection[point_i] = selection;
553 });
554 data.selected_points.foreach_index(
555 GrainSize(512), [&](const int point_i) { points_selection[point_i] = 1.0f; });
556 }
557 else {
558 data.selected_points.foreach_index(
559 GrainSize(256), [&](const int point_i, const int index_pos) {
560 const float distance_to_unselected = data.distances_to_unselected[index_pos];
561 const float selection = distance_to_unselected <= -distance ? 0.0f : 1.0f;
562 points_selection[point_i] = selection;
563 });
564 data.unselected_points.foreach_index(
565 GrainSize(512), [&](const int point_i) { points_selection[point_i] = 0.0f; });
566 }
567}
568
569static int select_grow_update(bContext *C, wmOperator *op, const float mouse_diff_x)
570{
571 GrowOperatorData &op_data = *static_cast<GrowOperatorData *>(op->customdata);
572
573 for (std::unique_ptr<GrowOperatorDataPerCurve> &curve_op_data : op_data.per_curve) {
574 Curves &curves_id = *curve_op_data->curves_id;
575 CurvesGeometry &curves = curves_id.geometry.wrap();
576 const float distance = curve_op_data->pixel_to_distance_factor * mouse_diff_x;
577
579 const OffsetIndices points_by_curve = curves.points_by_curve();
580
581 /* Grow or shrink selection based on precomputed distances. */
582 switch (selection.domain) {
584 update_points_selection(*curve_op_data, distance, selection.span);
585 break;
586 }
588 Array<float> new_points_selection(curves.points_num());
589 update_points_selection(*curve_op_data, distance, new_points_selection);
590 /* Propagate grown point selection to the curve selection. */
591 MutableSpan<float> curves_selection = selection.span;
592 for (const int curve_i : curves.curves_range()) {
593 const IndexRange points = points_by_curve[curve_i];
594 const Span<float> points_selection = new_points_selection.as_span().slice(points);
595 const float max_selection = *std::max_element(points_selection.begin(),
596 points_selection.end());
597 curves_selection[curve_i] = max_selection;
598 }
599 break;
600 }
601 default:
603 }
604
605 selection.finish();
606
607 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
608 * attribute for now. */
610 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &curves_id);
611 }
612
613 return OPERATOR_FINISHED;
614}
615
616static void select_grow_invoke_per_curve(const Curves &curves_id,
617 const Object &curves_ob,
618 const ARegion &region,
619 const View3D &v3d,
620 const RegionView3D &rv3d,
621 GrowOperatorDataPerCurve &curve_op_data)
622{
623 const CurvesGeometry &curves = curves_id.geometry.wrap();
624 const Span<float3> positions = curves.positions();
625
626 if (const bke::GAttributeReader original_selection = curves.attributes().lookup(".selection")) {
627 curve_op_data.original_selection = GArray<>(original_selection.varray.type(),
628 original_selection.varray.size());
629 original_selection.varray.materialize(curve_op_data.original_selection.data());
630 }
631
632 /* Find indices of selected and unselected points. */
634 curves_id, curve_op_data.selected_points_memory);
635 curve_op_data.unselected_points = curve_op_data.selected_points.complement(
636 curves.points_range(), curve_op_data.unselected_points_memory);
637
639 1024 < curve_op_data.selected_points.size() + curve_op_data.unselected_points.size(),
640 [&]() {
641 /* Build KD-tree for the selected points. */
642 KDTree_3d *kdtree = BLI_kdtree_3d_new(curve_op_data.selected_points.size());
643 BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(kdtree); });
644 curve_op_data.selected_points.foreach_index([&](const int point_i) {
645 const float3 &position = positions[point_i];
646 BLI_kdtree_3d_insert(kdtree, point_i, position);
647 });
648 BLI_kdtree_3d_balance(kdtree);
649
650 /* For each unselected point, compute the distance to the closest selected point. */
651 curve_op_data.distances_to_selected.reinitialize(curve_op_data.unselected_points.size());
653 curve_op_data.unselected_points.index_range(), 256, [&](const IndexRange range) {
654 for (const int i : range) {
655 const int point_i = curve_op_data.unselected_points[i];
656 const float3 &position = positions[point_i];
657 KDTreeNearest_3d nearest;
658 BLI_kdtree_3d_find_nearest(kdtree, position, &nearest);
659 curve_op_data.distances_to_selected[i] = nearest.dist;
660 }
661 });
662 },
663 [&]() {
664 /* Build KD-tree for the unselected points. */
665 KDTree_3d *kdtree = BLI_kdtree_3d_new(curve_op_data.unselected_points.size());
666 BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(kdtree); });
667 curve_op_data.unselected_points.foreach_index([&](const int point_i) {
668 const float3 &position = positions[point_i];
669 BLI_kdtree_3d_insert(kdtree, point_i, position);
670 });
671 BLI_kdtree_3d_balance(kdtree);
672
673 /* For each selected point, compute the distance to the closest unselected point. */
674 curve_op_data.distances_to_unselected.reinitialize(curve_op_data.selected_points.size());
676 curve_op_data.selected_points.index_range(), 256, [&](const IndexRange range) {
677 for (const int i : range) {
678 const int point_i = curve_op_data.selected_points[i];
679 const float3 &position = positions[point_i];
680 KDTreeNearest_3d nearest;
681 BLI_kdtree_3d_find_nearest(kdtree, position, &nearest);
682 curve_op_data.distances_to_unselected[i] = nearest.dist;
683 }
684 });
685 });
686
687 const float4x4 &curves_to_world_mat = curves_ob.object_to_world();
688 float4x4 world_to_curves_mat = math::invert(curves_to_world_mat);
689
690 const float4x4 projection = ED_view3d_ob_project_mat_get(&rv3d, &curves_ob);
691
692 /* Compute how mouse movements in screen space are converted into grow/shrink distances in
693 * object space. */
694 curve_op_data.pixel_to_distance_factor = threading::parallel_reduce(
695 curve_op_data.selected_points.index_range(),
696 256,
697 FLT_MAX,
698 [&](const IndexRange range, float pixel_to_distance_factor) {
699 for (const int i : range) {
700 const int point_i = curve_op_data.selected_points[i];
701 const float3 &pos_cu = positions[point_i];
702
703 const float2 pos_re = ED_view3d_project_float_v2_m4(&region, pos_cu, projection);
704 if (pos_re.x < 0 || pos_re.y < 0 || pos_re.x > region.winx || pos_re.y > region.winy) {
705 continue;
706 }
707 /* Compute how far this point moves in curve space when it moves one unit in screen
708 * space. */
709 const float2 pos_offset_re = pos_re + float2(1, 0);
710 float3 pos_offset_wo;
711 ED_view3d_win_to_3d(&v3d,
712 &region,
713 math::transform_point(curves_to_world_mat, pos_cu),
714 pos_offset_re,
715 pos_offset_wo);
716 const float3 pos_offset_cu = math::transform_point(world_to_curves_mat, pos_offset_wo);
717 const float dist_cu = math::distance(pos_cu, pos_offset_cu);
718 const float dist_re = math::distance(pos_re, pos_offset_re);
719 const float factor = dist_cu / dist_re;
720 math::min_inplace(pixel_to_distance_factor, factor);
721 }
722 return pixel_to_distance_factor;
723 },
724 [](const float a, const float b) { return std::min(a, b); });
725}
726
727static int select_grow_invoke(bContext *C, wmOperator *op, const wmEvent *event)
728{
729 Object *active_ob = CTX_data_active_object(C);
730 ARegion *region = CTX_wm_region(C);
731 View3D *v3d = CTX_wm_view3d(C);
733
734 GrowOperatorData *op_data = MEM_new<GrowOperatorData>(__func__);
735 op->customdata = op_data;
736
737 op_data->initial_mouse_x = event->xy[0];
738
739 Curves &curves_id = *static_cast<Curves *>(active_ob->data);
740 auto curve_op_data = std::make_unique<GrowOperatorDataPerCurve>();
741 curve_op_data->curves_id = &curves_id;
742 select_grow_invoke_per_curve(curves_id, *active_ob, *region, *v3d, *rv3d, *curve_op_data);
743 op_data->per_curve.append(std::move(curve_op_data));
744
747}
748
749static int select_grow_modal(bContext *C, wmOperator *op, const wmEvent *event)
750{
751 GrowOperatorData &op_data = *static_cast<GrowOperatorData *>(op->customdata);
752 const int mouse_x = event->xy[0];
753 const int mouse_diff_x = mouse_x - op_data.initial_mouse_x;
754 switch (event->type) {
755 case MOUSEMOVE: {
756 select_grow_update(C, op, mouse_diff_x);
757 break;
758 }
759 case LEFTMOUSE: {
760 MEM_delete(&op_data);
761 return OPERATOR_FINISHED;
762 }
763 case EVT_ESCKEY:
764 case RIGHTMOUSE: {
765 /* Undo operator by resetting the selection to the original value. */
766 for (std::unique_ptr<GrowOperatorDataPerCurve> &curve_op_data : op_data.per_curve) {
767 Curves &curves_id = *curve_op_data->curves_id;
768 CurvesGeometry &curves = curves_id.geometry.wrap();
769 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
770
771 attributes.remove(".selection");
772 if (!curve_op_data->original_selection.is_empty()) {
773 attributes.add(
774 ".selection",
776 bke::cpp_type_to_custom_data_type(curve_op_data->original_selection.type()),
777 bke::AttributeInitVArray(GVArray::ForSpan(curve_op_data->original_selection)));
778 }
779
780 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
781 * attribute for now. */
783 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &curves_id);
784 }
785 MEM_delete(&op_data);
786 return OPERATOR_CANCELLED;
787 }
788 }
790}
791
792} // namespace select_grow
793
795{
796 ot->name = "Select Grow";
797 ot->idname = __func__;
798 ot->description = "Select curves which are close to curves that are selected already";
799
803
805
806 PropertyRNA *prop;
807 prop = RNA_def_float(ot->srna,
808 "distance",
809 0.1f,
810 -FLT_MAX,
811 FLT_MAX,
812 "Distance",
813 "By how much to grow the selection",
814 -10.0f,
815 10.0f);
817}
818
820
822{
824 return false;
825 }
826 Scene *scene = CTX_data_scene(C);
828 if (brush == nullptr) {
829 return false;
830 }
832 return false;
833 }
834 return true;
835}
836
857
859{
860 Scene *scene = CTX_data_scene(C);
861 ARegion *region = op_data.region;
862
863 const float min_distance = op_data.brush->curves_sculpt_settings->minimum_distance;
864 const float brush_radius = BKE_brush_size_get(scene, op_data.brush);
865
866 float3 tangent_x_cu = math::cross(op_data.normal_cu, float3{0, 0, 1});
867 if (math::is_zero(tangent_x_cu)) {
868 tangent_x_cu = math::cross(op_data.normal_cu, float3{0, 1, 0});
869 }
870 tangent_x_cu = math::normalize(tangent_x_cu);
871 const float3 tangent_y_cu = math::normalize(math::cross(op_data.normal_cu, tangent_x_cu));
872
873 /* Sample a few points to get a good estimate of how large the grid has to be. */
874 Vector<float3> points_wo;
875 points_wo.append(op_data.pos_cu + min_distance * tangent_x_cu);
876 points_wo.append(op_data.pos_cu + min_distance * tangent_y_cu);
877 points_wo.append(op_data.pos_cu - min_distance * tangent_x_cu);
878 points_wo.append(op_data.pos_cu - min_distance * tangent_y_cu);
879
880 Vector<float2> points_re;
881 for (const float3 &pos_wo : points_wo) {
882 float2 pos_re;
883 ED_view3d_project_v2(region, pos_wo, pos_re);
884 points_re.append(pos_re);
885 }
886
887 float2 origin_re;
888 ED_view3d_project_v2(region, op_data.pos_cu, origin_re);
889
890 int needed_points = 0;
891 for (const float2 &pos_re : points_re) {
892 const float distance = math::length(pos_re - origin_re);
893 const int needed_points_iter = (brush_radius * 2.0f) / distance;
894
895 if (needed_points_iter > needed_points) {
896 needed_points = needed_points_iter;
897 }
898 }
899
900 /* Limit to a hard-coded number since it only adds noise at some point. */
901 return std::min(300, needed_points);
902}
903
904static void min_distance_edit_draw(bContext *C, int /*x*/, int /*y*/, void *customdata)
905{
906 Scene *scene = CTX_data_scene(C);
907 MinDistanceEditData &op_data = *static_cast<MinDistanceEditData *>(customdata);
908
909 const float min_distance = op_data.brush->curves_sculpt_settings->minimum_distance;
910
911 float3 tangent_x_cu = math::cross(op_data.normal_cu, float3{0, 0, 1});
912 if (math::is_zero(tangent_x_cu)) {
913 tangent_x_cu = math::cross(op_data.normal_cu, float3{0, 1, 0});
914 }
915 tangent_x_cu = math::normalize(tangent_x_cu);
916 const float3 tangent_y_cu = math::normalize(math::cross(op_data.normal_cu, tangent_x_cu));
917
918 const int points_per_side = calculate_points_per_side(C, op_data);
919 const int points_per_axis_num = 2 * points_per_side + 1;
920
921 Vector<float3> points_wo;
922 for (const int x_i : IndexRange(points_per_axis_num)) {
923 for (const int y_i : IndexRange(points_per_axis_num)) {
924 const float x_iter = min_distance * (x_i - (points_per_axis_num - 1) / 2.0f);
925 const float y_iter = min_distance * (y_i - (points_per_axis_num - 1) / 2.0f);
926
927 const float3 point_pos_cu = op_data.pos_cu + op_data.normal_cu * 0.0001f +
928 x_iter * tangent_x_cu + y_iter * tangent_y_cu;
929 const float3 point_pos_wo = math::transform_point(op_data.curves_to_world_mat, point_pos_cu);
930 points_wo.append(point_pos_wo);
931 }
932 }
933
934 float4 circle_col = float4(op_data.brush->add_col);
935 float circle_alpha = op_data.brush->cursor_overlay_alpha;
936 float brush_radius_re = BKE_brush_size_get(scene, op_data.brush);
937
938 /* Draw the grid. */
942
943 ARegion *region = op_data.region;
944 RegionView3D *rv3d = op_data.rv3d;
945 wmWindow *win = CTX_wm_window(C);
946
947 /* It does the same as: `view3d_operator_needs_opengl(C);`. */
948 wmViewport(&region->winrct);
950 GPU_matrix_set(rv3d->viewmat);
951
952 GPUVertFormat *format3d = immVertexFormat();
953
954 const uint pos3d = GPU_vertformat_attr_add(format3d, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
955 const uint col3d = GPU_vertformat_attr_add(format3d, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
956 const uint siz3d = GPU_vertformat_attr_add(format3d, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
957
960 immBegin(GPU_PRIM_POINTS, points_wo.size());
961
962 float3 brush_origin_wo = math::transform_point(op_data.curves_to_world_mat, op_data.pos_cu);
963 float2 brush_origin_re;
964 ED_view3d_project_v2(region, brush_origin_wo, brush_origin_re);
965
966 /* Smooth alpha transition until the brush edge. */
967 const int alpha_border_re = 20;
968 const float dist_to_inner_border_re = brush_radius_re - alpha_border_re;
969
970 for (const float3 &pos_wo : points_wo) {
971 float2 pos_re;
972 ED_view3d_project_v2(region, pos_wo, pos_re);
973
974 const float dist_to_point_re = math::distance(pos_re, brush_origin_re);
975 const float alpha = 1.0f - ((dist_to_point_re - dist_to_inner_border_re) / alpha_border_re);
976
977 immAttr1f(siz3d, 3.0f);
978 immAttr4f(col3d, 0.9f, 0.9f, 0.9f, alpha);
979 immVertex3fv(pos3d, pos_wo);
980 }
981 immEnd();
983
984 /* Reset the drawing settings. */
985 GPU_point_size(1.0f);
988
989 int4 scissor;
990 GPU_scissor_get(scissor);
991 wmWindowViewport(win);
992 GPU_scissor(scissor[0], scissor[1], scissor[2], scissor[3]);
993
994 /* Draw the brush circle. */
995 GPU_matrix_translate_2f(float(op_data.initial_mouse.x), float(op_data.initial_mouse.y));
996
999
1001
1002 immUniformColor3fvAlpha(circle_col, circle_alpha);
1003 imm_draw_circle_wire_2d(pos2d, 0.0f, 0.0f, brush_radius_re, 80);
1004
1007}
1008
1010{
1012 ARegion *region = CTX_wm_region(C);
1013 View3D *v3d = CTX_wm_view3d(C);
1014 Scene *scene = CTX_data_scene(C);
1015
1016 Object &curves_ob_orig = *CTX_data_active_object(C);
1017 Curves &curves_id_orig = *static_cast<Curves *>(curves_ob_orig.data);
1018 Object &surface_ob_orig = *curves_id_orig.surface;
1019 Object *surface_ob_eval = DEG_get_evaluated_object(depsgraph, &surface_ob_orig);
1020 if (surface_ob_eval == nullptr) {
1021 return OPERATOR_CANCELLED;
1022 }
1023 Mesh *surface_me_eval = BKE_object_get_evaluated_mesh(surface_ob_eval);
1024 if (surface_me_eval == nullptr) {
1025 return OPERATOR_CANCELLED;
1026 }
1027
1028 BVHTreeFromMesh surface_bvh_eval;
1029 BKE_bvhtree_from_mesh_get(&surface_bvh_eval, surface_me_eval, BVHTREE_FROM_CORNER_TRIS, 2);
1030 BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval); });
1031
1032 const int2 mouse_pos_int_re{event->mval};
1033 const float2 mouse_pos_re{mouse_pos_int_re};
1034
1035 float3 ray_start_wo, ray_end_wo;
1037 depsgraph, region, v3d, mouse_pos_re, ray_start_wo, ray_end_wo, true);
1038
1039 const CurvesSurfaceTransforms transforms{curves_ob_orig, &surface_ob_orig};
1040
1041 const float3 ray_start_su = math::transform_point(transforms.world_to_surface, ray_start_wo);
1042 const float3 ray_end_su = math::transform_point(transforms.world_to_surface, ray_end_wo);
1043 const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su);
1044
1045 BVHTreeRayHit ray_hit;
1046 ray_hit.dist = FLT_MAX;
1047 ray_hit.index = -1;
1048 BLI_bvhtree_ray_cast(surface_bvh_eval.tree,
1049 ray_start_su,
1050 ray_direction_su,
1051 0.0f,
1052 &ray_hit,
1053 surface_bvh_eval.raycast_callback,
1054 &surface_bvh_eval);
1055 if (ray_hit.index == -1) {
1056 WM_report(RPT_ERROR, "Cursor must be over the surface mesh");
1057 return OPERATOR_CANCELLED;
1058 }
1059
1060 const float3 hit_pos_su = ray_hit.co;
1061 const float3 hit_normal_su = ray_hit.no;
1062
1063 const float3 hit_pos_cu = math::transform_point(transforms.surface_to_curves, hit_pos_su);
1064 const float3 hit_normal_cu = math::normalize(
1065 math::transform_direction(transforms.surface_to_curves_normal, hit_normal_su));
1066
1067 MinDistanceEditData *op_data = MEM_new<MinDistanceEditData>(__func__);
1068 op_data->curves_to_world_mat = transforms.curves_to_world;
1069 op_data->normal_cu = hit_normal_cu;
1070 op_data->pos_cu = hit_pos_cu;
1071 op_data->initial_mouse = event->xy;
1074
1075 if (op_data->initial_minimum_distance <= 0.0f) {
1076 op_data->initial_minimum_distance = 0.01f;
1077 }
1078
1079 op->customdata = op_data;
1080
1081 /* Temporarily disable other paint cursors. */
1083 op_data->orig_paintcursors = wm->paintcursors;
1084 BLI_listbase_clear(&wm->paintcursors);
1085
1086 /* Add minimum distance paint cursor. */
1089
1090 op_data->region = CTX_wm_region(C);
1091 op_data->rv3d = CTX_wm_region_view3d(C);
1092
1094 ED_region_tag_redraw(region);
1096}
1097
1098static int min_distance_edit_modal(bContext *C, wmOperator *op, const wmEvent *event)
1099{
1100 ARegion *region = CTX_wm_region(C);
1101 MinDistanceEditData &op_data = *static_cast<MinDistanceEditData *>(op->customdata);
1102
1103 auto finish = [&]() {
1105
1106 /* Remove cursor. */
1107 WM_paint_cursor_end(static_cast<wmPaintCursor *>(op_data.cursor));
1108 /* Restore original paint cursors. */
1109 wm->paintcursors = op_data.orig_paintcursors;
1110
1111 ED_region_tag_redraw(region);
1112 MEM_delete(&op_data);
1113 };
1114
1115 switch (event->type) {
1116 case MOUSEMOVE: {
1117 const int2 mouse_pos_int_re{event->xy};
1118 const float2 mouse_pos_re{mouse_pos_int_re};
1119
1120 const float mouse_diff_x = mouse_pos_int_re.x - op_data.initial_mouse.x;
1121 const float factor = powf(2, mouse_diff_x / UI_UNIT_X / 10.0f);
1123 factor;
1124
1125 ED_region_tag_redraw(region);
1127 break;
1128 }
1129 case LEFTMOUSE: {
1130 if (event->val == KM_PRESS) {
1132 finish();
1133 return OPERATOR_FINISHED;
1134 }
1135 break;
1136 }
1137 case RIGHTMOUSE:
1138 case EVT_ESCKEY: {
1140 finish();
1142 return OPERATOR_CANCELLED;
1143 }
1144 }
1145
1147}
1148
1149} // namespace min_distance_edit
1150
1152{
1153 ot->name = "Edit Minimum Distance";
1154 ot->idname = __func__;
1155 ot->description = "Change the minimum distance used by the density brush";
1156
1160
1162}
1163
1164} // namespace blender::ed::sculpt_paint
1165
1166/* -------------------------------------------------------------------- */
1169
1179
bool BKE_brush_use_alpha_pressure(const Brush *brush)
Definition brush.cc:1096
int BKE_brush_size_get(const Scene *scene, const Brush *brush)
Definition brush.cc:1075
bool BKE_brush_use_size_pressure(const Brush *brush)
Definition brush.cc:1091
float BKE_brush_alpha_get(const Scene *scene, const Brush *brush)
Definition brush.cc:1153
void BKE_brush_tag_unsaved_changes(Brush *brush)
Definition brush.cc:621
void free_bvhtree_from_mesh(BVHTreeFromMesh *data)
Definition bvhutils.cc:1160
BVHTree * BKE_bvhtree_from_mesh_get(BVHTreeFromMesh *data, const Mesh *mesh, BVHCacheType bvh_cache_type, int tree_type)
Definition bvhutils.cc:899
@ BVHTREE_FROM_CORNER_TRIS
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:1762
const Brush * BKE_paint_brush_for_read(const Paint *paint)
Definition paint.cc:654
Paint * BKE_paint_get_active_from_paintmode(Scene *sce, PaintMode mode)
Definition paint.cc:371
bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint)
Definition paint.cc:1685
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:649
const uchar PAINT_CURSOR_SCULPT_CURVES[3]
Definition paint.cc:247
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
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(struct ListBase *lb)
MINLINE void copy_v3_v3_uchar(unsigned char r[3], const unsigned char a[3])
#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)
Object * DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object)
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1085
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
@ 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_RUNNING_MODAL
void ED_paint_cursor_start(Paint *paint, bool(*poll)(bContext *C))
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:634
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 immAttr1f(uint attr_id, float x)
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
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:175
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(eGPUBlend blend)
Definition gpu_state.cc:42
void GPU_scissor(int x, int y, int width, int height)
Definition gpu_state.cc:188
void GPU_point_size(float size)
Definition gpu_state.cc:167
void GPU_scissor_get(int coords[4])
Definition gpu_state.cc:257
@ GPU_FETCH_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between and object coordinate space Combine Create a color from its and value channels Color Retrieve a color attribute
@ PROP_DISTANCE
Definition RNA_types.hh:159
#define C
Definition RandGen.cpp:29
#define UI_ITEM_NONE
#define UI_UNIT_X
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
@ UI_ITEM_R_SLIDER
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DATA
Definition WM_types.hh:475
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:198
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define ND_MODE
Definition WM_types.hh:412
#define NC_SCENE
Definition WM_types.hh:345
#define ND_TOOLSETTINGS
Definition WM_types.hh:416
@ KM_PRESS
Definition WM_types.hh:284
BPy_StructRNA * depsgraph
static unsigned long seed
Definition btSoftBody.h:39
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:388
Span< T > as_span() const
Definition BLI_array.hh:232
const void * data() const
static GVArray ForSpan(GSpan span)
NonCopyable(const NonCopyable &other)=delete
NonMovable(NonMovable &&other)=delete
constexpr const T * end() const
Definition BLI_span.hh:225
constexpr const T * begin() const
Definition BLI_span.hh:221
int64_t size() const
void append(const T &value)
bool remove(const StringRef attribute_id)
bool add(const StringRef attribute_id, const AttrDomain domain, const eCustomDataType data_type, const AttributeInit &initializer)
IndexMask complement(const IndexMask &universe, IndexMaskMemory &memory) const
void foreach_index(Fn &&fn) const
local_group_size(16, 16) .push_constant(Type b
void ED_operatortypes_sculpt_curves()
#define powf(x, y)
format
eCustomDataType cpp_type_to_custom_data_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:96
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 void min_distance_edit_draw(bContext *C, int, int, void *customdata)
static int min_distance_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static int min_distance_edit_modal(bContext *C, wmOperator *op, const wmEvent *event)
static int calculate_points_per_side(bContext *C, MinDistanceEditData &op_data)
static int select_grow_invoke(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 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 int select_grow_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void select_random_ui(bContext *, wmOperator *op)
static int select_random_exec(bContext *C, 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 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()
static int sculpt_curves_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event)
float brush_radius_get(const Scene &scene, 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 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)
static int curves_sculptmode_toggle_exec(bContext *C, wmOperator *op)
bool curves_sculpt_poll_view3d(bContext *C)
std::unique_ptr< CurvesSculptStrokeOperation > new_smooth_operation()
std::unique_ptr< CurvesSculptStrokeOperation > new_delete_operation()
std::unique_ptr< CurvesSculptStrokeOperation > new_selection_paint_operation(const BrushStrokeMode brush_mode, const bContext &C)
int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintStroke **stroke_p)
float brush_strength_get(const Scene &scene, const Brush &brush, const StrokeExtension &stroke_extension)
static int sculpt_curves_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
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:199
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:95
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
float distance(float a, float b)
MatBase< float, 4, 4 > float4x4
GPU_SHADER_INTERFACE_INFO(overlay_edit_curve_handle_iface, "vert").flat(Type pos vertex_in(1, Type::UINT, "data") .vertex_out(overlay_edit_curve_handle_iface) .geometry_layout(PrimitiveIn Frequency::GEOMETRY storage_buf(1, Qualifier::READ, "uint", "data[]", Frequency::GEOMETRY) .push_constant(Type Frequency::GEOMETRY selection[]
void paint_init_pivot(Object *ob, Scene *scene)
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
#define INT32_MAX
Definition stdint.h:137
unsigned int uint32_t
Definition stdint.h:80
#define INT32_MIN
Definition stdint.h:136
BVHTree_RayCastCallback raycast_callback
float co[3]
Definition BLI_kdopbvh.h:69
float no[3]
Definition BLI_kdopbvh.h:71
float add_col[4]
int cursor_overlay_alpha
struct BrushCurvesSculptSettings * curves_sculpt_settings
char curves_sculpt_brush_type
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
std::unique_ptr< CurvesSculptStrokeOperation > operation
Vector< std::unique_ptr< GrowOperatorDataPerCurve > > per_curve
short val
Definition WM_types.hh:724
short type
Definition WM_types.hh:722
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
int(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1036
struct ReportList * reports
struct uiLayout * layout
struct wmOperatorType * type
struct PointerRNA * ptr
VecBase< float, 4 > float4
void WM_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:4125
#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)