Blender V4.3
paint_stroke.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2009 by Nicholas Bishop. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <algorithm>
10#include <cfloat>
11#include <cmath>
12
13#include "MEM_guardedalloc.h"
14
15#include "BLI_math_matrix.h"
16#include "BLI_rand.hh"
17#include "BLI_utildefines.h"
18
19#include "DNA_brush_types.h"
20#include "DNA_curve_types.h"
21#include "DNA_object_types.h"
22#include "DNA_scene_types.h"
23
24#include "RNA_access.hh"
25
26#include "BKE_brush.hh"
27#include "BKE_colortools.hh"
28#include "BKE_context.hh"
29#include "BKE_curve.hh"
30#include "BKE_image.hh"
31#include "BKE_paint.hh"
32
33#include "WM_api.hh"
34#include "WM_types.hh"
35
36#include "GPU_immediate.hh"
37#include "GPU_state.hh"
38
39#include "ED_screen.hh"
40#include "ED_view3d.hh"
41
42#include "IMB_imbuf_types.hh"
43
44#include "paint_intern.hh"
45#include "sculpt_cloth.hh"
46#include "sculpt_intern.hh"
47
48// #define DEBUG_TIME
49
50#ifdef DEBUG_TIME
51# include "BLI_time_utildefines.h"
52#endif
53
55
58 float pressure;
59};
60
62 std::unique_ptr<PaintModeData> mode_data;
65 std::optional<RandomNumberGenerator> rng;
66
67 /* Cached values */
71
72 /* Paint stroke can use up to PAINT_MAX_INPUT_SAMPLES prior inputs
73 * to smooth the stroke */
78
82
84 /* space distance covered so far */
86
87 /* Set whether any stroke step has yet occurred
88 * e.g. in sculpt mode, stroke doesn't start until cursor
89 * passes over the mesh */
91 /* Set when enough motion was found for rake rotation */
93 /* event that started stroke, for modal() return */
95 /* check if stroke variables have been initialized */
97 /* check if various brush mapping variables have been initialized */
100 /* cached_pressure stores initial pressure for size pressure influence mainly */
102 /* last pressure will store last pressure value for use in interpolation for space strokes */
105
107
108 float zoom_2d;
110
111 /* Tilt, as read from the event. */
112 float x_tilt;
113 float y_tilt;
114
115 /* line constraint */
118
124
125 bool original; /* Ray-cast original mesh at start of stroke. */
126};
127
128/*** Cursors ***/
129static void paint_draw_smooth_cursor(bContext *C, int x, int y, void *customdata)
130{
132 Brush *brush = BKE_paint_brush(paint);
133 PaintStroke *stroke = static_cast<PaintStroke *>(customdata);
135
136 if ((mode == PaintMode::GPencil) && (paint->flags & PAINT_SHOW_BRUSH) == 0) {
137 return;
138 }
139
140 if (stroke && brush) {
141 GPU_line_smooth(true);
143
144 ARegion *region = stroke->vc.region;
145
148 immUniformColor4ubv(paint->paint_cursor_col);
149
151 immVertex2f(pos, x, y);
153 stroke->last_mouse_position[0] + region->winrct.xmin,
154 stroke->last_mouse_position[1] + region->winrct.ymin);
155
156 immEnd();
157
159
161 GPU_line_smooth(false);
162 }
163}
164
165static void paint_draw_line_cursor(bContext *C, int x, int y, void *customdata)
166{
168 PaintStroke *stroke = static_cast<PaintStroke *>(customdata);
169
170 GPU_line_smooth(true);
171
172 uint shdr_pos = GPU_vertformat_attr_add(
174
176
177 float viewport_size[4];
178 GPU_viewport_size_get_f(viewport_size);
179 immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
180
181 immUniform1i("colors_len", 2); /* "advanced" mode */
182 const float alpha = float(paint->paint_cursor_col[3]) / 255.0f;
183 immUniform4f("color", 0.0f, 0.0f, 0.0f, alpha);
184 immUniform4f("color2", 1.0f, 1.0f, 1.0f, alpha);
185 immUniform1f("dash_width", 6.0f);
186 immUniform1f("udash_factor", 0.5f);
187
189
190 ARegion *region = stroke->vc.region;
191
192 if (stroke->constrain_line) {
193 immVertex2f(shdr_pos,
194 stroke->last_mouse_position[0] + region->winrct.xmin,
195 stroke->last_mouse_position[1] + region->winrct.ymin);
196
197 immVertex2f(shdr_pos,
198 stroke->constrained_pos[0] + region->winrct.xmin,
199 stroke->constrained_pos[1] + region->winrct.ymin);
200 }
201 else {
202 immVertex2f(shdr_pos,
203 stroke->last_mouse_position[0] + region->winrct.xmin,
204 stroke->last_mouse_position[1] + region->winrct.ymin);
205
206 immVertex2f(shdr_pos, x, y);
207 }
208
209 immEnd();
210
212
213 GPU_line_smooth(false);
214}
215
216static bool image_paint_brush_type_require_location(const Brush &brush, const PaintMode mode)
217{
218 switch (mode) {
220 if (ELEM(brush.sculpt_brush_type,
228 {
229 return false;
230 }
231 else if (cloth::is_cloth_deform_brush(brush)) {
232 return false;
233 }
234 else {
235 return true;
236 }
237 default:
238 break;
239 }
240
241 return true;
242}
243
244static bool paint_stroke_use_scene_spacing(const Brush &brush, const PaintMode mode)
245{
246 switch (mode) {
248 return brush.flag & BRUSH_SCENE_SPACING;
249 default:
250 break;
251 }
252 return false;
253}
254
255static bool image_paint_brush_type_raycast_original(const Brush &brush, PaintMode /*mode*/)
256{
257 return brush.flag & (BRUSH_ANCHORED | BRUSH_DRAG_DOT);
258}
259
261 const PaintMode mode)
262{
263 if (brush.flag & BRUSH_ANCHORED) {
264 return false;
265 }
266
267 switch (mode) {
269 if (ELEM(brush.sculpt_brush_type,
278 {
279 return false;
280 }
281 else {
282 return true;
283 }
284 default:
285 break;
286 }
287
288 return true;
289}
290
292 const Brush &brush,
293 PaintMode mode,
294 PaintStroke *stroke,
295 const float mouse_init[2],
296 float mouse[2],
297 float pressure,
298 float r_location[3],
299 bool *r_location_is_set)
300{
301 Scene *scene = CTX_data_scene(C);
302 UnifiedPaintSettings &ups = *stroke->ups;
303 bool location_sampled = false;
304 bool location_success = false;
305 /* Use to perform all operations except applying the stroke,
306 * needed for operations that require cursor motion (rake). */
307 bool is_dry_run = false;
308 bool do_random = false;
309 bool do_random_mask = false;
310 *r_location_is_set = false;
311 /* XXX: Use pressure value from first brush step for brushes which don't
312 * support strokes (grab, thumb). They depends on initial state and
313 * brush coord/pressure/etc.
314 * It's more an events design issue, which doesn't split coordinate/pressure/angle
315 * changing events. We should avoid this after events system re-design */
316 if (!stroke->brush_init) {
317 copy_v2_v2(stroke->initial_mouse, mouse);
318 copy_v2_v2(ups.last_rake, mouse);
319 copy_v2_v2(ups.tex_mouse, mouse);
320 copy_v2_v2(ups.mask_tex_mouse, mouse);
321 stroke->cached_size_pressure = pressure;
322
323 stroke->brush_init = true;
324 }
325
326 if (paint_supports_dynamic_size(brush, mode)) {
327 copy_v2_v2(ups.tex_mouse, mouse);
328 copy_v2_v2(ups.mask_tex_mouse, mouse);
329 stroke->cached_size_pressure = pressure;
330 }
331
332 /* Truly temporary data that isn't stored in properties */
333
334 ups.stroke_active = true;
336
337 ups.pixel_radius = BKE_brush_size_get(scene, &brush);
338 ups.initial_pixel_radius = BKE_brush_size_get(scene, &brush);
339
340 if (BKE_brush_use_size_pressure(&brush) && paint_supports_dynamic_size(brush, mode)) {
341 ups.pixel_radius *= stroke->cached_size_pressure;
342 }
343
344 if (paint_supports_dynamic_tex_coords(brush, mode)) {
345
346 if (ELEM(brush.mtex.brush_map_mode,
350 {
351 do_random = true;
352 }
353
356 }
357 else {
358 copy_v2_v2(ups.tex_mouse, mouse);
359 }
360
361 /* take care of mask texture, if any */
362 if (brush.mask_mtex.tex) {
363
364 if (ELEM(brush.mask_mtex.brush_map_mode,
368 {
369 do_random_mask = true;
370 }
371
374 }
375 else {
376 copy_v2_v2(ups.mask_tex_mouse, mouse);
377 }
378 }
379 }
380
381 if (brush.flag & BRUSH_ANCHORED) {
382 bool hit = false;
383 float halfway[2];
384
385 const float dx = mouse[0] - stroke->initial_mouse[0];
386 const float dy = mouse[1] - stroke->initial_mouse[1];
387
388 ups.anchored_size = ups.pixel_radius = sqrtf(dx * dx + dy * dy);
389
390 ups.brush_rotation = ups.brush_rotation_sec = atan2f(dy, dx) + float(0.5f * M_PI);
391
392 if (brush.flag & BRUSH_EDGE_TO_EDGE) {
393 halfway[0] = dx * 0.5f + stroke->initial_mouse[0];
394 halfway[1] = dy * 0.5f + stroke->initial_mouse[1];
395
396 if (stroke->get_location) {
397 if (stroke->get_location(C, r_location, halfway, stroke->original)) {
398 hit = true;
399 location_sampled = true;
400 location_success = true;
401 *r_location_is_set = true;
402 }
403 else if (!image_paint_brush_type_require_location(brush, mode)) {
404 hit = true;
405 }
406 }
407 else {
408 hit = true;
409 }
410 }
411 if (hit) {
413 copy_v2_v2(ups.tex_mouse, halfway);
414 copy_v2_v2(ups.mask_tex_mouse, halfway);
415 copy_v2_v2(mouse, halfway);
416 ups.anchored_size /= 2.0f;
417 ups.pixel_radius /= 2.0f;
418 stroke->stroke_distance = ups.pixel_radius;
419 }
420 else {
422 copy_v2_v2(mouse, stroke->initial_mouse);
423 stroke->stroke_distance = ups.pixel_radius;
424 }
425 ups.pixel_radius /= stroke->zoom_2d;
426 ups.draw_anchored = true;
427 }
428 else {
429 /* here we are using the initial mouse coordinate because we do not want the rake
430 * result to depend on jittering */
431 if (!stroke->brush_init) {
432 copy_v2_v2(ups.last_rake, mouse_init);
433 }
434 /* curve strokes do their own rake calculation */
435 else if (!(brush.flag & BRUSH_CURVE)) {
436 if (!paint_calculate_rake_rotation(ups, brush, mouse_init, mode, stroke->rake_started)) {
437 /* Not enough motion to define an angle. */
438 if (!stroke->rake_started) {
439 is_dry_run = true;
440 }
441 }
442 else {
443 stroke->rake_started = true;
444 }
445 }
446 }
447
448 if ((do_random || do_random_mask) && !stroke->rng) {
449 /* Lazy initialization. */
451 }
452
453 if (do_random) {
455 ups.brush_rotation += -brush.mtex.random_angle / 2.0f +
456 brush.mtex.random_angle * stroke->rng->get_float();
457 }
458 }
459
460 if (do_random_mask) {
462 ups.brush_rotation_sec += -brush.mask_mtex.random_angle / 2.0f +
463 brush.mask_mtex.random_angle * stroke->rng->get_float();
464 }
465 }
466
467 if (!location_sampled) {
468 if (stroke->get_location) {
469 if (stroke->get_location(C, r_location, mouse, stroke->original)) {
470 location_success = true;
471 *r_location_is_set = true;
472 }
473 else if (!image_paint_brush_type_require_location(brush, mode)) {
474 location_success = true;
475 }
476 }
477 else {
478 zero_v3(r_location);
479 location_success = true;
480 /* don't set 'r_location_is_set', since we don't want to use the value. */
481 }
482 }
483
484 return location_success && (is_dry_run == false);
485}
486
487static bool paint_stroke_use_dash(const Brush &brush)
488{
489 /* Only these stroke modes support dash lines */
490 return brush.flag & BRUSH_SPACE || brush.flag & BRUSH_LINE || brush.flag & BRUSH_CURVE;
491}
492
493static bool paint_stroke_use_jitter(PaintMode mode, const Brush &brush, bool invert)
494{
495 bool use_jitter = (brush.flag & BRUSH_ABSOLUTE_JITTER) ? (brush.jitter_absolute != 0) :
496 (brush.jitter != 0);
497
498 /* jitter-ed brush gives weird and unpredictable result for this
499 * kinds of stroke, so manually disable jitter usage (sergey) */
500 use_jitter &= (brush.flag & (BRUSH_DRAG_DOT | BRUSH_ANCHORED)) == 0;
501 use_jitter &= (!ELEM(mode, PaintMode::Texture2D, PaintMode::Texture3D) ||
503
504 return use_jitter;
505}
506
508 const PaintStroke &stroke,
509 const PaintMode mode,
510 const Brush &brush,
511 const float pressure,
512 const float mval[2],
513 float r_mouse_out[2])
514{
515 if (paint_stroke_use_jitter(mode, brush, stroke.stroke_mode == BRUSH_STROKE_INVERT)) {
516 float delta[2];
517 float factor = stroke.zoom_2d;
518
519 if (brush.flag & BRUSH_JITTER_PRESSURE) {
520 factor *= pressure;
521 }
522
523 BKE_brush_jitter_pos(scene, brush, mval, r_mouse_out);
524
525 /* XXX: meh, this is round about because
526 * BKE_brush_jitter_pos isn't written in the best way to
527 * be reused here */
528 if (factor != 1.0f) {
529 sub_v2_v2v2(delta, r_mouse_out, mval);
530 mul_v2_fl(delta, factor);
531 add_v2_v2v2(r_mouse_out, mval, delta);
532 }
533 }
534 else {
535 copy_v2_v2(r_mouse_out, mval);
536 }
537}
538
539/* Put the location of the next stroke dot into the stroke RNA and apply it to the mesh */
541 bContext *C, wmOperator *op, PaintStroke *stroke, const float mval[2], float pressure)
542{
543 Scene &scene = *CTX_data_scene(C);
546 const Brush &brush = *BKE_paint_brush_for_read(&paint);
547 UnifiedPaintSettings *ups = stroke->ups;
548 float mouse_out[2];
549 PointerRNA itemptr;
550 float location[3];
551
552/* the following code is adapted from texture paint. It may not be needed but leaving here
553 * just in case for reference (code in texpaint removed as part of refactoring).
554 * It's strange that only texpaint had these guards. */
555#if 0
556 /* special exception here for too high pressure values on first touch in
557 * windows for some tablets, then we just skip first touch. */
558 if (tablet && (pressure >= 0.99f) &&
559 ((pop->s.brush.flag & BRUSH_SPACING_PRESSURE) ||
560 BKE_brush_use_alpha_pressure(pop->s.brush) || BKE_brush_use_size_pressure(pop->s.brush)))
561 {
562 return;
563 }
564
565 /* This can be removed once fixed properly in
566 * BKE_brush_painter_paint(
567 * BrushPainter *painter, BrushFunc func,
568 * float *pos, double time, float pressure, void *user);
569 * at zero pressure we should do nothing 1/2^12 is 0.0002
570 * which is the sensitivity of the most sensitive pen tablet available */
571 if (tablet && (pressure < 0.0002f) &&
572 ((pop->s.brush.flag & BRUSH_SPACING_PRESSURE) ||
573 BKE_brush_use_alpha_pressure(pop->s.brush) || BKE_brush_use_size_pressure(pop->s.brush)))
574 {
575 return;
576 }
577#endif
578
579 /* copy last position -before- jittering, or space fill code
580 * will create too many dabs */
581 copy_v2_v2(stroke->last_mouse_position, mval);
582 stroke->last_pressure = pressure;
583
584 if (paint_stroke_use_scene_spacing(brush, mode)) {
585 float world_space_position[3];
586
588 C, world_space_position, stroke->last_mouse_position, stroke->original))
589 {
590 copy_v3_v3(stroke->last_world_space_position, world_space_position);
591 mul_m4_v3(stroke->vc.obact->object_to_world().ptr(), stroke->last_world_space_position);
592 }
593 else {
595 }
596 }
597
598 /* Get jitter position (same as mval if no jitter is used). */
599 paint_stroke_jitter_pos(scene, *stroke, mode, brush, pressure, mval, mouse_out);
600
601 bool is_location_is_set;
603 C, brush, mode, stroke, mval, mouse_out, pressure, location, &is_location_is_set);
604 if (is_location_is_set) {
605 copy_v3_v3(ups->last_location, location);
606 }
607 if (!ups->last_hit) {
608 return;
609 }
610
611 /* Dash */
612 bool add_step = true;
613 if (paint_stroke_use_dash(brush)) {
614 int dash_samples = stroke->tot_samples % brush.dash_samples;
615 float dash = float(dash_samples) / float(brush.dash_samples);
616 if (dash > brush.dash_ratio) {
617 add_step = false;
618 }
619 }
620
621 /* Add to stroke */
622 if (add_step) {
623 RNA_collection_add(op->ptr, "stroke", &itemptr);
624 RNA_float_set(&itemptr, "size", ups->pixel_radius);
625 RNA_float_set_array(&itemptr, "location", location);
626 /* Mouse coordinates modified by the stroke type options. */
627 RNA_float_set_array(&itemptr, "mouse", mouse_out);
628 /* Original mouse coordinates. */
629 RNA_float_set_array(&itemptr, "mouse_event", mval);
630 RNA_boolean_set(&itemptr, "pen_flip", stroke->pen_flip);
631 RNA_float_set(&itemptr, "pressure", pressure);
632 RNA_float_set(&itemptr, "x_tilt", stroke->x_tilt);
633 RNA_float_set(&itemptr, "y_tilt", stroke->y_tilt);
634
635 stroke->update_step(C, op, stroke, &itemptr);
636
637 /* don't record this for now, it takes up a lot of memory when doing long
638 * strokes with small brush size, and operators have register disabled */
639 RNA_collection_clear(op->ptr, "stroke");
640 }
641
642 stroke->tot_samples++;
643}
644
645/* Returns zero if no sculpt changes should be made, non-zero otherwise */
647 const PaintSample *sample,
648 PaintMode mode,
649 float r_mouse[2],
650 float *r_pressure)
651{
652 if (paint_supports_smooth_stroke(stroke, *stroke->brush, mode)) {
653 float radius = stroke->brush->smooth_stroke_radius * stroke->zoom_2d;
654 float u = stroke->brush->smooth_stroke_factor;
655
656 /* If the mouse is moving within the radius of the last move,
657 * don't update the mouse position. This allows sharp turns. */
658 if (len_squared_v2v2(stroke->last_mouse_position, sample->mouse) < square_f(radius)) {
659 return false;
660 }
661
662 interp_v2_v2v2(r_mouse, sample->mouse, stroke->last_mouse_position, u);
663 *r_pressure = interpf(sample->pressure, stroke->last_pressure, u);
664 }
665 else {
666 r_mouse[0] = sample->mouse[0];
667 r_mouse[1] = sample->mouse[1];
668 *r_pressure = sample->pressure;
669 }
670
671 return true;
672}
673
675 const Scene *scene,
676 PaintStroke *stroke,
677 float size_pressure,
678 float spacing_pressure)
679{
682 const Brush &brush = *BKE_paint_brush_for_read(paint);
683 float size_clamp = 0.0f;
684 float size = BKE_brush_size_get(scene, stroke->brush) * size_pressure;
685 if (paint_stroke_use_scene_spacing(brush, mode)) {
686 if (!BKE_brush_use_locked_size(scene, &brush)) {
687 float last_object_space_position[3];
688 mul_v3_m4v3(last_object_space_position,
689 stroke->vc.obact->world_to_object().ptr(),
691 size_clamp = paint_calc_object_space_radius(stroke->vc, last_object_space_position, size);
692 }
693 else {
694 size_clamp = BKE_brush_unprojected_radius_get(scene, &brush) * size_pressure;
695 }
696 }
697 else {
698 /* brushes can have a minimum size of 1.0 but with pressure it can be smaller than a pixel
699 * causing very high step sizes, hanging blender #32381. */
700 size_clamp = max_ff(1.0f, size);
701 }
702
703 float spacing = stroke->brush->spacing;
704
705 /* apply spacing pressure */
706 if (stroke->brush->flag & BRUSH_SPACING_PRESSURE) {
707 spacing = spacing * (1.5f - spacing_pressure);
708 }
709
710 if (cloth::is_cloth_deform_brush(brush)) {
711 /* The spacing in tools that use the cloth solver should not be affected by the brush radius to
712 * avoid affecting the simulation update rate when changing the radius of the brush.
713 * With a value of 100 and the brush default of 10 for spacing, a simulation step runs every 2
714 * pixels movement of the cursor. */
715 size_clamp = 100.0f;
716 }
717
718 /* stroke system is used for 2d paint too, so we need to account for
719 * the fact that brush can be scaled there. */
720 spacing *= stroke->zoom_2d;
721
722 if (paint_stroke_use_scene_spacing(brush, mode)) {
723 /* Low pressure on size (with tablets) can cause infinite recursion in paint_space_stroke(),
724 * see #129853. */
725 return max_ff(FLT_EPSILON, size_clamp * spacing / 50.0f);
726 }
727 return max_ff(stroke->zoom_2d, size_clamp * spacing / 50.0f);
728}
729
730static float paint_stroke_overlapped_curve(const Brush &br, float x, float spacing)
731{
732 /* Avoid division by small numbers, can happen
733 * on some pen setups. See #105341.
734 */
735
736 spacing = max_ff(spacing, 0.1f);
737
738 const int n = 100 / spacing;
739 const float h = spacing / 50.0f;
740 const float x0 = x - 1;
741
742 float sum = 0;
743 for (int i = 0; i < n; i++) {
744 float xx;
745
746 xx = fabsf(x0 + i * h);
747
748 if (xx < 1.0f) {
749 sum += BKE_brush_curve_strength(&br, xx, 1);
750 }
751 }
752
753 return sum;
754}
755
756static float paint_stroke_integrate_overlap(const Brush &br, float factor)
757{
758 float spacing = br.spacing * factor;
759
760 if (!(br.flag & BRUSH_SPACE_ATTEN && (br.spacing < 100))) {
761 return 1.0;
762 }
763
764 int m = 10;
765 float g = 1.0f / m;
766 float max = 0;
767 for (int i = 0; i < m; i++) {
768 float overlap = fabs(paint_stroke_overlapped_curve(br, i * g, spacing));
769
770 if (overlap > max) {
771 max = overlap;
772 }
773 }
774
775 if (max == 0.0f) {
776 return 1.0f;
777 }
778 return 1.0f / max;
779}
780
782 const Scene *scene,
783 PaintStroke *stroke,
784 float pressure,
785 float dpressure,
786 float length)
787{
788 if (BKE_brush_use_size_pressure(stroke->brush)) {
789 /* use pressure to modify size. set spacing so that at 100%, the circles
790 * are aligned nicely with no overlap. for this the spacing needs to be
791 * the average of the previous and next size. */
792 float s = paint_space_stroke_spacing(C, scene, stroke, 1.0f, pressure);
793 float q = s * dpressure / (2.0f * length);
794 float pressure_fac = (1.0f + q) / (1.0f - q);
795
796 float last_size_pressure = stroke->last_pressure;
797 float new_size_pressure = stroke->last_pressure * pressure_fac;
798
799 /* average spacing */
800 float last_spacing = paint_space_stroke_spacing(
801 C, scene, stroke, last_size_pressure, pressure);
802 float new_spacing = paint_space_stroke_spacing(C, scene, stroke, new_size_pressure, pressure);
803
804 return 0.5f * (last_spacing + new_spacing);
805 }
806
807 /* no size pressure */
808 return paint_space_stroke_spacing(C, scene, stroke, 1.0f, pressure);
809}
810
811/* For brushes with stroke spacing enabled, moves mouse in steps
812 * towards the final mouse location. */
814 wmOperator *op,
815 PaintStroke *stroke,
816 const float final_mouse[2],
817 float final_pressure)
818{
819 const Scene *scene = CTX_data_scene(C);
820 ARegion *region = CTX_wm_region(C);
821 UnifiedPaintSettings *ups = stroke->ups;
824 const Brush &brush = *BKE_paint_brush_for_read(&paint);
825 int count = 0;
826
827 const bool use_scene_spacing = paint_stroke_use_scene_spacing(brush, mode);
828 float d_world_space_position[3] = {0.0f};
829
830 float no_pressure_spacing = paint_space_stroke_spacing(C, scene, stroke, 1.0f, 1.0f);
831 float pressure = stroke->last_pressure;
832 float dpressure = final_pressure - stroke->last_pressure;
833
834 float dmouse[2];
835 sub_v2_v2v2(dmouse, final_mouse, stroke->last_mouse_position);
836 float length = normalize_v2(dmouse);
837
838 if (use_scene_spacing) {
839 float world_space_position[3];
840 bool hit = SCULPT_stroke_get_location(C, world_space_position, final_mouse, stroke->original);
841 mul_m4_v3(stroke->vc.obact->object_to_world().ptr(), world_space_position);
842 if (hit && stroke->stroke_over_mesh) {
843 sub_v3_v3v3(d_world_space_position, world_space_position, stroke->last_world_space_position);
844 length = len_v3(d_world_space_position);
845 stroke->stroke_over_mesh = true;
846 }
847 else {
848 length = 0.0f;
849 zero_v3(d_world_space_position);
850 stroke->stroke_over_mesh = hit;
851 if (stroke->stroke_over_mesh) {
852 copy_v3_v3(stroke->last_world_space_position, world_space_position);
853 }
854 }
855 }
856
857 while (length > 0.0f) {
859 C, scene, stroke, pressure, dpressure, length);
860 BLI_assert(spacing >= 0.0f);
861
862 float mouse[2];
863
864 if (length >= spacing) {
865 if (use_scene_spacing) {
866 float final_world_space_position[3];
867 normalize_v3(d_world_space_position);
868 mul_v3_v3fl(final_world_space_position, d_world_space_position, spacing);
869 add_v3_v3v3(final_world_space_position,
871 final_world_space_position);
872 ED_view3d_project_v2(region, final_world_space_position, mouse);
873
874 mul_v3_v3fl(stroke->last_scene_spacing_delta, d_world_space_position, spacing);
875 }
876 else {
877 mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing;
878 mouse[1] = stroke->last_mouse_position[1] + dmouse[1] * spacing;
879 }
880 pressure = stroke->last_pressure + (spacing / length) * dpressure;
881
883 spacing / no_pressure_spacing);
884
885 stroke->stroke_distance += spacing / stroke->zoom_2d;
886 paint_brush_stroke_add_step(C, op, stroke, mouse, pressure);
887
888 length -= spacing;
889 pressure = stroke->last_pressure;
890 dpressure = final_pressure - stroke->last_pressure;
891
892 count++;
893 }
894 else {
895 break;
896 }
897 }
898
899 return count;
900}
901
902/**** Public API ****/
903
905 wmOperator *op,
906 StrokeGetLocation get_location,
907 StrokeTestStart test_start,
908 StrokeUpdateStep update_step,
909 StrokeRedraw redraw,
910 StrokeDone done,
911 int event_type)
912{
914 PaintStroke *stroke = MEM_new<PaintStroke>(__func__);
915 ToolSettings *toolsettings = CTX_data_tool_settings(C);
916 UnifiedPaintSettings *ups = &toolsettings->unified_paint_settings;
918 Brush *br = stroke->brush = BKE_paint_brush(paint);
920 float zoomx, zoomy;
921
923
924 stroke->get_location = get_location;
925 stroke->test_start = test_start;
926 stroke->update_step = update_step;
927 stroke->redraw = redraw;
928 stroke->done = done;
929 stroke->event_type = event_type; /* for modal, return event */
930 stroke->ups = ups;
931 stroke->stroke_mode = RNA_enum_get(op->ptr, "mode");
932
935
936 get_imapaint_zoom(C, &zoomx, &zoomy);
937 stroke->zoom_2d = max_ff(zoomx, zoomy);
938
939 /* Check here if color sampling the main brush should do color conversion. This is done here
940 * to avoid locking up to get the image buffer during sampling. */
941 ups->do_linear_conversion = false;
942 ups->colorspace = nullptr;
943
944 if (br->mtex.tex && br->mtex.tex->type == TEX_IMAGE && br->mtex.tex->ima) {
946 br->mtex.tex->ima, &br->mtex.tex->iuser, nullptr);
947 if (tex_ibuf && tex_ibuf->float_buffer.data == nullptr) {
948 ups->do_linear_conversion = true;
949 ups->colorspace = tex_ibuf->byte_buffer.colorspace;
950 }
951 BKE_image_pool_release_ibuf(br->mtex.tex->ima, tex_ibuf, nullptr);
952 }
953
954 if (stroke->stroke_mode == BRUSH_STROKE_INVERT) {
955 if (br->flag & BRUSH_CURVE) {
957 }
958 }
959 /* initialize here */
960 ups->overlap_factor = 1.0;
961 ups->stroke_active = true;
962
963 if (rv3d) {
964 rv3d->rflag |= RV3D_PAINTING;
965 }
966
967 /* Preserve location from last stroke while applying and resetting
968 * ups->average_stroke_counter to 1.
969 */
970 if (ups->average_stroke_counter) {
971 mul_v3_fl(ups->average_stroke_accum, 1.0f / float(ups->average_stroke_counter));
972 ups->average_stroke_counter = 1;
973 }
974
975 /* initialize here to avoid initialization conflict with threaded strokes */
977 if (paint->flags & PAINT_USE_CAVITY_MASK) {
978 BKE_curvemapping_init(paint->cavity_curve);
979 }
980
982
984
985 return stroke;
986}
987
989{
991 if (rv3d) {
992 rv3d->rflag &= ~RV3D_PAINTING;
993 }
994
996
997 if (stroke == nullptr) {
998 return;
999 }
1000
1001 UnifiedPaintSettings *ups = stroke->ups;
1002 ups->draw_anchored = false;
1003 ups->stroke_active = false;
1004
1005 if (stroke->timer) {
1007 }
1008
1009 if (stroke->stroke_cursor) {
1010 WM_paint_cursor_end(static_cast<wmPaintCursor *>(stroke->stroke_cursor));
1011 }
1012
1013 MEM_delete(stroke);
1014}
1015
1016static void stroke_done(bContext *C, wmOperator *op, PaintStroke *stroke)
1017{
1018 UnifiedPaintSettings *ups = stroke->ups;
1019
1020 /* reset rotation here to avoid doing so in cursor display */
1021 if (!(stroke->brush->mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) {
1022 ups->brush_rotation = 0.0f;
1023 }
1024
1025 if (!(stroke->brush->mask_mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) {
1026 ups->brush_rotation_sec = 0.0f;
1027 }
1028
1029 if (stroke->stroke_started) {
1030 if (stroke->redraw) {
1031 stroke->redraw(C, stroke, true);
1032 }
1033
1034 if (stroke->done) {
1035 stroke->done(C, stroke);
1036 }
1037 }
1038
1039 paint_stroke_free(C, op, stroke);
1040}
1041
1046
1048{
1049 if ((br.flag & BRUSH_SPACE) == 0) {
1050 return false;
1051 }
1052
1054 /* The Cloth Brush is a special case for stroke spacing. Even if it has grab modes which do
1055 * not support dynamic size, stroke spacing needs to be enabled so it is possible to control
1056 * whether the simulation runs constantly or only when the brush moves when using the cloth
1057 * grab brushes. */
1058 return true;
1059 }
1060
1061 if (mode == PaintMode::SculptCurves &&
1063 {
1064 return false;
1065 }
1066
1068 /* No spacing needed for now. */
1069 return false;
1070 }
1071
1072 return paint_supports_dynamic_size(br, mode);
1073}
1074
1091
1093{
1094 if (br.flag & BRUSH_ANCHORED) {
1095 return false;
1096 }
1097
1098 switch (mode) {
1099 case PaintMode::Sculpt:
1100 if (sculpt_is_grab_tool(br)) {
1101 return false;
1102 }
1103 break;
1104
1105 case PaintMode::Texture2D: /* fall through */
1108 return false;
1109 }
1110 break;
1111
1112 default:
1113 break;
1114 }
1115 return true;
1116}
1117
1119{
1120 /* The grease pencil draw tool needs to enable this when the `stroke_mode` is set to
1121 * `BRUSH_STROKE_SMOOTH`. */
1122 if (mode == PaintMode::GPencil &&
1125 {
1126 return true;
1127 }
1128 if (!(brush.flag & BRUSH_SMOOTH_STROKE) ||
1130 {
1131 return false;
1132 }
1133
1134 switch (mode) {
1135 case PaintMode::Sculpt:
1136 if (sculpt_is_grab_tool(brush)) {
1137 return false;
1138 }
1139 break;
1140 default:
1141 break;
1142 }
1143 return true;
1144}
1145
1147{
1148 /* omit: PAINT_WEIGHT, PAINT_SCULPT_UV, PAINT_INVALID */
1149 return ELEM(
1151}
1152
1154{
1155 if (br.flag & BRUSH_ANCHORED) {
1156 return false;
1157 }
1158
1159 switch (mode) {
1160 case PaintMode::Sculpt:
1161 if (sculpt_is_grab_tool(br)) {
1162 return false;
1163 }
1164 break;
1165 default:
1166 break;
1167 }
1168 return true;
1169}
1170
1171#define PAINT_STROKE_MODAL_CANCEL 1
1172
1174{
1175 static EnumPropertyItem modal_items[] = {
1176 {PAINT_STROKE_MODAL_CANCEL, "CANCEL", 0, "Cancel", "Cancel and undo a stroke in progress"},
1177 {0}};
1178
1179 static const char *name = "Paint Stroke Modal";
1180
1181 wmKeyMap *keymap = WM_modalkeymap_find(keyconf, name);
1182
1183 /* This function is called for each space-type, only needs to add map once. */
1184 if (!keymap) {
1185 keymap = WM_modalkeymap_ensure(keyconf, name, modal_items);
1186 }
1187
1188 return keymap;
1189}
1190
1192 PaintStroke *stroke, int input_samples, float x, float y, float pressure)
1193{
1194 PaintSample *sample = &stroke->samples[stroke->cur_sample];
1195 int max_samples = std::clamp(input_samples, 1, PAINT_MAX_INPUT_SAMPLES);
1196
1197 sample->mouse[0] = x;
1198 sample->mouse[1] = y;
1199 sample->pressure = pressure;
1200
1201 stroke->cur_sample++;
1202 if (stroke->cur_sample >= max_samples) {
1203 stroke->cur_sample = 0;
1204 }
1205 if (stroke->num_samples < max_samples) {
1206 stroke->num_samples++;
1207 }
1208}
1209
1211{
1212 memset(average, 0, sizeof(*average));
1213
1214 BLI_assert(stroke->num_samples > 0);
1215
1216 for (int i = 0; i < stroke->num_samples; i++) {
1217 add_v2_v2(average->mouse, stroke->samples[i].mouse);
1218 average->pressure += stroke->samples[i].pressure;
1219 }
1220
1221 mul_v2_fl(average->mouse, 1.0f / stroke->num_samples);
1222 average->pressure /= stroke->num_samples;
1223
1224 // printf("avg=(%f, %f), num=%d\n", average->mouse[0], average->mouse[1], stroke->num_samples);
1225}
1226
1232 wmOperator *op,
1233 PaintStroke *stroke,
1234 float spacing,
1235 float *length_residue,
1236 const float old_pos[2],
1237 const float new_pos[2])
1238{
1239 UnifiedPaintSettings *ups = stroke->ups;
1241 Brush &brush = *BKE_paint_brush(paint);
1243 ARegion *region = CTX_wm_region(C);
1244
1245 const bool use_scene_spacing = paint_stroke_use_scene_spacing(brush, mode);
1246
1247 float mouse[2], dmouse[2];
1248 float length;
1249 float d_world_space_position[3] = {0.0f};
1250 float world_space_position_old[3], world_space_position_new[3];
1251
1252 copy_v2_v2(stroke->last_mouse_position, old_pos);
1253
1254 if (use_scene_spacing) {
1255 bool hit_old = SCULPT_stroke_get_location(
1256 C, world_space_position_old, old_pos, stroke->original);
1257 bool hit_new = SCULPT_stroke_get_location(
1258 C, world_space_position_new, new_pos, stroke->original);
1259 mul_m4_v3(stroke->vc.obact->object_to_world().ptr(), world_space_position_old);
1260 mul_m4_v3(stroke->vc.obact->object_to_world().ptr(), world_space_position_new);
1261 if (hit_old && hit_new && stroke->stroke_over_mesh) {
1262 sub_v3_v3v3(d_world_space_position, world_space_position_new, world_space_position_old);
1263 length = len_v3(d_world_space_position);
1264 stroke->stroke_over_mesh = true;
1265 }
1266 else {
1267 length = 0.0f;
1268 zero_v3(d_world_space_position);
1269 stroke->stroke_over_mesh = hit_new;
1270 if (stroke->stroke_over_mesh) {
1271 copy_v3_v3(stroke->last_world_space_position, world_space_position_old);
1272 }
1273 }
1274 }
1275 else {
1276 sub_v2_v2v2(dmouse, new_pos, old_pos);
1277 length = normalize_v2(dmouse);
1278 }
1279
1280 BLI_assert(length >= 0.0f);
1281
1282 if (length == 0.0f) {
1283 return;
1284 }
1285
1286 while (length > 0.0f) {
1287 float spacing_final = spacing - *length_residue;
1288 length += *length_residue;
1289 *length_residue = 0.0;
1290
1291 if (length >= spacing) {
1292 if (use_scene_spacing) {
1293 float final_world_space_position[3];
1294 normalize_v3(d_world_space_position);
1295 mul_v3_v3fl(final_world_space_position, d_world_space_position, spacing_final);
1297 final_world_space_position, world_space_position_old, final_world_space_position);
1298 ED_view3d_project_v2(region, final_world_space_position, mouse);
1299 }
1300 else {
1301 mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing_final;
1302 mouse[1] = stroke->last_mouse_position[1] + dmouse[1] * spacing_final;
1303 }
1304
1306
1307 stroke->stroke_distance += spacing / stroke->zoom_2d;
1308 paint_brush_stroke_add_step(C, op, stroke, mouse, 1.0);
1309
1310 length -= spacing;
1311 spacing_final = spacing;
1312 }
1313 else {
1314 break;
1315 }
1316 }
1317
1318 *length_residue = length;
1319}
1320
1322 wmOperator *op,
1323 PaintStroke *stroke,
1324 const float mouse[2])
1325{
1326 Brush *br = stroke->brush;
1327 if (stroke->stroke_started && (br->flag & BRUSH_LINE)) {
1329
1330 paint_brush_stroke_add_step(C, op, stroke, stroke->last_mouse_position, 1.0);
1331 paint_space_stroke(C, op, stroke, mouse, 1.0);
1332 }
1333}
1334
1336{
1337 const Brush &br = *stroke->brush;
1338 if (!(br.flag & BRUSH_CURVE)) {
1339 return false;
1340 }
1341
1343 const Scene *scene = CTX_data_scene(C);
1344 const float spacing = paint_space_stroke_spacing(C, scene, stroke, 1.0f, 1.0f);
1345 const PaintCurve *pc = br.paint_curve;
1346 const PaintCurvePoint *pcp;
1347 float length_residue = 0.0f;
1348 int i;
1349
1350 if (!pc) {
1351 return true;
1352 }
1353
1354#ifdef DEBUG_TIME
1355 TIMEIT_START_AVERAGED(whole_stroke);
1356#endif
1357
1358 pcp = pc->points;
1360
1361 for (i = 0; i < pc->tot_points - 1; i++, pcp++) {
1362 int j;
1363 float data[(PAINT_CURVE_NUM_SEGMENTS + 1) * 2];
1364 float tangents[(PAINT_CURVE_NUM_SEGMENTS + 1) * 2];
1365 const PaintCurvePoint *pcp_next = pcp + 1;
1366 bool do_rake = false;
1367
1368 for (j = 0; j < 2; j++) {
1370 pcp->bez.vec[2][j],
1371 pcp_next->bez.vec[0][j],
1372 pcp_next->bez.vec[1][j],
1373 data + j,
1375 sizeof(float[2]));
1376 }
1377
1380 {
1381 do_rake = true;
1382 for (j = 0; j < 2; j++) {
1384 pcp->bez.vec[2][j],
1385 pcp_next->bez.vec[0][j],
1386 pcp_next->bez.vec[1][j],
1387 tangents + j,
1389 sizeof(float[2]));
1390 }
1391 }
1392
1393 for (j = 0; j < PAINT_CURVE_NUM_SEGMENTS; j++) {
1394 if (do_rake) {
1395 float rotation = atan2f(tangents[2 * j + 1], tangents[2 * j]) + float(0.5f * M_PI);
1396 paint_update_brush_rake_rotation(ups, br, rotation);
1397 }
1398
1399 if (!stroke->stroke_started) {
1400 stroke->last_pressure = 1.0;
1401 copy_v2_v2(stroke->last_mouse_position, data + 2 * j);
1402
1405 C, stroke->last_world_space_position, data + 2 * j, stroke->original);
1406 mul_m4_v3(stroke->vc.obact->object_to_world().ptr(), stroke->last_world_space_position);
1407 }
1408
1409 stroke->stroke_started = stroke->test_start(C, op, stroke->last_mouse_position);
1410
1411 if (stroke->stroke_started) {
1412 paint_brush_stroke_add_step(C, op, stroke, data + 2 * j, 1.0);
1414 C, op, stroke, spacing, &length_residue, data + 2 * j, data + 2 * (j + 1));
1415 }
1416 }
1417 else {
1419 C, op, stroke, spacing, &length_residue, data + 2 * j, data + 2 * (j + 1));
1420 }
1421 }
1422 }
1423
1424 stroke_done(C, op, stroke);
1425
1426#ifdef DEBUG_TIME
1427 TIMEIT_END_AVERAGED(whole_stroke);
1428#endif
1429
1430 return true;
1431}
1432
1433static void paint_stroke_line_constrain(PaintStroke *stroke, float mouse[2])
1434{
1435 if (stroke->constrain_line) {
1436 float line[2];
1437 float angle, len, res;
1438
1439 sub_v2_v2v2(line, mouse, stroke->last_mouse_position);
1440 angle = atan2f(line[1], line[0]);
1441 len = len_v2(line);
1442
1443 /* divide angle by PI/4 */
1444 angle = 4.0f * angle / float(M_PI);
1445
1446 /* now take residue */
1447 res = angle - floorf(angle);
1448
1449 /* residue decides how close we are at a certain angle */
1450 if (res <= 0.5f) {
1452 }
1453 else {
1454 angle = (floorf(angle) + 1.0f) * float(M_PI_4);
1455 }
1456
1457 mouse[0] = stroke->constrained_pos[0] = len * cosf(angle) + stroke->last_mouse_position[0];
1458 mouse[1] = stroke->constrained_pos[1] = len * sinf(angle) + stroke->last_mouse_position[1];
1459 }
1460}
1461
1462int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintStroke **stroke_p)
1463{
1464 Scene *scene = CTX_data_scene(C);
1467 PaintStroke *stroke = *stroke_p;
1468 Brush *br = stroke->brush = BKE_paint_brush(paint);
1469 PaintSample sample_average;
1470 float mouse[2];
1471 bool first_dab = false;
1472 bool first_modal = false;
1473 bool redraw = false;
1474
1475 if (event->type == INBETWEEN_MOUSEMOVE &&
1477 {
1479 }
1480
1481 /* see if tablet affects event. Line, anchored and drag dot strokes do not support pressure */
1482 const float tablet_pressure = WM_event_tablet_data(event, &stroke->pen_flip, nullptr);
1483 float pressure = ((br->flag & (BRUSH_LINE | BRUSH_ANCHORED | BRUSH_DRAG_DOT)) ? 1.0f :
1484 tablet_pressure);
1485
1486 /* When processing a timer event the pressure from the event is 0, so use the last valid
1487 * pressure. */
1488 if (event->type == TIMER) {
1489 pressure = stroke->last_tablet_event_pressure;
1490 }
1491 else {
1492 stroke->last_tablet_event_pressure = pressure;
1493 }
1494
1495 int input_samples = BKE_brush_input_samples_get(scene, br);
1496 paint_stroke_add_sample(stroke, input_samples, event->mval[0], event->mval[1], pressure);
1497 paint_stroke_sample_average(stroke, &sample_average);
1498
1499 /* Tilt. */
1500 if (WM_event_is_tablet(event)) {
1501 stroke->x_tilt = event->tablet.x_tilt;
1502 stroke->y_tilt = event->tablet.y_tilt;
1503 }
1504
1505#ifdef WITH_INPUT_NDOF
1506 /* let NDOF motion pass through to the 3D view so we can paint and rotate simultaneously!
1507 * this isn't perfect... even when an extra MOUSEMOVE is spoofed, the stroke discards it
1508 * since the 2D deltas are zero -- code in this file needs to be updated to use the
1509 * post-NDOF_MOTION MOUSEMOVE */
1510 if (event->type == NDOF_MOTION) {
1511 return OPERATOR_PASS_THROUGH;
1512 }
1513#endif
1514
1515 /* one time initialization */
1516 if (!stroke->stroke_init) {
1517 if (paint_stroke_curve_end(C, op, stroke)) {
1518 *stroke_p = nullptr;
1519 return OPERATOR_FINISHED;
1520 }
1521
1522 stroke->stroke_init = true;
1523 first_modal = true;
1524 }
1525
1526 /* one time stroke initialization */
1527 if (!stroke->stroke_started) {
1528 stroke->last_pressure = sample_average.pressure;
1529 copy_v2_v2(stroke->last_mouse_position, sample_average.mouse);
1530 if (paint_stroke_use_scene_spacing(*br, mode)) {
1532 C, stroke->last_world_space_position, sample_average.mouse, stroke->original);
1533 mul_m4_v3(stroke->vc.obact->object_to_world().ptr(), stroke->last_world_space_position);
1534 }
1535 stroke->stroke_started = stroke->test_start(C, op, sample_average.mouse);
1536
1537 if (stroke->stroke_started) {
1538 /* StrokeTestStart often updates the currently active brush so we need to re-retrieve it
1539 * here. */
1540 br = BKE_paint_brush(paint);
1541
1542 if (paint_supports_smooth_stroke(stroke, *br, mode)) {
1547 stroke);
1548 }
1549
1550 if (br->flag & BRUSH_AIRBRUSH) {
1551 stroke->timer = WM_event_timer_add(
1553 }
1554
1555 if (br->flag & BRUSH_LINE) {
1558 }
1559
1560 first_dab = true;
1561 }
1562 }
1563
1564 /* Cancel */
1565 if (event->type == EVT_MODAL_MAP && event->val == PAINT_STROKE_MODAL_CANCEL) {
1566 if (op->type->cancel) {
1567 op->type->cancel(C, op);
1568 }
1569 else {
1570 paint_stroke_cancel(C, op, stroke);
1571 }
1572 return OPERATOR_CANCELLED;
1573 }
1574
1575 if (event->type == stroke->event_type && !first_modal) {
1576 if (event->val == KM_RELEASE) {
1577 copy_v2_fl2(mouse, event->mval[0], event->mval[1]);
1578 paint_stroke_line_constrain(stroke, mouse);
1579 paint_stroke_line_end(C, op, stroke, mouse);
1580 stroke_done(C, op, stroke);
1581 *stroke_p = nullptr;
1582 return OPERATOR_FINISHED;
1583 }
1584 }
1585 else if (ELEM(event->type, EVT_RETKEY, EVT_SPACEKEY)) {
1586 paint_stroke_line_end(C, op, stroke, sample_average.mouse);
1587 stroke_done(C, op, stroke);
1588 *stroke_p = nullptr;
1589 return OPERATOR_FINISHED;
1590 }
1591 else if (br->flag & BRUSH_LINE) {
1592 if (event->modifier & KM_ALT) {
1593 stroke->constrain_line = true;
1594 }
1595 else {
1596 stroke->constrain_line = false;
1597 }
1598
1599 copy_v2_fl2(mouse, event->mval[0], event->mval[1]);
1600 paint_stroke_line_constrain(stroke, mouse);
1601
1602 if (stroke->stroke_started && (first_modal || ISMOUSE_MOTION(event->type))) {
1605 {
1606 copy_v2_v2(stroke->ups->last_rake, stroke->last_mouse_position);
1607 }
1608 paint_calculate_rake_rotation(*stroke->ups, *br, mouse, mode, true);
1609 }
1610 }
1611 else if (first_modal ||
1612 /* regular dabs */
1613 (!(br->flag & BRUSH_AIRBRUSH) && ISMOUSE_MOTION(event->type)) ||
1614 /* airbrush */
1615 ((br->flag & BRUSH_AIRBRUSH) && event->type == TIMER &&
1616 event->customdata == stroke->timer))
1617 {
1618 if (paint_smooth_stroke(stroke, &sample_average, mode, mouse, &pressure)) {
1619 if (stroke->stroke_started) {
1620 if (paint_space_stroke_enabled(*br, mode)) {
1621 if (paint_space_stroke(C, op, stroke, mouse, pressure)) {
1622 redraw = true;
1623 }
1624 }
1625 else {
1626 float dmouse[2];
1627 sub_v2_v2v2(dmouse, mouse, stroke->last_mouse_position);
1628 stroke->stroke_distance += len_v2(dmouse);
1629 paint_brush_stroke_add_step(C, op, stroke, mouse, pressure);
1630 redraw = true;
1631 }
1632 }
1633 }
1634 }
1635
1636 /* we want the stroke to have the first daub at the start location
1637 * instead of waiting till we have moved the space distance */
1638 if (first_dab && paint_space_stroke_enabled(*br, mode) && !(br->flag & BRUSH_SMOOTH_STROKE)) {
1640 paint_brush_stroke_add_step(C, op, stroke, sample_average.mouse, sample_average.pressure);
1641 redraw = true;
1642 }
1643
1644 /* Don't update the paint cursor in #INBETWEEN_MOUSEMOVE events. */
1645 if (event->type != INBETWEEN_MOUSEMOVE) {
1646 wmWindow *window = CTX_wm_window(C);
1647 ARegion *region = CTX_wm_region(C);
1648
1649 if (region && (paint->flags & PAINT_SHOW_BRUSH)) {
1650 WM_paint_cursor_tag_redraw(window, region);
1651 }
1652 }
1653
1654 /* Draw for all events (even in between) otherwise updating the brush
1655 * display is noticeably delayed.
1656 */
1657 if (redraw && stroke->redraw) {
1658 stroke->redraw(C, stroke, false);
1659 }
1660
1662}
1663
1665{
1666 /* only when executed for the first time */
1667 if (stroke->stroke_started == 0) {
1668 PropertyRNA *strokeprop;
1669 PointerRNA firstpoint;
1670 float mouse[2];
1671
1672 strokeprop = RNA_struct_find_property(op->ptr, "stroke");
1673
1674 if (RNA_property_collection_lookup_int(op->ptr, strokeprop, 0, &firstpoint)) {
1675 RNA_float_get_array(&firstpoint, "mouse", mouse);
1676 stroke->stroke_started = stroke->test_start(C, op, mouse);
1677 }
1678 }
1679
1680 if (stroke->stroke_started) {
1681 RNA_BEGIN (op->ptr, itemptr, "stroke") {
1682 stroke->update_step(C, op, stroke, &itemptr);
1683 }
1684 RNA_END;
1685 }
1686
1687 bool ok = (stroke->stroke_started != 0);
1688
1689 stroke_done(C, op, stroke);
1690
1692}
1693
1695{
1696 stroke_done(C, op, stroke);
1697}
1698
1700{
1701 return &stroke->vc;
1702}
1703
1705{
1706 return stroke->mode_data.get();
1707}
1708
1710{
1711 return stroke->pen_flip;
1712}
1713
1715{
1716 return stroke->stroke_mode == BRUSH_STROKE_INVERT;
1717}
1718
1720{
1721 return stroke->stroke_distance;
1722}
1723
1724void paint_stroke_set_mode_data(PaintStroke *stroke, std::unique_ptr<PaintModeData> mode_data)
1725{
1726 stroke->mode_data = std::move(mode_data);
1727}
1728
1730{
1731 return stroke->stroke_started;
1732}
1733
1734static const bToolRef *brush_tool_get(const bContext *C)
1735{
1738 ScrArea *area = CTX_wm_area(C);
1739 ARegion *region = CTX_wm_region(C);
1740
1741 if (paint && ob && BKE_paint_brush(paint) &&
1742 (area && ELEM(area->spacetype, SPACE_VIEW3D, SPACE_IMAGE)) &&
1743 (region && region->regiontype == RGN_TYPE_WINDOW))
1744 {
1745 if (area->runtime.tool && area->runtime.tool->runtime &&
1747 {
1748 return area->runtime.tool;
1749 }
1750 }
1751 return nullptr;
1752}
1753
1755{
1756 /* Check the current tool is a brush. */
1757 return brush_tool_get(C) != nullptr;
1758}
1759
1761{
1762 const bToolRef *tref = brush_tool_get(C);
1763 if (!tref) {
1764 return false;
1765 }
1766
1767 /* Don't use brush cursor when the tool sets its own cursor. */
1768 if (tref->runtime->cursor != WM_CURSOR_DEFAULT) {
1769 return false;
1770 }
1771
1772 return true;
1773}
1774
1775} // namespace blender::ed::sculpt_paint
void BKE_brush_jitter_pos(const Scene &scene, const Brush &brush, const float pos[2], float jitterpos[2])
Definition brush.cc:1224
bool BKE_brush_use_alpha_pressure(const Brush *brush)
Definition brush.cc:1096
void BKE_brush_randomize_texture_coords(UnifiedPaintSettings *ups, bool mask)
Definition brush.cc:1251
float BKE_brush_unprojected_radius_get(const Scene *scene, const Brush *brush)
Definition brush.cc:1133
int BKE_brush_size_get(const Scene *scene, const Brush *brush)
Definition brush.cc:1075
float BKE_brush_curve_strength(eBrushCurvePreset preset, const CurveMapping *cumap, float distance, float brush_radius)
Definition brush.cc:1388
bool BKE_brush_use_size_pressure(const Brush *brush)
Definition brush.cc:1091
bool BKE_brush_use_locked_size(const Scene *scene, const Brush *brush)
Definition brush.cc:1083
int BKE_brush_input_samples_get(const Scene *scene, const Brush *brush)
Definition brush.cc:1180
void BKE_curvemapping_init(CurveMapping *cumap)
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
ScrArea * CTX_wm_area(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
ToolSettings * CTX_data_tool_settings(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)
void BKE_curve_forward_diff_bezier(float q0, float q1, float q2, float q3, float *p, int it, int stride)
Definition curve.cc:1663
void BKE_curve_forward_diff_tangent_bezier(float q0, float q1, float q2, float q3, float *p, int it, int stride)
Definition curve.cc:1691
ImBuf * BKE_image_pool_acquire_ibuf(Image *ima, ImageUser *iuser, ImagePool *pool)
void BKE_image_pool_release_ibuf(Image *ima, ImBuf *ibuf, ImagePool *pool)
PaintMode
Definition BKE_paint.hh:99
@ SculptGreasePencil
Definition BKE_paint.hh:116
void BKE_paint_set_overlay_override(enum eOverlayFlags flag)
Definition paint.cc:297
void paint_update_brush_rake_rotation(UnifiedPaintSettings &ups, const Brush &brush, float rotation)
Definition paint.cc:1980
const Brush * BKE_paint_brush_for_read(const Paint *paint)
Definition paint.cc:654
Paint * BKE_paint_get_active_from_context(const bContext *C)
Definition paint.cc:477
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:649
bool paint_calculate_rake_rotation(UnifiedPaintSettings &ups, const Brush &brush, const float mouse_pos[2], PaintMode paint_mode, bool stroke_has_started)
Definition paint.cc:2005
PaintMode BKE_paintmode_get_active_from_context(const bContext *C)
Definition paint.cc:506
#define BLI_assert(a)
Definition BLI_assert.h:50
MINLINE float max_ff(float a, float b)
MINLINE float square_f(float a)
MINLINE float interpf(float target, float origin, float t)
#define M_PI
#define M_PI_4
void mul_m4_v3(const float M[4][4], float r[3])
void mul_v3_m4v3(float r[3], const float mat[4][4], const float vec[3])
MINLINE float len_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_squared_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v2_fl2(float v[2], float x, float y)
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void mul_v2_fl(float r[2], float f)
void interp_v2_v2v2(float r[2], const float a[2], const float b[2], float t)
Definition math_vector.c:21
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void add_v2_v2(float r[2], const float a[2])
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void add_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE float normalize_v2(float n[2])
MINLINE void zero_v3(float r[3])
MINLINE void mul_v3_v3fl(float r[3], const float a[3], float f)
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3(float n[3])
MINLINE float len_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
unsigned int uint
Utility defines for timing/benchmarks.
#define TIMEIT_START_AVERAGED(var)
#define TIMEIT_END_AVERAGED(var)
#define ELEM(...)
eBrushGPaintType
@ GPAINT_BRUSH_TYPE_DRAW
@ SCULPT_BRUSH_TYPE_THUMB
@ SCULPT_BRUSH_TYPE_GRAB
@ SCULPT_BRUSH_TYPE_BOUNDARY
@ SCULPT_BRUSH_TYPE_CLOTH
@ SCULPT_BRUSH_TYPE_POSE
@ SCULPT_BRUSH_TYPE_SNAKE_HOOK
@ SCULPT_BRUSH_TYPE_ELASTIC_DEFORM
@ SCULPT_BRUSH_TYPE_ROTATE
@ BRUSH_SPACE_ATTEN
@ BRUSH_DRAG_DOT
@ BRUSH_SPACING_PRESSURE
@ BRUSH_LINE
@ BRUSH_CURVE
@ BRUSH_EDGE_TO_EDGE
@ BRUSH_SMOOTH_STROKE
@ BRUSH_ANCHORED
@ BRUSH_JITTER_PRESSURE
@ BRUSH_ABSOLUTE_JITTER
@ BRUSH_USE_GRADIENT
@ BRUSH_SCENE_SPACING
@ BRUSH_AIRBRUSH
@ BRUSH_SPACE
@ IMAGE_PAINT_BRUSH_TYPE_FILL
@ IMAGE_PAINT_BRUSH_TYPE_CLONE
@ BRUSH_CLOTH_DEFORM_GRAB
eOverlayFlags
eBrushCurvesSculptType
@ CURVES_SCULPT_BRUSH_TYPE_ADD
@ CURVES_SCULPT_BRUSH_TYPE_DENSITY
Object is a sort of wrapper for general info.
@ PAINT_USE_CAVITY_MASK
@ PAINT_SHOW_BRUSH
#define PAINT_MAX_INPUT_SAMPLES
@ RGN_TYPE_WINDOW
#define RGN_TYPE_ANY
@ SPACE_IMAGE
@ SPACE_VIEW3D
#define SPACE_TYPE_ANY
@ MTEX_MAP_MODE_AREA
@ MTEX_MAP_MODE_RANDOM
@ MTEX_MAP_MODE_VIEW
@ MTEX_ANGLE_RANDOM
@ MTEX_ANGLE_RAKE
@ TEX_IMAGE
@ RV3D_PAINTING
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
@ TOOLREF_FLAG_USE_BRUSHES
void ED_view3d_project_v2(const ARegion *region, const float world[3], float r_region_co[2])
ViewContext ED_view3d_viewcontext_init(bContext *C, Depsgraph *depsgraph)
void immUniformColor4ubv(const unsigned char rgba[4])
void immUniform4f(const char *name, float x, float y, float z, float w)
void immEnd()
void immUnbindProgram()
void immUniform2f(const char *name, float x, float y)
void immVertex2f(uint attr_id, float x, float y)
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
void immUniform1i(const char *name, int x)
void immUniform1f(const char *name, float x)
GPUVertFormat * immVertexFormat()
void immBegin(GPUPrimType, uint vertex_len)
@ GPU_PRIM_LINES
@ GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR
@ GPU_SHADER_3D_UNIFORM_COLOR
@ 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_line_smooth(bool enable)
Definition gpu_state.cc:78
void GPU_viewport_size_get_f(float coords[4])
Definition gpu_state.cc:262
@ GPU_FETCH_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:125
Contains defines and structs used throughout the imbuf module.
Read Guarded memory(de)allocation.
#define RNA_BEGIN(sptr, itemptr, propname)
#define RNA_END
#define C
Definition RandGen.cpp:29
@ KM_RELEASE
Definition WM_types.hh:285
@ KM_ALT
Definition WM_types.hh:257
BPy_StructRNA * depsgraph
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
SIMD_FORCE_INLINE btScalar length(const btQuaternion &q)
Return the length of a quaternion.
static T sum(const btAlignedObjectArray< T > &items)
static RandomNumberGenerator from_random_seed()
Definition rand.cc:365
#define sinf(x)
#define cosf(x)
#define atan2f(x, y)
#define floorf(x)
#define fabsf(x)
#define sqrtf(x)
int len
draw_view in_light_buf[] float
int count
CCL_NAMESPACE_BEGIN ccl_device float invert(float color, float factor)
Definition invert.h:9
ccl_device_inline float average(const float2 a)
ccl_device_inline float2 fabs(const float2 a)
bool is_cloth_deform_brush(const Brush &brush)
static void paint_stroke_add_sample(PaintStroke *stroke, int input_samples, float x, float y, float pressure)
int paint_stroke_exec(bContext *C, wmOperator *op, PaintStroke *stroke)
static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, PaintStroke *stroke, const float mval[2], float pressure)
static void paint_stroke_line_constrain(PaintStroke *stroke, float mouse[2])
bool paint_supports_texture(PaintMode mode)
static const bToolRef * brush_tool_get(const bContext *C)
void(*)(const bContext *C, PaintStroke *stroke, bool final) StrokeRedraw
static bool paint_smooth_stroke(PaintStroke *stroke, const PaintSample *sample, PaintMode mode, float r_mouse[2], float *r_pressure)
bool paint_stroke_started(PaintStroke *stroke)
static void paint_line_strokes_spacing(bContext *C, wmOperator *op, PaintStroke *stroke, float spacing, float *length_residue, const float old_pos[2], const float new_pos[2])
static bool sculpt_is_grab_tool(const Brush &br)
void paint_stroke_jitter_pos(Scene &scene, const PaintStroke &stroke, const PaintMode mode, const Brush &brush, const float pressure, const float mval[2], float r_mouse_out[2])
void paint_stroke_cancel(bContext *C, wmOperator *op, PaintStroke *stroke)
bool paint_supports_dynamic_tex_coords(const Brush &br, PaintMode mode)
bool paint_supports_dynamic_size(const Brush &br, PaintMode mode)
static void paint_draw_line_cursor(bContext *C, int x, int y, void *customdata)
void(*)(bContext *C, wmOperator *op, PaintStroke *stroke, PointerRNA *itemptr) StrokeUpdateStep
static float paint_stroke_integrate_overlap(const Brush &br, float factor)
static void stroke_done(const bContext *C, PaintStroke *stroke)
static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *stroke)
bool paint_stroke_flipped(PaintStroke *stroke)
static void paint_stroke_sample_average(const PaintStroke *stroke, PaintSample *average)
static void paint_draw_smooth_cursor(bContext *C, int x, int y, void *customdata)
static float paint_space_stroke_spacing_variable(bContext *C, const Scene *scene, PaintStroke *stroke, float pressure, float dpressure, float length)
static void paint_stroke_line_end(bContext *C, wmOperator *op, PaintStroke *stroke, const float mouse[2])
static int paint_space_stroke(bContext *C, wmOperator *op, PaintStroke *stroke, const float final_mouse[2], float final_pressure)
static bool paint_stroke_use_jitter(PaintMode mode, const Brush &brush, bool invert)
static bool paint_stroke_use_dash(const Brush &brush)
ViewContext * paint_stroke_view_context(PaintStroke *stroke)
bool paint_brush_cursor_poll(bContext *C)
static bool image_paint_brush_type_require_inbetween_mouse_events(const Brush &brush, const PaintMode mode)
int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintStroke **stroke_p)
bool(*)(bContext *C, float location[3], const float mouse[2], bool force_original) StrokeGetLocation
static bool image_paint_brush_type_require_location(const Brush &brush, const PaintMode mode)
static bool curves_sculpt_brush_uses_spacing(const eBrushCurvesSculptType tool)
wmKeyMap * paint_stroke_modal_keymap(wmKeyConfig *keyconf)
bool(*)(bContext *C, wmOperator *op, const float mouse[2]) StrokeTestStart
bool paint_brush_update(bContext *C, const Brush &brush, PaintMode mode, PaintStroke *stroke, const float mouse_init[2], float mouse[2], float pressure, float r_location[3], bool *r_location_is_set)
void * paint_stroke_mode_data(PaintStroke *stroke)
float paint_stroke_distance_get(PaintStroke *stroke)
bool paint_space_stroke_enabled(const Brush &br, PaintMode mode)
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)
static bool paint_stroke_use_scene_spacing(const Brush &brush, const PaintMode mode)
static float paint_space_stroke_spacing(bContext *C, const Scene *scene, PaintStroke *stroke, float size_pressure, float spacing_pressure)
static float paint_stroke_overlapped_curve(const Brush &br, float x, float spacing)
void(*)(const bContext *C, PaintStroke *stroke) StrokeDone
static bool image_paint_brush_type_raycast_original(const Brush &brush, PaintMode)
bool paint_stroke_inverted(PaintStroke *stroke)
bool paint_brush_tool_poll(bContext *C)
void paint_stroke_set_mode_data(PaintStroke *stroke, std::unique_ptr< PaintModeData > mode_data)
bool paint_supports_smooth_stroke(PaintStroke *stroke, const Brush &br, PaintMode mode)
VecBase< float, 2 > float2
VecBase< float, 3 > float3
bool get_imapaint_zoom(bContext *C, float *zoomx, float *zoomy)
@ BRUSH_STROKE_SMOOTH
@ BRUSH_STROKE_NORMAL
@ BRUSH_STROKE_INVERT
float paint_calc_object_space_radius(const ViewContext &vc, const blender::float3 &center, float pixel_radius)
#define PAINT_CURVE_NUM_SEGMENTS
#define PAINT_STROKE_MODAL_CANCEL
void RNA_collection_clear(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
void RNA_float_get_array(PointerRNA *ptr, const char *name, float *values)
void RNA_collection_add(PointerRNA *ptr, const char *name, PointerRNA *r_value)
bool RNA_property_collection_lookup_int(PointerRNA *ptr, PropertyRNA *prop, int key, PointerRNA *r_ptr)
void RNA_float_set(PointerRNA *ptr, const char *name, float value)
void RNA_enum_set(PointerRNA *ptr, const char *name, int value)
void RNA_float_set_array(PointerRNA *ptr, const char *name, const float *values)
int RNA_enum_get(PointerRNA *ptr, const char *name)
bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mval[2], bool force_original)
Definition sculpt.cc:4732
float vec[3][3]
char sculpt_brush_type
float jitter
int cloth_deform_type
struct MTex mtex
float smooth_stroke_factor
int smooth_stroke_radius
char image_brush_type
struct CurveMapping * curve
int dash_samples
int jitter_absolute
float dash_ratio
char curves_sculpt_brush_type
struct MTex mask_mtex
char gpencil_brush_type
struct PaintCurve * paint_curve
int overlay_flags
ColorSpace * colorspace
ImBufFloatBuffer float_buffer
ImBufByteBuffer byte_buffer
char brush_angle_mode
char brush_map_mode
float random_angle
struct Tex * tex
PaintCurvePoint * points
struct bToolRef * tool
ScrArea_Runtime runtime
struct ImageUser iuser
struct Image * ima
struct UnifiedPaintSettings unified_paint_settings
struct ColorSpace * colorspace
ARegion * region
Definition ED_view3d.hh:73
Object * obact
Definition ED_view3d.hh:71
bToolRef_Runtime * runtime
std::unique_ptr< PaintModeData > mode_data
std::optional< RandomNumberGenerator > rng
PaintSample samples[PAINT_MAX_INPUT_SAMPLES]
int ymin
int xmin
short val
Definition WM_types.hh:724
int mval[2]
Definition WM_types.hh:728
uint8_t modifier
Definition WM_types.hh:739
short type
Definition WM_types.hh:722
void * customdata
Definition WM_types.hh:772
void(* cancel)(bContext *C, wmOperator *op)
Definition WM_types.hh:1028
struct wmOperatorType * type
struct PointerRNA * ptr
float max
@ WM_CURSOR_DEFAULT
Definition wm_cursors.hh:15
void WM_paint_cursor_tag_redraw(wmWindow *win, ARegion *)
Definition wm_draw.cc:1533
float WM_event_tablet_data(const wmEvent *event, bool *r_pen_flip, float r_tilt[2])
bool WM_event_is_tablet(const wmEvent *event)
#define ISMOUSE_MOTION(event_type)
@ TIMER
@ EVT_MODAL_MAP
@ EVT_SPACEKEY
@ NDOF_MOTION
@ INBETWEEN_MOUSEMOVE
@ EVT_RETKEY
wmKeyMap * WM_modalkeymap_ensure(wmKeyConfig *keyconf, const char *idname, const EnumPropertyItem *items)
Definition wm_keymap.cc:933
wmKeyMap * WM_modalkeymap_find(wmKeyConfig *keyconf, const char *idname)
Definition wm_keymap.cc:960
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 WM_event_timer_remove(wmWindowManager *wm, wmWindow *, wmTimer *timer)
wmTimer * WM_event_timer_add(wmWindowManager *wm, wmWindow *win, const int event_type, const double time_step)