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/* -------------------------------------------------------------------- */
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
81/* -------------------------------------------------------------------- */
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
238static int sculpt_curves_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
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
277/* -------------------------------------------------------------------- */
282{
283 Scene *scene = CTX_data_scene(C);
284 wmMsgBus *mbus = CTX_wm_message_bus(C);
285
287 BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->curves_sculpt);
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{
318 wmMsgBus *mbus = CTX_wm_message_bus(C);
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
352
354}
355
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
381 MutableSpan<float> selection = attribute.span;
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
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");
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);
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);
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
800 ot->invoke = select_grow::select_grow_invoke;
801 ot->modal = select_grow::select_grow_modal;
802 ot->poll = curves::editable_curves_poll;
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
819namespace min_distance_edit {
820
822{
823 if (!curves::curves_with_surface_poll(C)) {
824 return false;
825 }
826 Scene *scene = CTX_data_scene(C);
827 const Brush *brush = BKE_paint_brush_for_read(&scene->toolsettings->curves_sculpt->paint);
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
1009static int min_distance_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1010{
1011 Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
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;
1072 op_data->brush = BKE_paint_brush(&scene->toolsettings->curves_sculpt->paint);
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;
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
1157 ot->poll = min_distance_edit::min_distance_edit_poll;
1158 ot->invoke = min_distance_edit::min_distance_edit_invoke;
1159 ot->modal = min_distance_edit::min_distance_edit_modal;
1160
1162}
1163
1164} // namespace blender::ed::sculpt_paint
1165
1166/* -------------------------------------------------------------------- */
1171{
1172 using namespace blender::ed::sculpt_paint;
1173 WM_operatortype_append(SCULPT_CURVES_OT_brush_stroke);
1174 WM_operatortype_append(CURVES_OT_sculptmode_toggle);
1175 WM_operatortype_append(SCULPT_CURVES_OT_select_random);
1176 WM_operatortype_append(SCULPT_CURVES_OT_select_grow);
1177 WM_operatortype_append(SCULPT_CURVES_OT_min_distance_edit);
1178}
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
@ PROP_DISTANCE
Definition RNA_types.hh:159
#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
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:198
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DATA
Definition WM_types.hh:475
@ KM_PRESS
Definition WM_types.hh:284
#define ND_MODE
Definition WM_types.hh:412
#define NC_SCENE
Definition WM_types.hh:345
#define ND_TOOLSETTINGS
Definition WM_types.hh:416
static unsigned long seed
Definition btSoftBody.h:39
Span< T > as_span() const
Definition BLI_array.hh:232
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:388
const void * data() const
constexpr void fill(const T &value) const
Definition BLI_span.hh:518
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)
IndexMask complement(const IndexMask &universe, IndexMaskMemory &memory) const
void foreach_index(Fn &&fn) const
OffsetIndices slice(const IndexRange range) const
local_group_size(16, 16) .push_constant(Type b
void ED_operatortypes_sculpt_curves()
const Depsgraph * depsgraph
#define powf(x, y)
format
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_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
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
unsigned char paint_cursor_col[4]
float viewmat[4][4]
float winmat[4][4]
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
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1036
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
void(* ui)(bContext *C, wmOperator *op)
Definition WM_types.hh:1053
StructRNA * srna
Definition WM_types.hh:1080
void(* cancel)(bContext *C, wmOperator *op)
Definition WM_types.hh:1028
struct ReportList * reports
struct uiLayout * layout
struct wmOperatorType * type
struct PointerRNA * ptr
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)